OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 150 | Go to most recent revision | Details | 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";
32
    public final static Color WHITE_TRANSPARENCY_COLOR = new Color(255, 255, 255, 180);
33
    public static final Color LIGHT_BLUE = new Color(202, 212, 220);
34
    public final static Color LIGHT = new Color(222, 222, 222);
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) {
394
        final int c = (this.getWidth() - HOURS_LABEL_WIDTH) / getColumnCount();
395
 
396
        return c;
397
    }
398
 
399
    public int getColumnX(int column) {
400
        int x = HOURS_LABEL_WIDTH;
401
        for (int i = 0; i < column; i++) {
402
            x += getColumnWidth(i);
403
 
404
        }
405
        return x;
406
    }
407
 
408
    public void paintHeader(Graphics g) {
409
 
410
        g.setColor(Color.WHITE);
411
        g.fillRect(0, 0, this.getWidth() + 100, getHeaderHeight());
412
        g.setColor(Color.GRAY);
413
        // Vertical lines
414
        int columnCount = getColumnCount();
415
        int x = HOURS_LABEL_WIDTH;
416
 
417
        for (int i = 0; i < columnCount - 1; i++) {
418
            x += getColumnWidth(i);
419
            g.drawLine(x, YEAR_HEIGHT, x, this.getHeaderHeight());
420
        }
421
        g.setColor(Color.DARK_GRAY);
422
        // Text : month & year
423
        Graphics2D g2 = (Graphics2D) g;
424
        g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
425
        g.setFont(getFont().deriveFont(13f));
426
 
427
        String strTitle = getTitle();
428
        Rectangle r = g.getFontMetrics().getStringBounds(strTitle, g).getBounds();
429
        int w = 0;
430
        for (int i = 0; i < getColumnCount(); i++) {
431
            w += getColumnWidth(i);
432
        }
433
        g.drawString(strTitle, (int) (HOURS_LABEL_WIDTH + (w - r.getWidth()) / 2), 15);
434
 
435
        // Text : day
436
        final Font font1 = getFont().deriveFont(12f);
437
        g.setFont(font1);
438
        x = HOURS_LABEL_WIDTH;
439
        Rectangle clipRect = g.getClipBounds();
440
        for (int i = 0; i < columnCount; i++) {
441
            String str = getColumnTitle(i);
442
            r = g.getFontMetrics().getStringBounds(str, g).getBounds();
443
            int columnWidth = getColumnWidth(i);
444
            g.setClip(x, YEAR_HEIGHT, columnWidth - 1, YEAR_HEIGHT + 20);
445
            // Centering
446
            int x2 = (int) (x + (columnWidth - r.getWidth()) / 2);
447
            // If no room, left align
448
            if (x2 < x + 2) {
449
                x2 = x + 2;
450
            }
451
            g.drawString(str, x2, YEAR_HEIGHT + 15);
452
            x += columnWidth;
453
 
454
        }
455
        g.setClip(clipRect);
456
        paintHeaderAlldays(g);
457
    }
458
 
459
    public String getTitle() {
460
        return "Title";
461
    }
462
 
463
    public void paintHeaderAlldays(Graphics g) {
464
        // Draw alldays
465
        // for (AllDayItem jCalendarItem : this.allDayItems) {
466
        // int w = 0;
467
        // for (int i = jCalendarItem.getX(); i < jCalendarItem.getX() + jCalendarItem.getW(); i++)
468
        // {
469
        // w += getColumnWidth(i);
470
        // }
471
        // int x2 = getColumnX(jCalendarItem.getX());
472
        // jCalendarItem.draw(g2, x2, YEAR_HEIGHT + TITLE_HEIGHT, allDayItemsRowHeight, w);
473
        // }
474
    }
475
 
476
    public int getHeaderHeight() {
477
        return YEAR_HEIGHT + TITLE_HEIGHT + this.allDayItemsRowCount * this.allDayItemsRowHeight + 4;
478
    }
479
 
480
    @Override
481
    public Dimension getPreferredScrollableViewportSize() {
482
        return new Dimension(1024, 768);
483
    }
484
 
485
    @Override
486
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
487
        return orientation * this.rowHeight * 2;
488
    }
489
 
490
    @Override
491
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
492
        return this.rowHeight;
493
    }
494
 
495
    @Override
496
    public boolean getScrollableTracksViewportWidth() {
497
        return true;
498
    }
499
 
500
    @Override
501
    public boolean getScrollableTracksViewportHeight() {
502
        return false;
503
    }
504
 
505
    public void mouseWheelMoved(MouseWheelEvent e, MouseWheelListener[] l) {
506
 
507
        if (e.getModifiers() == InputEvent.CTRL_MASK) {
508
 
509
            if (e.getWheelRotation() < 0) {
510
                zoom(22);
511
            } else {
512
                zoom(-22);
513
            }
514
 
515
            e.consume();
516
        } else {
517
            for (int i = 0; i < l.length; i++) {
518
                MouseWheelListener mouseWheelListener = l[i];
519
                mouseWheelListener.mouseWheelMoved(e);
520
            }
521
        }
522
 
523
    }
524
 
525
    public void setZoom(int zIndex) {
526
        if (zIndex >= 0) {
527
            int rh = 22 + zIndex * 22;
528
 
529
            this.rowHeight = rh;
530
            // update size
531
            final int w = this.getSize().width;
532
            final int h = getPreferredSize().height;
533
            setSize(new Dimension(w, h));
534
            validate();
535
            repaint();
536
        }
537
    }
538
 
539
    private void zoom(int v) {
540
        if (rowHeight < 200 && rowHeight > 60) {
541
            rowHeight += v;
542
            // update size
543
            final int w = this.getSize().width;
544
            final int h = getPreferredSize().height;
545
            setSize(new Dimension(w, h));
546
            validate();
547
            repaint();
548
        }
549
    }
550
 
551
    public int getRowHeight() {
552
        return this.rowHeight;
553
    }
554
 
555
    public void setPopupMenuProvider(JPopupMenuProvider popupProvider) {
556
        this.popupProvider = popupProvider;
557
    }
558
 
559
    public Set<ItemPartView> getSelectedItems() {
560
        return selectedItems;
561
    }
562
 
563
    public ItemPartView getItemPartAt(int x, int y) {
564
        ItemPartView newSelection = null;
565
        for (List<ItemPartView> l : itemParts) {
566
            for (ItemPartView p : l) {
567
                if (p.contains(x, y)) {
568
                    newSelection = p;
569
                    break;
570
                }
571
            }
572
        }
573
        return newSelection;
574
    }
575
 
576
    public void addItemPartHoverListener(ItemPartHoverListener l) {
577
        this.hListeners.add(l);
578
    }
579
 
580
    public void removeItemPartHoverListener(ItemPartHoverListener l) {
581
        this.hListeners.remove(l);
582
    }
583
 
584
    abstract public String getColumnTitle(int index);
585
 
586
    abstract public int getColumnCount();
587
 
588
    abstract public void reload();
589
 
590
    public void deselectAll() {
591
        for (ItemPartView p : selectedItems) {
592
            p.setSelected(false);
593
        }
594
        selectedItems.clear();
595
 
596
    }
597
}