1
2
3
4
5
6
7
8
9
10
11 package de.jaret.util.ui.datechooser;
12
13 import java.util.Date;
14 import java.util.List;
15 import java.util.Vector;
16
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.events.KeyEvent;
19 import org.eclipse.swt.events.KeyListener;
20 import org.eclipse.swt.events.MouseEvent;
21 import org.eclipse.swt.events.MouseListener;
22 import org.eclipse.swt.events.PaintEvent;
23 import org.eclipse.swt.events.PaintListener;
24 import org.eclipse.swt.graphics.Color;
25 import org.eclipse.swt.graphics.GC;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.graphics.Rectangle;
28 import org.eclipse.swt.layout.GridData;
29 import org.eclipse.swt.layout.GridLayout;
30 import org.eclipse.swt.widgets.Canvas;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Display;
33 import org.eclipse.swt.widgets.Event;
34 import org.eclipse.swt.widgets.Listener;
35
36 import de.jaret.util.date.JaretDate;
37 import de.jaret.util.swt.SwtGraphicsHelper;
38
39 /***
40 * The TimeChooserPanel is intended to be used as a dropdown for the TimeFieldCombo (@see
41 * de.jaret.swt.util.datechooser.TimeChooser). However if it seems useful it is possible to be used as a standalone
42 * control for selecting a time.
43 * <p>
44 * Time is represented as the time part of a java.util.Date.
45 * </p>
46 *
47 * @author Peter Kliem
48 * @version $Id: TimeChooserPanel.java 576 2007-10-03 12:57:38Z olk $
49 */
50 public class TimeChooserPanel extends Composite implements MouseListener {
51 /*** currently selected time in this date. */
52 protected Date _date;
53
54 /*** color for marking selected time in the panel. */
55 protected static final Color MARKERCOLOR = Display.getCurrent().getSystemColor(SWT.COLOR_BLUE);
56
57 /*** the time grid canvas. */
58 protected TimeGrid _timeGrid;
59
60 /*** listener list. */
61 protected List<IDateChooserListener> _listenerList;
62
63 /*** currennt col width in the chooser. */
64 protected int _columnWidth;
65 /*** current row heigth in the chooser. */
66 protected int _rowHeight;
67
68 /***
69 * Constructor for the TimeChooserPanel.
70 *
71 * @param parent Composite parent
72 * @param style style bits selection will need a double click.
73 */
74 public TimeChooserPanel(Composite parent, int style) {
75 super(parent, style);
76
77
78 createControls();
79
80
81 setDate(new Date());
82
83
84 addKeyListener(new KeyListener() {
85 public void keyPressed(KeyEvent event) {
86 handleKeyPressed(event);
87 }
88
89 public void keyReleased(KeyEvent arg0) {
90 }
91 });
92
93
94 Listener listener = new Listener() {
95 public void handleEvent(Event event) {
96 switch (event.type) {
97 case SWT.MouseWheel:
98 int count = -event.count / 3;
99 int colIdx = event.x / _columnWidth;
100 switch (colIdx) {
101 case 0:
102 rollAMPM(count);
103 break;
104 case 1:
105 rollHours(count);
106 break;
107 case 2:
108 rollMinutes(count);
109 break;
110
111 default:
112 break;
113 }
114 break;
115 default:
116 throw new RuntimeException("unsupported event");
117 }
118 }
119
120 };
121
122 addListener(SWT.MouseWheel, listener);
123
124 }
125
126 /***
127 * {@inheritDoc}
128 */
129 public void dispose() {
130 super.dispose();
131 }
132
133 /***
134 * {@inheritDoc} Propagate the change to the elements.
135 */
136 public void setBackground(Color color) {
137 super.setBackground(color);
138 _timeGrid.setBackground(color);
139 }
140
141 /***
142 * Handles the key events of the panel.
143 *
144 * @param event KeyEvent to be processed
145 */
146 private void handleKeyPressed(KeyEvent event) {
147 if ((event.stateMask & SWT.SHIFT) != 0) {
148 switch (event.keyCode) {
149 case SWT.ARROW_DOWN:
150 rollMinutes(1);
151 break;
152 case SWT.ARROW_UP:
153 rollMinutes(-1);
154 break;
155 default:
156
157 break;
158 }
159 } else {
160 switch (event.keyCode) {
161 case SWT.ESC:
162 fireChoosingCanceled();
163 break;
164 case SWT.CR:
165 fireDateChosen(getDate());
166 break;
167 case SWT.ARROW_DOWN:
168 rollHours(1);
169 break;
170 case SWT.ARROW_UP:
171 rollHours(-1);
172 break;
173 default:
174
175 break;
176 }
177
178 }
179 }
180
181 /***
182 * intermediate change: update monthlabel etc.
183 */
184 private void intermediateChange() {
185 redraw();
186 fireIntermediateChange(getDate());
187 forceFocus();
188 }
189
190 /***
191 * Set the currently selected date. A value of <code>null</code> will be transformed to the current date.
192 *
193 * @param date Date to be displayed
194 */
195 public void setDate(Date date) {
196
197 if (date == null) {
198 date = new Date();
199 }
200 if (!date.equals(_date)) {
201 _date = date;
202 redraw();
203 }
204 }
205
206 /***
207 * Get the selected Date.
208 *
209 * @return the currently selected date.
210 */
211 public Date getDate() {
212 return _date;
213 }
214
215 /***
216 * {@inheritDoc} Also redraws the grid.
217 */
218 public void redraw() {
219 super.redraw();
220 _timeGrid.redraw();
221 }
222
223 /***
224 * Create the controls that compose the timechooserpanel.
225 */
226 private void createControls() {
227 GridLayout gridLayout = new GridLayout();
228 gridLayout.numColumns = 1;
229 setLayout(gridLayout);
230
231
232 _timeGrid = new TimeGrid(this, SWT.NULL);
233 GridData gd = new GridData(GridData.FILL_BOTH);
234 _timeGrid.setLayoutData(gd);
235 _timeGrid.addMouseListener(this);
236 _timeGrid.setBackground(getBackground());
237 }
238
239 /***
240 * The TimeGrid is a private member class extending Canvas.
241 *
242 * @author Peter Kliem
243 * @version $Id: TimeChooserPanel.java 576 2007-10-03 12:57:38Z olk $
244 */
245 class TimeGrid extends Canvas {
246
247 /***
248 * Construct a time grid.
249 *
250 * @param parent parent composite
251 * @param style style bits
252 */
253 public TimeGrid(Composite parent, int style) {
254 super(parent, style);
255 addPaintListener(new PaintListener() {
256 public void paintControl(PaintEvent event) {
257 onPaint(event);
258 }
259
260 });
261 }
262
263 protected Point _sizeCache;
264
265 /***
266 * {@inheritDoc}
267 */
268 public Point computeSize(int arg0, int arg1, boolean arg2) {
269 if (_sizeCache == null) {
270 GC gc = new GC(this);
271 Point extent = gc.stringExtent(">=12");
272 gc.dispose();
273 _sizeCache = new Point(extent.x * 3, extent.y * 12);
274 }
275
276 return _sizeCache;
277 }
278
279 /***
280 * Do any calculations necessary to support drawing.
281 */
282 public void updateInternals() {
283 _columnWidth = getClientArea().width / 3;
284 _rowHeight = getClientArea().height / 12;
285 }
286
287 /***
288 * The paint method.
289 *
290 * @param event the pain tevent
291 */
292 private void onPaint(PaintEvent event) {
293
294 updateInternals();
295
296 GC gc = event.gc;
297
298 drawMarks(gc);
299
300 for (int i = 0; i < 12; i++) {
301 int y = i * _rowHeight;
302 drawAMPM(gc, y, i);
303 drawHour(gc, y, i);
304 drawMinute(gc, y, i);
305 }
306
307
308
309
310
311
312
313
314
315 }
316
317 private void drawMarks(GC gc) {
318 Color bg = gc.getBackground();
319 gc.setBackground(MARKERCOLOR);
320 JaretDate date = new JaretDate(_date);
321
322 int y = date.getHours() < 12 ? 0 : _rowHeight;
323 Rectangle mark = new Rectangle(0, y, _columnWidth, _rowHeight);
324 gc.fillRectangle(mark);
325
326 int hour = date.getHours();
327 hour -= (hour >= 12 ? 12 : 0);
328 y = hour * _rowHeight;
329 mark = new Rectangle(_columnWidth, y, _columnWidth, _rowHeight);
330 gc.fillRectangle(mark);
331
332 y = date.getMinutes() * getClientArea().height / 60;
333 mark = new Rectangle(2 * _columnWidth, y, 2 * _columnWidth, _rowHeight);
334 gc.fillRectangle(mark);
335
336 gc.setBackground(bg);
337 }
338
339 private void drawAMPM(GC gc, int y, int i) {
340 if (i < 2) {
341 int x = 0;
342 String hour = i == 0 ? "<12" : ">=12";
343 SwtGraphicsHelper.drawStringRightAlignedVTop(gc, hour, x + _columnWidth, y);
344 }
345 }
346
347 private void drawMinute(GC gc, int y, int i) {
348 int x = 2 * _columnWidth;
349 String minute = i * 5 + "";
350 SwtGraphicsHelper.drawStringRightAlignedVTop(gc, minute, x + _columnWidth, y);
351 }
352
353 private void drawHour(GC gc, int y, int i) {
354 JaretDate date = new JaretDate(_date);
355 int x = _columnWidth;
356 String hour = date.getHours() < 12 ? (i + "") : ((i + 12) + "");
357 SwtGraphicsHelper.drawStringRightAlignedVTop(gc, hour, x + _columnWidth, y);
358 }
359
360 }
361
362 /***
363 * {@inheritDoc}
364 */
365 public void mouseDoubleClick(MouseEvent event) {
366 boolean success = setTimeByLocation(event.x, event.y);
367 if (success) {
368 fireDateChosen(getDate());
369 }
370 }
371
372 /***
373 * Set the time of the date by a clicked location.
374 *
375 * @param x x
376 * @param y y
377 * @return true if the click was successful in selecting a new time
378 */
379 private boolean setTimeByLocation(int x, int y) {
380 int colIdx = x / _columnWidth;
381 int rowIdx = y / _rowHeight;
382 boolean success = false;
383 JaretDate d = new JaretDate(_date);
384
385 switch (colIdx) {
386 case 0:
387 if (rowIdx == 0) {
388 success = true;
389 if (d.getHours() >= 12) {
390 d.setHours(d.getHours() - 12);
391 }
392 } else if (rowIdx == 1) {
393 success = true;
394 if (d.getHours() < 12) {
395 d.setHours(d.getHours() + 12);
396 }
397 }
398 break;
399 case 1:
400 int h = d.getHours() < 12 ? rowIdx : rowIdx + 12;
401 d.setHours(h);
402 success = true;
403 break;
404 case 2:
405 int min = rowIdx * 5;
406 d.setMinutes(min);
407 success = true;
408 break;
409 default:
410 success = false;
411 break;
412 }
413
414 if (success) {
415 redraw();
416 _date = d.getDate();
417 }
418
419 return success;
420 }
421
422 /***
423 * Roll the minutes by count * 5 Minutes.
424 *
425 * @param count units to roll
426 */
427 private void rollMinutes(int count) {
428 JaretDate d = new JaretDate(_date);
429 if (count < 0 && d.getMinutes() > 0) {
430 int m = d.getMinutes() + count * 5;
431 m = m < 0 ? 0 : m;
432 d.setMinutes(m);
433 _date = d.getDate();
434 fireIntermediateChange(_date);
435 redraw();
436 } else if (count > 0 && d.getMinutes() < 59) {
437 int m = d.getMinutes() + count * 5;
438 m = m > 55 ? 55 : m;
439 d.setMinutes(m);
440 _date = d.getDate();
441 fireIntermediateChange(_date);
442 redraw();
443 }
444
445 }
446
447 /***
448 * Roll the hour field by count units. Will flip over the am/pm section if necessary.
449 *
450 * @param count units to roll
451 */
452 private void rollHours(int count) {
453 JaretDate d = new JaretDate(_date);
454 if (count < 0 && d.getHours() > 0) {
455 int h = d.getHours() + count;
456 h = h < 0 ? 0 : h;
457 d.setHours(h);
458 _date = d.getDate();
459 fireIntermediateChange(_date);
460 redraw();
461 } else if (count > 0 && d.getHours() < 23) {
462 int h = d.getHours() + count;
463 h = h > 23 ? 23 : h;
464 d.setHours(h);
465 _date = d.getDate();
466 fireIntermediateChange(_date);
467 redraw();
468 }
469 }
470
471 /***
472 * Roll the am/pm section by count units.
473 *
474 * @param count units to roll
475 */
476 private void rollAMPM(int count) {
477 JaretDate d = new JaretDate(_date);
478 if (count < 0 && d.getHours() >= 12) {
479 d.setHours(d.getHours() - 12);
480 _date = d.getDate();
481 fireIntermediateChange(_date);
482 redraw();
483 } else if (count > 0 && d.getHours() < 12) {
484 d.setHours(d.getHours() + 12);
485 _date = d.getDate();
486 fireIntermediateChange(_date);
487 redraw();
488 }
489 }
490
491 /***
492 * {@inheritDoc}
493 */
494 public void mouseDown(MouseEvent event) {
495 boolean success = setTimeByLocation(event.x, event.y);
496 if (success) {
497 fireIntermediateChange(getDate());
498 }
499 }
500
501 /***
502 * {@inheritDoc}
503 */
504 public void mouseUp(MouseEvent event) {
505
506 }
507
508 /***
509 * Add a DateChooserListener to be informed about changes.
510 *
511 * @param listener the DateChooserListener to be added
512 */
513 public synchronized void addDateChooserListener(IDateChooserListener listener) {
514 if (_listenerList == null) {
515 _listenerList = new Vector<IDateChooserListener>();
516 }
517 _listenerList.add(listener);
518 }
519
520 /***
521 * Remove a DateChooserListener.
522 *
523 * @param listener the DateChooserListener to be removed
524 */
525 public synchronized void remDateChooserListener(IDateChooserListener listener) {
526 if (_listenerList == null) {
527 return;
528 }
529 _listenerList.remove(listener);
530 }
531
532 /***
533 * Inform listeners about a chosing action.
534 *
535 * @param date date chosen
536 */
537 protected void fireDateChosen(Date date) {
538 if (_listenerList != null) {
539 for (IDateChooserListener listener : _listenerList) {
540 listener.dateChosen(date);
541 }
542 }
543 }
544
545 /***
546 * Inform listeners about an intermediate date change.
547 *
548 * @param date date currently selected
549 */
550 protected void fireIntermediateChange(Date date) {
551 if (_listenerList != null) {
552 for (IDateChooserListener listener : _listenerList) {
553 listener.dateIntermediateChange(date);
554 }
555 }
556 }
557
558 /***
559 * Inform listeners about cancellation f the choosing.
560 */
561 protected void fireChoosingCanceled() {
562 if (_listenerList != null) {
563 for (IDateChooserListener listener : _listenerList) {
564 listener.choosingCanceled();
565 }
566 }
567 }
568
569 }