OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 17 | Rev 26 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.ui.component.combo;
15
 
16
import java.awt.Color;
17
import java.awt.Component;
18
import java.awt.Dimension;
19
import java.awt.Font;
20
import java.awt.Point;
21
import java.awt.event.ActionEvent;
22
import java.awt.event.MouseAdapter;
23
import java.awt.event.MouseEvent;
24
import java.awt.event.MouseMotionAdapter;
25
 
26
import javax.swing.Action;
27
import javax.swing.BorderFactory;
28
import javax.swing.BoxLayout;
29
import javax.swing.DefaultListCellRenderer;
30
import javax.swing.JLabel;
31
import javax.swing.JList;
32
import javax.swing.JPopupMenu;
33
import javax.swing.JScrollPane;
34
import javax.swing.ListModel;
35
import javax.swing.ListSelectionModel;
36
import javax.swing.ScrollPaneConstants;
37
 
38
public class ISearchableComboPopup<T> extends JPopupMenu {
39
 
40
    private static final int MAXROW = 10;
41
 
42
    private final JList list;
43
    private int minWitdh = 150;
44
    private final ISearchableCombo<T> text;
45
 
46
    ISearchableComboPopup(final ListModel listModel, final ISearchableCombo<T> text) {
47
        // Liste de la popup
48
        this.list = new JList(listModel);
49
        this.text = text;
50
        uiInit();
51
        // Listeners
52
        this.list.addMouseMotionListener(new ListMouseMotionHandler());
53
    }
54
 
55
    private ISearchableCombo<T> getCombo() {
56
        return this.text;
57
    }
58
 
59
    private ListModel getListModel() {
60
        return this.list.getModel();
61
    }
62
 
63
    private void uiInit() {
64
        this.list.setFocusable(false);
65
        this.list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
66
        this.list.addMouseListener(new MouseAdapter() {
67
            public void mouseReleased(MouseEvent e) {
68
                validateSelection();
69
            }
70
        });
71
        this.list.setCellRenderer(new DefaultListCellRenderer() {
72
            @SuppressWarnings("unchecked")
73
            @Override
74
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
75
                // MAYBE optimize
76
                final JLabel comp = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
77
                comp.setFont(getCombo().getFont());
78
                if (value instanceof Action) {
25 ilm 79
                    comp.setFont(comp.getFont().deriveFont(Font.ITALIC));
17 ilm 80
                    comp.setText((String) ((Action) value).getValue(Action.NAME));
81
                    comp.setIcon(null);
82
                } else {
83
                    final ISearchableComboItem<T> val = (ISearchableComboItem<T>) value;
84
                    comp.setText(val.asString());
85
                    comp.setIcon(getCombo().getIcon(val));
86
                    if (getCombo().isEmptyItem(val)) {
87
                        comp.setFont(comp.getFont().deriveFont(Font.ITALIC));
88
                    }
89
                }
90
                return comp;
91
            }
92
        });
93
        // Scroller
94
        JScrollPane scroller = new JScrollPane(this.list, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
95
        scroller.setFocusable(false);
96
        scroller.getVerticalScrollBar().setFocusable(false);
97
        scroller.setBorder(null);
98
        // Popup
99
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
100
        setBorderPainted(true);
101
        setBorder(BorderFactory.createLineBorder(Color.black));
102
        setOpaque(true);
103
        add(scroller);
104
 
105
        setFocusable(false);
106
    }
107
 
108
    public Dimension getPreferredSize() {
109
        Dimension d = super.getPreferredSize();
110
 
111
        int width = d.width;
112
        if (width > 500)
113
            width = 500;
114
        width = Math.max(width, this.minWitdh) + 2;
115
 
116
        Dimension newD = new Dimension(width, d.height);
117
        return newD;
118
    }
119
 
120
    public void setMinWith(int i) {
121
        this.minWitdh = i;
122
    }
123
 
124
    protected class ListMouseMotionHandler extends MouseMotionAdapter {
125
        public void mouseMoved(MouseEvent anEvent) {
126
            updateListBoxSelection(anEvent.getPoint());
127
        }
128
    }
129
 
130
    protected void updateListBoxSelection(Point location) {
131
        this.updateListBoxSelection(location, true);
132
    }
133
 
134
    private void updateListBoxSelection(Point location, boolean shouldScroll) {
135
        if (this.list == null)
136
            return;
137
        int index = this.list.locationToIndex(location);
138
        if (index == -1) {
139
            if (location.y < 0)
140
                index = 0;
141
            else
142
                index = this.getListModel().getSize() - 1;
143
        }
144
        if (this.list.getSelectedIndex() != index) {
145
            // MAYBE optimize (already faster than setSelectedIndex())
146
            this.list.getSelectionModel().setSelectionInterval(index, index);
147
            if (shouldScroll)
148
                this.list.ensureIndexIsVisible(index);
149
        }
150
    }
151
 
152
    public void selectNext() {
153
        int i = this.list.getSelectedIndex() + 1;
154
        if (i < this.getListModel().getSize()) {
155
            this.list.setSelectedIndex(i);
156
            this.list.ensureIndexIsVisible(i);
157
        }
158
    }
159
 
160
    public void selectNextPage() {
161
        int i = Math.min(MAXROW + Math.max(this.list.getSelectedIndex(), 0), this.getListModel().getSize() - 1);
162
        if (i < this.getListModel().getSize()) {
163
            this.list.setSelectedIndex(i);
164
            this.list.ensureIndexIsVisible(i);
165
        }
166
    }
167
 
168
    public void selectPrevious() {
169
        int i = this.list.getSelectedIndex() - 1;
170
        if (i >= 0) {
171
            this.list.setSelectedIndex(i);
172
            this.list.ensureIndexIsVisible(i);
173
        } else {
174
            this.setVisible(false);
175
        }
176
    }
177
 
178
    public void selectPreviousPage() {
179
        int i = Math.max(0, this.list.getSelectedIndex() - MAXROW);
180
        this.list.setSelectedIndex(i);
181
        this.list.ensureIndexIsVisible(i);
182
    }
183
 
184
    final void validateSelection() {
185
        final Object sel = this.list.getSelectedValue();
186
        // if no selection, don't change the combo
187
        if (sel != null) {
188
            if (sel instanceof ISearchableComboItem) {
25 ilm 189
                // don't call setValue() with sel.getOriginal() to handle list with the same item
190
                // multiple times.
191
                this.getCombo().setValue((ISearchableComboItem<T>) sel);
17 ilm 192
            } else if (sel instanceof Action) {
193
                ((Action) sel).actionPerformed(new ActionEvent(this.getCombo(), ActionEvent.ACTION_PERFORMED, this.getCombo().getName()));
194
            } else
195
                throw new IllegalStateException("unknown selection: " + sel);
196
            this.setVisible(false);
197
        }
198
    }
199
 
200
    public void open() {
25 ilm 201
        // JList always displays visibleRowCount even when fewer items exists
202
        // so if we put a high number we get a big blank popup
203
        // handle this in open() and not with a SimpleListDataListener since :
204
        // 1. open() is called only once whereas add is called multiple times in
205
        // ISearchableCombo.setMatchingCompletions().
206
        // 2. open() is always called when the list is modified.
207
        final int size = getListModel().getSize();
208
        // rowCount == 0 looks like a bug so show 3 empty rows
209
        final int rowCount = size == 0 ? 3 : Math.min(size, 30);
210
        if (this.list.getVisibleRowCount() != rowCount) {
211
            // checking if rowCount changes doesn't work (one reason is probably that we're
212
            // called before Swing and so setVisible displays an empty list)
213
            this.list.setVisibleRowCount(rowCount);
214
            if (this.isShowing()) {
215
                // since "visible row count" is not dynamic
216
                setVisible(false);
217
            }
218
        }
17 ilm 219
        // si on est pas déjà affiché
220
        // afficher même qd pas d'items : si l'user clique il faut qu'il voit la liste même vide
221
        if (!this.isShowing())
222
            this.show(this.getCombo(), 0, this.getCombo().getBounds().height);
223
        this.list.setSelectedValue(this.getCombo().getSelection(), true);
224
    }
225
 
226
    public void close() {
227
        this.setVisible(false);
228
    }
229
 
230
}