Java/GWT/DatePicker — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 06:35, 1 июня 2010

DatePicker with GNU license

From: http://code.google.ru/p/gwt-datepicker/
License: GNU Lesser public license!

package com.jexp.gwt.client;
import com.google.gwt.i18n.client.*;
import com.google.gwt.user.client.*;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.core.client.*;
import java.util.Date;
/**
 * Main class of the DatePicker. It extends the TextBox widget and manages a Date object.
 * When it is clicked, it opens a PopupCalendar on which we can select a new date. <br>
 * Example of use :  <br>
 * <code>
 * DatePicker datePicker = new DatePicker();<br>
 * RootPanel.get().add(datePicker);<br>
 * </code>
 * You can specify a theme (see the CSS file DatePickerStyle.css) and
 * the date to initialize the date picker.
 * Enjoy xD
 * @author Nicolas Wetzel (nicolas.wetzel@zenika.ru)
 * @author Jean-Philippe Dournel
 */
class DatePicker extends TextBox {
  private PopupCalendar popup;
  private Date date;
  private DateTimeFormat dateFormatter;
  
  {
    dateFormatter = DateUtil.getDateTimeFormat();
    popup = new PopupCalendar(this);
  }
  /**
   * Default constructor. It creates a DatePicker which shows the current
   * month.
   */
  public DatePicker() {
    super();
    setText("");  
    sinkEvents(Event.ONCHANGE | Event.ONKEYPRESS);
  }
  /**
   * Create a DatePicker which show a specific Date.
   * @param date Date to show
   */
  public DatePicker(Date date) {
    this();
    this.date = date;
    synchronizeFromDate();
  }
  
  /**
   * Create a DatePicker which uses a specific theme.
   * @param theme Theme name
   */
  public DatePicker(String theme) {
    this();
    setTheme(theme);
  }
  
  /**
   * Create a DatePicker which specifics date and theme.
   * @param date Date to show
   * @param theme Theme name
   */
  public DatePicker(Date date, String theme) {
    this();
    this.date = date;
    synchronizeFromDate();
    setTheme(theme);
  }
  /**
   * Return the Date contained in the DatePicker.
   * @return The Date
   */
  public Date getDate() {
    return date;
  }
  /**
   * Set the Date of the datePicker and synchronize it with the display.
   * @param value
   */
  public void setDate(Date value) {
    this.date = value;
    synchronizeFromDate();
  }
  /**
   * Return the theme name.
   * @return Theme name
   */
  public String getTheme() {
    return popup.getTheme();
  }
  /**
   * Set the theme name.
   * @param theme Theme name
   */
  public void setTheme(String theme) {
    popup.setTheme(theme);
  }
  public void onBrowserEvent(Event event) {
    switch (DOM.eventGetType(event)) {
      case Event.ONCLICK:
        showPopup();
      break;
      case Event.ONBLUR:
        popup.hidePopupCalendar();
      break;
      case Event.ONCHANGE:
        parseDate();
      break;
      case Event.ONKEYPRESS:
        if (DOM.eventGetKeyCode(event) == 13) {
          parseDate();
          showPopup();
          break;
        }
    }
  }
  
  /**
   * Display the date in the DatePicker.
   */
  public void synchronizeFromDate() {
    this.setText(dateFormatter.format(this.date));
  }
  /**
   * Display the PopupCalendar.
   */
  private void showPopup() {
    if (this.date != null) {
      popup.setDisplayedMonth(this.date);
    }
    popup.setPopupPosition(this.getAbsoluteLeft()+150, this.getAbsoluteTop());
    popup.displayMonth();
  }
  /**
   * Parse the date entered in the DatePicker.
   */
  private void parseDate() {
    date = dateFormatter.parse(getText());
    synchronizeFromDate();
  }
}
/**
 * Popup used by the datePicker. It represents a calendar and allows the user to
 * select a date. It is localizable thanks to the DateTimerFormat class(GWT
 * class) and the DateLocale class.
 * 
 * @author Nicolas Wetzel (nicolas.wetzel@zenika.ru)
 * @author Jean-Philippe Dournel
 */
class PopupCalendar extends PopupPanel {
  private boolean leave;
  private String theme;
  private final DatePicker datePicker;
  private DateTimeFormat dayNameFormat;
  private DateTimeFormat monthFormat;
  private DateTimeFormat dayNumberFormat;
  private Label currentMonth;
  private Grid daysGrid;
  private Date displayedMonth;
  {
    this.leave = true;
    this.theme = "blue";
    this.dayNameFormat = DateTimeFormat.getFormat("E");
    this.monthFormat = DateTimeFormat.getFormat("MMMM yyyy");
    this.dayNumberFormat = DateTimeFormat.getFormat("d");
    this.daysGrid = new Grid(7, 7);
  }
  /**
   * Create a calendar popup. You have to call the displayMonth method to
   * display the the popup.
   * 
   * @param datePicker
   *            The date picker on which the popup is attached
   */
  public PopupCalendar(DatePicker datePicker) {
    super(true);
    this.datePicker = datePicker;
    this.setStyleName(theme + "-date-picker");
    VerticalPanel panel = new VerticalPanel();
    this.add(panel);
    sinkEvents(Event.ONBLUR);
    drawMonthLine(panel);
    drawWeekLine(panel);
    drawDayGrid(panel);
  }
  
  /**
   * Return the month displayed by the PopupCalendar.
   * @return a Date pointing to the month
   */
  public Date getDisplayedMonth() {
    return displayedMonth;
  }
  /**
   * Set the month which is display by the PopupCalendar.
   * @param displayedMonth The Date to display
   */
  public void setDisplayedMonth(Date displayedMonth) {
    this.displayedMonth = displayedMonth;
  }
  
  /**
   * Return the theme used by the PopupCalendar.
   * @return Name of the theme
   */
  public String getTheme() {
    return this.theme;
  }
  /**
   * Set the theme used by the PopupCalendar.
   * @param theme Name of the theme
   */
  public void setTheme(String theme) {
    this.theme = theme;
    this.setStyleName(theme + "-date-picker");
  }  
  /**
   * Refresh the PopupCalendar and show it.
   */
  public void displayMonth() {
    if (this.displayedMonth == null) {
      if (datePicker.getDate() != null)
        this.displayedMonth = datePicker.getDate();
      else {
        this.displayedMonth = new Date();
      }
    }
    this.drawLabelMoisAnnee();
    this.drawDaysGridContent(this.displayedMonth);
    show();
  }
  /**
   * This method is destined to be used by the DatePicker in case of focus lost.
   * It creates a delay before the popup hides to allows the popup to catch
   * a click and eventually update the Date of the DatePicker.
   */
  public void hidePopupCalendar() {
    DeferredCommand.addCommand(new Command() {
      public void execute() {
        Timer t = new Timer() {
          public void run() {
            if (leave) {
              hide();
            } else {
              leave = true;
            }
          }
        };
        t.schedule(300);
      }
    });
  }
  /**
   * Draw the monthLine with contains navigations buttons (change the month
   * and the year) and displayed the displayed month.
   * 
   * @param panel
   *            The panel contained in the popup
   */
  private void drawMonthLine(Panel panel) {
    Grid monthLine = new Grid(1, 5);
    monthLine.setStyleName(theme + "-" + "month-line");
    HTMLTable.CellFormatter monthCellFormatter = monthLine.getCellFormatter();
    Label previousYear = new Label("?);
    previousYear.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        leave = false;
        PopupCalendar.this.changeMonth(-12);
      }
    });
    monthLine.setWidget(0, 0, previousYear);
    Label previousMonth = new Label("?);
    previousMonth.addClickListener(new ClickListener() {
      public void onClick(com.google.gwt.user.client.ui.Widget sender) {
        leave = false;
        PopupCalendar.this.changeMonth(-1);
      };
    });
    monthLine.setWidget(0, 1, previousMonth);
    monthCellFormatter.setWidth(0, 2, "60%");
    currentMonth = new Label();
    currentMonth.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        leave = false;
      }
    });
    monthLine.setWidget(0, 2, currentMonth);
    Label nextMonth = new Label("?);
    nextMonth.addClickListener(new ClickListener() {
      public void onClick(com.google.gwt.user.client.ui.Widget sender) {
        leave = false;
        PopupCalendar.this.changeMonth(1);
      };
    });
    monthLine.setWidget(0, 3, nextMonth);
    Label nextYear = new Label("?);
    nextYear.addClickListener(new ClickListener() {
      public void onClick(Widget sender) {
        leave = false;
        PopupCalendar.this.changeMonth(12);
      }
    });
    monthLine.setWidget(0, 4, nextYear);
    panel.add(monthLine);
  }
  /**
   * Draw the week line which displays first letter of week days. example : S
   * M T ....etc
   * 
   * @param panel
   *            The panel contained in the popup
   */
  private void drawWeekLine(Panel panel) {
    Grid weekLine = new Grid(1, 7);
    weekLine.setStyleName(theme + "-" + "week-line");
    Date weekFirstday = DateUtil.getWeekFirstDay();
    for (int i = 0; i < 7; i++) {
      weekLine.setText(0, i, dayNameFormat.format(
          DateUtil.addDays(weekFirstday, i)).substring(0, 1)
          .toUpperCase());
    }
    panel.add(weekLine);
  }
  /**
   * Display the grid which contains the days. When a day is clicked, it
   * updates the Date contained in the DatePicker.
   * @param panel
   *            The panel contained in the popup
   */
  private void drawDayGrid(Panel panel) {
    this.daysGrid.addTableListener(new TableListener() {
      public void onCellClicked(SourcesTableEvents sender, int row,
          int cell) {
        Date selectedDay = DateUtil.addDays(
            getDaysGridOrigin(displayedMonth), row * 7 + cell);
        datePicker.setDate(selectedDay);
        datePicker.synchronizeFromDate();
        PopupCalendar.this.hide();
        leave = true;
      };
    });
    daysGrid.setStyleName(theme + "-" + "day-grid");
    panel.add(daysGrid);
  }
  /**
   * Update the Label which shows the displayed month (in the month line).
   */
  private void drawLabelMoisAnnee() {
    currentMonth
        .setText(monthFormat.format(this.displayedMonth).toLowerCase());
  }
  /**
   * Draw the days into the days grid. Days drawn are the days of the displayed month
   * and few days after and before the displayed month.
   * @param displayedMonth Date of the displayed month
   */
  private void drawDaysGridContent(Date displayedMonth) {
    HTMLTable.CellFormatter cfJours = daysGrid.getCellFormatter();
    Date cursor = this.getDaysGridOrigin(displayedMonth);
    for (int i = 0; i < 7; i++) {
      for (int j = 0; j < 7; j++) {
        daysGrid.setText(i, j, dayNumberFormat.format(cursor));
        cfJours.removeStyleName(i, j, theme + "-" + "selected");
        cfJours.removeStyleName(i, j, theme + "-"
            + "current-month-selected");
        cfJours.removeStyleName(i, j, theme + "-" + "other-day");
        cfJours.removeStyleName(i, j, theme + "-"
            + "current-month-other-day");
        cfJours.removeStyleName(i, j, theme + "-" + "week-end");
        cfJours.removeStyleName(i, j, theme + "-"
            + "current-month-week-end");
        if (datePicker.getDate() != null
            && DateUtil.areEquals(datePicker.getDate(), cursor))
          if (displayedMonth.getMonth() == cursor.getMonth())
            cfJours.addStyleName(i, j, theme + "-"
                + "current-month-selected");
          else
            cfJours.addStyleName(i, j, theme + "-" + "selected");
        else if (DateUtil.isInWeekEnd(cursor))
          if (displayedMonth.getMonth() == cursor.getMonth())
            cfJours.addStyleName(i, j, theme + "-"
                + "current-month-week-end");
          else
            cfJours.addStyleName(i, j, theme + "-" + "week-end");
        else if (displayedMonth.getMonth() == cursor.getMonth())
          cfJours.addStyleName(i, j, theme + "-"
              + "current-month-other-day");
        else
          cfJours.addStyleName(i, j, theme + "-" + "other-day");
        cursor = DateUtil.addDays(cursor, 1);
      }
    }
  }
  /**
   * Change the displayed month.
   * @param i Number of month to add to the displayed month
   */
  protected void changeMonth(int i) {
    this.displayedMonth = DateUtil.addMonths(this.displayedMonth, i);
    this.displayMonth();
  }
  /**
   * Return the first day to display. If the month first day is after the 5th
   * day of the week, it return the first day of the week. Else, it returns
   * the first day of the week before.
   * 
   * @param displayedMonth
   * @return The first day to display in the grid
   */
  private Date getDaysGridOrigin(Date displayedMonth) {
    int currentYear = displayedMonth.getYear();
    int currentMonth = displayedMonth.getMonth();
    HTMLTable.CellFormatter cfJours = daysGrid.getCellFormatter();
    Date monthFirstDay = new Date(currentYear, currentMonth, 1);
    int indice = DateUtil.getWeekDayIndex(monthFirstDay);
    Date origineTableau;
    if (indice > 4) {
      origineTableau = DateUtil.getWeekFirstDay(monthFirstDay);
    } else {
      origineTableau = DateUtil.getWeekFirstDay(DateUtil.addDays(
          monthFirstDay, -7));
    }
    return origineTableau;
  }
}

/**
 * Utility class which ease the use of Date classes with the DatePicker.
 * 
 * @author Nicolas Wetzel (nicolas.wetzel@zenika.ru)
 * @author Jean-Philippe Dournel
 * 
 */
class DateUtil {
  /**
   * Add days to the Date object.
   * 
   * @param date
   *            The Date to modify
   * @param days
   *            Number of day to add
   * @return The modified Date object
   */
  public static Date addDays(Date date, int days) {
    return new Date(date.getYear(), date.getMonth(), date.getDate() + days);
  }
  /**
   * Add months to the Date object.
   * 
   * @param date
   *            The Date to modify
   * @param months
   *            Number of month to add
   * @return The modified Date object
   */
  public static Date addMonths(Date date, int months) {
    return new Date(date.getYear(), date.getMonth() + months, date
        .getDate());
  }
  /**
   * Test if two Date objects represent the same day. It tests if the days,
   * the months and the years are equals.
   * 
   * @param date1
   *            First Date
   * @param date2
   *            Second Date
   * @return true if the days are the same
   */
  public static boolean areEquals(Date date1, Date date2) {
    return date1.getDate() == date2.getDate()
        && date1.getMonth() == date2.getMonth()
        && date1.getYear() == date2.getYear();
  }
  /**
   * Return a Date object with represents the first day of a month contained
   * in another Date object.
   * 
   * @param date The Date containing the month
   * @return The first day of the month
   */
  public static Date getMonthFirstDay(Date date) {
    Date current = date;
    while (current.getDate() != 1) {
      current = new Date(current.getYear(), current.getMonth(), current
          .getDate() - 1);
    }
    return current;
  }
  /**
   * Returns the place of the day in the week.
   * Example : sunday = 0, monday = 1 ....
   * Depends on the locale. 
   * @param day The day
   * @return The place of the day
   */
  public static int getWeekDayIndex(Date day) {
    DateLocale locale = (DateLocale) GWT.create(DateLocale.class);
    int[] daysOrder = locale.getDAY_ORDER();
    int dayIndex = day.getDay();
    for (int i = 0; i < 7; i++) {
      if (dayIndex == daysOrder[i]) {
        return i;
      }
    }
    return -1;
  }
  /**
   * Returns the first day of the current week.
   * @return Date pointing to the first day 
   */
  public static Date getWeekFirstDay() {
    return getWeekFirstDay(new Date());
  }
  /**
   * Returns the first day of the week containing a Date object.
   * @param date The Date
   * @return The Date pointing to the first day
   */
  public static Date getWeekFirstDay(Date date) {
    Date current = date;
    DateLocale local = (DateLocale) GWT.create(DateLocale.class);
    int firstDay = local.getDAY_ORDER()[0];
    while (current.getDay() != firstDay) {
      current = new Date(current.getYear(), current.getMonth(), current
          .getDate() - 1);
    }
    return current;
  }
  /**
   * Test if a day is a weekend day.
   * @param day The Date to test
   * @return true if the Date is a weekend day
   */
  public static boolean isInWeekEnd(Date day) {
    int dayIndex = day.getDay();
    return (dayIndex == 0 | dayIndex == 6) ? true : false;
  }
  /**
   * Get the DateTimeFormat corresponding to the locale.
   * @return DateTimeFormat
   */
  public static DateTimeFormat getDateTimeFormat() {
    DateLocale locale = (DateLocale) GWT.create(DateLocale.class);
    DateTimeFormat format = locale.getDateTimeFormat();
    return format;
  }
}

/**
 * This interface is used to extend the localization property
 * of the DateTimeFormat of GWT. It adds the support of the days order 
 * which is not the same in all countries. 
 * It uses the Localizable interface. See GWT documentation for more detail.
 * @author Jean-Philippe Dournel
 *
 */
interface DateLocale extends Localizable{
  
  /**
   * Returns an array containing the order of days in the week.
   * For a week starting by the monday, it"ll return {1,2,3,4,5,6,0}.
   * @return Array of days index
   */
  public int[] getDAY_ORDER();
  
  /**
   * Return the DateTimeFormat corresponding to the date pattern used in
   * the country. For example : "dd/MM/yyyy" in France and "MM/dd/yyyy" in the US.
   * @return DateTimeFormat
   */
  public DateTimeFormat getDateTimeFormat();
  
}
/**
 * DateLocale implementation for US.
 * @author Jean-Philippe Dournel
 *
 */
class DateLocale_ implements DateLocale {
  public int[] DAYS_ORDER = { 0, 1, 2, 3, 4, 5, 6 };
  public int[] getDAY_ORDER() {
    return DAYS_ORDER;
  }
  public DateTimeFormat getDateTimeFormat() {
    DateTimeFormat format = DateTimeFormat.getFormat("MM/dd/yyyy");
    return format;
  }
}
/**
 * DateLocale implementation for UK.
 * @author Jean-Philippe Dournel
 *
 */
class DateLocale_en_GB implements DateLocale {
  public int[] DAYS_ORDER = { 1, 2, 3, 4, 5, 6, 0 };
  public int[] getDAY_ORDER() {
    return DAYS_ORDER;
  }
  public DateTimeFormat getDateTimeFormat() {
    DateTimeFormat format = DateTimeFormat.getFormat("dd/MM/yyyy");
    return format;
  }
}
/**
 * DateLocale implementation for french-speaking Canada.
 * @author Jean-Philippe Dournel
 *
 */
class DateLocale_fr_CA implements DateLocale {
  public int[] DAYS_ORDER = { 0, 1, 2, 3, 4, 5, 6 };
  public int[] getDAY_ORDER() {
    return DAYS_ORDER;
  }
  
  public DateTimeFormat getDateTimeFormat() {
    DateTimeFormat format = DateTimeFormat.getFormat("dd/MM/yyyy");
    return format;
  }
}
/**
 * DateLocale implementation for France.
 * @author Jean-Philippe Dournel
 *
 */
class DateLocale_fr implements DateLocale {
  
  public int[] DAYS_ORDER = { 1, 2, 3, 4, 5, 6, 0 };
  public int[] getDAY_ORDER() {
    return DAYS_ORDER;
  }
  
  public DateTimeFormat getDateTimeFormat() {
    DateTimeFormat format = DateTimeFormat.getFormat("dd/MM/yyyy");
    return format;
  }
  
}
public class GWTClient implements EntryPoint{
  public void onModuleLoad() {
    DatePicker datePicker = new DatePicker("blue");
    RootPanel.get().add(datePicker);
    
  }
}





Simple Calendar Widget for GWT

package com.jexp.gwt.client;
import com.google.gwt.core.client.*;
import com.google.gwt.user.client.*;
import java.util.Date;
import com.google.gwt.user.client.ui.*;
public class GWTClient implements EntryPoint {
    FlowPanel fp = new FlowPanel();
  public GWTClient() {
        final CalendarWidget calendar = new CalendarWidget();
          calendar.addChangeListener(new ChangeListener() {
        
          public void onChange(Widget sender) {
            Window.alert("Date selected: " + calendar.getDate());
          }
        });
     fp.add(calendar);
  }
  public void onModuleLoad() {
    RootPanel.get().add(fp);
  }
}
/*
Simple Calendar Widget for GWT
Copyright (C) 2006 Alexei Sokolov http://gwt.ruponents.googlepages.ru/
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ 
//package com.gwt.ruponents.client;

 class CalendarWidget extends Composite 
    implements ClickListener, SourcesChangeEvents {
  private class NavBar extends Composite implements ClickListener{
    public final DockPanel bar = new DockPanel();
    public final Button prevMonth = new Button("&lt;", this);
    public final Button prevYear = new Button("&lt;&lt;", this);
    public final Button nextYear = new Button("&gt;&gt;", this);
    public final Button nextMonth = new Button("&gt;", this);
    public final HTML title = new HTML();
    private final CalendarWidget calendar;
    public NavBar(CalendarWidget calendar) {
      this.calendar = calendar;
      setWidget(bar);
      bar.setStyleName("navbar");
      title.setStyleName("header");
      HorizontalPanel prevButtons = new HorizontalPanel();
      prevButtons.add(prevMonth);
      prevButtons.add(prevYear);
      HorizontalPanel nextButtons = new HorizontalPanel();
      nextButtons.add(nextYear);
      nextButtons.add(nextMonth);
      bar.add(prevButtons, DockPanel.WEST);
      bar.setCellHorizontalAlignment(prevButtons, DockPanel.ALIGN_LEFT);
      bar.add(nextButtons, DockPanel.EAST);
      bar.setCellHorizontalAlignment(nextButtons, DockPanel.ALIGN_RIGHT);
      bar.add(title, DockPanel.CENTER);
      bar.setVerticalAlignment(DockPanel.ALIGN_MIDDLE);
      bar.setCellHorizontalAlignment(title, HasAlignment.ALIGN_CENTER);
      bar.setCellVerticalAlignment(title, HasAlignment.ALIGN_MIDDLE);
      bar.setCellWidth(title, "100%");
    }
    public void onClick(Widget sender) {
      if (sender == prevMonth) {
        calendar.prevMonth();
      } else if (sender == prevYear) {
        calendar.prevYear();
      } else if (sender == nextYear) {
        calendar.nextYear();
      } else if (sender == nextMonth) {
        calendar.nextMonth();
      }
    }
  }
  private static class CellHTML extends HTML {
    private int day;
       
    public CellHTML(String text, int day) {
      super(text);
      this.day = day;
    }
       
    public int getDay() {
      return day;
    }
  }
  
  private final NavBar navbar = new NavBar(this);
  private final DockPanel outer = new DockPanel();
  private final Grid grid = new Grid(6, 7) {
    public boolean clearCell(int row, int column) {
      boolean retValue = super.clearCell(row, column);
           
      Element td = getCellFormatter().getElement(row, column);
      DOM.setInnerHTML(td, "");
      return retValue;
    }
  };
  private Date date = new Date();
  private ChangeListenerCollection changeListeners;
   
  private String[] days = new String[] { "Sunday", "Monday", "Tuesday",
      "Wednesday", "Thursday", "Friday", "Saturday" };
  private String[] months = new String[] { "January", "February", "March",
      "April", "May", "June", "July", "August", "September", "October",
      "November", "December" };
  public CalendarWidget() {
    setWidget(outer);
    grid.setStyleName("table");
    grid.setCellSpacing(0);
    outer.add(navbar, DockPanel.NORTH);
    outer.add(grid, DockPanel.CENTER);
    drawCalendar();
    setStyleName("CalendarWidget");
  }
  private void drawCalendar() {
    int year = getYear();
    int month = getMonth();
    int day = getDay();
    setHeaderText(year, month);
    grid.getRowFormatter().setStyleName(0, "weekheader");
    for (int i = 0; i < days.length; i++) {
      grid.getCellFormatter().setStyleName(0, i, "days");
      grid.setText(0, i, days[i].substring(0, 3));
    }
    Date now = new Date();
    int sameDay = now.getDate();
    int today = (now.getMonth() == month && now.getYear()+1900 == year) ? sameDay : 0;
       
    int firstDay = new Date(year - 1900, month, 1).getDay();
    int numOfDays = getDaysInMonth(year, month);
    int j = 0;
    for (int i = 1; i < 6; i++) {
      for (int k = 0; k < 7; k++, j++) {
        int displayNum = (j - firstDay + 1);
        if (j < firstDay || displayNum > numOfDays) {
          grid.getCellFormatter().setStyleName(i, k, "empty");
          grid.setHTML(i, k, "&nbsp;");
        } else {
          HTML html = new CellHTML(
            "<span>" + String.valueOf(displayNum) + "</span>", 
            displayNum);
          html.addClickListener(this);
          grid.getCellFormatter().setStyleName(i, k, "cell");
          if (displayNum == today) {
            grid.getCellFormatter().addStyleName(i, k, "today");
          } else if (displayNum == sameDay) {
            grid.getCellFormatter().addStyleName(i, k, "day");
          }
          grid.setWidget(i, k, html);
        }
      }
    }
  }
  protected void setHeaderText(int year, int month) {
    navbar.title.setText(months[month] + ", " + year);
  }
   
  private int getDaysInMonth(int year, int month) {
    switch (month) {
      case 1:
        if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
          return 29; // leap year
        else
          return 28;
      case 3:
        return 30;
      case 5:
        return 30;
      case 8:
        return 30;
      case 10:
        return 30;
      default:
        return 31;
    }
  }
  public void prevMonth() {
    int month = getMonth() - 1;
    if (month < 0) {
      setDate(getYear() - 1, 11, getDay());
    } else {
      setMonth(month);
    }
    drawCalendar();
  }
  public void nextMonth() {
    int month = getMonth() + 1;
    if (month > 11) {
      setDate(getYear() + 1, 0, getDay());
    } else {
      setMonth(month);
    }
    drawCalendar();
  }
   
  public void prevYear() {
    setYear(getYear() - 1);
    drawCalendar();
  }
  public void nextYear() {
    setYear(getYear() + 1);
    drawCalendar();
  }
  private void setDate(int year, int month, int day) {
    date = new Date(year - 1900, month, day);
  }
   
  private void setYear(int year) {
    date.setYear(year - 1900);
  }
  private void setMonth(int month) {
    date.setMonth(month);
  }
  public int getYear() {
    return 1900 + date.getYear();
  }
  public int getMonth() {
    return date.getMonth();
  }
  public int getDay() {
    return date.getDate();
  }
   
  public Date getDate() {
    return date;
  }
  public void onClick(Widget sender) {
    CellHTML cell = (CellHTML)sender;
    setDate(getYear(), getMonth(), cell.getDay());
    drawCalendar();
    if (changeListeners != null) {
      changeListeners.fireChange(this);
    }
  }
  public void addChangeListener(ChangeListener listener) {
    if (changeListeners == null)
      changeListeners = new ChangeListenerCollection();
    changeListeners.add(listener);
  }
  public void removeChangeListener(ChangeListener listener) {
    if (changeListeners != null)
      changeListeners.remove(listener);
  }
}  
///
.CalendarWidget {
  border: 1px solid #ACA899;
}
.CalendarWidget .navbar {
  width: 100%;
  background-color: #C3D9FF;
  vertical-align: middle;
  border-bottom: 1px solid #ACA899;
}
.CalendarWidget .navbar .gwt-Button {
  padding-left: 5px;
  padding-right: 5px;
}
.CalendarWidget .table {
  font: 10pt sans-serif;
  text-align: center;
}
.CalendarWidget .weekheader {
  background-color: #ACA899;
}
.CalendarWidget .weekheader .days {
  width: 3em;
}
.CalendarWidget .cell {
  cursor:pointer;
}
.CalendarWidget .cell .gwt-HTML {
  border: 1px solid #ACA899;
}
.CalendarWidget .cell .gwt-HTML span {
  width: 100%;
  height: 100%;
  line-height: 2em;
}
.CalendarWidget .today .gwt-HTML {
  background-color: #C3D9FF;
}
.CalendarWidget .day .gwt-HTML {
  border: 1px solid #C3D9FF;
}