]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/DateTime.java
Remove invalid SHA-256-Digests
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / DateTime.java
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;
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;
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  */
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  */
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
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
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
226 protected void checkSubclass () {
227         if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
228 }
229
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
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
278 int defaultBackground () {
279         return OS.GetSysColor (OS.COLOR_WINDOW);
280 }
281
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
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
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  */
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  */
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  */
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  */
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
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  */
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  */
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
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  */
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  */
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  */
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  */
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  */
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  */
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
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  */
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  */
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  */
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
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
733 TCHAR windowClass () {
734         return (style & SWT.CALENDAR) != 0 ? CalendarClass : DateTimeClass;
735 }
736
737 @Override
738 long windowProc () {
739         return (style & SWT.CALENDAR) != 0 ? CalendarProc : DateTimeProc;
740 }
741
742 @Override
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
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
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
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
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
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 }