/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.coregui.client.inventory.common.graph;
import java.util.Date;
import com.smartgwt.client.types.FormErrorOrientation;
import com.smartgwt.client.types.SelectionType;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.DateItem;
import com.smartgwt.client.widgets.form.fields.RowSpacerItem;
import com.smartgwt.client.widgets.form.fields.SpinnerItem;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import org.rhq.coregui.client.components.measurement.RefreshIntervalMenu;
import org.rhq.coregui.client.inventory.AutoRefresh;
import org.rhq.coregui.client.util.Log;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton.ButtonColor;
import org.rhq.coregui.client.util.enhanced.EnhancedVLayout;
/**
* Component to allow selection of Date/Time range for graphs using a radio button group.
* The DateTimeButton enum defines the button labels and time ranges change this if you
* wish to add/delete custom time ranges.
*
* @author Mike Thompson
*/
public class ButtonBarDateTimeRangeEditor extends EnhancedVLayout {
private static final String TIMERANGE = "graphtimerange";
private static final int BUTTON_WIDTH = 28;
public final String DATE_TIME_FORMAT = MSG.common_buttonbar_datetime_format_moment_js();
private Refreshable refreshable;
private Label dateRangeLabel;
private DateTimeButtonBarClickHandler dateTimeButtonBarClickHandler;
// just a reference to pass to CustomDateRangeWindow as it must be final
// so 'this' won't work
final private ButtonBarDateTimeRangeEditor self;
private boolean isAutoRefresh;
private RefreshIntervalMenu refreshIntervalMenu;
private EnhancedIButton refreshButton;
public ButtonBarDateTimeRangeEditor(Refreshable refreshable) {
this.self = this;
this.refreshable = refreshable;
this.isAutoRefresh = this.refreshable instanceof AutoRefresh;
dateTimeButtonBarClickHandler = new DateTimeButtonBarClickHandler();
}
private void createButtons() {
ToolStrip toolStrip = new ToolStrip();
toolStrip.setWidth100();
toolStrip.setHeight(34);
toolStrip.addSpacer(10);
for (DateTimeButton dateTimeButton : DateTimeButton.values()) {
IButton oneHourButton = new EnhancedIButton(dateTimeButton.label);
oneHourButton.setWidth(BUTTON_WIDTH);
oneHourButton.setActionType(SelectionType.RADIO);
oneHourButton.setRadioGroup(TIMERANGE);
oneHourButton.addClickHandler(dateTimeButtonBarClickHandler);
toolStrip.addMember(oneHourButton);
}
IButton customButton = new EnhancedIButton(MSG.common_buttonbar_custom());
customButton.setWidth(60);
customButton.setActionType(SelectionType.RADIO);
customButton.setRadioGroup(TIMERANGE);
customButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent clickEvent) {
CustomDateRangeWindow customDateRangeWindow = new CustomDateRangeWindow(MSG
.common_buttonbar_custom_window_title(), MSG.common_buttonbar_custom_window_subtitle(), self,
new Date(CustomDateRangeState.getInstance().getStartTime()), new Date(CustomDateRangeState
.getInstance().getEndTime()));
CustomDateRangeState.getInstance().setCustomDateRangeActive(true);
customDateRangeWindow.show();
}
});
toolStrip.addMember(customButton);
toolStrip.addSpacer(30);
dateRangeLabel = new Label();
dateRangeLabel.setWidth(400);
dateRangeLabel.addStyleName("graphDateTimeRangeLabel");
showUserFriendlyTimeRange(CustomDateRangeState.getInstance().getStartTime(), CustomDateRangeState.getInstance()
.getEndTime());
toolStrip.addMember(dateRangeLabel);
// Only allow auto refresh rate change in views actually performing auto refresh, otherwise offer only refresh
toolStrip.addSpacer(20);
if (isAutoRefresh) {
refreshIntervalMenu = new RefreshIntervalMenu((AutoRefresh)refreshable);
toolStrip.addMember(refreshIntervalMenu);
} else {
this.refreshButton = new EnhancedIButton(MSG.common_button_refresh());
refreshButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent clickEvent) {
redrawGraphs();
}
});
toolStrip.addMember(refreshButton);
}
addMember(toolStrip);
}
public boolean isCustomTimeRangeActive() {
return CustomDateRangeState.getInstance().isCustomDateRangeActive();
}
public void setCustomDateRangeActive(boolean customDateRangeActive) {
CustomDateRangeState.getInstance().setCustomDateRangeActive(customDateRangeActive);
}
public void redrawGraphs() {
refreshable.refreshData();
}
@Override
protected void onDraw() {
super.onDraw();
removeMembers(getMembers());
createButtons();
}
@Override
public void parentResized() {
super.parentResized();
removeMembers(getMembers());
createButtons();
}
public Long getStartTime() {
return CustomDateRangeState.getInstance().getStartTime();
}
public Long getEndTime() {
return CustomDateRangeState.getInstance().getEndTime();
}
public Date calculateStartDate(Date endDate, String dateTimeSelection) {
long dateTimeOffset = 0;
for (DateTimeButton dateTimeButton : DateTimeButton.values()) {
if (dateTimeButton.label.equals(dateTimeSelection)) {
dateTimeOffset = dateTimeButton.timeSpanInSeconds * 1000;
break;
}
}
return new Date(endDate.getTime() - dateTimeOffset);
}
public native void showUserFriendlyTimeRange(double startTime, double endTime) /*-{
"use strict";
var startDateMoment = $wnd.moment(startTime),
endDateMoment = $wnd.moment(endTime),
dateTimeFormat = this.@org.rhq.coregui.client.inventory.common.graph.ButtonBarDateTimeRangeEditor::DATE_TIME_FORMAT,
formattedDateRange = startDateMoment.format(dateTimeFormat) + ' - ' + endDateMoment.format(dateTimeFormat),
timeRange = endDateMoment.from(startDateMoment,true);
$wnd.jQuery('.graphDateTimeRangeLabel').text(formattedDateRange+' ('+timeRange+')');
}-*/;
public void updateTimeRangeToNow() {
if (!isCustomTimeRangeActive()) {
Date now = new Date();
long timeRange = CustomDateRangeState.getInstance().getTimeRange();
Date newStartDate = new Date(now.getTime() - timeRange);
showUserFriendlyTimeRange(newStartDate.getTime(), now.getTime());
saveDateRange(newStartDate.getTime(), now.getTime());
}
}
/**
* Whenever we make a change to the date range save it here so it gets propagated to
* the correct places.
*
* @param startTime double because JSNI doesn't support long
* @param endTime double because JSNI doesn't support long
*/
public void saveDateRange(double startTime, double endTime) {
// if the encompassing view already handles its own refresh (e.g. AbstractD3GrpahListView) then don't
// let a preference update cause a whole gui refresh (which it does by default to apply the new preference).
// the two refreshes are redundant at best, step on each other at worst..
boolean allowPreferenceUpdateRefresh = !isAutoRefresh;
CustomDateRangeState.getInstance().saveDateRange(startTime, endTime, allowPreferenceUpdateRefresh);
}
private class DateTimeButtonBarClickHandler implements ClickHandler {
@Override
public void onClick(ClickEvent clickEvent) {
IButton button = (IButton) clickEvent.getSource();
String selectedDateTimeRange = button.getTitle();
CustomDateRangeState.getInstance().setCustomDateRangeActive(false);
Date now = new Date();
Date calculatedStartDateTime = calculateStartDate(now, selectedDateTimeRange);
saveDateRange(calculatedStartDateTime.getTime(), now.getTime());
redrawGraphs();
showUserFriendlyTimeRange(calculatedStartDateTime.getTime(), now.getTime());
}
}
@SuppressWarnings("GwtInconsistentSerializableClass")
/**
* This enum defines the button labels and time ranges used in the toolbar.
*/
private enum DateTimeButton {
oneHour("1h", 60 * 60), fourHour("4h", 4 * 60 * 60), eightHour("8h", 8 * 60 * 60), twelveHour("12h",
12 * 60 * 60), oneDay("1d", 24 * 60 * 60), fiveDay("5d", 5 * 24 * 60 * 60), oneMonth("1m",
30 * 24 * 60 * 60), threeMonth("3m", 3 * 30 * 24 * 60 * 60), sixMonth("6m", 6 * 30 * 24 * 60 * 60);
private final String label;
private final long timeSpanInSeconds;
DateTimeButton(String label, long timeSpanInSeconds) {
this.label = label;
this.timeSpanInSeconds = timeSpanInSeconds;
}
}
public class CustomDateRangeWindow extends Window {
public CustomDateRangeWindow(String title, String windowTitle,
final ButtonBarDateTimeRangeEditor buttonBarDateTimeRangeEditor, Date startTime, Date endTime) {
super();
setTitle("");
setShowMinimizeButton(false);
setShowMaximizeButton(false);
setShowCloseButton(true);
setIsModal(true);
setShowModalMask(true);
setWidth(420);
setHeight(480);
setShowResizer(true);
setCanDragResize(true);
centerInPage();
DynamicForm form = new DynamicForm();
form.setGroupTitle(windowTitle + " "+title);
form.setIsGroup(true);
form.setMargin(20);
form.setAutoFocus(true);
form.setShowErrorText(true);
form.setErrorOrientation(FormErrorOrientation.BOTTOM);
form.setHeight100();
form.setWidth100();
form.setPadding(5);
form.setLayoutAlign(VerticalAlignment.BOTTOM);
final DateItem startDateItem = new DateItem("startDate", MSG.common_buttonbar_start_date());
startDateItem.setValue(startTime);
final SpinnerItem startTimeHours = new SpinnerItem("startTimeHours",MSG.chart_slider_button_bar_hour());
startTimeHours.setMax(23);
startTimeHours.setMin(0);
startTimeHours.setWidth(60);
startTimeHours.setValue(startDateItem.getValueAsDate().getHours());
final SpinnerItem startTimeMinutes = new SpinnerItem("startTimeMinutes",MSG.chart_slider_button_bar_minute());
startTimeMinutes.setMax(59);
startTimeMinutes.setStep(5);
startTimeMinutes.setMin(0);
startTimeMinutes.setWidth(60);
startTimeMinutes.setEndRow(true);
startTimeMinutes.setValue(startDateItem.getValueAsDate().getMinutes());
final DateItem endDateItem = new DateItem("endDate", MSG.common_buttonbar_end_date());
endDateItem.setValue(endTime);
final SpinnerItem endTimeHours = new SpinnerItem("endTimeHours", MSG.chart_slider_button_bar_hour());
endTimeHours.setMax(23);
endTimeHours.setMin(0);
endTimeHours.setWidth(60);
endTimeHours.setValue(endDateItem.getValueAsDate().getHours());
final SpinnerItem endTimeMinutes = new SpinnerItem("endTimeMinutes",MSG.chart_slider_button_bar_minute());
endTimeMinutes.setMax(59);
endTimeMinutes.setMin(0);
endTimeMinutes.setStep(5);
endTimeMinutes.setWidth(60);
endTimeMinutes.setValue(endDateItem.getValueAsDate().getMinutes());
form.setFields(startDateItem, startTimeHours, startTimeMinutes,
new RowSpacerItem(), new RowSpacerItem(),
endDateItem,
endTimeHours, endTimeMinutes,
new RowSpacerItem());
addItem(form);
final HTMLFlow startBeforeEndLabel = new HTMLFlow();
startBeforeEndLabel.setMargin(15);
startBeforeEndLabel.setHeight(30);
startBeforeEndLabel.setExtraSpace(0);
addItem(startBeforeEndLabel);
HLayout buttonHLayout = new HLayout();
buttonHLayout.setMargin(35);
buttonHLayout.setMembersMargin(20);
IButton cancelButton = new EnhancedIButton(MSG.common_buttonbar_custom_cancel());
cancelButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent clickEvent) {
CustomDateRangeWindow.this.destroy();
}
});
buttonHLayout.addMember(cancelButton);
IButton saveButton = new EnhancedIButton(MSG.common_buttonbar_custom_save(), ButtonColor.BLUE);
saveButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent clickEvent) {
//@todo: eventually get rid of deprecated calls but not in 3.2.1 minor release
Date newStartDate = new Date(startDateItem.getValueAsDate().getYear(), startDateItem
.getValueAsDate().getMonth(), startDateItem.getValueAsDate().getDate(),
(Integer) startTimeHours.getValue(), (Integer) startTimeMinutes.getValue());
Date newEndDate = new Date(endDateItem.getValueAsDate().getYear(), endDateItem.getValueAsDate()
.getMonth(), endDateItem.getValueAsDate().getDate(), (Integer) endTimeHours.getValue(),
(Integer) endTimeMinutes.getValue());
if (newStartDate.before(newEndDate)) {
startBeforeEndLabel.setContents("");
buttonBarDateTimeRangeEditor.saveDateRange(newStartDate.getTime(), newEndDate.getTime());
redrawGraphs();
showUserFriendlyTimeRange(newStartDate.getTime(), newEndDate.getTime());
CustomDateRangeWindow.this.destroy();
} else {
startBeforeEndLabel.setContents("<img src='images/resources/availability_red_24.png'/> "
+ MSG.view_measureTable_startBeforeEnd());
}
}
});
buttonHLayout.addMember(saveButton);
addItem(buttonHLayout);
addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClickEvent event) {
try {
CustomDateRangeWindow.this.destroy();
} catch (Throwable e) {
Log.warn("Cannot destroy custom date range window.", e);
}
}
});
}
}
}