Fixed 'Tetris' calendar problem.

This commit is contained in:
Dobie Wollert
2012-12-04 03:57:45 -08:00
parent d36e2263cb
commit a0da681497
122 changed files with 14427 additions and 116 deletions

View File

@ -9,8 +9,6 @@
<classpathentry kind="lib" path="war/WEB-INF/lib/guice-assistedinject-3.0.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/guice-servlet-3.0.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/gwt-bootstrap-2.1.1.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/gwt-cal-0.9.3.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/gwt-dnd-3.1.2.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/gwt-maps.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/javax.inject.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/jsr305-1.3.9.jar"/>
@ -32,18 +30,8 @@
<classpathentry kind="lib" path="war/WEB-INF/lib/transaction-api-1.1.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/xpp3-1.1.4c.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/args4j-2.0.21.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/gwt-dnd-3.1.2.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="/home/akira/gwt-2.5.0.rc1/gwt-user.jar">
<attributes>
<attribute name="javadoc_location" value="file:/home/akira/gwt-2.5.0.rc1/doc/javadoc/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="/home/akira/gwt-2.5.0.rc1/gwt-dev.jar">
<attributes>
<attribute name="javadoc_location" value="file:/home/akira/gwt-2.5.0.rc1/doc/javadoc/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="/home/akira/gwt-2.5.0.rc1/validation-api-1.0.0.GA.jar" sourcepath="/home/akira/gwt-2.5.0.rc1/validation-api-1.0.0.GA-sources.jar"/>
<classpathentry kind="lib" path="/home/akira/gwt-2.5.0.rc1/validation-api-1.0.0.GA-sources.jar"/>
<classpathentry kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
<classpathentry kind="output" path="war/WEB-INF/classes"/>
</classpath>

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
filesCopiedToWebInfLib=

View File

@ -1,3 +1,4 @@
eclipse.preferences.version=1
entryPointModules=
filesCopiedToWebInfLib=gwt-servlet.jar
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBW11dPjwvZXh0cmEtYXJncz48dm0tYXJncz48IVtDREFUQVstWG14NTEybV1dPjwvdm0tYXJncz48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5iaW9tZWQuQmlvbWVkPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=

7
server.properties Normal file
View File

@ -0,0 +1,7 @@
email.username=schedule
email.password=success4
database.host=biomed.akira.gs:3306
database.name=biomed_devel
database.username=biomed_devel
database.password=pXVt9QMSq8XURfXm
devmode=true

View File

@ -0,0 +1,181 @@
package com.allen_sauer.gwt.dnd.client.drop;
import java.util.Date;
import com.allen_sauer.gwt.dnd.client.DragContext;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.dayview.AppointmentWidget;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Widget;
public class DayViewDropController extends AbsolutePositionDropController {
private int gridX;
private int gridY;
int dayStartsAt = 0;
private int intervalsPerHour;
private int snapSize;
private int columns;
private int rows;
private Date date;
private int maxProxyHeight = -1;
public void setColumns(final int columns) {
this.columns = columns;
}
public void setDate(final Date date) {
this.date = date;
}
public void setSnapSize(final int snapSize) {
this.snapSize = snapSize;
}
public void setIntervalsPerHour(final int intervalsPerHour) {
this.intervalsPerHour = intervalsPerHour;
this.rows = intervalsPerHour * 24;
}
public void setDayStartsAt(final int dayStartsAt) {
this.dayStartsAt = dayStartsAt;
}
public void setMaxProxyHeight(final int maxProxyHeight) {
this.maxProxyHeight = maxProxyHeight;
}
public DayViewDropController(final AbsolutePanel dropTarget) {
super(dropTarget);
}
@SuppressWarnings("deprecation")
@Override
public void onDrop(final DragContext context) {
super.onDrop(context);
//get the top and left position and the widget
int top =draggableList.get(0).desiredY;
int left=draggableList.get(0).desiredX;
Widget widget = context.draggable;
//set the 'snapped' top and left position of the widget
left = Math.max(0, Math.min(left, dropTarget.getOffsetWidth() - widget.getOffsetWidth()));
top = Math.max(0, Math.min(top, dropTarget.getOffsetHeight() - widget.getOffsetHeight()));
left = Math.round((float) left / gridX) * gridX;
top = Math.round((float) top / gridY) * gridY;
//figure out which row the appointment was dragged to
int intervalStart = (int) Math.floor(top / gridY);
int intervalSpan = Math.round(widget.getOffsetHeight() / snapSize);
//figure out which day (column) the appointment was dragged to
int day = (int) Math.floor(left / gridX);
day = Math.max(0, day);
day = Math.min(day, columns - 1);
//get the appointment, create the start & end date
Appointment appt = ((AppointmentWidget)widget).getAppointment();
Date start = (Date)date.clone();
Date end = (Date)date.clone();
start.setDate(start.getDate()+day);
end.setDate(end.getDate()+day);
start.setHours(dayStartsAt);
start.setMinutes(0);
start.setSeconds(0);
start.setMinutes((intervalStart)*(60/intervalsPerHour));
end.setHours(dayStartsAt);
end.setMinutes(0);
end.setSeconds(0);
end.setMinutes((intervalStart + intervalSpan)*(60/intervalsPerHour));
appt.setStart(start);
appt.setEnd(end);
}
// @Override
// public void drop(Widget widget, int left, int top) {
//
// }
// @Override
// public void drop(Widget widget, int left, int top) {
// left = Math.max(0, Math.min(left, dropTarget.getOffsetWidth() - widget.getOffsetWidth()));
// top = Math.max(0, Math.min(top, dropTarget.getOffsetHeight() - widget.getOffsetHeight()));
// left = Math.round((float) left / gridX) * gridX;
// top = Math.round((float) top / gridY) * gridY;
//
// System.out.println("on drop");
//
//
// int intervalStart = (int) Math.floor(top / rows);
// int intervalSpan = 2;
// int day = (int) Math.floor(left / columns);
// day = Math.min(0, day);
// day = Math.min(day, columns);
// day = day-1; //convert to a 0-based day index
//
// Appointment appt = ((AppointmentWidget)widget).getAppointment();
// Date start = (Date)date.clone();
// Date end = (Date)date.clone();
//
// start.setDate(start.getDate()+day);
// end.setDate(end.getDate()+day);
//
// start.setHours(0);
// start.setMinutes((intervalStart)*(60/intervalsPerHour));
// end.setHours(0);
// end.setMinutes((intervalStart + intervalSpan)*(60/intervalsPerHour));
//
// System.out.println("new start: "+start);
//
// appt.setStart(start);
// appt.setEnd(end);
//
//
// }
@Override
public void onMove(final DragContext context) {
super.onMove(context);
gridX = (int) Math.floor(dropTarget.getOffsetWidth() / columns);
gridY = (int) Math.floor(dropTarget.getOffsetHeight() / rows);
for (Draggable draggable : draggableList) {
draggable.desiredX = context.desiredDraggableX - dropTargetOffsetX + draggable.relativeX;
draggable.desiredY = context.desiredDraggableY - dropTargetOffsetY + draggable.relativeY;
draggable.desiredX = Math.max(0, Math.min(draggable.desiredX, dropTargetClientWidth - draggable.offsetWidth));
draggable.desiredY = Math.max(0, Math.min(draggable.desiredY, dropTargetClientHeight - draggable.offsetHeight));
draggable.desiredX = (int)Math.floor((double) draggable.desiredX / gridX) * gridX;
draggable.desiredY = (int)Math.round((double) draggable.desiredY / gridY) * gridY;
dropTarget.add(draggable.positioner, draggable.desiredX, draggable.desiredY);
}
}
@Override
public void onEnter(final DragContext context) {
super.onEnter(context);
for (Draggable draggable : draggableList) {
int width = draggable.positioner.getOffsetWidth();
int height = draggable.positioner.getOffsetHeight();
if (maxProxyHeight > 0 && height > maxProxyHeight) {
height = maxProxyHeight - 5;
}
draggable.positioner.setPixelSize(width, height);
}
}
}

View File

@ -0,0 +1,55 @@
package com.allen_sauer.gwt.dnd.client.drop;
import com.allen_sauer.gwt.dnd.client.DragContext;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.util.DragClientBundle;
import com.allen_sauer.gwt.dnd.client.util.WidgetArea;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class DayViewPickupDragController extends PickupDragController {
private int maxProxyHeight = -1;
public DayViewPickupDragController(AbsolutePanel boundaryPanel,
boolean allowDroppingOnBoundaryPanel) {
super(boundaryPanel, allowDroppingOnBoundaryPanel);
}
@Override
public void dragMove() {
try {
super.dragMove();
} catch (NullPointerException ex) {
}
}
@Override
protected Widget newDragProxy(DragContext context) {
AbsolutePanel container = new AbsolutePanel();
container.getElement().getStyle().setProperty("overflow", "visible");
WidgetArea draggableArea = new WidgetArea(context.draggable, null);
for (Widget widget : context.selectedWidgets) {
WidgetArea widgetArea = new WidgetArea(widget, null);
Widget proxy = new SimplePanel();
int height = widget.getOffsetHeight();
if (maxProxyHeight > 0 && height > maxProxyHeight) {
height = maxProxyHeight - 5;
}
proxy.setPixelSize(widget.getOffsetWidth(), height);
proxy.addStyleName(DragClientBundle.INSTANCE.css().proxy());
container.add(proxy,
widgetArea.getLeft() - draggableArea.getLeft(),
widgetArea.getTop() - draggableArea.getTop());
}
return container;
}
public void setMaxProxyHeight(int maxProxyHeight) {
this.maxProxyHeight = maxProxyHeight;
}
}

View File

@ -0,0 +1,85 @@
package com.allen_sauer.gwt.dnd.client.drop;
import java.util.Date;
import com.allen_sauer.gwt.dnd.client.AbstractDragController;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.dayview.AppointmentWidget;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Widget;
public class DayViewResizeController extends AbstractDragController {
public DayViewResizeController(AbsolutePanel boundaryPanel) {
super(boundaryPanel);
}
int dayStartsAt = 0;
int snapSize;
int intervalsPerHour;
public void setSnapSize(int snapSize) {
this.snapSize = snapSize;
}
public void setIntervalsPerHour(int intervalsPerHour) {
this.intervalsPerHour = intervalsPerHour;
}
public void setDayStartsAt(int dayStartsAt) {
this.dayStartsAt = dayStartsAt;
}
public void dragMove() {
Widget appointment = context.draggable.getParent();
//calculates difference between elements position on screen
// and how many pixels the user is trying to drag it
int delta = context.draggable.getAbsoluteTop()
- context.desiredDraggableY;
//get the height of the widget
int contentHeight = appointment.getOffsetHeight();
//make sure the height of the widget is not < the minimum size
int newHeight = Math.max(contentHeight - delta, snapSize);
//get the 'snapped' height. basically it gets the rounded
// intervals spanned, then multiples it by the snapSize
int snapHeight = Math.round(
(float) newHeight / snapSize) * snapSize;
appointment.setHeight(snapHeight + "px");
}
@SuppressWarnings("deprecation")
public void dragEnd() {
AppointmentWidget apptWidget = (AppointmentWidget)context.draggable.getParent();
int apptHeight = apptWidget.getOffsetHeight();
Appointment appt = apptWidget.getAppointment();
//get the start date
Date end = (Date)appt.getStart().clone();
//get the "top" location of the appointment widget
float topFloat = DOM.getIntStyleAttribute(apptWidget.getElement(), "top");
//get the grid span
//int intervalStart = Math.round(topFloat / snapSize);
int intervalSpan = Math.round(apptHeight / snapSize);
//set the end based on the new dragged value
//end.setHours(dayStartsAt);
end.setMinutes(end.getMinutes() + intervalSpan * (60 / intervalsPerHour));
//update the end
appt.setEnd(end);
super.dragEnd();
}
}

View File

@ -0,0 +1,227 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009,2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.allen_sauer.gwt.dnd.client.drop;
import java.util.Date;
import com.allen_sauer.gwt.dnd.client.DragContext;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.monthview.AppointmentWidget;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FlexTable;
/**
* Controls the Move and Drop (after dragging) events that can be generated in
* the Month View.
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*/
public class MonthViewDropController extends AbsolutePositionDropController {
private int daysPerWeek;
private int weeksPerMonth;
private Date firstDateDisplayed;
/**
* Flextable that displays a Month in grid format.
*/
final private FlexTable monthGrid;
/**
* List of all cells currently highlighted as an appointment is being
* dragged.
*/
private Element[] highlightedCells;
private static final String BACKGROUND = "backgroundColor";
public MonthViewDropController(final AbsolutePanel dropTarget,
final FlexTable monthGrid) {
super(dropTarget);
this.monthGrid = monthGrid;
}
public void setDaysPerWeek(final int daysPerWeek) {
this.daysPerWeek = daysPerWeek;
}
public void setWeeksPerMonth(final int weeksPerMonth) {
this.weeksPerMonth = weeksPerMonth;
}
public Date getFirstDateDisplayed() {
return firstDateDisplayed;
}
public void setFirstDateDisplayed(final Date firstDateDisplayed) {
this.firstDateDisplayed = firstDateDisplayed;
}
/**
* Manages the highlighting of cells in the month view grid when an
* <code>Appointment</code> is being dragged.
*
* @param context An object providing information about the object being
* dragged
*/
@Override
public void onMove(final DragContext context) {
//super.onMove(context);
//get the draggable object
Draggable draggable = draggableList.get(0);
//make sure it isn't null (shouldn't ever be)
if (draggable == null)
return;
int col = getColumn(context, draggable);
int row = getRow(context, draggable);
Element currHoveredCell =
monthGrid.getFlexCellFormatter().getElement(row, col);
//If this cell isn't already highlighted, we need to highlight
if (highlightedCells == null || highlightedCells.length < 0 ||
!currHoveredCell.equals(highlightedCells[0])) {
if (highlightedCells != null) {
for (Element elem : highlightedCells) {
if (elem != null)
DOM.setStyleAttribute(elem, BACKGROUND, "#FFFFFF");
}
}
Date startDate =
((AppointmentWidget) draggable.widget).getAppointment().getStart();
Date endDate =
((AppointmentWidget) draggable.widget).getAppointment().getEnd();
int dateDiff = DateUtils.differenceInDays(endDate, startDate) + 1;
dateDiff = (dateDiff <= 0) ? 1 : dateDiff;
highlightedCells = getCells(row, col, dateDiff);
//TODO: month view highlighted cell style be moved to the css style sheet
for (Element elem : highlightedCells) {
if (elem != null) {
DOM.setStyleAttribute(elem, BACKGROUND, "#C3D9FF");
}
}
}
}
/**
* Callback method executed once the drag has completed. We need to reset the
* background color of all previously highlighted cells. Also need to
* actually change the appointment's start / end date here (code doesn't
* exist yet).
*
* @param context An object containing information of what was dragged and
* dropped, including the <code>Appointment</code>,
* coordinates of the drop, etc.
*/
@Override
public void onDrop(final DragContext context) {
super.onDrop(context);
for (Element elem : highlightedCells) {
if (elem != null) {
DOM.setStyleAttribute(elem, BACKGROUND, "#FFFFFF");
}
}
highlightedCells = null;
Draggable draggable = draggableList.get(0);
Appointment appointment =
((AppointmentWidget) context.draggable).getAppointment();
long originalStartToEndTimeDistance =
appointment.getEnd().getTime() - appointment.getStart().getTime();
//get the column and row for the draggable widget
int row = getRow(context, draggable) - 1;
int col = getColumn(context, draggable);
int cell = row * daysPerWeek + col;
//calculate the new start & end dates
Date newStart = DateUtils.shiftDate(firstDateDisplayed, cell);
DateUtils.copyTime(appointment.getStart(), newStart);
Date newEnd = new Date(newStart.getTime() + originalStartToEndTimeDistance);
//Set the appointment's new start & end dates
appointment.setStart(newStart);
appointment.setEnd(newEnd);
}
/**
* Gets all the cells (as DOM Elements) that an appointment spans. Note: It
* only includes cells in the table. If an appointment ends in the following
* month the last cell in the list will be the last cell in the table.
*
* @param row Appointment's starting row
* @param col Appointment's starting column
* @param days Number of days an appointment spans
* @return Cell elements that an appointment spans
*/
protected Element[] getCells(int row, int col, int days) {
Element[] elems = new Element[days];
for (int i = 0; i < days; i++) {
if (col > daysPerWeek - 1) {
col = 0;
row++;
}
/*
* Cheap code here. If the row / cell throw an out of index exception
* we just break. This kind of sucks because we have to
* now account for null items in the Element[] array.
*/
try {
elems[i] = monthGrid.getFlexCellFormatter().getElement(row, col);
} catch (Exception ex) {
break;
}
col++;
}
return elems;
}
public int getRow(final DragContext context, final Draggable draggable) {
int y =
context.desiredDraggableY - dropTargetOffsetY + draggable.relativeY;
return (int)
Math.floor(y / (monthGrid.getOffsetHeight() / weeksPerMonth)) + 1;
}
public int getColumn(final DragContext context, final Draggable draggable) {
int x =
context.desiredDraggableX - dropTargetOffsetX + draggable.relativeX;
return (int)
Math.floor(x / (monthGrid.getOffsetWidth() / daysPerWeek));
}
}

View File

@ -0,0 +1,19 @@
package com.allen_sauer.gwt.dnd.client.drop;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.google.gwt.user.client.ui.AbsolutePanel;
public class MonthViewPickupDragController extends PickupDragController {
public MonthViewPickupDragController(AbsolutePanel boundaryPanel,
boolean allowDroppingOnBoundaryPanel) {
super(boundaryPanel, allowDroppingOnBoundaryPanel);
}
@Override
public void dragMove() {
try {
super.dragMove();
} catch(NullPointerException ex) { }
}
}

View File

@ -43,4 +43,7 @@
<replace-with class="com.biomed.client.resources.BiomedDayViewStyleManager">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.dayview.DayViewStyleManager"/>
</replace-with>
<replace-with class="com.biomed.client.resources.BiomedTechViewStyleManager">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.techview.TechViewStyleManager"/>
</replace-with>
</module>

View File

@ -0,0 +1,18 @@
package com.biomed.client.resources;
import com.biomed.client.ui.schedule.BiomedAppointment;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.ThemeAppointmentStyle;
import com.bradrydzewski.gwt.calendar.client.techview.TechViewStyleManager;
public class BiomedTechViewStyleManager extends TechViewStyleManager {
@Override
protected ThemeAppointmentStyle getDefaultViewAppointmentStyleForTheme() {
return BiomedAppointmentTheme.GRAY;
}
@Override
protected ThemeAppointmentStyle getViewAppointmentStyleForTheme(Appointment appointment) {
return ((BiomedAppointment) appointment).getTheme();
}
}

View File

@ -2,15 +2,20 @@ package com.biomed.client.ui.schedule;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import com.biomed.client.resources.BiomedAppointmentTheme;
import com.biomed.client.ui.renderers.UserDTORenderer;
import com.biomed.shared.api.dto.UserDTO;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.Calendar;
import com.bradrydzewski.gwt.calendar.client.CalendarSettings;
import com.bradrydzewski.gwt.calendar.client.CalendarViews;
import com.github.gwtbootstrap.client.ui.ValueListBox;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
@ -20,6 +25,8 @@ import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasConstrainedValue;
import com.google.gwt.user.datepicker.client.DateBox;
import com.google.maps.gwt.client.Geocoder;
import com.google.maps.gwt.client.Geocoder.Callback;
import com.google.maps.gwt.client.GeocoderRequest;
@ -44,6 +51,12 @@ public class SchedulePanel extends Composite implements Schedule.View {
@UiField
FlowPanel mapContainer;
@UiField
DateBox datePicker;
@UiField(provided = true)
ValueListBox<UserDTO> techPicker;
private static final LatLng center = LatLng.create(39.257147, -76.685686);
private Calendar calendar;
@ -55,6 +68,7 @@ public class SchedulePanel extends Composite implements Schedule.View {
@Inject
public SchedulePanel(Binder binder) {
techPicker = new ValueListBox<UserDTO>(new UserDTORenderer("All"));
initWidget(binder.createAndBindUi(this));
buildCalendar();
@ -99,7 +113,7 @@ public class SchedulePanel extends Composite implements Schedule.View {
calendar = new Calendar();
calendar.setSettings(settings);
calendar.setView(CalendarViews.DAY);
calendar.setView(CalendarViews.TECH);
calendar.setWidth("100%");
calendar.setHeight("100%");
calContainer.add(calendar);
@ -115,8 +129,11 @@ public class SchedulePanel extends Composite implements Schedule.View {
}
markers.clear();
int columnId = -1;
Map<Integer, Integer> columnMap = Maps.newHashMap();
for (Workorder w : workorders) {
String address = null;
String address = null;
if (w.clientAddress != null) {
address = w.clientAddress + "\n" + w.clientCity + ", " + w.clientState + ". " + w.clientZip;
}
@ -132,6 +149,14 @@ public class SchedulePanel extends Composite implements Schedule.View {
apt.setStart(w.jobStart);
apt.setEnd(w.jobEnd);
apt.setTheme(BiomedAppointmentTheme.STYLES.get(w.color));
if (columnMap.containsKey(w.techId)) {
apt.setColumnId(columnMap.get(w.techId));
} else {
columnId++;
columnMap.put(w.techId, columnId);
apt.setColumnId(columnId);
}
calendar.addAppointment(apt);
appointments.add(apt);
@ -178,4 +203,8 @@ public class SchedulePanel extends Composite implements Schedule.View {
geocoder = Geocoder.create();
}
public HasConstrainedValue<UserDTO> getTechField() {
return techPicker;
}
}

View File

@ -1,9 +1,21 @@
<ui:UiBinder
xmlns:ui="urn:ui:com.google.gwt.uibinder"
xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:b="urn:import:com.github.gwtbootstrap.client.ui">
xmlns:b="urn:import:com.github.gwtbootstrap.client.ui"
xmlns:dp="urn:import:com.google.gwt.user.datepicker.client">
<g:HTMLPanel ui:field="rootPanel" styleName="scheduler">
<div class="scheduler-head-bar" style="display: none">
<b:Button size="MINI" type="PRIMARY">New Workorder</b:Button>
<div class="item">
<label>Date: </label>
<dp:DateBox ui:field="datePicker"></dp:DateBox>
</div>
<div class="item">
<label>Tech: </label>
<b:ValueListBox ui:field="techPicker"></b:ValueListBox>
</div>
</div>
<g:FlowPanel ui:field="mapContainer" />
<g:FlowPanel ui:field="calContainer" />
</g:HTMLPanel>

View File

@ -32,7 +32,8 @@ public class BiomedModule extends AbstractModule {
private void bindProperties() {
Properties prop = new Properties();
try {
prop.load(new FileInputStream("/srv/biomed/server.properties"));
System.out.println("Current Directory: " + System.getProperty("user.dir"));
prop.load(new FileInputStream("../server.properties"));
Names.bindProperties(binder(), prop);
} catch (IOException e) {
e.printStackTrace();

View File

@ -7,6 +7,7 @@ import java.util.Map;
import java.util.TimeZone;
import javax.inject.Inject;
import javax.inject.Named;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.auth.oauth2.TokenResponseException;
@ -27,121 +28,143 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class CalendarApi {
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
private static final String CLIENT_ID = "333768673996-904fuljpb428q9r37m2uujislhal5kt9.apps.googleusercontent.com";
private static final String CLIENT_SECRET = "PAmHruqVbAXzEnnTwelx-Ll7";
private static final String REFRESH_TOKEN = "1/fWvZebnDAhY0MlgK1IImcrGIDm4ZlILiRM8_47HsUFc";
private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
private static final JsonFactory JSON_FACTORY = new JacksonFactory();
private static final String CLIENT_ID = "333768673996-904fuljpb428q9r37m2uujislhal5kt9.apps.googleusercontent.com";
private static final String CLIENT_SECRET = "PAmHruqVbAXzEnnTwelx-Ll7";
private static final String REFRESH_TOKEN = "1/fWvZebnDAhY0MlgK1IImcrGIDm4ZlILiRM8_47HsUFc";
@Inject
public CalendarApi() {
}
private final boolean devmode;
public String scheduleEvent(String email, Date startDate, Date endDate, String summary, String description, String location) {
EventAttendee attendee = new EventAttendee();
attendee.setEmail(email);
@Inject
public CalendarApi(@Named("devmode") boolean devmode) {
this.devmode = devmode;
}
Event event = new Event();
event.setSummary(summary);
event.setLocation(location);
event.setDescription(description);
public String scheduleEvent(String email, Date startDate, Date endDate,
String summary, String description, String location) {
if (devmode) {
return null;
}
List<EventAttendee> attendees = Lists.newArrayList();
attendees.add(attendee);
event.setAttendees(attendees);
EventAttendee attendee = new EventAttendee();
attendee.setEmail(email);
DateTime start = new DateTime(startDate, TimeZone.getTimeZone("UTC"));
event.setStart(new EventDateTime().setDateTime(start));
DateTime end = new DateTime(endDate, TimeZone.getTimeZone("UTC"));
event.setEnd(new EventDateTime().setDateTime(end));
Event event = new Event();
event.setSummary(summary);
event.setLocation(location);
event.setDescription(description);
Calendar calendar = buildCalendar();
try {
Event resultEvent = calendar.events().insert("api@atlanticbiomedical.com", event).execute();
return resultEvent.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
List<EventAttendee> attendees = Lists.newArrayList();
attendees.add(attendee);
event.setAttendees(attendees);
public void rescheduleEvent(String eventId, String email, Date startDate, Date endDate) {
Calendar calendar = buildCalendar();
DateTime start = new DateTime(startDate, TimeZone.getTimeZone("UTC"));
event.setStart(new EventDateTime().setDateTime(start));
DateTime end = new DateTime(endDate, TimeZone.getTimeZone("UTC"));
event.setEnd(new EventDateTime().setDateTime(end));
try {
Event event = calendar.events().get("api@atlanticbiomedical.com", eventId).execute();
Calendar calendar = buildCalendar();
try {
Event resultEvent = calendar.events()
.insert("api@atlanticbiomedical.com", event).execute();
return resultEvent.getId();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
EventAttendee attendee = new EventAttendee();
attendee.setEmail(email);
List<EventAttendee> attendees = event.getAttendees();
attendees.clear();
attendees.add(attendee);
public void rescheduleEvent(String eventId, String email, Date startDate,
Date endDate) {
if (devmode) {
return;
}
DateTime start = new DateTime(startDate, TimeZone.getTimeZone("UTC"));
event.setStart(new EventDateTime().setDateTime(start));
DateTime end = new DateTime(endDate, TimeZone.getTimeZone("UTC"));
event.setEnd(new EventDateTime().setDateTime(end));
Calendar calendar = buildCalendar();
calendar.events().update("api@atlanticbiomedical.com", eventId, event).execute();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
try {
Event event = calendar.events()
.get("api@atlanticbiomedical.com", eventId).execute();
public void deleteEvent(String eventId) {
Calendar calendar = buildCalendar();
EventAttendee attendee = new EventAttendee();
attendee.setEmail(email);
List<EventAttendee> attendees = event.getAttendees();
attendees.clear();
attendees.add(attendee);
try {
calendar.events().delete("api@atlanticbiomedical.com", eventId).execute();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
DateTime start = new DateTime(startDate, TimeZone.getTimeZone("UTC"));
event.setStart(new EventDateTime().setDateTime(start));
DateTime end = new DateTime(endDate, TimeZone.getTimeZone("UTC"));
event.setEnd(new EventDateTime().setDateTime(end));
private static Map<String, String> buildCalendarMap() {
Map<String, String> result = Maps.newHashMap();
calendar.events().update("api@atlanticbiomedical.com", eventId, event)
.execute();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
try {
Calendar calendar = buildCalendar();
CalendarList feed = calendar.calendarList().list().execute();
for (CalendarListEntry entry : feed.getItems()) {
String summary = entry.getSummary();
if (summary != null && summary.endsWith("atlanticbiomedical.com")) {
result.put(summary, entry.getId());
}
public void deleteEvent(String eventId) {
if (devmode) {
return;
}
System.out.println("Found Calendar: " + summary + " -> " + entry.getId());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
Calendar calendar = buildCalendar();
return result;
}
try {
calendar.events().delete("api@atlanticbiomedical.com", eventId).execute();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void test() throws IOException {
Calendar calendar = buildCalendar();
CalendarList feed = calendar.calendarList().list().execute();
for (CalendarListEntry entry : feed.getItems()) {
System.out.println(entry.getId());
}
}
private static Map<String, String> buildCalendarMap() {
Map<String, String> result = Maps.newHashMap();
private static Calendar buildCalendar() {
try {
GoogleCredential credential = new GoogleCredential().setAccessToken(getAccessToken());
Calendar calendar = Calendar.builder(HTTP_TRANSPORT, JSON_FACTORY)
.setApplicationName("BiomedPortal/1.0").setHttpRequestInitializer(credential).build();
return calendar;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
try {
Calendar calendar = buildCalendar();
CalendarList feed = calendar.calendarList().list().execute();
for (CalendarListEntry entry : feed.getItems()) {
String summary = entry.getSummary();
if (summary != null && summary.endsWith("atlanticbiomedical.com")) {
result.put(summary, entry.getId());
}
private static String getAccessToken() throws TokenResponseException, IOException {
TokenResponse response = new GoogleRefreshTokenRequest(HTTP_TRANSPORT, JSON_FACTORY,
REFRESH_TOKEN, CLIENT_ID, CLIENT_SECRET).execute();
return response.getAccessToken();
}
System.out.println("Found Calendar: " + summary + " -> "
+ entry.getId());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return result;
}
public void test() throws IOException {
Calendar calendar = buildCalendar();
CalendarList feed = calendar.calendarList().list().execute();
for (CalendarListEntry entry : feed.getItems()) {
System.out.println(entry.getId());
}
}
private static Calendar buildCalendar() {
try {
GoogleCredential credential = new GoogleCredential()
.setAccessToken(getAccessToken());
Calendar calendar = Calendar.builder(HTTP_TRANSPORT, JSON_FACTORY)
.setApplicationName("BiomedPortal/1.0")
.setHttpRequestInitializer(credential).build();
return calendar;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String getAccessToken() throws TokenResponseException,
IOException {
TokenResponse response = new GoogleRefreshTokenRequest(HTTP_TRANSPORT,
JSON_FACTORY, REFRESH_TOKEN, CLIENT_ID, CLIENT_SECRET).execute();
return response.getAccessToken();
}
}

View File

@ -0,0 +1,49 @@
<module>
<!-- Inherit the core Web Toolkit stuff -->
<inherits name="com.google.gwt.user.User" />
<inherits name="com.google.gwt.user.UserAgent"/>
<inherits name='com.allen_sauer.gwt.dnd.gwt-dnd'/>
<inherits name="com.google.gwt.i18n.I18N" />
<extend-property name="locale" values="ar"/>
<extend-property name="locale" values="bg"/>
<extend-property name="locale" values="cs"/>
<extend-property name="locale" values="da"/>
<extend-property name="locale" values="de"/>
<extend-property name="locale" values="es"/>
<extend-property name="locale" values="es_MX"/>
<extend-property name="locale" values="fi"/>
<extend-property name="locale" values="fr"/>
<extend-property name="locale" values="hu"/>
<extend-property name="locale" values="it"/>
<extend-property name="locale" values="ja"/>
<extend-property name="locale" values="ko"/>
<extend-property name="locale" values="nl"/>
<extend-property name="locale" values="nn_NO"/>
<extend-property name="locale" values="no"/>
<extend-property name="locale" values="pl_PL"/>
<extend-property name="locale" values="pl"/>
<extend-property name="locale" values="pt_BR"/>
<extend-property name="locale" values="ru"/>
<extend-property name="locale" values="sk"/>
<extend-property name="locale" values="sl"/>
<extend-property name="locale" values="sv_SE"/>
<extend-property name="locale" values="sv"/>
<extend-property name="locale" values="tr"/>
<extend-property name="locale" values="zh_CN"/>
<extend-property name="locale" values="zh_TW"/>
<!-- Deferred Binding for better browser compatibility -->
<replace-with class="com.bradrydzewski.gwt.calendar.client.util.impl.FormattingImpl">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.util.impl.FormattingImpl"/>
</replace-with>
<replace-with class="com.bradrydzewski.gwt.calendar.client.util.impl.FormattingIE6Impl">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.util.impl.FormattingImpl"/>
<any>
<when-property-is name="user.agent" value="ie6"/>
<when-property-is name="user.agent" value="ie7"/>
</any>
</replace-with>
</module>

View File

@ -0,0 +1,455 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010-2011 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.biomed.shared.api.dto.UserDTO;
/**
* Represents an event that Calendar Views display and manipulate through the
* gwt-cal provided user interface elements.
* <p>
* The <code>Appointment</code> class provides a set of text-based properties to
* describe it, including a <em>title, description, location, createdBy</em>,
* etc. Additional to these, there is a set of properties that exist to provide
* the gwt-cal components with information useful during the
* <code>Appointment</code> rendering in the widget views (<code>allDay</code>,
* <code>recurring</code>, etc.)
* </p>
* <p>
* All <code>Appointment</code> properties are ultimately used by the gwt-cal
* views and it is up to these components to decide how to render (if at all)
* them as well as to provide appropriate runtime features to modify them.
* </p>
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*/
@SuppressWarnings("serial")
public class Appointment implements Comparable<Appointment>, Serializable {
private String id;
private String title;
private String description;
private Date start;
private Date end;
private String location;
private String createdBy;
private List<Attendee> attendees = new ArrayList<Attendee>();
private boolean allDay = false;
private AppointmentStyle style = AppointmentStyle.DEFAULT;
private String customStyle;
private boolean readOnly = false;
private int columnId;
public int getColumnId() {
return columnId;
}
public void setColumnId(int columnId) {
this.columnId = columnId;
}
/**
* <p>
* Creates an <code>Appointment</code> with the following attributes set to
* <code>null</code>
*
* <ul>
* <li><code>title</code></li>
* <li><code>description</code></li>
* <li><code>start</code></li>
* <li><code>end</code></li>
* <li><code><code>location</code></li>
* <li><code>createdBy</code></li>
* </ul>
* the <code>attendees</code> collection empty the <code>allDay</code> and
* the <code>readOnly</code> property <code>false</code>.
* </p>
*
*/
public Appointment() {
}
/**
* Returns the unique identifier for this <code>Appointment</code>. The
* field is optional (and not used by gwt-cal) and therefore may be null.
*
* @return A unique identifier for this Appointment (optional).
*/
public String getId() {
return id;
}
/**
* Sets the unique identifier of this <code>Appointment</code>. This
* identifier is optional.
*
* @param id Arbitrary string to uniquely identify the appointment.
*/
public void setId(final String id) {
this.id = id;
}
/**
* Returns the configured start time-stamp of this <code>Appointment</code>.
*
* @return A date object with the date and time this appointment starts on
*/
public Date getStart() {
return start;
}
/**
* Sets the start time-stamp of this <code>Appointment</code>.
*
* @param start
* A date object with the date and time this appointment starts
*/
public void setStart(final Date start) {
this.start = start;
}
/**
* Returns the configured end time-stamp of this <code>Appointment</code>.
*
* @return A date object with the date and time this appointment ends on
*/
public Date getEnd() {
return end;
}
/**
* Sets the end time-stamp of this <code>Appointment</code>.
*
* @param end
* A date object with the date and time this appointment ends on
*/
public void setEnd(final Date end) {
this.end = end;
}
/**
* Returns the identifying title of this <code>Appointment</code>.
*
* @return The title's short text
*/
public String getTitle() {
return title;
}
/**
* Sets the identifying title of this <code>Appointment</code>.
*
* @param title
* The title's short text
*/
public void setTitle(final String title) {
this.title = title;
}
/**
* Returns a description for this <code>Appointment</code>.
*
* @return The <code>appointment</code>'s description
*/
public String getDescription() {
return description;
}
/**
* Sets the description of this <code>Appointment</code>.
*
* @param description
* The title's short text
*/
public void setDescription(final String description) {
this.description = description;
}
/**
* Returns a location of this <code>Appointment</code>.
*
* @return The <code>appointment</code> location.
*/
public String getLocation() {
return location;
}
/**
* Sets the location of this <code>Appointment</code>.
*
* @param location
* The <code>appointment</code> location
*/
public void setLocation(final String location) {
this.location = location;
}
/**
* Returns a creator of this <code>Appointment</code>.
*
* @return The <code>appointment</code> creator description.
*/
public String getCreatedBy() {
return createdBy;
}
/**
* Sets the creator of this <code>Appointment</code>.
*
* @param createdBy
* The <code>appointment</code> creator description.
*/
public void setCreatedBy(final String createdBy) {
this.createdBy = createdBy;
}
/**
* Returns the collection of associated attendees.
*
* @return The currently configured list of attendees
*/
public List<Attendee> getAttendees() {
return attendees;
}
/**
* Sets the attendees associated to this <code>Appointment</code>.
*
* @param attendees
* The entities associated (<em>attending</em>) this
* <code>Appointment</code>
*/
public void setAttendees(final List<Attendee> attendees) {
this.attendees = attendees;
}
/**
* Compares this <code>Appointment</code> with the specified
* <code>appointment</code> based first on the <code>start</code> dates of
* each appointment and then (if they happen to be the same), on the
* <code>end</code> dates.
*
* @param appointment
* The appointment to compare this one to
* @return a negative integer if <code>this</code> appointment
* <em>is before</em> <code>appointment</code>, <code>zero</code> if
* both appointments have the same <code>start</code>/
* <code>end</code> dates, and a positive integer if
* <code>this</code> appointment <em>is after</em>
* <code>appointment</code>.
*/
public int compareTo(final Appointment appointment) {
int compare = this.getStart().compareTo(appointment.getStart());
if (compare == 0) {
compare = appointment.getEnd().compareTo(this.getEnd());
}
return compare;
}
/**
* Tells whether this <code>Appointment</code> spans more than a single day,
* based on its <code>start</code> and <code>end</code> properties.
*
* @return <code>true</code> if the <code>start</code> and <code>end</code>
* dates fall on different dates, <code>false</code> otherwise.
*/
public boolean isMultiDay() {
if (getEnd() != null && getStart() != null) {
return !DateUtils.areOnTheSameDay(getEnd(), getStart());
}
throw new IllegalStateException(
"Calculating isMultiDay with no start/end dates set");
}
/**
* Returns the configured value of the <code>allDay</code> property, which
* indicates if this <code>Appointment</code> should be considered as
* spanning all day. It is left to the view rendering this
* <code>Appointment</code> to decide how to render an appointment based on
* this property value. For instance, the month view, will display the
* <code>Appointment</code> at the top of the days in a week.
*
* @return The current value of the <code>allDay</code> property
*/
public boolean isAllDay() {
return allDay;
}
/**
* Configures the the <code>allDay</code> property, which indicates if this
* <code>Appointment</code> should be considered as spanning all day. It is
* left to the view rendering this <code>Appointment</code> to decide how to
* render an appointment based on this property value. For instance, the
* month view, will display the <code>Appointment</code> at the top of the
* days in a week.
*
* @param allDay
* The current value of the <code>allDay</code> property
*/
public void setAllDay(final boolean allDay) {
this.allDay = allDay;
}
public AppointmentStyle getStyle() {
return style;
}
public void setStyle(final AppointmentStyle style) {
this.style = style;
}
public String getCustomStyle() {
return customStyle;
}
public void setCustomStyle(final String customStyle) {
this.customStyle = customStyle;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(final boolean readOnly) {
this.readOnly = readOnly;
}
public Appointment clone() {
Appointment clone = new Appointment();
clone.setId(this.id);
clone.setAllDay(this.allDay);
clone.setAttendees(new ArrayList<Attendee>(this.attendees));
clone.setCreatedBy(this.createdBy);
clone.setDescription(this.description);
clone.setEnd(DateUtils.newDate(this.end));
clone.setLocation(this.location);
clone.setStart(DateUtils.newDate(this.start));
clone.setTitle(this.title);
clone.setStyle(this.style);
clone.setCustomStyle(this.customStyle);
clone.setReadOnly(this.readOnly);
return clone;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + Boolean.valueOf(this.allDay).hashCode();
result = prime * result + ((attendees == null) ? 0 : attendees.hashCode());
result = prime * result + ((createdBy == null) ? 0 : createdBy.hashCode());
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((end == null) ? 0 : end.hashCode());
result = prime * result + ((start == null) ? 0 : start.hashCode());
result = prime * result + ((location == null) ? 0 : location.hashCode());
result = prime * result + ((title == null) ? 0 : title.hashCode());
result = prime * result + Boolean.valueOf(this.readOnly).hashCode();
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Appointment)) {
return false;
}
Appointment other = (Appointment) obj;
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (description == null) {
if (other.description != null) {
return false;
}
} else if (!description.equals(other.description)) {
return false;
}
if (allDay != other.allDay) {
return false;
}
if (attendees == null) {
if (other.attendees != null) {
return false;
}
} else if (!attendees.equals(other.attendees)) {
return false;
}
if (createdBy == null) {
if (other.createdBy != null) {
return false;
}
} else if (!createdBy.equals(other.createdBy)) {
return false;
}
if (end == null) {
if (other.end != null) {
return false;
}
} else if (!end.equals(other.end)) {
return false;
}
if (start == null) {
if (other.start != null) {
return false;
}
} else if (!start.equals(other.start)) {
return false;
}
if (location == null) {
if (other.location != null) {
return false;
}
} else if (!location.equals(other.location)) {
return false;
}
if (title == null) {
if (other.title != null) {
return false;
}
} else if (!title.equals(other.title)) {
return false;
}
if (readOnly != other.readOnly) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,355 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Manages operations and state for the entire set of appointments displayed by
* the GWT calendar. <p></p>
* <p/>
* The key responsibilities of the <code>AppointmentManager</code> are: <ul>
* <li>Keep the calendar collection of appointments and provide operations to
* update it</li> <li>Identify one of the appointments as the &quot;currently
* selected&quot;</li> <li>Provide &quot;navigation&quot; methods to move the
* &quot;currently selected&quot; from appointment to appointment over the
* collection</li> <li>Keep track of changes to the set of appointments to
* identify when sorting of the appointments -if client code needs them
* chronologically ordered- should take place, i.e. appointments are not
* guaranteed to be always in chronological order</li> </ul>
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*/
public class AppointmentManager {
/**
* A reference to the &quot;currently selected appointment&quot;. Will be
* <code>null</code> when no currently selected appointment has been set.
*/
private Appointment selectedAppointment = null;
/**
* A reference to the most recent appointment to receive
* a mouse over event.
*/
private Appointment hoveredAppointment = null;
/**
* A copy of the last appointment that was updated,
* prior to the update taking place.
*/
private Appointment rollbackAppointment = null;
/**
* A reference to the last appointment that was updated.
*/
private Appointment committedAppointment = null;
/**
* The collection of appointments to be maintained by this
* <code>AppointmentManager</code>.
*/
private ArrayList<Appointment> appointments = new ArrayList<Appointment>();
/**
* Internal state flag indicating whether the collection of appointments
* needs to be sorted.
*/
private boolean sortPending = true;
/**
* Returns the collection of appointments that this <code>AppointmentManager</code>
* maintains. <strong>Warning</strong>: this method returns a modifiable
* reference to the internally managed list of appointments; client code
* might break the invariants that this <code>AppointmentManager</code>
* enforces if it performs any operations that modify the returned
* collection.
*
* @return The appointments managed by this <code>AppointmentManager</code>.
*/
public List<Appointment> getAppointments() {
return appointments;
}
/**
* Adds an appointment to the collection of appointments maintained by this
* <code>ApplicationManager</code>.
*
* @param appt The appointment to be made part of this manager's managed
* collection
*/
public void addAppointment(Appointment appt) {
if (appt != null) {
appointments.add(appt);
sortPending = true;
}
}
/**
* Adds multiple appointments to the collection maintained by this
* <code>ApplicationManager</code>.
*
* @param appointments The appointments that will be made part of this
* manager's managed collection
*/
public void addAppointments(List<Appointment> appointments) {
if (appointments != null) {
for (Appointment appointment : appointments) {
addAppointment(appointment);
}
}
}
/**
* Removes the <code>appointment</code> from this manager's managed
* collection.
*
* @param appointment The appointment to remove
*/
public void removeAppointment(Appointment appointment) {
if (appointment != null) {
boolean wasRemoved = appointments.remove(appointment);
if (wasRemoved) {
sortPending = true;
}
//I'd rather have the client keep the reference to the selected one if they need it than having here
//a reference to a thing I no longer should know about...
if (hasAppointmentSelected() &&
getSelectedAppointment().equals(appointment)) {
selectedAppointment = null;
}
}
}
/**
* Removes the &quot;currently selected&quot; appointment from this
* manager's collection.
*/
public void removeCurrentlySelectedAppointment() {
if (hasAppointmentSelected()) {
removeAppointment(getSelectedAppointment());
selectedAppointment = null;
}
}
/**
* Empties the collection of managed appointments.
*/
public void clearAppointments() {
appointments.clear();
}
/**
* Sets the appointment that should be considered the &quot;currently
* selected&quot; appointment.
*
* @param selectedAppointment The appointment to consider &quot;currently
* selected&quot;
*/
public void setSelectedAppointment(Appointment selectedAppointment) {
if (selectedAppointment != null &&
appointments.contains(selectedAppointment)) {
this.selectedAppointment = selectedAppointment;
}
}
/**
* Indicates whether there is a &quot;currently selected&quot; appointment
* at the moment.
*
* @return <code>true</code> if there is a currently selected appointment
* for the collection managed by this component, <code>false</code>
* otherwise
*/
public boolean hasAppointmentSelected() {
return selectedAppointment != null;
}
/**
* Returns the appointment in this manager's collection that is
* &quot;currently selected&quot;.
*
* @return The currently selected appointment
*/
public Appointment getSelectedAppointment() {
return selectedAppointment;
}
/**
* Sorts the collection of appointments by their natural order.
*/
public void sortAppointments() {
if (sortPending) {
Collections.sort(appointments);
sortPending = false;
}
}
/**
* Moves the &quot;currently selected&quot; to the previous appointment in
* the managed collection of this <code>AppointmentManager</code>. <br/> The
* &quot;previous&quot; appointment will be the appointment before the
* currently selected appointment in the set of appointments (whether
* ordered or not).
* <p/>
* <br/> Because this operation depends on a &quot;currently selected
* appointment&quot;, no previous appointment is considered to exist if
* there is no &quot;currently selected appointment.&quot; or it is the
* first in the set.
*
* @return <code>true</code> if selecting the previous appointment was
* successful, <code>false</code> no currently selected appointment
* is set or the currently selected appointment is the first in the
* collection.
*/
public boolean selectPreviousAppointment() {
boolean moveSucceeded = false;
if (getSelectedAppointment() != null) {
int selectedApptIndex =
getAppointments().indexOf(getSelectedAppointment());
Appointment prevAppt;
if (selectedApptIndex > 0 && (prevAppt =
getAppointments().get(selectedApptIndex - 1)) != null) {
selectedAppointment = prevAppt;
moveSucceeded = true;
}
}
return moveSucceeded;
}
/**
* Moves the &quot;currently selected&quot; to the next appointment in the
* managed collection of this <code>AppointmentManager</code>. <br/> The
* &quot;next&quot; appointment will be the appointment after the currently
* selected appointment in the set of appointments (whether ordered or
* not).
* <p/>
* <br/> Because this operation depends on a &quot;currently selected
* appointment&quot;, no next appointment is considered to exist if there is
* no &quot;currently selected appointment or it is the last in the
* set.&quot;
*
* @return <code>true</code> if selecting the previous appointment was
* successful, <code>false</code> no currently selected appointment
* is set or the currently selected appointment is the last in the
* collection.
*/
public boolean selectNextAppointment() {
boolean moveSucceeded = false;
if (getSelectedAppointment() != null) {
int selectedApptIndex =
getAppointments().indexOf(getSelectedAppointment());
int lastIndex = getAppointments().size() - 1;
Appointment nextAppt;
if (selectedApptIndex < lastIndex
&& (nextAppt = getAppointments().get(selectedApptIndex + 1)) != null) {
selectedAppointment = nextAppt;
moveSucceeded = true;
}
}
return moveSucceeded;
}
/**
* Resets the &quot;currently selected&quot; appointment of this manager.
* <p></p>If this manager has a currently selected appointment, the
* appointment <code>selected</code> property will be set to
* <code>false</code> and this manager's <code>selectedAppointment</code>
* property will be set to <code>null</code>.
*/
public void resetSelectedAppointment() {
if (hasAppointmentSelected()) {
//selectedAppointment.setSelected(false);
selectedAppointment = null;
}
}
/**
* Tells whether the passed <code>appointment</code> is the same one as the
* one that is currently selected in this manager.
*
* @param appointment The appointment to test to be the same as the
* currently selected
* @return <code>true</code> if there is a currently selected appointment
* and it is equal to the passed <code>appointment</code>
*/
public boolean isTheSelectedAppointment(Appointment appointment) {
return hasAppointmentSelected() && selectedAppointment.equals(
appointment);
}
public void commit() {
rollbackAppointment = null;
committedAppointment = null;
}
public void rollback() {
//if the snapshot block is empty, we can do nothing
if(rollbackAppointment==null && committedAppointment==null)
return;
//if there is no committed appointment, we assume
// this was a delete operation. We re-add the appointment
if (committedAppointment == null) {
addAppointment(rollbackAppointment);
//if there is no rollback appointment, we assume
// this was an add or update operation. We remove the appointment
} else if (rollbackAppointment == null) {
removeAppointment(committedAppointment);
//else, we assume this is an update
} else {
removeAppointment(committedAppointment);
addAppointment(rollbackAppointment);
}
commit();
}
public void setRollbackAppointment(Appointment appt) {
sortPending = true;
commit();
rollbackAppointment = appt;
}
public void setCommittedAppointment(Appointment appt) {
sortPending = true;
committedAppointment = appt;
}
public void resetHoveredAppointment() {
this.hoveredAppointment = null;
}
public void setHoveredAppointment(Appointment hoveredAppointment) {
this.hoveredAppointment = hoveredAppointment;
}
public Appointment getHoveredAppointment() {
return hoveredAppointment;
}
}

View File

@ -0,0 +1,12 @@
package com.bradrydzewski.gwt.calendar.client;
import java.io.Serializable;
import com.google.gwt.user.client.rpc.IsSerializable;
public enum AppointmentStyle implements Serializable, IsSerializable {
BLUE, RED, PINK, PURPLE, DARK_PURPLE, STEELE_BLUE, LIGHT_BLUE,
TEAL, LIGHT_TEAL, GREEN, LIGHT_GREEN, YELLOW_GREEN, YELLOW,
ORANGE, RED_ORANGE, LIGHT_BROWN, LIGHT_PURPLE, GREY, BLUE_GREY,
YELLOW_GREY, BROWN, DEFAULT, CUSTOM
}

View File

@ -0,0 +1,150 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.io.Serializable;
/**
* A simple JavaBean class representing an entity associated to an appointment,
* most likely a person, but might as well be a resource (like a conference room or a
* projector).
*/
@SuppressWarnings("serial")
public class Attendee implements Serializable {
/**
* The <code>Attendee</code> id. This field can be used to relate the Attendee with some
* external source (contact, resource, ....)
*/
private String id;
/**
* The <code>Attendee</code> name (if a person) or description
* (when a resource).
*/
private String name;
/**
* This <code>Attendee</code> email address.
*/
private String email;
/**
* The status of attendance of this attendee to an <code>Appointment</code>.
*/
private Attending attending = Attending.Maybe;
/**
* A URL to an image to depict this <code>Attendee</code>.
*/
private String imageUrl;
/**
* Returns the name (if a person) or description (when a resource)
* of this <code>Attendee</code>.
*
* @return The currently configured name of this attendee
*/
public String getName() {
return name;
}
/**
* Sets the name (if a person) or description (when a resource)
* of this <code>Attendee</code>.
*
* @param name The name of this attendee
*/
public void setName(String name) {
this.name = name;
}
/**
* Returns this <code>Attendee</code> email address.
*
* @return The email address
*/
public String getEmail() {
return email;
}
/**
* Sets this <code>Attendee</code> email address.
* @param email The email address
*/
public void setEmail(String email) {
this.email = email;
}
/**
* Returns the attendance status of <code>this</code> attendant
* to the <code>Appointment</code> referencing it.
* @return The attendance status of this <code>Attendee</code>
*/
public Attending getAttending() {
return attending;
}
/**
* Sets the attendance status of <code>this</code> attendant
* to the <code>Appointment</code> referencing it.
*
* @param attending The attendance status
*/
public void setAttending(Attending attending) {
this.attending = attending;
}
/**
* Returns the URL to an image to (optionally) depict this <code>Attendee</code>
* in the views.
* @return A URL (relative or absolute) meaningful within the
* deployed application context
*/
public String getImageUrl() {
return imageUrl;
}
/**
* Sets the URL to an image to (optionally) depict this <code>Attendee</code>
* in the views.
* @param imageUrl A URL (relative or absolute) meaningful within the
*/
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
/**
* Returns the attendance ID.
* @return The attendance ID
* @since 0.9.4
*/
public String getId() {
return id;
}
/**
* Sets the attendance the ID.
* @param id The ID
* @since 0.9.4
*/
public void setId(String id) {
this.id = id;
}
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.io.Serializable;
import com.google.gwt.user.client.rpc.IsSerializable;
/**
* Indicates whether or not an Attendee will be attending an
* {@link Appointment}.
*
* @author Brad Rydzewski
* @since 0.9.0
*/
public enum Attending implements Serializable, IsSerializable {
/**
* Indicates an Attendee will be attending.
*/
Yes,
/**
* Indicates an Attendee will not be attending.
*/
No,
/**
* Indicates an Attendee has not yet made up their mind
* whether or not they will be attending.
*/
Maybe
}

View File

@ -0,0 +1,163 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import com.bradrydzewski.gwt.calendar.client.dayview.DayView;
import com.bradrydzewski.gwt.calendar.client.monthview.MonthView;
import com.bradrydzewski.gwt.calendar.client.techview.TechView;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.ProvidesResize;
import com.google.gwt.user.client.ui.RequiresResize;
public class Calendar extends CalendarWidget implements RequiresResize,
ProvidesResize {
/**
* The component to manage the presentation of appointments in a single day
* layout.
*/
private DayView dayView = null;
/**
* The component to manage the presentation of appointments in a month.
*/
private MonthView monthView = null;
private TechView techView = null;
private CalendarViews selectedView = null;
/**
* Constructs a <code>Calendar</code> with the DayView currently displayed.
*/
public Calendar() {
this(CalendarViews.DAY, CalendarSettings.DEFAULT_SETTINGS);
}
public Calendar(CalendarSettings settings) {
this(CalendarViews.DAY, settings);
}
/**
* Constructs a <code>Calendar</code> with the given CalendarView displayed by
* default.
*/
public Calendar(CalendarViews view, CalendarSettings settings) {
super();
this.setSettings(settings);
setView(view);
}
/**
* Constructs a <code>Calendar</code> with the a user-defined CalendarView
* displayed by default.
*/
public Calendar(CalendarView view) {
super();
setView(view);
}
/**
* Sets the CalendarView that should be used by the Calendar to display the
* list of appointments.
*
* @param view
*/
final public void setView(CalendarViews view) {
setView(view, getDays());
}
/**
* Sets the current view of this calendar.
*
* @param view
* The ID of a view used to visualize the appointments managed by the
* calendar
* @param days
* The number of days to display in the view, which can be ignored by
* some views.
*/
public void setView(CalendarViews view, int days) {
switch (view) {
case DAY: {
if (dayView == null) {
dayView = new DayView();
}
dayView.setDisplayedDays(days);
setView(dayView);
break;
}
case AGENDA: {
// TODO: need to cache agendaView, but there is a layout bug after a
// calendar item is deleted.
// agendaView = new AgendaView();
// setView(agendaView);
// break;
throw new RuntimeException("Agenda View is not yet supported");
}
case MONTH: {
if (monthView == null) {
monthView = new MonthView();
}
setView(monthView);
break;
}
case TECH: {
if (techView == null) {
techView = new TechView();
}
setView(techView);
break;
}
}
selectedView = view;
}
/**
* Gets the current view of this calendar.
*
* @return Current view
*/
public CalendarViews getCalendarView() {
return selectedView;
}
public void onResize() {
resizeTimer.schedule(500);
}
private Timer resizeTimer = new Timer() {
/**
* Snapshot of the Calendar's height at the last time it was resized.
*/
private int height;
@Override
public void run() {
int newHeight = getOffsetHeight();
if (newHeight != height) {
height = newHeight;
doSizing();
if (getView() instanceof MonthView)
doLayout();
}
}
};
}

View File

@ -0,0 +1,312 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009-2011 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.i18n.CalendarConstants;
import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.DateTimeFormat;
@SuppressWarnings("deprecation")
final public class CalendarFormat {
public static final CalendarConstants MESSAGES =
(CalendarConstants) GWT.create(CalendarConstants.class);
public static final int HOURS_IN_DAY = 24;
private static final DateTimeFormat DEFAULT_DAY_OF_MONTH_FORMAT =
DateTimeFormat.getFormat(MESSAGES.dayOfMonthFormat());
private static final DateTimeFormat DEFAULT_DAY_OF_WEEK_FORMAT =
DateTimeFormat.getFormat(MESSAGES.weekdayFormat());
private static final DateTimeFormat DEFAULT_DAY_OF_WEEK_ABBREVIATED_FORMAT =
DateTimeFormat.getFormat(MESSAGES.weekdayFormat());
private static final DateTimeFormat DEFAULT_HOUR_FORMAT =
DateTimeFormat.getFormat(MESSAGES.timeFormat());
private static final DateTimeFormat DEFAULT_DATE_FORMAT =
DateTimeFormat.getFormat(MESSAGES.dateFormat());
private static String DEFAULT_AM_LABEL = MESSAGES.am();
private static String DEFAULT_PM_LABEL = MESSAGES.pm();
private static String DEFAULT_NOON_LABEL = MESSAGES.noon();
private String[] weekDayNames = new String[7];
private String[] dayOfWeekAbbreviatedNames = new String[7];
private String[] dayOfMonthNames = new String[32];
private String[] hours = new String[24];
private DateTimeFormat dayOfMonthFormat = null;
private DateTimeFormat dayOfWeekFormat = null;
private DateTimeFormat dayOfWeekAbbreviatedFormat = null;
private DateTimeFormat timeFormat = null;
private DateTimeFormat dateFormat = null;
private String am = null;
private String pm = null;
private String noon = null;
private boolean useNoonLabel = true;
private int firstDayOfWeek = Integer.valueOf(MESSAGES.firstDayOfWeek());
public static CalendarFormat INSTANCE = new CalendarFormat();
private CalendarFormat() {
dayOfMonthFormat = DEFAULT_DAY_OF_MONTH_FORMAT;
dayOfWeekFormat = DEFAULT_DAY_OF_WEEK_FORMAT;
dayOfWeekAbbreviatedFormat = DEFAULT_DAY_OF_WEEK_ABBREVIATED_FORMAT;
timeFormat = DEFAULT_HOUR_FORMAT;
dateFormat = DEFAULT_DATE_FORMAT;
am = DEFAULT_AM_LABEL;
pm = DEFAULT_PM_LABEL;
noon = DEFAULT_NOON_LABEL;
refreshWeekDayNames();
refreshMonthDayNames();
generateHourLabels();
}
/**
* Configures the formatting pattern to render the days of the week using
* <code>DateTimeFormat</code>.
*
* @param formatPattern The pattern to format day names
* @see com.google.gwt.i18n.client.DateTimeFormat#getFormat(String)
*/
public void setDayOfWeekFormat(String formatPattern) {
dayOfWeekFormat = DateTimeFormat.getFormat(formatPattern);
refreshWeekDayNames();
}
/**
* Returns the names (labels) of the days of the week.
*
* @return The days of the 7 days of the week, formatted with the current
* configuration
*/
public String[] getDayOfWeekNames() {
return weekDayNames;
}
/**
* Configures the formatting pattern to render the days of the week in an
* abbreviated manner using <code>DateTimeFormat</code>.
*
* @param formatPattern The pattern to format day names
* @see com.google.gwt.i18n.client.DateTimeFormat#getFormat(String)
*/
public void setDayOfWeekAbbreviatedFormat(String formatPattern) {
dayOfWeekAbbreviatedFormat = DateTimeFormat.getFormat(formatPattern);
refreshWeekDayNames();
}
public String[] getDayOfWeekAbbreviatedNames() {
return dayOfWeekAbbreviatedNames;
}
private void refreshWeekDayNames() {
Date date = new Date();
for (int i = 1; i <= 7; i++) {
date.setDate(i);
int dayOfWeek = date.getDay();
weekDayNames[dayOfWeek] = dayOfWeekFormat.format(date);
dayOfWeekAbbreviatedNames[dayOfWeek] =
dayOfWeekAbbreviatedFormat.format(date);
}
}
/**
* Configures the formatting pattern to render the days of the month using
* <code>DateTimeFormat</code>. Most likely, <code>formatPattern</code> will
* contain at the minimum, the format to render the number of the
* corresponding day.
*
* @param formatPattern The pattern to format days in the month view
* @see com.google.gwt.i18n.client.DateTimeFormat#getFormat(String)
*/
public void setDayOfMonthFormat(String formatPattern) {
dayOfMonthFormat = DateTimeFormat.getFormat(formatPattern);
refreshMonthDayNames();
}
private void refreshMonthDayNames() {
Date date = new Date();
date.setMonth(0);
for (int i = 1; i < 32; ++i) {
date.setDate(i);
dayOfMonthNames[i] = dayOfMonthFormat.format(date);
}
}
public void setDateFormat(String formatPattern) {
dateFormat = DateTimeFormat.getFormat(formatPattern);
}
public DateTimeFormat getDateFormat() {
return dateFormat;
}
/**
* Sets the pattern used to format the displayed hours and re-generates
* all hour labels.
*
* @param formatPattern A legal format following the patterns
* in {@link com.google.gwt.i18n.client.DateTimeFormat}
*/
public void setTimeFormat(String formatPattern) {
timeFormat = DateTimeFormat.getFormat(formatPattern);
generateHourLabels();
}
public DateTimeFormat getTimeFormat() {
return timeFormat;
}
/**
* Allows programmatic configuration of the 24 hour labels in the calendar.
*
* @param hourLabels The labels to be used as labels for the hours of the
* day.
* @throws IllegalArgumentException If the <code>hourLabels</code> array is
* <code>null</code>, does not have 24
* elements, or any of the elements is
* <code>null</code>
*/
public void setHourLabels(String[] hourLabels) {
if (hourLabels == null || hourLabels.length != HOURS_IN_DAY) {
throw new IllegalArgumentException(
"24 Hour labels expected. Please provide an array with 24 non-null values");
}
for (int i = 0; i < HOURS_IN_DAY; i++) {
if (hourLabels[i] == null) {
throw new IllegalArgumentException(
"Hour @ position " + i + " is null.");
}
hours[i] = hourLabels[i];
}
}
/**
* Default logic to generate the labels for the hours.
*/
private void generateHourLabels() {
Date date = new Date();
date.setHours(0);
date.setMinutes(0);
String hour;
for (int i = 0; i < HOURS_IN_DAY; i++) {
date.setHours(i);
hour = timeFormat.format(date);
//shortTimeFormat.format(date);
hours[i] = hour;
}
}
/**
* Returns the currently configured day to start weeks in the
* <code>MonthView</code>. The default value is read from the
* <code>CalendarConstants</code> i18n configuration file, but it can be
* changed through the <code>setFirstDayOfWeek</code> method of this class.
*
* @return The currently configured day to start weeks, <code>0</code> for
* Sunday, <code>1</code> for Monday, and so on.
*/
public int getFirstDayOfWeek() {
return firstDayOfWeek;
}
/**
* Configures the first day in the week when rendering the month view.
*
* @param firstDayOfWeek The first day of the week, where Sunday is
* represented by <code>0</code>, Monday by
* <code>1</code>, and so on.
*/
public void setFirstDayOfWeek(int firstDayOfWeek) {
this.firstDayOfWeek = Math.abs(firstDayOfWeek % 7);
}
public String getAm() {
return am;
}
public void setAm(String am) {
this.am = am;
}
public String getPm() {
return pm;
}
public void setPm(String pm) {
this.pm = pm;
}
/**
* Returns the currently configured label for the noon (12 p.m.).
*
* @return The configured label for the 12 p.m. time either through the
* <code>CalendarConstants</code> or the <code>setNoon</code> method
* of this class.
*/
public String getNoon() {
return noon;
}
/**
* Configures the label to show for the 12 p.m.
*
* @param noon A label to show instead of 12 p.m.
*/
public void setNoon(String noon) {
this.noon = noon;
}
/**
* Returns the configured labels for the 24 hours of the day. Some labels
* will vary, depending con configuration, for example &quot;Noon&quot;
* instead of &quot;12&quot;.
*
* @return An array of Strings with the corresponding labels for the hours in
* a day
*/
public String[] getHourLabels() {
return hours;
}
/**
* Indicates if we want to use the NoonLabel or the 12 p.m.
* @param use <code>true</true> if we want to use the NoonLabel, <code>false</true> otherwise
* @since 0.9.4
*/
public void setUseNoonLabel(boolean use) {
this.useNoonLabel = use;
}
/**
* Indicates if we are using the NoonLabel for the 12 p.m.
* @since 0.9.4
*/
public boolean isUseNoonLabel() {
return useNoonLabel;
}
}

View File

@ -0,0 +1,182 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Brad Rydzewski
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class CalendarSettings {
public static CalendarSettings DEFAULT_SETTINGS = new CalendarSettings();
private int pixelsPerInterval = 30; //IE6 cannot be less than 20!!!!!
private int intervalsPerHour = 2;
private int workingHourStart = 8;
private int workingHourEnd = 17;
private int scrollToHour = 8; //default hour that gets scrolled to
private boolean enableDragDrop = true;
private boolean offsetHourLabels = false;
private boolean showWeekNumbers = false;
private boolean showMultiDay = true;
private List<Date> holidays = new ArrayList<Date>();
private int dayStartsAt = 0;
/*
* Clicks required to fire TimeBlockClickEvent.
*/
private Click timeBlockClickNumber = Click.Single;
public CalendarSettings() {
}
public int getPixelsPerInterval() {
return pixelsPerInterval;
}
public void setPixelsPerInterval(final int px) {
pixelsPerInterval = px;
}
public int getIntervalsPerHour() {
return intervalsPerHour;
}
public void setIntervalsPerHour(final int intervals) {
intervalsPerHour = intervals;
}
public int getWorkingHourStart() {
return workingHourStart;
}
public void setWorkingHourStart(final int start) {
workingHourStart = start;
}
public int getWorkingHourEnd() {
return workingHourEnd;
}
public void setWorkingHourEnd(final int end) {
workingHourEnd = end;
}
public int getScrollToHour() {
return scrollToHour;
}
public void setScrollToHour(final int hour) {
scrollToHour = hour;
}
public boolean isEnableDragDrop() {
return enableDragDrop;
}
public void setEnableDragDrop(final boolean enableDragDrop) {
this.enableDragDrop = enableDragDrop;
}
public boolean isOffsetHourLabels() {
return offsetHourLabels;
}
public void setOffsetHourLabels(final boolean offsetHourLabels) {
this.offsetHourLabels = offsetHourLabels;
}
public Click getTimeBlockClickNumber() {
return timeBlockClickNumber;
}
public void setTimeBlockClickNumber(final Click timeBlockClickNumber) {
this.timeBlockClickNumber = timeBlockClickNumber;
}
/**
* @deprecated As of release 0.9.4 removed since is not really needed
*/
@Deprecated
public void setEnableDragDropCreation(final boolean dragDropCreation) {
}
/**
* @deprecated As of release 0.9.4 removed since is not really needed
*/
@Deprecated
public boolean getEnableDragDropCreation() {
return false;
}
public void setDayStartsAt(int dayStartsAt) {
this.dayStartsAt = dayStartsAt;
DateUtils.setDayStartsAt(dayStartsAt);
}
public int getDayStartsAt() {
return dayStartsAt;
}
/**
*
* @param showWeekNumbers
* @since 0.9.4
*/
public void setShowWeekNumbers(final boolean showWeekNumbers) {
this.showWeekNumbers = showWeekNumbers;
}
/**
*
* @since 0.9.4
*/
public boolean isShowingWeekNumbers() {
return showWeekNumbers;
}
public void setHolidays(List<Date> holidays) {
this.holidays = holidays;
}
public List<Date> getHolidays() {
return holidays;
}
/**
*
* @since 0.9.4
*/
public void setShowMultiday(final boolean visible) {
this.showMultiDay = visible;
}
/**
*
* @since 0.9.4
*/
public boolean isMultidayVisible() {
return showMultiDay;
}
public enum Click {
Double, Single, Drag
}
}

View File

@ -0,0 +1,285 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionEvent;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionHandler;
import com.bradrydzewski.gwt.calendar.client.event.HasDaySelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasWeekSelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionEvent;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Label;
/**
* Abstract base class defining the operations to render a calendar and
* user-input dispatching methods. <p/> <p></p>Subclasses will provide the
* details of rendering the calendar to visualize by day (Day View), monthly
* (month view), agenda (list view) and the logic implementing the user-input
* event processing.
*
* @author Brad Rydzewski
*/
public abstract class CalendarView implements HasSettings, HasWeekSelectionHandlers<Date>, HasDaySelectionHandlers<Date> {
/**
* Calendar widget bound to the view.
*
* @see CalendarWidget
*/
protected CalendarWidget calendarWidget = null;
/**
* Number of days the calendar should display at a given time, 3 by
* default.
*/
private int displayedDays = 3;
/**
* Attaches this view to the provided {@link CalendarWidget}.
*
* @param calendarWidget The interactive widget containing the calendar
*/
public void attach(CalendarWidget calendarWidget) {
this.calendarWidget = calendarWidget;
}
/**
* Detaches this view from the currently associated {@link CalendarWidget}.
* TODO: The CalendarWidget might still have a reference to this
* CalendarView, is that correct??
*/
public void detatch() {
calendarWidget = null;
}
/**
* Returns the CSS style name of this calendar view.
*
* @return The CSS style that should be used when rendering this calendar
* view
*/
public abstract String getStyleName();
public void doSizing() {
}
public abstract void doLayout();
/**
* Configures this view component's currently selected
* <code>appointment</code>. Notification to the calendar widget associated
* is optional and controlled with the <code>fireEvent</code> flag.
*
* @param appointment The appointment in the calendar in-memory model to be
* configured as the currently selected
* @param fireEvent Indicates whether a selection event should be
* triggered by the parent widget so that it informs its
* set of registered listeners about the change
*/
// public void setSelectedAppointment(Appointment appointment,
// boolean fireEvent) {
// calendarWidget.setSelectedAppointment(appointment);
// if (fireEvent) {
// calendarWidget.fireSelectionEvent(appointment);
// }
// }
/**
* Configures this view component's currently selected
* <code>appointment</code> and notifies widget about the change in the
* model state.
*
* @param appointment The appointment in the calendar in-memory model to be
* configured as the currently selected
*/
// public void setSelectedAppointment(Appointment appointment) {
// setSelectedAppointment(appointment, true);
// }
/**
* Returns the configured number of days the calendar should display at a
* given time.
*
* @return The number of days this calendar view should display at a given
* time
*/
public int getDisplayedDays() {
return displayedDays;
}
/**
* Sets the configured number of days the calendar should display at a given
* time.
*
* @param displayedDays The number of days this calendar view should display
* at a given time
*/
public void setDisplayedDays(int displayedDays) {
this.displayedDays = displayedDays;
}
/* on clicks */
public abstract void onDoubleClick(Element element, Event event);
public abstract void onSingleClick(Element element, Event event);
public abstract void onMouseOver(Element element, Event event);
/**
* Processes user {@link com.google.gwt.event.dom.client.KeyCodes#KEY_DELETE}
* and {@link com.google.gwt.event.dom.client.KeyCodes.KEY_BACKSPACE}
* keystrokes. The <code>CalendarView</code> implementation is empty so that
* subclasses are not forced to implement it if no specific logic is needed
* for {@link com.google.gwt.event.dom.client.KeyCodes#KEY_DELETE} or
* {@link com.google.gwt.event.dom.client.KeyCodes#KEY_BACKSPACE} keystrokes.
*/
public void onDeleteKeyPressed() {
}
/**
* Processes user {@link com.google.gwt.event.dom.client.KeyCodes#KEY_UP}
* keystrokes. The <code>CalendarView</code> implementation is empty so that
* subclasses are not forced to implement it if no specific logic is needed
* for {@link com.google.gwt.event.dom.client.KeyCodes#KEY_UP} keystrokes.
*/
public void onUpArrowKeyPressed() {
}
/**
* Processes user {@link com.google.gwt.event.dom.client.KeyCodes#KEY_DOWN}
* keystrokes. The <code>CalendarView</code> implementation is empty so that
* subclasses are not forced to implement it if no specific logic is needed
* for {@link com.google.gwt.event.dom.client.KeyCodes#KEY_DOWN}
* keystrokes.
*/
public void onDownArrowKeyPressed() {
}
/**
* Processes user {@link com.google.gwt.event.dom.client.KeyCodes#KEY_LEFT}
* keystrokes. The <code>CalendarView</code> implementation is empty so that
* subclasses are not forced to implement it if no specific logic is needed
* for {@link com.google.gwt.event.dom.client.KeyCodes#KEY_LEFT}
* keystrokes.
*/
public void onLeftArrowKeyPressed() {
}
/**
* Processes user {@link com.google.gwt.event.dom.client.KeyCodes#KEY_RIGHT}
* keystrokes. The <code>CalendarView</code> implementation is empty so that
* subclasses are not forced to implement it if no specific logic is needed
* for {@link com.google.gwt.event.dom.client.KeyCodes#KEY_RIGHT}
* keystrokes.
*/
public void onRightArrowKeyPressed() {
}
public abstract void onAppointmentSelected(Appointment appt);
public final void selectAppointment(Appointment appt) {
calendarWidget.setSelectedAppointment(appt, true);
}
public final void selectNextAppointment() {
calendarWidget.selectNextAppointment();
}
public final void selectPreviousAppointment() {
calendarWidget.selectPreviousAppointment();
}
public final void updateAppointment(Appointment toAppt) {
calendarWidget.fireUpdateEvent(toAppt);
}
public final void deleteAppointment(Appointment appt) {
calendarWidget.fireDeleteEvent(appt);
}
public final void openAppointment(Appointment appt) {
calendarWidget.fireOpenEvent(appt);
}
public final void createAppointment(Appointment appt) {
createAppointment(appt.getStart(), appt.getEnd());
}
public final void createAppointment(Date start, Date end) {
calendarWidget.fireTimeBlockClickEvent(start);
}
public void scrollToHour(int hour) {
}
public CalendarSettings getSettings() {
return calendarWidget.getSettings();
}
public void setSettings(CalendarSettings settings) {
calendarWidget.setSettings(settings);
}
protected void addDayClickHandler(final Label dayLabel, final Date day) {
dayLabel.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
fireSelectedDay(day);
}
});
}
protected void addWeekClickHandler(final Label weekLabel, final Date day) {
weekLabel.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
fireSelectedWeek(day);
}
});
}
protected void fireSelectedDay(final Date day) {
DaySelectionEvent.fire(this, day);
}
protected void fireSelectedWeek(final Date day) {
WeekSelectionEvent.fire(this, day);
}
public HandlerRegistration addWeekSelectionHandler(
WeekSelectionHandler<Date> handler) {
return calendarWidget.addHandler(handler, WeekSelectionEvent.getType());
}
public HandlerRegistration addDaySelectionHandler(
DaySelectionHandler<Date> handler) {
return calendarWidget.addHandler(handler, DaySelectionEvent.getType());
}
public void fireEvent(GwtEvent<?> event) {
calendarWidget.fireEvent(event);
}
}

View File

@ -0,0 +1,48 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import com.bradrydzewski.gwt.calendar.client.agenda.AgendaView;
import com.bradrydzewski.gwt.calendar.client.dayview.DayView;
import com.bradrydzewski.gwt.calendar.client.monthview.MonthView;
/**
* Enumeration that represents each standard {@link CalendarView}.
* @author Brad Rydzewski
* @since 0.9.0
*/
public enum CalendarViews {
/**
* Represents the {@link DayView}, which presents a set of
* Appointments a single day at a time.
*/
DAY,
/**
* Represents the {@link MonthView}, which presents a set of
* Appointments for a whole month.
*/
MONTH,
/**
* Represents the {@link AgendaView}, which presents a set of
* Appointments as a list.
*/
AGENDA,
TECH,
}

View File

@ -0,0 +1,565 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.util.Date;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.event.CreateEvent;
import com.bradrydzewski.gwt.calendar.client.event.CreateHandler;
import com.bradrydzewski.gwt.calendar.client.event.DateRequestEvent;
import com.bradrydzewski.gwt.calendar.client.event.DateRequestHandler;
import com.bradrydzewski.gwt.calendar.client.event.DeleteEvent;
import com.bradrydzewski.gwt.calendar.client.event.DeleteHandler;
import com.bradrydzewski.gwt.calendar.client.event.HasDateRequestHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasDeleteHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasMouseOverHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasTimeBlockClickHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasUpdateHandlers;
import com.bradrydzewski.gwt.calendar.client.event.MouseOverEvent;
import com.bradrydzewski.gwt.calendar.client.event.MouseOverHandler;
import com.bradrydzewski.gwt.calendar.client.event.TimeBlockClickEvent;
import com.bradrydzewski.gwt.calendar.client.event.TimeBlockClickHandler;
import com.bradrydzewski.gwt.calendar.client.event.UpdateEvent;
import com.bradrydzewski.gwt.calendar.client.event.UpdateHandler;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Widget;
/**
* <code>CalendarWidget</code> is an {@link com.bradrydzewski.gwt.calendar.client.InteractiveWidget}
* that maintains a calendar model (a set of {@link Appointment} objects)
* managed through an {@link AppointmentManager}.
* <p/>
* TODO: Need Calendar "View" - CHECK
* TODO: Need CalendarSettings
* TODO: Need LayoutStrategy - CHECK
* TODO: Need DragDropStrategy
* TODO: Need ResizeStrategy ??? or is this same as DragDrop
* TODO: Add AppointmentBuilder ??? downside is that if the Appointment object is updated, need to refersh widget
*
* @author Brad Rydzewski
* @see com.bradrydzewski.gwt.calendar.client.InteractiveWidget
*/
public class CalendarWidget extends InteractiveWidget implements
HasSelectionHandlers<Appointment>, HasDeleteHandlers<Appointment>,
HasOpenHandlers<Appointment>, HasTimeBlockClickHandlers<Date>,
HasUpdateHandlers<Appointment>, HasDateRequestHandlers<Date>,
HasMouseOverHandlers<Appointment>,
HasLayout, HasAppointments {
/**
* Set to <code>true</code> if the calendar layout is suspended and cannot
* be triggered.
*/
private boolean layoutSuspended = false;
/**
* Set to <code>true</code> if the calendar is pending the layout of its
* appointments.
*/
private boolean layoutPending = false;
/**
* The date currently displayed by the calendar. Set to current system date
* by default.
*/
private Date date;
/**
* Calendar settings, set to default.
*/
private CalendarSettings settings = CalendarSettings.DEFAULT_SETTINGS;
/**
* The component to manage the set of appointments displayed by this
* <code>CalendarWidget</code>.
*/
private AppointmentManager appointmentManager = null;
private CalendarView view = null;
/**
* Creates a <code>CalendarWidget</code> with an empty set of appointments
* and the current system date as the date currently displayed by the
* calendar.
*/
public CalendarWidget() {
this(new Date());
}
public CalendarWidget(Date date) {
super();
appointmentManager = new AppointmentManager();
this.date = date;
DateUtils.resetTime(this.date);
}
/**
* Changes the current view of this calendar widget to the specified
* <code>view</code>. By setting this widget's current view the whole widget
* panel is cleared.
*
* @param view The {@link CalendarView} implementation to render this
* widget's underlying calendar
*/
public final void setView(CalendarView view) {
this.getRootPanel().clear();
this.view = view;
this.view.attach(this);
this.setStyleName(this.view.getStyleName());
this.refresh();
}
public final CalendarView getView() {
return view;
}
public Date getDate() {
return (Date) date.clone();
}
public void setDate(Date date, int days) {
Date dateCopy = (Date)date.clone();
DateUtils.resetTime(dateCopy);
this.date = dateCopy;
view.setDisplayedDays(days);
refresh();
}
public void setDate(Date date) {
setDate(date, getDays());
}
/**
* Moves this calendar widget current <code>date</code> as many days as
* specified by the <code>numOfDays</code> parameter.
*
* @param numOfDays The number of days to change the calendar date forward
* (positive number) or backwards.
*/
@SuppressWarnings("deprecation")
public void addDaysToDate(int numOfDays) {
this.date.setDate(this.date.getDate() + numOfDays);
}
public int getDays() {
return view == null ? 3 : view.getDisplayedDays();
}
public void setDays(int days) {
view.setDisplayedDays(days);
refresh();
}
/**
* Returns the collection of appointments in the underlying in-memory model
* of this calendar widget. <strong>Warning</strong>: the returned
* collection of apointments can be modified by client code, possibly
* breaking the system model invariants.
*
* @return The set of appointments to be displayed by this calendar widget
* @see AppointmentManager#getAppointments()
*/
public List<Appointment> getAppointments() {
return appointmentManager.getAppointments();
}
/**
* Removes an appointment from the calendar.
*
* @param appointment the item to be removed.
*/
public void removeAppointment(Appointment appointment) {
removeAppointment(appointment, false);
}
/**
* Removes the currently selected appointment from the model, if such
* appointment is set.
*/
public void removeCurrentlySelectedAppointment() {
appointmentManager.removeCurrentlySelectedAppointment();
}
/**
* Removes an appointment from the calendar.
*
* @param appointment the item to be removed.
* @param fireEvents <code>true</code> to allow deletion events to be
* fired
*/
public void removeAppointment(Appointment appointment, boolean fireEvents) {
boolean commitChange = true;
if (fireEvents) {
commitChange = DeleteEvent.fire(this, getSelectedAppointment());
}
if (commitChange) {
appointmentManager.removeAppointment(appointment);
refresh();
}
}
/**
* Resets the &quot;currently selected&quot; appointment of this calendar.
*
* @see com.bradrydzewski.gwt.calendar.client.AppointmentManager
*/
public void resetSelectedAppointment() {
appointmentManager.resetSelectedAppointment();
}
/**
* Adds an appointment to the calendar.
*
* @param appointment item to be added
*/
public void addAppointment(Appointment appointment) {
if (appointment == null) {
throw new NullPointerException("Added appointment cannot be null.");
}
appointmentManager.addAppointment(appointment);
refresh();
}
/**
* Adds each appointment in the list to the calendar.
*
* @param appointments items to be added.
*/
public void addAppointments(List<Appointment> appointments) {
appointmentManager.addAppointments(appointments);
refresh();
}
/**
* Clears all appointment items.
*/
public void clearAppointments() {
appointmentManager.clearAppointments();
refresh();
}
/**
* Sets the currently selected item.
*
* @param appointment the item to be selected, or <code>null</code> to
* de-select all items.
*/
public void setSelectedAppointment(Appointment appointment) {
setSelectedAppointment(appointment, true);
}
public void setSelectedAppointment(Appointment appointment,
boolean fireEvents) {
appointmentManager.setSelectedAppointment(appointment);
if (fireEvents) {
fireSelectionEvent(appointment);
}
}
/**
* Indicates whether there is a &quot;currently selected&quot; appointment
* at the moment.
*
* @return <code>true</code> if there is an appointment currently selected,
* <code>false</code> if it is <code>null</code>.
* @see com.bradrydzewski.gwt.calendar.client.AppointmentManager#hasAppointmentSelected()
*/
public boolean hasAppointmentSelected() {
return appointmentManager.hasAppointmentSelected();
}
/**
* Gets the currently selected item.
*
* @return the selected item.
*/
public Appointment getSelectedAppointment() {
return appointmentManager.getSelectedAppointment();
}
/**
* Tells whether the passed <code>appointment</code> is the currently
* selected appointment.
*
* @param appointment The appointment to test to be the currently selected
* @return <code>true</code> if there is a currently selected appointment
* and happens to be equal to the passed <code>appointment</code>
* @see com.bradrydzewski.gwt.calendar.client.AppointmentManager#isTheSelectedAppointment(Appointment)
*/
public boolean isTheSelectedAppointment(Appointment appointment) {
return appointmentManager.isTheSelectedAppointment(appointment);
}
/**
* Performs all layout calculations for the list of appointments and resizes
* the Calendar View appropriately.
*/
protected void refresh() {
if (layoutSuspended) {
layoutPending = true;
return;
}
appointmentManager.resetHoveredAppointment();
appointmentManager.sortAppointments();
doLayout();
doSizing();
}
public void doLayout() {
view.doLayout();
}
public void doSizing() {
view.doSizing();
}
public void onLoad() {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
public void execute() {
refresh();
}
});
}
/**
* Suspends the calendar from performing a layout. This can be useful when
* adding a large number of appointments at a time, since a layout is
* performed each time an appointment is added.
*/
public void suspendLayout() {
layoutSuspended = true;
}
/**
* Allows the calendar to perform a layout, sizing the component and placing
* all appointments. If a layout is pending it will get executed when this
* method is called.
*/
public void resumeLayout() {
layoutSuspended = false;
if (layoutPending) {
refresh();
}
}
public CalendarSettings getSettings() {
return this.settings;
}
public void setSettings(CalendarSettings settings) {
this.settings = settings;
}
public void scrollToHour(int hour) {
view.scrollToHour(hour);
}
public boolean selectPreviousAppointment() {
boolean selected = appointmentManager.selectPreviousAppointment();
if (selected) {
fireSelectionEvent(getSelectedAppointment());
}
return selected;
}
public boolean selectNextAppointment() {
boolean selected = appointmentManager.selectNextAppointment();
if (selected) {
fireSelectionEvent(getSelectedAppointment());
}
return selected;
}
@Override
public void onDeleteKeyPressed() {
view.onDeleteKeyPressed();
}
@Override
public void onDoubleClick(Element element, Event event) {
view.onDoubleClick(element, event);
}
@Override
public void onDownArrowKeyPressed() {
view.onDownArrowKeyPressed();
}
@Override
public void onLeftArrowKeyPressed() {
view.onLeftArrowKeyPressed();
}
@Override
public void onMouseDown(Element element, Event event) {
view.onSingleClick(element, event);
}
public void onMouseOver(Element element, Event event) {
view.onMouseOver(element, event);
}
@Override
public void onRightArrowKeyPressed() {
view.onRightArrowKeyPressed();
}
@Override
public void onUpArrowKeyPressed() {
view.onUpArrowKeyPressed();
}
public void fireOpenEvent(Appointment appointment) {
OpenEvent.fire(this, appointment);
}
public void fireDeleteEvent(Appointment appointment) {
//fire the event to notify the client
boolean allow = DeleteEvent.fire(this, appointment);
if (allow) {
appointmentManager.removeAppointment(appointment);
refresh();
}
}
public void fireSelectionEvent(Appointment appointment) {
view.onAppointmentSelected(appointment);
SelectionEvent.fire(this, appointment);
}
public void fireMouseOverEvent(
Appointment appointment, Element element) {
//we need to make sure we aren't re-firing the event
// for the same appointment. This is a bit problematic,
// because the mouse over event will fire for an appointment's
// child elements (title label, footer, body, for example)
// and will cause this method to be called with a null
// appointment. this is a temp workaround, but basically
// an appointment cannot be hovered twice in a row
if (appointment != null
&& !appointment.equals(appointmentManager
.getHoveredAppointment())) {
appointmentManager.setHoveredAppointment(appointment);
MouseOverEvent.fire(this, appointment, element);
}
}
public void fireTimeBlockClickEvent(Date date) {
TimeBlockClickEvent.fire(this, date);
}
public void fireCreateEvent(Appointment appointment) {
boolean allow = CreateEvent.fire(this, appointment);
if (!allow) {
appointmentManager.rollback();
refresh();
}
}
public void fireDateRequestEvent(Date date) {
DateRequestEvent.fire(this, date);
}
public void fireDateRequestEvent(Date date, Element clicked) {
DateRequestEvent.fire(this, date, clicked);
}
public void fireUpdateEvent(Appointment appointment) {
//refresh the appointment
refresh();
//fire the event to notify the client
boolean allow = UpdateEvent.fire(this, appointment);
if (!allow) {
appointmentManager.rollback();
refresh();
}
}
public HandlerRegistration addSelectionHandler(
SelectionHandler<Appointment> handler) {
return addHandler(handler, SelectionEvent.getType());
}
public HandlerRegistration addDeleteHandler(
DeleteHandler<Appointment> handler) {
return addHandler(handler, DeleteEvent.getType());
}
public HandlerRegistration addMouseOverHandler(
MouseOverHandler<Appointment> handler) {
return addHandler(handler, MouseOverEvent.getType());
}
public HandlerRegistration addTimeBlockClickHandler(
TimeBlockClickHandler<Date> handler) {
return addHandler(handler, TimeBlockClickEvent.getType());
}
public HandlerRegistration addUpdateHandler(UpdateHandler<Appointment> handler) {
return addHandler(handler, UpdateEvent.getType());
}
public HandlerRegistration addCreateHandler(
CreateHandler<Appointment> handler) {
return addHandler(handler, CreateEvent.getType());
}
public HandlerRegistration addOpenHandler(
OpenHandler<Appointment> handler) {
return addHandler(handler, OpenEvent.getType());
}
public HandlerRegistration addDateRequestHandler(
DateRequestHandler<Date> handler) {
return addHandler(handler, DateRequestEvent.getType());
}
public void addToRootPanel(Widget widget) {
getRootPanel().add(widget);
}
public void setRollbackAppointment(Appointment appt) {
appointmentManager.setRollbackAppointment(appt);
}
public void setCommittedAppointment(Appointment appt) {
appointmentManager.setCommittedAppointment(appt);
}
}

View File

@ -0,0 +1,405 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009-2011 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import java.util.Date;
/**
* Contains utility methods involving dates. This class should remain GWT-API
* independent.
*
* @author Carlos D. Morales
*/
public class DateUtils {
/**
* Number of milliseconds in a day.
*/
public static final long MILLIS_IN_A_DAY = 1000 * 60 * 60 * 24;
/**
* Defines the Number of Days in a Week.
*/
public static final int DAYS_IN_A_WEEK = 7;
private static int dayStartsAt = 0;
public static void setDayStartsAt(int start) {
dayStartsAt = start;
}
public static int getDayStartsAt() {
return dayStartsAt;
}
/**
* Provides a <code>null</code>-safe way to return the number of milliseconds
* on a <code>date</code>.
*
* @param date The date whose value in milliseconds will be returned
* @return The number of milliseconds in <code>date</code>, <code>0</code>
* (zero) if <code>date</code> is <code>null</code>.
*/
private static long safeInMillis(Date date) {
return date != null ? date.getTime() : 0;
}
/**
* Returns the number of days between the passed dates.
*
* @param endDate The upper limit of the date range
* @param startDate The lower limit of the date range
* @return The number of days between <code>endDate</code> and
* <code>starDate</code> (inclusive)
*/
@SuppressWarnings(value = "deprecation")
public static int differenceInDays(Date endDate, Date startDate) {
int difference = 0;
if (!areOnTheSameDay(endDate, startDate)) {
int endDateOffset = -(endDate.getTimezoneOffset() * 60 * 1000);
long endDateInstant = endDate.getTime() + endDateOffset;
int startDateOffset = -(startDate.getTimezoneOffset() * 60 * 1000);
long startDateInstant = startDate.getTime() + startDateOffset;
double differenceDouble =
(double) Math.abs(endDateInstant - startDateInstant) /
(double) MILLIS_IN_A_DAY;
differenceDouble = Math.max(1.0D, differenceDouble);
difference = (int) differenceDouble;
}
return difference;
}
/**
* Returns the full year (4-digits) of the passed <code>date</code>.
* @param date The date whose year will be returned
* @return The full year of the passed <code>date</code>.
*/
@SuppressWarnings("deprecation")
public static int year(Date date) {
return 1900 + date.getYear();
}
/**
* Moves a date <code>shift</code> days. A clone of <code>date</code> to
* prevent undesired object modifications.
*
* @param date The date to shift
* @param shift The number of days to push the original <code>date</code>
* <em>forward</em>
* @return A <em>new</em> date pushed <code>shift</code> days forward
*/
@SuppressWarnings("deprecation")
public static Date shiftDate(Date date, int shift) {
Date result = (Date) date.clone();
result.setDate(date.getDate() + shift);
return result;
}
/**
* Resets the date to have no time modifiers (hours, minutes, seconds.)
*
* @param date The date to reset
*/
@SuppressWarnings("deprecation")
public static void resetTime(Date date) {
long milliseconds = safeInMillis(date);
milliseconds = (milliseconds / 1000) * 1000;
date.setTime(milliseconds);
date.setHours(DateUtils.getDayStartsAt());
date.setMinutes(0);
date.setSeconds(0);
}
/**
* Indicates whether two dates are on the same date by comparing their day,
* month and year values. Time values such as hours and minutes are not
* considered in this comparison.
*
* @param dateOne The first date to test
* @param dateTwo The second date to test
* @return <code>true</code> if both dates have their <code>date</code>,
* <code>month</code> and <code>year</code> properties with the
* <em>exact</em> same values (whatever they are)
*/
@SuppressWarnings("deprecation")
public static boolean areOnTheSameDay(Date dateOne, Date dateTwo) {
Date end;
Date start;
if (dateTwo.getTime() > dateOne.getTime()) {
end = (Date) dateTwo.clone();
start = (Date) dateOne.clone();
} else {
start = (Date) dateTwo.clone();
end = (Date) dateOne.clone();
}
if (DateUtils.dayStartsAt != 0) {
long time = start.getTime()
- (1000 * 60 * 60 * DateUtils.dayStartsAt);
start = new Date(time);
time = end.getTime()
- (1000 * 60 * 60 * DateUtils.dayStartsAt);
end = new Date(time);
}
return start.getDate() == end.getDate()
&& start.getMonth() == end.getMonth()
&& start.getYear() == end.getYear();
}
/**
* Indicates whether two dates are on the same month of the same year.
*
* @param dateOne The first date of the comparison
* @param dateTwo The second date of the comparison
* @return <code>true</code> if both dates have the same year and month,
* <code>false</code> otherwise
*/
@SuppressWarnings("deprecation")
public static boolean areOnTheSameMonth(Date dateOne, Date dateTwo) {
Date end = (Date)dateTwo.clone();
if (DateUtils.dayStartsAt != 0) {
long time = end.getTime() - (1000 * 60 * 60 * DateUtils.dayStartsAt);
end = new Date(time);
}
return dateOne.getYear() == end.getYear() &&
dateOne.getMonth() == end.getMonth();
}
/**
* Returns a clone of the <code>anyDayInMonth</code> date set to the
* <em>first</em> day of whatever its month is.
*
* @param anyDayInMonth Any date on a month+year
* @return A clone of the <code>anyDayInMonth</code> date, representing the
* first day of that same month and year
*/
@SuppressWarnings("deprecation")
public static Date firstOfTheMonth(Date anyDayInMonth) {
Date first = (Date) anyDayInMonth.clone();
first.setDate(1);
return first;
}
/**
* Moves the date of the passed object to be one day after whatever date it
* has.
*
* @param date An object representing a date
* @return The day
*/
@SuppressWarnings("deprecation")
public static Date moveOneDayForward(Date date) {
date.setDate(date.getDate() + 1);
return date;
}
/**
* Returns the date corresponding to the first day of the next month relative
* to the passed <code>date</code>.
*
* @param date The reference date
* @return The first day of the next month, if the month of the passed date
* corresponds to december (<code>11</code>) <em>one</em> will be
* added to the year of the returned date.
*/
@SuppressWarnings("deprecation")
public static Date firstOfNextMonth(Date date) {
Date firstOfNextMonth = null;
if (date != null) {
int year = date.getMonth() == 11 ? date.getYear() + 1 : date.getYear();
firstOfNextMonth = new Date(year, date.getMonth() + 1 % 11, 1);
}
return firstOfNextMonth;
}
/**
* Returns a day <em>exactly</em> 24 hours before the instant passed as
* <code>date</code>. // TODO: This logic should address the time zone
* offset
*
* @param date A point in time from which the moment 24 hours before will be
* calculated
* @return A new object <em>24</em> hours prior to the passed
* <code>date</code>
*/
public static Date previousDay(Date date) {
return new Date(date.getTime() - MILLIS_IN_A_DAY);
}
/**
* Copies the hours, minutes and seconds in the <code>source</code> date into
* the <code>target</code> date object.
*
* @param source The date with the hour, minutes and seconds to be copied
* @param target The date whose time fields will be set
*/
@SuppressWarnings("deprecation")
public static void copyTime(Date source, Date target) {
target.setHours(source.getHours());
target.setMinutes(source.getMinutes());
target.setSeconds(source.getSeconds());
}
/**
* Returns the amount of minutes elapsed since the beginning of the passed
* <code>day</code>.
*
* @param day The day to calculate the elapsed minutes
* @return The number of minutes since <code>day</code> started
*/
@SuppressWarnings("deprecation")
public static int minutesSinceDayStarted(Date day) {
int minutes = (day.getHours() - dayStartsAt) * 60 + day.getMinutes();
if (day.getHours() < dayStartsAt) {
minutes = ((24 - dayStartsAt) + day.getHours()) * 60 + day.getMinutes();
}
return minutes;
}
/**
* Creates a new date with whatever date/time the passed <code>date</code>
* object represents.
*
* @param date The source date
* @return A new date object representing the same date and time as the passed
* object
*/
public static Date newDate(Date date) {
Date result = null;
if (date != null) {
result = new Date(date.getTime());
}
return result;
}
@SuppressWarnings("deprecation")
public static boolean isWeekend(final Date day) {
return day.getDay()==0 || day.getDay()==6;
}
@SuppressWarnings("deprecation")
public static Integer weekday(final Date date) {
int firstDayOfWeek = Integer.valueOf(CalendarFormat.INSTANCE.getFirstDayOfWeek());
int weekday = date.getDay();
if ((firstDayOfWeek == 1) && (weekday == 0)) {
weekday = 7;
}
return weekday;
}
@SuppressWarnings("deprecation")
public static int calendarWeekIso(final Date inputDate) {
final int daysWeek = 7;
int firstDayOfWeek = Integer.valueOf(CalendarFormat.INSTANCE.getFirstDayOfWeek());
int thursdayDay = 4 + firstDayOfWeek;
Date thisThursday = new Date(inputDate.getYear(), inputDate.getMonth(),
inputDate.getDate() - weekday(inputDate) + thursdayDay);
Date firstThursdayOfYear = new Date(thisThursday.getYear(), 0, 1);
while (weekday(firstThursdayOfYear) != thursdayDay) {
firstThursdayOfYear.setDate(firstThursdayOfYear.getDate() + 1);
}
Date firstMondayOfYear = new Date(firstThursdayOfYear.getYear(), 0,
firstThursdayOfYear.getDate() - 3);
Long cw = (thisThursday.getTime() - firstMondayOfYear.getTime())
/ MILLIS_IN_A_DAY / daysWeek + 1;
return cw.intValue();
}
/**
* Adds or subtracts the specified amount of days for the given Date.
*
* @param date
* A point in time
* @param days
* Number of days to add or substract
* @return A new object <em>days</em> after to the passed <code>date</code>
*/
public static Date addDays(final Date date, final int days) {
return new Date(date.getTime() + (days * MILLIS_IN_A_DAY));
}
/**
* Adds or subtracts the specified amount of weeks for the given Date.
*
* @param date
* A point in time
* @param weeks
* Number of weeks to add or substract
* @return A new object <em>weeks</em> after to the passed <code>date</code>
*/
public static Date addWeeks(final Date date, final int weeks) {
return new Date(date.getTime() + (weeks * (MILLIS_IN_A_DAY * DAYS_IN_A_WEEK)));
}
/**
* Adds or subtracts the specified amount of months for the given Date.
*
* @param date
* A point in time
* @param months
* Number of months to add or substract
* @return A new object <em>weeks</em> after to the passed <code>date</code>
*/
public static Date addMonths(final Date date, final int months) {
return new Date(date.getTime() + (months * (MILLIS_IN_A_DAY * DAYS_IN_A_WEEK)));
}
/**
* Returns the total number of days between <code>start</code> and
* <code>end</code>. The calculation is rounded to take in account
* the saving day calculation.
*
* @param start
* The first day in the period
* @param end
* The last day in the period
* @return The number of days between <code>start</code> and
* <code>end</code>, the minimum difference being one (
* <code>1</code>)
*/
public static int daysInPeriod(final Date start, final Date end) {
long diff = end.getTime() - start.getTime();
double days = ((double)diff / MILLIS_IN_A_DAY) + 1;
Long result = Math.round(days);
return result.intValue();
}
/**
* Calculates the amount of days in the month the
* @param day
* @return
*/
public static int daysInMonth(final Date date) {
return 32 - new Date(date.getYear(), date.getMonth(), 32).getDate();
}
}

View File

@ -0,0 +1,17 @@
package com.bradrydzewski.gwt.calendar.client;
import java.util.List;
public interface HasAppointments {
void removeAppointment(Appointment appointment);
void removeAppointment(Appointment appointment, boolean fireEvents);
void addAppointment(Appointment appointment);
void addAppointments(List<Appointment> appointments);
void clearAppointments();
Appointment getSelectedAppointment();
void setSelectedAppointment(Appointment appointment);
void setSelectedAppointment(Appointment appointment,
boolean fireEvents);
boolean hasAppointmentSelected();
}

View File

@ -0,0 +1,21 @@
package com.bradrydzewski.gwt.calendar.client;
public interface HasLayout {
/**
* Forces the widget to re-calculate and perform
* layout operations.
*/
public void doLayout();
/**
* Suspends the widget from performing layout operations.
*/
public void suspendLayout();
/**
* Enables the widget to perform layout operations. Any pending layout
* operations will be executed.
*/
public void resumeLayout();
}

View File

@ -0,0 +1,7 @@
package com.bradrydzewski.gwt.calendar.client;
public interface HasSettings {
public CalendarSettings getSettings();
public void setSettings(CalendarSettings settings);
}

View File

@ -0,0 +1,253 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.RootPanel;
/**
* Abstract class for widgets that react to keystrokes and mouse gestures
* providing a centralized place for the association between user inputs and the
* logic to perform. Subclasses will implement the <code>onXXXKeyPressed</code>,
* <code>onMouseDown</code> and <code>onDoubleClick</code> methods to provide
* the custom event processing logic.
*
* @author Brad Rydzewski
*/
public abstract class InteractiveWidget extends Composite {
/**
* Focus widget used to add keyboard and mouse focus to a calendar.
*/
private FocusPanel focusPanel = new FocusPanel();
/**
* Main panel hold all other components.
*/
protected FlowPanel rootPanel = new FlowPanel();
/**
* Used by focus widget to make sure a key stroke is only handled once by
* the calendar.
*/
private boolean lastWasKeyDown = false;
public InteractiveWidget() {
initWidget(rootPanel);
//Sink events, mouse and keyboard for now
sinkEvents(Event.ONMOUSEDOWN | Event.ONDBLCLICK | Event.KEYEVENTS | Event.ONMOUSEOVER);
hideFocusPanel();
//Add key handler events to the focus panel
focusPanel.addKeyPressHandler(new KeyPressHandler() {
public void onKeyPress(KeyPressEvent event) {
if (!lastWasKeyDown) {
keyboardNavigation(event.getNativeEvent().getKeyCode());
}
lastWasKeyDown = false;
}
});
focusPanel.addKeyUpHandler(new KeyUpHandler() {
public void onKeyUp(KeyUpEvent event) {
lastWasKeyDown = false;
}
});
focusPanel.addKeyDownHandler(new KeyDownHandler() {
public void onKeyDown(KeyDownEvent event) {
keyboardNavigation(event.getNativeEvent().getKeyCode());
lastWasKeyDown = true;
}
});
}
/**
* Makes the widget's focus panel invisible.
*/
private void hideFocusPanel() {
/*
focusPanel.setVisible(false);
RootPanel.get().add(focusPanel);
DOM.setStyleAttribute(focusPanel.getElement(), "position", "absolute");
DOM.setStyleAttribute(focusPanel.getElement(), "top", "0");
DOM.setStyleAttribute(focusPanel.getElement(), "left", "0");
DOM.setStyleAttribute(focusPanel.getElement(), "height", "100%");
DOM.setStyleAttribute(focusPanel.getElement(), "width", "100%");
*/
RootPanel.get().add(focusPanel);
DOM.setStyleAttribute(focusPanel.getElement(), "position", "absolute");
DOM.setStyleAttribute(focusPanel.getElement(), "top", "-10");
DOM.setStyleAttribute(focusPanel.getElement(), "left", "-10");
DOM.setStyleAttribute(focusPanel.getElement(), "height", "0px");
DOM.setStyleAttribute(focusPanel.getElement(), "width", "0px");
}
public ComplexPanel getRootPanel() {
return rootPanel;
}
/**
* Processes mouse double-click events. Concrete interactive widgets should
* provide the component's specific logic.
*
* @param element The HTML DOM element that originated the event
*/
public abstract void onDoubleClick(Element element, Event event);
/**
* Processes mouse over events. Concrete interactive widgets
* should provide the component's specific logic.
*
* @param element The HTML DOM element that originated the event
* @param event The HTML DOM event that was triggered
*/
public abstract void onMouseOver(Element element, Event event);
/**
* Processes mouse button pressing events. Concrete interactive widgets
* should provide the component's specific logic.
*
* @param element The HTML DOM element that originated the event
*/
public abstract void onMouseDown(Element element, Event event);
/**
* Processes {@link com.google.gwt.event.dom.client.KeyCodes.KEY_DELETE}
* and {@link com.google.gwt.event.dom.client.KeyCodes.KEY_BACKSPACE}
* keystrokes. Concrete interactive widgets should provide the component's
* specific logic.
*/
public abstract void onDeleteKeyPressed();
/**
* Processes {@link com.google.gwt.event.dom.client.KeyCodes.KEY_UP}
* keystrokes. Concrete interactive widgets should provide the component's
* specific logic.
*/
public abstract void onUpArrowKeyPressed();
/**
* Processes {@link com.google.gwt.event.dom.client.KeyCodes.KEY_DOWN}
* keystrokes. Concrete interactive widgets should provide the component's
* specific logic.
*/
public abstract void onDownArrowKeyPressed();
/**
* Processes {@link com.google.gwt.event.dom.client.KeyCodes.KEY_LEFT}
* keystrokes. Concrete interactive widgets should provide the component's
* specific logic.
*/
public abstract void onLeftArrowKeyPressed();
/**
* Processes {@link com.google.gwt.event.dom.client.KeyCodes.KEY_RIGHT}
* keystrokes. Concrete interactive widgets should provide the component's
* specific logic.
*/
public abstract void onRightArrowKeyPressed();
@Override
public void onBrowserEvent(Event event) {
int eventType = DOM.eventGetType(event);
Element element = DOM.eventGetTarget(event);
switch (eventType) {
case Event.ONDBLCLICK: {
onDoubleClick(element, event);
focusPanel.setFocus(true);
break;
}
case Event.ONMOUSEDOWN: {
if (DOM.eventGetCurrentTarget(event) == getElement()) {
onMouseDown(element, event);
focusPanel.setFocus(true);
//Cancel events so Firefox / Chrome don't
//give child widgets with scrollbars focus.
//TODO: Should not cancel onMouseDown events in the event an appointment would have a child widget with a scrollbar (if this would ever even happen).
DOM.eventCancelBubble(event, true);
DOM.eventPreventDefault(event);
return;
}
}
case Event.ONMOUSEOVER:{
if (DOM.eventGetCurrentTarget(event) == getElement()){
onMouseOver(element, event);
DOM.eventCancelBubble(event, true);
DOM.eventPreventDefault(event);
return;
}
}
}
super.onBrowserEvent(event);
}
/**
* Dispatches the processing of a key being pressed to the this widget
* <code>onXXXXKeyPressed</code> methods.
*
* @param key Pressed key code
*/
protected void keyboardNavigation(int key) {
switch (key) {
case KeyCodes.KEY_BACKSPACE: {
onDeleteKeyPressed();
break;
}
case KeyCodes.KEY_DELETE: {
onDeleteKeyPressed();
break;
}
case KeyCodes.KEY_LEFT: {
onLeftArrowKeyPressed();
break;
}
case KeyCodes.KEY_UP: {
onUpArrowKeyPressed();
break;
}
case KeyCodes.KEY_RIGHT: {
onRightArrowKeyPressed();
break;
}
case KeyCodes.KEY_DOWN: {
onDownArrowKeyPressed();
break;
}
}
}
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2011 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client;
/**
* Defines the style attribute values that will vary with a theme
* when a particular theme+style is applied. The currently active
* {@link com.bradrydzewski.gwt.calendar.client.monthview.MonthViewStyleManager} or
* {@link com.bradrydzewski.gwt.calendar.client.dayview.DayViewStyleManager} will use the
* strings in the theme style to style the elements in the view.
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*
* @see com.bradrydzewski.gwt.calendar.theme.google.client.GoogleAppointmentStyle
* @see com.bradrydzewski.gwt.calendar.theme.ical.client.ICalAppointmentStyle
*/
public interface ThemeAppointmentStyle {
public String getBackgroundHeader();
public String getBackground();
public String getSelectedBorder();
public String getHeaderText();
public String getSelectedBackgroundImage();
public String getBorder();
}

View File

@ -0,0 +1,397 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.agenda;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.Attendee;
import com.bradrydzewski.gwt.calendar.client.CalendarView;
import com.bradrydzewski.gwt.calendar.client.CalendarWidget;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.util.AppointmentUtil;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.AbsolutePanel;
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.HasVerticalAlignment;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class AgendaView extends CalendarView {
/**
* Adapter class that maps an Appointment to the widgets (DIV's, etc) that represent
* it on the screen. This is necessary because a single appointment is represented by
* many widgets. For example, an appointment is represented by a title widget,
* a description widget, and has a "get more details" label.
*
* By mapping an appointment to these widgets we can easily figure out which
* appointment the user is interacting with as they click around the AgendaView.
* @author Brad Rydzewski
* @version 1.0
* @since 0.9.0
*/
class AgendaViewAppointmentAdapter {
private Widget titleLabel;
private Widget dateLabel;
private Widget detailsLabel;
private Appointment appointment;
private Widget detailsPanel;
public AgendaViewAppointmentAdapter(Widget titleLabel, Widget dateLabel,
Widget detailsPanel, Widget detailsLabel, Appointment appointment) {
super();
this.titleLabel = titleLabel;
this.dateLabel = dateLabel;
this.detailsLabel = detailsLabel;
this.detailsPanel = detailsPanel;
this.appointment = appointment;
}
public Widget getTitleLabel() {
return titleLabel;
}
public Widget getDateLabel() {
return dateLabel;
}
public Widget getDetailsLabel() {
return detailsLabel;
}
public Appointment getAppointment() {
return appointment;
}
public Widget getDetailsPanel() {
return detailsPanel;
}
}
class AppointmentDetailPanel extends Composite {
private Label moreDetailsRow = new Label();
public AppointmentDetailPanel(SimplePanel detailContainer, Appointment appt) {
initWidget(detailContainer);
// add the detail widget
detailContainer.setStyleName("detailContainer");
AbsolutePanel detailDecorator = new AbsolutePanel();
detailDecorator.setStyleName("detailDecorator");
detailContainer.setVisible(false);
detailContainer.add(detailDecorator);
if (appt.getLocation() != null
&& !appt.getLocation().isEmpty()) {
AbsolutePanel whereRow = new AbsolutePanel();
InlineLabel whereHeader = new InlineLabel("Where: ");
whereHeader.setStyleName("detailHeader");
whereRow.add(whereHeader);
whereRow.add(new InlineLabel(appt.getLocation()));
detailDecorator.add(whereRow);
}
if (appt.getCreatedBy() != null
&& !appt.getCreatedBy().isEmpty()) {
AbsolutePanel creatorRow = new AbsolutePanel();
InlineLabel creatorHeader = new InlineLabel("Creator: ");
creatorHeader.setStyleName("detailHeader");
creatorRow.add(creatorHeader);
creatorRow.add(new InlineLabel(appt.getCreatedBy()));
detailDecorator.add(creatorRow);
}
if (appt.getAttendees() != null
&& !appt.getAttendees().isEmpty()) {
AbsolutePanel whoRow = new AbsolutePanel();
InlineLabel whoHeader = new InlineLabel("Who: ");
whoHeader.setStyleName("detailHeader");
whoRow.add(whoHeader);
for (int a = 0; a < appt.getAttendees().size(); a++) {
Attendee attendee = appt.getAttendees().get(a);
String comma = (a < appt.getAttendees().size() - 1) ? ", "
: "";
String labelText = attendee.getEmail() + comma;
whoRow.add(new InlineLabel(labelText));
}
detailDecorator.add(whoRow);
}
DOM.setInnerHTML(moreDetailsRow.getElement(),
"more details&#0187;");
moreDetailsRow.setStyleName("moreDetailsButton");
//moreDetailsRow.addClickHandler(appointmentClickHandler);
detailDecorator.add(moreDetailsRow);
}
public Label getMoreDetailsLabel() {
return moreDetailsRow;
}
}
/**
* FlexTable used to display a list of appointments.
*/
private FlexTable appointmentGrid = new FlexTable();
/**
* List of appointment adapters, used to map widgets to the appointments
* they represent.
*/
private ArrayList<AgendaViewAppointmentAdapter> appointmentAdapterList =
new ArrayList<AgendaViewAppointmentAdapter>();
/**
* DateTime format used to represent a day.
*/
private static final DateTimeFormat DEFAULT_DATE_FORMAT =
DateTimeFormat.getFormat("EEE MMM d");
/**
* DateTime format used when displaying an appointments start and end time.
*/
private static final DateTimeFormat DEFAULT_TIME_FORMAT =
DateTimeFormat.getShortTimeFormat();
/**
* Style used to format this view.
*/
private String styleName = "gwt-cal-ListView";
/**
* Adds the calendar view to the calendar widget and performs required formatting.
*/
public void attach(CalendarWidget widget) {
super.attach(widget);
appointmentGrid.setCellPadding(5);
appointmentGrid.setCellSpacing(0);
appointmentGrid.setBorderWidth(0);
appointmentGrid.setWidth("100%");
//DOM.setStyleAttribute(appointmentGrid.getElement(), "tableLayout", "fixed");
calendarWidget.getRootPanel().add(appointmentGrid);
calendarWidget.getRootPanel().add(appointmentGrid);
}
/**
* Gets the style name associated with this particular view
* @return Style name.
*/
public String getStyleName() {
return styleName;
}
@Override
public void doLayout() {
appointmentAdapterList.clear();
appointmentGrid.clear();
for (int i = appointmentGrid.getRowCount() - 1; i >= 0; i--) {
appointmentGrid.removeRow(i);
}
//Get the start date, make sure time is 0:00:00 AM
Date startDate = (Date) calendarWidget.getDate().clone();
Date today = new Date();
Date endDate = (Date) calendarWidget.getDate().clone();
endDate.setDate(endDate.getDate() + 1);
DateUtils.resetTime(today);
DateUtils.resetTime(startDate);
DateUtils.resetTime(endDate);
int row = 0;
for (int i = 0; i < calendarWidget.getDays(); i++) {
// Filter the list by date
List<Appointment> filteredList = AppointmentUtil
.filterListByDate(calendarWidget.getAppointments(), startDate, endDate);
if (filteredList != null && filteredList.size() > 0) {
appointmentGrid.setText(row, 0, DEFAULT_DATE_FORMAT.format(startDate));
appointmentGrid.getCellFormatter().setVerticalAlignment(row, 0,
HasVerticalAlignment.ALIGN_TOP);
appointmentGrid.getFlexCellFormatter().setRowSpan(row, 0,
filteredList.size());
appointmentGrid.getFlexCellFormatter().setStyleName(row, 0,
"dateCell");
int startingCell = 1;
//Row styles will alternate, so we set the style accordingly
String rowStyle = (i % 2 == 0) ? "row" : "row-alt";
//If a Row represents the current date (Today) then we style it differently
if (startDate.equals(today))
rowStyle += "-today";
for (Appointment appt : filteredList) {
// add the time range
String timeSpanString = DEFAULT_TIME_FORMAT.format(appt.getStart())
+ " - " + DEFAULT_TIME_FORMAT.format(appt.getEnd());
Label timeSpanLabel = new Label(timeSpanString.toLowerCase());
appointmentGrid.setWidget(row, startingCell, timeSpanLabel);
// add the title and description
FlowPanel titleContainer = new FlowPanel();
InlineLabel titleLabel = new InlineLabel(appt.getTitle());
titleContainer.add(titleLabel);
InlineLabel descLabel = new InlineLabel(" - "
+ appt.getDescription());
descLabel.setStyleName("descriptionLabel");
titleContainer.add(descLabel);
appointmentGrid.setWidget(row, startingCell + 1,
titleContainer);
SimplePanel detailContainerPanel = new SimplePanel();
AppointmentDetailPanel detailContainer= new AppointmentDetailPanel(detailContainerPanel, appt);
appointmentAdapterList.add(new AgendaViewAppointmentAdapter(
titleLabel, timeSpanLabel, detailContainerPanel,
detailContainer.getMoreDetailsLabel(), appt));
//add the detail container
titleContainer.add(detailContainer);
//add click handlers to title, date and details link
timeSpanLabel.addClickHandler(appointmentClickHandler);
titleLabel.addClickHandler(appointmentClickHandler);
detailContainer.getMoreDetailsLabel().addClickHandler(
appointmentClickHandler);
// Format the Cells
appointmentGrid.getCellFormatter().setVerticalAlignment(
row, startingCell, HasVerticalAlignment.ALIGN_TOP);
appointmentGrid.getCellFormatter().setVerticalAlignment(
row, startingCell + 1,
HasVerticalAlignment.ALIGN_TOP);
appointmentGrid.getCellFormatter().setStyleName(row,
startingCell, "timeCell");
appointmentGrid.getCellFormatter().setStyleName(row,
startingCell + 1, "titleCell");
appointmentGrid.getRowFormatter().setStyleName(row,
rowStyle);
// increment the row
// make sure the starting column is reset to 0
startingCell = 0;
row++;
}
}
// increment the date
startDate.setDate(startDate.getDate() + 1);
endDate.setDate(endDate.getDate() + 1);
}
}
/**
* Handles appointments being clicked. Based on the clicked widget will determine
* exactly which appointment was clicked and may 1) expand / collaps the appointment
* details 2) set the selected appointment or 3) trigger an appointment clicked
* event.
*/
private ClickHandler appointmentClickHandler = new ClickHandler() {
public void onClick(ClickEvent event) {
//get the appointment adapter based on the clicked widget
AgendaViewAppointmentAdapter adapter =
getAppointmentFromClickedWidget((Widget)event.getSource());
if (adapter != null) {
if(event.getSource().equals(adapter.getDetailsLabel())) {
//set the selected appointment
calendarWidget.setSelectedAppointment(adapter.getAppointment(), true);
//setSelectedAppointment(adapter.getAppointment(),false);
//calendarWidget.fireOpenEvent(adapter.getAppointment());
} else {
//expand the panel if it is not yet expended
adapter.getDetailsPanel().setVisible(
!adapter.getDetailsPanel().isVisible());
}
}
}
};
/**
* Given Widget w determine which appointment was clicked. This is necessary because
* each appointment has 3 widgets that can be clicked - the title, date range and
* description.
* @param w Widget that was clicked.
* @return Appointment mapped to that widget.
*/
protected AgendaViewAppointmentAdapter getAppointmentFromClickedWidget(Widget w) {
for(AgendaViewAppointmentAdapter a : appointmentAdapterList) {
if(w.equals(a.dateLabel) || w.equals(a.detailsLabel) ||
w.equals(a.titleLabel) || w.equals(a.getDetailsPanel())) {
return a;
}
}
return null;
}
@Override
public void onDoubleClick(Element element, Event event) {
// TODO Auto-generated method stub
}
@Override
public void onSingleClick(Element element, Event event) {
// TODO Auto-generated method stub
}
public void onMouseOver(Element element, Event event) {
}
@Override
public void onAppointmentSelected(Appointment appt) {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,170 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import static com.bradrydzewski.gwt.calendar.client.DateUtils.minutesSinceDayStarted;
import java.util.ArrayList;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
/**
* The Appointment Adapter is used to track the layout of an
* {@link Appointment}. It adds additional fields required to
* calculate layout that are used by the Layout Strategy classes.
*
* This adapter allows us to keep these fields outside of the
* {@link Appointment} class and keep the layout computations' complexity
* away from the user.
*
* @author Brad Rydzewski
*/
public class AppointmentAdapter {
private Appointment appointment;
private int cellStart;
private int cellSpan;
private int columnStart = -1;
private int columnSpan;
private int appointmentStart;
private int appointmentEnd;
private float cellPercentFill;
private float cellPercentStart;
private List<TimeBlock> intersectingBlocks;
private float top;
private float left;
private float width;
private float height;
public float getTop() {
return top;
}
public void setTop(float top) {
this.top = top;
}
public float getLeft() {
return left;
}
public void setLeft(float left) {
this.left = left;
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public AppointmentAdapter(Appointment appointment) {
this.appointment = appointment;
this.appointmentStart = minutesSinceDayStarted(appointment.getStart());
this.appointmentEnd = minutesSinceDayStarted(appointment.getEnd());
this.intersectingBlocks = new ArrayList<TimeBlock>();
}
public int getCellStart() {
return cellStart;
}
public void setCellStart(int cellStart) {
this.cellStart = cellStart;
}
public int getCellSpan() {
return cellSpan;
}
public void setCellSpan(int cellSpan) {
this.cellSpan = cellSpan;
}
public int getColumnStart() {
return columnStart;
}
public void setColumnStart(int columnStart) {
this.columnStart = columnStart;
}
public int getColumnSpan() {
return columnSpan;
}
public void setColumnSpan(int columnSpan) {
this.columnSpan = columnSpan;
}
public int getAppointmentStart() {
return appointmentStart;
}
public void setAppointmentStart(int appointmentStart) {
this.appointmentStart = appointmentStart;
}
public int getAppointmentEnd() {
return appointmentEnd;
}
public void setAppointmentEnd(int appointmentEnd) {
this.appointmentEnd = appointmentEnd;
}
public List<TimeBlock> getIntersectingBlocks() {
return intersectingBlocks;
}
public void setIntersectingBlocks(List<TimeBlock> intersectingBlocks) {
this.intersectingBlocks = intersectingBlocks;
}
public Appointment getAppointment() {
return appointment;
}
public float getCellPercentFill() {
return cellPercentFill;
}
public void setCellPercentFill(float cellPercentFill) {
this.cellPercentFill = cellPercentFill;
}
public float getCellPercentStart() {
return cellPercentStart;
}
public void setCellPercentStart(float cellPercentStart) {
this.cellPercentStart = cellPercentStart;
}
}

View File

@ -0,0 +1,241 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.google.gwt.event.dom.client.HasAllMouseHandlers;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class AppointmentWidget extends FlowPanel {
class Div extends ComplexPanel implements HasAllMouseHandlers {
public Div() {
setElement(DOM.createDiv());
}
public void add(Widget w) {
super.add(w, getElement());
}
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return addDomHandler(handler, MouseDownEvent.getType());
}
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return addDomHandler(handler, MouseUpEvent.getType());
}
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return addDomHandler(handler, MouseMoveEvent.getType());
}
public HandlerRegistration addMouseWheelHandler(
MouseWheelHandler handler) {
return addDomHandler(handler, MouseWheelEvent.getType());
}
}
private String title;
private String description;
private Date start;
private Date end;
private boolean selected;
private float top;
private float left;
private float width;
private float height;
private Widget headerPanel = new Div();
private Panel bodyPanel = new SimplePanel();
private Widget footerPanel = new Div();
private Panel timelinePanel = new SimplePanel();
private Panel timelineFillPanel = new SimplePanel();
private boolean multiDay = false;
private Appointment appointment;
public AppointmentWidget() {
this.setStylePrimaryName("gwt-appointment");
headerPanel.setStylePrimaryName("header");
bodyPanel.setStylePrimaryName("body");
footerPanel.setStylePrimaryName("footer");
timelinePanel.setStylePrimaryName("timeline");
timelineFillPanel.setStylePrimaryName("timeline-fill");
this.add(headerPanel);
this.add(bodyPanel);
this.add(footerPanel);
this.add(timelinePanel);
timelinePanel.add(timelineFillPanel);
DOM.setStyleAttribute(this.getElement(), "position", "absolute");
}
public Widget getBody() {
return this.bodyPanel;
}
public Widget getHeader() {
return this.headerPanel;
}
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
public Date getEnd() {
return end;
}
public void setEnd(Date end) {
this.end = end;
}
public boolean isSelected() {
return selected;
}
public float getTop() {
return top;
}
public void setTop(float top) {
this.top = top;
DOM.setStyleAttribute(this.getElement(), "top", top + "px");
}
public float getLeft() {
return left;
}
public void setLeft(float left) {
this.left = left;
DOM.setStyleAttribute(this.getElement(), "left", left + "%");
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
DOM.setStyleAttribute(this.getElement(), "width", width + "%");
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
DOM.setStyleAttribute(this.getElement(), "height", height + "px");
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
DOM.setInnerHTML(headerPanel.getElement(), title);
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
DOM.setInnerHTML(bodyPanel.getElement(), description);
}
public void formatTimeline(float top, float height) {
timelineFillPanel.setHeight(height + "%");
DOM.setStyleAttribute(timelineFillPanel.getElement(), "top", top + "%");
}
public int compareTo(AppointmentWidget appt) {
// -1 0 1
// less, equal, greater
int compare = this.getStart().compareTo(appt.getStart());
if (compare == 0) {
compare = appt.getEnd().compareTo(this.getEnd());
}
return compare;
}
public Widget getMoveHandle() {
return headerPanel;
}
public Widget getResizeHandle() {
return footerPanel;
}
public boolean isMultiDay() {
return multiDay;
}
public void setMultiDay(boolean isMultiDay) {
this.multiDay = isMultiDay;
}
public Appointment getAppointment() {
return appointment;
}
public void setAppointment(Appointment appointment) {
this.appointment = appointment;
if (appointment.isReadOnly()) {
this.remove(footerPanel);
}
}
}

View File

@ -0,0 +1,571 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandler;
import com.allen_sauer.gwt.dnd.client.DragStartEvent;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.DayViewDropController;
import com.allen_sauer.gwt.dnd.client.drop.DayViewPickupDragController;
import com.allen_sauer.gwt.dnd.client.drop.DayViewResizeController;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.CalendarSettings.Click;
import com.bradrydzewski.gwt.calendar.client.CalendarView;
import com.bradrydzewski.gwt.calendar.client.CalendarWidget;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionHandler;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionHandler;
import com.bradrydzewski.gwt.calendar.client.util.AppointmentUtil;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
public class DayView extends CalendarView {
private DayViewHeader dayViewHeader = null;
private DayViewBody dayViewBody = null;
private DayViewMultiDayBody multiViewBody = null;
private DayViewLayoutStrategy layoutStrategy = null;
private final List<AppointmentWidget> appointmentWidgets = new ArrayList<AppointmentWidget>();
/**
* List of AppointmentAdapter objects that represent the currently selected
* appointment.
*/
private List<AppointmentWidget> selectedAppointmentWidgets = new ArrayList<AppointmentWidget>();
private final DayViewStyleManager styleManager = GWT.create(DayViewStyleManager.class);
private DayViewResizeController resizeController = null;
private DayViewDropController dropController = null;
private PickupDragController dragController = null;
private DayViewResizeController proxyResizeController = null;
public DayView() {
super();
}
private int getMaxProxyHeight() {
// For a comfortable use, the Proxy should be, top 2/3 (66%) of the view
return (2 * (dayViewBody.getScrollPanel().getOffsetHeight() / 3));
}
@SuppressWarnings("deprecation")
public void doLayout() {
// PERFORM APPOINTMENT LAYOUT NOW
final Date date = (Date) calendarWidget.getDate().clone();
if (getSettings().isMultidayVisible()) {
multiViewBody.setDays((Date) date.clone(), calendarWidget.getDays());
}
dayViewHeader.setDays((Date) date.clone(), calendarWidget.getDays());
dayViewHeader.setYear((Date) date.clone());
dayViewBody.setDays((Date) date.clone(), calendarWidget.getDays());
dayViewBody.getTimeline().prepare();
dropController.setColumns(calendarWidget.getDays());
dropController.setIntervalsPerHour(calendarWidget.getSettings().getIntervalsPerHour());
dropController.setDayStartsAt(getSettings().getDayStartsAt());
dropController.setDate((Date)calendarWidget.getDate().clone());
dropController.setSnapSize(
calendarWidget.getSettings().getPixelsPerInterval());
dropController.setMaxProxyHeight(getMaxProxyHeight());
resizeController.setIntervalsPerHour(
calendarWidget.getSettings().getIntervalsPerHour());
resizeController.setDayStartsAt(getSettings().getDayStartsAt());
resizeController.setSnapSize(
calendarWidget.getSettings().getPixelsPerInterval());
proxyResizeController.setSnapSize(calendarWidget.getSettings().getPixelsPerInterval());
proxyResizeController.setIntervalsPerHour(calendarWidget.getSettings().getIntervalsPerHour());
proxyResizeController.setDayStartsAt(getSettings().getDayStartsAt());
this.selectedAppointmentWidgets.clear();
appointmentWidgets.clear();
// HERE IS WHERE WE DO THE LAYOUT
Date startDate = (Date) calendarWidget.getDate().clone();
Date endDate = (Date) calendarWidget.getDate().clone();
endDate.setDate(endDate.getDate() + 1);
DateUtils.resetTime(startDate);
DateUtils.resetTime(endDate);
startDate.setHours(startDate.getHours());
endDate.setHours(endDate.getHours());
for (int i = 0; i < calendarWidget.getDays(); i++) {
List<Appointment> filteredList = AppointmentUtil
.filterListByDate(calendarWidget.getAppointments(), startDate, endDate);
// perform layout
List<AppointmentAdapter> appointmentAdapters = layoutStrategy
.doLayout(filteredList, i, calendarWidget.getDays());
// add all appointments back to the grid
addAppointmentsToGrid(appointmentAdapters, false);
startDate.setDate(startDate.getDate() + 1);
endDate.setDate(endDate.getDate() + 1);
}
List<Appointment> filteredList =
AppointmentUtil.filterListByDateRange(calendarWidget.getAppointments(),
calendarWidget.getDate(), calendarWidget.getDays());
ArrayList<AppointmentAdapter> adapterList = new ArrayList<AppointmentAdapter>();
int desiredHeight = layoutStrategy.doMultiDayLayout(filteredList,
adapterList, calendarWidget.getDate(), calendarWidget.getDays());
if (getSettings().isMultidayVisible()) {
multiViewBody.grid.setHeight(desiredHeight + "px");
addAppointmentsToGrid(adapterList, true);
}
}
/**
* Adds the Appointments to the view.
* @param appointmentList List of Appointments
* @param addToMultiView <code>true</code> if is adding the appointments to the multiview section, <code>false</code> otherwise
*/
private void addAppointmentsToGrid(final List<AppointmentAdapter> appointmentList, final boolean addToMultiView) {
for (AppointmentAdapter appt : appointmentList) {
AppointmentWidget panel = new AppointmentWidget();
panel.setWidth(appt.getWidth());
panel.setHeight(appt.getHeight());
panel.setTitle(appt.getAppointment().getTitle());
panel.setTop(appt.getTop());
panel.setLeft(appt.getLeft());
panel.setAppointment(appt.getAppointment());
boolean selected = calendarWidget.isTheSelectedAppointment(panel
.getAppointment());
if (selected) {
selectedAppointmentWidgets.add(panel);
}
styleManager.applyStyle(panel, selected);
appointmentWidgets.add(panel);
if (addToMultiView) {
panel.setMultiDay(true);
this.multiViewBody.grid.add(panel);
} else {
panel.setDescription(appt.getAppointment().getDescription());
dayViewBody.getGrid().grid.add(panel);
//make footer 'draggable'
if (calendarWidget.getSettings().isEnableDragDrop() && !appt.getAppointment().isReadOnly()) {
resizeController.makeDraggable(panel.getResizeHandle());
dragController.makeDraggable(panel, panel.getMoveHandle());
}
}
}
}
@Override
public void scrollToHour(final int hour) {
dayViewBody.getScrollPanel().setVerticalScrollPosition((hour - getSettings().getDayStartsAt()) *
getSettings().getIntervalsPerHour() * getSettings().getPixelsPerInterval());
}
public void doSizing() {
if (calendarWidget.getOffsetHeight() > 0) {
if (getSettings().isMultidayVisible()) {
dayViewBody.setHeight(calendarWidget.getOffsetHeight() - 2
- dayViewHeader.getOffsetHeight()
- multiViewBody.getOffsetHeight() + "px");
} else {
dayViewBody.setHeight(calendarWidget.getOffsetHeight() - 2
- dayViewHeader.getOffsetHeight() + "px");
}
}
}
public void onDeleteKeyPressed() {
if (calendarWidget.getSelectedAppointment() != null) {
calendarWidget.fireDeleteEvent(calendarWidget.getSelectedAppointment());
}
}
public void onDoubleClick(Element element, Event event) {
List<AppointmentWidget> list = findAppointmentWidgetsByElement(element);
if (!list.isEmpty()) {
Appointment appt = list.get(0).getAppointment();
//if (appt.equals(calendarWidget.getSelectedAppointment()))
calendarWidget.fireOpenEvent(appt);
// exit out
} else if (getSettings().getTimeBlockClickNumber() == Click.Double
&& element == dayViewBody.getGrid().gridOverlay.getElement()) {
int x = DOM.eventGetClientX(event) + Window.getScrollLeft();
int y = DOM.eventGetClientY(event) + Window.getScrollTop();
timeBlockClick(x, y);
}
}
public void onSingleClick(final Element element, final Event event) {
// Ignore the scroll panel
if (dayViewBody.getScrollPanel().getElement().equals(element)) {
return;
}
Appointment appointment = findAppointmentByElement(element);
if (appointment != null) {
selectAppointment(appointment);
} else if ((getSettings().getTimeBlockClickNumber() == Click.Single
|| getSettings().getTimeBlockClickNumber() == Click.Drag)
&& element == dayViewBody.getGrid().gridOverlay
.getElement()) {
int x = DOM.eventGetClientX(event) + Window.getScrollLeft();
int y = DOM.eventGetClientY(event) + Window.getScrollTop();
timeBlockClick(x, y);
}
}
public void onMouseOver(final Element element, final Event event) {
Appointment appointment = findAppointmentByElement(element);
calendarWidget.fireMouseOverEvent(appointment, element);
}
@Override
public void onAppointmentSelected(Appointment appointment) {
List<AppointmentWidget> clickedAppointmentAdapters =
findAppointmentWidget(appointment);
if (!clickedAppointmentAdapters.isEmpty()) {
for (AppointmentWidget adapter : selectedAppointmentWidgets) {
styleManager.applyStyle(adapter, false);
}
for (AppointmentWidget adapter : clickedAppointmentAdapters) {
styleManager.applyStyle(adapter, true);
}
selectedAppointmentWidgets.clear();
selectedAppointmentWidgets = clickedAppointmentAdapters;
float height = clickedAppointmentAdapters.get(0).getHeight();
// scrollIntoView ONLY if the appointment fits in the viewable area
if (dayViewBody.getScrollPanel().getOffsetHeight() > height) {
DOM.scrollIntoView(clickedAppointmentAdapters.get(0).getElement());
}
}
}
public void onRightArrowKeyPressed() {
calendarWidget.selectNextAppointment();
}
public void onUpArrowKeyPressed() {
calendarWidget.selectPreviousAppointment();
}
public void onDownArrowKeyPressed() {
calendarWidget.selectNextAppointment();
}
public void onLeftArrowKeyPressed() {
calendarWidget.selectPreviousAppointment();
}
@Override
public String getStyleName() {
return "gwt-cal";
}
@Override
public void attach(CalendarWidget widget) {
super.attach(widget);
if (dayViewBody == null) {
dayViewBody = new DayViewBody(this);
dayViewHeader = new DayViewHeader(this);
layoutStrategy = new DayViewLayoutStrategy(this);
if (getSettings().isMultidayVisible()) {
multiViewBody = new DayViewMultiDayBody(this);
}
}
calendarWidget.getRootPanel().add(dayViewHeader);
if (getSettings().isMultidayVisible()) {
calendarWidget.getRootPanel().add(multiViewBody);
}
calendarWidget.getRootPanel().add(dayViewBody);
if (getSettings() != null) {
scrollToHour(getSettings().getScrollToHour());
}
// Creates the different Controllers, if needed
createDragController();
createDropController();
createResizeController();
}
private void createDragController() {
if (dragController == null) {
dragController = new DayViewPickupDragController(dayViewBody.getGrid().grid, false);
dragController.setBehaviorDragProxy(true);
dragController.setBehaviorDragStartSensitivity(1);
dragController.setBehaviorConstrainedToBoundaryPanel(true); //do I need these?
dragController.setConstrainWidgetToBoundaryPanel(true); //do I need these?
dragController.setBehaviorMultipleSelection(false);
dragController.addDragHandler(new DragHandler(){
public void onDragEnd(DragEndEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireUpdateEvent(appt);
}
public void onDragStart(DragStartEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
calendarWidget.setRollbackAppointment(appt.clone());
((DayViewPickupDragController)dragController).setMaxProxyHeight(getMaxProxyHeight());
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {
}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {
}
});
}
}
private void createDropController() {
if (dropController==null) {
dropController = new DayViewDropController(dayViewBody.getGrid().grid);
dragController.registerDropController(dropController);
}
}
private void createResizeController() {
if (resizeController == null) {
resizeController = new DayViewResizeController(dayViewBody.getGrid().grid);
resizeController.addDragHandler(new DragHandler(){
public void onDragEnd(DragEndEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable.getParent()).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireUpdateEvent(appt);
}
public void onDragStart(DragStartEvent event) {
calendarWidget
.setRollbackAppointment(((AppointmentWidget) event
.getContext().draggable.getParent()).getAppointment()
.clone());
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {}
});
}
if(proxyResizeController == null) {
proxyResizeController = new DayViewResizeController(dayViewBody.getGrid().grid);
proxyResizeController.addDragHandler(new DragHandler(){
long startTime = 0L;
int initialX = 0;
int initialY = 0;
Date startDate;
public void onDragEnd(DragEndEvent event) {
long clickTime = System.currentTimeMillis() - startTime;
int y = event.getContext().mouseY;
if (clickTime <= 500 && initialY == y) {
calendarWidget.fireTimeBlockClickEvent(startDate);
} else {
Appointment appt = ((AppointmentWidget) event.getContext().draggable.getParent()).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireCreateEvent(appt);
}
}
public void onDragStart(DragStartEvent event) {
startTime = System.currentTimeMillis();
initialX = event.getContext().mouseX;
initialY = event.getContext().mouseY;
startDate = getCoordinatesDate(initialX, initialY);
calendarWidget.setRollbackAppointment(null);
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {}
});
}
}
@SuppressWarnings("deprecation")
private Date getCoordinatesDate(int x, int y) {
int left = dayViewBody.getGrid().gridOverlay.getAbsoluteLeft();
int top = dayViewBody.getScrollPanel().getAbsoluteTop();
int width = dayViewBody.getGrid().gridOverlay.getOffsetWidth();
int scrollOffset = dayViewBody.getScrollPanel().getVerticalScrollPosition();
// x & y are based on screen position,need to get x/y relative to
// component
int relativeY = y - top + scrollOffset;
int relativeX = x - left;
// find the interval clicked and day clicked
double interval = Math.floor(relativeY
/ (double) getSettings().getPixelsPerInterval());
double day = Math.floor((double) relativeX
/ ((double) width / (double) calendarWidget.getDays()));
// create new appointment date based on click
Date newStartDate = calendarWidget.getDate();
newStartDate.setHours(getSettings().getDayStartsAt());
newStartDate.setMinutes(0);
newStartDate.setSeconds(0);
newStartDate.setMinutes((int) interval
* (60 / getSettings().getIntervalsPerHour()));
newStartDate.setDate(newStartDate.getDate() + (int) day);
return newStartDate;
}
private void timeBlockClick(int x, int y) {
int left = dayViewBody.getGrid().gridOverlay.getAbsoluteLeft();
int top = dayViewBody.getScrollPanel().getAbsoluteTop();
int width = dayViewBody.getGrid().gridOverlay.getOffsetWidth();
int scrollOffset = dayViewBody.getScrollPanel().getVerticalScrollPosition();
// x & y are based on screen position,need to get x/y relative to
// component
int relativeY = y - top + scrollOffset;
int relativeX = x - left;
// find the interval clicked and day clicked
double day = Math.floor((double) relativeX
/ ((double) width / (double) calendarWidget.getDays()));
Date newStartDate = getCoordinatesDate(x, y);
if (getSettings().getTimeBlockClickNumber() != Click.Drag) {
calendarWidget.fireTimeBlockClickEvent(newStartDate);
} else {
int snapSize = calendarWidget.getSettings().getPixelsPerInterval();
// Create the proxy
width = width / calendarWidget.getDays();
left = (int) day * width;
// Adjust the start to the closest interval
top = (int)Math.floor(
(float) relativeY / snapSize) * snapSize;
AppointmentWidget proxy = new AppointmentWidget();
Appointment app = new Appointment();
app.setStart(newStartDate);
app.setEnd(newStartDate);
proxy.setAppointment(app);
proxy.setStart(newStartDate);
proxy.setPixelSize(width, /*height*/ snapSize);
dayViewBody.getGrid().grid.add(proxy, left, top);
styleManager.applyStyle(proxy, false);
proxyResizeController.makeDraggable(proxy.getResizeHandle());
NativeEvent evt = Document.get().createMouseDownEvent(1, 0, 0, x, y, false,
false, false, false, NativeEvent.BUTTON_LEFT);
proxy.getResizeHandle().getElement().dispatchEvent(evt);
}
}
private List<AppointmentWidget> findAppointmentWidgetsByElement(
Element element) {
return findAppointmentWidget(findAppointmentByElement(element));
}
/**
* Returns the {@link Appointment} indirectly associated to the passed
* <code>element</code>. Each Appointment drawn on the CalendarView maps to
* a Widget and therefore an Element. This method attempts to find an
* Appointment based on the provided Element. If no match is found a null
* value is returned.
*
* @param element
* Element to look up.
* @return Appointment matching the element.
*/
private Appointment findAppointmentByElement(Element element) {
Appointment appointmentAtElement = null;
for (AppointmentWidget widget : appointmentWidgets) {
if (DOM.isOrHasChild(widget.getElement(), element)) {
appointmentAtElement = widget.getAppointment();
break;
}
}
return appointmentAtElement;
}
/**
* Finds any related adapters that match the given Appointment.
*
* @param appt
* Appointment to match.
* @return List of related AppointmentWidget objects.
*/
private List<AppointmentWidget> findAppointmentWidget(Appointment appt) {
ArrayList<AppointmentWidget> appointmentAdapters = new ArrayList<AppointmentWidget>();
if (appt != null) {
for (AppointmentWidget widget : appointmentWidgets) {
if (widget.getAppointment().equals(appt)) {
appointmentAdapters.add(widget);
}
}
}
return appointmentAdapters;
}
public HandlerRegistration addWeekSelectionHandler(
WeekSelectionHandler<Date> handler) {
return dayViewHeader.addWeekSelectionHandler(handler);
}
public HandlerRegistration addDaySelectionHandler(
DaySelectionHandler<Date> handler) {
return dayViewHeader.addDaySelectionHandler(handler);
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
public class DayViewBody extends Composite {
private FlexTable layout = new FlexTable();
private ScrollPanel scrollPanel = new ScrollPanel();
private DayViewTimeline timeline = null;
private DayViewGrid grid = null;
private HasSettings settings = null;
public void add(Widget w) {
scrollPanel.add(w);
}
public ScrollPanel getScrollPanel() {
return scrollPanel;
}
public DayViewGrid getGrid() {
return grid;
}
public DayViewTimeline getTimeline() {
return timeline;
}
public DayViewGrid getDayViewGrid() {
return grid;
}
public DayViewTimeline getDayViewTimeline() {
return timeline;
}
public DayViewBody(HasSettings settings) {
initWidget(scrollPanel);
this.settings = settings;
this.timeline = new DayViewTimeline(settings);
this.grid = new DayViewGrid(settings);
scrollPanel.setStylePrimaryName("scroll-area");
DOM.setStyleAttribute(scrollPanel.getElement(), "overflowX",
"hidden");
DOM.setStyleAttribute(scrollPanel.getElement(), "overflowY",
"scroll");
// create the calendar body layout table
// calendarBodyLayoutTable.setStyleName("scroll-area");
layout.setCellPadding(0);
layout.setBorderWidth(0);
layout.setCellSpacing(0);
layout.getColumnFormatter().setWidth(1, "99%");
// set vertical alignment
VerticalAlignmentConstant valign = HasVerticalAlignment.ALIGN_TOP;
layout.getCellFormatter().setVerticalAlignment(0, 0, valign);
layout.getCellFormatter().setVerticalAlignment(0, 1, valign);
grid.setStyleName("gwt-appointment-panel");
//TODO: use CSS to set table layout
layout.getCellFormatter().setWidth(0, 0, "50px");
DOM.setStyleAttribute(layout.getElement(), "tableLayout", "fixed");
layout.setWidget(0, 0, timeline);
layout.setWidget(0, 1, grid);
scrollPanel.add(layout);
}
public void setDays(Date date, int days) {
grid.build(settings.getSettings().getWorkingHourStart(),
settings.getSettings().getWorkingHourEnd(), days);
}
}

View File

@ -0,0 +1,105 @@
package com.bradrydzewski.gwt.calendar.client.dayview;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.FormattingUtil;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
/**
* The DayGrid draws the grid that displays days / time intervals in the
* body of the calendar.
*
* @author Brad Rydzewski
*/
public class DayViewGrid /*Impl*/ extends Composite {
class Div extends ComplexPanel {
public Div() {
setElement(DOM.createDiv());
}
@Override
public boolean remove(Widget w) {
return super.remove(w);
}
@Override
public void add(Widget w) {
super.add(w, getElement());
}
}
protected AbsolutePanel grid = new AbsolutePanel();
protected SimplePanel gridOverlay = new SimplePanel();
private HasSettings settings = null;
private static final int HOURS_PER_DAY = 24;
public DayViewGrid(HasSettings settings) { //was DayViewGridImpl
initWidget(grid);
this.settings = settings;
}
public void build(int workingHourStart, int workingHourStop, int days) {
grid.clear();
int intervalsPerHour = settings.getSettings().getIntervalsPerHour(); //2; //30 minute intervals
float intervalSize = settings.getSettings().getPixelsPerInterval();
this.setHeight((intervalsPerHour * (intervalSize) * 24) + "px");
float dayWidth = 100f / days;
float dayLeft = 0f;
int dayStartsAt = settings.getSettings().getDayStartsAt();
for (int i = 0; i < HOURS_PER_DAY; i++) {
boolean isWorkingHours = ((i + dayStartsAt) >= workingHourStart && (i + dayStartsAt) <= workingHourStop);
//create major interval
SimplePanel sp1 = new SimplePanel();
sp1.setStyleName("major-time-interval");
sp1.setHeight(intervalSize + FormattingUtil.getBorderOffset() + "px");
//if working hours set
if (isWorkingHours) {
sp1.addStyleName("working-hours");
}
//add to body
grid.add(sp1);
for (int x = 0; x < intervalsPerHour - 1; x++) {
SimplePanel sp2 = new SimplePanel();
sp2.setStyleName("minor-time-interval");
sp2.setHeight(intervalSize + FormattingUtil.getBorderOffset() + "px");
if (isWorkingHours) {
sp2.addStyleName("working-hours");
}
grid.add(sp2);
}
}
for (int day = 0; day < days; day++) {
dayLeft = dayWidth * day;
SimplePanel dayPanel = new SimplePanel();
dayPanel.setStyleName("day-separator");
grid.add(dayPanel);
DOM.setStyleAttribute(dayPanel.getElement(), "left", dayLeft + "%");
}
gridOverlay.setHeight("100%");
gridOverlay.setWidth("100%");
DOM.setStyleAttribute(gridOverlay.getElement(), "position", "absolute");
DOM.setStyleAttribute(gridOverlay.getElement(), "left", "0px");
DOM.setStyleAttribute(gridOverlay.getElement(), "top", "0px");
grid.add(gridOverlay);
}
}

View File

@ -0,0 +1,220 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.CalendarFormat;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionEvent;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionHandler;
import com.bradrydzewski.gwt.calendar.client.event.HasDaySelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasWeekSelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionEvent;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionHandler;
import com.bradrydzewski.gwt.calendar.client.util.WindowUtils;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
public class DayViewHeader extends Composite implements HasWeekSelectionHandlers<Date>, HasDaySelectionHandlers<Date> {
private FlexTable header = new FlexTable();
private VerticalPanel timePanel = new VerticalPanel();
private AbsolutePanel dayPanel = new AbsolutePanel();
private AbsolutePanel weekPanel = new AbsolutePanel();
private AbsolutePanel splitter = new AbsolutePanel();
private static final String GWT_CALENDAR_HEADER_STYLE = "gwt-calendar-header";
private static final String DAY_CELL_CONTAINER_STYLE = "day-cell-container";
private static final String WEEK_CELL_CONTAINER_STYLE = "week-cell-container";
private static final String YEAR_CELL_STYLE = "year-cell";
private static final String SPLITTER_STYLE = "splitter";
private final boolean showWeekNumbers;
private final HasSettings settings;
public DayViewHeader(HasSettings settings) {
initWidget(header);
this.settings = settings;
header.setStyleName(GWT_CALENDAR_HEADER_STYLE);
dayPanel.setStyleName(DAY_CELL_CONTAINER_STYLE);
weekPanel.setStyleName(WEEK_CELL_CONTAINER_STYLE);
timePanel.setWidth("100%");
showWeekNumbers = settings.getSettings().isShowingWeekNumbers();
header.insertRow(0);
header.insertRow(0);
header.insertCell(0, 0);
header.insertCell(0, 0);
header.insertCell(0, 0);
header.setWidget(0, 1, timePanel);
header.getCellFormatter().setStyleName(0, 0, YEAR_CELL_STYLE);
header.getCellFormatter().setWidth(0, 2,
WindowUtils.getScrollBarWidth(true) + "px");
header.getFlexCellFormatter().setColSpan(1, 0, 3);
header.setCellPadding(0);
header.setBorderWidth(0);
header.setCellSpacing(0);
if (showWeekNumbers) {
timePanel.add(weekPanel);
}
timePanel.add(dayPanel);
splitter.setStylePrimaryName(SPLITTER_STYLE);
header.setWidget(1, 0, splitter);
}
public void setDays(Date date, int days) {
dayPanel.clear();
weekPanel.clear();
float dayWidth = 100f / days;
float dayLeft;
int week = DateUtils.calendarWeekIso(date);
int previousDayWeek = week;
Date previousDate = date;
float weekWidth = 0f;
float weekLeft = 0f;
for (int i = 0; i < days; i++) {
// set the left position of the day splitter to
// the width * incremented value
dayLeft = dayWidth * i;
// increment the date by 1
if (i > 0) {
DateUtils.moveOneDayForward(date);
} else {
// initialize the week values
weekLeft = dayLeft;
weekWidth = dayWidth;
}
String headerTitle = CalendarFormat.INSTANCE.getDateFormat().format(date);
Label dayLabel = new Label();
dayLabel.setStylePrimaryName("day-cell");
dayLabel.setWidth(dayWidth + "%");
dayLabel.setText(headerTitle);
DOM.setStyleAttribute(dayLabel.getElement(), "left", dayLeft + "%");
addDayClickHandler(dayLabel, (Date) date.clone());
boolean found = false;
for (Date day : settings.getSettings().getHolidays()) {
if (DateUtils.areOnTheSameDay(day, date)) {
dayLabel.setStyleName("day-cell-holiday");
found = true;
break;
}
}
// set the style of the header to show that it is today
if (DateUtils.areOnTheSameDay(new Date(), date)) {
dayLabel.setStyleName("day-cell-today");
} else if (!found && DateUtils.isWeekend(date)) {
dayLabel.setStyleName("day-cell-weekend");
}
if (showWeekNumbers) {
week = DateUtils.calendarWeekIso(date);
boolean lastDay = i + 1 == days;
if ((previousDayWeek != week) || lastDay) {
if (lastDay) {
previousDayWeek = week;
previousDate = date;
}
String weekTitle = "W " + previousDayWeek;
Label weekLabel = new Label();
weekLabel.setStylePrimaryName("week-cell");
weekLabel.setWidth(weekWidth + "%");
weekLabel.setText(weekTitle);
DOM.setStyleAttribute(weekLabel.getElement(), "left", weekLeft + "%");
addWeekClickHandler(weekLabel, previousDate);
weekPanel.add(weekLabel);
weekWidth = dayWidth;
weekLeft = dayLeft + dayWidth;
} else {
weekWidth += dayWidth;
}
previousDayWeek = week;
previousDate = date;
}
dayPanel.add(dayLabel);
}
}
public void setYear(Date date) {
setYear(DateUtils.year(date));
}
public void setYear(int year) {
header.setText(0, 0, String.valueOf(year));
}
private void addDayClickHandler(final Label dayLabel, final Date day) {
dayLabel.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
fireSelectedDay(day);
}
});
}
private void addWeekClickHandler(final Label weekLabel, final Date day) {
weekLabel.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
fireSelectedWeek(day);
}
});
}
private void fireSelectedDay(final Date day) {
DaySelectionEvent.fire(this, day);
}
private void fireSelectedWeek(final Date day) {
WeekSelectionEvent.fire(this, day);
}
public HandlerRegistration addWeekSelectionHandler(
WeekSelectionHandler<Date> handler) {
return addHandler(handler, WeekSelectionEvent.getType());
}
public HandlerRegistration addDaySelectionHandler(
DaySelectionHandler<Date> handler) {
return addHandler(handler, DaySelectionEvent.getType());
}
}

View File

@ -0,0 +1,439 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.AppointmentUtil;
/**
* Responsible for arranging all Appointments, visually, on a screen in a manner
* similar to the Microsoft Outlook / Windows Vista calendar.
* See: <img src='http://www.microsoft.com/library/media/1033/athome/images/moredone/calendar.gif'/>
* <p>
* Note how overlapping appointments are displayed in the provided image
*
* @author Brad Rydzewski
* @version 1.0 6/07/09
* @since 1.0
*/
public class DayViewLayoutStrategy {
private static final int MINUTES_PER_HOUR = 60;
private static final int HOURS_PER_DAY = 24;
private final HasSettings settings;
public DayViewLayoutStrategy(final HasSettings settings) {
this.settings = settings;
}
public List<AppointmentAdapter> doLayout(List<Appointment> appointments, int dayIndex, int dayCount) {
int intervalsPerHour = settings.getSettings().getIntervalsPerHour(); //30 minute intervals
float intervalSize = settings.getSettings().getPixelsPerInterval(); //25 pixels per interval
/*
* Note: it is important that all appointments are sorted by Start date
* (asc) and Duration (desc) for this algorithm to work. If that is not
* the case, it won't work, at all!! Maybe this is a problem that needs
* to be addressed
*/
// set to 30 minutes. this means there will be 48 cells. 60min / 30min
// interval * 24
//int minutesPerInterval = 30;
// interval size, set to 100px
//float sizeOfInterval = 25f;
// a calendar can view multiple days at a time. sets number of visible
// days
// TODO: use this later, not currently implemented
// float numberOfDays = dates.size();
int minutesPerInterval = MINUTES_PER_HOUR / intervalsPerHour;
// get number of cells (time blocks)
int numberOfTimeBlocks = MINUTES_PER_HOUR / minutesPerInterval * HOURS_PER_DAY;
TimeBlock[] timeBlocks = new TimeBlock[numberOfTimeBlocks];
for (int i = 0; i < numberOfTimeBlocks; i++) {
TimeBlock t = new TimeBlock();
t.setStart(i * minutesPerInterval);
t.setEnd(t.getStart() + minutesPerInterval);
t.setOrder(i);
t.setTop((float) i * intervalSize);
t.setBottom(t.getTop() + intervalSize);
timeBlocks[i] = t;
}
// each appointment will get "wrapped" in an appoinetment cell object,
// so that we can assign it a location in the grid, row and
// column span, etc.
ArrayList<AppointmentAdapter> appointmentCells = new ArrayList<AppointmentAdapter>();
// Map<TimeBlock,TimeBlock> blockGroup = new
// HashMap<TimeBlock,TimeBlock>();
int groupMaxColumn = 0; // track total columns here! this will reset
// when a group completes
int groupStartIndex = -1;
int groupEndIndex = -2;
// Question: how to distinguish start / finish of a new group?
// Answer: when endCell of previous appointments < startCell of new
// appointment
// for each appointments, we need to see if it intersects with each time
// block
for (Appointment appointment : appointments) {
TimeBlock startBlock = null;
TimeBlock endBlock = null;
// if(blockGroupEndCell)
// wrap appointment with AppointmentInterface Cell and add to list
AppointmentAdapter apptCell = new AppointmentAdapter(appointment);
appointmentCells.add(apptCell);
// get the first time block in which the appointment should appear
// TODO: since appointments are sorted, we should never need to
// re-evaluate a time block that had zero matches...
// store the index of the currently evaluated time block, if no
// match, increment
// that will prevent the same block from ever being re-evaluated
// after no match found
for (TimeBlock block : timeBlocks) {
// does the appointment intersect w/ the block???
if (block.intersectsWith(apptCell)) {
// we found one! set as start block and exit loop
startBlock = block;
// blockGroup.put(block, block);
if (groupEndIndex < startBlock.getOrder()) {
//System.out.println(" prior group max cols: "
// + groupMaxColumn);
for (int i = groupStartIndex; i <= groupEndIndex; i++) {
TimeBlock tb = timeBlocks[i];
tb.setTotalColumns(groupMaxColumn + 1);
//System.out.println(" total col set for block: "
// + i);
}
groupStartIndex = startBlock.getOrder();
//System.out.println("new group at: " + groupStartIndex);
groupMaxColumn = 0;
}
break;
} else {
// here is where I would increment, as per above to-do
}
}
// add the appointment to the start block
startBlock.getAppointments().add(apptCell);
// add block to appointment
apptCell.getIntersectingBlocks().add(startBlock);
// set the appointments column, if it has not already been set
// if it has been set, we need to get it for reference later on in
// this method
int column = startBlock.getFirstAvailableColumn();
apptCell.setColumnStart(column);
apptCell.setColumnSpan(1); // hard-code to 1, for now
// we track the max column for a time block
// if a column get's added make sure we increment
// if (startBlock.getTotalColumns() <= column) {
// startBlock.setTotalColumns(column+1);
// }
// add column to block's list of occupied columns, so that the
// column cannot be given to another appointment
startBlock.getOccupiedColumns().put(column, column);
// sets the start cell of the appt to the current block
// we can do this since the blocks are ordered ascending
apptCell.setCellStart(startBlock.getOrder());
// go through all subsequent blocks...
// find intersections
for (int i = startBlock.getOrder() + 1; i < timeBlocks.length; i++) {
// get the nextTimeBlock
TimeBlock nextBlock = timeBlocks[i];
// exit look if end date > block start, since no subsequent
// blocks will ever intersect
// if (apptCell.getAppointmentEnd() > nextBlock.getStart()) {
// break; //does appt intersect with this block?
// }
if (nextBlock.intersectsWith(apptCell)) {
// yes! add appointment to the block
// register start column
nextBlock.getAppointments().add(apptCell);
nextBlock.getOccupiedColumns().put(column, column);
endBlock = nextBlock; // this may change if intersects with
// next block
// add block to appointments list of intersecting blocks
apptCell.getIntersectingBlocks().add(nextBlock);
// we track the max column for a time block
// if a column get's added make sure we increment
// if (nextBlock.getTotalColumns() <= column) {
// nextBlock.setTotalColumns(column+1);
// }
// blockGroup.put(nextBlock, nextBlock);
}
}
// if end block was never set, use the start block
endBlock = (endBlock == null) ? startBlock : endBlock;
// maybe here is the "end" of a group, where we then evaluate max
// column
if (column > groupMaxColumn) {
groupMaxColumn = column;
// System.out.println(" max col: " + groupMaxColumn);
}
if (groupEndIndex < endBlock.getOrder()) {
groupEndIndex = endBlock.getOrder();
//System.out.println(" end index (re)set: " + groupEndIndex);
}
// for(int i = groupStartIndex; i<=groupEndIndex; i++) {
// timeBlocks[i].setTotalColumns(groupMaxColumn);
// }
// groupMaxColumn=1;
// }
// for(TimeBlock timeBlock : blockGroup.values()) {
//
// }
// blockGroup = new HashMap<TimeBlock,TimeBlock>();
// set the appointments cell span (top to bottom)
apptCell.setCellSpan(endBlock.getOrder() - startBlock.getOrder() + 1);
}
for (int i = groupStartIndex; i <= groupEndIndex; i++) {
TimeBlock tb = timeBlocks[i];
tb.setTotalColumns(groupMaxColumn + 1);
//System.out.println(" total col set for block: " + i);
}
// we need to know the MAX number of cells for each time block.
// so unfortunately we have to go back through the list to find this out
/*
* for(AppointmentCell apptCell : appointmentCells) {
*
* for (TimeBlock block : apptCell.getIntersectingBlocks()) {
*
* int maxCol = 0;
*
* //find the max cell for (AppointmentCell apptCell :
* block.getAppointments()) { int col = apptCell.getColumnStart(); if
* (col > maxCol) { maxCol = col; } }
*
* block.setTotalColumns(maxCol+1); } }
*/
//last step is to calculate the adjustment reuired for 'multi-day' / multi-column
float widthAdj = 1f / dayCount;
float paddingLeft = .5f;
float paddingRight = .5f;
float paddingBottom = 2;
// now that everything has been assigned a cell, column and spans
// we can calculate layout
// Note: this can only be done after every single appointment has
// been assigned a position in the grid
for (AppointmentAdapter apptCell : appointmentCells) {
float width = 1f / (float) apptCell.getIntersectingBlocks().get(0).getTotalColumns() * 100;
float left = (float) apptCell.getColumnStart() / (float) apptCell.getIntersectingBlocks().get(0).getTotalColumns() * 100;
//AppointmentInterface appt = apptCell.getAppointment();
apptCell.setTop((float) apptCell.getCellStart() * intervalSize); // ok!
apptCell.setLeft((widthAdj * 100 * dayIndex) + (left * widthAdj) + paddingLeft); // ok
apptCell.setWidth(width * widthAdj - paddingLeft - paddingRight); // ok!
apptCell.setHeight((float) apptCell.getIntersectingBlocks().size() * ((float) intervalSize) - paddingBottom); // ok!
float apptStart = apptCell.getAppointmentStart();
float apptEnd = apptCell.getAppointmentEnd();
float blockStart = timeBlocks[apptCell.getCellStart()].getStart();
float blockEnd = timeBlocks[apptCell.getCellStart() + apptCell.getCellSpan() - 1].getEnd();
float blockDuration = blockEnd - blockStart;
float apptDuration = apptEnd - apptStart;
float timeFillHeight = apptDuration / blockDuration * 100f;
float timeFillStart = (apptStart - blockStart) / blockDuration * 100f;
// System.out.println("apptStart: "+apptStart);
// System.out.println("apptEnd: "+apptEnd);
// System.out.println("blockStart: "+blockStart);
// System.out.println("blockEnd: "+blockEnd);
// System.out.println("timeFillHeight: "+timeFillHeight);
//System.out.println("timeFillStart: "+timeFillStart);
//System.out.println("------------");
apptCell.setCellPercentFill(timeFillHeight);
apptCell.setCellPercentStart(timeFillStart);
//appt.formatTimeline(apptCell.getCellPercentStart(), apptCell.getCellPercentFill());
}
return appointmentCells;
}
public int doMultiDayLayout(List<Appointment> appointments, List<AppointmentAdapter> adapters, Date start, int days) {
//create array to hold all appointments for a particular day
//HashMap<Date, ArrayList<AppointmentAdapter>> appointmentDayMap
// = new HashMap<Date, ArrayList<AppointmentAdapter>>();
//for a particular day need to track all used rows
HashMap<Integer, HashMap<Integer, Integer>> daySlotMap = new HashMap<Integer, HashMap<Integer, Integer>>();
int minHeight = 30;
int maxRow = 0;
//convert appointment to adapter
for (Appointment appointment : appointments) {
adapters.add(new AppointmentAdapter(appointment));
}
//create array of dates
ArrayList<Date> dateList = new ArrayList<Date>();
Date tempStartDate = (Date)start.clone();
// tempStartDate.setHours(0);
// tempStartDate.setMinutes(0);
// tempStartDate.setSeconds(0);
for (int i = 0; i < days; i++) {
Date d = (Date) tempStartDate.clone();
DateUtils.resetTime(d);
//appointmentDayMap.put(d, new ArrayList<AppointmentAdapter>());
daySlotMap.put(i, new HashMap<Integer, Integer>());
dateList.add(d);
DateUtils.moveOneDayForward(tempStartDate);
}
//add appointments to each day
for (AppointmentAdapter adapter : adapters) {
int columnSpan = 0; //number of columns spanned
boolean isStart = true; //indicates if current column is appointment start column
//set column & span
for (int i = 0; i < dateList.size(); i++) {
Date date = dateList.get(i);
boolean isWithinRange =
AppointmentUtil.rangeContains(
adapter.getAppointment(), date);
//System.out.println(" isWithinRange == " + isWithinRange + " for start: " + adapter.getAppointment().getStart() + " end: " + adapter.getAppointment().getEnd() + " date: "+date);
//while we are at it, we can set the adapters start column
// and colun span
if (isWithinRange) {
//appointmentDayMap.get(date).add(adapter);
if (isStart) {
adapter.setColumnStart(i);
isStart = false;
}
adapter.setColumnSpan(columnSpan);
columnSpan++;
}
}
//now we set the row, which cannot be more than total # of appointments
for (int x = 0; x < adapters.size(); x++) {
boolean isRowOccupied = false;
for (int y = adapter.getColumnStart(); y <= adapter.getColumnStart() + adapter.getColumnSpan(); y++) {
try{
HashMap<Integer, Integer> rowMap = daySlotMap.get(y);
if (rowMap.containsKey(x)) {
isRowOccupied = true;
//System.out.println(" row [" + x+"] is occupied for day [" + y+"]");
} else {
//isRowOccupied = false;
break; //break out of loop, nothing found in row slot
}
}catch(Exception ex) {
//System.out.println("Exception: y=" + y + " x=" + x + " adapters.size=" + adapters.size() + " start="+adapter.getAppointment().getStart() + " end="+adapter.getAppointment().getEnd().toString());
}
}
if (!isRowOccupied) {
//add row to maps
for (int y = adapter.getColumnStart(); y <= adapter.getColumnStart() + adapter.getColumnSpan(); y++) {
HashMap<Integer, Integer> rowMap = daySlotMap.get(y);
rowMap.put(x, x);
if (x > maxRow) {
maxRow = x;
}
//System.out.println(" > added "+ x + " to row list for column " + y);
}
//set the row (also named cell)
adapter.setCellStart(x);
//break loop
//now we set the appointment's location
//Appointment appt = adapter.getAppointment();
float top = adapter.getCellStart() * 25f + 5f;
float width = ((float) adapter.getColumnSpan() + 1f) / days * 100f - 1f; //10f=padding
float left = ((float) adapter.getColumnStart()) / days * 100f + .5f; //10f=padding
//float left = (float) adapter.getColumnStart() / (float) apptCell.getIntersectingBlocks().get(0).getTotalColumns() * 100;
adapter.setWidth(width);
adapter.setLeft(left);
adapter.setTop(top);
adapter.setHeight(20);
//System.out.println("set appointment [" + appt.getTitle() + "] layout left: " + left + " top: " + top + " width: " + width);
break;
}
}
//System.out.println("multi-day layout -- title: \"" + adapter.getAppointment().getTitle() + "\" | col start: " + adapter.getColumnStart() + " | colspan: " + adapter.getColumnSpan() + " | row: " + adapter.getCellStart() + " | start date: " + adapter.getAppointment().getStart() + " | end date: " + adapter.getAppointment().getEnd());
}
int height = (maxRow + 1) * 25 + 5;
return Math.max(height, minHeight);
}
}

View File

@ -0,0 +1,108 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.WindowUtils;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.SimplePanel;
/**
* A widget to render multi-day appointments in a day view.
*
* @author Brad Rydzewski
*/
public class DayViewMultiDayBody extends Composite {
private FlexTable header = new FlexTable();
protected AbsolutePanel grid = new AbsolutePanel();
private AbsolutePanel splitter = new AbsolutePanel();
private static final String TIMELINE_EMPTY_CELL_STYLE = "leftEmptyCell";
private static final String SCROLLBAR_EMPTY_CELL_STYLE = "rightEmptyCell";
private static final String DAY_CONTAINER_CELL_STYLE = "centerDayContainerCell";
private static final String SPLITTER_STYLE = "splitter";
protected SimplePanel gridOverlay = new SimplePanel();
public DayViewMultiDayBody(HasSettings settings) {
initWidget(header);
this.header.setStyleName("multiDayBody");
this.setWidth("100%");
/* insert two rows ... first row holds multi-day appointments,
* second row is just a splitter */
header.insertRow(0);
header.insertRow(0);
//insert 3 cells
//1st cell is empty to align with the timeline
//2nd cell holds appointments
//3rd cell is empty, aligns with scrollbar
header.insertCell(0, 0);
header.insertCell(0, 0);
header.insertCell(0, 0);
//add panel to hold appointments
header.setWidget(0, 1, grid);
//set cell styles
header.getCellFormatter().setStyleName(0, 0, TIMELINE_EMPTY_CELL_STYLE);
header.getCellFormatter().setStyleName(0, 1, DAY_CONTAINER_CELL_STYLE);
header.getCellFormatter().setStyleName(0, 2, SCROLLBAR_EMPTY_CELL_STYLE);
header.getCellFormatter().setWidth(0, 2,
WindowUtils.getScrollBarWidth(true) + "px");
//default grid to 50px height
grid.setHeight("30px");
header.getFlexCellFormatter().setColSpan(1, 0, 3);
header.setCellPadding(0);
header.setBorderWidth(0);
header.setCellSpacing(0);
splitter.setStylePrimaryName(SPLITTER_STYLE);
header.setWidget(1, 0, splitter);
}
public void setDays(Date date, int days) {
grid.clear();
float dayWidth = 100f / days;
float dayLeft;
for (int day = 0; day < days; day++) {
dayLeft = dayWidth * day;
SimplePanel dayPanel = new SimplePanel();
dayPanel.setStyleName("day-separator");
grid.add(dayPanel);
DOM.setStyleAttribute(dayPanel.getElement(), "left", dayLeft + "%");
}
gridOverlay.setHeight("100%");
gridOverlay.setWidth("100%");
DOM.setStyleAttribute(gridOverlay.getElement(), "position", "absolute");
DOM.setStyleAttribute(gridOverlay.getElement(), "left", "0px");
DOM.setStyleAttribute(gridOverlay.getElement(), "top", "0px");
grid.add(gridOverlay);
}
}

View File

@ -0,0 +1,80 @@
package com.bradrydzewski.gwt.calendar.client.dayview;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.ThemeAppointmentStyle;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
public abstract class DayViewStyleManager {
protected static final String APPOINTMENT_STYLE = "dv-appointment";
protected static final String APPOINTMENT_STYLE_SELECTED = "-selected";
protected static final String APPOINTMENT_STYLE_MULTIDAY = "-multiday";
protected static final String BACKGROUND_COLOR_STYLE_ATTRIBUTE = "backgroundColor";
protected static final String BACKGROUND_IMAGE_STYLE_ATTRIBUTE = "backgroundImage";
protected static final String BORDER_COLOR_STYLE_ATTRIBUTE = "borderColor";
protected static final String COLOR_STYLE_ATTRIBUTE = "color";
public void applyStyle(AppointmentWidget widget, boolean selected) {
doApplyStyleInternal(widget, selected);
}
protected abstract ThemeAppointmentStyle getViewAppointmentStyleForTheme(Appointment appointment);
protected abstract ThemeAppointmentStyle getDefaultViewAppointmentStyleForTheme();
private void doApplyStyleInternal(AppointmentWidget widget, boolean selected) {
// Extract the Appointment for later reference
Appointment appointment = widget.getAppointment();
// Extract the DOM Element for later reference
Element elem = widget.getElement();
Element bodyElem = widget.getBody().getElement();
Element headerElem = widget.getHeader().getElement();
// Is MultiDay?
boolean multiDay = appointment.isMultiDay() || appointment.isAllDay();
//Lookup the style from the map
ThemeAppointmentStyle style = getViewAppointmentStyleForTheme(appointment);
//Determine Style Name
String styleName = APPOINTMENT_STYLE;
if(multiDay) styleName+=APPOINTMENT_STYLE_MULTIDAY;
if(selected) styleName+=APPOINTMENT_STYLE_SELECTED;
widget.setStylePrimaryName(styleName);
//If no style is found, apply the default blue style
//TODO: need to check for a custom style
if(style==null)
style = getDefaultViewAppointmentStyleForTheme();
if (multiDay)
DOM.setStyleAttribute(elem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackgroundHeader());
else
DOM.setStyleAttribute(elem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackground());
DOM.setStyleAttribute(elem, BORDER_COLOR_STYLE_ATTRIBUTE, style.getBackgroundHeader());
DOM.setStyleAttribute(bodyElem, COLOR_STYLE_ATTRIBUTE, style.getSelectedBorder());
DOM.setStyleAttribute(headerElem, COLOR_STYLE_ATTRIBUTE, style.getHeaderText());
DOM.setStyleAttribute(headerElem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackgroundHeader());
if (multiDay)
return;
if (selected && style.getSelectedBackgroundImage() != null) {
DOM.setStyleAttribute(elem, BACKGROUND_IMAGE_STYLE_ATTRIBUTE, "url("+ style.getSelectedBackgroundImage() + ")");
} else {
DOM.setStyleAttribute(elem, BACKGROUND_IMAGE_STYLE_ATTRIBUTE, "none");
}
}
}

View File

@ -0,0 +1,108 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import com.bradrydzewski.gwt.calendar.client.CalendarFormat;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.FormattingUtil;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
/**
* A sequential display of the hours in a day. Each
* hour label should visually line up to a cell in the DayGrid.
*
* @author Brad Rydzewski
*/
public class DayViewTimeline extends Composite {
private AbsolutePanel timelinePanel = new AbsolutePanel();
private HasSettings settings = null;
private static final String TIME_LABEL_STYLE = "hour-label";
public DayViewTimeline(HasSettings settings) {
initWidget(timelinePanel);
timelinePanel.setStylePrimaryName("time-strip");
this.settings = settings;
prepare();
}
public final void prepare() {
timelinePanel.clear();
float labelHeight = settings.getSettings().getIntervalsPerHour()
* settings.getSettings().getPixelsPerInterval();
int i = 0;
if (settings.getSettings().isOffsetHourLabels()) {
i = 1;
SimplePanel sp = new SimplePanel();
sp.setHeight((labelHeight / 2) + "px");
timelinePanel.add(sp);
}
int dayStartsAt = settings.getSettings().getDayStartsAt();
while (i < CalendarFormat.HOURS_IN_DAY) {
int index = i + dayStartsAt;
if (index >= CalendarFormat.HOURS_IN_DAY) {
index = index - CalendarFormat.HOURS_IN_DAY;
}
String hour = CalendarFormat.INSTANCE.getHourLabels()[index];
index++;
i++;
//block
SimplePanel hourWrapper = new SimplePanel();
hourWrapper.setStylePrimaryName(TIME_LABEL_STYLE);
hourWrapper.setHeight((labelHeight + FormattingUtil.getBorderOffset()) + "px");
FlowPanel flowPanel = new FlowPanel();
flowPanel.setStyleName("hour-layout");
String amPm = " ";
if (index < 13)
amPm += CalendarFormat.INSTANCE.getAm();
else if (index > 13)
amPm += CalendarFormat.INSTANCE.getPm();
else {
if (CalendarFormat.INSTANCE.isUseNoonLabel()) {
hour = CalendarFormat.INSTANCE.getNoon();
amPm = "";
} else {
amPm += CalendarFormat.INSTANCE.getPm();
}
}
Label hourLabel = new Label(hour);
hourLabel.setStylePrimaryName("hour-text");
flowPanel.add(hourLabel);
Label amPmLabel = new Label(amPm);
amPmLabel.setStylePrimaryName("ampm-text");
flowPanel.add(amPmLabel);
hourWrapper.add(flowPanel);
timelinePanel.add(hourWrapper);
}
}
}

View File

@ -0,0 +1,148 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.dayview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a block of time that contains one or many Appointments. An example
* of a timeblock could be 10am - 10:30 am, where a block of time is 30 minutes.
* <p> A block of time can contain or be interested by one or many appointments.
* Using the above example, a time block from 10am - 10:30 am could contain the
* following appointments:
* <ul>
* <li>10:00 - 10:30, where start / end are same as time block</li>
* <li>9:00 - 10:30, where the appointment starts before the time block
* starts but ends at same time as time block</li>
* <li>9:00 - 10:20, where appointment start before
* and ends after start of time block</li>
* <li>9:00 - 11:00, where appointment starts before and ends after the time
* block does</li>
* <li>10:05 - 10:25, where appointment starts after but ends before the
* time block</li> </ul></p>
* Above
* are example use cases of a time block and how it relates to an appointment.
* Note that an appointment can intersect with one ore many time blocks. This
* class holds a number of parameters allowing a time block to manage its
* appointments and determine where the appointments should be arranged within
* itself.
*
* @author Brad Rydzewski
*/
public class TimeBlock {
private List<AppointmentAdapter> appointments = new ArrayList<AppointmentAdapter>();
private Map<Integer, Integer> occupiedColumns = new HashMap<Integer, Integer>();
private int totalColumns = 1;
private int order;
private String name;
private int start;
private int end;
private float top;
private float bottom;
public List<AppointmentAdapter> getAppointments() {
return appointments;
}
public Map<Integer, Integer> getOccupiedColumns() {
return occupiedColumns;
}
public int getFirstAvailableColumn() {
int col = 0;
while (true) {
if (occupiedColumns.containsKey(col))
col++;
else return col;
}
}
public int getTotalColumns() {
return totalColumns;
}
public void setTotalColumns(int totalColumns) {
this.totalColumns = totalColumns;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public float getBottom() {
return bottom;
}
public void setBottom(float bottom) {
this.bottom = bottom;
}
public float getTop() {
return top;
}
public void setTop(float top) {
this.top = top;
}
public boolean intersectsWith(int apptStart, int apptEnd) {
//scenario 1: start date of appt between start and end of block
if (apptStart >= this.getStart() && apptStart < this.getEnd())
return true;
//scenario 2: end date of appt > block start date &
// start date of appt before block start date
return apptEnd > this.getStart() && apptStart < this.getStart();
}
public boolean intersectsWith(AppointmentAdapter appt) {
return intersectsWith(appt.getAppointmentStart(),
appt.getAppointmentEnd());
}
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* TODO: Complete JavaDoc comments.
* @author Brad Rydzewski
*/
public class CreateEvent<T> extends GwtEvent<CreateHandler<T>> {
/**
* Handler type.
*/
private static Type<CreateHandler<?>> TYPE;
private boolean cancelled = false;
public boolean isCancelled() {
return cancelled;
}
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Fires a open event on all registered handlers in the handler manager.If no
* such handlers exist, this method will do nothing.
*
* @param <T> the target type
* @param source the source of the handlers
* @param target the target
*/
public static <T> boolean fire(HasUpdateHandlers<T> source, T target) {
if (TYPE != null) {
CreateEvent<T> event = new CreateEvent<T>(target);
source.fireEvent(event);
return !event.isCancelled();
}
return true;
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<CreateHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<CreateHandler<?>>();
}
return TYPE;
}
private final T target;
/**
* Creates a new delete event.
*
* @param target the ui object being opened
*/
protected CreateEvent(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
@Override
public final Type<CreateHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the target.
*
* @return the target
*/
public T getTarget() {
return target;
}
// Because of type erasure, our static type is
// wild carded, yet the "real" type should use our I param.
@Override
protected void dispatch(CreateHandler<T> handler) {
handler.onCreate(this);
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface CreateHandler<T> extends EventHandler {
/**
* Called when {@link DeleteEvent} is fired.
*
* @param event the {@link DeleteEvent} that was fired
*/
void onCreate(CreateEvent<T> event);
}

View File

@ -0,0 +1,104 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* TODO: Complete JavaDoc comments.
* @author Brad Rydzewski
*/
public class DateRequestEvent<T> extends GwtEvent<DateRequestHandler<T>> {
/**
* Handler type.
*/
private static Type<DateRequestHandler<?>> TYPE;
/**
* Fires a open event on all registered handlers in the handler manager.If no
* such handlers exist, this method will do nothing.
*
* @param <T> the target type
* @param source the source of the handlers
* @param target the target
*/
public static <T> void fire(HasDateRequestHandlers<T> source, T target) {
fire(source, target, null);
}
public static <T> void fire(HasDateRequestHandlers<T> source, T target, Object widget) {
if (TYPE != null) {
DateRequestEvent<T> event = new DateRequestEvent<T>(target, widget);
source.fireEvent(event);
}
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<DateRequestHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<DateRequestHandler<?>>();
}
return TYPE;
}
private final T target;
private final Object clicked;
/**
* Creates a new delete event.
*
* @param target the ui object being opened
*/
protected DateRequestEvent(T target, Object clicked) {
this.target = target;
this.clicked = clicked;
}
@SuppressWarnings("unchecked")
@Override
public final Type<DateRequestHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the target.
*
* @return the target
*/
public T getTarget() {
return target;
}
public Object getClicked() {
return clicked;
}
// Because of type erasure, our static type is
// wild carded, yet the "real" type should use our I param.
@Override
protected void dispatch(DateRequestHandler<T> handler) {
handler.onDateRequested(this);
}
}

View File

@ -0,0 +1,30 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
public interface DateRequestHandler<T> extends EventHandler {
/**
* Called when {@link DeleteEvent} is fired.
*
* @param event the {@link DeleteEvent} that was fired
*/
void onDateRequested(DateRequestEvent<T> event);
}

View File

@ -0,0 +1,99 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* Represents a selection event.
*
* @param <T>
* the type being selected
*/
public class DaySelectionEvent<T> extends GwtEvent<DaySelectionHandler<T>> {
/**
* Handler type.
*/
private static Type<DaySelectionHandler<?>> TYPE;
/**
* Fires a selection event on all registered handlers in the handler
* manager.If no such handlers exist, this method will do nothing.
*
* @param <T>
* the selected item type
* @param source
* the source of the handlers
* @param selectedItem
* the selected item
*/
public static <T> void fire(HasDaySelectionHandlers<T> source, T selectedItem) {
if (TYPE != null) {
DaySelectionEvent<T> event = new DaySelectionEvent<T>(
selectedItem);
source.fireEvent(event);
}
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<DaySelectionHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<DaySelectionHandler<?>>();
}
return TYPE;
}
private final T selectedItem;
/**
* Creates a new selection event.
*
* @param selectedItem
* selected item
*/
protected DaySelectionEvent(T selectedItem) {
this.selectedItem = selectedItem;
}
// The instance knows its BeforeSelectionHandler is of type I, but the TYPE
// field itself does not, so we have to do an unsafe cast here.
@SuppressWarnings("unchecked")
@Override
public final Type<DaySelectionHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the selected item.
*
* @return the selected item
*/
public T getSelectedItem() {
return selectedItem;
}
@Override
protected void dispatch(DaySelectionHandler<T> handler) {
handler.onSelection(this);
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link DaySelectionEvent} events.
*
* @param <T> the type being opened
*/
public interface DaySelectionHandler<T> extends EventHandler {
/**
* Called when {@link DaySelectionEvent} is fired.
*
* @param event the {@link DaySelectionEvent} that was fired
*/
void onSelection(DaySelectionEvent<T> event);
}

View File

@ -0,0 +1,106 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* TODO: Complete Javadoc comments.
* @author Brad Rydzewski
*/
public class DeleteEvent<T> extends GwtEvent<DeleteHandler<T>> {
/**
* Handler type.
*/
private static Type<DeleteHandler<?>> TYPE;
private boolean cancelled = false;
public boolean isCancelled() {
return cancelled;
}
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Fires a open event on all registered handlers in the handler manager.If no
* such handlers exist, this method will do nothing.
*
* @param <T> the target type
* @param source the source of the handlers
* @param target the target
*/
public static <T> boolean fire(HasDeleteHandlers<T> source, T target) {
if (TYPE != null) {
DeleteEvent<T> event = new DeleteEvent<T>(target);
source.fireEvent(event);
return !event.isCancelled();
}
return true;
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<DeleteHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<DeleteHandler<?>>();
}
return TYPE;
}
private final T target;
/**
* Creates a new delete event.
*
* @param target the ui object being opened
*/
protected DeleteEvent(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
@Override
public final Type<DeleteHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the target.
*
* @return the target
*/
public T getTarget() {
return target;
}
// Because of type erasure, our static type is
// wild carded, yet the "real" type should use our I param.
@Override
protected void dispatch(DeleteHandler<T> handler) {
handler.onDelete(this);
}
}

View File

@ -0,0 +1,19 @@
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface DeleteHandler<T> extends EventHandler {
/**
* Called when {@link DeleteEvent} is fired.
*
* @param event the {@link DeleteEvent} that was fired
*/
void onDelete(DeleteEvent<T> event);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link DateRequestEvent} events.
*
* @param <T> the type being opened
*/
public interface HasDateRequestHandlers<T> extends HasHandlers {
/**
* Adds a {@link DateRequestEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addDateRequestHandler(DateRequestHandler<T> handler);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link WeekSelectionEvent} events.
*
* @param <T> the type being opened
*/
public interface HasDaySelectionHandlers<T> extends HasHandlers {
/**
* Adds a {@link WeekSelectionEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addDaySelectionHandler(DaySelectionHandler<T> handler);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface HasDeleteHandlers<T> extends HasHandlers {
/**
* Adds a {@link DeleteEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addDeleteHandler(DeleteHandler<T> handler);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link MouseOverEvent} events.
*
* @param <T> the type being opened
*/
public interface HasMouseOverHandlers<T> extends HasHandlers {
/**
* Adds a {@link DeleteEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addMouseOverHandler(MouseOverHandler<T> handler);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface HasTimeBlockClickHandlers<T> extends HasHandlers {
/**
* Adds a {@link DeleteEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addTimeBlockClickHandler(TimeBlockClickHandler<T> handler);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link UpdateEvent} events.
*
* @param <T> the type being opened
*/
public interface HasUpdateHandlers<T> extends HasHandlers {
/**
* Adds a {@link UpdateEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addUpdateHandler(UpdateHandler<T> handler);
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.HasHandlers;
/**
* A widget that implements this interface is a public source of
* {@link WeekSelectionEvent} events.
*
* @param <T> the type being opened
*/
public interface HasWeekSelectionHandlers<T> extends HasHandlers {
/**
* Adds a {@link WeekSelectionEvent} handler.
*
* @param handler the handler
* @return the registration for the event
*/
HandlerRegistration addWeekSelectionHandler(WeekSelectionHandler<T> handler);
}

View File

@ -0,0 +1,91 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* TODO: Complete JavaDoc comments.
*
* @author Brad Rydzewski
*/
public class MouseOverEvent<T> extends GwtEvent<MouseOverHandler<T>> {
/**
* Handler type.
*/
private static Type<MouseOverHandler<?>> TYPE;
private final T target;
private final Object element;
/**
* Fires a open event on all registered handlers in the handler manager.If no
* such handlers exist, this method will do nothing.
*
* @param <T> the target type
* @param source the source of the handlers
* @param target the dom element
*/
public static <T> void fire(HasMouseOverHandlers<T> source, T target, Object element) {
if (TYPE != null) {
MouseOverEvent<T> event = new MouseOverEvent<T>(target, element);
source.fireEvent(event);
}
}
protected MouseOverEvent(T target, Object element) {
this.target = target;
this.element = element;
}
public T getTarget() {
return target;
}
public Object getElement() {
return element;
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<MouseOverHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<MouseOverHandler<?>>();
}
return TYPE;
}
@SuppressWarnings("unchecked")
@Override
public final Type<MouseOverHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
// Because of type erasure, our static type is
// wild carded, yet the "real" type should use our I param.
@Override
protected void dispatch(MouseOverHandler<T> handler) {
handler.onMouseOver(this);
}
}

View File

@ -0,0 +1,18 @@
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link MouseOverEvent} events.
*
* @param <T> the type being hovered
*/
public interface MouseOverHandler<T> extends EventHandler {
/**
* Called when {@link MouseOverEvent} is fired.
*
* @param event the {@link MouseOverEvent} that was fired
*/
void onMouseOver(MouseOverEvent<T> event);
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
/**
* <code>RollbackException</code> can be thrown to rollback or cancel any
* changes made and not yet committed at the time of an Event. <p></p> An
* example is when an {@link com.bradrydzewski.gwt.calendar.client.Appointment}
* is deleted by the end-user. A DeleteEvent is raised and the change can be
* reversed by throwing the RollbackException.
*
* @author Brad Rydzewski
*/
public class RollbackException extends Exception {
/**
* Default empty constructor.
*/
public RollbackException() {
}
}

View File

@ -0,0 +1,96 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* TODO: Complete JavaDoc comments.
*
* @author Brad Rydzewski
*/
public class TimeBlockClickEvent<T> extends GwtEvent<TimeBlockClickHandler<T>> {
/**
* Handler type.
*/
private static Type<TimeBlockClickHandler<?>> TYPE;
/**
* Fires a open event on all registered handlers in the handler manager.If no
* such handlers exist, this method will do nothing.
*
* @param <T> the target type
* @param source the source of the handlers
* @param target the target
*/
public static <T> void fire(HasTimeBlockClickHandlers<T> source, T target) {
if (TYPE != null) {
TimeBlockClickEvent<T> event = new TimeBlockClickEvent<T>(target);
source.fireEvent(event);
}
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<TimeBlockClickHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<TimeBlockClickHandler<?>>();
}
return TYPE;
}
private final T target;
/**
* Creates a new delete event.
*
* @param target the ui object being opened
*/
protected TimeBlockClickEvent(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
@Override
public final Type<TimeBlockClickHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the target.
*
* @return the target
*/
public T getTarget() {
return target;
}
// Because of type erasure, our static type is
// wild carded, yet the "real" type should use our I param.
@Override
protected void dispatch(TimeBlockClickHandler<T> handler) {
handler.onTimeBlockClick(this);
}
}

View File

@ -0,0 +1,18 @@
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface TimeBlockClickHandler<T> extends EventHandler {
/**
* Called when {@link DeleteEvent} is fired.
*
* @param event the {@link DeleteEvent} that was fired
*/
void onTimeBlockClick(TimeBlockClickEvent<T> event);
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* TODO: Complete JavaDoc comments.
* @author Brad Rydzewski
*/
public class UpdateEvent<T> extends GwtEvent<UpdateHandler<T>> {
/**
* Handler type.
*/
private static Type<UpdateHandler<?>> TYPE;
private boolean cancelled = false;
public boolean isCancelled() {
return cancelled;
}
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
/**
* Fires a open event on all registered handlers in the handler manager.If no
* such handlers exist, this method will do nothing.
*
* @param <T> the target type
* @param source the source of the handlers
* @param target the target
*/
public static <T> boolean fire(HasUpdateHandlers<T> source, T target) {
if (TYPE != null) {
UpdateEvent<T> event = new UpdateEvent<T>(target);
source.fireEvent(event);
return !event.isCancelled();
}
return true;
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<UpdateHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<UpdateHandler<?>>();
}
return TYPE;
}
private final T target;
/**
* Creates a new delete event.
*
* @param target the ui object being opened
*/
protected UpdateEvent(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
@Override
public final Type<UpdateHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the target.
*
* @return the target
*/
public T getTarget() {
return target;
}
// Because of type erasure, our static type is
// wild carded, yet the "real" type should use our I param.
@Override
protected void dispatch(UpdateHandler<T> handler) {
handler.onUpdate(this);
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface UpdateHandler<T> extends EventHandler {
/**
* Called when {@link DeleteEvent} is fired.
*
* @param event the {@link DeleteEvent} that was fired
*/
void onUpdate(UpdateEvent<T> event);
}

View File

@ -0,0 +1,99 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.GwtEvent;
/**
* Represents a selection event.
*
* @param <T>
* the type being selected
*/
public class WeekSelectionEvent<T> extends GwtEvent<WeekSelectionHandler<T>> {
/**
* Handler type.
*/
private static Type<WeekSelectionHandler<?>> TYPE;
/**
* Fires a selection event on all registered handlers in the handler
* manager.If no such handlers exist, this method will do nothing.
*
* @param <T>
* the selected item type
* @param source
* the source of the handlers
* @param selectedItem
* the selected item
*/
public static <T> void fire(HasWeekSelectionHandlers<T> source, T selectedItem) {
if (TYPE != null) {
WeekSelectionEvent<T> event = new WeekSelectionEvent<T>(
selectedItem);
source.fireEvent(event);
}
}
/**
* Gets the type associated with this event.
*
* @return returns the handler type
*/
public static Type<WeekSelectionHandler<?>> getType() {
if (TYPE == null) {
TYPE = new Type<WeekSelectionHandler<?>>();
}
return TYPE;
}
private final T selectedItem;
/**
* Creates a new selection event.
*
* @param selectedItem
* selected item
*/
protected WeekSelectionEvent(T selectedItem) {
this.selectedItem = selectedItem;
}
// The instance knows its BeforeSelectionHandler is of type I, but the TYPE
// field itself does not, so we have to do an unsafe cast here.
@SuppressWarnings("unchecked")
@Override
public final Type<WeekSelectionHandler<T>> getAssociatedType() {
return (Type) TYPE;
}
/**
* Gets the selected item.
*
* @return the selected item
*/
public T getSelectedItem() {
return selectedItem;
}
@Override
protected void dispatch(WeekSelectionHandler<T> handler) {
handler.onSelection(this);
}
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.event;
import com.google.gwt.event.shared.EventHandler;
/**
* Handler interface for {@link DeleteEvent} events.
*
* @param <T> the type being opened
*/
public interface WeekSelectionHandler<T> extends EventHandler {
/**
* Called when {@link DeleteEvent} is fired.
*
* @param event the {@link DeleteEvent} that was fired
*/
void onSelection(WeekSelectionEvent<T> event);
}

View File

@ -0,0 +1,87 @@
package com.bradrydzewski.gwt.calendar.client.i18n;
import com.google.gwt.i18n.client.Messages;
/**
* Defines the Strings that can be localized in gwt-cal.
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*/
public interface CalendarConstants extends Messages {
/**
* Footer for days in the <code>MonthView</code> in which there are more
* appointments to display than fit in the day cell.
*
* @param appointments The number of additional appointments in the day
* @return The localized string
*/
public String more(int appointments);
/**
* The localized text label for 12 p.m.
*
* @return The label for the noon
*/
public String noon();
/**
* String representing AM. For certain locales this
* will be null.
*
* @return AM string
*/
public String am();
/**
* String representing AM. For certain locales this
* will be null.
*
* @return PM string
*/
public String pm();
/**
* String used to format time in the <code>DayView</code>
* components timeline. Example: time is formatted in the US as h AM,
* and is formatted in French HH:00.
*
* @return The localized time format for the DayView
*/
public String timeFormat();
/**
* Each column in the <code>DayView</code> represents a date in time, and
* displays the date in its header (i.e. Wed, Jan 15). This property
* provides the pattern to format the date.
*
* @return The localized time format for the DayView
*/
public String dateFormat();
/**
* Each column in the <code>MonthView</code> represents a day of the week,
* and displays the day of the week in its header (i.e. Mon, Tue, Wed). This
* property provides the pattern to format the day of the week.
*
* @return The pattern to format the names of the days in the month view
*/
public String weekdayFormat();
/**
* Returns the pattern to format the days in a month.
*
* @return The pattern to format the names/numbers of days in a month
*/
public String dayOfMonthFormat();
/**
* Returns what the first day of the week is; e.g., SUNDAY in the U.S.,
* MONDAY in France.
*
* @return The text representing the first day of the week, zero-based
* (Sunday is 0, Monday is 1, Tuesday is 2, etc.)
*/
public String firstDayOfWeek();
}

View File

@ -0,0 +1,9 @@
noon=Noon
more=+{0} more
dayOfMonthFormat=d
timeFormat=h
dateFormat=EEE, MMM d
weekdayFormat=EEE
am=AM
pm=PM
firstDayOfWeek=0

View File

@ -0,0 +1,105 @@
package com.bradrydzewski.gwt.calendar.client.monthview;
import com.bradrydzewski.gwt.calendar.client.Appointment;
/**
* Contains common properties and behavior for layout descriptions that can be
* &quot;stacked&quot; on top of each month view's week.
*
* @author Carlos D. Morales
*/
public class AppointmentLayoutDescription {
/**
* The layer order (in a stack-like arrangement)assigned to this description
* by the <code>AppointmentStackingManager</code> when arranging the
* descriptions on the top of a week.
*/
private int stackOrder = 0;
/**
* The start day of the described <code>Appointment</code> when laid on the
* top of one of the weeks while visualizing a month through the
* <code>MonthView</code>.
*/
private int fromWeekDay = 0;
/**
* The end day of the described <code>Appointment</code> when laid on the top
* of one of the weeks while visualizing a month through the
* <code>MonthView</code>.
*/
private int toWeekDay = 0;
/**
* The underlying <code>Appointment</code> whose details will be displayed
* through the <code>MonthView</code>.
*/
private Appointment appointment = null;
public AppointmentLayoutDescription(int fromWeekDay, int toWeekDay,
Appointment appointment) {
this.toWeekDay = toWeekDay;
this.fromWeekDay = fromWeekDay;
this.appointment = appointment;
}
public AppointmentLayoutDescription(int weekDay,
Appointment appointment) {
this(weekDay, weekDay, appointment);
}
public boolean overlapsWithRange(int from, int to) {
return fromWeekDay >= from && fromWeekDay <= to ||
fromWeekDay <= from && toWeekDay >= from;
}
public void setStackOrder(int stackOrder) {
this.stackOrder = stackOrder;
}
public int getStackOrder() {
return stackOrder;
}
public int getWeekStartDay() {
return fromWeekDay;
}
public int getWeekEndDay() {
return toWeekDay;
}
public boolean spansMoreThanADay() {
return fromWeekDay != toWeekDay;
}
public AppointmentLayoutDescription split() {
AppointmentLayoutDescription secondPart = null;
if (spansMoreThanADay()) {
secondPart =
new AppointmentLayoutDescription(fromWeekDay + 1, toWeekDay,
appointment);
this.toWeekDay = this.fromWeekDay;
} else {
secondPart = this;
}
return secondPart;
}
public Appointment getAppointment() {
return appointment;
}
public void setAppointment(Appointment appointment) {
this.appointment = appointment;
}
@Override public String toString() {
return "AppointmentLayoutDescription{" + "stackOrder=" + stackOrder
+ ", fromWeekDay=" + fromWeekDay + ", toWeekDay=" + toWeekDay
+ ", appointment=[" + appointment.getTitle() + "]@"
+ appointment.hashCode() + '}';
}
}

View File

@ -0,0 +1,258 @@
package com.bradrydzewski.gwt.calendar.client.monthview;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Manages the <code>AppointmentLayoutDescription</code> in a stack-like
* structure arranged in layers. Layers are <code>0</code>-based (1nd. layer has
* an index of 0, 2nd. has an index of 1).
*
* @author Carlos D. Morales
*/
public class AppointmentStackingManager {
/**
* The highest layer index that has been allocated by this manager.
*/
private int highestLayer = 0;
/**
* The collection of <code>AppointmentLayoutDescription</code>s grouped by by
* layer (map key).
*/
private HashMap<Integer, ArrayList<AppointmentLayoutDescription>>
layeredDescriptions =
new HashMap<Integer, ArrayList<AppointmentLayoutDescription>>();
private int layerOverflowLimit = Integer.MAX_VALUE;
public void setLayerOverflowLimit(int layerOverflowLimit) {
this.layerOverflowLimit = layerOverflowLimit;
}
/**
* Associates the provided <code>description</code> to the first available
* layer in the collection administered by this manager. This manager will
* look up in the stack of layers (from lowest index to highest index) until
* a layer that <em>does not have</em> any <code>AppointmentLayoutDescription</code>s
* that overlap with the days that the description's appointment spans.
*
* @param description An appointment description object that can be laid on a
* layer
*/
public void assignLayer(AppointmentLayoutDescription description) {
boolean layerAssigned;
int currentlyInspectedLayer = 0;
do {
initLayer(currentlyInspectedLayer);
layerAssigned = assignLayer(currentlyInspectedLayer, description);
currentlyInspectedLayer++;
} while (!layerAssigned);
}
/**
* Returns the lowest layer index that is available on the specified
* <code>day</code>.
*
* @param day The day index for which the lowest layer index will be
* attempted to identify
* @return An integer representing the index of the layer (zero-based) for
* which an single day <code>Appointment</code> can be displayed on.
*/
public int lowestLayerIndex(int day) {
return (nextLowestLayerIndex(day, 0));
}
/**
* Returns the lowest layer index <em>higher than</em> <code>fromLayer</code>
* that is available on the specified <code>day</code>.
*
* @param day The day index for which the lowest layer index will be
* attempted to identify found
* @param fromLayer The layer index <em>after</em> which the search for next
* available layer should be started from
* @return An integer representing the index of the layer (zero-based) for
* which an single day <code>Appointment</code> can be displayed on.
*/
public int nextLowestLayerIndex(int day, int fromLayer) {
boolean layerFound = false;
int currentlyInspectedLayer = fromLayer;
do {
if (isLayerAllocated(currentlyInspectedLayer)) {
if (overlapsWithDescriptionInLayer(
layeredDescriptions.get(currentlyInspectedLayer), day,
day)) {
currentlyInspectedLayer++;
} else {
layerFound = true;
}
} else {
layerFound = true;
}
} while (!layerFound);
return currentlyInspectedLayer;
}
/**
* Returns all the <code>AppointmentLayoutDescription</code>s in the
* specified layer.
*
* @param layerIndex The index of a layer for which descriptions will be
* returned
* @return The collection of appointment descriptions in the layer,
* <code>null</code> if no appointment has been allocated for the
* layer at all
*/
public ArrayList<AppointmentLayoutDescription> getDescriptionsInLayer(
int layerIndex) {
return layeredDescriptions.get(layerIndex);
}
/**
* Verifies if the range defined by <code>start</code>-<code>end</code>
* overlaps with any of the appointment descriptions in
* <code>layerDescriptions</code>.
*
* @param layerDescriptions All the <code>AppointmentLayoutDescription</code>
* in a single layer.
* @param start The first day of the week in the range to test
* @param end The last day of the week in the range to test
* @return <code>true</code> if any appointment description in
* <code>layerDescriptions</code> overlaps with the specified range,
* <code>false</code> otherwise.
*/
private boolean overlapsWithDescriptionInLayer(
ArrayList<AppointmentLayoutDescription> layerDescriptions, int start,
int end) {
if (layerDescriptions != null) {
for (AppointmentLayoutDescription description : layerDescriptions) {
if (description.overlapsWithRange(start, end)) {
return true;
}
}
}
return false;
}
private boolean assignLayer(int layer,
AppointmentLayoutDescription description) {
ArrayList<AppointmentLayoutDescription> layerDescriptions = layeredDescriptions
.get(layer);
boolean assigned = false;
if (!overlapsWithDescriptionInLayer(layerDescriptions,
description.getWeekStartDay(), description.getWeekEndDay())) {
highestLayer = Math.max(highestLayer, layer);
if (layer > layerOverflowLimit && description.spansMoreThanADay()) {
AppointmentLayoutDescription split = description.split();
layerDescriptions.add(description);
assignLayer(split);
} else {
layerDescriptions.add(description);
}
assigned = true;
}
return assigned;
}
/**
* Indicates whether a specific layerIndex with index <code>layerIndex</code>
* has been allocated in the <code>layeredDescriptions</code> map.
*
* @param layerIndex The index of a layer to verify
* @return <code>true</code> if the <code>layeredDescriptions</code> map has
* an entry (<code>List&lt;AppointmentLayoutDescription&gt;</code>)
* for the <code>layerIndex</code>.
*/
private boolean isLayerAllocated(int layerIndex) {
return layeredDescriptions.get(layerIndex) != null;
}
/**
* Initializes the collection of descriptions for the layer with the
* specified <code>layerIndex</code>.
*
* @param layerIndex The index of a layer to initialize
*/
private void initLayer(int layerIndex) {
if (!isLayerAllocated(layerIndex)) {
layeredDescriptions.put(layerIndex,
new ArrayList<AppointmentLayoutDescription>());
}
}
/**
* Returns the number of appointments (multi-day or all-day, as that's the
* type of appointment that the stacking manager deals with only) that
* exceeded the <code>layerOverflowLimit</code> value when they were
* stacked.
*
* @param day The day to perform the count
* @return The number of days that got a layer higher than the configured
* <code>layerOverflowLimit</code> layer, if there were any
*/
public int multidayAppointmentsOverLimitOn(int day) {
int count = 0;
for (int layer = 0; layer <= highestLayer; layer++) {
ArrayList<AppointmentLayoutDescription> descriptions =
layeredDescriptions.get(layer);
if (descriptions != null) {
for (AppointmentLayoutDescription description : descriptions) {
if (layer > layerOverflowLimit &&
description.overlapsWithRange(day, day)) {
count++;
}
}
}
}
return count;
}
/**
* Indicates whether there are <em>any</em> appointments that encompass the
* specified <code>day</code>.
*
* @param day The day to test for appointments
* @return <code>true</code> if there are any descriptions in any layer for
* the specified <code>day</code>.
*/
public boolean areThereAppointmentsOn(int day) {
boolean thereAre = false;
for (int layersIndex = 0; layersIndex <= highestLayer; layersIndex++) {
ArrayList<AppointmentLayoutDescription> layerDescriptions =
layeredDescriptions.get(layersIndex);
if (overlapsWithDescriptionInLayer(layerDescriptions, day, day)) {
thereAre = true;
break;
}
}
return thereAre;
}
@Override public String toString() {
StringBuilder managerState = new StringBuilder();
for (int i = 0; i <= highestLayer; i++) {
ArrayList<AppointmentLayoutDescription> descriptions =
this.getDescriptionsInLayer(i);
if (descriptions == null) continue;
for (AppointmentLayoutDescription desc : descriptions) {
managerState.append("[").append(i).append("]");
for (int before = 0; before < desc.getWeekStartDay(); before++) {
managerState.append("_");
}
for (int dur = desc.getWeekStartDay(); dur <= desc.getWeekEndDay();
dur++) {
managerState.append("X");
}
for (int after = desc.getWeekEndDay(); after < 6; after++) {
managerState.append("_");
}
managerState.append(" ->").append(desc).append("\n");
}
}
return managerState.toString();
}
}

View File

@ -0,0 +1,63 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.monthview;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Label;
/**
* A panel used to render an <code>Appointment</code> in a
* <code>MonthView</code>. <p> Through an association to a domain-model
* <code>Appointment</code>, <code>AppointmentWidget</code>s allow displaying
* the appointment details <em>and</em> to capture mouse and keyboard events.
* </p>
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*/
public class AppointmentWidget extends FocusPanel {
/**
* The underlying <code>Appointment</code> represented by this panel.
*/
private Appointment appointment;
/**
* Creates an <code>AppointmentWidget</code> with a reference to the provided
* <code>appointment</code>.
*
* @param appointment The appointment to be displayed through this panel
* widget
*/
public AppointmentWidget(Appointment appointment) {
this.appointment = appointment;
Label titleLabel = new Label();
titleLabel.getElement().setInnerHTML(this.appointment.getTitle());
this.add(titleLabel);
}
/**
* Returns the <code>Appointment</code> this panel represents/is associated
* to.
*
* @return The domain model appointment rendered through this panel
*/
public Appointment getAppointment() {
return appointment;
}
}

View File

@ -0,0 +1,12 @@
package com.bradrydzewski.gwt.calendar.client.monthview;
/**
* Indicates whether the presence of an <code>Appointment</code> that spans
* multiple weeks in a month view is the <em>first</em>, second or before the
* last, or the <em>last</em>.
*
* @author Carlos D. Morales
*/
public enum AppointmentWidgetParts {
FIRST_WEEK, IN_BETWEEN, LAST_WEEK
}

View File

@ -0,0 +1,53 @@
package com.bradrydzewski.gwt.calendar.client.monthview;
import java.util.ArrayList;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
/**
* Contains the calculated layout description of all <code>Appointment</code>s
* in single day part of a week row in a <code>MonthView</code>.
* <p></p><strong>Note:</strong> A <code>DayLayoutDescription</code> is not
* aware of <em>multi-day</em> <code>Appointment</code>s that might span the day
* represented by this description.
*
* @author Carlos D. Morales
*/
public class DayLayoutDescription {
/**
* The list of <em>simple</em> appointments (not multiday, not all-day) in
* this single day.
*/
private List<Appointment> appointments = new ArrayList<Appointment>();
/**
* The index of the represented day in the corresponding parent week.
*/
private int dayIndex = -1;
public DayLayoutDescription(int dayIndex) {
this.dayIndex = dayIndex;
}
public List<Appointment> getAppointments() {
return appointments;
}
public int getTotalAppointmentCount() {
return appointments.size();
}
public void addAppointment(Appointment appointment) {
if (!appointment.isMultiDay()) {
appointments.add(appointment);
} else {
throw new IllegalArgumentException(
"Attempted to add a multiday appointment to a single day description");
}
}
public int getDayIndex() {
return dayIndex;
}
}

View File

@ -0,0 +1,138 @@
package com.bradrydzewski.gwt.calendar.client.monthview;
import java.util.Date;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
/**
* Describes the layout for all appointments in all the weeks displayed in a
* <code>MonthView</code>. This class is responsible for the distribution of the
* appointments over the multiple weeks they possibly span.
*
* @author Carlos D. Morales
*/
public class MonthLayoutDescription {
private Date calendarFirstDay = null;
private Date calendarLastDay = null;
private WeekLayoutDescription[] weeks = new WeekLayoutDescription[6];
public MonthLayoutDescription(Date calendarFirstDay,
int monthViewRequiredRows,
List<Appointment> appointments, int maxLayer) {
this.calendarFirstDay = calendarFirstDay;
this.calendarLastDay =
calculateLastDate(calendarFirstDay, monthViewRequiredRows);
placeAppointments(appointments, maxLayer);
}
public MonthLayoutDescription(Date calendarFirstDay,
int monthViewRequiredRows,
List<Appointment> appointments) {
this(calendarFirstDay, monthViewRequiredRows,
appointments, Integer.MAX_VALUE);
}
private void initWeek(int weekIndex, int maxLayer) {
if (weeks[weekIndex] == null) {
weeks[weekIndex] = new WeekLayoutDescription(calendarFirstDay,
calendarLastDay,
maxLayer);
}
}
private void placeAppointments(List<Appointment> appointments,
int maxLayer) {
for (Appointment appointment : appointments) {
if (overlapsWithMonth(appointment, calendarFirstDay,
calendarLastDay)) {
int startWeek =
calculateWeekFor(appointment.getStart(), calendarFirstDay);
/* Place appointments only in this month */
if (startWeek >= 0 && startWeek < weeks.length) {
initWeek(startWeek, maxLayer);
if (appointment.isMultiDay() || appointment.isAllDay()) {
positionMultidayAppointment(startWeek, appointment, maxLayer);
} else {
weeks[startWeek].addAppointment(appointment);
}
}
}
}
}
private boolean isMultiWeekAppointment(int startWeek, int endWeek) {
return startWeek != endWeek;
}
private void positionMultidayAppointment(int startWeek,
Appointment appointment, int maxLayer) {
int endWeek = calculateWeekFor(appointment.getEnd(), calendarFirstDay);
initWeek(endWeek, maxLayer);
if (isMultiWeekAppointment(startWeek, endWeek)) {
distributeOverWeeks(startWeek, endWeek, appointment, maxLayer);
} else {
weeks[startWeek].addMultiDayAppointment(appointment);
}
}
private void distributeOverWeeks(int startWeek, int endWeek,
Appointment appointment, int maxLayer) {
weeks[startWeek].addMultiWeekAppointment(appointment,
AppointmentWidgetParts.FIRST_WEEK);
for (int week = startWeek + 1; week < endWeek; week++) {
initWeek(week, maxLayer);
weeks[week].addMultiWeekAppointment(appointment,
AppointmentWidgetParts.IN_BETWEEN);
}
if (startWeek < endWeek) {
initWeek(endWeek, maxLayer);
weeks[endWeek].addMultiWeekAppointment(appointment,
AppointmentWidgetParts.LAST_WEEK);
}
}
private boolean overlapsWithMonth(Appointment appointment,
Date calendarFirstDate, Date calendarLastDate) {
return !(appointment.getStart().before(calendarFirstDate)
&& appointment.getEnd().before(calendarFirstDate)
|| appointment.getStart().after(calendarLastDate)
&& appointment.getEnd().after(calendarLastDate));
}
private int calculateWeekFor(Date testDate, Date calendarFirstDate) {
//fix for issue 66. differenceInDays returns abs value, causing problems
if(testDate.before(calendarFirstDate))
return 0;
int week = (int) Math.floor(
DateUtils.differenceInDays(
testDate, calendarFirstDate) / 7d);
return Math.min(week, weeks.length - 1);
}
@SuppressWarnings("deprecation")
private Date calculateLastDate(final Date startDate, int weeks) {
int daysInMonthGrid = weeks * 7;
Date endDate = (Date) startDate.clone();
endDate.setDate(endDate.getDate() + daysInMonthGrid - 1);
// fix for issue 164: The endDate is at the end of the day
endDate.setHours(23);
endDate.setMinutes(59);
endDate.setSeconds(59);
return endDate;
}
public WeekLayoutDescription[] getWeekDescriptions() {
return weeks;
}
}

View File

@ -0,0 +1,764 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2009 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.monthview;
import static com.bradrydzewski.gwt.calendar.client.DateUtils.moveOneDayForward;
import static com.bradrydzewski.gwt.calendar.client.monthview.MonthViewDateUtils.firstDateShownInAMonthView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandler;
import com.allen_sauer.gwt.dnd.client.DragStartEvent;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.MonthViewDropController;
import com.allen_sauer.gwt.dnd.client.drop.MonthViewPickupDragController;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.CalendarFormat;
import com.bradrydzewski.gwt.calendar.client.CalendarSettings.Click;
import com.bradrydzewski.gwt.calendar.client.CalendarView;
import com.bradrydzewski.gwt.calendar.client.CalendarWidget;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.event.HasDaySelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasWeekSelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.util.FormattingUtil;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
/**
* <p>
* A CalendarView that displays appointments for a given month. The Month is
* displayed in a grid-style view where cells represents days, columns
* represents days of the week (i.e. Monday, Tuesday, etc.) and rows represent a
* full week (Sunday through Saturday).
* <p/>
* <p/>
* <h3>CSS Style Rules</h3> <ul class='css'>
* <li>.gwt-cal-MonthView { }</li>
* <li>.dayCell { cell that represents a day }</li>
* <li>.dayCell-today { cell that represents today }</li>
* <li>.dayCell-disabled { cell's day falls outside the month }</li>
* <li>.dayCell-today-disabled { cell represents today, falls outside the month
* }</li>
* <li>.dayCellLabel { header for the cell }</li>
* <li>.dayCellLabel-today { cell represents today }</li>
* <li>.dayCellLabel-disabled { cell's day falls outside the month }</li>
* <li>.dayCellLabel-today-disabled { cell represents today, falls outside the
* month }</li>
* <li>.weekDayLabel { label for the days of the week }</li> </ul>
*
* @author Brad Rydzewski
* @since 0.9.0
*/
public class MonthView extends CalendarView implements HasWeekSelectionHandlers<Date>, HasDaySelectionHandlers<Date> {
public static final Comparator<Appointment> APPOINTMENT_COMPARATOR = new Comparator<Appointment>() {
public int compare(Appointment a1, Appointment a2) {
int compare = Boolean.valueOf(a2.isMultiDay()).compareTo(
a1.isMultiDay());
if (compare == 0) {
compare = a1.getStart().compareTo(a2.getStart());
}
if (compare == 0) {
compare = a2.getEnd().compareTo(a1.getEnd());
}
return compare;
}
};
private static final int DAYS_IN_A_WEEK = 7;
private final static String MONTH_VIEW = "gwt-cal-MonthView";
private final static String CANVAS_STYLE = "canvas";
private final static String GRID_STYLE = "grid";
private final static String CELL_STYLE = "dayCell";
private final static String MORE_LABEL_STYLE = "moreAppointments";
private final static String CELL_HEADER_STYLE = "dayCellLabel";
private final static String WEEKDAY_LABEL_STYLE = "weekDayLabel";
private final static String WEEKNUMBER_LABEL_STYLE = "weekNumberLabel";
/**
* List of appointment panels drawn on the month view canvas.
*/
private ArrayList<AppointmentWidget> appointmentsWidgets = new ArrayList<AppointmentWidget>();
/**
* All appointments are placed on this canvas and arranged.
*/
private AbsolutePanel appointmentCanvas = new AbsolutePanel();
/**
* All "+ n more" Labels, mapped to its cell in the MonthView Grid.
*/
private HashMap<Element, Integer> moreLabels = new HashMap<Element, Integer>();
private ArrayList<Label> dayLabels = new ArrayList<Label>();
private ArrayList<Widget> dayPanels = new ArrayList<Widget>();
/**
* The first date displayed on the MonthView (1st cell.) This date is not
* necessarily the first date of the month as the month view will sometimes
* display days from the adjacent months because of the number of days
* fitting in the visible grid.
*/
private Date firstDateDisplayed;
/**
* Grid that makes up the days and weeks of the MonthView.
*/
private FlexTable monthCalendarGrid = new FlexTable();
/**
* The number of rows required to display the entire month in grid format.
* Although most months span a total of five weeks, there are some months
* that span six weeks.
*/
private int monthViewRequiredRows = 5;
/**
* List of <code>AppointmentWidget</code>s that are associated to the
* currently selected <code>Appointment</code> appointment.
*/
private ArrayList<AppointmentWidget> selectedAppointmentWidgets = new ArrayList<AppointmentWidget>();
private PickupDragController dragController = null;
private MonthViewDropController monthViewDropController = null;
private MonthViewStyleManager styleManager = GWT.create(MonthViewStyleManager.class);
/**
* This method is called when the MonthView is attached to the Calendar and
* displayed. This is where all components are configured and added to the
* RootPanel.
*/
public void attach(CalendarWidget widget) {
super.attach(widget);
calendarWidget.addToRootPanel(monthCalendarGrid);
monthCalendarGrid.setCellPadding(0);
monthCalendarGrid.setBorderWidth(0);
monthCalendarGrid.setCellSpacing(0);
monthCalendarGrid.setStyleName(GRID_STYLE);
calendarWidget.addToRootPanel(appointmentCanvas);
appointmentCanvas.setStyleName(CANVAS_STYLE);
selectedAppointmentWidgets.clear();
if (dragController == null) {
dragController = new MonthViewPickupDragController(appointmentCanvas, true);
dragController.addDragHandler(new DragHandler() {
public void onDragEnd(DragEndEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireUpdateEvent(appt);
}
public void onDragStart(DragStartEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
calendarWidget.setRollbackAppointment(appt.clone());
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {
// do nothing
}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {
// do nothing
}
});
}
/*
* Need to re-set appointmentCanvas to position:absolute because gwt-dnd
* will set it to relative, but then the layout gets f***ed up
*/
DOM.setStyleAttribute(appointmentCanvas.getElement(), "position",
"absolute");
dragController.setBehaviorDragStartSensitivity(5);
dragController.setBehaviorDragProxy(true);
// instantiate our drop controller
monthViewDropController = new MonthViewDropController(
appointmentCanvas, monthCalendarGrid);
dragController.registerDropController(monthViewDropController);
}
/**
* Performs a Layout and arranges all appointments on the MonthView's
* appointment canvas.
*/
@Override
public void doLayout() {
// Clear all existing appointments
appointmentCanvas.clear();
monthCalendarGrid.clear();
appointmentsWidgets.clear();
moreLabels.clear();
dayLabels.clear();
dayPanels.clear();
selectedAppointmentWidgets.clear();
while (monthCalendarGrid.getRowCount() > 0) {
monthCalendarGrid.removeRow(0);
}
// Rebuild the month grid
buildCalendarGrid();
// (Re)calculate some variables
calculateCellHeight();
calculateCellAppointments();
// set variables needed by the drop controller
// monthViewDropController.setDayHeaderHeight(calculatedDayHeaderHeight);
monthViewDropController.setDaysPerWeek(DAYS_IN_A_WEEK);
// monthViewDropController.setWeekdayHeaderHeight(calculatedWeekDayHeaderHeight);
monthViewDropController.setWeeksPerMonth(monthViewRequiredRows);
monthViewDropController.setFirstDateDisplayed(firstDateDisplayed);
// Sort the appointments
// TODO: don't re-sort the appointment unless necessary
Collections.sort(calendarWidget.getAppointments(),
APPOINTMENT_COMPARATOR);
// Distribute appointments
MonthLayoutDescription monthLayoutDescription = new
MonthLayoutDescription(
firstDateDisplayed, monthViewRequiredRows,
calendarWidget.getAppointments(),
calculatedCellAppointments - 1);
int dayIndex = 0;
for (int row = 0; row < monthCalendarGrid.getRowCount() - 1; row++) {
for (int col = 0; col < DAYS_IN_A_WEEK; col++) {
Widget lbl = dayPanels.get(dayIndex);
placeDayLabelInGrid(lbl, col, row);
dayIndex++;
}
}
// Get the layouts for each week in the month
WeekLayoutDescription[] weeks = monthLayoutDescription
.getWeekDescriptions();
for (int weekOfMonth = 0; weekOfMonth < weeks.length
&& weekOfMonth < monthViewRequiredRows; weekOfMonth++) {
WeekLayoutDescription weekDescription = weeks[weekOfMonth];
if (weekDescription != null) {
layOnTopOfTheWeekHangingAppointments(
weekDescription, weekOfMonth);
layOnWeekDaysAppointments(weekDescription, weekOfMonth);
}
}
}
private void layOnTopOfTheWeekHangingAppointments(
WeekLayoutDescription weekDescription, int weekOfMonth) {
AppointmentStackingManager weekTopElements
= weekDescription.getTopAppointmentsManager();
for (int layer = 0; layer < calculatedCellAppointments; layer++) {
ArrayList<AppointmentLayoutDescription> descriptionsInLayer
= weekTopElements.getDescriptionsInLayer(layer);
if (descriptionsInLayer == null) {
break;
}
for (AppointmentLayoutDescription weekTopElement : descriptionsInLayer) {
layOnAppointment(weekTopElement.getAppointment(),
weekTopElement.getWeekStartDay(), weekTopElement
.getWeekEndDay(), weekOfMonth, layer);
}
}
}
private void layOnWeekDaysAppointments(WeekLayoutDescription week,
int weekOfMonth) {
AppointmentStackingManager topAppointmentManager = week
.getTopAppointmentsManager();
for (int dayOfWeek = 0; dayOfWeek < DAYS_IN_A_WEEK; dayOfWeek++) {
DayLayoutDescription dayAppointments = week
.getDayLayoutDescription(dayOfWeek);
int appointmentLayer =
topAppointmentManager.lowestLayerIndex(dayOfWeek);
if (dayAppointments != null) {
int count = dayAppointments.getAppointments().size();
for (int i = 0; i < count; i++) {
Appointment appointment
= dayAppointments.getAppointments().get(i);
appointmentLayer = topAppointmentManager
.nextLowestLayerIndex(dayOfWeek,
appointmentLayer);
if (appointmentLayer > calculatedCellAppointments - 1) {
int remaining = count + topAppointmentManager.multidayAppointmentsOverLimitOn(dayOfWeek) - i;
if (remaining == 1) {
layOnAppointment(appointment, dayOfWeek, dayOfWeek,
weekOfMonth, appointmentLayer);
} else {
layOnNMoreLabel(remaining, dayOfWeek, weekOfMonth);
}
break;
}
layOnAppointment(appointment, dayOfWeek, dayOfWeek,
weekOfMonth, appointmentLayer);
appointmentLayer++;
}
} else if (topAppointmentManager
.multidayAppointmentsOverLimitOn(dayOfWeek) > 0) {
layOnNMoreLabel(topAppointmentManager
.multidayAppointmentsOverLimitOn(dayOfWeek),
dayOfWeek, weekOfMonth);
}
}
}
private void layOnNMoreLabel(int moreCount, int dayOfWeek, int weekOfMonth) {
Label more = new Label(CalendarFormat.MESSAGES.more(moreCount));
more.setStyleName(MORE_LABEL_STYLE);
placeItemInGrid(more, dayOfWeek, dayOfWeek, weekOfMonth,
calculatedCellAppointments);
appointmentCanvas.add(more);
moreLabels.put(more.getElement(), (dayOfWeek) + (weekOfMonth * 7));
}
private void layOnAppointment(Appointment appointment, int colStart,
int colEnd, int row, int cellPosition) {
AppointmentWidget panel = new AppointmentWidget(appointment);
placeItemInGrid(panel, colStart, colEnd, row, cellPosition);
boolean selected = calendarWidget.isTheSelectedAppointment(appointment);
styleManager.applyStyle(panel, selected);
if(calendarWidget.getSettings().isEnableDragDrop() && !appointment.isReadOnly())
dragController.makeDraggable(panel);
if(selected)
selectedAppointmentWidgets.add(panel);
appointmentsWidgets.add(panel);
appointmentCanvas.add(panel);
}
/**
* Gets the Month View's primary style name.
*/
public String getStyleName() {
return MONTH_VIEW;
}
/**
* Handles the DoubleClick event to determine if an Appointment has been
* selected. If an appointment has been double clicked the OpenEvent will
* get fired for that appointment.
*/
public void onDoubleClick(Element clickedElement, Event event) {
if (clickedElement.equals(appointmentCanvas.getElement())) {
if (calendarWidget.getSettings().getTimeBlockClickNumber() == Click.Double) {
dayClicked(event);
}
} else {
ArrayList<AppointmentWidget> list = findAppointmentWidgetsByElement(clickedElement);
if (!list.isEmpty()) {
calendarWidget.fireOpenEvent(list.get(0).getAppointment());
}
}
}
/**
* Handles the a single click to determine if an appointment has been
* selected. If an appointment is clicked it's selected status will be set
* to true and a SelectionEvent will be fired.
*/
@Override
public void onSingleClick(Element clickedElement, Event event) {
if (clickedElement.equals(appointmentCanvas.getElement())) {
if (calendarWidget.getSettings().getTimeBlockClickNumber() == Click.Single) {
dayClicked(event);
}
} else {
Appointment appointment = findAppointmentByElement(clickedElement);
if (appointment != null) {
selectAppointment(appointment);
} else {
// else, lets see if a "+ n more" label was clicked
if (moreLabels.containsKey(clickedElement)) {
calendarWidget.fireDateRequestEvent(
cellDate(moreLabels.get(clickedElement)),
clickedElement);
}
}
}
}
public void onMouseOver(Element element, Event event) {
Appointment appointment = findAppointmentByElement(element);
calendarWidget.fireMouseOverEvent(appointment, element);
}
/**
* Returns the date corresponding to the <code>cell</code> (as if the
* month view grid was a big linear sequence of cells) in the month view
* grid.
* @param cell The cell number in the month view grid
* @return The date that corresponds to the given <code>cell</code>
*/
private Date cellDate(int cell) {
return DateUtils.shiftDate(firstDateDisplayed, cell);
}
private void dayClicked(Event event) {
int y = event.getClientY() + Window.getScrollTop() - DOM.getAbsoluteTop(appointmentCanvas.getElement());
int x = event.getClientX() + Window.getScrollLeft() - DOM.getAbsoluteLeft(appointmentCanvas.getElement());
int row = (int) Math.floor(y / (appointmentCanvas.getOffsetHeight() / monthViewRequiredRows));
int col = (int) Math.floor(x / (appointmentCanvas.getOffsetWidth() / DAYS_IN_A_WEEK));
calendarWidget.fireTimeBlockClickEvent(
cellDate(row * DAYS_IN_A_WEEK + col));
}
private ArrayList<AppointmentWidget> findAppointmentWidgetsByElement(
Element element) {
return findAppointmentWidgets(findAppointmentByElement(element));
}
/**
* Builds and formats the Calendar Grid. No appointments are included when
* building the grid.
*/
@SuppressWarnings("deprecation")
private void buildCalendarGrid() {
int firstDayOfWeek = CalendarFormat.INSTANCE.getFirstDayOfWeek();
int month = calendarWidget.getDate().getMonth();
firstDateDisplayed = firstDateShownInAMonthView(
calendarWidget.getDate(), firstDayOfWeek);
Date today = new Date();
DateUtils.resetTime(today);
/* Add the calendar weekday heading */
for (int i = 0; i < DAYS_IN_A_WEEK; i++) {
monthCalendarGrid
.setText(
0,
i,
CalendarFormat.INSTANCE
.getDayOfWeekAbbreviatedNames()[(i + firstDayOfWeek) % 7]);
monthCalendarGrid.getCellFormatter().setVerticalAlignment(0, i,
HasVerticalAlignment.ALIGN_TOP);
monthCalendarGrid.getCellFormatter().setStyleName(0, i,
WEEKDAY_LABEL_STYLE);
}
Date date = (Date)firstDateDisplayed.clone();
monthViewRequiredRows = MonthViewDateUtils.monthViewRequiredRows(
calendarWidget.getDate(), firstDayOfWeek);
int weekNumber = DateUtils.calendarWeekIso(date);
for (int monthGridRowIndex = 1; monthGridRowIndex <= monthViewRequiredRows; monthGridRowIndex++) {
for (int dayOfWeekIndex = 0; dayOfWeekIndex < DAYS_IN_A_WEEK; dayOfWeekIndex++) {
if (monthGridRowIndex != 1 || dayOfWeekIndex != 0) {
moveOneDayForward(date);
weekNumber = DateUtils.calendarWeekIso(date);
}
configureDayInGrid(monthGridRowIndex, dayOfWeekIndex,
date, date.equals(today),
date.getMonth() != month, weekNumber);
}
}
}
/**
* Configures a single day in the month grid of this <code>MonthView</code>.
*
* @param row
* The row in the grid on which the day will be set
* @param col
* The col in the grid on which the day will be set
* @param date
* The Date in the grid
* @param isToday
* Indicates whether the day corresponds to today in the month
* view
* @param notInCurrentMonth
* Indicates whether the day is in the current visualized month
* or belongs to any of the two adjacent months of the current
* month
* @param weekNumber
* The weekNumber to show in the cell, only appears in the first col.
*/
private void configureDayInGrid(int row, int col, Date date,
boolean isToday, boolean notInCurrentMonth, int weekNumber) {
HorizontalPanel panel = new HorizontalPanel();
String text = String.valueOf(date.getDate());
Label label = new Label(text);
StringBuilder headerStyle = new StringBuilder(CELL_HEADER_STYLE);
StringBuilder cellStyle = new StringBuilder(CELL_STYLE);
boolean found = false;
for (Date day : getSettings().getHolidays()) {
if (DateUtils.areOnTheSameDay(day, date)) {
headerStyle.append("-holiday");
cellStyle.append("-holiday");
found = true;
break;
}
}
if (isToday) {
headerStyle.append("-today");
cellStyle.append("-today");
} else if(!found && DateUtils.isWeekend(date)) {
headerStyle.append("-weekend");
cellStyle.append("-weekend");
}
if (notInCurrentMonth) {
headerStyle.append("-disabled");
}
label.setStyleName(headerStyle.toString());
addDayClickHandler(label, (Date)date.clone());
if (col == 0 && getSettings().isShowingWeekNumbers()) {
Label weekLabel = new Label(String.valueOf(weekNumber));
weekLabel.setStyleName(WEEKNUMBER_LABEL_STYLE);
panel.add(weekLabel);
panel.setCellWidth(weekLabel, "25px");
DOM.setStyleAttribute(label.getElement(), "paddingLeft", "5px");
addWeekClickHandler(weekLabel, (Date)date.clone());
}
panel.add(label);
appointmentCanvas.add(panel);
dayLabels.add(label);
dayPanels.add(panel);
//monthCalendarGrid.setWidget(row, col, panel);
monthCalendarGrid.getCellFormatter().setVerticalAlignment(row, col,
HasVerticalAlignment.ALIGN_TOP);
monthCalendarGrid.getCellFormatter().setStyleName(row, col,
cellStyle.toString());
}
/**
* Returns the {@link Appointment} indirectly associated to the passed
* <code>element</code>. Each Appointment drawn on the CalendarView maps to
* a Widget and therefore an Element. This method attempts to find an
* Appointment based on the provided Element. If no match is found a null
* value is returned.
*
* @param element Element to look up.
* @return Appointment matching the element.
*/
private Appointment findAppointmentByElement(Element element) {
Appointment appointmentAtElement = null;
for (AppointmentWidget widget : appointmentsWidgets) {
if (DOM.isOrHasChild(widget.getElement(), element)) {
appointmentAtElement = widget.getAppointment();
break;
}
}
return appointmentAtElement;
}
/**
* Finds any related <code>AppointmentWidgets</code> associated to the
* passed Appointment, <code>appt</code>.
*
* @param appt
* Appointment to match.
* @return List of related AppointmentWidget objects.
*/
private ArrayList<AppointmentWidget> findAppointmentWidgets(Appointment appt) {
ArrayList<AppointmentWidget> appointmentWidgets = new ArrayList<AppointmentWidget>();
if (appt != null) {
for (AppointmentWidget widget : appointmentsWidgets) {
if (widget.getAppointment().equals(appt)) {
appointmentWidgets.add(widget);
}
}
}
return appointmentWidgets;
}
public void onDeleteKeyPressed() {
if (calendarWidget.getSelectedAppointment() != null)
calendarWidget.fireDeleteEvent(calendarWidget
.getSelectedAppointment());
}
@Override
public void onAppointmentSelected(Appointment appt) {
ArrayList<AppointmentWidget> clickedAppointmentWidgets = findAppointmentWidgets(appt);
if (!clickedAppointmentWidgets.isEmpty()) {
for (AppointmentWidget widget : selectedAppointmentWidgets) {
//widget.removeStyleDependentName("selected");
//DOM.setStyleAttribute(widget.getElement(),
// "borderColor", widget.getAppointment().getAppointmentStyle().getBorder());
styleManager.applyStyle(widget, false);
}
for (AppointmentWidget widget : clickedAppointmentWidgets) {
//widget.addStyleDependentName("selected");
//DOM.setStyleAttribute(widget.getElement(),
// "borderColor", appt.getAppointmentStyle().getSelectedBorder());
styleManager.applyStyle(widget, true);
}
selectedAppointmentWidgets.clear();
selectedAppointmentWidgets = clickedAppointmentWidgets;
}
}
/**
* Multiple calculated (&quot;cached&quot;) values reused during
* laying out the month view elements.
*/
private int calculatedWeekDayHeaderHeight;
private int calculatedDayHeaderHeight;
/**
* Maximum appointments per cell (day).
*/
private int calculatedCellAppointments;
/**
* Height of each Cell (day), including the day's header.
*/
private float calculatedCellOffsetHeight;
/**
* Height of each Cell (day), excluding the day's header.
*/
private float calculatedCellHeight;
/**
* Calculates the height of each day cell in the Month grid. It excludes the
* height of each day's header, as well as the overall header that shows the
* weekday labels.
*/
private void calculateCellHeight() {
int gridHeight = monthCalendarGrid.getOffsetHeight();
int weekdayRowHeight = monthCalendarGrid.getRowFormatter()
.getElement(0).getOffsetHeight();
int dayHeaderHeight = dayLabels.get(0).getOffsetHeight();
calculatedCellOffsetHeight = (float) (gridHeight - weekdayRowHeight)
/ monthViewRequiredRows;
calculatedCellHeight = calculatedCellOffsetHeight - dayHeaderHeight;
calculatedWeekDayHeaderHeight = weekdayRowHeight;
calculatedDayHeaderHeight = dayHeaderHeight;
}
/**
* Calculates the maximum number of appointments that can be displayed in a
* given &quot;day cell&quot;.
*/
private void calculateCellAppointments() {
int paddingTop = appointmentPaddingTop();
int height = appointmentHeight();
calculatedCellAppointments = (int)
Math.floor((float) (calculatedCellHeight - paddingTop)
/ (float) (height + paddingTop)) - 1;
}
private static int appointmentPaddingTop() {
return 1 + (Math.abs(FormattingUtil.getBorderOffset()) * 3);
}
private static int appointmentHeight() {
// TODO: calculate appointment height dynamically
return 20;
}
private void placeItemInGrid(Widget panel, int colStart, int colEnd,
int row, int cellPosition) {
int paddingTop = appointmentPaddingTop() + 3;
int height = appointmentHeight();
float left = (float) colStart / (float) DAYS_IN_A_WEEK * 100f + .5f;
float width = ((float) (colEnd - colStart + 1) / (float) DAYS_IN_A_WEEK) * 100f - 1f;
float top = calculatedWeekDayHeaderHeight
+ (row * calculatedCellOffsetHeight)
+ calculatedDayHeaderHeight + paddingTop
+ (cellPosition * (height + paddingTop));
// System.out.println( "\t" + calculatedWeekDayHeaderHeight + " + (" + row +
// " * " + calculatedCellOffsetHeight + ") + " +
// calculatedDayHeaderHeight + " + " + paddingTop + " + (" +
// cellPosition+"*("+height+"+"+paddingTop + "));");
DOM.setStyleAttribute(panel.getElement(), "position", "absolute");
DOM.setStyleAttribute(panel.getElement(), "top", top + "px");
DOM.setStyleAttribute(panel.getElement(), "left", left + "%");
DOM.setStyleAttribute(panel.getElement(), "width", width + "%");
}
private void placeDayLabelInGrid(Widget panel, int col, int row) {
int paddingTop = appointmentPaddingTop();
float left = (float) col / (float) DAYS_IN_A_WEEK * 100f + .5f;
float width = (1f / (float) DAYS_IN_A_WEEK) * 100f - 1f;
float top = calculatedWeekDayHeaderHeight
+ (row * calculatedCellOffsetHeight)
+ paddingTop;
DOM.setStyleAttribute(panel.getElement(), "position", "absolute");
DOM.setStyleAttribute(panel.getElement(), "top", top + "px");
DOM.setStyleAttribute(panel.getElement(), "left", left + "%");
DOM.setStyleAttribute(panel.getElement(), "width", width + "%");
}
}

View File

@ -0,0 +1,118 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.monthview;
import static com.bradrydzewski.gwt.calendar.client.DateUtils.areOnTheSameMonth;
import static com.bradrydzewski.gwt.calendar.client.DateUtils.firstOfNextMonth;
import static com.bradrydzewski.gwt.calendar.client.DateUtils.previousDay;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
/**
* Contains date-related utilities with logic required to generate the {@link
* com.bradrydzewski.gwt.calendar.client.monthview.MonthView}.
*
* @author Carlos D. Morales
*/
public class MonthViewDateUtils {
/**
* Calculates the first date that should be displayed in a month view.
* Depending on the year and month, sometimes, viewing at a month will show
* days from the previous month in the first week.
*
* @param dayInMonth Any day in the month that is being visualized in a
* month view
* @param firstDayOfWeek The day the weeks start on the month view, Sunday is
* <code>0</code>, Monday is <code>1</code>, etc.
* @return The first date that should appear on the first week of a month
* view
*/
@SuppressWarnings("deprecation")
public static Date firstDateShownInAMonthView(Date dayInMonth,
int firstDayOfWeek) {
Date date = DateUtils.firstOfTheMonth(dayInMonth);
int firstDayOffset = firstDayOfWeek + date.getDate() - date.getDay();
date.setDate(firstDayOffset);
if (areOnTheSameMonth(date, dayInMonth) && date.getDate() > 1) {
date.setDate(firstDayOffset - 7);
}
return date;
}
/**
* Dynamically calculates the number of rows required to display all the days
* in a month.
*
* @param dayInMonth Any day in the month that is being visualized through
* the month view
* @param firstDayOfWeek The day the weeks start on the month view, Sunday is
* <code>0</code>, Monday is <code>1</code>, etc.
* @return The number of rows (which represent weeks in the month view)
* required to display all days in the month view
*/
@SuppressWarnings("deprecation")
public static int monthViewRequiredRows(Date dayInMonth,
int firstDayOfWeek) {
int requiredRows = 5;
Date firstOfTheMonthClone = (Date) dayInMonth.clone();
firstOfTheMonthClone.setDate(1);
Date firstDayInCalendar =
firstDateShownInAMonthView(dayInMonth, firstDayOfWeek);
if (firstDayInCalendar.getMonth() != firstOfTheMonthClone.getMonth()) {
Date lastDayOfPreviousMonth = previousDay(firstOfTheMonthClone);
int prevMonthOverlap =
daysInPeriod(firstDayInCalendar, lastDayOfPreviousMonth);
Date firstOfNextMonth = firstOfNextMonth(firstOfTheMonthClone);
int daysInMonth =
daysInPeriod(firstOfTheMonthClone, previousDay(firstOfNextMonth));
if (prevMonthOverlap + daysInMonth > 35) {
requiredRows = 6;
}
}
return requiredRows;
}
/**
* Returns the total number of days between <code>start</code> and
* <code>end</code>.
*
* @param start The first day in the period
* @param end The last day in the period
* @return The number of days between <code>start</code> and
* <code>end</code>, the minimum difference being one
* (<code>1</code>)
*/
@SuppressWarnings("deprecation")
private static int daysInPeriod(Date start, Date end) {
if (start.getMonth() != end.getMonth()) {
throw new IllegalArgumentException(
"Start and end dates must be in the same month.");
}
return 1 + end.getDate() - start.getDate();
}
}

View File

@ -0,0 +1,123 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2011 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.monthview;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.ThemeAppointmentStyle;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
/**
* Applies styles in a month view based on the currently selected theme. This class
* provides a template method to consistently style the appointments in a month view. Subclasses
* provide appropriate appointment styles based on a specific theme using methods
* {@link #getViewAppointmentStyleForTheme(com.bradrydzewski.gwt.calendar.client.Appointment)} and
* {@link #getDefaultViewAppointmentStyleForTheme()}.
*
* @author Brad Rydzewski
* @author Carlos D. Morales
* @see com.bradrydzewski.gwt.calendar.theme.google.client.GoogleMonthViewStyleManager
* @see com.bradrydzewski.gwt.calendar.theme.ical.client.ICalMonthViewStyleManager
*/
public abstract class MonthViewStyleManager {
protected static final String APPOINTMENT_STYLE = "appointment";
protected static final String APPOINTMENT_STYLE_SELECTED = "-selected";
protected static final String APPOINTMENT_STYLE_MULTIDAY = "-multiday";
protected static final String BACKGROUND_COLOR_STYLE_ATTRIBUTE = "backgroundColor";
protected static final String BORDER_COLOR_STYLE_ATTRIBUTE = "borderColor";
protected static final String COLOR_STYLE_ATTRIBUTE = "color";
/**
* Returns the appointment style appropriate to the passed appointment based on a specific theme.
*
* @param appointment An appointment to be displayed in the month view, should include a style to be used
* @return The style to use with a theme, can be <code>null</code> if a default should be used
* @see com.bradrydzewski.gwt.calendar.client.monthview.MonthViewStyleManager#getDefaultViewAppointmentStyleForTheme()
*/
protected abstract ThemeAppointmentStyle getViewAppointmentStyleForTheme(Appointment appointment);
/**
* Returns the default appointment style corresponding to the currently used theme. Subclasses
* should provide a style to be used if no appropriate style can be identified
* ({@link #getViewAppointmentStyleForTheme(com.bradrydzewski.gwt.calendar.client.Appointment)} returns <code>null</code>).
*
* @return The style to use with a theme if no specific style can be identified with
* {@link #getViewAppointmentStyleForTheme(com.bradrydzewski.gwt.calendar.client.Appointment)}
*/
protected abstract ThemeAppointmentStyle getDefaultViewAppointmentStyleForTheme();
/**
* Applies a style in the currently selected theme using the runtime-provided
* theme styles defined by {@link #getViewAppointmentStyleForTheme(com.bradrydzewski.gwt.calendar.client.Appointment)}
* and {@link #getDefaultViewAppointmentStyleForTheme()}.
*
* @param widget The widget to style
* @param selected Indicates if the appointment is the currently selected in the view
*/
public void applyStyle(AppointmentWidget widget, boolean selected) {
doApplyStyleInternal(widget, selected);
}
/**
* Template method to consistently apply the styles to an appointment in the month view.
*
* @param widget The widget to style
* @param selected Indicates if the appointment is the currently selected in the view
*/
protected void doApplyStyleInternal(AppointmentWidget widget, boolean selected) {
//Extract the Appointment for later reference
Appointment appointment = widget.getAppointment();
//Extract the DOM Element for later reference
Element elem = widget.getElement();
//Is MultiDay?
boolean multiDay = appointment.isMultiDay() || appointment.isAllDay();
//Lookup the style from the map
ThemeAppointmentStyle style = getViewAppointmentStyleForTheme(appointment);
//Determine Style Name
String styleName = APPOINTMENT_STYLE;
if (multiDay) styleName += APPOINTMENT_STYLE_MULTIDAY;
if (selected) styleName += APPOINTMENT_STYLE_SELECTED;
widget.setStylePrimaryName(styleName);
//If no style is found, apply the default blue style
//TODO: need to check for a custom style
if (style == null)
style = getDefaultViewAppointmentStyleForTheme();
//Apply Single vs. Multi-day style
if (multiDay) {
DOM.setStyleAttribute(elem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackground());
DOM.setStyleAttribute(elem, BORDER_COLOR_STYLE_ATTRIBUTE, style.getBorder());
} else {
DOM.setStyleAttribute(elem, COLOR_STYLE_ATTRIBUTE, style.getSelectedBorder());
}
//Apply style specific to selected appointments
if (selected) {
DOM.setStyleAttribute(elem, BORDER_COLOR_STYLE_ATTRIBUTE, style.getSelectedBorder());
}
}
}

View File

@ -0,0 +1,143 @@
package com.bradrydzewski.gwt.calendar.client.monthview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
/**
* Describes the layout of days (single, all and multi-day) within a single week
* that is visualized in the <code>MonthView</code>. A <code>WeekLayoutDescription</code>
* is not aware of any other thing than placing an appointment
* <em>horizontally</em>, i.e., without considering the exact week the
* appointment belongs to. It is the <code>MonthLayoutDescription</code>
* responsibility to allocate the month necessary <code>weeks</code> and
* distributing appointments over them.
*
* @author Carlos D. Morales
* @see com.bradrydzewski.gwt.calendar.client.monthview.MonthView
* @see com.bradrydzewski.gwt.calendar.client.monthview.MonthLayoutDescription
*/
public class WeekLayoutDescription {
public static final int FIRST_DAY = 0;
public static final int LAST_DAY = 6;
private AppointmentStackingManager topAppointmentsManager = null;
private DayLayoutDescription[] days = null;
private Date calendarFirstDay = null;
private Date calendarLastDay = null;
private int maxLayer = -1;
public WeekLayoutDescription(Date calendarFirstDay, Date calendarLastDay,
int maxLayer) {
this.calendarFirstDay = calendarFirstDay;
this.calendarLastDay = calendarLastDay;
days = new DayLayoutDescription[7];
this.maxLayer = maxLayer;
topAppointmentsManager = new AppointmentStackingManager();
topAppointmentsManager.setLayerOverflowLimit(this.maxLayer);
}
public WeekLayoutDescription(Date calendarFirstDay, Date calendarLastDay) {
this(calendarFirstDay, calendarLastDay, Integer.MAX_VALUE);
}
private void assertValidDayIndex(int day) {
if (day < FIRST_DAY || day > days.length) {
throw new IllegalArgumentException(
"Invalid day index (" + day + ")");
}
}
private DayLayoutDescription initDay(int day) {
assertValidDayIndex(day);
if (days[day] == null) {
days[day] = new DayLayoutDescription(day);
}
return days[day];
}
public boolean areThereAppointmentsOnDay(int day) {
assertValidDayIndex(day);
return days[day] != null
|| topAppointmentsManager.areThereAppointmentsOn(day);
}
public DayLayoutDescription getDayLayoutDescription(int day) {
assertValidDayIndex(day);
if (!areThereAppointmentsOnDay(day)) {
return null;
}
return days[day];
}
public void addAppointment(Appointment appointment) {
int dayOfWeek = dayInWeek(appointment.getStart());
if (appointment.isAllDay()) {
topAppointmentsManager.assignLayer(
new AppointmentLayoutDescription(dayOfWeek, appointment));
} else {
initDay(dayOfWeek).addAppointment(appointment);
}
}
public int currentStackOrderInDay(int dayIndex) {
return topAppointmentsManager.lowestLayerIndex(dayIndex);
}
public void addMultiDayAppointment(Appointment appointment) {
int weekStartDay = dayInWeek(appointment.getStart());
int weekEndDay = dayInWeek(appointment.getEnd());
if(!appointment.getEnd().before(calendarLastDay)) {
weekEndDay = LAST_DAY;
}
topAppointmentsManager.assignLayer(
new AppointmentLayoutDescription(weekStartDay, weekEndDay,
appointment));
}
public void addMultiWeekAppointment(Appointment appointment,
AppointmentWidgetParts presenceInMonth) {
switch (presenceInMonth) {
case FIRST_WEEK:
int weekStartDay = dayInWeek(appointment.getStart());
topAppointmentsManager.assignLayer(
new AppointmentLayoutDescription(weekStartDay, LAST_DAY,
appointment));
break;
case IN_BETWEEN:
topAppointmentsManager.assignLayer(
new AppointmentLayoutDescription(FIRST_DAY, LAST_DAY, appointment));
break;
case LAST_WEEK:
int weekEndDay = dayInWeek(appointment.getEnd());
topAppointmentsManager.assignLayer(
new AppointmentLayoutDescription(FIRST_DAY, weekEndDay,
appointment));
break;
}
}
private int dayInWeek(Date date) {
if (date.before(calendarFirstDay)) {
return FIRST_DAY;
}
if (date.after(calendarLastDay)) {
return LAST_DAY;
}
return (int) Math
.floor(DateUtils.differenceInDays(date, calendarFirstDay) % 7d);
}
public AppointmentStackingManager getTopAppointmentsManager() {
return topAppointmentsManager;
}
}

View File

@ -0,0 +1,170 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import static com.bradrydzewski.gwt.calendar.client.DateUtils.minutesSinceDayStarted;
import java.util.ArrayList;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
/**
* The Appointment Adapter is used to track the layout of an
* {@link Appointment}. It adds additional fields required to
* calculate layout that are used by the Layout Strategy classes.
*
* This adapter allows us to keep these fields outside of the
* {@link Appointment} class and keep the layout computations' complexity
* away from the user.
*
* @author Brad Rydzewski
*/
public class AppointmentAdapter {
private Appointment appointment;
private int cellStart;
private int cellSpan;
private int columnStart = -1;
private int columnSpan;
private int appointmentStart;
private int appointmentEnd;
private float cellPercentFill;
private float cellPercentStart;
private List<TimeBlock> intersectingBlocks;
private float top;
private float left;
private float width;
private float height;
public float getTop() {
return top;
}
public void setTop(float top) {
this.top = top;
}
public float getLeft() {
return left;
}
public void setLeft(float left) {
this.left = left;
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
}
public AppointmentAdapter(Appointment appointment) {
this.appointment = appointment;
this.appointmentStart = minutesSinceDayStarted(appointment.getStart());
this.appointmentEnd = minutesSinceDayStarted(appointment.getEnd());
this.intersectingBlocks = new ArrayList<TimeBlock>();
}
public int getCellStart() {
return cellStart;
}
public void setCellStart(int cellStart) {
this.cellStart = cellStart;
}
public int getCellSpan() {
return cellSpan;
}
public void setCellSpan(int cellSpan) {
this.cellSpan = cellSpan;
}
public int getColumnStart() {
return columnStart;
}
public void setColumnStart(int columnStart) {
this.columnStart = columnStart;
}
public int getColumnSpan() {
return columnSpan;
}
public void setColumnSpan(int columnSpan) {
this.columnSpan = columnSpan;
}
public int getAppointmentStart() {
return appointmentStart;
}
public void setAppointmentStart(int appointmentStart) {
this.appointmentStart = appointmentStart;
}
public int getAppointmentEnd() {
return appointmentEnd;
}
public void setAppointmentEnd(int appointmentEnd) {
this.appointmentEnd = appointmentEnd;
}
public List<TimeBlock> getIntersectingBlocks() {
return intersectingBlocks;
}
public void setIntersectingBlocks(List<TimeBlock> intersectingBlocks) {
this.intersectingBlocks = intersectingBlocks;
}
public Appointment getAppointment() {
return appointment;
}
public float getCellPercentFill() {
return cellPercentFill;
}
public void setCellPercentFill(float cellPercentFill) {
this.cellPercentFill = cellPercentFill;
}
public float getCellPercentStart() {
return cellPercentStart;
}
public void setCellPercentStart(float cellPercentStart) {
this.cellPercentStart = cellPercentStart;
}
}

View File

@ -0,0 +1,241 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.google.gwt.event.dom.client.HasAllMouseHandlers;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
public class AppointmentWidget extends FlowPanel {
class Div extends ComplexPanel implements HasAllMouseHandlers {
public Div() {
setElement(DOM.createDiv());
}
public void add(Widget w) {
super.add(w, getElement());
}
public HandlerRegistration addMouseDownHandler(MouseDownHandler handler) {
return addDomHandler(handler, MouseDownEvent.getType());
}
public HandlerRegistration addMouseUpHandler(MouseUpHandler handler) {
return addDomHandler(handler, MouseUpEvent.getType());
}
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return addDomHandler(handler, MouseMoveEvent.getType());
}
public HandlerRegistration addMouseWheelHandler(
MouseWheelHandler handler) {
return addDomHandler(handler, MouseWheelEvent.getType());
}
}
private String title;
private String description;
private Date start;
private Date end;
private boolean selected;
private float top;
private float left;
private float width;
private float height;
private Widget headerPanel = new Div();
private Panel bodyPanel = new SimplePanel();
private Widget footerPanel = new Div();
private Panel timelinePanel = new SimplePanel();
private Panel timelineFillPanel = new SimplePanel();
private boolean multiDay = false;
private Appointment appointment;
public AppointmentWidget() {
this.setStylePrimaryName("gwt-appointment");
headerPanel.setStylePrimaryName("header");
bodyPanel.setStylePrimaryName("body");
footerPanel.setStylePrimaryName("footer");
timelinePanel.setStylePrimaryName("timeline");
timelineFillPanel.setStylePrimaryName("timeline-fill");
this.add(headerPanel);
this.add(bodyPanel);
this.add(footerPanel);
this.add(timelinePanel);
timelinePanel.add(timelineFillPanel);
DOM.setStyleAttribute(this.getElement(), "position", "absolute");
}
public Widget getBody() {
return this.bodyPanel;
}
public Widget getHeader() {
return this.headerPanel;
}
public Date getStart() {
return start;
}
public void setStart(Date start) {
this.start = start;
}
public Date getEnd() {
return end;
}
public void setEnd(Date end) {
this.end = end;
}
public boolean isSelected() {
return selected;
}
public float getTop() {
return top;
}
public void setTop(float top) {
this.top = top;
DOM.setStyleAttribute(this.getElement(), "top", top + "px");
}
public float getLeft() {
return left;
}
public void setLeft(float left) {
this.left = left;
DOM.setStyleAttribute(this.getElement(), "left", left + "%");
}
public float getWidth() {
return width;
}
public void setWidth(float width) {
this.width = width;
DOM.setStyleAttribute(this.getElement(), "width", width + "%");
}
public float getHeight() {
return height;
}
public void setHeight(float height) {
this.height = height;
DOM.setStyleAttribute(this.getElement(), "height", height + "px");
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
DOM.setInnerHTML(headerPanel.getElement(), title);
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
DOM.setInnerHTML(bodyPanel.getElement(), description);
}
public void formatTimeline(float top, float height) {
timelineFillPanel.setHeight(height + "%");
DOM.setStyleAttribute(timelineFillPanel.getElement(), "top", top + "%");
}
public int compareTo(AppointmentWidget appt) {
// -1 0 1
// less, equal, greater
int compare = this.getStart().compareTo(appt.getStart());
if (compare == 0) {
compare = appt.getEnd().compareTo(this.getEnd());
}
return compare;
}
public Widget getMoveHandle() {
return headerPanel;
}
public Widget getResizeHandle() {
return footerPanel;
}
public boolean isMultiDay() {
return multiDay;
}
public void setMultiDay(boolean isMultiDay) {
this.multiDay = isMultiDay;
}
public Appointment getAppointment() {
return appointment;
}
public void setAppointment(Appointment appointment) {
this.appointment = appointment;
if (appointment.isReadOnly()) {
this.remove(footerPanel);
}
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
public class DayViewBody extends Composite {
private FlexTable layout = new FlexTable();
private ScrollPanel scrollPanel = new ScrollPanel();
private DayViewTimeline timeline = null;
private DayViewGrid grid = null;
private HasSettings settings = null;
public void add(Widget w) {
scrollPanel.add(w);
}
public ScrollPanel getScrollPanel() {
return scrollPanel;
}
public DayViewGrid getGrid() {
return grid;
}
public DayViewTimeline getTimeline() {
return timeline;
}
public DayViewGrid getDayViewGrid() {
return grid;
}
public DayViewTimeline getDayViewTimeline() {
return timeline;
}
public DayViewBody(HasSettings settings) {
initWidget(scrollPanel);
this.settings = settings;
this.timeline = new DayViewTimeline(settings);
this.grid = new DayViewGrid(settings);
scrollPanel.setStylePrimaryName("scroll-area");
DOM.setStyleAttribute(scrollPanel.getElement(), "overflowX",
"hidden");
DOM.setStyleAttribute(scrollPanel.getElement(), "overflowY",
"scroll");
// create the calendar body layout table
// calendarBodyLayoutTable.setStyleName("scroll-area");
layout.setCellPadding(0);
layout.setBorderWidth(0);
layout.setCellSpacing(0);
layout.getColumnFormatter().setWidth(1, "99%");
// set vertical alignment
VerticalAlignmentConstant valign = HasVerticalAlignment.ALIGN_TOP;
layout.getCellFormatter().setVerticalAlignment(0, 0, valign);
layout.getCellFormatter().setVerticalAlignment(0, 1, valign);
grid.setStyleName("gwt-appointment-panel");
//TODO: use CSS to set table layout
layout.getCellFormatter().setWidth(0, 0, "50px");
DOM.setStyleAttribute(layout.getElement(), "tableLayout", "fixed");
layout.setWidget(0, 0, timeline);
layout.setWidget(0, 1, grid);
scrollPanel.add(layout);
}
public void setDays(Date date, int days) {
grid.build(settings.getSettings().getWorkingHourStart(),
settings.getSettings().getWorkingHourEnd(), days);
}
}

View File

@ -0,0 +1,105 @@
package com.bradrydzewski.gwt.calendar.client.techview;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.FormattingUtil;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
/**
* The DayGrid draws the grid that displays days / time intervals in the
* body of the calendar.
*
* @author Brad Rydzewski
*/
public class DayViewGrid /*Impl*/ extends Composite {
class Div extends ComplexPanel {
public Div() {
setElement(DOM.createDiv());
}
@Override
public boolean remove(Widget w) {
return super.remove(w);
}
@Override
public void add(Widget w) {
super.add(w, getElement());
}
}
protected AbsolutePanel grid = new AbsolutePanel();
protected SimplePanel gridOverlay = new SimplePanel();
private HasSettings settings = null;
private static final int HOURS_PER_DAY = 24;
public DayViewGrid(HasSettings settings) { //was DayViewGridImpl
initWidget(grid);
this.settings = settings;
}
public void build(int workingHourStart, int workingHourStop, int days) {
grid.clear();
int intervalsPerHour = settings.getSettings().getIntervalsPerHour(); //2; //30 minute intervals
float intervalSize = settings.getSettings().getPixelsPerInterval();
this.setHeight((intervalsPerHour * (intervalSize) * 24) + "px");
float dayWidth = 100f / days;
float dayLeft = 0f;
int dayStartsAt = settings.getSettings().getDayStartsAt();
for (int i = 0; i < HOURS_PER_DAY; i++) {
boolean isWorkingHours = ((i + dayStartsAt) >= workingHourStart && (i + dayStartsAt) <= workingHourStop);
//create major interval
SimplePanel sp1 = new SimplePanel();
sp1.setStyleName("major-time-interval");
sp1.setHeight(intervalSize + FormattingUtil.getBorderOffset() + "px");
//if working hours set
if (isWorkingHours) {
sp1.addStyleName("working-hours");
}
//add to body
grid.add(sp1);
for (int x = 0; x < intervalsPerHour - 1; x++) {
SimplePanel sp2 = new SimplePanel();
sp2.setStyleName("minor-time-interval");
sp2.setHeight(intervalSize + FormattingUtil.getBorderOffset() + "px");
if (isWorkingHours) {
sp2.addStyleName("working-hours");
}
grid.add(sp2);
}
}
for (int day = 0; day < days; day++) {
dayLeft = dayWidth * day;
SimplePanel dayPanel = new SimplePanel();
dayPanel.setStyleName("day-separator");
grid.add(dayPanel);
DOM.setStyleAttribute(dayPanel.getElement(), "left", dayLeft + "%");
}
gridOverlay.setHeight("100%");
gridOverlay.setWidth("100%");
DOM.setStyleAttribute(gridOverlay.getElement(), "position", "absolute");
DOM.setStyleAttribute(gridOverlay.getElement(), "left", "0px");
DOM.setStyleAttribute(gridOverlay.getElement(), "top", "0px");
grid.add(gridOverlay);
}
}

View File

@ -0,0 +1,225 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.Calendar;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.CalendarFormat;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionEvent;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionHandler;
import com.bradrydzewski.gwt.calendar.client.event.HasDaySelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.HasWeekSelectionHandlers;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionEvent;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionHandler;
import com.bradrydzewski.gwt.calendar.client.util.WindowUtils;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
public class DayViewHeader extends Composite implements HasWeekSelectionHandlers<Date>, HasDaySelectionHandlers<Date> {
private FlexTable header = new FlexTable();
private VerticalPanel timePanel = new VerticalPanel();
private AbsolutePanel dayPanel = new AbsolutePanel();
private AbsolutePanel weekPanel = new AbsolutePanel();
private AbsolutePanel splitter = new AbsolutePanel();
private static final String GWT_CALENDAR_HEADER_STYLE = "gwt-calendar-header";
private static final String DAY_CELL_CONTAINER_STYLE = "day-cell-container";
private static final String WEEK_CELL_CONTAINER_STYLE = "week-cell-container";
private static final String YEAR_CELL_STYLE = "year-cell";
private static final String SPLITTER_STYLE = "splitter";
private final boolean showWeekNumbers;
private final HasSettings settings;
public DayViewHeader(HasSettings settings) {
initWidget(header);
this.settings = settings;
header.setStyleName(GWT_CALENDAR_HEADER_STYLE);
dayPanel.setStyleName(DAY_CELL_CONTAINER_STYLE);
weekPanel.setStyleName(WEEK_CELL_CONTAINER_STYLE);
timePanel.setWidth("100%");
showWeekNumbers = settings.getSettings().isShowingWeekNumbers();
header.insertRow(0);
header.insertRow(0);
header.insertCell(0, 0);
header.insertCell(0, 0);
header.insertCell(0, 0);
header.setWidget(0, 1, timePanel);
header.getCellFormatter().setStyleName(0, 0, YEAR_CELL_STYLE);
header.getCellFormatter().setWidth(0, 2,
WindowUtils.getScrollBarWidth(true) + "px");
header.getFlexCellFormatter().setColSpan(1, 0, 3);
header.setCellPadding(0);
header.setBorderWidth(0);
header.setCellSpacing(0);
if (showWeekNumbers) {
timePanel.add(weekPanel);
}
timePanel.add(dayPanel);
splitter.setStylePrimaryName(SPLITTER_STYLE);
header.setWidget(1, 0, splitter);
}
public void setDays(Date date, int days) {
dayPanel.clear();
weekPanel.clear();
float dayWidth = 100f / days;
float dayLeft;
int week = DateUtils.calendarWeekIso(date);
int previousDayWeek = week;
Date previousDate = date;
float weekWidth = 0f;
float weekLeft = 0f;
for (int i = 0; i < days; i++) {
// set the left position of the day splitter to
// the width * incremented value
dayLeft = dayWidth * i;
// increment the date by 1
if (i > 0) {
DateUtils.moveOneDayForward(date);
} else {
// initialize the week values
weekLeft = dayLeft;
weekWidth = dayWidth;
}
String headerTitle = CalendarFormat.INSTANCE.getDateFormat().format(date);
Label dayLabel = new Label();
dayLabel.setStylePrimaryName("day-cell");
dayLabel.setWidth(dayWidth + "%");
dayLabel.setText(headerTitle);
DOM.setStyleAttribute(dayLabel.getElement(), "left", dayLeft + "%");
addDayClickHandler(dayLabel, (Date) date.clone());
boolean found = false;
for (Date day : settings.getSettings().getHolidays()) {
if (DateUtils.areOnTheSameDay(day, date)) {
dayLabel.setStyleName("day-cell-holiday");
found = true;
break;
}
}
// set the style of the header to show that it is today
if (DateUtils.areOnTheSameDay(new Date(), date)) {
dayLabel.setStyleName("day-cell-today");
} else if (!found && DateUtils.isWeekend(date)) {
dayLabel.setStyleName("day-cell-weekend");
}
if (showWeekNumbers) {
week = DateUtils.calendarWeekIso(date);
boolean lastDay = i + 1 == days;
if ((previousDayWeek != week) || lastDay) {
if (lastDay) {
previousDayWeek = week;
previousDate = date;
}
String weekTitle = "W " + previousDayWeek;
Label weekLabel = new Label();
weekLabel.setStylePrimaryName("week-cell");
weekLabel.setWidth(weekWidth + "%");
weekLabel.setText(weekTitle);
DOM.setStyleAttribute(weekLabel.getElement(), "left", weekLeft + "%");
addWeekClickHandler(weekLabel, previousDate);
weekPanel.add(weekLabel);
weekWidth = dayWidth;
weekLeft = dayLeft + dayWidth;
} else {
weekWidth += dayWidth;
}
previousDayWeek = week;
previousDate = date;
}
dayPanel.add(dayLabel);
}
}
public void setYear(Date date) {
setYear(DateUtils.year(date));
}
public void setYear(int year) {
header.setText(0, 0, String.valueOf(year));
}
public void setFuckingDayOfYear(Date date) {
}
private void addDayClickHandler(final Label dayLabel, final Date day) {
dayLabel.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
fireSelectedDay(day);
}
});
}
private void addWeekClickHandler(final Label weekLabel, final Date day) {
weekLabel.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
fireSelectedWeek(day);
}
});
}
private void fireSelectedDay(final Date day) {
DaySelectionEvent.fire(this, day);
}
private void fireSelectedWeek(final Date day) {
WeekSelectionEvent.fire(this, day);
}
public HandlerRegistration addWeekSelectionHandler(
WeekSelectionHandler<Date> handler) {
return addHandler(handler, WeekSelectionEvent.getType());
}
public HandlerRegistration addDaySelectionHandler(
DaySelectionHandler<Date> handler) {
return addHandler(handler, DaySelectionEvent.getType());
}
}

View File

@ -0,0 +1,108 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.Date;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.WindowUtils;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.SimplePanel;
/**
* A widget to render multi-day appointments in a day view.
*
* @author Brad Rydzewski
*/
public class DayViewMultiDayBody extends Composite {
private FlexTable header = new FlexTable();
protected AbsolutePanel grid = new AbsolutePanel();
private AbsolutePanel splitter = new AbsolutePanel();
private static final String TIMELINE_EMPTY_CELL_STYLE = "leftEmptyCell";
private static final String SCROLLBAR_EMPTY_CELL_STYLE = "rightEmptyCell";
private static final String DAY_CONTAINER_CELL_STYLE = "centerDayContainerCell";
private static final String SPLITTER_STYLE = "splitter";
protected SimplePanel gridOverlay = new SimplePanel();
public DayViewMultiDayBody(HasSettings settings) {
initWidget(header);
this.header.setStyleName("multiDayBody");
this.setWidth("100%");
/* insert two rows ... first row holds multi-day appointments,
* second row is just a splitter */
header.insertRow(0);
header.insertRow(0);
//insert 3 cells
//1st cell is empty to align with the timeline
//2nd cell holds appointments
//3rd cell is empty, aligns with scrollbar
header.insertCell(0, 0);
header.insertCell(0, 0);
header.insertCell(0, 0);
//add panel to hold appointments
header.setWidget(0, 1, grid);
//set cell styles
header.getCellFormatter().setStyleName(0, 0, TIMELINE_EMPTY_CELL_STYLE);
header.getCellFormatter().setStyleName(0, 1, DAY_CONTAINER_CELL_STYLE);
header.getCellFormatter().setStyleName(0, 2, SCROLLBAR_EMPTY_CELL_STYLE);
header.getCellFormatter().setWidth(0, 2,
WindowUtils.getScrollBarWidth(true) + "px");
//default grid to 50px height
grid.setHeight("30px");
header.getFlexCellFormatter().setColSpan(1, 0, 3);
header.setCellPadding(0);
header.setBorderWidth(0);
header.setCellSpacing(0);
splitter.setStylePrimaryName(SPLITTER_STYLE);
header.setWidget(1, 0, splitter);
}
public void setDays(Date date, int days) {
grid.clear();
float dayWidth = 100f / days;
float dayLeft;
for (int day = 0; day < days; day++) {
dayLeft = dayWidth * day;
SimplePanel dayPanel = new SimplePanel();
dayPanel.setStyleName("day-separator");
grid.add(dayPanel);
DOM.setStyleAttribute(dayPanel.getElement(), "left", dayLeft + "%");
}
gridOverlay.setHeight("100%");
gridOverlay.setWidth("100%");
DOM.setStyleAttribute(gridOverlay.getElement(), "position", "absolute");
DOM.setStyleAttribute(gridOverlay.getElement(), "left", "0px");
DOM.setStyleAttribute(gridOverlay.getElement(), "top", "0px");
grid.add(gridOverlay);
}
}

View File

@ -0,0 +1,108 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import com.bradrydzewski.gwt.calendar.client.CalendarFormat;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.FormattingUtil;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
/**
* A sequential display of the hours in a day. Each
* hour label should visually line up to a cell in the DayGrid.
*
* @author Brad Rydzewski
*/
public class DayViewTimeline extends Composite {
private AbsolutePanel timelinePanel = new AbsolutePanel();
private HasSettings settings = null;
private static final String TIME_LABEL_STYLE = "hour-label";
public DayViewTimeline(HasSettings settings) {
initWidget(timelinePanel);
timelinePanel.setStylePrimaryName("time-strip");
this.settings = settings;
prepare();
}
public final void prepare() {
timelinePanel.clear();
float labelHeight = settings.getSettings().getIntervalsPerHour()
* settings.getSettings().getPixelsPerInterval();
int i = 0;
if (settings.getSettings().isOffsetHourLabels()) {
i = 1;
SimplePanel sp = new SimplePanel();
sp.setHeight((labelHeight / 2) + "px");
timelinePanel.add(sp);
}
int dayStartsAt = settings.getSettings().getDayStartsAt();
while (i < CalendarFormat.HOURS_IN_DAY) {
int index = i + dayStartsAt;
if (index >= CalendarFormat.HOURS_IN_DAY) {
index = index - CalendarFormat.HOURS_IN_DAY;
}
String hour = CalendarFormat.INSTANCE.getHourLabels()[index];
index++;
i++;
//block
SimplePanel hourWrapper = new SimplePanel();
hourWrapper.setStylePrimaryName(TIME_LABEL_STYLE);
hourWrapper.setHeight((labelHeight + FormattingUtil.getBorderOffset()) + "px");
FlowPanel flowPanel = new FlowPanel();
flowPanel.setStyleName("hour-layout");
String amPm = " ";
if (index < 13)
amPm += CalendarFormat.INSTANCE.getAm();
else if (index > 13)
amPm += CalendarFormat.INSTANCE.getPm();
else {
if (CalendarFormat.INSTANCE.isUseNoonLabel()) {
hour = CalendarFormat.INSTANCE.getNoon();
amPm = "";
} else {
amPm += CalendarFormat.INSTANCE.getPm();
}
}
Label hourLabel = new Label(hour);
hourLabel.setStylePrimaryName("hour-text");
flowPanel.add(hourLabel);
Label amPmLabel = new Label(amPm);
amPmLabel.setStylePrimaryName("ampm-text");
flowPanel.add(amPmLabel);
hourWrapper.add(flowPanel);
timelinePanel.add(hourWrapper);
}
}
}

View File

@ -0,0 +1,571 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandler;
import com.allen_sauer.gwt.dnd.client.DragStartEvent;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.DayViewDropController;
import com.allen_sauer.gwt.dnd.client.drop.DayViewPickupDragController;
import com.allen_sauer.gwt.dnd.client.drop.DayViewResizeController;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.CalendarSettings.Click;
import com.bradrydzewski.gwt.calendar.client.CalendarView;
import com.bradrydzewski.gwt.calendar.client.CalendarWidget;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.event.DaySelectionHandler;
import com.bradrydzewski.gwt.calendar.client.event.WeekSelectionHandler;
import com.bradrydzewski.gwt.calendar.client.util.AppointmentUtil;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
public class TechView extends CalendarView {
private DayViewHeader dayViewHeader = null;
private DayViewBody dayViewBody = null;
private DayViewMultiDayBody multiViewBody = null;
private TechViewLayoutStrategy layoutStrategy = null;
private final List<AppointmentWidget> appointmentWidgets = new ArrayList<AppointmentWidget>();
/**
* List of AppointmentAdapter objects that represent the currently selected
* appointment.
*/
private List<AppointmentWidget> selectedAppointmentWidgets = new ArrayList<AppointmentWidget>();
private final TechViewStyleManager styleManager = GWT.create(TechViewStyleManager.class);
private DayViewResizeController resizeController = null;
private DayViewDropController dropController = null;
private PickupDragController dragController = null;
private DayViewResizeController proxyResizeController = null;
public TechView() {
super();
}
private int getMaxProxyHeight() {
// For a comfortable use, the Proxy should be, top 2/3 (66%) of the view
return (2 * (dayViewBody.getScrollPanel().getOffsetHeight() / 3));
}
@SuppressWarnings("deprecation")
public void doLayout() {
// PERFORM APPOINTMENT LAYOUT NOW
final Date date = (Date) calendarWidget.getDate().clone();
if (getSettings().isMultidayVisible()) {
multiViewBody.setDays((Date) date.clone(), calendarWidget.getDays());
}
dayViewHeader.setDays((Date) date.clone(), calendarWidget.getDays());
dayViewHeader.setYear((Date) date.clone());
dayViewBody.setDays((Date) date.clone(), calendarWidget.getDays());
dayViewBody.getTimeline().prepare();
dropController.setColumns(calendarWidget.getDays());
dropController.setIntervalsPerHour(calendarWidget.getSettings().getIntervalsPerHour());
dropController.setDayStartsAt(getSettings().getDayStartsAt());
dropController.setDate((Date)calendarWidget.getDate().clone());
dropController.setSnapSize(
calendarWidget.getSettings().getPixelsPerInterval());
dropController.setMaxProxyHeight(getMaxProxyHeight());
resizeController.setIntervalsPerHour(
calendarWidget.getSettings().getIntervalsPerHour());
resizeController.setDayStartsAt(getSettings().getDayStartsAt());
resizeController.setSnapSize(
calendarWidget.getSettings().getPixelsPerInterval());
proxyResizeController.setSnapSize(calendarWidget.getSettings().getPixelsPerInterval());
proxyResizeController.setIntervalsPerHour(calendarWidget.getSettings().getIntervalsPerHour());
proxyResizeController.setDayStartsAt(getSettings().getDayStartsAt());
this.selectedAppointmentWidgets.clear();
appointmentWidgets.clear();
// HERE IS WHERE WE DO THE LAYOUT
Date startDate = (Date) calendarWidget.getDate().clone();
Date endDate = (Date) calendarWidget.getDate().clone();
endDate.setDate(endDate.getDate() + 1);
DateUtils.resetTime(startDate);
DateUtils.resetTime(endDate);
startDate.setHours(startDate.getHours());
endDate.setHours(endDate.getHours());
for (int i = 0; i < calendarWidget.getDays(); i++) {
List<Appointment> filteredList = AppointmentUtil
.filterListByDate(calendarWidget.getAppointments(), startDate, endDate);
// perform layout
List<AppointmentAdapter> appointmentAdapters = layoutStrategy
.doLayout(filteredList, i, calendarWidget.getDays());
// add all appointments back to the grid
addAppointmentsToGrid(appointmentAdapters, false);
startDate.setDate(startDate.getDate() + 1);
endDate.setDate(endDate.getDate() + 1);
}
List<Appointment> filteredList =
AppointmentUtil.filterListByDateRange(calendarWidget.getAppointments(),
calendarWidget.getDate(), calendarWidget.getDays());
ArrayList<AppointmentAdapter> adapterList = new ArrayList<AppointmentAdapter>();
int desiredHeight = layoutStrategy.doMultiDayLayout(filteredList,
adapterList, calendarWidget.getDate(), calendarWidget.getDays());
if (getSettings().isMultidayVisible()) {
multiViewBody.grid.setHeight(desiredHeight + "px");
addAppointmentsToGrid(adapterList, true);
}
}
/**
* Adds the Appointments to the view.
* @param appointmentList List of Appointments
* @param addToMultiView <code>true</code> if is adding the appointments to the multiview section, <code>false</code> otherwise
*/
private void addAppointmentsToGrid(final List<AppointmentAdapter> appointmentList, final boolean addToMultiView) {
for (AppointmentAdapter appt : appointmentList) {
AppointmentWidget panel = new AppointmentWidget();
panel.setWidth(appt.getWidth());
panel.setHeight(appt.getHeight());
panel.setTitle(appt.getAppointment().getTitle());
panel.setTop(appt.getTop());
panel.setLeft(appt.getLeft());
panel.setAppointment(appt.getAppointment());
boolean selected = calendarWidget.isTheSelectedAppointment(panel
.getAppointment());
if (selected) {
selectedAppointmentWidgets.add(panel);
}
styleManager.applyStyle(panel, selected);
appointmentWidgets.add(panel);
if (addToMultiView) {
panel.setMultiDay(true);
this.multiViewBody.grid.add(panel);
} else {
panel.setDescription(appt.getAppointment().getDescription());
dayViewBody.getGrid().grid.add(panel);
//make footer 'draggable'
if (calendarWidget.getSettings().isEnableDragDrop() && !appt.getAppointment().isReadOnly()) {
resizeController.makeDraggable(panel.getResizeHandle());
dragController.makeDraggable(panel, panel.getMoveHandle());
}
}
}
}
@Override
public void scrollToHour(final int hour) {
dayViewBody.getScrollPanel().setVerticalScrollPosition((hour - getSettings().getDayStartsAt()) *
getSettings().getIntervalsPerHour() * getSettings().getPixelsPerInterval());
}
public void doSizing() {
if (calendarWidget.getOffsetHeight() > 0) {
if (getSettings().isMultidayVisible()) {
dayViewBody.setHeight(calendarWidget.getOffsetHeight() - 2
- dayViewHeader.getOffsetHeight()
- multiViewBody.getOffsetHeight() + "px");
} else {
dayViewBody.setHeight(calendarWidget.getOffsetHeight() - 2
- dayViewHeader.getOffsetHeight() + "px");
}
}
}
public void onDeleteKeyPressed() {
if (calendarWidget.getSelectedAppointment() != null) {
calendarWidget.fireDeleteEvent(calendarWidget.getSelectedAppointment());
}
}
public void onDoubleClick(Element element, Event event) {
List<AppointmentWidget> list = findAppointmentWidgetsByElement(element);
if (!list.isEmpty()) {
Appointment appt = list.get(0).getAppointment();
//if (appt.equals(calendarWidget.getSelectedAppointment()))
calendarWidget.fireOpenEvent(appt);
// exit out
} else if (getSettings().getTimeBlockClickNumber() == Click.Double
&& element == dayViewBody.getGrid().gridOverlay.getElement()) {
int x = DOM.eventGetClientX(event) + Window.getScrollLeft();
int y = DOM.eventGetClientY(event) + Window.getScrollTop();
timeBlockClick(x, y);
}
}
public void onSingleClick(final Element element, final Event event) {
// Ignore the scroll panel
if (dayViewBody.getScrollPanel().getElement().equals(element)) {
return;
}
Appointment appointment = findAppointmentByElement(element);
if (appointment != null) {
selectAppointment(appointment);
} else if ((getSettings().getTimeBlockClickNumber() == Click.Single
|| getSettings().getTimeBlockClickNumber() == Click.Drag)
&& element == dayViewBody.getGrid().gridOverlay
.getElement()) {
int x = DOM.eventGetClientX(event) + Window.getScrollLeft();
int y = DOM.eventGetClientY(event) + Window.getScrollTop();
timeBlockClick(x, y);
}
}
public void onMouseOver(final Element element, final Event event) {
Appointment appointment = findAppointmentByElement(element);
calendarWidget.fireMouseOverEvent(appointment, element);
}
@Override
public void onAppointmentSelected(Appointment appointment) {
List<AppointmentWidget> clickedAppointmentAdapters =
findAppointmentWidget(appointment);
if (!clickedAppointmentAdapters.isEmpty()) {
for (AppointmentWidget adapter : selectedAppointmentWidgets) {
styleManager.applyStyle(adapter, false);
}
for (AppointmentWidget adapter : clickedAppointmentAdapters) {
styleManager.applyStyle(adapter, true);
}
selectedAppointmentWidgets.clear();
selectedAppointmentWidgets = clickedAppointmentAdapters;
float height = clickedAppointmentAdapters.get(0).getHeight();
// scrollIntoView ONLY if the appointment fits in the viewable area
if (dayViewBody.getScrollPanel().getOffsetHeight() > height) {
DOM.scrollIntoView(clickedAppointmentAdapters.get(0).getElement());
}
}
}
public void onRightArrowKeyPressed() {
calendarWidget.selectNextAppointment();
}
public void onUpArrowKeyPressed() {
calendarWidget.selectPreviousAppointment();
}
public void onDownArrowKeyPressed() {
calendarWidget.selectNextAppointment();
}
public void onLeftArrowKeyPressed() {
calendarWidget.selectPreviousAppointment();
}
@Override
public String getStyleName() {
return "gwt-cal";
}
@Override
public void attach(CalendarWidget widget) {
super.attach(widget);
if (dayViewBody == null) {
dayViewBody = new DayViewBody(this);
dayViewHeader = new DayViewHeader(this);
layoutStrategy = new TechViewLayoutStrategy(this);
if (getSettings().isMultidayVisible()) {
multiViewBody = new DayViewMultiDayBody(this);
}
}
calendarWidget.getRootPanel().add(dayViewHeader);
if (getSettings().isMultidayVisible()) {
calendarWidget.getRootPanel().add(multiViewBody);
}
calendarWidget.getRootPanel().add(dayViewBody);
if (getSettings() != null) {
scrollToHour(getSettings().getScrollToHour());
}
// Creates the different Controllers, if needed
createDragController();
createDropController();
createResizeController();
}
private void createDragController() {
if (dragController == null) {
dragController = new DayViewPickupDragController(dayViewBody.getGrid().grid, false);
dragController.setBehaviorDragProxy(true);
dragController.setBehaviorDragStartSensitivity(1);
dragController.setBehaviorConstrainedToBoundaryPanel(true); //do I need these?
dragController.setConstrainWidgetToBoundaryPanel(true); //do I need these?
dragController.setBehaviorMultipleSelection(false);
dragController.addDragHandler(new DragHandler(){
public void onDragEnd(DragEndEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireUpdateEvent(appt);
}
public void onDragStart(DragStartEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
calendarWidget.setRollbackAppointment(appt.clone());
((DayViewPickupDragController)dragController).setMaxProxyHeight(getMaxProxyHeight());
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {
}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {
}
});
}
}
private void createDropController() {
if (dropController==null) {
dropController = new DayViewDropController(dayViewBody.getGrid().grid);
dragController.registerDropController(dropController);
}
}
private void createResizeController() {
if (resizeController == null) {
resizeController = new DayViewResizeController(dayViewBody.getGrid().grid);
resizeController.addDragHandler(new DragHandler(){
public void onDragEnd(DragEndEvent event) {
Appointment appt = ((AppointmentWidget) event.getContext().draggable.getParent()).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireUpdateEvent(appt);
}
public void onDragStart(DragStartEvent event) {
calendarWidget
.setRollbackAppointment(((AppointmentWidget) event
.getContext().draggable.getParent()).getAppointment()
.clone());
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {}
});
}
if(proxyResizeController == null) {
proxyResizeController = new DayViewResizeController(dayViewBody.getGrid().grid);
proxyResizeController.addDragHandler(new DragHandler(){
long startTime = 0L;
int initialX = 0;
int initialY = 0;
Date startDate;
public void onDragEnd(DragEndEvent event) {
long clickTime = System.currentTimeMillis() - startTime;
int y = event.getContext().mouseY;
if (clickTime <= 500 && initialY == y) {
calendarWidget.fireTimeBlockClickEvent(startDate);
} else {
Appointment appt = ((AppointmentWidget) event.getContext().draggable.getParent()).getAppointment();
calendarWidget.setCommittedAppointment(appt);
calendarWidget.fireCreateEvent(appt);
}
}
public void onDragStart(DragStartEvent event) {
startTime = System.currentTimeMillis();
initialX = event.getContext().mouseX;
initialY = event.getContext().mouseY;
startDate = getCoordinatesDate(initialX, initialY);
calendarWidget.setRollbackAppointment(null);
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {}
});
}
}
@SuppressWarnings("deprecation")
private Date getCoordinatesDate(int x, int y) {
int left = dayViewBody.getGrid().gridOverlay.getAbsoluteLeft();
int top = dayViewBody.getScrollPanel().getAbsoluteTop();
int width = dayViewBody.getGrid().gridOverlay.getOffsetWidth();
int scrollOffset = dayViewBody.getScrollPanel().getVerticalScrollPosition();
// x & y are based on screen position,need to get x/y relative to
// component
int relativeY = y - top + scrollOffset;
int relativeX = x - left;
// find the interval clicked and day clicked
double interval = Math.floor(relativeY
/ (double) getSettings().getPixelsPerInterval());
double day = Math.floor((double) relativeX
/ ((double) width / (double) calendarWidget.getDays()));
// create new appointment date based on click
Date newStartDate = calendarWidget.getDate();
newStartDate.setHours(getSettings().getDayStartsAt());
newStartDate.setMinutes(0);
newStartDate.setSeconds(0);
newStartDate.setMinutes((int) interval
* (60 / getSettings().getIntervalsPerHour()));
newStartDate.setDate(newStartDate.getDate() + (int) day);
return newStartDate;
}
private void timeBlockClick(int x, int y) {
int left = dayViewBody.getGrid().gridOverlay.getAbsoluteLeft();
int top = dayViewBody.getScrollPanel().getAbsoluteTop();
int width = dayViewBody.getGrid().gridOverlay.getOffsetWidth();
int scrollOffset = dayViewBody.getScrollPanel().getVerticalScrollPosition();
// x & y are based on screen position,need to get x/y relative to
// component
int relativeY = y - top + scrollOffset;
int relativeX = x - left;
// find the interval clicked and day clicked
double day = Math.floor((double) relativeX
/ ((double) width / (double) calendarWidget.getDays()));
Date newStartDate = getCoordinatesDate(x, y);
if (getSettings().getTimeBlockClickNumber() != Click.Drag) {
calendarWidget.fireTimeBlockClickEvent(newStartDate);
} else {
int snapSize = calendarWidget.getSettings().getPixelsPerInterval();
// Create the proxy
width = width / calendarWidget.getDays();
left = (int) day * width;
// Adjust the start to the closest interval
top = (int)Math.floor(
(float) relativeY / snapSize) * snapSize;
AppointmentWidget proxy = new AppointmentWidget();
Appointment app = new Appointment();
app.setStart(newStartDate);
app.setEnd(newStartDate);
proxy.setAppointment(app);
proxy.setStart(newStartDate);
proxy.setPixelSize(width, /*height*/ snapSize);
dayViewBody.getGrid().grid.add(proxy, left, top);
styleManager.applyStyle(proxy, false);
proxyResizeController.makeDraggable(proxy.getResizeHandle());
NativeEvent evt = Document.get().createMouseDownEvent(1, 0, 0, x, y, false,
false, false, false, NativeEvent.BUTTON_LEFT);
proxy.getResizeHandle().getElement().dispatchEvent(evt);
}
}
private List<AppointmentWidget> findAppointmentWidgetsByElement(
Element element) {
return findAppointmentWidget(findAppointmentByElement(element));
}
/**
* Returns the {@link Appointment} indirectly associated to the passed
* <code>element</code>. Each Appointment drawn on the CalendarView maps to
* a Widget and therefore an Element. This method attempts to find an
* Appointment based on the provided Element. If no match is found a null
* value is returned.
*
* @param element
* Element to look up.
* @return Appointment matching the element.
*/
private Appointment findAppointmentByElement(Element element) {
Appointment appointmentAtElement = null;
for (AppointmentWidget widget : appointmentWidgets) {
if (DOM.isOrHasChild(widget.getElement(), element)) {
appointmentAtElement = widget.getAppointment();
break;
}
}
return appointmentAtElement;
}
/**
* Finds any related adapters that match the given Appointment.
*
* @param appt
* Appointment to match.
* @return List of related AppointmentWidget objects.
*/
private List<AppointmentWidget> findAppointmentWidget(Appointment appt) {
ArrayList<AppointmentWidget> appointmentAdapters = new ArrayList<AppointmentWidget>();
if (appt != null) {
for (AppointmentWidget widget : appointmentWidgets) {
if (widget.getAppointment().equals(appt)) {
appointmentAdapters.add(widget);
}
}
}
return appointmentAdapters;
}
public HandlerRegistration addWeekSelectionHandler(
WeekSelectionHandler<Date> handler) {
return dayViewHeader.addWeekSelectionHandler(handler);
}
public HandlerRegistration addDaySelectionHandler(
DaySelectionHandler<Date> handler) {
return dayViewHeader.addDaySelectionHandler(handler);
}
}

View File

@ -0,0 +1,440 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
import com.bradrydzewski.gwt.calendar.client.HasSettings;
import com.bradrydzewski.gwt.calendar.client.util.AppointmentUtil;
/**
* Responsible for arranging all Appointments, visually, on a screen in a manner
* similar to the Microsoft Outlook / Windows Vista calendar.
* See: <img src='http://www.microsoft.com/library/media/1033/athome/images/moredone/calendar.gif'/>
* <p>
* Note how overlapping appointments are displayed in the provided image
*
* @author Brad Rydzewski
* @version 1.0 6/07/09
* @since 1.0
*/
public class TechViewLayoutStrategy {
private static final int MINUTES_PER_HOUR = 60;
private static final int HOURS_PER_DAY = 24;
private final HasSettings settings;
public TechViewLayoutStrategy(final HasSettings settings) {
this.settings = settings;
}
public List<AppointmentAdapter> doLayout(List<Appointment> appointments, int dayIndex, int dayCount) {
int intervalsPerHour = settings.getSettings().getIntervalsPerHour(); //30 minute intervals
float intervalSize = settings.getSettings().getPixelsPerInterval(); //25 pixels per interval
/*
* Note: it is important that all appointments are sorted by Start date
* (asc) and Duration (desc) for this algorithm to work. If that is not
* the case, it won't work, at all!! Maybe this is a problem that needs
* to be addressed
*/
// set to 30 minutes. this means there will be 48 cells. 60min / 30min
// interval * 24
//int minutesPerInterval = 30;
// interval size, set to 100px
//float sizeOfInterval = 25f;
// a calendar can view multiple days at a time. sets number of visible
// days
// TODO: use this later, not currently implemented
// float numberOfDays = dates.size();
int minutesPerInterval = MINUTES_PER_HOUR / intervalsPerHour;
// get number of cells (time blocks)
int numberOfTimeBlocks = MINUTES_PER_HOUR / minutesPerInterval * HOURS_PER_DAY;
TimeBlock[] timeBlocks = new TimeBlock[numberOfTimeBlocks];
for (int i = 0; i < numberOfTimeBlocks; i++) {
TimeBlock t = new TimeBlock();
t.setStart(i * minutesPerInterval);
t.setEnd(t.getStart() + minutesPerInterval);
t.setOrder(i);
t.setTop((float) i * intervalSize);
t.setBottom(t.getTop() + intervalSize);
timeBlocks[i] = t;
}
// each appointment will get "wrapped" in an appoinetment cell object,
// so that we can assign it a location in the grid, row and
// column span, etc.
ArrayList<AppointmentAdapter> appointmentCells = new ArrayList<AppointmentAdapter>();
// Map<TimeBlock,TimeBlock> blockGroup = new
// HashMap<TimeBlock,TimeBlock>();
int groupMaxColumn = 0; // track total columns here! this will reset
// when a group completes
int groupStartIndex = -1;
int groupEndIndex = -2;
// Question: how to distinguish start / finish of a new group?
// Answer: when endCell of previous appointments < startCell of new
// appointment
// for each appointments, we need to see if it intersects with each time
// block
for (Appointment appointment : appointments) {
TimeBlock startBlock = null;
TimeBlock endBlock = null;
// if(blockGroupEndCell)
// wrap appointment with AppointmentInterface Cell and add to list
AppointmentAdapter apptCell = new AppointmentAdapter(appointment);
appointmentCells.add(apptCell);
// get the first time block in which the appointment should appear
// TODO: since appointments are sorted, we should never need to
// re-evaluate a time block that had zero matches...
// store the index of the currently evaluated time block, if no
// match, increment
// that will prevent the same block from ever being re-evaluated
// after no match found
for (TimeBlock block : timeBlocks) {
// does the appointment intersect w/ the block???
if (block.intersectsWith(apptCell)) {
// we found one! set as start block and exit loop
startBlock = block;
// blockGroup.put(block, block);
if (groupEndIndex < startBlock.getOrder()) {
//System.out.println(" prior group max cols: "
// + groupMaxColumn);
for (int i = groupStartIndex; i <= groupEndIndex; i++) {
TimeBlock tb = timeBlocks[i];
tb.setTotalColumns(groupMaxColumn + 1);
//System.out.println(" total col set for block: "
// + i);
}
groupStartIndex = startBlock.getOrder();
//System.out.println("new group at: " + groupStartIndex);
groupMaxColumn = 0;
}
break;
} else {
// here is where I would increment, as per above to-do
}
}
// add the appointment to the start block
startBlock.getAppointments().add(apptCell);
// add block to appointment
apptCell.getIntersectingBlocks().add(startBlock);
// set the appointments column, if it has not already been set
// if it has been set, we need to get it for reference later on in
// this method
// int column = startBlock.getFirstAvailableColumn();
int column = appointment.getColumnId();
apptCell.setColumnStart(column);
apptCell.setColumnSpan(1); // hard-code to 1, for now
// we track the max column for a time block
// if a column get's added make sure we increment
// if (startBlock.getTotalColumns() <= column) {
// startBlock.setTotalColumns(column+1);
// }
// add column to block's list of occupied columns, so that the
// column cannot be given to another appointment
startBlock.getOccupiedColumns().put(column, column);
// sets the start cell of the appt to the current block
// we can do this since the blocks are ordered ascending
apptCell.setCellStart(startBlock.getOrder());
// go through all subsequent blocks...
// find intersections
for (int i = startBlock.getOrder() + 1; i < timeBlocks.length; i++) {
// get the nextTimeBlock
TimeBlock nextBlock = timeBlocks[i];
// exit look if end date > block start, since no subsequent
// blocks will ever intersect
// if (apptCell.getAppointmentEnd() > nextBlock.getStart()) {
// break; //does appt intersect with this block?
// }
if (nextBlock.intersectsWith(apptCell)) {
// yes! add appointment to the block
// register start column
nextBlock.getAppointments().add(apptCell);
nextBlock.getOccupiedColumns().put(column, column);
endBlock = nextBlock; // this may change if intersects with
// next block
// add block to appointments list of intersecting blocks
apptCell.getIntersectingBlocks().add(nextBlock);
// we track the max column for a time block
// if a column get's added make sure we increment
// if (nextBlock.getTotalColumns() <= column) {
// nextBlock.setTotalColumns(column+1);
// }
// blockGroup.put(nextBlock, nextBlock);
}
}
// if end block was never set, use the start block
endBlock = (endBlock == null) ? startBlock : endBlock;
// maybe here is the "end" of a group, where we then evaluate max
// column
if (column > groupMaxColumn) {
groupMaxColumn = column;
// System.out.println(" max col: " + groupMaxColumn);
}
if (groupEndIndex < endBlock.getOrder()) {
groupEndIndex = endBlock.getOrder();
//System.out.println(" end index (re)set: " + groupEndIndex);
}
// for(int i = groupStartIndex; i<=groupEndIndex; i++) {
// timeBlocks[i].setTotalColumns(groupMaxColumn);
// }
// groupMaxColumn=1;
// }
// for(TimeBlock timeBlock : blockGroup.values()) {
//
// }
// blockGroup = new HashMap<TimeBlock,TimeBlock>();
// set the appointments cell span (top to bottom)
apptCell.setCellSpan(endBlock.getOrder() - startBlock.getOrder() + 1);
}
for (int i = groupStartIndex; i <= groupEndIndex; i++) {
TimeBlock tb = timeBlocks[i];
tb.setTotalColumns(groupMaxColumn + 1);
//System.out.println(" total col set for block: " + i);
}
// we need to know the MAX number of cells for each time block.
// so unfortunately we have to go back through the list to find this out
/*
* for(AppointmentCell apptCell : appointmentCells) {
*
* for (TimeBlock block : apptCell.getIntersectingBlocks()) {
*
* int maxCol = 0;
*
* //find the max cell for (AppointmentCell apptCell :
* block.getAppointments()) { int col = apptCell.getColumnStart(); if
* (col > maxCol) { maxCol = col; } }
*
* block.setTotalColumns(maxCol+1); } }
*/
//last step is to calculate the adjustment reuired for 'multi-day' / multi-column
float widthAdj = 1f / dayCount;
float paddingLeft = .5f;
float paddingRight = .5f;
float paddingBottom = 2;
// now that everything has been assigned a cell, column and spans
// we can calculate layout
// Note: this can only be done after every single appointment has
// been assigned a position in the grid
for (AppointmentAdapter apptCell : appointmentCells) {
float width = 1f / (float) apptCell.getIntersectingBlocks().get(0).getTotalColumns() * 100;
float left = (float) apptCell.getColumnStart() / (float) apptCell.getIntersectingBlocks().get(0).getTotalColumns() * 100;
//AppointmentInterface appt = apptCell.getAppointment();
apptCell.setTop((float) apptCell.getCellStart() * intervalSize); // ok!
apptCell.setLeft((widthAdj * 100 * dayIndex) + (left * widthAdj) + paddingLeft); // ok
apptCell.setWidth(width * widthAdj - paddingLeft - paddingRight); // ok!
apptCell.setHeight((float) apptCell.getIntersectingBlocks().size() * ((float) intervalSize) - paddingBottom); // ok!
float apptStart = apptCell.getAppointmentStart();
float apptEnd = apptCell.getAppointmentEnd();
float blockStart = timeBlocks[apptCell.getCellStart()].getStart();
float blockEnd = timeBlocks[apptCell.getCellStart() + apptCell.getCellSpan() - 1].getEnd();
float blockDuration = blockEnd - blockStart;
float apptDuration = apptEnd - apptStart;
float timeFillHeight = apptDuration / blockDuration * 100f;
float timeFillStart = (apptStart - blockStart) / blockDuration * 100f;
// System.out.println("apptStart: "+apptStart);
// System.out.println("apptEnd: "+apptEnd);
// System.out.println("blockStart: "+blockStart);
// System.out.println("blockEnd: "+blockEnd);
// System.out.println("timeFillHeight: "+timeFillHeight);
//System.out.println("timeFillStart: "+timeFillStart);
//System.out.println("------------");
apptCell.setCellPercentFill(timeFillHeight);
apptCell.setCellPercentStart(timeFillStart);
//appt.formatTimeline(apptCell.getCellPercentStart(), apptCell.getCellPercentFill());
}
return appointmentCells;
}
public int doMultiDayLayout(List<Appointment> appointments, List<AppointmentAdapter> adapters, Date start, int days) {
//create array to hold all appointments for a particular day
//HashMap<Date, ArrayList<AppointmentAdapter>> appointmentDayMap
// = new HashMap<Date, ArrayList<AppointmentAdapter>>();
//for a particular day need to track all used rows
HashMap<Integer, HashMap<Integer, Integer>> daySlotMap = new HashMap<Integer, HashMap<Integer, Integer>>();
int minHeight = 30;
int maxRow = 0;
//convert appointment to adapter
for (Appointment appointment : appointments) {
adapters.add(new AppointmentAdapter(appointment));
}
//create array of dates
ArrayList<Date> dateList = new ArrayList<Date>();
Date tempStartDate = (Date)start.clone();
// tempStartDate.setHours(0);
// tempStartDate.setMinutes(0);
// tempStartDate.setSeconds(0);
for (int i = 0; i < days; i++) {
Date d = (Date) tempStartDate.clone();
DateUtils.resetTime(d);
//appointmentDayMap.put(d, new ArrayList<AppointmentAdapter>());
daySlotMap.put(i, new HashMap<Integer, Integer>());
dateList.add(d);
DateUtils.moveOneDayForward(tempStartDate);
}
//add appointments to each day
for (AppointmentAdapter adapter : adapters) {
int columnSpan = 0; //number of columns spanned
boolean isStart = true; //indicates if current column is appointment start column
//set column & span
for (int i = 0; i < dateList.size(); i++) {
Date date = dateList.get(i);
boolean isWithinRange =
AppointmentUtil.rangeContains(
adapter.getAppointment(), date);
//System.out.println(" isWithinRange == " + isWithinRange + " for start: " + adapter.getAppointment().getStart() + " end: " + adapter.getAppointment().getEnd() + " date: "+date);
//while we are at it, we can set the adapters start column
// and colun span
if (isWithinRange) {
//appointmentDayMap.get(date).add(adapter);
if (isStart) {
adapter.setColumnStart(i);
isStart = false;
}
adapter.setColumnSpan(columnSpan);
columnSpan++;
}
}
//now we set the row, which cannot be more than total # of appointments
for (int x = 0; x < adapters.size(); x++) {
boolean isRowOccupied = false;
for (int y = adapter.getColumnStart(); y <= adapter.getColumnStart() + adapter.getColumnSpan(); y++) {
try{
HashMap<Integer, Integer> rowMap = daySlotMap.get(y);
if (rowMap.containsKey(x)) {
isRowOccupied = true;
//System.out.println(" row [" + x+"] is occupied for day [" + y+"]");
} else {
//isRowOccupied = false;
break; //break out of loop, nothing found in row slot
}
}catch(Exception ex) {
//System.out.println("Exception: y=" + y + " x=" + x + " adapters.size=" + adapters.size() + " start="+adapter.getAppointment().getStart() + " end="+adapter.getAppointment().getEnd().toString());
}
}
if (!isRowOccupied) {
//add row to maps
for (int y = adapter.getColumnStart(); y <= adapter.getColumnStart() + adapter.getColumnSpan(); y++) {
HashMap<Integer, Integer> rowMap = daySlotMap.get(y);
rowMap.put(x, x);
if (x > maxRow) {
maxRow = x;
}
//System.out.println(" > added "+ x + " to row list for column " + y);
}
//set the row (also named cell)
adapter.setCellStart(x);
//break loop
//now we set the appointment's location
//Appointment appt = adapter.getAppointment();
float top = adapter.getCellStart() * 25f + 5f;
float width = ((float) adapter.getColumnSpan() + 1f) / days * 100f - 1f; //10f=padding
float left = ((float) adapter.getColumnStart()) / days * 100f + .5f; //10f=padding
//float left = (float) adapter.getColumnStart() / (float) apptCell.getIntersectingBlocks().get(0).getTotalColumns() * 100;
adapter.setWidth(width);
adapter.setLeft(left);
adapter.setTop(top);
adapter.setHeight(20);
//System.out.println("set appointment [" + appt.getTitle() + "] layout left: " + left + " top: " + top + " width: " + width);
break;
}
}
//System.out.println("multi-day layout -- title: \"" + adapter.getAppointment().getTitle() + "\" | col start: " + adapter.getColumnStart() + " | colspan: " + adapter.getColumnSpan() + " | row: " + adapter.getCellStart() + " | start date: " + adapter.getAppointment().getStart() + " | end date: " + adapter.getAppointment().getEnd());
}
int height = (maxRow + 1) * 25 + 5;
return Math.max(height, minHeight);
}
}

View File

@ -0,0 +1,80 @@
package com.bradrydzewski.gwt.calendar.client.techview;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.ThemeAppointmentStyle;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
public abstract class TechViewStyleManager {
protected static final String APPOINTMENT_STYLE = "dv-appointment";
protected static final String APPOINTMENT_STYLE_SELECTED = "-selected";
protected static final String APPOINTMENT_STYLE_MULTIDAY = "-multiday";
protected static final String BACKGROUND_COLOR_STYLE_ATTRIBUTE = "backgroundColor";
protected static final String BACKGROUND_IMAGE_STYLE_ATTRIBUTE = "backgroundImage";
protected static final String BORDER_COLOR_STYLE_ATTRIBUTE = "borderColor";
protected static final String COLOR_STYLE_ATTRIBUTE = "color";
public void applyStyle(AppointmentWidget widget, boolean selected) {
doApplyStyleInternal(widget, selected);
}
protected abstract ThemeAppointmentStyle getViewAppointmentStyleForTheme(Appointment appointment);
protected abstract ThemeAppointmentStyle getDefaultViewAppointmentStyleForTheme();
private void doApplyStyleInternal(AppointmentWidget widget, boolean selected) {
// Extract the Appointment for later reference
Appointment appointment = widget.getAppointment();
// Extract the DOM Element for later reference
Element elem = widget.getElement();
Element bodyElem = widget.getBody().getElement();
Element headerElem = widget.getHeader().getElement();
// Is MultiDay?
boolean multiDay = appointment.isMultiDay() || appointment.isAllDay();
//Lookup the style from the map
ThemeAppointmentStyle style = getViewAppointmentStyleForTheme(appointment);
//Determine Style Name
String styleName = APPOINTMENT_STYLE;
if(multiDay) styleName+=APPOINTMENT_STYLE_MULTIDAY;
if(selected) styleName+=APPOINTMENT_STYLE_SELECTED;
widget.setStylePrimaryName(styleName);
//If no style is found, apply the default blue style
//TODO: need to check for a custom style
if(style==null)
style = getDefaultViewAppointmentStyleForTheme();
if (multiDay)
DOM.setStyleAttribute(elem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackgroundHeader());
else
DOM.setStyleAttribute(elem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackground());
DOM.setStyleAttribute(elem, BORDER_COLOR_STYLE_ATTRIBUTE, style.getBackgroundHeader());
DOM.setStyleAttribute(bodyElem, COLOR_STYLE_ATTRIBUTE, style.getSelectedBorder());
DOM.setStyleAttribute(headerElem, COLOR_STYLE_ATTRIBUTE, style.getHeaderText());
DOM.setStyleAttribute(headerElem, BACKGROUND_COLOR_STYLE_ATTRIBUTE, style.getBackgroundHeader());
if (multiDay)
return;
if (selected && style.getSelectedBackgroundImage() != null) {
DOM.setStyleAttribute(elem, BACKGROUND_IMAGE_STYLE_ATTRIBUTE, "url("+ style.getSelectedBackgroundImage() + ")");
} else {
DOM.setStyleAttribute(elem, BACKGROUND_IMAGE_STYLE_ATTRIBUTE, "none");
}
}
}

View File

@ -0,0 +1,148 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.techview;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Represents a block of time that contains one or many Appointments. An example
* of a timeblock could be 10am - 10:30 am, where a block of time is 30 minutes.
* <p> A block of time can contain or be interested by one or many appointments.
* Using the above example, a time block from 10am - 10:30 am could contain the
* following appointments:
* <ul>
* <li>10:00 - 10:30, where start / end are same as time block</li>
* <li>9:00 - 10:30, where the appointment starts before the time block
* starts but ends at same time as time block</li>
* <li>9:00 - 10:20, where appointment start before
* and ends after start of time block</li>
* <li>9:00 - 11:00, where appointment starts before and ends after the time
* block does</li>
* <li>10:05 - 10:25, where appointment starts after but ends before the
* time block</li> </ul></p>
* Above
* are example use cases of a time block and how it relates to an appointment.
* Note that an appointment can intersect with one ore many time blocks. This
* class holds a number of parameters allowing a time block to manage its
* appointments and determine where the appointments should be arranged within
* itself.
*
* @author Brad Rydzewski
*/
public class TimeBlock {
private List<AppointmentAdapter> appointments = new ArrayList<AppointmentAdapter>();
private Map<Integer, Integer> occupiedColumns = new HashMap<Integer, Integer>();
private int totalColumns = 1;
private int order;
private String name;
private int start;
private int end;
private float top;
private float bottom;
public List<AppointmentAdapter> getAppointments() {
return appointments;
}
public Map<Integer, Integer> getOccupiedColumns() {
return occupiedColumns;
}
public int getFirstAvailableColumn() {
int col = 0;
while (true) {
if (occupiedColumns.containsKey(col))
col++;
else return col;
}
}
public int getTotalColumns() {
return totalColumns;
}
public void setTotalColumns(int totalColumns) {
this.totalColumns = totalColumns;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public int getEnd() {
return end;
}
public void setEnd(int end) {
this.end = end;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public float getBottom() {
return bottom;
}
public void setBottom(float bottom) {
this.bottom = bottom;
}
public float getTop() {
return top;
}
public void setTop(float top) {
this.top = top;
}
public boolean intersectsWith(int apptStart, int apptEnd) {
//scenario 1: start date of appt between start and end of block
if (apptStart >= this.getStart() && apptStart < this.getEnd())
return true;
//scenario 2: end date of appt > block start date &
// start date of appt before block start date
return apptEnd > this.getStart() && apptStart < this.getStart();
}
public boolean intersectsWith(AppointmentAdapter appt) {
return intersectsWith(appt.getAppointmentStart(),
appt.getAppointmentEnd());
}
}

View File

@ -0,0 +1,114 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2010 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.client.util;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.bradrydzewski.gwt.calendar.client.Appointment;
import com.bradrydzewski.gwt.calendar.client.DateUtils;
/**
* Utility class for several operations involving time and {@link Appointment}
* objects.
*
* @author Brad Rydzewski
* @author Carlos D. Morales
*/
public class AppointmentUtil {
public static List<Appointment> filterListByDateRange(List<Appointment> fullList, Date date,
int days) {
ArrayList<Appointment> group = new ArrayList<Appointment>();
Date startDate = (Date) date.clone();
DateUtils.resetTime(startDate);
Date endDate = DateUtils.shiftDate(date, days);
for (Appointment appointment : fullList) {
if ((appointment.isMultiDay() || appointment.isAllDay())
&& rangeContains(appointment, startDate, endDate)) {
group.add(appointment);
}
}
return group;
}
@SuppressWarnings("deprecation")
public static boolean rangeContains(Appointment appt, Date date) {
Date rangeEnd = (Date) date.clone();
rangeEnd.setDate(rangeEnd.getDate() + 1);
DateUtils.resetTime(rangeEnd);
return rangeContains(appt, date, rangeEnd);
}
/**
* Indicates whether the specified <code>appointment</code> falls within the
* date range defined by <code>rangeStart</code> and <code>rangeEnd</code>.
*
* @param appointment The appointment to test
* @param rangeStart The range lower limit
* @param rangeEnd The range upper limit
* @return <code>true</code> if the appointment's date falls within the
* range, <code>false</code> otherwise.
*/
public static boolean rangeContains(Appointment appointment,
Date rangeStart, Date rangeEnd) {
long apptStartMillis = appointment.getStart().getTime();
long apptEndMillis = appointment.getEnd().getTime();
long rangeStartMillis = rangeStart.getTime();
long rangeEndMillis = rangeEnd.getTime();
return apptStartMillis >= rangeStartMillis
&& apptStartMillis < rangeEndMillis
|| apptStartMillis <= rangeStartMillis
&& apptEndMillis >= rangeStartMillis;
}
/**
* Filters a list of appointments and returns only appointments with a start
* date equal to the date provided. FYI - I hate everything about this
* method and am pissed off I have to use it. May be able to avoid it in the
* future
*
* @param fullList A full set of <code>Appointment</code>s, that will be filtered
* with the above described rule
* @param startDate The start date
* @return A list with all appointments whose start date is on or after the
* passed <code>startDate</code>
*/
public static List<Appointment> filterListByDate(List<Appointment> fullList, Date startDate, Date endDate) {
ArrayList<Appointment> group = new ArrayList<Appointment>();
for (Appointment appointment : fullList) {
if (!appointment.isMultiDay() && !appointment.isAllDay() &&
appointment.getEnd().before(endDate)) {
//TODO: probably can shorten this by using the compareTo method
if (appointment.getStart().after(startDate) ||
appointment.getStart().equals(startDate)) {
group.add(appointment);
}
}
}
return group;
}
}

View File

@ -0,0 +1,30 @@
package com.bradrydzewski.gwt.calendar.client.util;
import com.bradrydzewski.gwt.calendar.client.util.impl.FormattingImpl;
import com.google.gwt.core.client.GWT;
/**
* This utility provides access to data to help format widgets
* correctly across browsers.
* @author Brad Rydzewski
*/
public class FormattingUtil {
/**
* Implementation of formatting class. Holds browser-specific
* values, loaded by GWT deferred binding.
*/
private static FormattingImpl impl = GWT.create(FormattingImpl.class);
/**
* All CSS2 compliant browsers count the border height in the
* overall height of an Element. This method returns an offset
* value that should be added to the height or width of an item
* before setting its size. This will ensure consistent sizing
* across compliant and non-compliant browsers.
* @return
*/
public static int getBorderOffset() {
return impl.getBorderOffset();
}
}

View File

@ -0,0 +1,85 @@
package com.bradrydzewski.gwt.calendar.client.util;
import com.google.gwt.user.client.Window.Navigator;
/**
* Provides a set of re-usable methods related to the client's
* browser window.
*
* @author Brad Rydzewski
*/
public class WindowUtils {
/**
* Width in pixels of Client's scroll bar.
*/
private static int scrollBarWidth;
/**
* Gets the width of the client's scroll bar.
*
* @param useCachedValue Indicates if cached value should be used, or refreshed.
* @return Width, in pixels, of Client's scroll bar
*/
public static int getScrollBarWidth(boolean useCachedValue) {
/*
* OSX Lion doesn't show the scrollbars (ala iOS) which causes cosmetic problems: issue 143
*/
boolean isOSXLion = Navigator.getUserAgent().contains("Mac OS X 10.7") || Navigator.getUserAgent().contains("Mac OS X 10_7");
if (isOSXLion && Navigator.getUserAgent().contains("Safari")) {
// 0 seems to cause weird effects in Chrome
return 1;
}
if (useCachedValue && scrollBarWidth > 0) {
return scrollBarWidth;
}
/* sometimes getScrollBarWidth() temporarily returns a negative
* number. So when this happens we will return "17"
* which seems to be default on many systems...
* but we won't save it as a cached value.
*/
int tmpScrollBarWidth = getScrollBarWidth();
if (tmpScrollBarWidth <= 0) {
return 17;
}
scrollBarWidth = tmpScrollBarWidth;
return scrollBarWidth;
}
/**
* Calculates the width of the clients scroll bar, which can vary among operating systems,
* browsers and themes. Based on code from: http://www.alexandre-gomes.com/?p=115
*
* @return The width of the browser scrollbar in pixels
*/
private static native int getScrollBarWidth() /*-{
var inner = document.createElement("p");
inner.style.width = "100%";
inner.style.height = "200px";
var outer = document.createElement("div");
outer.style.position = "absolute";
outer.style.top = "0px";
outer.style.left = "0px";
outer.style.visibility = "hidden";
outer.style.width = "200px";
outer.style.height = "150px";
outer.style.overflow = "hidden";
outer.appendChild (inner);
document.body.appendChild (outer);
var w1 = inner.offsetWidth;
outer.style.overflow = "scroll";
var w2 = inner.offsetWidth;
if (w1 == w2) w2 = outer.clientWidth;
document.body.removeChild (outer);
return (w1 - w2);
}-*/;
}

View File

@ -0,0 +1,9 @@
package com.bradrydzewski.gwt.calendar.client.util.impl;
public class FormattingIE6Impl extends FormattingImpl {
@Override
public int getBorderOffset() {
return 0;
}
}

View File

@ -0,0 +1,8 @@
package com.bradrydzewski.gwt.calendar.client.util.impl;
public class FormattingImpl {
public int getBorderOffset() {
return -1;
}
}

View File

@ -0,0 +1,15 @@
<module>
<stylesheet src="gwt-cal-google.css"/>
<!-- <replace-with class="com.bradrydzewski.gwt.calendar.theme.google.client.GoogleAppointmentTheme">-->
<!-- <when-type-is class="com.bradrydzewski.gwt.calendar.client.AppointmentTheme"/>-->
<!-- </replace-with>-->
<replace-with class="com.bradrydzewski.gwt.calendar.theme.google.client.GoogleMonthViewStyleManager">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.monthview.MonthViewStyleManager"/>
</replace-with>
<replace-with class="com.bradrydzewski.gwt.calendar.theme.google.client.GoogleDayViewStyleManager">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.dayview.DayViewStyleManager"/>
</replace-with>
<replace-with class="com.bradrydzewski.gwt.calendar.theme.google.client.GoogleTechViewStyleManager">
<when-type-is class="com.bradrydzewski.gwt.calendar.client.techview.TechViewStyleManager"/>
</replace-with>
</module>

View File

@ -0,0 +1,177 @@
/*
* This file is part of gwt-cal
* Copyright (C) 2011 Scottsdale Software LLC
*
* gwt-cal 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, either version 3 of the License, or
* (at your option) any later version.
*
* 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, see <http://www.gnu.org/licenses/
*/
package com.bradrydzewski.gwt.calendar.theme.google.client;
import com.bradrydzewski.gwt.calendar.client.ThemeAppointmentStyle;
class GoogleAppointmentStyle implements ThemeAppointmentStyle {
public GoogleAppointmentStyle(String border, String background) {
super();
//set the border
this.border = background;
this.selectedBorder = border;
//set the body text
this.text = "#FFFFFF";
this.selectedText = text;
//set the header text
this.headerText = text;
this.selectedHeaderText = text;
//set the background colors
this.background = background;
this.selectedBackground = background;
//set the header colors to the same color as the border
this.backgroundHeader = border;
this.selectedBackgroundHeader = border;
}
protected String selectedBorder;
protected String selectedBackground;
protected String selectedBackgroundImage;
protected String selectedBackgroundHeader;
protected String selectedBackgroundFooter;
protected String selectedText;
protected String selectedHeaderText;
protected String border;
protected String background;
protected String backgroundImage;
protected String backgroundHeader;
protected String backgroundFooter;
protected String text;
protected String headerText;
public String getSelectedBorder() {
return selectedBorder;
}
public String getSelectedBackground() {
return selectedBackground;
}
public String getSelectedBackgroundHeader() {
return selectedBackgroundHeader;
}
public String getSelectedBackgroundFooter() {
return selectedBackgroundFooter;
}
public String getSelectedText() {
return selectedText;
}
public String getSelectedHeaderText() {
return selectedHeaderText;
}
public String getBorder() {
return border;
}
public String getBackground() {
return background;
}
public String getBackgroundHeader() {
return backgroundHeader;
}
public String getBackgroundFooter() {
return backgroundFooter;
}
public String getText() {
return text;
}
public String getHeaderText() {
return headerText;
}
public void setSelectedBorder(String selectedBorder) {
this.selectedBorder = selectedBorder;
}
public void setSelectedBackground(String selectedBackground) {
this.selectedBackground = selectedBackground;
}
public void setSelectedBackgroundHeader(String selectedBackgroundHeader) {
this.selectedBackgroundHeader = selectedBackgroundHeader;
}
public void setSelectedBackgroundFooter(String selectedBackgroundFooter) {
this.selectedBackgroundFooter = selectedBackgroundFooter;
}
public void setSelectedText(String selectedText) {
this.selectedText = selectedText;
}
public void setSelectedHeaderText(String selectedHeaderText) {
this.selectedHeaderText = selectedHeaderText;
}
public void setBorder(String border) {
this.border = border;
}
public void setBackground(String background) {
this.background = background;
}
public void setBackgroundHeader(String backgroundHeader) {
this.backgroundHeader = backgroundHeader;
}
public void setBackgroundFooter(String backgroundFooter) {
this.backgroundFooter = backgroundFooter;
}
public void setText(String text) {
this.text = text;
}
public void setHeaderText(String headerText) {
this.headerText = headerText;
}
public String getSelectedBackgroundImage() {
return selectedBackgroundImage;
}
public String getBackgroundImage() {
return backgroundImage;
}
public void setSelectedBackgroundImage(String selectedBackgroundImage) {
this.selectedBackgroundImage = selectedBackgroundImage;
}
public void setBackgroundImage(String backgroundImage) {
this.backgroundImage = backgroundImage;
}
}

View File

@ -0,0 +1,60 @@
package com.bradrydzewski.gwt.calendar.theme.google.client;
import java.util.HashMap;
import java.util.Map;
import com.bradrydzewski.gwt.calendar.client.AppointmentStyle;
final public class GoogleAppointmentTheme {
//border, background
public static final GoogleAppointmentStyle BLUE = new GoogleAppointmentStyle("#2952A3", "#668CD9");
public static final GoogleAppointmentStyle RED = new GoogleAppointmentStyle("#A32929", "#D96666");
public static final GoogleAppointmentStyle PINK = new GoogleAppointmentStyle("#B1365F", "#E67399");
public static final GoogleAppointmentStyle PURPLE = new GoogleAppointmentStyle("#7A367A", "#B373B3");
public static final GoogleAppointmentStyle DARK_PURPLE = new GoogleAppointmentStyle("#5229A3", "#8C66D9");
public static final GoogleAppointmentStyle STEELE_BLUE = new GoogleAppointmentStyle("#29527A", "#29527A");
public static final GoogleAppointmentStyle LIGHT_BLUE = new GoogleAppointmentStyle("#1B887A", "#59BFB3");
public static final GoogleAppointmentStyle TEAL = new GoogleAppointmentStyle("#28754E", "#65AD89");
public static final GoogleAppointmentStyle LIGHT_TEAL = new GoogleAppointmentStyle("#4A716C", "#85AAA5");
public static final GoogleAppointmentStyle GREEN = new GoogleAppointmentStyle("#0D7813", "#4CB052");
public static final GoogleAppointmentStyle LIGHT_GREEN = new GoogleAppointmentStyle("#528800", "#8CBF40");
public static final GoogleAppointmentStyle YELLOW_GREEN = new GoogleAppointmentStyle("#88880E", "#BFBF4D");
public static final GoogleAppointmentStyle YELLOW = new GoogleAppointmentStyle("#AB8B00", "#E0C240");
public static final GoogleAppointmentStyle ORANGE = new GoogleAppointmentStyle("#BE6D00", "#F2A640");
public static final GoogleAppointmentStyle RED_ORANGE = new GoogleAppointmentStyle("#B1440E", "#E6804D");
public static final GoogleAppointmentStyle LIGHT_BROWN = new GoogleAppointmentStyle("#865A5A", "#BE9494");
public static final GoogleAppointmentStyle LIGHT_PURPLE = new GoogleAppointmentStyle("#705770", "#A992A9");
public static final GoogleAppointmentStyle GREY = new GoogleAppointmentStyle("#4E5D6C", "#8997A5");
public static final GoogleAppointmentStyle BLUE_GREY = new GoogleAppointmentStyle("#5A6986", "#94A2bE");
public static final GoogleAppointmentStyle YELLOW_GREY = new GoogleAppointmentStyle("#6E6E41", "#A7A77D");
public static final GoogleAppointmentStyle BROWN = new GoogleAppointmentStyle("#8D6F47", "#C4A883");
public static final GoogleAppointmentStyle DEFAULT = BLUE;
public static final Map<AppointmentStyle, GoogleAppointmentStyle> STYLES = new HashMap<AppointmentStyle, GoogleAppointmentStyle>();
static {
STYLES.put(AppointmentStyle.BLUE, BLUE);
STYLES.put(AppointmentStyle.BLUE_GREY, BLUE_GREY);
STYLES.put(AppointmentStyle.BROWN, BROWN);
STYLES.put(AppointmentStyle.DARK_PURPLE, DARK_PURPLE);
STYLES.put(AppointmentStyle.GREEN, GREEN);
STYLES.put(AppointmentStyle.GREY, GREY);
STYLES.put(AppointmentStyle.LIGHT_BLUE, LIGHT_BLUE);
STYLES.put(AppointmentStyle.LIGHT_BROWN, LIGHT_BROWN);
STYLES.put(AppointmentStyle.LIGHT_GREEN, LIGHT_GREEN);
STYLES.put(AppointmentStyle.LIGHT_PURPLE, LIGHT_PURPLE);
STYLES.put(AppointmentStyle.LIGHT_TEAL, LIGHT_TEAL);
STYLES.put(AppointmentStyle.ORANGE, ORANGE);
STYLES.put(AppointmentStyle.PINK, PINK);
STYLES.put(AppointmentStyle.PURPLE, PURPLE);
STYLES.put(AppointmentStyle.RED, RED);
STYLES.put(AppointmentStyle.RED_ORANGE, RED_ORANGE);
STYLES.put(AppointmentStyle.STEELE_BLUE, STEELE_BLUE);
STYLES.put(AppointmentStyle.TEAL, TEAL);
STYLES.put(AppointmentStyle.YELLOW, YELLOW);
STYLES.put(AppointmentStyle.YELLOW_GREEN, YELLOW_GREEN);
STYLES.put(AppointmentStyle.YELLOW_GREY, YELLOW_GREY);
STYLES.put(AppointmentStyle.DEFAULT, DEFAULT);
}
private GoogleAppointmentTheme() { }
}

Some files were not shown because too many files have changed in this diff Show More