package org.ovirt.engine.ui.common.widget.editor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.ovirt.engine.ui.uicompat.ConstantsManager;
import org.ovirt.engine.ui.uicompat.UIConstants;
import com.google.gwt.core.client.GWT;
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.resources.client.ClientBundle;
import com.google.gwt.user.client.TakesValue;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Label;
/**
* This widget can be used to capture dates for monthly recurrent events. Currently it has been built to facilitate
* Quartz scheduling.
*/
public class DaysOfMonthSelector extends Composite implements TakesValue<String>, HasValue<String> {
private static final int DAYS_IN_WEEK = 7;
private static final int LAST_DAY_OF_MONTH_INDEX = 31;
private static final UIConstants constants = ConstantsManager.getInstance().getConstants();
private static Resources RESOURCES = GWT.create(Resources.class);
private final DaysOfMonthSelectorCss style;
private final FlowPanel wrapperPanel = new FlowPanel();
private final FlexTable daysOfMonth = new FlexTable();
// Starts from index 0 and goes upto 31(Assumed to be last day of month(recurrence))
List<Boolean> clickedList = new ArrayList<>();
public interface Resources extends ClientBundle {
@Source("org/ovirt/engine/ui/common/css/DaysOfMonthSelector.css")
DaysOfMonthSelectorCss daysOfMonthSelectorCSS();
}
/**
* DaysOfMonthSelector
*/
public DaysOfMonthSelector() {
initWidget(wrapperPanel);
style = RESOURCES.daysOfMonthSelectorCSS();
style.ensureInjected();
daysOfMonth.setStyleName(style.daysOfMonthWidget());
showDaysOfMonth();
daysOfMonth.addClickHandler(event -> {
Cell cellClicked = daysOfMonth.getCellForEvent(event);
if (cellClicked != null) {
int cellColumn = cellClicked.getCellIndex();
int cellRow = cellClicked.getRowIndex();
int actualCellIndex = (cellRow - 1) * DAYS_IN_WEEK + cellColumn;
if (!clickedList.get(actualCellIndex)) {
ValueChangeEvent.fire(DaysOfMonthSelector.this,
addSelectedDate(getValue(), getDateFromIndex(actualCellIndex)));
} else {
ValueChangeEvent.fire(DaysOfMonthSelector.this,
removeSelectedDate(getValue(), getDateFromIndex(actualCellIndex)));
}
}
});
}
private void onSelectedItemsChange(int date, boolean clicked) {
String className = style.normalFlexTableCell();
if (clicked) {
className = style.selectedFlexTableCell();
}
daysOfMonth.getCellFormatter()
.getElement(getRowForTheDay(date), getColumnForTheDay(date))
.setClassName(className);
}
private void showDaysOfMonth() {
int row = 0;
int column = 0;
for (int i = 0; i < LAST_DAY_OF_MONTH_INDEX; i++) {
if ((i + 1) % DAYS_IN_WEEK == 1 && i != 1) {
row++;
column = 0;
}
daysOfMonth.setWidget(row, column, new Label(Integer.toString(i + 1)));
daysOfMonth.getCellFormatter().getElement(row, column).addClassName(style.normalFlexTableCell());
clickedList.add(i, false);
column++;
}
Label widget = new Label(constants.lastDay());
clickedList.add(LAST_DAY_OF_MONTH_INDEX, false);
daysOfMonth.setWidget(row, column, widget);
daysOfMonth.getFlexCellFormatter().setColSpan(row, column, 4);
wrapperPanel.add(daysOfMonth);
}
/**
* When the mapped ListModel does a setSelectedItem, this is invoked. This method sets the passed dates selected
* @param value
* Comma separated string of dates to mark selected
* @param fireEvents
* whether to fire ValueChangeEvent
*/
@Override
public void setValue(String value, boolean fireEvents) {
clearSelections();
if (value != null && !value.isEmpty()) {
for (String valueInProcess : Arrays.asList(value.split(","))) {//$NON-NLS-1$
int selectedIndex = getIndexFromDate(valueInProcess);
if (clickedList.get(selectedIndex) != null) {
clickedList.set(selectedIndex, true);
onSelectedItemsChange(selectedIndex + 1, true);
}
}
if (fireEvents) {
ValueChangeEvent.fire(this, value);
}
}
}
/**
* When the mapped ListModel does a setSelectedItem, this is invoked. This method sets the passed dates selected
* @param value
* Comma separated string of dates to mark selected
*/
@Override
public void setValue(String value) {
setValue(value, false);
}
private void clearSelections() {
for (int index = 0; index <= LAST_DAY_OF_MONTH_INDEX; index++) {
clickedList.set(index, false);
onSelectedItemsChange(index + 1, false);
}
}
/**
* This method calculates and returns the comma-separated string of dates selected in the widget.
* @return String Comma-separated string of dates selected in the widget.
*/
@Override
public String getValue() {
String selectedValues = null;
for (int dayInProcess = 0; dayInProcess <= LAST_DAY_OF_MONTH_INDEX; dayInProcess++) {
if (clickedList.get(dayInProcess)) {
selectedValues = addSelectedDate(selectedValues, getDateFromIndex(dayInProcess));
}
}
return selectedValues;
}
private String addSelectedDate(String selectedValues, String dateString) {
if (selectedValues == null) {
selectedValues = dateString;
} else {
selectedValues = selectedValues.concat(",");//$NON-NLS-1$
selectedValues = selectedValues.concat(dateString);
}
return selectedValues;
}
private String removeSelectedDate(String selectedValues, String dayInProcess) {
List<String> selectedDatesList = new ArrayList<>(Arrays.asList(selectedValues.split(",")));//$NON-NLS-1$
selectedDatesList.remove(dayInProcess);
String selectedDatesString = null;
for (String currentSelectedDate : selectedDatesList) {
selectedDatesString = addSelectedDate(selectedDatesString, currentSelectedDate);
}
return selectedDatesString;
}
private String getDateFromIndex(int dayInProcess) {
return dayInProcess == LAST_DAY_OF_MONTH_INDEX ? "L" : Integer.toString(dayInProcess + 1);//$NON-NLS-1$
}
private int getIndexFromDate(String value) {
return value.equals("L") ? LAST_DAY_OF_MONTH_INDEX : Integer.parseInt(value) - 1;//$NON-NLS-1$
}
private int getRowForTheDay(int date) {
int row = date / DAYS_IN_WEEK;
return date % DAYS_IN_WEEK == 0 ? row : row + 1;
}
private int getColumnForTheDay(int date) {
int probableColumn = date % DAYS_IN_WEEK;
int cellColumn = probableColumn == 0 ? 6 : probableColumn - 1;
return cellColumn;
}
@Override
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
}