ca.aeso.evq.client.widgets.DatePicker.java Source code

Java tutorial

Introduction

Here is the source code for ca.aeso.evq.client.widgets.DatePicker.java

Source

/*
 * Copyright 2007 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package ca.aeso.evq.client.widgets;

import java.util.Date;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ChangeListenerCollection;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * DatePicker widget displays a small Gregorian calendar dates to select
 * a date by the user.
 *  
 * <p>It has following features:
 * 
 * <ul>
 * <li>Fully internationalized by default locale</li>
 * <li>Optional display of dates form the adjacent dates months</li>
 * <li>Add a special formatting for a given day</li>
 * <li>Select any date as the start date and month.
 *  Today's date is the default selection and the default displayed month.</li>
 * </ul>
 * </p>
 * <p>CSS hooks:
  <table border="1" bordercolor="#000000" cellpadding="3" cellspacing="0">
<tbody>
<tr>
  <td style="FONT-WEIGHT:bold">
    Style name<br/>
  </td>
  <td style="FONT-WEIGHT:bold">
    Widget region affected<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker<br/>
  </td>
  <td>
    Entire widget<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .grid<br/>
  </td>
  <td>
    Grid in the DatePicker.
    This includes week names and the date numbers.<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .title<br/>
  </td>
  <td>
    Month and Year titles on the top<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .control<br/>
  </td>
  <td>
    Month and year increment and decrement buttons<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .control-menu<br/>
  </td>
  <td>
    Month and year list available by clicking on them<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .control-block<br/>
  </td>
  <td>
    The block of month/year display with its
    increment decrement controls<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .control-pane<br/>
  </td>
  <td>
    Top area containing month and year controls<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .control-today<br/>
  </td>
  <td>
    Clickable today button<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .weekday<br/>
  </td>
  <td>
    Any date<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .week-names<br/>
  </td>
  <td>
    Weekday names<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .week-numbers<br/>
  </td>
  <td>
    Week of the year number<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .weekend-start<br/>
  </td>
  <td>
    Weekend startdate<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .weekend-end<br/>
  </td>
  <td>
    Weekend end date<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .today<br/>
  </td>
  <td>
    Special formatting for today
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .selected<br/>
  </td>
  <td>
    Special formatting for the selected date<br/>
  </td>
</tr>
<tr>
  <td>
    .goog-date-picker .other-month
  </td>
  <td>
    Opacity reducing formatting for the adjacent months<br/>
  </td>
</tr>
</tbody>
  </table> 
  </p>
 * <p>Following are the incomplete features.
 * Some components of these features exist.
 * However, they do not function completely.
 * <ul>
 * <li>Optional display week numbers</li>
 * </ul>
 * </p>
 * <p>Desired features
 * <ul>
 * <li>Disable calendar functions beyond an end date.
 * This is useful when document archives. Default today.</li>
 * <li>Disable calendar functions beyond an start date.
 * This is useful for reservation systems. Default today.</li>
 * <li>Disable calendar functions on a specific date.
 * This is useful for reservation systems.</li>
 * <li>Right to Left layout depending on the page layout.</li>
 * </ul>
 * </p>
 */

public class DatePicker extends DialogBox implements ClickListener, ChangeListener, SourcesChangeEvents {

    private static final String STYLE_DATE_PICKER = "goog-date-picker";
    private static final String STYLE_GRID = "grid";

    private static final String STYLE_CONTROL = "control";
    private static final String STYLE_CONTROL_BLOCK = "control-block";
    private static final String STYLE_CONTROL_PANE = "control-pane";
    private static final String STYLE_CONTROL_MENU = "control-menu";
    private static final String STYLE_CONTROL_TODAY = "control-today";
    private static final String STYLE_TITLE = "title";

    private static final String STYLE_WEEK_NAMES = "week-names";
    private static final String STYLE_WEEK_NUMBERS = "numbers";

    private static final String STYLE_WEEKDAY = "weekday";
    private static final String STYLE_WEEKEND_START = "weekend-start";
    private static final String STYLE_WEEKEND_END = "weekend-end";
    private static final String STYLE_OTHER_MONTHS = "other-month";

    private static final String STYLE_TODAY = "today";
    private static final String STYLE_SELECTED = "selected";

    // Unique numbers representing various actions available in the controls.
    private static final int ACTION_PREV_MONTH = 0;
    private static final int ACTION_SET_MONTH = 1;
    private static final int ACTION_NEXT_MONTH = 2;
    private static final int ACTION_PREV_YEAR = 3;
    private static final int ACTION_SET_YEAR = 4;
    private static final int ACTION_NEXT_YEAR = 5;
    private static final int ACTION_TODAY = 6;
    private static final int ACTION_CLOSE = 7;

    // Unique numbers representing formating actions.
    private static final int FORMAT_ACTION_REMOVE = 0;
    private static final int FORMAT_ACTION_ADD = 1;

    private LocaleCalendarUtils dateTable;

    private ChangeListenerCollection changeListeners = new ChangeListenerCollection();

    private HorizontalPanel control = new HorizontalPanel();
    private FlexTable grid;

    // menu based action

    private int weekOfYearOffset = 0;
    private boolean showYearMonthListing = true;
    private boolean showTodayButton = false;
    private int monthAction;
    private int yearAction;

    private DatePickerCell today;
    private final VerticalPanel panel = new VerticalPanel();

    private int prevMonthSize;
    private int prevMonthDays;
    private int currMonthSize;
    private int nextMonthDays;
    private int gridStart;

    /**
     * Constructor of the DatePicker class.
     * 
     */
    public DatePicker() {

        super(true); // hide dialogue box when clicked outside

        dateTable = new LocaleCalendarUtils(false);
        dateTable.specialDate(LocaleCalendarUtils.TODAY).setTag(STYLE_TODAY);
        dateTable.specialDate(LocaleCalendarUtils.SELECTED).setTag(STYLE_SELECTED);

        grid = new FlexTable();
        grid.setWidth("100%");
        grid.setStyleName(STYLE_GRID);

        panel.addStyleName(STYLE_DATE_PICKER);
        panel.setHorizontalAlignment(VerticalPanel.ALIGN_CENTER);

        drawControlPane();
        panel.add(control);

        // grid operations
        addDaysOfWeek();
        initialGrid();
        gridUpdate();

        panel.add(grid);

        // today label
        today = dateTable.todayCell();
        today.setValue(ACTION_TODAY);
        today.addStyleName(STYLE_CONTROL_TODAY);
        today.addClickListener(this);
        enableTodayButton();

        super.setWidget(panel);
    }

    /**
     * Public method complete ClickListner interface. This adds a listener to the
     * listeners for Change events. Namely, a date clicked by the user.
     * 
     * @param listener - listener for events
     */
    public void addChangeListener(ChangeListener listener) {
        changeListeners.add(listener);
    }

    /**
     * Public method onChange which is fired when user clicks on the
     * list menu in the widget.
     * @param sender - widget on which user clicked.
     *
     */
    public void onChange(Widget sender) {

        if (sender instanceof ListBox) {

            ListBox list = (ListBox) sender;
            String val = list.getValue(list.getSelectedIndex());
            int ival = Integer.valueOf(val).intValue();

            if (list == dateTable.monthNames()) {
                action(monthAction, ival);
            } else {
                action(yearAction, ival);
            }

            changeListeners.fireChange(this);
        }
    }

    /**
     * Public method onClick which is fired when user clicks on the widget.
     * @param sender - widget on which user clicked.
     *
     */
    public void onClick(Widget sender) {

        if (sender instanceof DatePickerCell) {

            DatePickerCell cell = (DatePickerCell) sender;
            int type = cell.type();
            int value = cell.value();

            if (type == LocaleCalendarUtils.TYPE_CONTROL) {
                action(value);
            } else {
                formatSpecialDates(FORMAT_ACTION_REMOVE);

                dateTable.selectedDate(type, value);

                if (type == LocaleCalendarUtils.TYPE_CURR_MONTH) {
                    formatSpecialDates(FORMAT_ACTION_ADD);
                } else {
                    gridUpdate();
                }
            }
            changeListeners.fireChange(sender);
        }
    }

    /**
     * Public method removeChangeListener() removes a element from the list of 
     * listeners which will get fired on an widget change event. Example for 
     * listener change event is user click on a date.
     * 
     * @param listener - listener for widget change events
     * 
     */
    public void removeChangeListener(ChangeListener listener) {
        changeListeners.remove(listener);
    }

    /**
     * Public method to get the value of the user selected date for the
     * DatePicker object.
     * 
     * @return Date - value of the selected date
     */
    public Date selectedDate() {
        return dateTable;
    }

    /**
     * Public method to set the default day and date of the widget.
     * Date picker for the given month and year will be displayed.
     * The given date will be marked as the selected date.
     * 
     * @param date - Java Date value to be set
     */
    public void setFullDate(Date date) {
        formatSpecialDates(FORMAT_ACTION_REMOVE);
        dateTable.setFullDate(date);
        gridUpdate();
    }

    /**
     * Public method to set a given special formatting for the given date.
     * 
     * @param date - Date to be formatted specially
     * @param style - CSS style to be used
     */
    public void setSpecialDate(Date date, String style) {
        DatePickerDate specialDay = dateTable.addSpecialDay(date);
        specialDay.setTag(style);
        formatSpecialDates(FORMAT_ACTION_ADD);
    }

    /**
     * Public method to enable or disable displaying the trailing
     * and leading dates from previous and next months.
     * 
     * @param show - A boolean indicating whether to show or not
     * 
     */
    public void showAdjacentMonths(boolean show) {
        dateTable.enableAdjacentMonths(show);
        gridUpdate();
    }

    /**
     * Public method to display the listing of adjacent months and years by
     * clicking on the month or year in the title.
     * 
     * @param show - A boolean indicating whether to show or not
     * 
     */
    public void showTodayButton(boolean show) {
        this.showTodayButton = show;

        enableTodayButton();
    }

    /**
     * Public method to enable or disable displaying the week number of the year.
     * (implementation not completed)
     * 
     * @param show - A boolean indicating whether to show or not
     * 
     */
    public void showWeekOfYear(boolean show) {
        this.weekOfYearOffset = show ? 1 : 0;
        gridUpdate();
    }

    /**
     * Public method to display the listing of adjacent months and years by
     * clicking on the month or year in the title.
     * 
     * @param show - A boolean indicating whether to show or not
     * 
     */
    public void showYearMonthListing(boolean show) {
        this.showYearMonthListing = show;
        drawControlPane();
    }

    private void action(int actionNum, int arg) {
        formatSpecialDates(FORMAT_ACTION_REMOVE);
        switch (actionNum) {
        case ACTION_SET_MONTH:
            dateTable.setMonth(arg);
            break;
        case ACTION_SET_YEAR:
            dateTable.setYear(arg);
            break;
        }
        gridUpdate();
    }

    private void action(int actionNum) {

        if (actionNum == ACTION_CLOSE) {
            this.hide();
        } else {
            formatSpecialDates(FORMAT_ACTION_REMOVE);
            switch (actionNum) {
            case ACTION_PREV_MONTH:
                dateTable.addMonths(-1);
                break;
            case ACTION_NEXT_MONTH:
                dateTable.addMonths(1);
                break;
            case ACTION_PREV_YEAR:
                dateTable.addMonths(-12);
                break;
            case ACTION_NEXT_YEAR:
                dateTable.addMonths(12);
                break;
            case ACTION_TODAY:
                dateTable.setToday();
                break;
            }
            gridUpdate();
        }
    }

    private void addDaysOfWeek() {

        String[] dayOfWeekNames = LocaleCalendarUtils.dayOfWeekNames();

        for (int col = 0; col < 7; col++) {
            int dayOfWeek = (col + dateTable.weekStart()) % 7;
            grid.setText(0, col + weekOfYearOffset, dayOfWeekNames[dayOfWeek]);
            grid.getCellFormatter().setStyleName(0, col + weekOfYearOffset, STYLE_WEEK_NAMES);
        }
    }

    private void addWeekOfYear() {

        String[] weekOfYear = dateTable.weekOfYear();

        for (int row = 0; row < 7; row++) {
            grid.setText(row + 1, 0, weekOfYear[row]);
            grid.getCellFormatter().addStyleName(row + 1, 0, STYLE_WEEK_NUMBERS);
        }
    }

    private void colFormat(int tableRow, int tableCol, int weekendStart, int weekendEnd, DatePickerCell w) {
        String style;

        if (tableCol == (weekendStart + weekOfYearOffset)) {
            style = STYLE_WEEKEND_START;
        } else if (tableCol == (weekendEnd + weekOfYearOffset)) {
            style = STYLE_WEEKEND_END;
        } else {
            style = STYLE_WEEKDAY;
        }
        w.setStyleName(style);
        grid.getCellFormatter().addStyleName(tableRow, tableCol, style);
    }

    private void dateFormat(int diffFromMonthStart, String style, int action) {

        if (diffFromMonthStart < -prevMonthDays || diffFromMonthStart >= currMonthSize + nextMonthDays) {
            return;
        }

        int i = diffFromMonthStart + prevMonthDays + gridStart;

        int row = i / 7;
        int col = i % 7;
        int tableRow = row + 1;
        int tableCol = col + weekOfYearOffset;

        switch (action) {
        case FORMAT_ACTION_REMOVE:
            grid.getCellFormatter().removeStyleName(tableRow, tableCol, style);
            grid.getWidget(tableRow, tableCol).removeStyleName(style);
            break;
        case FORMAT_ACTION_ADD:
            grid.getCellFormatter().addStyleName(tableRow, tableCol, style);
            grid.getWidget(tableRow, tableCol).addStyleName(style);
            break;
        }
    }

    private void drawControlPane() {
        control.clear();
        control.setWidth("100%");
        control.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
        control.addStyleName(STYLE_CONTROL_PANE);

        Widget yearControl = drawControls(dateTable.yearNames(), dateTable.yearName(), ACTION_PREV_YEAR,
                ACTION_NEXT_YEAR, ACTION_SET_YEAR);
        Widget monthControl = drawControls(dateTable.monthNames(), dateTable.monthName(), ACTION_PREV_MONTH,
                ACTION_NEXT_MONTH, ACTION_SET_MONTH);

        if (dateTable.isYearBeforeMonth()) {
            control.add(yearControl);
            control.add(monthControl);
        } else {
            control.add(monthControl);
            control.add(yearControl);
        }
    }

    private Widget drawControls(ListBox names, Label name, int prev, int next, int set) {

        HorizontalPanel hp = new HorizontalPanel();
        hp.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
        hp.addStyleName(STYLE_CONTROL_BLOCK);

        if (names == dateTable.monthNames()) {
            monthAction = set;
        } else {
            yearAction = set;
        }

        // move left
        //    if (!showYearMonthListing || set == ACTION_SET_MONTH) {
        //      DatePickerCell left = new DatePickerCell("\u00ab"); // \u00ab is <<
        //      left.setType(LocaleCalendarUtils.TYPE_CONTROL);
        //      left.setValue(prev);
        //      left.addStyleName(STYLE_CONTROL);
        //      left.addClickListener(this);
        //      hp.add(left);
        //    }

        // Need list box or not
        if (showYearMonthListing) {

            names.setVisibleItemCount(1);
            names.addStyleName(STYLE_CONTROL_MENU);
            names.addChangeListener(this);

            hp.add(names);

        } else {

            name.addStyleName(STYLE_TITLE);
            hp.add(name);
        }

        // move right
        //    if (!showYearMonthListing || set == ACTION_SET_MONTH) {
        //      DatePickerCell right = new DatePickerCell("\u00bb"); // \u00ab is >>
        //      right.setType(LocaleCalendarUtils.TYPE_CONTROL);
        //      right.setValue(next);
        //      right.addStyleName(STYLE_CONTROL);
        //      right.addClickListener(this);
        //      hp.add(right);
        //    }

        return hp;
    }

    private void enableTodayButton() {
        if (showTodayButton) {
            panel.add(today);
        } else {
            panel.remove(today);
        }
    }

    private void formatSpecialDates(int action) {
        int numSpecialDays = dateTable.numSpecialDays();

        for (int i = 0; i < numSpecialDays; i++) {
            DatePickerDate date = dateTable.specialDate(i);
            dateFormat(date.dayDiff(), date.tag(), action);
        }
    }

    private void gridUpdate() {

        DatePickerCell[] dayOfMonthNames = dateTable.dayOfMonthNames();
        DatePickerCell[] dayOfMonthNamesPrev = dateTable.dayOfMonthNamesPrev();
        DatePickerCell[] dayOfMonthNamesNext = dateTable.dayOfMonthNamesNext();

        prevMonthSize = dateTable.prevMonthSize();
        prevMonthDays = dateTable.prevMonthDays();
        currMonthSize = dateTable.currMonthSize();
        nextMonthDays = dateTable.nextMonthDays();
        gridStart = dateTable.gridStart();
        int weekendStart = dateTable.weekendStart();
        int weekendEnd = dateTable.weekendEnd();

        if (weekOfYearOffset > 0) {
            addWeekOfYear();
        }

        int rowCount = grid.getRowCount();
        for (int i = rowCount - 1; i > 0; i--) { // 0th row is the week names
            grid.removeRow(i);
        }

        for (int i = 0; i < prevMonthDays; i++) {
            int row = i / 7;
            int col = i % 7;
            int tableRow = row + 1;
            int tableCol = col + weekOfYearOffset;
            int dayOfMonth = prevMonthSize - prevMonthDays + i; // 0 based

            colFormat(tableRow, tableCol, weekendStart, weekendEnd, dayOfMonthNamesPrev[dayOfMonth]);
            grid.setWidget(tableRow, tableCol, dayOfMonthNamesPrev[dayOfMonth]);
            grid.getCellFormatter().addStyleName(tableRow, tableCol, STYLE_OTHER_MONTHS);
        }

        for (int i = 0; i < currMonthSize; i++) {
            int row = (gridStart + prevMonthDays + i) / 7;
            int col = (gridStart + prevMonthDays + i) % 7;
            int tableRow = row + 1;
            int tableCol = col + weekOfYearOffset;

            colFormat(tableRow, tableCol, weekendStart, weekendEnd, dayOfMonthNames[i]);
            grid.setWidget(tableRow, tableCol, dayOfMonthNames[i]);
        }

        for (int i = 0; i < nextMonthDays; i++) {
            int row = (currMonthSize + prevMonthDays + i) / 7;
            int col = (currMonthSize + prevMonthDays + i) % 7;
            int tableRow = row + 1;
            int tableCol = col + weekOfYearOffset;

            colFormat(tableRow, tableCol, weekendStart, weekendEnd, dayOfMonthNamesNext[i]);
            grid.setWidget(tableRow, tableCol, dayOfMonthNamesNext[i]);
            grid.getCellFormatter().addStyleName(tableRow, tableCol, STYLE_OTHER_MONTHS);
        }
        formatSpecialDates(FORMAT_ACTION_ADD);
    }

    private void initialGrid() {

        DatePickerCell[] dayOfMonthNamesPrev = dateTable.dayOfMonthNamesPrev();
        DatePickerCell[] dayOfMonthNamesNext = dateTable.dayOfMonthNamesNext();
        DatePickerCell[] dayOfMonthNames = dateTable.dayOfMonthNames();

        for (int i = 0; i < 31; i++) {
            dayOfMonthNamesPrev[i].addClickListener(this);
            dayOfMonthNamesNext[i].addClickListener(this);
            dayOfMonthNames[i].addClickListener(this);
        }
    }
}