de.swm.commons.mobile.client.widgets.date.DatePopup.java Source code

Java tutorial

Introduction

Here is the source code for de.swm.commons.mobile.client.widgets.date.DatePopup.java

Source

/*
 * Copyright 2011 SWM Services GmbH.
 * 
 * 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 de.swm.commons.mobile.client.widgets.date;

import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.Style.FontWeight;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import de.swm.commons.mobile.client.SWMMobile;
import de.swm.commons.mobile.client.widgets.*;

import java.util.Date;

/**
 * Popup to select date and time.
 */
public class DatePopup extends PopupPanel {

    /**
     * Additional style: margin.
     */
    private static final double MARGIN_YEAR = 12.0;

    /**
     * Additional style: margin.
     */
    private static final double MARGIN_PIXEL_SIZE_4_0 = 4.0;

    /**
     * Number Formatter for two-digit number strings.
     */
    private final NumberFormat numFormat = NumberFormat.getFormat("00");

    /**
     * Date selection handler.
     */
    public interface DateSelectionHandler {

        /**
         * Sets the selected date.
         *
         * @param selectedDate the selected date
         */
        void dateSelected(Date selectedDate);

        /**
         * Called, if date selection was canceled.
         */
        void dateSelectionCancelled();

    }

    /**
     * Defines the different fields in the popup.
     */
    private enum DateField {
        DAY, MONTH, YEAR, HOUR, MINUTE
    }

    /**
     * The underlying selection handler.
     */
    private DateSelectionHandler selectionHandler;

    /**
     * Label displaying the day.
     */
    private TextBox dayLabel;

    /**
     * Label displaying the month.
     */
    private TextBox monthLabel;

    /**
     * Label displaying the year.
     */
    private TextBox yearLabel;

    /**
     * Label displaying the hour.
     */
    private TextBox hourLabel;

    /**
     * Label displaying the minute.
     */
    private TextBox minuteLabel;

    /**
     * Delegate for date calculations.
     */
    private DateCalculation dateCalc;

    private final VerticalPanel mainPanel;

    private final DateStyle dateStyle;

    private TextBox dateTextBox;

    private final Label dateTimeSelectionCaption;

    private DropDownList<String> dropDown;

    /**
     * Default constructor.
     *
     * @param givenDate date to start with
     * @param dateStyle der date style
     * @param handler   date selection handler
     */
    public DatePopup(Date givenDate, final DateStyle dateStyle, DateSelectionHandler handler) {

        setStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().datePopup());
        setGlassStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateGlassPanel());

        selectionHandler = handler;
        if (givenDate == null) {
            givenDate = new Date();
        }

        this.dateStyle = dateStyle;
        dateCalc = new DateCalculation(givenDate);

        mainPanel = new VerticalPanel();
        dateTimeSelectionCaption = new Label(SWMMobile.getI18N().dateTimeSelectionCaption());
        mainPanel.add(dateTimeSelectionCaption);
        dateTimeSelectionCaption.setVisible(false);

        if (SWMMobile.getOsDetection().isIOs()) {
            renderIOS5DateBox(givenDate, dateStyle);
        } else {
            renderDateBox(dateStyle);
        }

        setWidget(mainPanel);
    }

    /**
     * Adds another user-defined widget to the date popup, e.g. for additional options when choosing the date.
     *
     * @param widget   additional widget
     * @param position of the widget in the main vertical panel (0=first widget)
     */
    public void addWidget(Widget widget, int position) {
        mainPanel.insert(widget, position);
    }

    /**
     * Adds a drop down list to choose times relative to the current time (e.g., "in 10 Minutes").
     *
     * @param choiceTitles          titles of the available choices
     * @param choiceRelativeMinutes for each choice, difference to the current time in minutes
     * @param initialChoice         initially selected choice - must be one of choiceRelativeMinutes, or null if no selection
     * @param includeNowButton      whether a "now" button should be included to choose the current time
     * @param closeOnChoice         whether the date popup should be immediately closed when a relative choice is made
     */
    public void addRelativeTimeChooser(String[] choiceTitles, int[] choiceRelativeMinutes, Integer initialChoice,
            final boolean includeNowButton, final boolean closeOnChoice) {
        if (choiceTitles.length != choiceRelativeMinutes.length) {
            throw new IllegalArgumentException(
                    "Must supply same number of choiceTitles and choiceRelativeMilliseconds!");
        }

        HorizontalPanel relativeTimeChooserPanel = new HorizontalPanel();
        relativeTimeChooserPanel.addStyleName(
                SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateRelativeTimeChooserPanel());
        if (includeNowButton) {
            Button nowButton = new Button(SWMMobile.getI18N().nowButton(), new ClickHandler() {

                /**
                 * {@inheritDoc}
                 */
                @Override
                public void onClick(ClickEvent event) {
                    Date currentDate = new Date();
                    if (closeOnChoice) {
                        hide();
                        selectionHandler.dateSelected(currentDate);
                    } else {
                        updateDate(currentDate);
                    }
                }

            });
            relativeTimeChooserPanel.add(nowButton);
        }

        Label label = new Label(SWMMobile.getI18N().relativeTimeChooserLabel());
        relativeTimeChooserPanel.add(label);

        dropDown = new DropDownList<String>();
        dropDown.add(new DropDownItem(null, ""));
        for (int i = 0; i < choiceTitles.length; i++) {
            dropDown.add(new DropDownItem(new Integer(choiceRelativeMinutes[i]).toString(), choiceTitles[i]));
        }

        if (initialChoice != null) {
            // TODO: possibly update date selector time for initial choice?
            dropDown.setValue(initialChoice.toString());
        }

        dropDown.addValueChangeHandler(new ValueChangeHandler<String>() {

            @Override
            public void onValueChange(ValueChangeEvent<String> event) {
                if ((event.getValue() == null) || event.getValue().equals("") || event.getValue().equals("null")) {
                    return;
                }
                int relativeMinutes = Integer.parseInt(event.getValue().toString());
                Date newDate = new Date(new Date().getTime() + relativeMinutes * 60000);
                if (closeOnChoice) {
                    hide();
                    selectionHandler.dateSelected(newDate);
                } else {
                    updateDate(newDate);
                }
            }

        });

        relativeTimeChooserPanel.add(dropDown);
        mainPanel.insert(relativeTimeChooserPanel, 0);
    }

    /**
     * Clears the relative time displayed in the relative time chooser.
     */
    private void clearRelativeTime() {

        if (dropDown != null) {
            dropDown.setValue(null);
        }
    }

    /**
     * Sets the visibility of the caption for the date/time selection (default: caption is not visible).
     *
     * @param visible visibility of caption for date/time selection
     */
    public void setDateTimeSelectionCaptionVisible(boolean visible) {
        dateTimeSelectionCaption.setVisible(visible);
    }

    /**
     * Renders the date box for devices where native date input field is not avalilable.
     *
     * @param givenDate
     * @param dateStyle
     */
    private void renderIOS5DateBox(final Date givenDate, final DateStyle dateStyle) {

        final InputElement dateInput = createNumberInputElement(dateStyle.getHtmlInputType());

        dateInput.focus();
        dateTextBox = TextBox.wrap(dateInput);
        dateTextBox.setSize("95%", "40px");

        dateTextBox.setValue(dateCalc.formatToRfc3339(givenDate, dateStyle, true));
        // TODO: on value change, call clearRelativeTime() (special handling necessary)

        HorizontalPanel dateInputPanel = new HorizontalPanel();
        dateInputPanel.addStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateInputPanel());
        dateInputPanel.add(dateTextBox);
        mainPanel.add(dateInputPanel);

        Widget commandPanel = createCommandPanel(new ClickHandler() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void onClick(ClickEvent event) {
                hide();
                selectionHandler.dateSelected(dateCalc.parseRfc3339(dateTextBox.getText().trim(), dateStyle, true));
            }

        });
        mainPanel.add(commandPanel);

        dateTextBox.setFocus(true);
    }

    /**
     * Renders the date box for devices where native date input field is not avalilable.
     */
    private void renderDateBox(DateStyle dateStyle) {
        HorizontalPanel spinnerPanel = new HorizontalPanel();
        spinnerPanel.addStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateSpinnerPanel());

        VerticalPanel dayPanel = createSpinner(DateField.DAY);
        dayPanel.getElement().getStyle().setMarginRight(MARGIN_PIXEL_SIZE_4_0, Unit.PX);
        spinnerPanel.add(dayPanel);

        VerticalPanel monthPanel = createSpinner(DateField.MONTH);
        monthPanel.getElement().getStyle().setMarginRight(MARGIN_PIXEL_SIZE_4_0, Unit.PX);
        spinnerPanel.add(monthPanel);

        VerticalPanel yearPanel = createSpinner(DateField.YEAR);
        yearPanel.getElement().getStyle().setMarginRight(MARGIN_YEAR, Unit.PX);
        spinnerPanel.add(yearPanel);

        VerticalPanel hourPanel = createSpinner(DateField.HOUR);
        spinnerPanel.add(hourPanel);

        Label separatorLabel = new Label(":");
        separatorLabel.setVisible(false);
        separatorLabel.getElement().getStyle().setFontWeight(FontWeight.BOLD);
        spinnerPanel.add(separatorLabel);

        VerticalPanel minutePanel = createSpinner(DateField.MINUTE);
        spinnerPanel.add(minutePanel);

        if (DateStyle.DATE.equals(dateStyle) || DateStyle.DATETIME.equals(dateStyle)) {
            dayPanel.setVisible(true);
            monthPanel.setVisible(true);
            yearPanel.setVisible(true);
        }

        if (DateStyle.TIME.equals(dateStyle) || DateStyle.DATETIME.equals(dateStyle)) {
            hourPanel.setVisible(true);
            separatorLabel.setVisible(true);
            minutePanel.setVisible(true);
        }

        mainPanel.add(spinnerPanel);

        Widget commandPanel = createCommandPanel(new ClickHandler() {

            /**
             * {@inheritDoc}
             */
            @Override
            public void onClick(ClickEvent event) {
                hide();
                Date selectedDate = dateCalc.getDate();
                selectionHandler.dateSelected(selectedDate);
            }

        });
        mainPanel.add(commandPanel);
    }

    /**
     * Creates the command panel (with the buttons to confirm or cancel the date choice).
     * Can be overridden if command panel should contain different widgets.
     *
     * @param okClickHandler ClickHandler that is called when the confirm button is pressed and which parses the date.
     * @return Widget for Command Panel
     */
    protected Widget createCommandPanel(final ClickHandler okClickHandler) {
        HorizontalPanel commandPanel = new HorizontalPanel();
        commandPanel.addStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateCommandPanel());
        Button okButton = new Button(SWMMobile.getI18N().confirmButton(), okClickHandler);
        okButton.addStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateOkButton());
        commandPanel.add(okButton);
        Button cancelButton = new Button(SWMMobile.getI18N().cancelButton(), new ClickHandler() {

            /**
             * {@inheritDoc}
             */
            @Override
            public void onClick(ClickEvent event) {
                hide();
                selectionHandler.dateSelectionCancelled();
            }

        });
        cancelButton.addStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateCancelButton());
        commandPanel.add(cancelButton);
        return commandPanel;
    }

    /**
     * Will display the popup centered.
     *
     * @param glassEffect true if glass efect
     */
    public void showCentered(boolean glassEffect) {
        setGlassEnabled(glassEffect);
        setPopupPositionAndShow(new PositionCallback() {

            /**
             * {@inheritDoc}
             */
            @Override
            public void setPosition(int offsetWidth, int offsetHeight) {
                int left = (Window.getClientWidth() - offsetWidth) / 2;
                int top = (Window.getClientHeight() - offsetHeight) / 2;
                setPopupPosition(left, top);
            }
        });
    }

    public DateSelectionHandler getSelectionHandler() {
        return selectionHandler;
    }

    public void setSelectionHandler(DateSelectionHandler selectionHandler) {
        this.selectionHandler = selectionHandler;
    }

    /**
     * Will create the spinner buttons.
     *
     * @param df the date field
     * @return spinner button panel
     */
    private VerticalPanel createSpinner(final DateField df) {
        VerticalPanel vPanel = new VerticalPanel();
        vPanel.setVisible(false);
        ImageButton upButton = new ImageButton(SWMMobile.getTheme().getMGWTImageBundle().arrowup(),
                new ClickHandler() {
                    /**
                     * {@inheritDoc}
                     */
                    @Override
                    public void onClick(ClickEvent event) {
                        switch (df) {
                        case DAY:
                            dateCalc.incrementDay();
                            break;
                        case MONTH:
                            dateCalc.incrementMonth();
                            break;
                        case YEAR:
                            dateCalc.incrementYear();
                            break;
                        case HOUR:
                            dateCalc.incrementHour();
                            break;
                        case MINUTE:
                            dateCalc.incrementMinute();
                            break;
                        default:
                            break;
                        }
                        updateLabels();
                        clearRelativeTime();
                    }

                });
        vPanel.add(upButton);

        final TextBox label;
        switch (df) {
        case DAY:
            dayLabel = new TextBox();
            dayLabel.setWidth("2rem");
            dayLabel.setText(dateCalc.getDay() + ".");
            label = dayLabel;
            break;
        case MONTH:
            monthLabel = new TextBox();
            monthLabel.setWidth("2rem");
            monthLabel.setText(dateCalc.getMonth() + ".");
            label = monthLabel;
            break;
        case YEAR:
            yearLabel = new TextBox();
            yearLabel.setWidth("4rem");
            yearLabel.setText(dateCalc.getYear());
            label = yearLabel;
            break;
        case HOUR:
            hourLabel = new TextBox();
            hourLabel.setWidth("2rem");
            hourLabel.setText(numFormat.format(dateCalc.getHour()));
            label = hourLabel;
            break;
        case MINUTE:
            minuteLabel = new TextBox();
            minuteLabel.setWidth("2rem");
            label = minuteLabel;
            minuteLabel.setText(numFormat.format(dateCalc.getMinute()));
            break;
        default:
            label = new TextBox();
            break;
        }
        label.addFocusHandler(new FocusHandler() {

            @Override
            public void onFocus(FocusEvent event) {
                label.setText("");
            }
        });
        label.addBlurHandler(new BlurHandler() {

            @Override
            public void onBlur(BlurEvent event) {
                switch (df) {
                case DAY:
                    dateCalc.setDay(label.getText());
                    updateLabels();
                    clearRelativeTime();
                    break;
                case MONTH:
                    dateCalc.setMonth(label.getText());
                    updateLabels();
                    clearRelativeTime();
                    break;
                case YEAR:
                    dateCalc.setYear(label.getText());
                    updateLabels();
                    clearRelativeTime();
                    break;
                case HOUR:
                    dateCalc.setHour(label.getText());
                    updateLabels();
                    clearRelativeTime();
                    break;
                case MINUTE:
                    dateCalc.setMinute(label.getText());
                    updateLabels();
                    clearRelativeTime();
                    break;
                default:
                    break;
                }

            }
        });

        label.setStyleName(SWMMobile.getTheme().getMGWTCssBundle().getPopupsCss().dateValueLabel());
        vPanel.add(label);

        ImageButton downButton = new ImageButton(SWMMobile.getTheme().getMGWTImageBundle().arrowdown(),
                new ClickHandler() {

                    @Override
                    public void onClick(ClickEvent event) {
                        switch (df) {
                        case DAY:
                            dateCalc.decrementDay();
                            break;
                        case MONTH:
                            dateCalc.decrementMonth();
                            break;
                        case YEAR:
                            dateCalc.decrementYear();
                            break;
                        case HOUR:
                            dateCalc.decrementHour();
                            break;
                        case MINUTE:
                            dateCalc.decrementMinute();
                            break;
                        default:
                            break;
                        }
                        updateLabels();
                        clearRelativeTime();
                    }
                });
        vPanel.add(downButton);

        return vPanel;
    }

    /**
     * Update the date displayed in the date popup.
     *
     * @param newDate date to be displayed
     */
    private void updateDate(Date newDate) {
        dateCalc = new DateCalculation(newDate);
        if (dateTextBox != null) {
            dateTextBox.setValue(dateCalc.formatToRfc3339(newDate, dateStyle, true));
        } else {
            updateLabels();
        }
    }

    /**
     * Update the labels with internal state.
     */
    private void updateLabels() {
        yearLabel.setText(dateCalc.getYear());
        monthLabel.setText(dateCalc.getMonth() + ".");
        dayLabel.setText(dateCalc.getDay() + ".");
        hourLabel.setText(numFormat.format(dateCalc.getHour()));
        minuteLabel.setText(numFormat.format(dateCalc.getMinute()));
    }

    /**
     * Create the HTML input element.
     *
     * @param type the type
     * @return the input element.
     */
    private static native InputElement createNumberInputElement(String type) /*-{
                                                                             var e = $doc.createElement("INPUT");
                                                                             e.type = type;
                                                                             return e;
                                                                             }-*/;

}