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-2007, International Business Machines Corporation and    *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9
10package com.ibm.icu.dev.demo.calendar;
11
12import java.awt.BorderLayout;
13import java.awt.Button;
14import java.awt.Choice;
15import java.awt.Color;
16import java.awt.Component;
17import java.awt.Container;
18import java.awt.Dimension;
19import java.awt.FlowLayout;
20import java.awt.Font;
21import java.awt.FontMetrics;
22import java.awt.Frame;
23import java.awt.Graphics;
24import java.awt.GridBagConstraints;
25import java.awt.GridBagLayout;
26import java.awt.Label;
27import java.awt.Panel;
28import java.awt.Rectangle;
29import java.awt.event.ActionEvent;
30import java.awt.event.ActionListener;
31import java.awt.event.ItemEvent;
32import java.awt.event.ItemListener;
33import java.awt.event.WindowAdapter;
34import java.awt.event.WindowEvent;
35import java.util.Date;
36import java.util.Locale;
37
38import com.ibm.icu.dev.demo.impl.DemoApplet;
39import com.ibm.icu.dev.demo.impl.DemoUtility;
40import com.ibm.icu.text.DateFormat;
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.SimpleTimeZone;
48
49/**
50 * A Frame is a top-level window with a title. The default layout for a frame
51 * is BorderLayout.  The CalendarFrame class defines the window layout of
52 * CalendarDemo.
53 */
54class CalendarFrame extends Frame
55{
56    /**
57     * For serialization
58     */
59    private static final long serialVersionUID = -4289697663503820619L;
60
61    private static final boolean DEBUG = false;
62
63    private DemoApplet applet;
64
65    /**
66     * Constructs a new CalendarFrame that is initially invisible.
67     */
68    public CalendarFrame(DemoApplet myApplet)
69    {
70        super("Calendar Demo");
71        this.applet = myApplet;
72        init();
73
74        // When the window is closed, we want to shut down the applet or application
75        addWindowListener(
76            new WindowAdapter() {
77                public void windowClosing(WindowEvent e) {
78                    setVisible(false);
79                    dispose();
80
81                    if (applet != null) {
82                        applet.demoClosed();
83                    } else System.exit(0);
84                }
85            } );
86    }
87
88    private Choice          displayMenu;
89    private Locale[]        locales = DemoUtility.getG7Locales();
90
91    private Calendar        calendars[]   = new Calendar[2];
92    private Choice          calMenu[]     = new Choice[2];
93    private ColoredLabel    monthLabel[]  = new ColoredLabel[2];
94    private DateFormat      monthFormat[] = new DateFormat[2];
95
96    private Button          prevYear;
97    private Button          prevMonth;
98    private Button          gotoToday;
99    private Button          nextMonth;
100    private Button          nextYear;
101    private CalendarPanel   calendarPanel;
102
103    private static void add(Container container, Component component,
104                            GridBagLayout g, GridBagConstraints c,
105                            int gridwidth, int weightx)
106    {
107        c.gridwidth = gridwidth;
108        c.weightx = weightx;
109        g.setConstraints(component, c);
110        container.add(component);
111    }
112
113    /**
114     * Initializes the applet. You never need to call this directly, it
115     * is called automatically by the system once the applet is created.
116     */
117    public void init() {
118        setBackground(DemoUtility.bgColor);
119        setLayout(new BorderLayout(10,10));
120
121        Panel topPanel = new Panel();
122        GridBagLayout g = new GridBagLayout();
123        topPanel.setLayout(g);
124        GridBagConstraints c = new GridBagConstraints();
125        c.fill = GridBagConstraints.HORIZONTAL;
126
127        // Build the two menus for selecting which calendar is displayed,
128        // plus the month/year label for each calendar
129        for (int i = 0; i < 2; i++) {
130            calMenu[i] = new Choice();
131            for (int j = 0; j < CALENDARS.length; j++) {
132                calMenu[i].addItem(CALENDARS[j].name);
133            }
134            calMenu[i].setBackground(DemoUtility.choiceColor);
135            calMenu[i].select(i);
136            calMenu[i].addItemListener(new CalMenuListener());
137
138            // Label for the current month name
139            monthLabel[i] = new ColoredLabel("", COLORS[i]);
140            monthLabel[i].setFont(DemoUtility.titleFont);
141
142            // And the default calendar to use for this slot
143            calendars[i] = CALENDARS[i].calendar;
144
145            add(topPanel, calMenu[i], g, c, 5, 0);
146            add(topPanel, monthLabel[i], g, c, GridBagConstraints.REMAINDER, 1);
147        }
148
149        // Now add the next/previous year/month buttons:
150        prevYear = new Button("<<");
151        prevYear.addActionListener(new AddAction(Calendar.YEAR, -1));
152
153        prevMonth = new Button("<");
154        prevMonth.addActionListener(new AddAction(Calendar.MONTH, -1));
155
156        gotoToday = new Button("Today");
157        gotoToday.addActionListener( new ActionListener()
158        {
159            public void actionPerformed(ActionEvent e) {
160                calendarPanel.setDate( new Date() );
161                updateMonthName();
162            }
163        } );
164
165        nextMonth = new Button(">");
166        nextMonth.addActionListener(new AddAction(Calendar.MONTH, 1));
167
168        nextYear = new Button(">>");
169        nextYear.addActionListener(new AddAction(Calendar.YEAR, 1));
170
171        c.fill = GridBagConstraints.NONE;
172        add(topPanel, prevYear,  g, c, 1, 0);
173        add(topPanel, prevMonth, g, c, 1, 0);
174        add(topPanel, gotoToday, g, c, 1, 0);
175        add(topPanel, nextMonth, g, c, 1, 0);
176        add(topPanel, nextYear,  g, c, 1, 0);
177
178        // Now add the menu for selecting the display language
179        Panel displayPanel = new Panel();
180        {
181            displayMenu = new Choice();
182            Locale defaultLocale = Locale.getDefault();
183            int bestMatch = -1, thisMatch = -1;
184            int selectMe = 0;
185
186            for (int i = 0; i < locales.length; i++) {
187                if (i > 0 &&
188                        locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
189                    i < locales.length - 1 &&
190                        locales[i].getLanguage().equals(locales[i+1].getLanguage()))
191                {
192                    displayMenu.addItem( locales[i].getDisplayName() );
193                } else {
194                    displayMenu.addItem( locales[i].getDisplayLanguage());
195                }
196
197                thisMatch = DemoUtility.compareLocales(locales[i], defaultLocale);
198
199                if (thisMatch >= bestMatch) {
200                    bestMatch = thisMatch;
201                    selectMe = i;
202                }
203            }
204
205            displayMenu.setBackground(DemoUtility.choiceColor);
206            displayMenu.select(selectMe);
207
208            displayMenu.addItemListener( new ItemListener()
209            {
210                 public void itemStateChanged(ItemEvent e) {
211                    Locale loc = locales[displayMenu.getSelectedIndex()];
212                    calendarPanel.setLocale( loc );
213                    monthFormat[0] = monthFormat[1] = null;
214                    updateMonthName();
215                    repaint();
216                }
217            } );
218
219            Label l1 = new Label("Display Language:", Label.RIGHT);
220            l1.setFont(DemoUtility.labelFont);
221
222            displayPanel.setLayout(new FlowLayout());
223            displayPanel.add(l1);
224            displayPanel.add(displayMenu);
225
226        }
227        c.fill = GridBagConstraints.NONE;
228        c.anchor = GridBagConstraints.EAST;
229
230        add(topPanel, displayPanel, g, c, GridBagConstraints.REMAINDER, 0);
231
232        // The title, buttons, etc. go in a panel at the top of the window
233        add("North", topPanel);
234
235        // The copyright notice goes at the bottom of the window
236        Label copyright = new Label(DemoUtility.copyright1, Label.LEFT);
237        copyright.setFont(DemoUtility.creditFont);
238        add("South", copyright);
239
240        // Now create the big calendar panel and stick it in the middle
241        calendarPanel = new CalendarPanel( locales[displayMenu.getSelectedIndex()] );
242        add("Center", calendarPanel);
243
244        for (int i = 0; i < 2; i++) {
245            calendarPanel.setCalendar(i, calendars[i]);
246            calendarPanel.setColor(i, COLORS[i]);
247        }
248
249        updateMonthName();
250    }
251
252
253    private void updateMonthName()
254    {
255            for (int i = 0; i < 2; i++) {
256                try {
257                    if (monthFormat[i] == null) {     // TODO: optimize
258                        DateFormat f = DateFormat.getDateTimeInstance(
259                                                calendars[i], DateFormat.MEDIUM, -1,
260                                                locales[displayMenu.getSelectedIndex()]);
261                        if (f instanceof com.ibm.icu.text.SimpleDateFormat) {
262                            com.ibm.icu.text.SimpleDateFormat f1 = (com.ibm.icu.text.SimpleDateFormat) f;
263                            f1.applyPattern("MMMM, yyyy G");
264                            f1.setTimeZone(new SimpleTimeZone(0, "UTC"));
265                        }
266                        monthFormat[i] = f;
267                    }
268                } catch (ClassCastException e) {
269                    //hey {lw} - there's something wrong in this routine that cuases exceptions.
270                    System.out.println(e);
271                }
272
273                monthLabel[i].setText( monthFormat[i].format( calendarPanel.firstOfMonth() ));
274            }
275    }
276
277    /**
278     * CalMenuListener responds to events in the two popup menus that select
279     * the calendar systems to be used in the display.  It figures out which
280     * of the two menus the event occurred in and updates the corresponding
281     * element of the calendars[] array to match the new selection.
282     */
283    private class CalMenuListener implements ItemListener
284    {
285         public void itemStateChanged(ItemEvent e)
286         {
287            for (int i = 0; i < calMenu.length; i++)
288            {
289                if (e.getItemSelectable() == calMenu[i])
290                {
291                    // We found the menu that the event happened in.
292                    // Figure out which new calendar they selected.
293                    Calendar newCal = CALENDARS[ calMenu[i].getSelectedIndex() ].calendar;
294
295                    if (newCal != calendars[i])
296                    {
297                        // If any of the other menus are set to the same new calendar
298                        // we're about to use for this menu, set them to the current
299                        // calendar from *this* menu so we won't have two the same
300                        for (int j = 0; j < calendars.length; j++) {
301                            if (j != i && calendars[j] == newCal) {
302                                calendars[j] = calendars[i];
303                                calendarPanel.setCalendar(j, calendars[j]);
304                                monthFormat[j] = null;
305
306                                for (int k = 0; k < CALENDARS.length; k++) {
307                                    if (calendars[j] == CALENDARS[k].calendar) {
308                                        calMenu[j].select(k);
309                                        break;
310                                    }
311                                }
312                            }
313                        }
314                        // Now update this menu to use the new calendar the user selected
315                        calendars[i] = newCal;
316                        calendarPanel.setCalendar(i, newCal);
317                        monthFormat[i] = null;
318
319                        updateMonthName();
320                    }
321                    break;
322                }
323            }
324         }
325    }
326
327    /**
328     * AddAction handles the next/previous year/month buttons...
329     */
330    private class AddAction implements ActionListener {
331        AddAction(int field, int amount) {
332            this.field = field;
333            this.amount = amount;
334        }
335
336        public void actionPerformed(ActionEvent e) {
337            calendarPanel.add(field, amount);
338            updateMonthName();
339        }
340
341        private int field, amount;
342    }
343
344    /**
345     * ColoredLabel is similar to java.awt.Label, with two differences:
346     *
347     *  - You can set its text color
348     *
349     *  - It draws text using drawString rather than using a host-specific
350     *    "Peer" object like AWT does.  On 1.2, using drawString gives
351     *    us Bidi reordering for free.
352     */
353    static private class ColoredLabel extends Component {
354        /**
355         * For serialization
356         */
357        private static final long serialVersionUID = 5004484960341875722L;
358        public ColoredLabel(String label) {
359            text = label;
360        }
361
362        public ColoredLabel(String label, Color c) {
363            text = label;
364            color = c;
365        }
366
367        public void setText(String label) {
368            text = label;
369            repaint();
370        }
371
372        public void setFont(Font f) {
373            font = f;
374            repaint();
375        }
376
377        public void paint(Graphics g) {
378            FontMetrics fm = g.getFontMetrics(font);
379
380            Rectangle bounds = getBounds();
381
382            g.setColor(color);
383            g.setFont(font);
384            g.drawString(text, fm.stringWidth("\u00a0"),
385                         bounds.height/2 + fm.getHeight()
386                         - fm.getAscent() + fm.getLeading()/2);
387        }
388
389        public Dimension getPreferredSize() {
390            return getMinimumSize();
391        }
392
393        public Dimension getMinimumSize() {
394            FontMetrics fm = getFontMetrics(font);
395
396            return new Dimension( fm.stringWidth(text) + 2*fm.stringWidth("\u00a0"),
397                                  fm.getHeight() + fm.getLeading()*2);
398        }
399
400        String text;
401        Color color = Color.black;
402        Font font = DemoUtility.labelFont;
403    }
404
405    /**
406     * Print out the error message while debugging this program.
407     */
408    public void errorText(String s)
409    {
410        if (DEBUG)
411        {
412            System.out.println(s);
413        }
414    }
415
416    class CalendarRec {
417        public CalendarRec(String nameStr, Calendar cal)
418        {
419            name = nameStr;
420            calendar = cal;
421        }
422
423        Calendar  calendar;
424        String              name;
425    }
426
427    private final CalendarRec[] CALENDARS = {
428        new CalendarRec("Gregorian Calendar",       new GregorianCalendar()),
429        new CalendarRec("Hebrew Calendar",          new HebrewCalendar()),
430        new CalendarRec("Islamic Calendar",         makeIslamic(false)),
431        new CalendarRec("Islamic Civil Calendar ",  makeIslamic(true)),
432        new CalendarRec("Buddhist Calendar",        new BuddhistCalendar()),
433        new CalendarRec("Japanese Calendar",        new JapaneseCalendar()),
434    };
435
436    static private final Calendar makeIslamic(boolean civil) {
437        IslamicCalendar cal = new IslamicCalendar();
438        cal.setCivil(civil);
439        return cal;
440    }
441
442    static final Color[] COLORS = { Color.blue, Color.black };
443}
444
445