1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
  24  * All rights reserved.
  25  */
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.text.*;
  30 import java.util.*;
  31 
  32 /**
  33  * This class creates a dialog box that helps the user enter date and
  34  * time with mouse clicks.  The dialog box need only be created
  35  * once. The Ok and Cancel buttons merely call setVisible with an
  36  *  argument of false.
  37  */
  38 
  39 // The layout will consist of 3 panels: topPanel contains the
  40 // different labels and fields. middlePanel contains the buttons
  41 // midnight and now. bottomPanel contains the buttons ok, cancel and
  42 // help. The last two panels are separated by a LineSeparator.
  43 
  44 public class DateTimeDialog extends Dialog {
  45 
  46     private boolean save;
  47 
  48     private Frame parent;
  49 
  50     private DCPanel dateDCPanel;
  51     private DCPanel yearDCPanel;
  52     private DCPanel hourDCPanel;
  53     private DCPanel minuteDCPanel;
  54     private DCPanel secondDCPanel;
  55 
  56     private Choice month;
  57 
  58     private DCCircularTextField date;
  59     private DCCircularTextField hour;
  60     private DCCircularTextField second;
  61     private DCCircularTextField minute;
  62     private DCTextField year;
  63 
  64     private Button ok;
  65     private Button cancel;
  66     private Button help;
  67     private Button now;
  68     private Button midnight;
  69 
  70     private HelpDialog hd = null;
  71 
  72     private Panel topPanel;
  73     private Panel middlePanel;
  74     private Panel bottomPanel;
  75 
  76     private GregorianCalendar calendar = null;
  77     private static int MONTH_LEN[] = {31, 28, 31, 30, 31, 30, 31,
  78                                     31, 30, 31, 30, 31};
  79     private static DateFormat df =
  80     DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
  81                                  DateFormat.MEDIUM);
  82     private static Toolkit toolkit = Toolkit.getDefaultToolkit();
  83 
  84   // For I18N
  85     private static ResourceBundle rb =
  86     ResourceBundle.getBundle("GuiResource" /* NOI18N */);
  87     private static ResourceBundle hrb =
  88     ResourceBundle.getBundle("HelpData" /* NOI18N */);
  89 
  90     /**
  91      * Constructor that lays out the componeents and sets the different
  92      * event handlers.
  93      */
  94     public DateTimeDialog(Frame parent, Color background, Color foreground) {
  95     super(parent, getString("SEAM Date/Time Helper"), true);
  96 
  97     this.parent = parent;
  98 
  99     setLayout(new GridBagLayout());
 100     addLabels();
 101     addFields(background, foreground);
 102     addDCPanels();
 103     addButtons();
 104     addFocusListeners();
 105     setCurrentTime();
 106     setSize(250, 300);
 107     setResizable(false);
 108 
 109     addWindowListener(new DCWindowListener());
 110     //      initializeFocusOnTextField();
 111     }
 112 
 113     /**
 114      * Adds the labels only
 115      */
 116     private void addLabels() {
 117 
 118     GridBagConstraints gbc = new GridBagConstraints();
 119     gbc.weighty = 1;
 120 
 121     topPanel = new Panel();
 122     topPanel.setLayout(new GridBagLayout());
 123     gbc.gridwidth = GridBagConstraints.REMAINDER;
 124     gbc.fill = GridBagConstraints.BOTH;
 125     gbc.anchor = GridBagConstraints.CENTER;
 126     gbc.gridx = 0;
 127     gbc.gridy = 0;
 128     add(topPanel, gbc);
 129 
 130     gbc.fill = GridBagConstraints.NONE;
 131     gbc.anchor = GridBagConstraints.EAST;
 132     gbc.gridx = 0;
 133     gbc.gridwidth = 1;
 134 
 135     gbc.gridy = 0;
 136     topPanel.add(new Label(getString("Month")), gbc);
 137 
 138     gbc.gridy = 1;
 139     topPanel.add(new Label(getString("Date")), gbc);
 140 
 141     gbc.gridy = 2;
 142     topPanel.add(new Label(getString("Year")), gbc);
 143 
 144     gbc.gridy = 3;
 145     topPanel.add(new Label(getString("Hour")), gbc);
 146 
 147     gbc.gridy = 4;
 148     topPanel.add(new Label(getString("Minute")), gbc);
 149 
 150     gbc.gridy = 5;
 151     topPanel.add(new Label(getString("Second")), gbc);
 152     }
 153 
 154     /**
 155      * Adds the fields that will store the month, year, date, hour,
 156      * minute and second.
 157      */
 158     private void addFields(Color background, Color foreground) {
 159 
 160     GridBagConstraints gbc = new GridBagConstraints();
 161     gbc.weighty = 1;
 162 
 163     month = new Choice();
 164     initializeMonth();
 165 
 166     date = new DCCircularTextField("1", 2);
 167     date.setMinimum(1);
 168     date.setBackground(background);
 169     date.setForeground(foreground);
 170 
 171     hour = new DCCircularTextField("00", 2);
 172     hour.setMaximum(23);
 173     hour.setBackground(background);
 174     hour.setForeground(foreground);
 175     minute = new DCCircularTextField("00", 2);
 176     minute.setBackground(background);
 177     minute.setForeground(foreground);
 178     second = new DCCircularTextField("00", 2);
 179     second.setBackground(background);
 180     second.setForeground(foreground);
 181 
 182     year  = new DCTextField("2000", 4);
 183     year.setBackground(background);
 184     year.setForeground(foreground);
 185 
 186     Panel tempPanel = new Panel();
 187     tempPanel.add(month);
 188     gbc.gridwidth = GridBagConstraints.REMAINDER;
 189     gbc.fill = GridBagConstraints.HORIZONTAL;
 190     gbc.anchor = GridBagConstraints.WEST;
 191     gbc.gridx = 1;
 192     gbc.gridy = 0;
 193     topPanel.add(tempPanel, gbc);
 194 
 195 
 196     // Remaining fields are in topPanel
 197     gbc.gridwidth = 1;
 198     gbc.fill = GridBagConstraints.NONE;
 199     gbc.gridx = 1;
 200 
 201     gbc.gridy = 1;
 202     topPanel.add(date, gbc);
 203 
 204     gbc.gridy = 2;
 205     topPanel.add(year, gbc);
 206 
 207     gbc.gridy = 3;
 208     topPanel.add(hour, gbc);
 209 
 210     gbc.gridy = 4;
 211     topPanel.add(minute, gbc);
 212 
 213     gbc.gridy = 5;
 214     topPanel.add(second, gbc);
 215 
 216     }
 217 
 218   // Adds the panels with the +/- buttons for each DCField
 219     private void addDCPanels() {
 220 
 221     GridBagConstraints gbc = new GridBagConstraints();
 222     gbc.weighty = 1;
 223 
 224     gbc.gridx = 2;
 225     gbc.gridwidth = GridBagConstraints.REMAINDER;
 226     gbc.gridheight = 1;
 227     gbc.fill = GridBagConstraints.NONE;
 228 
 229     dateDCPanel = new DCPanel();
 230     yearDCPanel = new DCPanel();
 231     hourDCPanel = new DCPanel();
 232     minuteDCPanel = new DCPanel();
 233     secondDCPanel = new DCPanel();
 234 
 235     gbc.gridy = 1;
 236     topPanel.add(dateDCPanel, gbc);
 237 
 238     gbc.gridy = GridBagConstraints.RELATIVE;
 239     topPanel.add(yearDCPanel, gbc);
 240     topPanel.add(hourDCPanel, gbc);
 241     topPanel.add(minuteDCPanel, gbc);
 242     topPanel.add(secondDCPanel, gbc);
 243 
 244     dateDCPanel.setListener(date);
 245     yearDCPanel.setListener(year);
 246     hourDCPanel.setListener(hour);
 247     minuteDCPanel.setListener(minute);
 248     secondDCPanel.setListener(second);
 249 
 250     }
 251 
 252 
 253     /**
 254      * Sets the strings in the month pull-down menu. Also adds a listener
 255      * that will modify the maximum date allowed depending on the month.
 256      */
 257     private void initializeMonth() {
 258     DateFormatSymbols dfSymbols = new DateFormatSymbols();
 259     String[] monthStrings = dfSymbols.getMonths();
 260 
 261         month.removeAll();
 262 
 263         for (int i = 0; i < monthStrings.length; i++) {
 264         month.add(monthStrings[i]);
 265         }
 266 
 267         month.addItemListener(new DCMonthChangeListener());
 268     }
 269 
 270   // Adds all the buttons
 271     private void addButtons() {
 272 
 273         GridBagConstraints gbc = new GridBagConstraints();
 274         gbc.weighty = 1;
 275 
 276 
 277         middlePanel = new Panel();
 278         now  = new Button(getString("Now"));
 279         midnight        = new Button(getString("Midnight"));
 280         middlePanel.add(midnight);
 281         middlePanel.add(now);
 282         gbc.fill = GridBagConstraints.HORIZONTAL;
 283         gbc.gridwidth = GridBagConstraints.REMAINDER;
 284         gbc.gridx = 0;
 285         gbc.gridy = 1;
 286         add(middlePanel, gbc);
 287 
 288         gbc.gridwidth = GridBagConstraints.REMAINDER;
 289         gbc.gridheight = 1;
 290         gbc.fill = GridBagConstraints.BOTH;
 291         gbc.gridx = 0;
 292         gbc.gridy = 2;
 293         add(new LineSeparator(), gbc);
 294 
 295         bottomPanel = new Panel();
 296         ok = new Button(getString("OK"));
 297         cancel =        new Button(getString("Cancel"));
 298         help = new Button(getString("Help"));
 299         bottomPanel.add(ok);
 300         bottomPanel.add(cancel);
 301         bottomPanel.add(help);
 302         gbc.fill = GridBagConstraints.HORIZONTAL;
 303         gbc.gridwidth = GridBagConstraints.REMAINDER;
 304         gbc.gridx = 0;
 305         gbc.gridy = 3;
 306         add(bottomPanel, gbc);
 307 
 308         DCButtonListener bl = new DCButtonListener();
 309         ok.addActionListener(bl);
 310         cancel.addActionListener(bl);
 311         help.addActionListener(bl);
 312         now.addActionListener(bl);
 313         midnight.addActionListener(bl);
 314 
 315     }
 316 
 317     /**
 318      * Adds a listener to all the text fields so that when they go out
 319      * of focus (by tab or clicking), their values are checked for
 320      * errors.
 321      */
 322     private void addFocusListeners() {
 323     FocusListener fl = new DCFocusListener();
 324     date.addFocusListener(fl);
 325     year.addFocusListener(fl);
 326     hour.addFocusListener(fl);
 327     minute.addFocusListener(fl);
 328     second.addFocusListener(fl);
 329     }
 330 
 331     /**
 332      * Closes (hides) the dialog box when the user is done
 333      * @param save true if the box is being dismissed by clicking on
 334      * "ok" and the user wants to retain the modified value, false
 335      * otherwise.
 336      */
 337     private void dateTimeDialogClose(boolean save) {
 338         if (save == true) {
 339         if (!updateFromGui())
 340            return;
 341     }
 342     this.save = save;
 343     setVisible(false);
 344     }
 345 
 346     /**
 347      * Checks to see is all text fields contain valid values.
 348      * @return true if all are valid, false otherwise.
 349      */
 350     private boolean updateFromGui() {
 351         return (checkErrorAndSet(date) && checkErrorAndSet(year) &&
 352                 checkErrorAndSet(hour) && checkErrorAndSet(minute) &&
 353                 checkErrorAndSet(second));
 354     }
 355 
 356     /**
 357      * Checks the value stored as text in the field and sets its numeric
 358      * value to that if it is legitimate.
 359      * @return true if the value was legitimate and got set, false
 360      * otherwise.
 361      */
 362     private boolean checkErrorAndSet(DCTextField tf) {
 363         int i = 0;
 364         boolean errorState = false;
 365         try {
 366         i = new Integer(tf.getText().trim()).intValue();
 367         errorState = !tf.checkValue(i);
 368         } catch (NumberFormatException e2) {
 369         errorState =  true;
 370         }
 371         if (errorState) {
 372         tf.selectAll();
 373         toolkit.beep();
 374         }
 375         else
 376         tf.setValue(i);
 377         return !errorState;
 378     }
 379 
 380     /**
 381      * Checks if the user requested that the value in this
 382      * DateTimeDialog be used e.g., by clicking on "Ok" instead of
 383      * "Cancel."
 384      * @return true if the user wants to save the value in the
 385      * DateTimeDialog, false otherwise.
 386      */
 387 
 388     public boolean isSaved() {
 389         return save;
 390     }
 391 
 392     /**
 393      * Sets the date and time in fields to the current date and time.
 394      */
 395     public void setCurrentTime() {
 396         setDate(new Date());
 397     }
 398 
 399     /**
 400      * Sets the current date of the DateTimeDialog and updates the gui
 401      *   components to reflect that.
 402      * @param date the Date to set it to.
 403      */
 404     public void setDate(Date newDate) {
 405         calendar = new GregorianCalendar();
 406         calendar.setTime(newDate);
 407 
 408     // update gui components now
 409 
 410     year.setValue(calendar.get(Calendar.YEAR));
 411     month.select(calendar.get(Calendar.MONTH));
 412     date.setValue(calendar.get(Calendar.DATE));
 413 
 414     // Make sure the date is in the valid range for the given month
 415     fixDateField();
 416 
 417     hour.setValue(calendar.get(Calendar.HOUR_OF_DAY));
 418     minute.setValue(calendar.get(Calendar.MINUTE));
 419     second.setValue(calendar.get(Calendar.SECOND));
 420 
 421     }
 422 
 423     /**
 424      * Set the time fields to midnight, i.e., clears them.
 425      */
 426     private void setMidnight() {
 427             hour.setValue(0);
 428             minute.setValue(0);
 429             second.setValue(0);
 430     }
 431 
 432     /**
 433      * Make sure the date does not exceed the maximum allowable value
 434      * for the currently selected month.
 435      */
 436     private void fixDateField() {
 437         int monthIndex = month.getSelectedIndex();
 438         int max = MONTH_LEN[monthIndex];
 439         date.setMaximum(calendar.isLeapYear(year.getValue()) &&
 440                 monthIndex == 1 ? max + 1 : max);
 441     }
 442 
 443   // * **********************************************
 444   //     I N N E R    C L A S S E S   F O L L O W
 445   // ***********************************************
 446 
 447     /**
 448      * Listener for closing the dialog box through the window close
 449      * menu.
 450      */
 451     private class DCWindowListener extends WindowAdapter {
 452     public  void windowClosing(WindowEvent e) {
 453         dateTimeDialogClose(false);
 454         }
 455     }
 456 
 457     /**
 458      * Listener for any change in the month selected through the
 459      * pull down menu
 460      */
 461     private class DCMonthChangeListener implements ItemListener {
 462     public void itemStateChanged(ItemEvent e) {
 463         fixDateField();
 464     }
 465     }
 466 
 467     /**
 468      * Listener for all the buttons. The listener is shared for the sake
 469      * of reducing the number of overall listeners.
 470      * TBD: I18N the help
 471      */
 472     private class DCButtonListener implements ActionListener {
 473     public void actionPerformed(ActionEvent e) {
 474         if (e.getSource() == ok) {
 475         DateTimeDialog.this.dateTimeDialogClose(true);
 476         }
 477         else
 478         if (e.getSource() == cancel) {
 479           DateTimeDialog.this.dateTimeDialogClose(false);
 480         }
 481         else
 482           if (e.getSource() == now) {
 483             DateTimeDialog.this.setCurrentTime();
 484           }
 485           else
 486             if (e.getSource() == midnight) {
 487                 DateTimeDialog.this.setMidnight();
 488             }
 489             else
 490                 if (e.getSource() == help) {
 491                     if (hd != null)
 492                         hd.setVisible(true);
 493                 else {
 494                     hd = new
 495                     HelpDialog(DateTimeDialog.this.parent,
 496                         getString("Help for Date and Time Dialog"), false);
 497                     hd.setVisible(true);
 498                     hd.setText(getString(hrb, "DateTimeDialogHelp"));
 499                 }
 500             }
 501         } // actionPerformed
 502     }
 503 
 504     /**
 505      * Listener for any change in focus with respect to the text
 506      * fields. When a text field is going out of focus, it detemines if the
 507      * text value in it is valid. If not, it returns focus to that text
 508      * field.
 509      */
 510     private class DCFocusListener extends FocusAdapter {
 511 
 512         public void focusLost(FocusEvent e) {
 513         if (!checkErrorAndSet((DCTextField)e.getSource()))
 514           ((DCTextField)e.getSource()).requestFocus();
 515         }
 516     }
 517 
 518     /**
 519      * The string representation of the dialog box.
 520      * @return a String which contians the date and time in locale
 521      * default format, but to MEDIUM length formatting style.
 522      */
 523     public String toString() {
 524         calendar = new GregorianCalendar(year.getValue(),
 525                                         month.getSelectedIndex(),
 526                                         date.getValue(),
 527                                         hour.getValue(),
 528                                         minute.getValue(),
 529                                         second.getValue());
 530         return df.format(calendar.getTime());
 531     }
 532 
 533     /**
 534      * Call rb.getString(), but catch exception and return English
 535      * key so that small spelling errors don't cripple the GUI
 536      *
 537      */
 538     private static final String getString(String key) {
 539     return (getString(rb, key));
 540     }
 541 
 542     private static final String getString(ResourceBundle rb, String key) {
 543     try {
 544         String res = rb.getString(key);
 545         return res;
 546     } catch (MissingResourceException e) {
 547         System.out.println("Missing resource "+key+", using English.");
 548         return key;
 549         }
 550     }
 551 
 552     /* BEGIN JSTYLED */
 553     /*
 554     public static final void main(String args[]) {
 555     Frame f = new Frame();
 556     //  while (true){
 557         DateTimeDialog d = new DateTimeDialog(f, Color.white, Color.black);
 558         d.setVisible(true);
 559         System.out.println(d.toString());
 560       //    }
 561     }
 562     */
 563     /* END JSTYLED */
 564 }