All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.swm.commons.mobile.client.widgets.date.DatePopup Maven / Gradle / Ivy

There is a newer version: 3.1
Show newest version
/*
 * 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 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();
		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() {

			@Override
			public void onValueChange(ValueChangeEvent 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;
    }-*/;

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy