1 /******************************************************************************* 2 * Copyright (c) 2000, 2008 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.widgets.DateTime; 14 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.SWTException; 17 import org.eclipse.swt.events.SelectionEvent; 18 import org.eclipse.swt.events.SelectionListener; 19 import org.eclipse.swt.graphics.Color; 20 import org.eclipse.swt.graphics.Font; 21 import org.eclipse.swt.graphics.GC; 22 import org.eclipse.swt.graphics.Point; 23 import org.eclipse.swt.graphics.Rectangle; 24 import org.eclipse.swt.internal.gtk.OS; 25 import org.eclipse.swt.internal.Compatibility; 26 27 import org.eclipse.swt.widgets.Composite; 28 import org.eclipse.swt.widgets.Listener; 29 import org.eclipse.swt.widgets.Button; 30 import org.eclipse.swt.widgets.Event; 31 import org.eclipse.swt.widgets.Text; 32 import org.eclipse.swt.widgets.TypedListener; 33 34 import java.lang.all; 35 36 version(Tango){ 37 import tango.util.Convert; 38 39 static import tango.text.Util; 40 //static import tango.text.locale.Core; 41 static import tango.time.Time; 42 static import tango.time.WallClock; 43 static import tango.time.chrono.Gregorian; 44 static import tango.time.chrono.Calendar; 45 } else { // Phobos 46 import std.conv; 47 static import std.datetime; 48 } 49 50 51 private class Calendar{ 52 enum { 53 AM, 54 PM 55 } 56 enum { 57 AM_PM, 58 HOUR, 59 MINUTE, 60 SECOND, 61 MONTH, 62 YEAR, 63 DAY_OF_MONTH, 64 DAY_SELECTED, 65 MONTH_CHANGED, 66 HOUR_OF_DAY, 67 } 68 private static const int[] MONTH_DAYS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]; 69 static private Calendar instance; 70 71 private int second; 72 private int minute; 73 private int hour; 74 private int dayofmonth; 75 private int month; 76 private int year; 77 78 static Calendar getInstance(){ 79 if( instance is null ){ 80 synchronized { 81 if( instance is null ){ 82 instance = new Calendar; 83 } 84 } 85 } 86 return instance; 87 } 88 89 public this(){ 90 version(Tango){ 91 tango.time.Time.Time time = tango.time.WallClock.WallClock.now(); 92 tango.time.Time.TimeSpan span = time.time.span; 93 this.second = span.seconds % 60; 94 this.minute = span.minutes % 60; 95 this.hour = span.hours; 96 auto greg = tango.time.chrono.Gregorian.Gregorian.generic; 97 this.dayofmonth = greg.getDayOfMonth( time ); 98 this.month = greg.getMonth( time ); 99 this.year = greg.getYear( time ); 100 } else { // Phobos 101 auto time = std.datetime.Clock.currTime(); 102 this.second = time.second; 103 this.minute = time.minute; 104 this.hour = time.hour; 105 this.dayofmonth = time.day; 106 this.month = time.month; 107 this.year = time.year; 108 } 109 } 110 int getActualMaximum(int field){ 111 switch( field ){ 112 case YEAR: 113 return 2100; 114 case MONTH: 115 return cast(int)MONTH_DAYS.length -1; 116 case DAY_OF_MONTH: 117 return MONTH_DAYS[month]; 118 case HOUR: 119 return 11; 120 case HOUR_OF_DAY: 121 return 23; 122 case MINUTE: 123 return 59; 124 case SECOND: 125 return 59; 126 case AM_PM: 127 return PM; 128 default: assert( false, Format( "no matching switch case for field {}.", field )); 129 } 130 } 131 132 int getActualMinimum(int field){ 133 switch( field ){ 134 case YEAR: 135 return 1900; 136 case MONTH: 137 return 0; 138 case DAY_OF_MONTH: 139 return 1; 140 case HOUR: 141 case HOUR_OF_DAY: 142 return 0; 143 case MINUTE: 144 return 0; 145 case SECOND: 146 return 0; 147 case AM_PM: 148 return AM; 149 default: assert( false, Format( "no matching switch case for field {}.", field )); 150 } 151 } 152 153 int getMaximum(int field){ 154 switch( field ){ 155 case YEAR: 156 return 2100; 157 case MONTH: 158 return 11; 159 case DAY_OF_MONTH: 160 return 31; 161 case HOUR: 162 return 11; 163 case HOUR_OF_DAY: 164 return 23; 165 case MINUTE: 166 return 59; 167 case SECOND: 168 return 59; 169 case AM_PM: 170 return PM; 171 default: assert( false, Format( "no matching switch case for field {}.", field )); 172 } 173 } 174 175 int getMinimum(int field){ 176 switch( field ){ 177 case YEAR: 178 return 1900; 179 case MONTH: 180 return 0; 181 case DAY_OF_MONTH: 182 return 1; 183 case HOUR: 184 case HOUR_OF_DAY: 185 return 0; 186 case MINUTE: 187 return 0; 188 case SECOND: 189 return 0; 190 case AM_PM: 191 return AM; 192 default: assert( false, Format( "no matching switch case for field {}.", field )); 193 } 194 } 195 int get(int field){ 196 switch( field ){ 197 case YEAR: 198 return year; 199 case MONTH: 200 return month; 201 case DAY_OF_MONTH: 202 return dayofmonth; 203 case HOUR: 204 return hour; 205 case HOUR_OF_DAY: 206 return hour % 12; 207 case MINUTE: 208 return minute; 209 case SECOND: 210 return second; 211 case AM_PM: 212 return ( hour < 12 ) ? AM : PM; 213 default: assert( false, Format( "no matching switch case for field {}.", field )); 214 } 215 } 216 void set( int year, int month, int day ){ 217 this.year = year; 218 this.month = month; 219 this.dayofmonth = day; 220 } 221 void set(int field, int value){ 222 switch( field ){ 223 case YEAR: 224 year = value; 225 break; 226 case MONTH: 227 assert( value >= 0 && value < 12 ); 228 month = value; 229 break; 230 case DAY_OF_MONTH: 231 assert( value > 0 && value <= getActualMaximum( DAY_OF_MONTH ) ); 232 dayofmonth = value; 233 break; 234 case HOUR: 235 assert( value >= 0 && value < 12 ); 236 hour = value; 237 break; 238 case HOUR_OF_DAY: 239 assert( value >= 0 && value < 24 ); 240 hour = value; 241 break; 242 case MINUTE: 243 assert( value >= 0 && value < 60 ); 244 minute = value; 245 break; 246 case SECOND: 247 assert( value >= 0 && value < 60 ); 248 second = value; 249 break; 250 case AM_PM: 251 if( get(field) is AM ){ 252 if( value is AM ){ 253 return; 254 } 255 else{ 256 hour += 12; 257 } 258 } 259 else{ // PM 260 if( value is AM ){ 261 hour -= 12; 262 } 263 else{ 264 return; 265 } 266 } 267 break; 268 default: assert( false, Format( "no matching switch case for field {}.", field )); 269 } 270 } 271 272 void roll(int field, int value){ 273 switch( field ){ 274 case YEAR: 275 year = value; 276 break; 277 case MONTH: 278 month += value; 279 month %= 12; 280 break; 281 case DAY_OF_MONTH: 282 dayofmonth += value; 283 dayofmonth %= getActualMaximum( DAY_OF_MONTH ); 284 break; 285 case HOUR: 286 case HOUR_OF_DAY: 287 hour += value; 288 hour %= 24; 289 break; 290 case MINUTE: 291 minute += value; 292 minute %= 60; 293 break; 294 case SECOND: 295 second += value; 296 second %= 60; 297 break; 298 case AM_PM: 299 set( AM_PM, get( AM_PM ) is AM ? PM : AM ); 300 break; 301 default: assert( false, Format( "no matching switch case for field {}.", field )); 302 } 303 } 304 } 305 306 307 private class DateFormatSymbols { 308 private enum String[] ampm = [ "AM"[], "PM" ]; 309 TryConst!(String[]) getAmPmStrings(){ 310 return ampm; 311 } 312 } 313 314 315 /** 316 * Instances of this class are selectable user interface 317 * objects that allow the user to enter and modify date 318 * or time values. 319 * <p> 320 * Note that although this class is a subclass of <code>Composite</code>, 321 * it does not make sense to add children to it, or set a layout on it. 322 * </p> 323 * <dl> 324 * <dt><b>Styles:</b></dt> 325 * <dd>DATE, TIME, CALENDAR, SHORT, MEDIUM, LONG</dd> 326 * <dt><b>Events:</b></dt> 327 * <dd>Selection</dd> 328 * </dl> 329 * <p> 330 * Note: Only one of the styles DATE, TIME, or CALENDAR may be specified, 331 * and only one of the styles SHORT, MEDIUM, or LONG may be specified. 332 * </p><p> 333 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 334 * </p> 335 * 336 * @see <a href="http://www.eclipse.org/swt/snippets/#datetime">DateTime snippets</a> 337 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 338 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 339 * 340 * @since 3.3 341 */ 342 public class DateTime : Composite { 343 int day, month, year, hours, minutes, seconds; 344 345 static const int MIN_YEAR = 1752; // Gregorian switchover in North America: September 19, 1752 346 static const int MAX_YEAR = 9999; 347 348 /* Emulated DATE and TIME variables */ 349 Calendar calendar; 350 DateFormatSymbols formatSymbols; 351 Button down, up; 352 Text text; 353 String format; 354 Point[] fieldIndices; 355 int[] fieldNames; 356 int fieldCount, currentField = 0, characterCount = 0; 357 bool ignoreVerify = false; 358 static const String DEFAULT_SHORT_DATE_FORMAT = "MM/YYYY"; 359 static const String DEFAULT_MEDIUM_DATE_FORMAT = "MM/DD/YYYY"; 360 static const String DEFAULT_LONG_DATE_FORMAT = "MM/DD/YYYY"; 361 static const String DEFAULT_SHORT_TIME_FORMAT = "HH:MM AM"; 362 static const String DEFAULT_MEDIUM_TIME_FORMAT = "HH:MM:SS AM"; 363 static const String DEFAULT_LONG_TIME_FORMAT = "HH:MM:SS AM"; 364 365 366 367 /** 368 * Constructs a new instance of this class given its parent 369 * and a style value describing its behavior and appearance. 370 * <p> 371 * The style value is either one of the style constants defined in 372 * class <code>SWT</code> which is applicable to instances of this 373 * class, or must be built by <em>bitwise OR</em>'ing together 374 * (that is, using the <code>int</code> "|" operator) two or more 375 * of those <code>SWT</code> style constants. The class description 376 * lists the style constants that are applicable to the class. 377 * Style bits are also inherited from superclasses. 378 * </p> 379 * 380 * @param parent a composite control which will be the parent of the new instance (cannot be null) 381 * @param style the style of control to construct 382 * 383 * @exception IllegalArgumentException <ul> 384 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 385 * </ul> 386 * @exception SWTException <ul> 387 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 388 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 389 * </ul> 390 * 391 * @see SWT#DATE 392 * @see SWT#TIME 393 * @see SWT#CALENDAR 394 * @see Widget#checkSubclass 395 * @see Widget#getStyle 396 */ 397 public this (Composite parent, int style) { 398 super (parent, checkStyle (style)); 399 if ((this.style & SWT.CALENDAR) is 0) { 400 /* SWT.DATE and SWT.TIME */ 401 calendar = Calendar.getInstance(); 402 formatSymbols = new DateFormatSymbols(); 403 404 text = new Text(this, SWT.SINGLE); 405 /* disable the native drag and drop for the date/time text field */ 406 OS.gtk_drag_dest_unset(text.handle); 407 if ((this.style & SWT.DATE) !is 0) { 408 setFormat((this.style & SWT.SHORT) !is 0 ? DEFAULT_SHORT_DATE_FORMAT : (this.style & SWT.LONG) !is 0 ? DEFAULT_LONG_DATE_FORMAT : DEFAULT_MEDIUM_DATE_FORMAT); 409 } else { // SWT.TIME 410 setFormat((this.style & SWT.SHORT) !is 0 ? DEFAULT_SHORT_TIME_FORMAT : (this.style & SWT.LONG) !is 0 ? DEFAULT_LONG_TIME_FORMAT : DEFAULT_MEDIUM_TIME_FORMAT); 411 } 412 text.setText(getFormattedString(this.style)); 413 Listener listener = new class () Listener { 414 public void handleEvent(Event event) { 415 switch(event.type) { 416 case SWT.KeyDown: onKeyDown(event); break; 417 case SWT.FocusIn: onFocusIn(event); break; 418 case SWT.FocusOut: onFocusOut(event); break; 419 case SWT.MouseDown: onMouseClick(event); break; 420 case SWT.MouseUp: onMouseClick(event); break; 421 case SWT.Verify: onVerify(event); break; 422 default: 423 } 424 } 425 }; 426 text.addListener(SWT.KeyDown, listener); 427 text.addListener(SWT.FocusIn, listener); 428 text.addListener(SWT.FocusOut, listener); 429 text.addListener(SWT.MouseDown, listener); 430 text.addListener(SWT.MouseUp, listener); 431 text.addListener(SWT.Verify, listener); 432 up = new Button(this, SWT.ARROW | SWT.UP); 433 //up.setToolTipText(SWT.getMessage ("SWT_Up")); //$NON-NLS-1$ 434 down = new Button(this, SWT.ARROW | SWT.DOWN); 435 //down.setToolTipText(SWT.getMessage ("SWT_Down")); //$NON-NLS-1$ 436 up.addListener(SWT.Selection, new class() Listener { 437 public void handleEvent(Event event) { 438 incrementField(+1); 439 text.setFocus(); 440 } 441 }); 442 down.addListener(SWT.Selection, new class() Listener { 443 public void handleEvent(Event event) { 444 incrementField(-1); 445 text.setFocus(); 446 } 447 }); 448 addListener(SWT.Resize, new class() Listener { 449 public void handleEvent(Event event) { 450 onResize(event); 451 } 452 }); 453 } 454 } 455 456 static int checkStyle (int style) { 457 /* 458 * Even though it is legal to create this widget 459 * with scroll bars, they serve no useful purpose 460 * because they do not automatically scroll the 461 * widget's client area. The fix is to clear 462 * the SWT style. 463 */ 464 style &= ~(SWT.H_SCROLL | SWT.V_SCROLL); 465 style = checkBits (style, SWT.DATE, SWT.TIME, SWT.CALENDAR, 0, 0, 0); 466 return checkBits (style, SWT.MEDIUM, SWT.SHORT, SWT.LONG, 0, 0, 0); 467 } 468 469 /** 470 * Adds the listener to the collection of listeners who will 471 * be notified when the control is selected by the user, by sending 472 * it one of the messages defined in the <code>SelectionListener</code> 473 * interface. 474 * <p> 475 * <code>widgetSelected</code> is called when the user changes the control's value. 476 * <code>widgetDefaultSelected</code> is not called. 477 * </p> 478 * 479 * @param listener the listener which should be notified 480 * 481 * @exception IllegalArgumentException <ul> 482 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 483 * </ul> 484 * @exception SWTException <ul> 485 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 486 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 487 * </ul> 488 * 489 * @see SelectionListener 490 * @see #removeSelectionListener 491 * @see SelectionEvent 492 */ 493 public void addSelectionListener (SelectionListener listener) { 494 checkWidget (); 495 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 496 TypedListener typedListener = new TypedListener (listener); 497 addListener (SWT.Selection, typedListener); 498 addListener (SWT.DefaultSelection, typedListener); 499 } 500 501 override 502 protected void checkSubclass () { 503 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 504 } 505 506 override 507 public Point computeSize (int wHint, int hHint, bool changed) { 508 checkWidget (); 509 int width = 0, height = 0; 510 if (wHint is SWT.DEFAULT || hHint is SWT.DEFAULT) { 511 if ((style & SWT.CALENDAR) !is 0) { 512 // TODO: CALENDAR computeSize 513 width = 300; 514 height = 200; 515 } else { 516 /* SWT.DATE and SWT.TIME */ 517 GC gc = new GC(text); 518 Point textSize = gc.stringExtent(getComputeSizeString(style)); 519 gc.dispose(); 520 Rectangle trim = text.computeTrim(0, 0, textSize.x, textSize.y); 521 Point buttonSize = up.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed); 522 width = trim.width + buttonSize.x; 523 height = Math.max(trim.height, buttonSize.y); 524 } 525 } 526 if (width is 0) width = DEFAULT_WIDTH; 527 if (height is 0) height = DEFAULT_HEIGHT; 528 if (wHint !is SWT.DEFAULT) width = wHint; 529 if (hHint !is SWT.DEFAULT) height = hHint; 530 int border = getBorderWidth (); 531 width += border * 2; height += border * 2; 532 return new Point (width, height); 533 } 534 535 override 536 void createHandle (int index) { 537 if ((style & SWT.CALENDAR) !is 0) { 538 state |= HANDLE; 539 fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 540 if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES); 541 OS.gtk_fixed_set_has_window (fixedHandle, true); 542 handle = cast(GtkWidget*)OS.gtk_calendar_new (); 543 if (handle is null) error (SWT.ERROR_NO_HANDLES); 544 OS.gtk_container_add (fixedHandle, handle); 545 if (OS.GTK_VERSION >= OS.buildVERSION(2, 4, 0)) { 546 OS.gtk_calendar_set_display_options(handle, OS.GTK_CALENDAR_SHOW_HEADING | OS.GTK_CALENDAR_SHOW_DAY_NAMES); 547 } else { 548 OS.gtk_calendar_display_options(handle, OS.GTK_CALENDAR_SHOW_HEADING | OS.GTK_CALENDAR_SHOW_DAY_NAMES); 549 } 550 } else { 551 super.createHandle(index); 552 } 553 } 554 555 override 556 void createWidget (int index) { 557 super.createWidget (index); 558 if ((style & SWT.CALENDAR) !is 0) { 559 getDate(); 560 } 561 } 562 563 void commitCurrentField() { 564 if (characterCount > 0) { 565 characterCount = 0; 566 int fieldName = fieldNames[currentField]; 567 int start = fieldIndices[currentField].x; 568 int end = fieldIndices[currentField].y; 569 String value = text.getText(start, end - 1); 570 int s = value.lastIndexOf(' '); 571 if (s !is -1) value = value.substring(s + 1); 572 int newValue = unformattedIntValue(fieldName, value, characterCount is 0, calendar.getActualMaximum(fieldName)); 573 if (newValue !is -1) setTextField(fieldName, newValue, true, true); 574 } 575 } 576 577 String formattedStringValue(int fieldName, int value, bool adjust) { 578 if (fieldName is Calendar.AM_PM) { 579 return formatSymbols.getAmPmStrings()[value]; 580 } 581 if (adjust) { 582 if (fieldName is Calendar.HOUR && value is 0) { 583 return to!(String)(12); 584 } 585 if (fieldName is Calendar.MONTH) { 586 return to!(String)(value + 1); 587 } 588 } 589 return to!(String)(value); 590 } 591 592 String getComputeSizeString(int style) { 593 if ((style & SWT.DATE) !is 0) { 594 return (style & SWT.SHORT) !is 0 ? DEFAULT_SHORT_DATE_FORMAT : (style & SWT.LONG) !is 0 ? DEFAULT_LONG_DATE_FORMAT : DEFAULT_MEDIUM_DATE_FORMAT; 595 } 596 // SWT.TIME 597 return (style & SWT.SHORT) !is 0 ? DEFAULT_SHORT_TIME_FORMAT : (style & SWT.LONG) !is 0 ? DEFAULT_LONG_TIME_FORMAT : DEFAULT_MEDIUM_TIME_FORMAT; 598 } 599 600 int getFieldIndex(int fieldName) { 601 for (int i = 0; i < fieldCount; i++) { 602 if (fieldNames[i] is fieldName) { 603 return i; 604 } 605 } 606 return -1; 607 } 608 609 String getFormattedString(int style) { 610 if ((style & SWT.TIME) !is 0) { 611 auto ampm = formatSymbols.getAmPmStrings(); 612 int h = calendar.get(Calendar.HOUR); if (h is 0) h = 12; 613 int m = calendar.get(Calendar.MINUTE); 614 int s = calendar.get(Calendar.SECOND); 615 int a = calendar.get(Calendar.AM_PM); 616 if ((style & SWT.SHORT) !is 0) return "" ~ (h < 10 ? " " : "") ~ to!(String)(h) ~ ":" ~ (m < 10 ? "0" : "") ~ to!(String)(m) ~ " " ~ ampm[a]; 617 return "" ~ (h < 10 ? " " : "") ~ to!(String)(h) ~ ":" ~ (m < 10 ? "0" : "") ~ to!(String)(m) ~ ":" ~ (s < 10 ? "0" : "") ~ to!(String)(s) ~ " " ~ ampm[a]; 618 } 619 /* SWT.DATE */ 620 int y = calendar.get(Calendar.YEAR); 621 int m = calendar.get(Calendar.MONTH) + 1; 622 int d = calendar.get(Calendar.DAY_OF_MONTH); 623 if ((style & SWT.SHORT) !is 0) return "" ~ (m < 10 ? " " : "") ~ to!(String)(m) ~ "/" ~ to!(String)(y); 624 return "" ~ (m < 10 ? " " : "") ~ to!(String)(m) ~ "/" ~ (d < 10 ? " " : "") ~ to!(String)(d) ~ "/" ~ to!(String)(y); 625 } 626 627 void getDate() { 628 uint y; 629 uint m; 630 uint d; 631 OS.gtk_calendar_get_date(handle, &y, &m, &d); 632 year = y; 633 month = m; 634 day = d; 635 } 636 637 /** 638 * Returns the receiver's date, or day of the month. 639 * <p> 640 * The first day of the month is 1, and the last day depends on the month and year. 641 * </p> 642 * 643 * @return a positive integer beginning with 1 644 * 645 * @exception SWTException <ul> 646 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 647 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 648 * </ul> 649 */ 650 public int getDay () { 651 checkWidget (); 652 if ((style & SWT.CALENDAR) !is 0) { 653 getDate(); 654 return day; 655 } else { 656 return calendar.get(Calendar.DAY_OF_MONTH); 657 } 658 } 659 660 /** 661 * Returns the receiver's hours. 662 * <p> 663 * Hours is an integer between 0 and 23. 664 * </p> 665 * 666 * @return an integer between 0 and 23 667 * 668 * @exception SWTException <ul> 669 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 670 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 671 * </ul> 672 */ 673 public int getHours () { 674 checkWidget (); 675 if ((style & SWT.CALENDAR) !is 0) { 676 return hours; 677 } else { 678 return calendar.get(Calendar.HOUR_OF_DAY); 679 } 680 } 681 682 /** 683 * Returns the receiver's minutes. 684 * <p> 685 * Minutes is an integer between 0 and 59. 686 * </p> 687 * 688 * @return an integer between 0 and 59 689 * 690 * @exception SWTException <ul> 691 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 692 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 693 * </ul> 694 */ 695 public int getMinutes () { 696 checkWidget (); 697 if ((style & SWT.CALENDAR) !is 0) { 698 return minutes; 699 } else { 700 return calendar.get(Calendar.MINUTE); 701 } 702 } 703 704 /** 705 * Returns the receiver's month. 706 * <p> 707 * The first month of the year is 0, and the last month is 11. 708 * </p> 709 * 710 * @return an integer between 0 and 11 711 * 712 * @exception SWTException <ul> 713 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 714 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 715 * </ul> 716 */ 717 public int getMonth () { 718 checkWidget (); 719 if ((style & SWT.CALENDAR) !is 0) { 720 getDate(); 721 return month; 722 } else { 723 return calendar.get(Calendar.MONTH); 724 } 725 } 726 727 override 728 String getNameText() { 729 if((style & SWT.TIME) !is 0){ 730 return Format( "{}:{}:{}", getHours(), getMinutes(), getSeconds() ); 731 } 732 else{ 733 return Format( "{}/{}/{}", (getMonth() + 1), getDay(), getYear() ); 734 } 735 } 736 737 /** 738 * Returns the receiver's seconds. 739 * <p> 740 * Seconds is an integer between 0 and 59. 741 * </p> 742 * 743 * @return an integer between 0 and 59 744 * 745 * @exception SWTException <ul> 746 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 747 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 748 * </ul> 749 */ 750 public int getSeconds () { 751 checkWidget (); 752 if ((style & SWT.CALENDAR) !is 0) { 753 return seconds; 754 } else { 755 return calendar.get(Calendar.SECOND); 756 } 757 } 758 759 /** 760 * Returns the receiver's year. 761 * <p> 762 * The first year is 1752 and the last year is 9999. 763 * </p> 764 * 765 * @return an integer between 1752 and 9999 766 * 767 * @exception SWTException <ul> 768 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 769 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 770 * </ul> 771 */ 772 public int getYear () { 773 checkWidget (); 774 if ((style & SWT.CALENDAR) !is 0) { 775 getDate(); 776 return year; 777 } else { 778 return calendar.get(Calendar.YEAR); 779 } 780 } 781 782 override int gtk_day_selected (GtkWidget* widget) { 783 sendSelectionEvent (); 784 return 0; 785 } 786 787 override int gtk_month_changed (GtkWidget* widget) { 788 sendSelectionEvent (); 789 return 0; 790 } 791 792 override 793 void hookEvents () { 794 super.hookEvents(); 795 if ((style & SWT.CALENDAR) !is 0) { 796 OS.g_signal_connect_closure (handle, OS.day_selected.ptr, display.closures [DAY_SELECTED], false); 797 OS.g_signal_connect_closure (handle, OS.month_changed.ptr, display.closures [MONTH_CHANGED], false); 798 } 799 } 800 801 bool isValid(int fieldName, int value) { 802 Calendar validCalendar; 803 if ((style & SWT.CALENDAR) !is 0) { 804 validCalendar = Calendar.getInstance(); 805 validCalendar.set(Calendar.YEAR, year); 806 validCalendar.set(Calendar.MONTH, month); 807 } else { 808 validCalendar = calendar; 809 } 810 int min = validCalendar.getActualMinimum(fieldName); 811 int max = validCalendar.getActualMaximum(fieldName); 812 return value >= min && value <= max; 813 } 814 815 bool isValid(int year, int month, int day) { 816 Calendar valid = Calendar.getInstance(); 817 valid.set(year, month, day); 818 return valid.get(Calendar.YEAR) is year && valid.get(Calendar.MONTH) is month && valid.get(Calendar.DAY_OF_MONTH) is day; 819 } 820 821 void incrementField(int amount) { 822 int fieldName = fieldNames[currentField]; 823 int value = calendar.get(fieldName); 824 if (fieldName is Calendar.HOUR) { 825 int max = calendar.getMaximum(Calendar.HOUR); 826 int min = calendar.getMinimum(Calendar.HOUR); 827 if ((value is max && amount is 1) || (value is min && amount is -1)) { 828 int temp = currentField; 829 currentField = getFieldIndex(Calendar.AM_PM); 830 setTextField(Calendar.AM_PM, (calendar.get(Calendar.AM_PM) + 1) % 2, true, true); 831 currentField = temp; 832 } 833 } 834 setTextField(fieldName, value + amount, true, true); 835 } 836 837 void onKeyDown(Event event) { 838 int fieldName; 839 switch (event.keyCode) { 840 case SWT.ARROW_RIGHT: 841 case SWT.KEYPAD_DIVIDE: 842 // a right arrow or a valid separator navigates to the field on the right, with wraping 843 selectField((currentField + 1) % fieldCount); 844 break; 845 case SWT.ARROW_LEFT: 846 // navigate to the field on the left, with wrapping 847 int index = currentField - 1; 848 selectField(index < 0 ? fieldCount - 1 : index); 849 break; 850 case SWT.ARROW_UP: 851 case SWT.KEYPAD_ADD: 852 // set the value of the current field to value + 1, with wrapping 853 commitCurrentField(); 854 incrementField(+1); 855 break; 856 case SWT.ARROW_DOWN: 857 case SWT.KEYPAD_SUBTRACT: 858 // set the value of the current field to value - 1, with wrapping 859 commitCurrentField(); 860 incrementField(-1); 861 break; 862 case SWT.HOME: 863 // set the value of the current field to its minimum 864 fieldName = fieldNames[currentField]; 865 setTextField(fieldName, calendar.getActualMinimum(fieldName), true, true); 866 break; 867 case SWT.END: 868 // set the value of the current field to its maximum 869 fieldName = fieldNames[currentField]; 870 setTextField(fieldName, calendar.getActualMaximum(fieldName), true, true); 871 break; 872 default: 873 switch (event.character) { 874 case '/': 875 case ':': 876 case '-': 877 case '.': 878 // a valid separator navigates to the field on the right, with wraping 879 selectField((currentField + 1) % fieldCount); 880 break; 881 default: 882 } 883 } 884 } 885 886 void onFocusIn(Event event) { 887 selectField(currentField); 888 } 889 890 void onFocusOut(Event event) { 891 commitCurrentField(); 892 } 893 894 void onMouseClick(Event event) { 895 if (event.button !is 1) return; 896 Point sel = text.getSelection(); 897 for (int i = 0; i < fieldCount; i++) { 898 if (sel.x >= fieldIndices[i].x && sel.x <= fieldIndices[i].y) { 899 currentField = i; 900 break; 901 } 902 } 903 selectField(currentField); 904 } 905 906 void onResize(Event event) { 907 Rectangle rect = getClientArea (); 908 int width = rect.width; 909 int height = rect.height; 910 Point buttonSize = up.computeSize(SWT.DEFAULT, height); 911 int buttonHeight = buttonSize.y / 2; 912 text.setBounds(0, 0, width - buttonSize.x, height); 913 up.setBounds(width - buttonSize.x, 0, buttonSize.x, buttonHeight); 914 down.setBounds(width - buttonSize.x, buttonHeight, buttonSize.x, buttonHeight); 915 } 916 917 void onVerify(Event event) { 918 if (ignoreVerify) return; 919 event.doit = false; 920 int fieldName = fieldNames[currentField]; 921 int start = fieldIndices[currentField].x; 922 int end = fieldIndices[currentField].y; 923 int length_ = end - start; 924 String newText = event.text; 925 if (fieldName is Calendar.AM_PM) { 926 auto ampm = formatSymbols.getAmPmStrings(); 927 if (newText.equalsIgnoreCase(ampm[Calendar.AM].substring(0, 1)) || newText.equalsIgnoreCase(ampm[Calendar.AM])) { 928 setTextField(fieldName, Calendar.AM, true, false); 929 } else if (newText.equalsIgnoreCase(ampm[Calendar.PM].substring(0, 1)) || newText.equalsIgnoreCase(ampm[Calendar.PM])) { 930 setTextField(fieldName, Calendar.PM, true, false); 931 } 932 return; 933 } 934 if (characterCount > 0) { 935 try { 936 Integer.parseInt(newText); 937 } catch (NumberFormatException ex) { 938 return; 939 } 940 String value = text.getText(start, end - 1); 941 int s = value.lastIndexOf(' '); 942 if (s !is -1) value = value.substring(s + 1); 943 newText = value ~ newText; 944 } 945 ptrdiff_t newTextLength = newText.length; 946 bool first = characterCount is 0; 947 characterCount = (newTextLength < length_) ? cast(int)/*64bit*/newTextLength : 0; 948 int max = calendar.getActualMaximum(fieldName); 949 int min = calendar.getActualMinimum(fieldName); 950 int newValue = unformattedIntValue(fieldName, newText, characterCount is 0, max); 951 if (newValue is -1) { 952 characterCount = 0; 953 return; 954 } 955 if (first && newValue is 0 && length_ > 1) { 956 setTextField(fieldName, newValue, false, false); 957 } else if (min <= newValue && newValue <= max) { 958 setTextField(fieldName, newValue, characterCount is 0, characterCount is 0); 959 } else { 960 if (newTextLength >= length_) { 961 newText = newText.substring(cast(int)/*64bit*/(newTextLength - length_ + 1)); 962 newValue = unformattedIntValue(fieldName, newText, characterCount is 0, max); 963 if (newValue !is -1) { 964 characterCount = length_ - 1; 965 if (min <= newValue && newValue <= max) { 966 setTextField(fieldName, newValue, characterCount is 0, true); 967 } 968 } 969 } 970 } 971 } 972 973 override 974 void releaseWidget () { 975 super.releaseWidget(); 976 //TODO: need to do anything here? 977 } 978 979 /** 980 * Removes the listener from the collection of listeners who will 981 * be notified when the control is selected by the user. 982 * 983 * @param listener the listener which should no longer be notified 984 * 985 * @exception IllegalArgumentException <ul> 986 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 987 * </ul> 988 * @exception SWTException <ul> 989 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 990 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 991 * </ul> 992 * 993 * @see SelectionListener 994 * @see #addSelectionListener 995 */ 996 public void removeSelectionListener (SelectionListener listener) { 997 checkWidget (); 998 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 999 if (eventTable is null) return; 1000 eventTable.unhook (SWT.Selection, listener); 1001 eventTable.unhook (SWT.DefaultSelection, listener); 1002 } 1003 1004 void selectField(int index) { 1005 if (index !is currentField) { 1006 commitCurrentField(); 1007 } 1008 int start = fieldIndices[index].x; 1009 int end = fieldIndices[index].y; 1010 Point pt = text.getSelection(); 1011 if (index is currentField && start is pt.x && end is pt.y) return; 1012 currentField = index; 1013 display.asyncExec(new class( start, end ) Runnable { 1014 int start, end; 1015 this( int start, int end ){ 1016 this.start = start; this.end = end; 1017 } 1018 public void run() { 1019 if (!text.isDisposed()) { 1020 String value = text.getText(start, end - 1); 1021 int s = value.lastIndexOf(' '); 1022 if (s is -1 ) s = start; 1023 else s = start + s + 1; 1024 text.setSelection(s, end); 1025 } 1026 } 1027 }); 1028 } 1029 1030 void sendSelectionEvent () { 1031 uint y; 1032 uint m; 1033 uint d; 1034 OS.gtk_calendar_get_date(handle, &y, &m, &d); 1035 //TODO: hours, minutes, seconds? 1036 if (d !is day || 1037 m !is month || 1038 y !is year) { 1039 year = y; 1040 month = m; 1041 day = d; 1042 postEvent (SWT.Selection); 1043 } 1044 } 1045 1046 override 1047 public void setBackground(Color color) { 1048 checkWidget(); 1049 super.setBackground(color); 1050 if (text !is null) text.setBackground(color); 1051 } 1052 1053 override 1054 public void setFont(Font font) { 1055 checkWidget(); 1056 super.setFont(font); 1057 if (text !is null) text.setFont(font); 1058 redraw(); 1059 } 1060 1061 override 1062 public void setForeground(Color color) { 1063 checkWidget(); 1064 super.setForeground(color); 1065 if (text !is null) text.setForeground(color); 1066 } 1067 1068 /*public*/ void setFormat(String string) { 1069 checkWidget(); 1070 // TODO: this needs to be locale sensitive 1071 fieldCount = (style & SWT.DATE) !is 0 ? ((style & SWT.SHORT) !is 0 ? 2 : 3) : ((style & SWT.SHORT) !is 0 ? 3 : 4); 1072 fieldIndices = new Point[fieldCount]; 1073 fieldNames = new int[fieldCount]; 1074 if ((style & SWT.DATE) !is 0) { 1075 fieldNames[0] = Calendar.MONTH; 1076 fieldIndices[0] = new Point(0, 2); 1077 if ((style & SWT.SHORT) !is 0) { 1078 fieldNames[1] = Calendar.YEAR; 1079 fieldIndices[1] = new Point(3, 7); 1080 } else { 1081 fieldNames[1] = Calendar.DAY_OF_MONTH; 1082 fieldIndices[1] = new Point(3, 5); 1083 fieldNames[2] = Calendar.YEAR; 1084 fieldIndices[2] = new Point(6, 10); 1085 } 1086 } else { /* SWT.TIME */ 1087 fieldNames[0] = Calendar.HOUR; 1088 fieldIndices[0] = new Point(0, 2); 1089 fieldNames[1] = Calendar.MINUTE; 1090 fieldIndices[1] = new Point(3, 5); 1091 if ((style & SWT.SHORT) !is 0) { 1092 fieldNames[2] = Calendar.AM_PM; 1093 fieldIndices[2] = new Point(6, 8); 1094 } else { 1095 fieldNames[2] = Calendar.SECOND; 1096 fieldIndices[2] = new Point(6, 8); 1097 fieldNames[3] = Calendar.AM_PM; 1098 fieldIndices[3] = new Point(9, 11); 1099 } 1100 } 1101 } 1102 1103 void setField(int fieldName, int value) { 1104 if (calendar.get(fieldName) is value) return; 1105 if (fieldName is Calendar.AM_PM) { 1106 calendar.roll(Calendar.HOUR_OF_DAY, 12); // TODO: needs more work for setFormat and locale 1107 } 1108 calendar.set(fieldName, value); 1109 postEvent(SWT.Selection); 1110 } 1111 1112 void setTextField(int fieldName, int value, bool commit, bool adjust) { 1113 if (commit) { 1114 int max = calendar.getActualMaximum(fieldName); 1115 int min = calendar.getActualMinimum(fieldName); 1116 if (fieldName is Calendar.YEAR) { 1117 max = MAX_YEAR; 1118 min = MIN_YEAR; 1119 /* Special case: convert 1 or 2-digit years into reasonable 4-digit years. */ 1120 int currentYear = Calendar.getInstance().get(Calendar.YEAR); 1121 int currentCentury = (currentYear / 100) * 100; 1122 if (value < (currentYear + 30) % 100) value += currentCentury; 1123 else if (value < 100) value += currentCentury - 100; 1124 } 1125 if (value > max) value = min; // wrap 1126 if (value < min) value = max; // wrap 1127 } 1128 int start = fieldIndices[currentField].x; 1129 int end = fieldIndices[currentField].y; 1130 text.setSelection(start, end); 1131 String newValue = formattedStringValue(fieldName, value, adjust); 1132 StringBuffer buffer = new StringBuffer(newValue); 1133 /* Convert leading 0's into spaces. */ 1134 int prependCount = end - start - buffer.length(); 1135 for (int i = 0; i < prependCount; i++) { 1136 switch (fieldName) { 1137 case Calendar.MINUTE: 1138 case Calendar.SECOND: 1139 buffer.insert(0, 0); 1140 break; 1141 default: 1142 buffer.insert(0, ' '); 1143 break; 1144 } 1145 } 1146 newValue = buffer.toString(); 1147 ignoreVerify = true; 1148 text.insert(newValue); 1149 ignoreVerify = false; 1150 selectField(currentField); 1151 if (commit) setField(fieldName, value); 1152 } 1153 1154 /** 1155 * Sets the receiver's year, month, and day in a single operation. 1156 * <p> 1157 * This is the recommended way to set the date, because setting the year, 1158 * month, and day separately may result in invalid intermediate dates. 1159 * </p> 1160 * 1161 * @param year an integer between 1752 and 9999 1162 * @param month an integer between 0 and 11 1163 * @param day a positive integer beginning with 1 1164 * 1165 * @exception SWTException <ul> 1166 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1167 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1168 * </ul> 1169 * 1170 * @since 3.4 1171 */ 1172 public void setDate (int year, int month, int day) { 1173 checkWidget (); 1174 if (!isValid(year, month, day)) return; 1175 if ((style & SWT.CALENDAR) !is 0) { 1176 this.year = year; 1177 this.month = month; 1178 this.day = day; 1179 OS.gtk_calendar_select_month(handle, month, year); 1180 OS.gtk_calendar_select_day(handle, day); 1181 } else { 1182 calendar.set(Calendar.YEAR, year); 1183 calendar.set(Calendar.MONTH, month); 1184 calendar.set(Calendar.DAY_OF_MONTH, day); 1185 updateControl(); 1186 } 1187 } 1188 1189 /** 1190 * Sets the receiver's date, or day of the month, to the specified day. 1191 * <p> 1192 * The first day of the month is 1, and the last day depends on the month and year. 1193 * </p> 1194 * 1195 * @param day a positive integer beginning with 1 1196 * 1197 * @exception SWTException <ul> 1198 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1199 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1200 * </ul> 1201 */ 1202 public void setDay (int day) { 1203 checkWidget (); 1204 if (!isValid(Calendar.DAY_OF_MONTH, day)) return; 1205 if ((style & SWT.CALENDAR) !is 0) { 1206 this.day = day; 1207 OS.gtk_calendar_select_day(handle, day); 1208 } else { 1209 calendar.set(Calendar.DAY_OF_MONTH, day); 1210 updateControl(); 1211 } 1212 } 1213 1214 /** 1215 * Sets the receiver's hours. 1216 * <p> 1217 * Hours is an integer between 0 and 23. 1218 * </p> 1219 * 1220 * @param hours an integer between 0 and 23 1221 * 1222 * @exception SWTException <ul> 1223 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1224 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1225 * </ul> 1226 */ 1227 public void setHours (int hours) { 1228 checkWidget (); 1229 if (!isValid(Calendar.HOUR_OF_DAY, hours)) return; 1230 if ((style & SWT.CALENDAR) !is 0) { 1231 this.hours = hours; 1232 } else { 1233 calendar.set(Calendar.HOUR_OF_DAY, hours); 1234 updateControl(); 1235 } 1236 } 1237 1238 /** 1239 * Sets the receiver's minutes. 1240 * <p> 1241 * Minutes is an integer between 0 and 59. 1242 * </p> 1243 * 1244 * @param minutes an integer between 0 and 59 1245 * 1246 * @exception SWTException <ul> 1247 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1248 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1249 * </ul> 1250 */ 1251 public void setMinutes (int minutes) { 1252 checkWidget (); 1253 if (!isValid(Calendar.MINUTE, minutes)) return; 1254 if ((style & SWT.CALENDAR) !is 0) { 1255 this.minutes = minutes; 1256 } else { 1257 calendar.set(Calendar.MINUTE, minutes); 1258 updateControl(); 1259 } 1260 } 1261 1262 /** 1263 * Sets the receiver's month. 1264 * <p> 1265 * The first month of the year is 0, and the last month is 11. 1266 * </p> 1267 * 1268 * @param month an integer between 0 and 11 1269 * 1270 * @exception SWTException <ul> 1271 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1272 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1273 * </ul> 1274 */ 1275 public void setMonth (int month) { 1276 checkWidget (); 1277 if (!isValid(Calendar.MONTH, month)) return; 1278 if ((style & SWT.CALENDAR) !is 0) { 1279 this.month = month; 1280 OS.gtk_calendar_select_month(handle, month, year); 1281 } else { 1282 calendar.set(Calendar.MONTH, month); 1283 updateControl(); 1284 } 1285 } 1286 1287 /** 1288 * Sets the receiver's seconds. 1289 * <p> 1290 * Seconds is an integer between 0 and 59. 1291 * </p> 1292 * 1293 * @param seconds an integer between 0 and 59 1294 * 1295 * @exception SWTException <ul> 1296 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1297 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1298 * </ul> 1299 */ 1300 public void setSeconds (int seconds) { 1301 checkWidget (); 1302 if (!isValid(Calendar.SECOND, seconds)) return; 1303 if ((style & SWT.CALENDAR) !is 0) { 1304 this.seconds = seconds; 1305 } else { 1306 calendar.set(Calendar.SECOND, seconds); 1307 updateControl(); 1308 } 1309 } 1310 1311 /** 1312 * Sets the receiver's hours, minutes, and seconds in a single operation. 1313 * 1314 * @param hours an integer between 0 and 23 1315 * @param minutes an integer between 0 and 59 1316 * @param seconds an integer between 0 and 59 1317 * 1318 * @exception SWTException <ul> 1319 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1320 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1321 * </ul> 1322 * 1323 * @since 3.4 1324 */ 1325 public void setTime (int hours, int minutes, int seconds) { 1326 checkWidget (); 1327 if (!isValid(Calendar.HOUR_OF_DAY, hours)) return; 1328 if (!isValid(Calendar.MINUTE, minutes)) return; 1329 if (!isValid(Calendar.SECOND, seconds)) return; 1330 if ((style & SWT.CALENDAR) !is 0) { 1331 this.hours = hours; 1332 this.minutes = minutes; 1333 this.seconds = seconds; 1334 } else { 1335 calendar.set(Calendar.HOUR_OF_DAY, hours); 1336 calendar.set(Calendar.MINUTE, minutes); 1337 calendar.set(Calendar.SECOND, seconds); 1338 updateControl(); 1339 } 1340 } 1341 1342 /** 1343 * Sets the receiver's year. 1344 * <p> 1345 * The first year is 1752 and the last year is 9999. 1346 * </p> 1347 * 1348 * @param year an integer between 1752 and 9999 1349 * 1350 * @exception SWTException <ul> 1351 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1352 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1353 * </ul> 1354 */ 1355 public void setYear (int year) { 1356 checkWidget (); 1357 //if (!isValid(Calendar.YEAR, year)) return; 1358 if (year < MIN_YEAR || year > MAX_YEAR) return; 1359 if ((style & SWT.CALENDAR) !is 0) { 1360 this.year = year; 1361 OS.gtk_calendar_select_month(handle, month, year); 1362 } else { 1363 calendar.set(Calendar.YEAR, year); 1364 updateControl(); 1365 } 1366 } 1367 1368 int unformattedIntValue(int fieldName, String newText, bool adjust, int max) { 1369 int newValue; 1370 try { 1371 newValue = Integer.parseInt(newText); 1372 } catch (NumberFormatException ex) { 1373 return -1; 1374 } 1375 if (fieldName is Calendar.MONTH && adjust) { 1376 newValue--; 1377 if (newValue is -1) newValue = max; 1378 } 1379 if (fieldName is Calendar.HOUR && adjust) { 1380 if (newValue is 12) newValue = 0; // TODO: needs more work for setFormat and locale 1381 } 1382 return newValue; 1383 } 1384 1385 public void updateControl() { 1386 if (text !is null) { 1387 String string = getFormattedString(style); 1388 ignoreVerify = true; 1389 text.setText(string); 1390 ignoreVerify = false; 1391 } 1392 redraw(); 1393 } 1394 }