/* * Copyright 2011 Google Inc. * * 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 com.google.gwt.sample.dynatablerf.client.widgets; import com.google.gwt.cell.client.ClickableTextCell; import com.google.gwt.cell.client.FieldUpdater; import com.google.gwt.core.client.GWT; import com.google.gwt.editor.client.EditorDelegate; import com.google.gwt.editor.client.ValueAwareEditor; import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory; import com.google.gwt.sample.dynatablerf.shared.DynaTableRequestFactory.ScheduleRequest; import com.google.gwt.sample.dynatablerf.shared.TimeSlotProxy; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Widget; import com.google.web.bindery.requestfactory.shared.Receiver; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; /** * Edits a list of time slots. */ public class TimeSlotListWidget extends Composite implements ValueAwareEditor<List<TimeSlotProxy>> { interface TableResources extends CellTable.Resources { @Override @Source(value = {CellTable.Style.DEFAULT_CSS, "CellTablePatch.css"}) CellTable.Style cellTableStyle(); } interface TimeSlotListWidgetUiBinder extends UiBinder<Widget, TimeSlotListWidget> { } private class ScheduleRow { int hour; ScheduleRow(int hour) { this.hour = hour; } public int getHour() { return hour; } public boolean isInUse(WeekDay day) { return currentSchedule.contains(new TimeSlotKey(day, hour)); } public void toggleInUse(WeekDay day) { final TimeSlotKey key = new TimeSlotKey(day, hour); if (currentSchedule.contains(key)) { currentSchedule.remove(key); table.redraw(); } else if (!existingSlots.containsKey(key)) { acceptClicks = false; ScheduleRequest context = factory.scheduleRequest(); context.createTimeSlot(day.ordinal(), hour * 60, hour * 60 + 50).fire( new Receiver<TimeSlotProxy>() { @Override public void onSuccess(TimeSlotProxy slot) { existingSlots.put(key, slot); backing.add(slot); currentSchedule.add(key); table.redraw(); acceptClicks = true; } }); } else { currentSchedule.add(key); table.redraw(); } } } private static class TimeSlotKey { private int hour; private WeekDay day; TimeSlotKey(WeekDay day, int hour) { this.day = day; this.hour = hour; } TimeSlotKey(TimeSlotProxy slot) { day = WeekDay.fromInt(slot.getDayOfWeek()); hour = slot.getStartMinutes() / 60; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } TimeSlotKey other = (TimeSlotKey) obj; if (day == null) { if (other.day != null) { return false; } } else if (!day.equals(other.day)) { return false; } if (hour != other.hour) { return false; } return true; } @Override public int hashCode() { return 31 * (31 + ((day == null) ? 0 : day.hashCode())) + hour; } } private class WeekDayColumn extends Column<ScheduleRow, String> { private WeekDay day; public WeekDayColumn(WeekDay day) { super(new ClickableTextCell()); this.day = day; } @Override public String getValue(ScheduleRow row) { if (day == null) { int hour = row.getHour(); return Integer.toString(hour <= 12 ? hour : hour - 12) + ":00" + ((hour < 12) ? "AM" : "PM"); } return row.isInUse(day) ? "X" : "."; } } private static final int ROWS_IN_A_DAY = 9; private static final int FIRST_HOUR = 8; private static TimeSlotListWidgetUiBinder uiBinder = GWT.create( TimeSlotListWidgetUiBinder.class); @UiField(provided = true) CellTable<ScheduleRow> table; private enum WeekDay { SUNDAY("Su"), MONDAY("Mo"), TUESDAY("Tu"), WEDNESDAY("We"), THURSDAY("Th"), FRIDAY("Fr"), SATURDAY("Sa"); public static WeekDay fromInt(int ordinal) { return values()[ordinal]; } private String shortName; WeekDay(String shortName) { this.shortName = shortName; } public String getShortName() { return shortName; } } private List<TimeSlotProxy> backing; private HashSet<TimeSlotKey> currentSchedule; private HashMap<TimeSlotKey, TimeSlotProxy> existingSlots; private DynaTableRequestFactory factory; private HashSet<TimeSlotKey> initialSchedule; private boolean acceptClicks = true; public TimeSlotListWidget(DynaTableRequestFactory factory) { this.factory = factory; table = new CellTable<TimeSlotListWidget.ScheduleRow>(ROWS_IN_A_DAY, GWT.<TableResources> create(TableResources.class)); table.addColumn(new WeekDayColumn(null), "Hour"); for (WeekDay day : WeekDay.values()) { WeekDayColumn col = new WeekDayColumn(day); class Updater implements FieldUpdater<ScheduleRow, String> { private WeekDay columnDay; public Updater(WeekDay day) { columnDay = day; } @Override public void update(int index, ScheduleRow row, String value) { if (acceptClicks) { row.toggleInUse(columnDay); } } } FieldUpdater<ScheduleRow, String> fieldUpdater = new Updater(day); col.setFieldUpdater(fieldUpdater); table.addColumn(col, day.getShortName()); } table.setRowCount(ROWS_IN_A_DAY, false); table.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.DISABLED); initWidget(uiBinder.createAndBindUi(this)); } @Override public void flush() { HashMap<TimeSlotProxy, TimeSlotKey> index = new HashMap<TimeSlotProxy, TimeSlotKey>(); for (TimeSlotProxy slot : backing) { index.put(slot, new TimeSlotKey(slot)); } // Compute slots that need to be removed from the backing initialSchedule.removeAll(currentSchedule); for (Iterator<TimeSlotProxy> iterator = backing.iterator(); iterator.hasNext();) { TimeSlotProxy slot = iterator.next(); TimeSlotKey key = index.get(slot); if (initialSchedule.contains(key)) { iterator.remove(); } } } @Override public void onPropertyChange(String... paths) { } @Override public void setDelegate(EditorDelegate<List<TimeSlotProxy>> delegate) { } @Override public void setValue(List<TimeSlotProxy> value) { backing = value; currentSchedule = new HashSet<TimeSlotKey>(); existingSlots = new HashMap<TimeSlotKey, TimeSlotProxy>(); initialSchedule = new HashSet<TimeSlotKey>(); for (TimeSlotProxy slot : backing) { TimeSlotKey key = new TimeSlotKey(slot); currentSchedule.add(key); existingSlots.put(key, slot); initialSchedule.add(new TimeSlotKey(slot)); } ArrayList<ScheduleRow> rows = new ArrayList<ScheduleRow>(ROWS_IN_A_DAY); for (int i = 0; i < ROWS_IN_A_DAY; i++) { rows.add(new ScheduleRow(FIRST_HOUR + i)); } table.setRowData(rows); } }