com.dianaui.universal.core.client.ui.DateTimePicker.java Source code

Java tutorial

Introduction

Here is the source code for com.dianaui.universal.core.client.ui.DateTimePicker.java

Source

/*
 * #%L
 * Diana UI Core
 * %%
 * Copyright (C) 2014 Diana UI
 * %%
 * 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.
 * #L%
 */
package com.dianaui.universal.core.client.ui;

import com.dianaui.universal.core.client.ui.base.helper.CalendarModel;
import com.dianaui.universal.core.client.ui.base.modal.ModalWithBackdrop;
import com.dianaui.universal.core.client.ui.constants.*;
import com.dianaui.universal.core.client.ui.html.Div;
import com.dianaui.universal.core.client.ui.html.UnorderedList;
import com.google.gwt.dom.client.*;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.datepicker.client.CalendarUtil;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author <a href='mailto:donbeave@gmail.com'>Alexey Zhokhov</a>
 */
public class DateTimePicker extends ModalWithBackdrop implements LeafValueEditor<Date>, HasValue<Date> {

    private static String TABLE_START = "<table class=\"table-condensed\">";
    private static String TABLE_END = "</table>";
    private static String TBODY_START = "<tbody>";
    private static String TBODY_END = "</tbody>";
    private final Div container;
    private CalendarModel calendarModel = new CalendarModel();
    private DayOfWeekFormat dayOfWeekFormat;
    private HourFormat hourFormat;
    private Date value = new Date();
    private boolean dateEnabled = true;
    private boolean timeEnabled = true;
    private boolean autoClose = true;
    private Collapse dateCollapse;
    private Div dateContainer;
    private Collapse timeCollapse;
    private Div timeContainer;
    private Days days;
    private Months months;
    private Years years;
    private Time time;
    private Hours hours;
    private Minutes minutes;
    private List<Date> disabledDates;
    private List<Date> enabledDates;

    public DateTimePicker() {
        setFade(false);
        setBackdrop(ModalBackdrop.FALSE);

        container = new Div();
        container.addStyleName(Styles.DATETIMEPICKER_WIDGET);
        container.addStyleName(Styles.DROPDOWN_MENU);

        add(container);
    }

    public CalendarModel getCalendarModel() {
        return calendarModel;
    }

    public DayOfWeekFormat getDayOfWeekFormat() {
        return dayOfWeekFormat;
    }

    public void setDayOfWeekFormat(final DayOfWeekFormat format) {
        this.dayOfWeekFormat = format;

        calendarModel.setDayOfWeekFormat(format.getFormat());

        checkForRedraw();
    }

    public HourFormat getHourFormat() {
        return hourFormat;
    }

    public void setHourFormat(final HourFormat format) {
        this.hourFormat = format;

        calendarModel.setHourFormat(format.getFormat());

        checkForRedraw();
    }

    public boolean isDateEnabled() {
        return dateEnabled;
    }

    public void setDateEnabled(final boolean enabled) {
        if (!timeEnabled && !enabled) {
            throw new RuntimeException("Can not disable date when time disabled");
        }
        this.dateEnabled = enabled;

        checkForRedraw();
    }

    public boolean isTimeEnabled() {
        return timeEnabled;
    }

    public void setTimeEnabled(final boolean enabled) {
        if (!dateEnabled && !enabled) {
            throw new RuntimeException("Can not disable time when date disabled");
        }
        this.timeEnabled = enabled;

        checkForRedraw();
    }

    public boolean isAutoClose() {
        return autoClose;
    }

    public void setAutoClose(final boolean enabled) {
        this.autoClose = enabled;
    }

    /*
     An list of dates that cannot be selected
     */
    public List<Date> getDisabledDates() {
        return disabledDates;
    }

    public void setDisabledDates(final List<Date> dates) {
        this.disabledDates = dates;

        if (dates != null) {
            for (Date date : dates) {
                CalendarUtil.resetTime(date);
            }
        }

        checkForRedraw();
    }

    /*
     An list of dates that can be selected
     */
    public List<Date> getEnabledDates() {
        return enabledDates;
    }

    public void setEnabledDates(final List<Date> dates) {
        this.enabledDates = dates;

        if (dates != null) {
            for (Date date : dates) {
                CalendarUtil.resetTime(date);
            }

            disabledDates = null;
        }

        checkForRedraw();
    }

    @Override
    protected void onShow() {
        container.clear();

        if (dateEnabled && timeEnabled) {
            UnorderedList list = new UnorderedList();
            list.setStyleName(Styles.LIST_UNSTYLED);

            if (dateCollapse == null) {
                dateCollapse = new Collapse(LIElement.TAG);

                initDateContainer();
                dateCollapse.add(dateContainer);
            }

            if (timeCollapse == null) {
                timeCollapse = new Collapse(LIElement.TAG);
                timeCollapse.setToggle(false);

                initTimeContainer();
                timeCollapse.add(timeContainer);
            }

            AnchorListItem switchItem = new AnchorListItem();
            switchItem.setStyleName(Styles.DATETIMEPICKER_SWITCH);
            switchItem.setGlyphicon(GlyphiconType.TIME);
            switchItem.getAnchor().setStyleName(Styles.BTN);
            switchItem.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    days.setDate(CalendarUtil.copyDate(value));
                    time.setDate(CalendarUtil.copyDate(value));
                    dateCollapse.toggle();
                    timeCollapse.toggle();
                }
            });

            list.add(dateCollapse);
            list.add(switchItem);
            list.add(timeCollapse);

            container.add(list);

            days.setDate(CalendarUtil.copyDate(value));
        } else if (dateEnabled) {
            initDateContainer();

            container.add(dateContainer);

            days.setDate(CalendarUtil.copyDate(value));
        } else {
            initTimeContainer();

            container.add(timeContainer);

            time.setDate(CalendarUtil.copyDate(value));
        }

        Event.setEventListener(getElement(), new EventListener() {
            @Override
            public void onBrowserEvent(Event event) {
                if (Event.ONCLICK == event.getTypeInt() && event.getEventTarget().equals(getElement())) {
                    hide();
                }
            }
        });

        setVisible(true);

        super.onShow();
    }

    private void initTimeContainer() {
        if (timeContainer == null) {
            timeContainer = new Div();
            timeContainer.setStyleName(Styles.TIMEPICKER);
        }

        if (time == null) {
            time = new Time();
            time.setDate(CalendarUtil.copyDate(value));
            time.setVisible(true);
        }

        timeContainer.add(time);
    }

    private void initDateContainer() {
        if (dateContainer == null) {
            dateContainer = new Div();
            dateContainer.setStyleName(Styles.DATEPICKER);
        }

        if (days == null) {
            days = new Days();
            days.setVisible(true);
        }

        dateContainer.add(days);
    }

    public void setPosition(final int left, final int top) {
        container.getElement().getStyle().setLeft(left, Style.Unit.PX);
        container.getElement().getStyle().setTop(top, Style.Unit.PX);
    }

    @Override
    public void setVisible(final boolean visible) {
        Style style = container.getElement().getStyle();

        if (visible) {
            style.setDisplay(Style.Display.BLOCK);
            style.setZIndex(9999);
            style.setPosition(Style.Position.ABSOLUTE);
            style.setProperty("right", "auto");
        } else {
            super.setVisible(false);

            style.clearDisplay();
            style.clearZIndex();
            style.clearPosition();
            style.clearTop();
            style.clearLeft();
        }
    }

    @Override
    public Date getValue() {
        return value;
    }

    @Override
    public void setValue(final Date value) {
        if (value != null)
            this.value = value;
    }

    @Override
    public void setValue(final Date value, final boolean fireEvents) {
        setValue(value);

        if (fireEvents)
            ValueChangeEvent.fire(this, value);
    }

    @Override
    public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<Date> handler) {
        return addHandler(handler, ValueChangeEvent.getType());
    }

    @Override
    public int getOffsetHeight() {
        return container.getOffsetHeight();
    }

    @Override
    public int getOffsetWidth() {
        return container.getOffsetWidth();
    }

    private void checkForRedraw() {
        if (isAttached() && isVisible())
            show();
    }

    /**
     * Remove all children of the node.
     *
     * @param element base element
     * @return node
     */
    public final native Node removeAllChildren(final Element element) /*-{
                                                                      while (element.lastChild) {
                                                                      element.removeChild(element.lastChild);
                                                                      }
                                                                      }-*/;

    protected abstract class Picker extends Div {

        Date date;
        TableElement table;

        public Picker() {
            table = DOM.createTable().cast();
            table.setClassName(Styles.TABLE_CONDENSED);

            setVisible(false);

            sinkEvents(Event.ONCLICK);
        }

        public abstract void setDate(Date date);

        @Override
        public void setVisible(boolean visible) {
            if (visible) {
                getElement().getStyle().setDisplay(Style.Display.BLOCK);
            } else {
                super.setVisible(false);
            }
        }

    }

    private abstract class DatePicker extends Picker {

        @Override
        public void onBrowserEvent(Event event) {
            super.onBrowserEvent(event);

            switch (DOM.eventGetType(event)) {
            case Event.ONCLICK:
                Element target = Element.as(event.getEventTarget());
                if (target.getClassName().equals(Styles.PREV)) {
                    onPrevClicked();
                } else if (target.getClassName().equals(Styles.SWITCH)) {
                    onSwitchClicked();
                } else if (target.getClassName().equals(Styles.NEXT)) {
                    onNextClicked();
                } else if (target.getTagName().toLowerCase().equals(TableCellElement.TAG_TD)
                        || target.getTagName().toLowerCase().equals(SpanElement.TAG)) {
                    onItemClicked(target);
                }
                break;
            }
        }

        abstract protected void onPrevClicked();

        abstract protected void onSwitchClicked();

        abstract protected void onNextClicked();

        abstract protected void onItemClicked(Element target);

    }

    private class Days extends DatePicker {

        public Days() {
            setStyleName(Styles.DATEPICKER_DAYS);
        }

        @SuppressWarnings("deprecation")
        public void setDate(Date date) {
            this.date = date;

            removeAllChildren(getElement());

            int[] daysOfWeek = CalendarUtil.getStartingDayOfWeek() == 0 ? new int[] { 0, 1, 2, 3, 4, 5, 6 }
                    : new int[] { 1, 2, 3, 4, 5, 6, 0 };

            String html = TABLE_START;

            // generate heading
            html += "<thead><tr>";
            html += "<th class=\"prev\"></th>";
            html += "<th colspan=\"5\" class=\"switch\">" + calendarModel.getMonthAndYearFormatter().format(date)
                    + "</th>";
            html += "<th class=\"next\"></th></tr><tr>";

            for (int day : daysOfWeek) {
                html += "<th class=\"dow\">" + calendarModel.formatDayOfWeek(day) + "</th>";
            }

            html += "</tr></thead>";

            // generate content
            html += TBODY_START;
            Date startDate = toStartDate(CalendarUtil.copyDate(date));
            CalendarUtil.resetTime(startDate);

            for (int row = 1; row <= CalendarModel.WEEKS_IN_MONTH; row++) {
                html += "<tr>";

                for (int column = 0; column < CalendarModel.DAYS_IN_WEEK; column++) {
                    String postfix = date.getMonth() > startDate.getMonth() ? " old"
                            : (date.getMonth() < startDate.getMonth() ? " new" : "");

                    if (CalendarUtil.isSameDate(startDate, new Date()))
                        postfix += " today";

                    if (CalendarUtil.isSameDate(startDate, value))
                        postfix += " active";

                    if (enabledDates != null) {
                        if (!enabledDates.contains(startDate))
                            postfix += " disabled";
                    } else if (disabledDates != null) {
                        if (disabledDates.contains(startDate))
                            postfix += " disabled";
                    }

                    html += "<td class=\"day" + postfix + "\">" + calendarModel.formatDayOfMonth(startDate)
                            + "</td>";

                    CalendarUtil.addDaysToDate(startDate, 1);
                }

                html += "</tr>";
            }

            html += TBODY_END + TABLE_END;

            getElement().setInnerHTML(html);
        }

        @Override
        protected void onPrevClicked() {
            CalendarUtil.setToFirstDayOfMonth(date);
            CalendarUtil.addMonthsToDate(date, -1);
            setDate(date);
        }

        @Override
        protected void onSwitchClicked() {
            if (months == null) {
                months = new Months();
            }
            dateContainer.add(months);

            months.setDate(date);
            months.setVisible(true);

            setVisible(false);
        }

        @Override
        protected void onNextClicked() {
            CalendarUtil.setToFirstDayOfMonth(date);
            CalendarUtil.addMonthsToDate(date, 1);
            setDate(date);
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onItemClicked(Element target) {
            Element tbody = target.getParentElement().getParentElement();

            for (int row = 0; row < tbody.getChildCount(); row++) {
                Element tr = tbody.getChild(row).cast();

                for (int column = 0; column < tr.getChildCount(); column++) {
                    if (target.equals(tr.getChild(column))) {
                        Date startDate = toStartDate(CalendarUtil.copyDate(date));
                        int plus = ((row * CalendarModel.DAYS_IN_WEEK)) + column;

                        CalendarUtil.addDaysToDate(startDate, plus);

                        if (value != null) {
                            startDate.setHours(value.getHours());
                            startDate.setMinutes(value.getMinutes());
                            startDate.setSeconds(value.getSeconds());
                        }
                        setValue(startDate, true);

                        // refresh view
                        setDate(startDate);

                        if (autoClose)
                            hide();
                    }
                }
            }
        }

        @SuppressWarnings("deprecation")
        private Date toStartDate(Date date) {
            CalendarUtil.setToFirstDayOfMonth(date);
            CalendarUtil.addDaysToDate(date, -(date.getDay() - CalendarUtil.getStartingDayOfWeek()));
            return date;
        }

    }

    private class Months extends DatePicker {

        public Months() {
            setStyleName(Styles.DATEPICKER_MONTHS);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void setDate(Date date) {
            this.date = date;

            removeAllChildren(getElement());

            String html = TABLE_START;

            // generate heading
            html += "<thead>";
            html += "<tr><th class=\"prev\"></th>";
            html += "<th colspan=\"5\" class=\"switch\">" + calendarModel.getYearFormatter().format(date) + "</th>";
            html += "<th class=\"next\"></th></tr>";
            html += "</thead>";

            // generate content
            html += TBODY_START;
            html += "<tr><td colspan=\"7\">";
            for (int month = 0; month < CalendarModel.MONTHS_IN_YEAR; month++) {
                String postfix = month == value.getMonth() ? " active" : "";
                html += "<span class=\"month" + postfix + "\">" + calendarModel.formatMonth(month) + "</span>";
            }
            html += "</td><tr>";

            html += TBODY_END + TABLE_END;

            getElement().setInnerHTML(html);
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onPrevClicked() {
            date.setYear(date.getYear() - 1);
            setDate(date);
        }

        @Override
        protected void onSwitchClicked() {
            if (years == null)
                years = new Years();

            dateContainer.add(years);

            years.setDate(date);
            years.setVisible(true);

            setVisible(false);
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onNextClicked() {
            date.setYear(date.getYear() + 1);
            setDate(date);
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onItemClicked(Element target) {
            for (int i = 0; i < target.getParentElement().getChildCount(); i++) {
                if (target.getParentElement().getChild(i).equals(target)) {
                    CalendarUtil.setToFirstDayOfMonth(date);
                    date.setMonth(i);

                    days.setDate(date);
                    days.setVisible(true);

                    setVisible(false);
                }
            }
        }

    }

    private class Years extends DatePicker {

        public Years() {
            setStyleName(Styles.DATEPICKER_DAYS);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void setDate(Date date) {
            this.date = date;

            removeAllChildren(getElement());

            String html = TABLE_START;

            // round year
            date.setYear(BigDecimal.valueOf(date.getYear() / 10).intValue() * 10);

            Date endDate = CalendarUtil.copyDate(date);
            endDate.setYear(date.getYear() + 9);

            String startYear = calendarModel.getYearFormatter().format(date);
            String endYear = calendarModel.getYearFormatter().format(endDate);

            // generate heading
            html += "<thead>";
            html += "<tr><th class=\"prev\"></th>";
            html += "<th colspan=\"5\" class=\"switch\">" + startYear + "-" + endYear + "</th>";
            html += "<th class=\"next\"></th></tr>";
            html += "</thead>";

            // generate content
            Date startDate = CalendarUtil.copyDate(date);
            startDate.setYear(date.getYear() - 1);

            html += "<tr><td colspan=\"7\">";
            for (int i = 0; i < 12; i++) {
                String postfix = startDate.getYear() < date.getYear() || startDate.getYear() > endDate.getYear()
                        ? " old"
                        : "";
                postfix += startDate.getYear() == value.getYear() ? " active" : "";

                html += "<span class=\"year" + postfix + "\">" + calendarModel.getYearFormatter().format(startDate)
                        + "</span>";

                startDate.setYear(startDate.getYear() + 1);
            }
            html += "</td><tr>" + TABLE_END;

            getElement().setInnerHTML(html);
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onPrevClicked() {
            date.setYear(date.getYear() - 10);
            setDate(date);
        }

        @Override
        protected void onSwitchClicked() {
            // nothing
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onNextClicked() {
            date.setYear(date.getYear() + 10);
            setDate(date);
        }

        @Override
        @SuppressWarnings("deprecation")
        protected void onItemClicked(Element target) {
            for (int i = 0; i < target.getParentElement().getChildCount(); i++) {
                if (target.getParentElement().getChild(i).equals(target)) {
                    CalendarUtil.setToFirstDayOfMonth(date);
                    date.setYear(date.getYear() + i - 1);

                    months.setDate(date);
                    months.setVisible(true);

                    setVisible(false);
                }
            }
        }

    }

    private class Time extends Picker {

        private final String incrementHoursClass = "incrementHours";
        private final String incrementMinutesClass = "incrementMinutes";
        private final String decrementHoursClass = "decrementHours";
        private final String decrementMinutesClass = "decrementMinutes";
        private final String togglePeriodClass = "togglePeriod";

        public Time() {
            setStyleName(Styles.TIMEPICKER_PICKER);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void setDate(Date date) {
            this.date = date;

            removeAllChildren(getElement());

            String html = TABLE_START + TBODY_START;

            // header
            html += "<tr>" + "<td>" + "<a class=\"btn " + incrementHoursClass
                    + "\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a>" + "</td>" + separator("")
                    + "<td>" + "<a class=\"btn " + incrementMinutesClass
                    + "\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a>" + "</td>"
                    + (hourFormat == null || hourFormat == HourFormat._12 ? separator("") : "") + "</tr>";

            // content
            html += "<tr>" + "<td><span class=\"" + Styles.TIMEPICKER_HOUR + " \">"
                    + calendarModel.getHourFormatter().format(date) + "</span></td>" + separator(":")
                    + "<td><span class=\"" + Styles.TIMEPICKER_MINUTE + "\">"
                    + calendarModel.getMinuteFormatter().format(date) + "</span></td>"
                    + (hourFormat == null || hourFormat == HourFormat._12
                            ? separator("") + "<td><button type=\"button\" class=\"btn btn-primary "
                                    + togglePeriodClass + "\">" + (date.getHours() > 11 ? "PM" : "AM")
                                    + "</button></td>"
                            : "")
                    + "</tr>";

            // footer
            html += "<tr>" + "<td>" + "<a class=\"btn " + decrementHoursClass
                    + "\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a>" + "</td>" + separator("")
                    + "<td>" + "<a class=\"btn " + decrementMinutesClass
                    + "\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a>" + "</td>"
                    + (hourFormat == null || hourFormat == HourFormat._12 ? separator("") : "") + "</tr>";

            html += TBODY_END + TABLE_END;

            getElement().setInnerHTML(html);
        }

        @Override
        public void onBrowserEvent(Event event) {
            super.onBrowserEvent(event);

            switch (DOM.eventGetType(event)) {
            case Event.ONCLICK:
                Element target = Element.as(event.getEventTarget());
                if (target.getClassName().contains(incrementHoursClass)
                        || target.getParentElement().getClassName().contains(incrementHoursClass)) {
                    onIncrementHoursClicked();
                } else if (target.getClassName().contains(incrementMinutesClass)
                        || target.getParentElement().getClassName().contains(incrementMinutesClass)) {
                    onIncrementMinutesClicked();
                } else if (target.getClassName().contains(decrementHoursClass)
                        || target.getParentElement().getClassName().contains(decrementHoursClass)) {
                    onDecrementHoursClicked();
                } else if (target.getClassName().contains(decrementMinutesClass)
                        || target.getParentElement().getClassName().contains(decrementMinutesClass)) {
                    onDecrementMinutesClicked();
                } else if (target.getClassName().contains(decrementMinutesClass)
                        || target.getParentElement().getClassName().contains(decrementMinutesClass)) {
                    onDecrementMinutesClicked();
                } else if (target.getClassName().contains(decrementMinutesClass)
                        || target.getParentElement().getClassName().contains(decrementMinutesClass)) {
                    onDecrementMinutesClicked();
                } else if (target.getClassName().contains(togglePeriodClass)
                        || target.getParentElement().getClassName().contains(togglePeriodClass)) {
                    onTogglePeriodClicked();
                } else if (target.getClassName().contains(Styles.TIMEPICKER_HOUR)) {
                    onHourClicked();
                } else if (target.getClassName().contains(Styles.TIMEPICKER_MINUTE)) {
                    onMinuteClicked();
                }

                break;
            }
        }

        @SuppressWarnings("deprecation")
        private void onDecrementMinutesClicked() {
            date.setMinutes(date.getMinutes() - 1);
            updateValue();
        }

        @SuppressWarnings("deprecation")
        private void onDecrementHoursClicked() {
            date.setHours(date.getHours() - 1);
            updateValue();
        }

        @SuppressWarnings("deprecation")
        private void onIncrementMinutesClicked() {
            date.setMinutes(date.getMinutes() + 1);
            updateValue();
        }

        @SuppressWarnings("deprecation")
        private void onIncrementHoursClicked() {
            date.setHours(date.getHours() + 1);
            updateValue();
        }

        @SuppressWarnings("deprecation")
        private void onTogglePeriodClicked() {
            if (date.getHours() > 11) {
                date.setHours(date.getHours() - 12);
            } else {
                date.setHours(date.getHours() + 12);
            }
            updateValue();
        }

        private void onHourClicked() {
            if (hours == null)
                hours = new Hours();

            timeContainer.add(hours);

            hours.setDate(date);
            hours.setVisible(true);

            setVisible(false);
        }

        private void onMinuteClicked() {
            if (minutes == null)
                minutes = new Minutes();

            timeContainer.add(minutes);

            minutes.setDate(date);
            minutes.setVisible(true);

            setVisible(false);
        }

        private void updateValue() {
            setDate(date);
            setValue(date, true);
        }

        private String separator(String text) {
            return "<td class=\"separator\">" + text + "</td>";
        }

    }

    private class Hours extends Picker {

        public Hours() {
            setStyleName(Styles.TIMEPICKER_HOURS);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void setDate(Date date) {
            this.date = date;

            removeAllChildren(getElement());

            String html = TABLE_START;

            // generate content
            Date startDate = CalendarUtil.copyDate(date);
            startDate.setHours(hourFormat == HourFormat._23 ? 0 : 1);

            html += TBODY_START;

            for (int row = 0; row < (hourFormat == null || hourFormat == HourFormat._12 ? 3 : 6); row++) {
                html += "<tr>";

                for (int column = 0; column < 4; column++) {
                    html += "<td class=\"hour\">" + calendarModel.getHourFormatter().format(startDate) + "</td>";

                    startDate.setHours(startDate.getHours() + 1);
                }

                html += "</tr>";
            }

            html += TBODY_END + TABLE_END;

            getElement().setInnerHTML(html);
        }

        @Override
        public void onBrowserEvent(Event event) {
            super.onBrowserEvent(event);

            switch (DOM.eventGetType(event)) {
            case Event.ONCLICK:
                Element target = Element.as(event.getEventTarget());
                if (target.getTagName().toLowerCase().equals(TableCellElement.TAG_TD))
                    onItemClicked(target);
                break;
            }
        }

        @SuppressWarnings("deprecation")
        private void onItemClicked(Element target) {
            Element tbody = target.getParentElement().getParentElement();

            for (int row = 0; row < tbody.getChildCount(); row++) {
                Element tr = tbody.getChild(row).cast();

                for (int column = 0; column < tr.getChildCount(); column++) {
                    if (target.equals(tr.getChild(column))) {
                        int hours = ((row * 4)) + column;

                        if (hourFormat == null || hourFormat == HourFormat._12 || hourFormat == HourFormat._24)
                            hours++;

                        date.setHours(hours);

                        setValue(date, true);

                        time.setDate(date);
                        time.setVisible(true);

                        setVisible(false);
                    }
                }
            }
        }

    }

    private class Minutes extends Picker {

        public Minutes() {
            setStyleName(Styles.TIMEPICKER_MINUTES);
        }

        @Override
        @SuppressWarnings("deprecation")
        public void setDate(Date date) {
            this.date = date;

            removeAllChildren(getElement());

            String html = TABLE_START;

            // generate content
            Date startDate = CalendarUtil.copyDate(date);
            startDate.setMinutes(0);

            html += TBODY_START;

            for (int row = 0; row < 3; row++) {
                html += "<tr>";

                for (int column = 0; column < 4; column++) {
                    html += "<td class=\"minute\">" + calendarModel.getMinuteFormatter().format(startDate)
                            + "</td>";

                    startDate.setMinutes(startDate.getMinutes() + 5);
                }

                html += "</tr>";
            }

            html += TBODY_END + TABLE_END;

            getElement().setInnerHTML(html);
        }

        @Override
        public void onBrowserEvent(Event event) {
            super.onBrowserEvent(event);

            switch (DOM.eventGetType(event)) {
            case Event.ONCLICK:
                Element target = Element.as(event.getEventTarget());
                if (target.getTagName().toLowerCase().equals(TableCellElement.TAG_TD))
                    onItemClicked(target);
                break;
            }
        }

        @SuppressWarnings("deprecation")
        private void onItemClicked(Element target) {
            Element tbody = target.getParentElement().getParentElement();

            for (int row = 0; row < tbody.getChildCount(); row++) {
                Element tr = tbody.getChild(row).cast();

                for (int column = 0; column < tr.getChildCount(); column++) {
                    if (target.equals(tr.getChild(column))) {
                        int minutes = (((row * 4)) + column) * 5;

                        date.setMinutes(minutes);

                        setValue(date, true);

                        time.setDate(date);
                        time.setVisible(true);

                        setVisible(false);
                    }
                }
            }
        }

    }

}