OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 149 | 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.touch;
15
 
16
import java.awt.Color;
17
import java.awt.Font;
18
import java.awt.Graphics;
19
import java.awt.Graphics2D;
20
import java.awt.GridLayout;
21
import java.awt.RenderingHints;
22
import java.awt.event.ActionEvent;
23
import java.awt.event.ActionListener;
24
import java.awt.event.MouseEvent;
25
import java.awt.event.MouseListener;
26
import java.awt.event.MouseMotionListener;
27
import java.awt.event.MouseWheelEvent;
28
import java.awt.event.MouseWheelListener;
29
import java.util.ArrayList;
30
import java.util.List;
31
 
32
import javax.swing.DefaultListModel;
33
import javax.swing.JButton;
34
import javax.swing.JComponent;
35
import javax.swing.JFrame;
36
import javax.swing.JPanel;
37
import javax.swing.ListModel;
38
import javax.swing.SwingUtilities;
39
import javax.swing.event.ListDataEvent;
40
import javax.swing.event.ListDataListener;
41
import javax.swing.event.ListSelectionEvent;
42
import javax.swing.event.ListSelectionListener;
43
 
44
public class ScrollableList extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener, ListDataListener {
45
    /**
149 ilm 46
     *
47
     */
17 ilm 48
    private static final long serialVersionUID = 8176007217030710406L;
49
 
50
    private ListModel model;
51
 
52
    int offsetY = 0;// >0 si list remontée
53
 
54
    private int cellHeight;
55
 
56
    private int mousePressedY;
57
    private int selectedIndex = -1;
58
 
59
    private ScrollAnimator a;
60
 
61
    private int lastMouseY;
62
 
63
    private boolean hasScrolled;
64
 
174 ilm 65
    private List<ListSelectionListener> selectionListeners = new ArrayList<>();
66
    private List<ActionListener> actionListeners = new ArrayList<>();
17 ilm 67
 
68
    public ScrollableList(ListModel model) {
69
        a = new ScrollAnimator(this);
70
        this.model = model;
71
        cellHeight = 60;
72
        this.addMouseListener(this);
73
        this.addMouseMotionListener(this);
74
        this.addMouseWheelListener(this);
75
        model.addListDataListener(this);
76
 
77
    }
78
 
79
    @Override
80
    public void paint(Graphics g) {
81
        if (isOpaque()) {
82
            g.setColor(Color.WHITE);
83
            g.fillRect(0, 0, this.getWidth(), this.getHeight());
84
        }
85
        int minIndex = getMinVisibleIndex();
86
 
87
        int maxIndex = getMaxVisibleIndex();
88
        int drawY = getYFromIndex(minIndex);
89
        for (int i = minIndex; i <= maxIndex; i++) {
90
            paintCell(g, model.getElementAt(i), i, (i == selectedIndex), drawY);
91
            drawY += cellHeight;
92
        }
93
 
94
    }
95
 
96
    public int getMinVisibleIndex() {
97
        int minIndex = getIndexFromY(0);
98
        if (minIndex < 0) {
99
            minIndex = 0;
100
        }
101
        return minIndex;
102
    }
103
 
104
    public int getMinFullyVisibleIndex() {
105
        int minIndex = getIndexFromY(0);
106
        if (minIndex < 0) {
107
            minIndex = 0;
108
        }
109
 
110
        if ((minIndex) * cellHeight - offsetY < 0) {
111
            minIndex++;
112
        }
113
        return minIndex;
114
    }
115
 
116
    public int getMaxVisibleIndex() {
117
        int maxIndex = getIndexFromY(this.getHeight());
118
        if (maxIndex >= model.getSize()) {
119
            maxIndex = model.getSize() - 1;
120
        }
121
        return maxIndex;
122
    }
123
 
124
    public int getMaxFullyVisibleIndex() {
125
        int maxIndex = getIndexFromY(this.getHeight());
126
        if (maxIndex >= model.getSize()) {
127
            maxIndex = model.getSize() - 1;
128
        }
129
        if (this.getHeight() % cellHeight > 0) {
130
            maxIndex--;
131
        }
132
        return maxIndex;
133
    }
134
 
135
    public void paintCell(Graphics g, Object object, int index, boolean selected, int posY) {
136
 
137
        g.setColor(Color.WHITE);
138
 
139
        g.fillRect(0, posY, this.getWidth(), this.cellHeight);
140
        g.setColor(Color.GRAY);
141
 
142
        g.drawLine(0, posY + this.cellHeight - 1, this.getWidth(), posY + this.cellHeight - 1);
143
 
144
        if (selected) {
145
            g.setColor(Color.BLACK);
146
        } else {
147
            g.setColor(Color.GRAY);
148
        }
149
        ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
83 ilm 150
 
151
        g.setFont(new Font("Arial", Font.BOLD, 38));
152
        g.drawString(object.toString(), 14, posY + 43);
17 ilm 153
    }
154
 
93 ilm 155
    public int getIndexFromY(int y) {
17 ilm 156
        return (offsetY + y) / cellHeight;
157
    }
158
 
93 ilm 159
    public int getYFromIndex(int index) {
17 ilm 160
        return (index * cellHeight) - offsetY;
161
    }
162
 
163
    public void setFixedCellHeight(int h) {
164
        this.cellHeight = h;
165
    }
166
 
167
    public int getCellHeight() {
168
        return cellHeight;
169
    }
170
 
171
    public void setOffsetY(int y) {
172
        if (offsetY != y) {
173
            this.offsetY = y;
174
            repaint();
175
        }
176
    }
177
 
178
    @Override
179
    public void mouseClicked(MouseEvent e) {
180
    }
181
 
182
    @Override
183
    public void mouseEntered(MouseEvent e) {
184
    }
185
 
186
    @Override
187
    public void mouseExited(MouseEvent e) {
188
    }
189
 
190
    @Override
191
    public void mousePressed(MouseEvent e) {
192
        this.mousePressedY = e.getY();
193
        this.lastMouseY = mousePressedY;
194
        hasScrolled = false;
195
    }
196
 
197
    @Override
198
    public void mouseReleased(MouseEvent e) {
199
        long targetOffset = cellHeight * Math.round((((double) this.offsetY)) / this.cellHeight);
200
 
201
        if (!hasScrolled) {
202
            setSelectedIndex(getIndexFromY(e.getY()));
203
        } else {
204
            scrollToOffset(targetOffset);
205
        }
174 ilm 206
        fireActionPerformed();
17 ilm 207
    }
208
 
209
    public void setSelectedIndex(int index) {
210
        setSelectedIndex(index, true);
211
    }
212
 
213
    public void setSelectedIndex(int index, boolean scroll) {
214
        if (index != selectedIndex) {
215
            this.selectedIndex = index;
216
            if (scroll) {
217
                scrollToSelectedIndex();
218
            }
219
            fireSelectionChanged();
220
        }
221
        repaint();
222
    }
223
 
224
    public Object getSelectedValue() {
225
        if (selectedIndex < 0 || selectedIndex > this.model.getSize() - 1) {
226
            return null;
227
        }
228
        return model.getElementAt(selectedIndex);
229
    }
230
 
231
    @Override
232
    public void mouseDragged(MouseEvent e) {
233
        // Pas besoin de scroller, il y a plus de place que de lignes
234
        if (model.getSize() <= (int) this.getHeight() / cellHeight) {
235
            return;
236
        }
237
        int dy = e.getY() - this.lastMouseY;
238
        this.setOffsetY(this.offsetY - dy);
239
        this.lastMouseY = e.getY();
240
        if (Math.abs(lastMouseY - this.mousePressedY) > 10) {
241
            hasScrolled = true;
242
        }
243
    }
244
 
245
    @Override
246
    public void mouseMoved(MouseEvent e) {
247
    }
248
 
249
    @Override
250
    public void mouseWheelMoved(MouseWheelEvent e) {
251
        long targetOffset = cellHeight * Math.round((((double) this.offsetY)) / this.cellHeight);
252
        targetOffset += e.getWheelRotation() * cellHeight * 2;
253
 
254
        scrollToOffset(targetOffset);
255
 
256
    }
257
 
258
    public void scrollToOffset(long targetOffset) {
259
 
260
        int max = (model.getSize()) * cellHeight - this.getHeight();
261
        if (targetOffset > max) {
262
            targetOffset = max;
263
        }
264
        if (targetOffset < 0) {
265
            targetOffset = 0;
266
        }
267
 
268
        a.stop();
269
        a.setStart(this.offsetY);
270
        a.setStop(targetOffset);
271
        AnimatorManager.getInstance().start(a);
272
    }
273
 
274
    public void scrollToSelectedIndex() {
275
        if (selectedIndex < 0 || selectedIndex > this.model.getSize() - 1) {
276
            return;
277
        }
278
        if (this.selectedIndex < getMinFullyVisibleIndex()) {
279
            scrollToOffset(this.selectedIndex * cellHeight);
280
        } else if (this.selectedIndex > getMaxFullyVisibleIndex()) {
281
            int targetOffset = (this.selectedIndex + 1) * cellHeight - this.getHeight();
282
 
283
            scrollToOffset(targetOffset);
284
        } else {
285
            if (offsetY < 0) {
286
                scrollToOffset(0);
287
            }
288
 
289
        }
290
    }
291
 
93 ilm 292
    public void setCellHeight(int cellHeight) {
293
        this.cellHeight = cellHeight;
294
        repaint();
295
    }
296
 
17 ilm 297
    public static void main(String[] args) {
298
        SwingUtilities.invokeLater(new Runnable() {
299
 
300
            @Override
301
            public void run() {
302
                DefaultListModel model1 = new DefaultListModel();
303
                for (int i = 0; i < 20; i++) {
304
                    model1.addElement("Item" + i);
305
                }
306
 
307
                DefaultListModel model2 = new DefaultListModel();
308
                for (int i = 0; i < 3; i++) {
309
                    model2.addElement("Item" + i);
310
                }
311
 
312
                final ScrollableList l1 = new ScrollableList(model1);
313
                final ScrollableList l2 = new ScrollableList(model2);
314
 
315
                JFrame f = new JFrame();
316
                JPanel p = new JPanel();
317
                p.setLayout(new GridLayout(1, 3));
318
                p.add(l1);
319
 
320
                JButton comp = new JButton("ScrollToSelected");
321
                p.add(comp);
322
                p.add(l2);
323
                f.setContentPane(p);
324
                f.setSize(600, 400);
325
                f.setLocation(10, 80);
326
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
327
                f.setVisible(true);
328
                comp.addActionListener(new ActionListener() {
329
                    @Override
330
                    public void actionPerformed(ActionEvent e) {
331
                        l1.scrollToSelectedIndex();
332
                        l2.scrollToSelectedIndex();
333
                    }
334
                });
335
            }
336
        });
337
 
338
    }
339
 
340
    public void clearSelection() {
341
        if (this.selectedIndex != -1) {
342
            this.selectedIndex = -1;
343
            fireSelectionChanged();
344
            repaint();
345
        }
346
    }
347
 
149 ilm 348
    /**
349
     * Select a value
350
     *
351
     * @return true if the object is not null and was found in the model
352
     */
353
    public boolean setSelectedValue(Object obj, boolean scroll) {
354
        if (obj == null) {
17 ilm 355
            clearSelection();
149 ilm 356
            return true;
17 ilm 357
        }
358
 
359
        int size = model.getSize();
360
        for (int i = 0; i < size; i++) {
361
            Object o = model.getElementAt(i);
149 ilm 362
            if (o.equals(obj)) {
17 ilm 363
                this.setSelectedIndex(i);
149 ilm 364
                return true;
17 ilm 365
            }
366
        }
149 ilm 367
        return false;
17 ilm 368
    }
369
 
370
    public void addListSelectionListener(ListSelectionListener selectionListener) {
371
        this.selectionListeners.add(selectionListener);
372
    }
373
 
374
    void fireSelectionChanged() {
375
        int size = this.selectionListeners.size();
376
        for (int i = 0; i < size; i++) {
377
            this.selectionListeners.get(i).valueChanged(new ListSelectionEvent(this, this.selectedIndex, this.selectedIndex, false));
378
        }
379
    }
380
 
174 ilm 381
    public void addActionListener(ActionListener listener) {
382
        this.actionListeners.add(listener);
383
    }
384
 
385
    private void fireActionPerformed() {
386
        int size = this.actionListeners.size();
387
        for (int i = 0; i < size; i++) {
388
            this.actionListeners.get(i).actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "click"));
389
        }
390
 
391
    }
392
 
17 ilm 393
    @Override
394
    public void contentsChanged(ListDataEvent e) {
395
        this.offsetY = 0;
396
        repaint();
397
    }
398
 
399
    @Override
400
    public void intervalAdded(ListDataEvent e) {
401
        contentsChanged(e);
402
    }
403
 
404
    @Override
405
    public void intervalRemoved(ListDataEvent e) {
406
        contentsChanged(e);
407
    }
408
 
409
}