OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 150 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
137 ilm 1
package org.jopencalendar.ui;
2
 
3
import java.awt.BasicStroke;
4
import java.awt.Color;
5
import java.awt.Container;
6
import java.awt.Dimension;
7
import java.awt.Font;
8
import java.awt.Graphics;
9
import java.awt.Graphics2D;
10
import java.awt.Rectangle;
11
import java.awt.RenderingHints;
12
import java.awt.event.InputEvent;
13
import java.awt.event.MouseAdapter;
14
import java.awt.event.MouseEvent;
15
import java.awt.event.MouseWheelEvent;
16
import java.awt.event.MouseWheelListener;
17
import java.util.ArrayList;
18
import java.util.HashSet;
19
import java.util.List;
20
import java.util.Set;
21
 
22
import javax.swing.JPanel;
23
import javax.swing.JPopupMenu;
24
import javax.swing.Scrollable;
25
import javax.swing.SwingUtilities;
26
 
27
import org.jopencalendar.model.JCalendarItem;
28
import org.jopencalendar.model.JCalendarItemPart;
29
 
30
public abstract class MultipleDayView extends JPanel implements Scrollable {
31
    public static final String CALENDARD_ITEMS_PROPERTY = "calendard_items_changed";
168 ilm 32
    public static final Color WHITE_TRANSPARENCY_COLOR = new Color(255, 255, 255, 180);
137 ilm 33
    public static final Color LIGHT_BLUE = new Color(202, 212, 220);
168 ilm 34
    public static final Color LIGHT = new Color(222, 222, 222);
137 ilm 35
    public static final int TITLE_HEIGHT = 20;
36
    public static final int HOURS_LABEL_WIDTH = 50;
37
 
38
    protected static final int YEAR_HEIGHT = 20;
39
    private int deltaY = 10;
40
    private int rowHeight = 44;
41
 
42
    private List<List<AllDayItem>> allDayItems = new ArrayList<List<AllDayItem>>();
43
    private List<List<ItemPartView>> itemParts = new ArrayList<List<ItemPartView>>();
44
 
45
    private int allDayItemsRowCount;
46
    public int allDayItemsRowHeight = 20;
47
 
48
    private Set<ItemPartView> selectedItems = new HashSet<ItemPartView>();
49
    // Right click menu
50
    private JPopupMenuProvider popupProvider;
51
 
52
    protected List<ItemPartHoverListener> hListeners = new ArrayList<ItemPartHoverListener>();
53
    private int startHour = 0;
54
    private int endHour = 24;
55
 
56
    public MultipleDayView() {
57
 
58
        this.addMouseListener(new MouseAdapter() {
59
            @Override
60
            public void mousePressed(MouseEvent e) {
61
                ItemPartView newSelection = getItemPartAt(e.getX(), e.getY());
62
                if (newSelection != null) {
63
                    boolean isCurrentlySelected = newSelection.isSelected();
64
                    // Click on a non selected item
65
                    boolean ctrlPressed = ((e.getModifiers() & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK);
66
                    if (!ctrlPressed) {
67
                        deselectAll();
68
                    }
69
                    if (e.getButton() == MouseEvent.BUTTON1) {
70
                        if (isCurrentlySelected) {
71
                            // Click on a selected item
72
                            newSelection.setSelected(false);
73
                            selectedItems.remove(newSelection);
74
                        } else {
75
                            newSelection.setSelected(true);
76
                            selectedItems.add(newSelection);
77
                        }
78
                    } else {
79
                        newSelection.setSelected(true);
80
                        selectedItems.add(newSelection);
81
                    }
82
                    repaint();
83
                }
84
                // Deselect all if nothing is selected
85
                if (newSelection == null && !selectedItems.isEmpty()) {
86
                    deselectAll();
87
                    repaint();
88
                }
89
                // For mac
90
                showPopup(e);
91
            }
92
 
93
            @Override
94
            public void mouseReleased(MouseEvent e) {
95
                showPopup(e);
96
            }
97
 
98
            private void showPopup(MouseEvent e) {
99
                if (e.isPopupTrigger()) {
100
                    ItemPartView newSelection = getItemPartAt(e.getX(), e.getY());
101
                    if (newSelection != null && !newSelection.isSelected()) {
102
                        newSelection.setSelected(true);
103
                        selectedItems.add(newSelection);
104
                        repaint();
105
                    }
106
 
107
                    if (popupProvider != null) {
108
                        // Selected parts
109
                        final List<JCalendarItemPart> sParts = new ArrayList<JCalendarItemPart>();
110
                        for (ItemPartView p : selectedItems) {
111
                            sParts.add(p.getPart());
112
                        }
113
                        // Current column parts
114
                        final List<JCalendarItemPart> currentColumnParts = new ArrayList<JCalendarItemPart>();
115
                        int columnIndex = getColumnIndex(e.getX());
116
                        List<ItemPartView> l = MultipleDayView.this.itemParts.get(columnIndex);
117
                        for (ItemPartView p : l) {
118
                            currentColumnParts.add(p.getPart());
119
                        }
120
 
121
                        JPopupMenu popup = popupProvider.getPopup(sParts, currentColumnParts);
122
                        if (popup != null) {
123
                            popup.show(MultipleDayView.this, e.getX(), e.getY());
124
                        }
125
                    }
126
                }
127
            }
128
        });
129
        this.addMouseMotionListener(new MouseAdapter() {
130
            ItemPartView previous;
131
 
132
            @Override
133
            public void mouseMoved(MouseEvent e) {
134
                if (hListeners.isEmpty()) {
135
                    return;
136
                }
137
                ItemPartView newSelection = getItemPartAt(e.getX(), e.getY());
138
 
139
                if (newSelection != previous) {
140
 
141
                    for (ItemPartHoverListener l : hListeners) {
142
                        if (newSelection == null) {
143
                            l.mouseOn(null);
144
                        } else {
145
                            l.mouseOn(newSelection.getPart());
146
                        }
147
                    }
148
                    previous = newSelection;
149
                }
150
 
151
            }
152
 
153
        });
154
 
155
    }
156
 
157
    protected int getColumnIndex(int currentX) {
158
        int x = HOURS_LABEL_WIDTH;
159
        int columnCount = getColumnCount();
160
        for (int i = 0; i < columnCount - 1; i++) {
161
            x += getColumnWidth(i);
162
            if (currentX < x) {
163
                return i;
164
            }
165
        }
166
        return 0;
167
    }
168
 
169
    public void setHourRange(int start, int end) {
170
        if (start < 0 || start > 24) {
171
            throw new IllegalArgumentException("Bad start hour : " + start);
172
        }
173
        if (end < 0 || end > 24) {
174
            throw new IllegalArgumentException("Bad end hour : " + end);
175
        }
176
        if (end - start < 1) {
177
            throw new IllegalArgumentException("Bad end hour must be > (start + 1)");
178
        }
179
        this.startHour = start;
180
        this.endHour = end;
181
        repaint();
182
    }
183
 
184
    protected void setItems(List<List<JCalendarItem>> list) {
185
        assert SwingUtilities.isEventDispatchThread();
186
        this.itemParts.clear();
187
        this.allDayItems.clear();
188
        for (List<JCalendarItem> l : list) {
189
            final List<AllDayItem> aDayItems = new ArrayList<AllDayItem>();
190
            final List<ItemPartView> aItems = new ArrayList<ItemPartView>();
191
            for (JCalendarItem jCalendarItem : l) {
192
                if (jCalendarItem.getDtStart().before(jCalendarItem.getDtEnd())) {
193
                    if (jCalendarItem.isDayOnly()) {
194
                        aDayItems.add(new AllDayItem(jCalendarItem));
195
                    } else {
196
                        aItems.addAll(ItemPartView.split(jCalendarItem));
197
                    }
198
                } else {
199
                    System.err.println(
200
                            "MultipleDayView.setItems() " + jCalendarItem.getSummary() + " start :" + jCalendarItem.getDtStart().getTime() + " after " + jCalendarItem.getDtEnd().getTime() + " !");
201
                }
202
            }
203
            this.allDayItems.add(aDayItems);
204
            this.itemParts.add(aItems);
205
        }
206
 
207
        layoutAllDayItems();
208
        layoutItemParts();
209
        // Tweak to force header to be relayouted and repainted
210
        final Container parent = getParent().getParent();
211
        final int w = parent.getSize().width;
212
        final int h = parent.getSize().height;
213
        parent.setSize(w + 1, h);
214
        parent.validate();
215
        parent.setSize(w, h);
216
        parent.validate();
217
        firePropertyChange(CALENDARD_ITEMS_PROPERTY, null, list);
218
    }
219
 
220
    private void layoutItemParts() {
221
        ItemPartViewLayouter layouter = new ItemPartViewLayouter();
222
        for (List<ItemPartView> items : itemParts) {
223
            layouter.layout(items);
224
        }
225
 
226
    }
227
 
228
    private void layoutAllDayItems() {
229
        // final Calendar c = getFirstDayCalendar();
230
        // Collections.sort(this.allDayItems, new Comparator<AllDayItem>() {
231
        //
232
        // @Override
233
        // public int compare(AllDayItem o1, AllDayItem o2) {
234
        // return o1.getLengthFrom(c) - o2.getLengthFrom(c);
235
        // }
236
        // });
237
        // final int size = this.allDayItems.size();
238
        // for (int i = 0; i < size; i++) {
239
        // AllDayItem jCalendarItem = this.allDayItems.get(i);
240
        // // set X and W
241
        // Calendar start = jCalendarItem.getItem().getDtStart();
242
        // Calendar end = jCalendarItem.getItem().getDtEnd();
243
        // if (start.before(getFirstDayCalendar())) {
244
        // start = getFirstDayCalendar();
245
        // } else {
246
        // jCalendarItem.setLeftBorder(true);
247
        // }
248
        //
249
        // if (end.after(getAfterLastDayCalendar())) {
250
        // end = getAfterLastDayCalendar();
251
        // end.add(Calendar.MILLISECOND, -1);
252
        // } else {
253
        // jCalendarItem.setRightBorder(true);
254
        // }
255
        // int x = (int) (start.getTimeInMillis() - getFirstDayCalendar().getTimeInMillis());
256
        // x = x / MILLIS_PER_DAY;
257
        // jCalendarItem.setX(x);
258
        // int l = ((int) (end.getTimeInMillis() - start.getTimeInMillis())) / MILLIS_PER_DAY + 1;
259
        // jCalendarItem.setW(l);
260
        //
261
        // // Y and H
262
        // jCalendarItem.setY(0);
263
        // jCalendarItem.setH(1);
264
        // for (int j = 0; j < i; j++) {
265
        // AllDayItem layoutedItem = this.allDayItems.get(j);
266
        // if (layoutedItem.conflictWith(jCalendarItem)) {
267
        // jCalendarItem.setY(layoutedItem.getY() + 1);
268
        // }
269
        // }
270
        // }
271
        // allDayItemsRowCount = 0;
272
        // for (int i = 0; i < size; i++) {
273
        // AllDayItem jCalendarItem = this.allDayItems.get(i);
274
        // if (jCalendarItem.getY() + jCalendarItem.getH() > allDayItemsRowCount) {
275
        // allDayItemsRowCount = jCalendarItem.getY() + jCalendarItem.getH();
276
        // }
277
        // }
278
    }
279
 
280
    @Override
281
    public Dimension getPreferredSize() {
282
        return new Dimension(800, TITLE_HEIGHT + (this.endHour - this.startHour) * rowHeight);
283
    }
284
 
285
    @Override
286
    protected void paintComponent(Graphics g) {
287
        Graphics2D g2 = (Graphics2D) g;
288
        float[] dash4 = { 8f, 4f };
289
 
290
        BasicStroke bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash4, 1f);
291
 
292
        final BasicStroke s = new BasicStroke(1f);
293
        g.setColor(Color.WHITE);
294
        g.fillRect(0, 0, this.getWidth(), this.getHeight());
295
        int columnCount = getColumnCount();
296
        if (columnCount < 1)
297
            return;
298
 
299
        g2.setStroke(s);
300
        // H lines : Hours
301
        int y = deltaY;
302
        int hourPaintedCount = this.endHour - this.startHour;
303
        for (int i = 0; i < hourPaintedCount; i++) {
304
            g.setColor(Color.LIGHT_GRAY);
305
            g.drawLine(HOURS_LABEL_WIDTH, y, this.getWidth(), y);
306
            y += rowHeight;
307
        }
308
        // H lines : 1/2 Hours
309
        y = deltaY + rowHeight / 2;
310
        if (rowHeight > 44) {
311
            g2.setStroke(s);
312
        } else {
313
            g2.setStroke(bs4);
314
        }
315
        g.setColor(LIGHT);
316
        for (int i = 0; i < hourPaintedCount; i++) {
317
            g.drawLine(HOURS_LABEL_WIDTH, y, this.getWidth(), y);
318
            y += rowHeight;
319
        }
320
 
321
        if (rowHeight > 44) {
322
            g2.setStroke(bs4);
323
            y = deltaY + rowHeight / 4;
324
            for (int i = 0; i < hourPaintedCount * 2; i++) {
325
                g.drawLine(HOURS_LABEL_WIDTH, y, this.getWidth(), y);
326
                y += rowHeight / 2;
327
            }
328
        }
329
 
330
        // Horizontal lines : 5 minutes
331
        g.setColor(Color.LIGHT_GRAY);
332
        if (rowHeight > 120) {
333
            g2.setStroke(s);
334
            final float h5 = rowHeight / 12f;
335
 
336
            // int w = this.getWidth();
337
            int x = HOURS_LABEL_WIDTH;
338
            for (int j = 0; j < columnCount - 1; j++) {
339
                x += getColumnWidth(j);
340
                y = deltaY + (int) h5;
341
                int x2 = x + 10;
342
                for (int i = 0; i < 24; i++) {
343
                    float y1 = y;
344
                    g.drawLine(x, (int) y1, x2, (int) y1);
345
                    y1 += h5;
346
                    g.drawLine(x, (int) y1, x2, (int) y1);
347
                    y1 += 2 * h5;
348
                    g.drawLine(x, (int) y1, x2, (int) y1);
349
                    y1 += h5;
350
                    g.drawLine(x, (int) y1, x2, (int) y1);
351
                    y += rowHeight / 2;
352
                }
353
            }
354
        }
355
 
356
        // Hours : text on left
357
        g2.setStroke(s);
358
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
359
        g.setFont(getFont().deriveFont(14f));
360
        y = deltaY + 6;
361
        g.setColor(Color.GRAY);
362
        for (int i = this.startHour; i < this.endHour; i++) {
363
            g.drawString(getHourLabel(i), 5, y);
364
            y += rowHeight;
365
        }
366
        // Vertical lines
367
        int x = HOURS_LABEL_WIDTH;
368
 
369
        for (int i = 0; i < columnCount - 1; i++) {
370
            x += getColumnWidth(i);
371
            g.drawLine(x, 0, x, this.getHeight());
372
        }
373
        int dY = deltaY - this.startHour * rowHeight;
374
        for (int i = 0; i < this.itemParts.size(); i++) {
375
            List<ItemPartView> items = this.itemParts.get(i);
376
            final int columnWidth = getColumnWidth(i);
377
            // Draw standard items
378
            for (ItemPartView jCalendarItem : items) {
379
                final int itemX = getColumnX(i);
380
                jCalendarItem.draw(g2, itemX, dY, rowHeight, columnWidth);
381
            }
382
        }
383
 
384
    }
385
 
386
    String getHourLabel(int i) {
387
        if (i < 10) {
388
            return "0" + i + ":00";
389
        }
390
        return String.valueOf(i) + ":00";
391
    }
392
 
393
    public int getColumnWidth(int column) {
168 ilm 394
        return (this.getWidth() - HOURS_LABEL_WIDTH) / getColumnCount();
137 ilm 395
    }
396
 
397
    public int getColumnX(int column) {
398
        int x = HOURS_LABEL_WIDTH;
399
        for (int i = 0; i < column; i++) {
400
            x += getColumnWidth(i);
401
 
402
        }
403
        return x;
404
    }
405
 
406
    public void paintHeader(Graphics g) {
407
 
408
        g.setColor(Color.WHITE);
409
        g.fillRect(0, 0, this.getWidth() + 100, getHeaderHeight());
410
        g.setColor(Color.GRAY);
411
        // Vertical lines
412
        int columnCount = getColumnCount();
413
        int x = HOURS_LABEL_WIDTH;
414
 
415
        for (int i = 0; i < columnCount - 1; i++) {
416
            x += getColumnWidth(i);
417
            g.drawLine(x, YEAR_HEIGHT, x, this.getHeaderHeight());
418
        }
419
        g.setColor(Color.DARK_GRAY);
420
        // Text : month & year
421
        Graphics2D g2 = (Graphics2D) g;
422
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
423
        g.setFont(getFont().deriveFont(13f));
424
 
425
        String strTitle = getTitle();
426
        Rectangle r = g.getFontMetrics().getStringBounds(strTitle, g).getBounds();
427
        int w = 0;
428
        for (int i = 0; i < getColumnCount(); i++) {
429
            w += getColumnWidth(i);
430
        }
431
        g.drawString(strTitle, (int) (HOURS_LABEL_WIDTH + (w - r.getWidth()) / 2), 15);
432
 
433
        // Text : day
434
        final Font font1 = getFont().deriveFont(12f);
435
        g.setFont(font1);
436
        x = HOURS_LABEL_WIDTH;
437
        Rectangle clipRect = g.getClipBounds();
438
        for (int i = 0; i < columnCount; i++) {
439
            String str = getColumnTitle(i);
440
            r = g.getFontMetrics().getStringBounds(str, g).getBounds();
441
            int columnWidth = getColumnWidth(i);
442
            g.setClip(x, YEAR_HEIGHT, columnWidth - 1, YEAR_HEIGHT + 20);
443
            // Centering
444
            int x2 = (int) (x + (columnWidth - r.getWidth()) / 2);
445
            // If no room, left align
446
            if (x2 < x + 2) {
447
                x2 = x + 2;
448
            }
449
            g.drawString(str, x2, YEAR_HEIGHT + 15);
450
            x += columnWidth;
451
 
452
        }
453
        g.setClip(clipRect);
454
        paintHeaderAlldays(g);
455
    }
456
 
457
    public String getTitle() {
458
        return "Title";
459
    }
460
 
461
    public void paintHeaderAlldays(Graphics g) {
462
        // Draw alldays
463
        // for (AllDayItem jCalendarItem : this.allDayItems) {
464
        // int w = 0;
465
        // for (int i = jCalendarItem.getX(); i < jCalendarItem.getX() + jCalendarItem.getW(); i++)
466
        // {
467
        // w += getColumnWidth(i);
468
        // }
469
        // int x2 = getColumnX(jCalendarItem.getX());
470
        // jCalendarItem.draw(g2, x2, YEAR_HEIGHT + TITLE_HEIGHT, allDayItemsRowHeight, w);
471
        // }
472
    }
473
 
474
    public int getHeaderHeight() {
475
        return YEAR_HEIGHT + TITLE_HEIGHT + this.allDayItemsRowCount * this.allDayItemsRowHeight + 4;
476
    }
477
 
478
    @Override
479
    public Dimension getPreferredScrollableViewportSize() {
480
        return new Dimension(1024, 768);
481
    }
482
 
483
    @Override
484
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
485
        return orientation * this.rowHeight * 2;
486
    }
487
 
488
    @Override
489
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
490
        return this.rowHeight;
491
    }
492
 
493
    @Override
494
    public boolean getScrollableTracksViewportWidth() {
495
        return true;
496
    }
497
 
498
    @Override
499
    public boolean getScrollableTracksViewportHeight() {
500
        return false;
501
    }
502
 
503
    public void mouseWheelMoved(MouseWheelEvent e, MouseWheelListener[] l) {
504
 
505
        if (e.getModifiers() == InputEvent.CTRL_MASK) {
506
 
507
            if (e.getWheelRotation() < 0) {
508
                zoom(22);
509
            } else {
510
                zoom(-22);
511
            }
512
 
513
            e.consume();
514
        } else {
515
            for (int i = 0; i < l.length; i++) {
516
                MouseWheelListener mouseWheelListener = l[i];
517
                mouseWheelListener.mouseWheelMoved(e);
518
            }
519
        }
520
 
521
    }
522
 
150 ilm 523
    public final void setZoom(int zIndex) {
137 ilm 524
        if (zIndex >= 0) {
150 ilm 525
            int rh = (1 + zIndex) * 22;
526
            setRowHeight(rh);
137 ilm 527
        }
528
    }
529
 
150 ilm 530
    public final int getZoom() {
531
        return (this.rowHeight / 22) - 1;
532
    }
533
 
137 ilm 534
    private void zoom(int v) {
150 ilm 535
        setRowHeight(this.rowHeight + v);
536
    }
537
 
538
    public final void setRowHeight(final int v) {
168 ilm 539
        if (this.rowHeight != v && v > 20 && v < 250) {
150 ilm 540
            this.rowHeight = v;
137 ilm 541
            // update size
542
            final int w = this.getSize().width;
543
            final int h = getPreferredSize().height;
544
            setSize(new Dimension(w, h));
545
            validate();
546
            repaint();
150 ilm 547
            this.firePropertyChange("rowHeight", null, v);
548
            this.firePropertyChange("zoom", null, getZoom());
137 ilm 549
        }
550
    }
551
 
150 ilm 552
    // how many pixels for one hour
553
    public final int getRowHeight() {
137 ilm 554
        return this.rowHeight;
555
    }
556
 
557
    public void setPopupMenuProvider(JPopupMenuProvider popupProvider) {
558
        this.popupProvider = popupProvider;
559
    }
560
 
561
    public Set<ItemPartView> getSelectedItems() {
562
        return selectedItems;
563
    }
564
 
565
    public ItemPartView getItemPartAt(int x, int y) {
566
        ItemPartView newSelection = null;
567
        for (List<ItemPartView> l : itemParts) {
568
            for (ItemPartView p : l) {
569
                if (p.contains(x, y)) {
570
                    newSelection = p;
571
                    break;
572
                }
573
            }
574
        }
575
        return newSelection;
576
    }
577
 
578
    public void addItemPartHoverListener(ItemPartHoverListener l) {
579
        this.hListeners.add(l);
580
    }
581
 
582
    public void removeItemPartHoverListener(ItemPartHoverListener l) {
583
        this.hListeners.remove(l);
584
    }
585
 
168 ilm 586
    public abstract String getColumnTitle(int index);
137 ilm 587
 
168 ilm 588
    public abstract int getColumnCount();
137 ilm 589
 
168 ilm 590
    public abstract void reload();
137 ilm 591
 
592
    public void deselectAll() {
593
        for (ItemPartView p : selectedItems) {
594
            p.setSelected(false);
595
        }
596
        selectedItems.clear();
597
 
598
    }
599
}