1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 1997-2008, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9
10package com.ibm.icu.dev.demo.calendar;
11
12import java.awt.Button;
13import java.awt.Checkbox;
14import java.awt.CheckboxGroup;
15import java.awt.Choice;
16import java.awt.Component;
17import java.awt.Container;
18import java.awt.FlowLayout;
19import java.awt.Font;
20import java.awt.Frame;
21import java.awt.GridLayout;
22import java.awt.Label;
23import java.awt.Panel;
24import java.awt.TextField;
25import java.awt.event.ActionEvent;
26import java.awt.event.ActionListener;
27import java.awt.event.ItemEvent;
28import java.awt.event.ItemListener;
29import java.awt.event.KeyEvent;
30import java.awt.event.WindowEvent;
31import java.text.ParsePosition;
32import java.util.Date;
33import java.util.Locale;
34
35import javax.swing.JTextField;
36
37import com.ibm.icu.dev.demo.impl.DemoApplet;
38import com.ibm.icu.dev.demo.impl.DemoUtility;
39import com.ibm.icu.text.DateFormat;
40import com.ibm.icu.text.SimpleDateFormat;
41import com.ibm.icu.util.BuddhistCalendar;
42import com.ibm.icu.util.Calendar;
43import com.ibm.icu.util.GregorianCalendar;
44import com.ibm.icu.util.HebrewCalendar;
45import com.ibm.icu.util.IslamicCalendar;
46import com.ibm.icu.util.JapaneseCalendar;
47import com.ibm.icu.util.TimeZone;
48
49/**
50 * CalendarCalc demonstrates how Date/Time formatter works.
51 */
52public class CalendarCalc extends DemoApplet
53{
54    /**
55     * For serialization
56     */
57    private static final long serialVersionUID = 4540103433916539296L;
58
59    /**
60     * The main function which defines the behavior of the MultiCalendarDemo
61     * applet when an applet is started.
62     */
63    public static void main(String argv[]) {
64        new CalendarCalc().showDemo();
65    }
66
67    /**
68     * This creates a CalendarCalcFrame for the demo applet.
69     */
70    public Frame createDemoFrame(DemoApplet applet) {
71        return new CalendarCalcFrame(applet);
72    }
73}
74
75/**
76 * A Frame is a top-level window with a title. The default layout for a frame
77 * is BorderLayout.  The CalendarCalcFrame class defines the window layout of
78 * MultiCalendarDemo.
79 */
80class CalendarCalcFrame extends Frame implements ActionListener
81{
82    /**
83     * For serialization
84     */
85    private static final long serialVersionUID = 8901485296258761846L;
86
87    static final Locale[] locales = DemoUtility.getG7Locales();
88
89    private DemoApplet              applet;
90    private long                    time = System.currentTimeMillis();
91
92    private static final RollAddField kRollAddFields[] = {
93        new RollAddField(Calendar.YEAR,                 "Year" ),
94        new RollAddField(Calendar.MONTH,                "Month" ),
95        new RollAddField(Calendar.WEEK_OF_MONTH,        "Week of Month" ),
96        new RollAddField(Calendar.WEEK_OF_YEAR,         "Week of Year" ),
97        new RollAddField(Calendar.DAY_OF_MONTH,         "Day of Month" ),
98        new RollAddField(Calendar.DAY_OF_WEEK,          "Day of Week" ),
99        new RollAddField(Calendar.DAY_OF_WEEK_IN_MONTH, "Day of Week in Month" ),
100        new RollAddField(Calendar.DAY_OF_YEAR,          "Day of Year" ),
101        new RollAddField(Calendar.AM_PM,                "AM/PM" ),
102        new RollAddField(Calendar.HOUR_OF_DAY,          "Hour of day" ),
103        new RollAddField(Calendar.HOUR,                 "Hour" ),
104        new RollAddField(Calendar.MINUTE,               "Minute" ),
105        new RollAddField(Calendar.SECOND,               "Second" ),
106    };
107
108    /**
109     * Constructs a new CalendarCalcFrame that is initially invisible.
110     */
111    public CalendarCalcFrame(DemoApplet applet)
112    {
113        super("Multiple Calendar Demo");
114        this.applet = applet;
115        init();
116        start();
117    }
118
119    /**
120     * Initializes the applet. You never need to call this directly, it
121     * is called automatically by the system once the applet is created.
122     */
123    public void init()
124    {
125        buildGUI();
126
127        patternText.setText( calendars[0].toPattern() );
128
129        // Force an update of the display
130        cityChanged();
131        millisFormat();
132        enableEvents(KeyEvent.KEY_RELEASED);
133        enableEvents(WindowEvent.WINDOW_CLOSING);
134    }
135
136    //------------------------------------------------------------
137    // package private
138    //------------------------------------------------------------
139    void addWithFont(Container container, Component foo, Font font) {
140        if (font != null)
141            foo.setFont(font);
142        container.add(foo);
143    }
144
145    /**
146     * Called to start the applet. You never need to call this method
147     * directly, it is called when the applet's document is visited.
148     */
149    public void start()
150    {
151        // do nothing
152    }
153
154    TextField patternText;
155
156    Choice dateMenu;
157    Choice localeMenu;
158
159    Button up;
160    Button down;
161
162    Checkbox getRoll;
163    Checkbox getAdd;
164
165    public void buildGUI()
166    {
167        setBackground(DemoUtility.bgColor);
168        setLayout(new FlowLayout()); // shouldn't be necessary, but it is.
169
170// TITLE
171        Label label1=new Label("Calendar Converter", Label.CENTER);
172        label1.setFont(DemoUtility.titleFont);
173        add(label1);
174        add(DemoUtility.createSpacer());
175
176// IO Panel
177        Panel topPanel = new Panel();
178        topPanel.setLayout(new FlowLayout());
179
180        CheckboxGroup group1= new CheckboxGroup();
181
182        // Set up the controls for each calendar we're demonstrating
183        for (int i = 0; i < calendars.length; i++)
184        {
185            Label label = new Label(calendars[i].name, Label.RIGHT);
186            label.setFont(DemoUtility.labelFont);
187            topPanel.add(label);
188
189            topPanel.add(calendars[i].text);
190
191            final int j = i;
192            calendars[i].text.addActionListener( new ActionListener() {
193                public void actionPerformed(ActionEvent e) {
194                    textChanged(j);
195                }
196            } );
197
198            calendars[i].rollAdd.setCheckboxGroup(group1);
199            topPanel.add(calendars[i].rollAdd);
200        }
201        calendars[0].rollAdd.setState(true);    // Make the first one selected
202
203        Label label4=new Label("Pattern", Label.RIGHT);
204        label4.setFont(DemoUtility.labelFont);
205        topPanel.add(label4);
206
207        patternText=new TextField(FIELD_COLUMNS);
208        patternText.setFont(DemoUtility.editFont);
209        topPanel.add(patternText);
210        topPanel.add(new Label(""));
211
212        DemoUtility.fixGrid(topPanel,3);
213        add(topPanel);
214        add(DemoUtility.createSpacer());
215
216// ROLL / ADD
217        Panel rollAddPanel=new Panel();
218        {
219            rollAddPanel.setLayout(new FlowLayout());
220
221            Panel rollAddBoxes = new Panel();
222            {
223                rollAddBoxes.setLayout(new GridLayout(2,1));
224                CheckboxGroup group2= new CheckboxGroup();
225                getRoll = new Checkbox("Roll",group2, false);
226                getAdd = new Checkbox("Add",group2, true);
227
228                rollAddBoxes.add(getRoll);
229                rollAddBoxes.add(getAdd);
230            }
231
232            Label dateLabel=new Label("Date Fields");
233            dateLabel.setFont(DemoUtility.labelFont);
234
235            dateMenu= new Choice();
236            dateMenu.setBackground(DemoUtility.choiceColor);
237            for (int i = 0; i < kRollAddFields.length; i++) {
238                dateMenu.addItem(kRollAddFields[i].name);
239                if (kRollAddFields[i].field == Calendar.MONTH) {
240                    dateMenu.select(i);
241                }
242            }
243
244            Panel upDown = new Panel();
245            {
246                upDown.setLayout(new GridLayout(2,1));
247
248                // *** If the images are not found, we use the label.
249                up = new Button("^");
250                down = new Button("v");
251                up.setBackground(DemoUtility.bgColor);
252                down.setBackground(DemoUtility.bgColor);
253                upDown.add(up);
254                upDown.add(down);
255                up.addActionListener(this);
256                down.addActionListener(this);
257            }
258
259            rollAddPanel.add(dateLabel);
260            rollAddPanel.add(dateMenu);
261            rollAddPanel.add(rollAddBoxes);
262            rollAddPanel.add(upDown);
263
264        }
265        Panel localePanel = new Panel();
266        {
267            // Make the locale popup menus
268            localeMenu= new Choice();
269            Locale defaultLocale = Locale.getDefault();
270            int bestMatch = -1, thisMatch = -1;
271            int selectMe = 0;
272
273            for (int i = 0; i < locales.length; i++) {
274                if (i > 0 && locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
275                    i < locales.length - 1 &&
276                        locales[i].getLanguage().equals(locales[i+1].getLanguage()))
277                {
278                    localeMenu.addItem( locales[i].getDisplayName() );
279                } else {
280                    localeMenu.addItem( locales[i].getDisplayLanguage());
281                }
282
283                thisMatch = DemoUtility.compareLocales(locales[i], defaultLocale);
284
285                if (thisMatch >= bestMatch) {
286                    bestMatch = thisMatch;
287                    selectMe = i;
288                }
289            }
290
291            localeMenu.setBackground(DemoUtility.choiceColor);
292            localeMenu.select(selectMe);
293
294            Label localeLabel =new Label("Display Locale");
295            localeLabel.setFont(DemoUtility.labelFont);
296
297            localePanel.add(localeLabel);
298            localePanel.add(localeMenu);
299            DemoUtility.fixGrid(localePanel,2);
300
301            localeMenu.addItemListener( new ItemListener() {
302                public void itemStateChanged(ItemEvent e) {
303                    Locale loc = locales[localeMenu.getSelectedIndex()];
304                    System.out.println("Change locale to " + loc.getDisplayName());
305
306                    for (int i = 0; i < calendars.length; i++) {
307                        calendars[i].setLocale(loc);
308                    }
309                    millisFormat();
310                }
311            } );
312        }
313        add(rollAddPanel);
314        add(DemoUtility.createSpacer());
315        add(localePanel);
316        add(DemoUtility.createSpacer());
317
318// COPYRIGHT
319        Panel copyrightPanel = new Panel();
320        addWithFont (copyrightPanel,new Label(DemoUtility.copyright1, Label.LEFT),
321            DemoUtility.creditFont);
322        DemoUtility.fixGrid(copyrightPanel,1);
323        add(copyrightPanel);
324    }
325
326    /**
327     * This function is called when users change the pattern text.
328     */
329    public void setFormatFromPattern() {
330        String timePattern = patternText.getText();
331
332        for (int i = 0; i < calendars.length; i++) {
333            calendars[i].applyPattern(timePattern);
334        }
335
336        millisFormat();
337    }
338
339    /**
340     * This function is called when it is necessary to parse the time
341     * string in one of the formatted date fields
342     */
343    public void textChanged(int index) {
344        String rightString = calendars[index].text.getText();
345
346        ParsePosition status = new ParsePosition(0);
347
348        if (rightString.length() == 0)
349        {
350            errorText("Error: no input to parse!");
351            return;
352        }
353
354        try {
355            Date date = calendars[index].format.parse(rightString, status);
356            time = date.getTime();
357        }
358        catch (Exception e) {
359            for (int i = 0; i < calendars.length; i++) {
360                if (i != index) {
361                    calendars[i].text.setText("ERROR");
362                }
363            }
364            errorText("Exception: " + e.getClass().toString() + " parsing: "+rightString);
365            return;
366        }
367
368        int start = calendars[index].text.getSelectionStart();
369        int end = calendars[index].text.getSelectionEnd();
370
371        millisFormat();
372
373        calendars[index].text.select(start,end);
374    }
375
376    /**
377     * This function is called when it is necessary to format the time
378     * in the "Millis" text field.
379     */
380    public void millisFormat() {
381        String out = "";
382
383        for (int i = 0; i < calendars.length; i++) {
384            try {
385                out = calendars[i].format.format(new Date(time));
386                calendars[i].text.setText(out);
387            }
388            catch (Exception e) {
389                calendars[i].text.setText("ERROR");
390                errorText("Exception: " + e.getClass().toString() + " formatting "
391                            + calendars[i].name + " " + time);
392            }
393        }
394    }
395
396
397    /**
398     * This function is called when users change the pattern text.
399     */
400    public void patternTextChanged() {
401        setFormatFromPattern();
402    }
403
404    /**
405     * This function is called when users select a new representative city.
406     */
407    public void cityChanged() {
408        TimeZone timeZone = TimeZone.getDefault();
409
410        for (int i = 0; i < calendars.length; i++) {
411            calendars[i].format.setTimeZone(timeZone);
412        }
413        millisFormat();
414    }
415
416    /**
417     * This function is called when users select a new time field
418     * to add or roll its value.
419     */
420    public void dateFieldChanged(boolean isUp) {
421        int field = kRollAddFields[dateMenu.getSelectedIndex()].field;
422
423        for (int i = 0; i < calendars.length; i++)
424        {
425            if (calendars[i].rollAdd.getState())
426            {
427                Calendar c = calendars[i].calendar;
428                c.setTime(new Date(time));
429
430                if (getAdd.getState()) {
431                    c.add(field, isUp ? 1 : -1);
432                } else {
433                    c.roll(field, isUp);
434                }
435
436                time = c.getTime().getTime();
437                millisFormat();
438                break;
439            }
440        }
441    }
442
443    /**
444     * Print out the error message while debugging this program.
445     */
446    public void errorText(String s)
447    {
448        if (true) {
449            System.out.println(s);
450        }
451    }
452
453    /**
454     * Called if an action occurs in the CalendarCalcFrame object.
455     */
456    public void actionPerformed(ActionEvent evt)
457    {
458        // *** Button events are handled here.
459        Object obj = evt.getSource();
460        System.out.println("action " + obj);
461        if (obj instanceof Button) {
462            if (evt.getSource() == up) {
463                dateFieldChanged(false);
464            } else
465                if (evt.getSource() == down) {
466                    dateFieldChanged(true);
467            }
468        }
469    }
470
471    /**
472     * Handles the event. Returns true if the event is handled and should not
473     * be passed to the parent of this component. The default event handler
474     * calls some helper methods to make life easier on the programmer.
475     */
476    protected void processKeyEvent(KeyEvent evt)
477    {
478        System.out.println("key " + evt);
479        if (evt.getID() == KeyEvent.KEY_RELEASED) {
480            if (evt.getSource() == patternText) {
481                patternTextChanged();
482            }
483            else {
484                for (int i = 0; i < calendars.length; i++) {
485                    if (evt.getSource() == calendars[i].text) {
486                        textChanged(i);
487                    }
488                }
489            }
490        }
491    }
492
493    protected void processWindowEvent(WindowEvent evt)
494    {
495        System.out.println("window " + evt);
496        if (evt.getID() == WindowEvent.WINDOW_CLOSING &&
497            evt.getSource() == this) {
498            this.hide();
499            this.dispose();
500
501            if (applet != null) {
502               applet.demoClosed();
503            } else System.exit(0);
504        }
505    }
506
507    /*
508    protected void processEvent(AWTEvent evt)
509    {
510        if (evt.getID() == AWTEvent. Event.ACTION_EVENT && evt.target == up) {
511            dateFieldChanged(true);
512            return true;
513        }
514        else if (evt.id == Event.ACTION_EVENT && evt.target == down) {
515            dateFieldChanged(false);
516            return true;
517        }
518    }
519    */
520
521    private static final int        FIELD_COLUMNS = 35;
522
523
524    class CalendarRec {
525        public CalendarRec(String nameStr, Calendar cal)
526        {
527            name = nameStr;
528            calendar = cal;
529            rollAdd = new Checkbox();
530
531            text = new JTextField("",FIELD_COLUMNS);
532            text.setFont(DemoUtility.editFont);
533
534            format = DateFormat.getDateInstance(cal, DateFormat.FULL,
535                                                Locale.getDefault());
536            //format.applyPattern(DEFAULT_FORMAT);
537        }
538
539        public void setLocale(Locale loc) {
540            String pattern = toPattern();
541
542            format = DateFormat.getDateInstance(calendar, DateFormat.FULL,
543                                                loc);
544            applyPattern(pattern);
545        }
546
547        public void applyPattern(String pattern) {
548            if (format instanceof SimpleDateFormat) {
549                ((SimpleDateFormat)format).applyPattern(pattern);
550//hey {al} -
551//            } else if (format instanceof java.text.SimpleDateFormat) {
552//                ((java.text.SimpleDateFormat)format).applyPattern(pattern);
553            }
554        }
555
556        private String toPattern() {
557            if (format instanceof SimpleDateFormat) {
558                return ((SimpleDateFormat)format).toPattern();
559//hey {al} -
560//            } else if (format instanceof java.text.SimpleDateFormat) {
561//                return ((java.text.SimpleDateFormat)format).toPattern();
562            }
563            return "";
564        }
565
566        Calendar  calendar;
567        DateFormat          format;
568        String              name;
569        JTextField           text;
570        Checkbox            rollAdd;
571    }
572
573    private final CalendarRec[] calendars = {
574        new CalendarRec("Gregorian",        new GregorianCalendar()),
575        new CalendarRec("Hebrew",           new HebrewCalendar()),
576        new CalendarRec("Islamic (civil)",  makeIslamic(true)),
577        new CalendarRec("Islamic (true)",   makeIslamic(false)),
578        new CalendarRec("Buddhist",         new BuddhistCalendar()),
579        new CalendarRec("Japanese",         new JapaneseCalendar()),
580//        new CalendarRec("Chinese",          new ChineseCalendar()),
581    };
582
583    static private final Calendar makeIslamic(boolean civil) {
584        IslamicCalendar cal = new IslamicCalendar();
585        cal.setCivil(civil);
586        return cal;
587    }
588}
589
590class RollAddField {
591    RollAddField(int field, String name) {
592        this.field = field;
593        this.name = name;
594    }
595    int field;
596    String name;
597}
598