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