1 /*******************************************************************************
2 * Copyright (c) 2000, 2012 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.widgets;
15
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.events.*;
18 import org.eclipse.swt.graphics.*;
19 import org.eclipse.swt.internal.win32.*;
20
21 /**
22 * Instances of this class are selectable user interface
23 * objects that allow the user to enter and modify date
24 * or time values.
25 * <p>
26 * Note that although this class is a subclass of <code>Composite</code>,
27 * it does not make sense to add children to it, or set a layout on it.
28 * </p>
29 * <dl>
30 * <dt><b>Styles:</b></dt>
31 * <dd>DATE, TIME, CALENDAR, SHORT, MEDIUM, LONG, DROP_DOWN, CALENDAR_WEEKNUMBERS</dd>
32 * <dt><b>Events:</b></dt>
33 * <dd>DefaultSelection, Selection</dd>
34 * </dl>
35 * <p>
36 * Note: Only one of the styles DATE, TIME, or CALENDAR may be specified,
37 * and only one of the styles SHORT, MEDIUM, or LONG may be specified.
38 * The DROP_DOWN style is only valid with the DATE style.
39 * </p><p>
40 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
41 * </p>
42 *
43 * @see <a href="http://www.eclipse.org/swt/snippets/#datetime">DateTime snippets</a>
44 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
45 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
46 *
47 * @since 3.3
48 * @noextend This class is not intended to be subclassed by clients.
49 */
50 public class DateTime extends Composite {
51 static final int MIN_YEAR = 1752; // Gregorian switchover in North America: September 19, 1752
52 static final int MAX_YEAR = 9999;
53 boolean doubleClick, ignoreSelection;
54 SYSTEMTIME lastSystemTime;
55 SYSTEMTIME time = new SYSTEMTIME (); // only used in calendar mode
56 static final long DateTimeProc;
57 static final TCHAR DateTimeClass = new TCHAR (0, OS.DATETIMEPICK_CLASS, true);
58 static final long CalendarProc;
59 static final TCHAR CalendarClass = new TCHAR (0, OS.MONTHCAL_CLASS, true);
60 static {
61 INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX ();
62 icex.dwSize = INITCOMMONCONTROLSEX.sizeof;
63 icex.dwICC = OS.ICC_DATE_CLASSES;
64 OS.InitCommonControlsEx (icex);
65 }
66 static {
67 WNDCLASS lpWndClass = new WNDCLASS ();
68 OS.GetClassInfo (0, DateTimeClass, lpWndClass);
69 DateTimeProc = lpWndClass.lpfnWndProc;
70 /*
71 * Feature in Windows. The date time window class
72 * does not include CS_DBLCLKS. This means that these
73 * controls will not get double click messages such as
74 * WM_LBUTTONDBLCLK. The fix is to register a new
75 * window class with CS_DBLCLKS.
76 *
77 * NOTE: Screen readers look for the exact class name
78 * of the control in order to provide the correct kind
79 * of assistance. Therefore, it is critical that the
80 * new window class have the same name. It is possible
81 * to register a local window class with the same name
82 * as a global class. Since bits that affect the class
83 * are being changed, it is possible that other native
84 * code, other than SWT, could create a control with
85 * this class name, and fail unexpectedly.
86 */
87 lpWndClass.hInstance = OS.GetModuleHandle (null);
88 lpWndClass.style &= ~OS.CS_GLOBALCLASS;
89 lpWndClass.style |= OS.CS_DBLCLKS;
OS.RegisterClass(DateTimeClass, lpWndClass)90 OS.RegisterClass (DateTimeClass, lpWndClass);
91 }
92 static {
93 WNDCLASS lpWndClass = new WNDCLASS ();
94 OS.GetClassInfo (0, CalendarClass, lpWndClass);
95 CalendarProc = lpWndClass.lpfnWndProc;
96 /*
97 * Feature in Windows. The date time window class
98 * does not include CS_DBLCLKS. This means that these
99 * controls will not get double click messages such as
100 * WM_LBUTTONDBLCLK. The fix is to register a new
101 * window class with CS_DBLCLKS.
102 *
103 * NOTE: Screen readers look for the exact class name
104 * of the control in order to provide the correct kind
105 * of assistance. Therefore, it is critical that the
106 * new window class have the same name. It is possible
107 * to register a local window class with the same name
108 * as a global class. Since bits that affect the class
109 * are being changed, it is possible that other native
110 * code, other than SWT, could create a control with
111 * this class name, and fail unexpectedly.
112 */
113 lpWndClass.hInstance = OS.GetModuleHandle (null);;
114 lpWndClass.style &= ~OS.CS_GLOBALCLASS;
115 lpWndClass.style |= OS.CS_DBLCLKS;
OS.RegisterClass(CalendarClass, lpWndClass)116 OS.RegisterClass (CalendarClass, lpWndClass);
117 }
118 static final char SINGLE_QUOTE = '\''; //$NON-NLS-1$ short date format may include quoted text
119 static final char DAY_FORMAT_CONSTANT = 'd'; //$NON-NLS-1$ 1-4 lowercase 'd's represent day
120 static final char MONTH_FORMAT_CONSTANT = 'M'; //$NON-NLS-1$ 1-4 uppercase 'M's represent month
121 static final char YEAR_FORMAT_CONSTANT = 'y'; //$NON-NLS-1$ 1-5 lowercase 'y's represent year
122 static final char HOURS_FORMAT_CONSTANT = 'h'; //$NON-NLS-1$ 1-2 upper or lowercase 'h's represent hours
123 static final char MINUTES_FORMAT_CONSTANT = 'm'; //$NON-NLS-1$ 1-2 lowercase 'm's represent minutes
124 static final char SECONDS_FORMAT_CONSTANT = 's'; //$NON-NLS-1$ 1-2 lowercase 's's represent seconds
125 static final char AMPM_FORMAT_CONSTANT = 't'; //$NON-NLS-1$ 1-2 lowercase 't's represent am/pm
126
127
128 /**
129 * Constructs a new instance of this class given its parent
130 * and a style value describing its behavior and appearance.
131 * <p>
132 * The style value is either one of the style constants defined in
133 * class <code>SWT</code> which is applicable to instances of this
134 * class, or must be built by <em>bitwise OR</em>'ing together
135 * (that is, using the <code>int</code> "|" operator) two or more
136 * of those <code>SWT</code> style constants. The class description
137 * lists the style constants that are applicable to the class.
138 * Style bits are also inherited from superclasses.
139 * </p>
140 *
141 * @param parent a composite control which will be the parent of the new instance (cannot be null)
142 * @param style the style of control to construct
143 *
144 * @exception IllegalArgumentException <ul>
145 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
146 * </ul>
147 * @exception SWTException <ul>
148 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
149 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
150 * </ul>
151 *
152 * @see SWT#DATE
153 * @see SWT#TIME
154 * @see SWT#CALENDAR
155 * @see SWT#CALENDAR_WEEKNUMBERS
156 * @see SWT#SHORT
157 * @see SWT#MEDIUM
158 * @see SWT#LONG
159 * @see SWT#DROP_DOWN
160 * @see Widget#checkSubclass
161 * @see Widget#getStyle
162 */
DateTime(Composite parent, int style)163 public DateTime (Composite parent, int style) {
164 super (parent, checkStyle (style));
165 if ((this.style & SWT.SHORT) != 0) {
166 String buffer = ((this.style & SWT.DATE) != 0) ? getCustomShortDateFormat() : getCustomShortTimeFormat();
167 TCHAR lpszFormat = new TCHAR (0, buffer, true);
168 OS.SendMessage (handle, OS.DTM_SETFORMAT, 0, lpszFormat);
169 }
170 }
171
172 /**
173 * Adds the listener to the collection of listeners who will
174 * be notified when the control is selected by the user, by sending
175 * it one of the messages defined in the <code>SelectionListener</code>
176 * interface.
177 * <p>
178 * <code>widgetSelected</code> is called when the user changes the control's value.
179 * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed.
180 * </p>
181 *
182 * @param listener the listener which should be notified
183 *
184 * @exception IllegalArgumentException <ul>
185 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
186 * </ul>
187 * @exception SWTException <ul>
188 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
189 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
190 * </ul>
191 *
192 * @see SelectionListener
193 * @see #removeSelectionListener
194 * @see SelectionEvent
195 */
addSelectionListener(SelectionListener listener)196 public void addSelectionListener (SelectionListener listener) {
197 checkWidget ();
198 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
199 TypedListener typedListener = new TypedListener (listener);
200 addListener (SWT.Selection, typedListener);
201 addListener (SWT.DefaultSelection, typedListener);
202 }
203
204 @Override
callWindowProc(long hwnd, int msg, long wParam, long lParam)205 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
206 if (handle == 0) return 0;
207 return OS.CallWindowProc (windowProc (), hwnd, msg, wParam, lParam);
208 }
209
checkStyle(int style)210 static int checkStyle (int style) {
211 /*
212 * Even though it is legal to create this widget
213 * with scroll bars, they serve no useful purpose
214 * because they do not automatically scroll the
215 * widget's client area. The fix is to clear
216 * the SWT style.
217 */
218 style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
219 style = checkBits (style, SWT.DATE, SWT.TIME, SWT.CALENDAR, 0, 0, 0);
220 style = checkBits (style, SWT.MEDIUM, SWT.SHORT, SWT.LONG, 0, 0, 0);
221 if ((style & SWT.DATE) == 0) style &=~ SWT.DROP_DOWN;
222 return style;
223 }
224
225 @Override
checkSubclass()226 protected void checkSubclass () {
227 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
228 }
229
computeSizeInPixels(int wHint, int hHint, boolean changed)230 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
231 checkWidget ();
232 int width = 0, height = 0;
233 if (wHint == SWT.DEFAULT || hHint == SWT.DEFAULT) {
234 if ((style & SWT.CALENDAR) != 0) {
235 RECT rect = new RECT ();
236 OS.SendMessage(handle, OS.MCM_GETMINREQRECT, 0, rect);
237 width = rect.right;
238 height = rect.bottom;
239 } else {
240 // customize the style of the drop-down calendar, to get the correct size
241 if ((style & SWT.CALENDAR_WEEKNUMBERS) != 0) {
242 // get current style and add week numbers to the calendar drop-down
243 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
244 OS.SendMessage(handle, OS.DTM_SETMCSTYLE, 0, bits | OS.MCS_WEEKNUMBERS);
245 }
246 SIZE size = new SIZE ();
247 OS.SendMessage(handle, OS.DTM_GETIDEALSIZE, 0, size);
248 width = size.cx;
249 height = size.cy;
250 // TODO: Can maybe use DTM_GETDATETIMEPICKERINFO for this
251 int upDownHeight = OS.GetSystemMetrics (OS.SM_CYVSCROLL) + 7;
252 height = Math.max (height, upDownHeight);
253 }
254 }
255 if (width == 0) width = DEFAULT_WIDTH;
256 if (height == 0) height = DEFAULT_HEIGHT;
257 if (wHint != SWT.DEFAULT) width = wHint;
258 if (hHint != SWT.DEFAULT) height = hHint;
259 int border = getBorderWidthInPixels ();
260 width += border * 2;
261 height += border * 2;
262 return new Point (width, height);
263 }
264
265 @Override
createHandle()266 void createHandle () {
267 super.createHandle ();
268 state &= ~(CANVAS | THEME_BACKGROUND);
269
270 if ((style & SWT.BORDER) == 0) {
271 int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
272 bits &= ~(OS.WS_EX_CLIENTEDGE | OS.WS_EX_STATICEDGE);
273 OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits);
274 }
275 }
276
277 @Override
defaultBackground()278 int defaultBackground () {
279 return OS.GetSysColor (OS.COLOR_WINDOW);
280 }
281
getCustomShortDateFormat()282 String getCustomShortDateFormat () {
283 TCHAR tchar = new TCHAR (getCodePage (), 80);
284 int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_SYEARMONTH, tchar, 80);
285 return size != 0 ? tchar.toString (0, size - 1) : "M/yyyy"; //$NON-NLS-1$
286 }
287
getCustomShortTimeFormat()288 String getCustomShortTimeFormat () {
289 StringBuilder buffer = new StringBuilder (getTimeFormat ());
290 int length = buffer.length ();
291 boolean inQuotes = false;
292 int start = 0, end = 0;
293 while (start < length) {
294 char ch = buffer.charAt (start);
295 if (ch == SINGLE_QUOTE) inQuotes = !inQuotes;
296 else if (ch == SECONDS_FORMAT_CONSTANT && !inQuotes) {
297 end = start + 1;
298 while (end < length && buffer.charAt (end) == SECONDS_FORMAT_CONSTANT) end++;
299 // skip the preceding separator
300 while (start > 0 && buffer.charAt (start) != MINUTES_FORMAT_CONSTANT) start--;
301 start++;
302 break;
303 }
304 start++;
305 }
306 if (start < end) buffer.delete (start, end);
307 return buffer.toString ();
308 }
309
getTimeFormat()310 String getTimeFormat () {
311 TCHAR tchar = new TCHAR (getCodePage (), 80);
312 int size = OS.GetLocaleInfo (OS.LOCALE_USER_DEFAULT, OS.LOCALE_STIMEFORMAT, tchar, 80);
313 return size > 0 ? tchar.toString (0, size - 1) : "h:mm:ss tt"; //$NON-NLS-1$
314 }
315
316 /**
317 * Returns the receiver's date, or day of the month.
318 * <p>
319 * The first day of the month is 1, and the last day depends on the month and year.
320 * </p>
321 *
322 * @return a positive integer beginning with 1
323 *
324 * @exception SWTException <ul>
325 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
326 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
327 * </ul>
328 */
getDay()329 public int getDay () {
330 checkWidget ();
331 SYSTEMTIME systime = new SYSTEMTIME ();
332 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
333 OS.SendMessage (handle, msg, 0, systime);
334 return systime.wDay;
335 }
336
337 /**
338 * Returns the receiver's hours.
339 * <p>
340 * Hours is an integer between 0 and 23.
341 * </p>
342 *
343 * @return an integer between 0 and 23
344 *
345 * @exception SWTException <ul>
346 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
347 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
348 * </ul>
349 */
getHours()350 public int getHours () {
351 checkWidget ();
352 if ((style & SWT.CALENDAR) != 0) return time.wHour;
353 SYSTEMTIME systime = new SYSTEMTIME ();
354 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
355 OS.SendMessage (handle, msg, 0, systime);
356 return systime.wHour;
357 }
358
359 /**
360 * Returns the receiver's minutes.
361 * <p>
362 * Minutes is an integer between 0 and 59.
363 * </p>
364 *
365 * @return an integer between 0 and 59
366 *
367 * @exception SWTException <ul>
368 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
369 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
370 * </ul>
371 */
getMinutes()372 public int getMinutes () {
373 checkWidget ();
374 if ((style & SWT.CALENDAR) != 0) return time.wMinute;
375 SYSTEMTIME systime = new SYSTEMTIME ();
376 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
377 OS.SendMessage (handle, msg, 0, systime);
378 return systime.wMinute;
379 }
380
381 /**
382 * Returns the receiver's month.
383 * <p>
384 * The first month of the year is 0, and the last month is 11.
385 * </p>
386 *
387 * @return an integer between 0 and 11
388 *
389 * @exception SWTException <ul>
390 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
391 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
392 * </ul>
393 */
getMonth()394 public int getMonth () {
395 checkWidget ();
396 SYSTEMTIME systime = new SYSTEMTIME ();
397 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
398 OS.SendMessage (handle, msg, 0, systime);
399 return systime.wMonth - 1;
400 }
401
402 @Override
getNameText()403 String getNameText() {
404 return (style & SWT.TIME) != 0 ? getHours() + ":" + getMinutes() + ":" + getSeconds()
405 : (getMonth() + 1) + "/" + getDay() + "/" + getYear();
406 }
407
408 /**
409 * Returns the receiver's seconds.
410 * <p>
411 * Seconds is an integer between 0 and 59.
412 * </p>
413 *
414 * @return an integer between 0 and 59
415 *
416 * @exception SWTException <ul>
417 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
418 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
419 * </ul>
420 */
getSeconds()421 public int getSeconds () {
422 checkWidget ();
423 if ((style & SWT.CALENDAR) != 0) return time.wSecond;
424 SYSTEMTIME systime = new SYSTEMTIME ();
425 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
426 OS.SendMessage (handle, msg, 0, systime);
427 return systime.wSecond;
428 }
429
430 /**
431 * Returns the receiver's year.
432 * <p>
433 * The first year is 1752 and the last year is 9999.
434 * </p>
435 *
436 * @return an integer between 1752 and 9999
437 *
438 * @exception SWTException <ul>
439 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
440 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
441 * </ul>
442 */
getYear()443 public int getYear () {
444 checkWidget ();
445 SYSTEMTIME systime = new SYSTEMTIME ();
446 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
447 OS.SendMessage (handle, msg, 0, systime);
448 return systime.wYear;
449 }
450
451 @Override
releaseWidget()452 void releaseWidget () {
453 super.releaseWidget ();
454 lastSystemTime = null;
455 }
456
457 /**
458 * Removes the listener from the collection of listeners who will
459 * be notified when the control is selected by the user.
460 *
461 * @param listener the listener which should no longer be notified
462 *
463 * @exception IllegalArgumentException <ul>
464 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
465 * </ul>
466 * @exception SWTException <ul>
467 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
468 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
469 * </ul>
470 *
471 * @see SelectionListener
472 * @see #addSelectionListener
473 */
removeSelectionListener(SelectionListener listener)474 public void removeSelectionListener (SelectionListener listener) {
475 checkWidget ();
476 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
477 if (eventTable == null) return;
478 eventTable.unhook (SWT.Selection, listener);
479 eventTable.unhook (SWT.DefaultSelection, listener);
480 }
481
482 /**
483 * Sets the receiver's year, month, and day in a single operation.
484 * <p>
485 * This is the recommended way to set the date, because setting the year,
486 * month, and day separately may result in invalid intermediate dates.
487 * </p>
488 *
489 * @param year an integer between 1752 and 9999
490 * @param month an integer between 0 and 11
491 * @param day a positive integer beginning with 1
492 *
493 * @exception SWTException <ul>
494 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
495 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
496 * </ul>
497 *
498 * @since 3.4
499 */
setDate(int year, int month, int day)500 public void setDate (int year, int month, int day) {
501 checkWidget ();
502 if (year < MIN_YEAR || year > MAX_YEAR) return;
503 SYSTEMTIME systime = new SYSTEMTIME ();
504 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
505 OS.SendMessage (handle, msg, 0, systime);
506 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
507 systime.wYear = (short)year;
508 systime.wMonth = (short)(month + 1);
509 systime.wDay = (short)day;
510 OS.SendMessage (handle, msg, 0, systime);
511 lastSystemTime = null;
512 }
513
514 /**
515 * Sets the receiver's date, or day of the month, to the specified day.
516 * <p>
517 * The first day of the month is 1, and the last day depends on the month and year.
518 * If the specified day is not valid for the receiver's month and year, then it is ignored.
519 * </p>
520 *
521 * @param day a positive integer beginning with 1
522 *
523 * @exception SWTException <ul>
524 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
525 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
526 * </ul>
527 *
528 * @see #setDate
529 */
setDay(int day)530 public void setDay (int day) {
531 checkWidget ();
532 SYSTEMTIME systime = new SYSTEMTIME ();
533 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
534 OS.SendMessage (handle, msg, 0, systime);
535 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
536 systime.wDay = (short)day;
537 OS.SendMessage (handle, msg, 0, systime);
538 lastSystemTime = null;
539 }
540
541 /**
542 * Sets the receiver's hours.
543 * <p>
544 * Hours is an integer between 0 and 23.
545 * </p>
546 *
547 * @param hours an integer between 0 and 23
548 *
549 * @exception SWTException <ul>
550 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
551 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
552 * </ul>
553 */
setHours(int hours)554 public void setHours (int hours) {
555 checkWidget ();
556 if (hours < 0 || hours > 23) return;
557 SYSTEMTIME systime = new SYSTEMTIME ();
558 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
559 OS.SendMessage (handle, msg, 0, systime);
560 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
561 systime.wHour = (short)hours;
562 OS.SendMessage (handle, msg, 0, systime);
563 if ((style & SWT.CALENDAR) != 0 && hours >= 0 && hours <= 23) time.wHour = (short)hours;
564 }
565
566 /**
567 * Sets the receiver's minutes.
568 * <p>
569 * Minutes is an integer between 0 and 59.
570 * </p>
571 *
572 * @param minutes an integer between 0 and 59
573 *
574 * @exception SWTException <ul>
575 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
576 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
577 * </ul>
578 */
setMinutes(int minutes)579 public void setMinutes (int minutes) {
580 checkWidget ();
581 if (minutes < 0 || minutes > 59) return;
582 SYSTEMTIME systime = new SYSTEMTIME ();
583 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
584 OS.SendMessage (handle, msg, 0, systime);
585 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
586 systime.wMinute = (short)minutes;
587 OS.SendMessage (handle, msg, 0, systime);
588 if ((style & SWT.CALENDAR) != 0 && minutes >= 0 && minutes <= 59) time.wMinute = (short)minutes;
589 }
590
591 /**
592 * Sets the receiver's month.
593 * <p>
594 * The first month of the year is 0, and the last month is 11.
595 * If the specified month is not valid for the receiver's day and year, then it is ignored.
596 * </p>
597 *
598 * @param month an integer between 0 and 11
599 *
600 * @exception SWTException <ul>
601 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
602 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
603 * </ul>
604 *
605 * @see #setDate
606 */
setMonth(int month)607 public void setMonth (int month) {
608 checkWidget ();
609 SYSTEMTIME systime = new SYSTEMTIME ();
610 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
611 OS.SendMessage (handle, msg, 0, systime);
612 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
613 systime.wMonth = (short)(month + 1);
614 OS.SendMessage (handle, msg, 0, systime);
615 lastSystemTime = null;
616 }
617
618 @Override
setOrientation(int orientation)619 public void setOrientation (int orientation) {
620 /* Currently supported only for CALENDAR style. */
621 if ((style & SWT.CALENDAR) != 0) super.setOrientation (orientation);
622 }
623 /**
624 * Sets the receiver's seconds.
625 * <p>
626 * Seconds is an integer between 0 and 59.
627 * </p>
628 *
629 * @param seconds an integer between 0 and 59
630 *
631 * @exception SWTException <ul>
632 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
633 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
634 * </ul>
635 */
setSeconds(int seconds)636 public void setSeconds (int seconds) {
637 checkWidget ();
638 if (seconds < 0 || seconds > 59) return;
639 SYSTEMTIME systime = new SYSTEMTIME ();
640 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
641 OS.SendMessage (handle, msg, 0, systime);
642 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
643 systime.wSecond = (short)seconds;
644 OS.SendMessage (handle, msg, 0, systime);
645 if ((style & SWT.CALENDAR) != 0 && seconds >= 0 && seconds <= 59) time.wSecond = (short)seconds;
646 }
647
648 /**
649 * Sets the receiver's hours, minutes, and seconds in a single operation.
650 *
651 * @param hours an integer between 0 and 23
652 * @param minutes an integer between 0 and 59
653 * @param seconds an integer between 0 and 59
654 *
655 * @exception SWTException <ul>
656 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
657 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
658 * </ul>
659 *
660 * @since 3.4
661 */
setTime(int hours, int minutes, int seconds)662 public void setTime (int hours, int minutes, int seconds) {
663 checkWidget ();
664 if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) return;
665 SYSTEMTIME systime = new SYSTEMTIME ();
666 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
667 OS.SendMessage (handle, msg, 0, systime);
668 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
669 systime.wHour = (short)hours;
670 systime.wMinute = (short)minutes;
671 systime.wSecond = (short)seconds;
672 OS.SendMessage (handle, msg, 0, systime);
673 if ((style & SWT.CALENDAR) != 0
674 && hours >= 0 && hours <= 23
675 && minutes >= 0 && minutes <= 59
676 && seconds >= 0 && seconds <= 59) {
677 time.wHour = (short)hours;
678 time.wMinute = (short)minutes;
679 time.wSecond = (short)seconds;
680 }
681 }
682
683 /**
684 * Sets the receiver's year.
685 * <p>
686 * The first year is 1752 and the last year is 9999.
687 * If the specified year is not valid for the receiver's day and month, then it is ignored.
688 * </p>
689 *
690 * @param year an integer between 1752 and 9999
691 *
692 * @exception SWTException <ul>
693 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
694 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
695 * </ul>
696 *
697 * @see #setDate
698 */
setYear(int year)699 public void setYear (int year) {
700 checkWidget ();
701 if (year < MIN_YEAR || year > MAX_YEAR) return;
702 SYSTEMTIME systime = new SYSTEMTIME ();
703 int msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_GETCURSEL : OS.DTM_GETSYSTEMTIME;
704 OS.SendMessage (handle, msg, 0, systime);
705 msg = (style & SWT.CALENDAR) != 0 ? OS.MCM_SETCURSEL : OS.DTM_SETSYSTEMTIME;
706 systime.wYear = (short)year;
707 OS.SendMessage (handle, msg, 0, systime);
708 lastSystemTime = null;
709 }
710
711 @Override
widgetStyle()712 int widgetStyle () {
713 int bits = super.widgetStyle () | OS.WS_TABSTOP;
714 if ((style & SWT.CALENDAR_WEEKNUMBERS) != 0) {
715 bits |= OS.MCS_WEEKNUMBERS;
716 }
717 if ((style & SWT.CALENDAR) != 0) return bits | OS.MCS_NOTODAY;
718 /*
719 * Bug in Windows: When WS_CLIPCHILDREN is set in a
720 * Date and Time Picker, the widget draws on top of
721 * the updown control. The fix is to clear the bits.
722 */
723 bits &= ~OS.WS_CLIPCHILDREN;
724 if ((style & SWT.TIME) != 0) bits |= OS.DTS_TIMEFORMAT;
725 if ((style & SWT.DATE) != 0) {
726 bits |= ((style & SWT.MEDIUM) != 0 ? OS.DTS_SHORTDATECENTURYFORMAT : OS.DTS_LONGDATEFORMAT);
727 if ((style & SWT.DROP_DOWN) == 0) bits |= OS.DTS_UPDOWN;
728 }
729 return bits;
730 }
731
732 @Override
windowClass()733 TCHAR windowClass () {
734 return (style & SWT.CALENDAR) != 0 ? CalendarClass : DateTimeClass;
735 }
736
737 @Override
windowProc()738 long windowProc () {
739 return (style & SWT.CALENDAR) != 0 ? CalendarProc : DateTimeProc;
740 }
741
742 @Override
wmNotifyChild(NMHDR hdr, long wParam, long lParam)743 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
744 switch (hdr.code) {
745 case OS.DTN_CLOSEUP: {
746 /*
747 * Feature in Windows. When the user selects the drop-down button,
748 * the DateTimePicker runs a modal loop and consumes WM_LBUTTONUP.
749 * This is done without adding a mouse capture. Since WM_LBUTTONUP
750 * is not delivered, the normal mechanism where a mouse capture is
751 * added on mouse down and removed when the mouse is released
752 * is broken, leaving an unwanted capture. The fix is to avoid
753 * setting capture on mouse down right after WM_LBUTTONUP is consumed.
754 */
755 display.captureChanged = true;
756 break;
757 }
758 case OS.MCN_SELCHANGE: {
759 if (ignoreSelection) break;
760 SYSTEMTIME systime = new SYSTEMTIME ();
761 OS.SendMessage (handle, OS.MCM_GETCURSEL, 0, systime);
762 sendSelectionEvent (SWT.Selection);
763 break;
764 }
765 case OS.DTN_DATETIMECHANGE: {
766 SYSTEMTIME systime = new SYSTEMTIME ();
767 OS.SendMessage (handle, OS.DTM_GETSYSTEMTIME, 0, systime);
768 if (lastSystemTime == null || systime.wDay != lastSystemTime.wDay || systime.wMonth != lastSystemTime.wMonth || systime.wYear != lastSystemTime.wYear) {
769 sendSelectionEvent (SWT.Selection);
770 if ((style & SWT.TIME) == 0) lastSystemTime = systime;
771 }
772 break;
773 }
774 }
775 return super.wmNotifyChild (hdr, wParam, lParam);
776 }
777
778 @Override
WM_CHAR(long wParam, long lParam)779 LRESULT WM_CHAR (long wParam, long lParam) {
780 LRESULT result = super.WM_CHAR (wParam, lParam);
781 if (result != null) return result;
782 /*
783 * Feature in Windows. For some reason, when the
784 * user presses tab, return or escape, Windows beeps.
785 * The fix is to look for these keys and not call
786 * the window proc.
787 */
788 switch ((int)wParam) {
789 case SWT.CR:
790 sendSelectionEvent (SWT.DefaultSelection);
791 // FALL THROUGH
792 case SWT.TAB:
793 case SWT.ESC: return LRESULT.ZERO;
794 }
795 return result;
796 }
797
798 @Override
WM_LBUTTONDBLCLK(long wParam, long lParam)799 LRESULT WM_LBUTTONDBLCLK (long wParam, long lParam) {
800 LRESULT result = super.WM_LBUTTONDBLCLK (wParam, lParam);
801 if (isDisposed ()) return LRESULT.ZERO;
802 if ((style & SWT.CALENDAR) != 0) {
803 MCHITTESTINFO pMCHitTest = new MCHITTESTINFO ();
804 pMCHitTest.cbSize = MCHITTESTINFO.sizeof;
805 POINT pt = new POINT ();
806 pt.x = OS.GET_X_LPARAM (lParam);
807 pt.y = OS.GET_Y_LPARAM (lParam);
808 pMCHitTest.pt = pt;
809 long code = OS.SendMessage (handle, OS.MCM_HITTEST, 0, pMCHitTest);
810 if ((code & OS.MCHT_CALENDARDATE) == OS.MCHT_CALENDARDATE) doubleClick = true;
811 }
812 return result;
813 }
814
815 @Override
WM_LBUTTONDOWN(long wParam, long lParam)816 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
817 LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
818 if (result == LRESULT.ZERO) return result;
819 doubleClick = false;
820 /*
821 * Feature in Windows. For some reason, the calendar control
822 * does not take focus on WM_LBUTTONDOWN. The fix is to
823 * explicitly set focus.
824 */
825 if ((style & SWT.CALENDAR) != 0) {
826 if ((style & SWT.NO_FOCUS) == 0) OS.SetFocus (handle);
827 }
828 return result;
829 }
830
831 @Override
WM_LBUTTONUP(long wParam, long lParam)832 LRESULT WM_LBUTTONUP (long wParam, long lParam) {
833 LRESULT result = super.WM_LBUTTONUP (wParam, lParam);
834 if (isDisposed ()) return LRESULT.ZERO;
835 if (doubleClick) sendSelectionEvent (SWT.DefaultSelection);
836 doubleClick = false;
837 return result;
838 }
839
840 @Override
WM_TIMER(long wParam, long lParam)841 LRESULT WM_TIMER (long wParam, long lParam) {
842 LRESULT result = super.WM_TIMER (wParam, lParam);
843 if (result != null) return result;
844 /*
845 * Feature in Windows. For some reason, Windows sends WM_NOTIFY with
846 * MCN_SELCHANGE at regular intervals. This is unexpected. The fix is
847 * to ignore MCN_SELCHANGE during WM_TIMER.
848 */
849 ignoreSelection = true;
850 long code = callWindowProc(handle, OS.WM_TIMER, wParam, lParam);
851 ignoreSelection = false;
852 return code == 0 ? LRESULT.ZERO : new LRESULT(code);
853 }
854 }
855