com.vaadin.v7.client.ui.calendar.schedule.SimpleDayCell Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-compatibility-client Show documentation
Show all versions of vaadin-compatibility-client Show documentation
Vaadin 7 compatibility package for Vaadin 8
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See for the full
* license.
*/
package com.vaadin.v7.client.ui.calendar.schedule;
import java.util.Date;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
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.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.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.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.FocusableFlowPanel;
import com.vaadin.v7.client.ui.VCalendar;
import com.vaadin.v7.shared.ui.calendar.DateConstants;
/**
* A class representing a single cell within the calendar in month-view.
*
* @since 7.1
* @author Vaadin Ltd.
*/
public class SimpleDayCell extends FocusableFlowPanel implements MouseUpHandler,
MouseDownHandler, MouseOverHandler, MouseMoveHandler {
private static int bottomSpacerHeight = -1;
private static int eventHeight = -1;
private static final int BORDERPADDINGSIZE = 1;
private final VCalendar calendar;
private Date date;
private int intHeight;
private final HTML bottomspacer;
private final Label caption;
private final CalendarEvent[] events = new CalendarEvent[10];
private final int cell;
private final int row;
private boolean monthNameVisible;
private HandlerRegistration mouseUpRegistration;
private HandlerRegistration mouseDownRegistration;
private HandlerRegistration mouseOverRegistration;
private boolean monthEventMouseDown;
private boolean labelMouseDown;
private int eventCount = 0;
private int startX = -1;
private int startY = -1;
private int startYrelative;
private int startXrelative;
// "from" date of date which is source of Dnd
private Date dndSourceDateFrom;
// "to" date of date which is source of Dnd
private Date dndSourceDateTo;
// "from" time of date which is source of Dnd
private Date dndSourceStartDateTime;
// "to" time of date which is source of Dnd
private Date dndSourceEndDateTime;
private int prevDayDiff = 0;
private int prevWeekDiff = 0;
private HandlerRegistration moveRegistration;
private CalendarEvent moveEvent;
private Widget clickedWidget;
private HandlerRegistration bottomSpacerMouseDownHandler;
private boolean scrollable = false;
private MonthGrid monthGrid;
private HandlerRegistration keyDownHandler;
public SimpleDayCell(VCalendar calendar, int row, int cell) {
this.calendar = calendar;
this.row = row;
this.cell = cell;
setStylePrimaryName("v-calendar-month-day");
caption = new Label();
bottomspacer = new HTML();
bottomspacer.setStyleName("v-calendar-bottom-spacer-empty");
bottomspacer.setWidth(3 + "em");
caption.setStyleName("v-calendar-day-number");
add(caption);
add(bottomspacer);
caption.addMouseDownHandler(this);
caption.addMouseUpHandler(this);
}
@Override
public void onLoad() {
bottomSpacerHeight = bottomspacer.getOffsetHeight();
eventHeight = bottomSpacerHeight;
}
public void setMonthGrid(MonthGrid monthGrid) {
this.monthGrid = monthGrid;
}
public MonthGrid getMonthGrid() {
return monthGrid;
}
@SuppressWarnings("deprecation")
public void setDate(Date date) {
int dateOfMonth = date.getDate();
if (monthNameVisible) {
caption.setText(dateOfMonth + " "
+ calendar.getMonthNames()[date.getMonth()]);
} else {
caption.setText("" + dateOfMonth);
}
this.date = date;
}
public Date getDate() {
return date;
}
public void reDraw(boolean clear) {
setHeightPX(intHeight + BORDERPADDINGSIZE, clear);
}
/*
* Events and whole cell content are drawn by this method. By the
* clear-argument, you can choose to clear all old content. Notice that
* clearing will also remove all element's event handlers.
*/
public void setHeightPX(int px, boolean clear) {
// measure from DOM if needed
if (px < 0) {
intHeight = getOffsetHeight() - BORDERPADDINGSIZE;
} else {
intHeight = px - BORDERPADDINGSIZE;
}
// Couldn't measure height or it ended up negative. Don't bother
// continuing
if (intHeight == -1) {
return;
}
if (clear) {
while (getWidgetCount() > 1) {
remove(1);
}
}
// How many events can be shown in UI
int slots = 0;
if (scrollable) {
for (int i = 0; i < events.length; i++) {
if (events[i] != null) {
slots = i + 1;
}
}
setHeight(intHeight + "px"); // Fixed height
} else {
// Dynamic height by the content
DOM.removeElementAttribute(getElement(), "height");
slots = (intHeight - caption.getOffsetHeight() - bottomSpacerHeight)
/ eventHeight;
if (slots > 10) {
slots = 10;
}
}
updateEvents(slots, clear);
}
public void updateEvents(int slots, boolean clear) {
int eventsAdded = 0;
for (int i = 0; i < slots; i++) {
CalendarEvent e = events[i];
if (e == null) {
// Empty slot
HTML slot = new HTML();
slot.setStyleName("v-calendar-spacer");
if (!clear) {
remove(i + 1);
insert(slot, i + 1);
} else {
add(slot);
}
} else {
// Event slot
eventsAdded++;
if (!clear) {
Widget w = getWidget(i + 1);
if (!(w instanceof MonthEventLabel)) {
remove(i + 1);
insert(createMonthEventLabel(e), i + 1);
}
} else {
add(createMonthEventLabel(e));
}
}
}
int remainingSpace = intHeight - ((slots * eventHeight)
+ bottomSpacerHeight + caption.getOffsetHeight());
int newHeight = remainingSpace + bottomSpacerHeight;
if (newHeight < 0) {
newHeight = eventHeight;
}
bottomspacer.setHeight(newHeight + "px");
if (clear) {
add(bottomspacer);
}
int more = eventCount - eventsAdded;
if (more > 0) {
if (bottomSpacerMouseDownHandler == null) {
bottomSpacerMouseDownHandler = bottomspacer
.addMouseDownHandler(this);
}
bottomspacer.setStyleName("v-calendar-bottom-spacer");
bottomspacer.setText("+ " + more);
} else {
if (!scrollable && bottomSpacerMouseDownHandler != null) {
bottomSpacerMouseDownHandler.removeHandler();
bottomSpacerMouseDownHandler = null;
}
if (scrollable) {
bottomspacer.setText("[ - ]");
} else {
bottomspacer.setStyleName("v-calendar-bottom-spacer-empty");
bottomspacer.setText("");
}
}
}
private MonthEventLabel createMonthEventLabel(CalendarEvent e) {
long rangeInMillis = e.getRangeInMilliseconds();
boolean timeEvent = rangeInMillis <= DateConstants.DAYINMILLIS
&& !e.isAllDay();
Date fromDatetime = e.getStartTime();
// Create a new MonthEventLabel
MonthEventLabel eventDiv = new MonthEventLabel();
eventDiv.addStyleDependentName("month");
eventDiv.addMouseDownHandler(this);
eventDiv.addMouseUpHandler(this);
eventDiv.setCalendar(calendar);
eventDiv.setEventIndex(e.getIndex());
eventDiv.setCalendarEvent(e);
if (timeEvent) {
eventDiv.setTimeSpecificEvent(true);
if (e.getStyleName() != null) {
eventDiv.addStyleDependentName(e.getStyleName());
}
eventDiv.setCaption(e.getCaption());
eventDiv.setTime(fromDatetime);
} else {
eventDiv.setTimeSpecificEvent(false);
Date from = e.getStart();
Date to = e.getEnd();
if (!e.getStyleName().isEmpty()) {
eventDiv.addStyleName("month-event " + e.getStyleName());
} else {
eventDiv.addStyleName("month-event");
}
int fromCompareToDate = from.compareTo(date);
int toCompareToDate = to.compareTo(date);
eventDiv.addStyleDependentName("all-day");
if (fromCompareToDate == 0) {
eventDiv.addStyleDependentName("start");
eventDiv.setCaption(e.getCaption());
} else if (fromCompareToDate < 0 && cell == 0) {
eventDiv.addStyleDependentName("continued-from");
eventDiv.setCaption(e.getCaption());
}
if (toCompareToDate == 0) {
eventDiv.addStyleDependentName("end");
} else if (toCompareToDate > 0
&& (cell + 1) == getMonthGrid().getCellCount(row)) {
eventDiv.addStyleDependentName("continued-to");
}
if (e.getStyleName() != null) {
eventDiv.addStyleDependentName(e.getStyleName() + "-all-day");
}
}
return eventDiv;
}
private void setUnlimitedCellHeight() {
scrollable = true;
addStyleDependentName("scrollable");
}
private void setLimitedCellHeight() {
scrollable = false;
removeStyleDependentName("scrollable");
}
public void addCalendarEvent(CalendarEvent e) {
eventCount++;
int slot = e.getSlotIndex();
if (slot == -1) {
for (int i = 0; i < events.length; i++) {
if (events[i] == null) {
events[i] = e;
e.setSlotIndex(i);
break;
}
}
} else {
events[slot] = e;
}
}
@SuppressWarnings("deprecation")
public void setMonthNameVisible(boolean b) {
monthNameVisible = b;
int dateOfMonth = date.getDate();
caption.setText(
dateOfMonth + " " + calendar.getMonthNames()[date.getMonth()]);
}
public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) {
return addDomHandler(handler, MouseMoveEvent.getType());
}
@Override
protected void onAttach() {
super.onAttach();
mouseUpRegistration = addDomHandler(this, MouseUpEvent.getType());
mouseDownRegistration = addDomHandler(this, MouseDownEvent.getType());
mouseOverRegistration = addDomHandler(this, MouseOverEvent.getType());
}
@Override
protected void onDetach() {
mouseUpRegistration.removeHandler();
mouseDownRegistration.removeHandler();
mouseOverRegistration.removeHandler();
super.onDetach();
}
@Override
public void onMouseUp(MouseUpEvent event) {
if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
return;
}
Widget w = (Widget) event.getSource();
if (moveRegistration != null) {
Event.releaseCapture(getElement());
moveRegistration.removeHandler();
moveRegistration = null;
keyDownHandler.removeHandler();
keyDownHandler = null;
}
if (w == bottomspacer && monthEventMouseDown) {
GWT.log("Mouse up over bottomspacer");
} else if (clickedWidget instanceof MonthEventLabel
&& monthEventMouseDown) {
MonthEventLabel mel = (MonthEventLabel) clickedWidget;
int endX = event.getClientX();
int endY = event.getClientY();
int xDiff = 0, yDiff = 0;
if (startX != -1 && startY != -1) {
xDiff = startX - endX;
yDiff = startY - endY;
}
startX = -1;
startY = -1;
prevDayDiff = 0;
prevWeekDiff = 0;
if (xDiff < -3 || xDiff > 3 || yDiff < -3 || yDiff > 3) {
eventMoved(moveEvent);
} else if (calendar.getEventClickListener() != null) {
CalendarEvent e = getEventByWidget(mel);
calendar.getEventClickListener().eventClick(e);
}
moveEvent = null;
} else if (w == this) {
getMonthGrid().setSelectionReady();
} else if (w instanceof Label && labelMouseDown) {
String clickedDate = calendar.getDateFormat().format(date);
if (calendar.getDateClickListener() != null) {
calendar.getDateClickListener().dateClick(clickedDate);
}
}
monthEventMouseDown = false;
labelMouseDown = false;
clickedWidget = null;
}
@Override
public void onMouseDown(MouseDownEvent event) {
if (calendar.isDisabled()
|| event.getNativeButton() != NativeEvent.BUTTON_LEFT) {
return;
}
Widget w = (Widget) event.getSource();
clickedWidget = w;
if (w instanceof MonthEventLabel) {
// event clicks should be allowed even when read-only
monthEventMouseDown = true;
if (calendar.isEventMoveAllowed()) {
startCalendarEventDrag(event, (MonthEventLabel) w);
}
} else if (w == bottomspacer) {
if (scrollable) {
setLimitedCellHeight();
} else {
setUnlimitedCellHeight();
}
reDraw(true);
} else if (w instanceof Label) {
labelMouseDown = true;
} else if (w == this && !scrollable) {
MonthGrid grid = getMonthGrid();
if (grid.isEnabled() && calendar.isRangeSelectAllowed()) {
grid.setSelectionStart(this);
grid.setSelectionEnd(this);
}
}
event.stopPropagation();
event.preventDefault();
}
@Override
public void onMouseOver(MouseOverEvent event) {
event.preventDefault();
getMonthGrid().setSelectionEnd(this);
}
@Override
public void onMouseMove(MouseMoveEvent event) {
if (clickedWidget instanceof MonthEventLabel && !monthEventMouseDown
|| (startY < 0 && startX < 0)) {
return;
}
MonthEventLabel w = (MonthEventLabel) clickedWidget;
if (calendar.isDisabledOrReadOnly()) {
Event.releaseCapture(getElement());
monthEventMouseDown = false;
startY = -1;
startX = -1;
return;
}
int currentY = event.getClientY();
int currentX = event.getClientX();
int moveY = (currentY - startY);
int moveX = (currentX - startX);
if ((moveY < 5 && moveY > -6) && (moveX < 5 && moveX > -6)) {
return;
}
int dateCellWidth = getWidth();
int dateCellHeigth = getHeigth();
Element parent = getMonthGrid().getElement();
int relativeX = event.getRelativeX(parent);
int relativeY = event.getRelativeY(parent);
int weekDiff = 0;
if (moveY > 0) {
weekDiff = (startYrelative + moveY) / dateCellHeigth;
} else {
weekDiff = (moveY - (dateCellHeigth - startYrelative))
/ dateCellHeigth;
}
int dayDiff = 0;
if (moveX >= 0) {
dayDiff = (startXrelative + moveX) / dateCellWidth;
} else {
dayDiff = (moveX - (dateCellWidth - startXrelative))
/ dateCellWidth;
}
// Check boundaries
if (relativeY < 0
|| relativeY >= (calendar.getMonthGrid().getRowCount()
* dateCellHeigth)
|| relativeX < 0
|| relativeX >= (calendar.getMonthGrid().getColumnCount()
* dateCellWidth)) {
return;
}
GWT.log("Event moving delta: " + weekDiff + " weeks " + dayDiff
+ " days" + " (" + getCell() + "," + getRow() + ")");
CalendarEvent e = moveEvent;
if (e == null) {
e = getEventByWidget(w);
}
Date from = e.getStart();
Date to = e.getEnd();
long daysMs = dayDiff * DateConstants.DAYINMILLIS;
long weeksMs = weekDiff * DateConstants.WEEKINMILLIS;
setDates(e, from, to, weeksMs + daysMs, false);
e.setStart(from);
e.setEnd(to);
if (w.isTimeSpecificEvent()) {
Date start = new Date();
Date end = new Date();
setDates(e, start, end, weeksMs + daysMs, true);
e.setStartTime(start);
e.setEndTime(end);
} else {
e.setStartTime(new Date(from.getTime()));
e.setEndTime(new Date(to.getTime()));
}
updateDragPosition(w, dayDiff, weekDiff);
}
private void setDates(CalendarEvent e, Date start, Date end, long shift,
boolean isDateTime) {
Date currentStart;
Date currentEnd;
if (isDateTime) {
currentStart = e.getStartTime();
currentEnd = e.getEndTime();
} else {
currentStart = e.getStart();
currentEnd = e.getEnd();
}
long duration = currentEnd.getTime() - currentStart.getTime();
if (isDateTime) {
start.setTime(dndSourceStartDateTime.getTime() + shift);
} else {
start.setTime(dndSourceDateFrom.getTime() + shift);
}
end.setTime((start.getTime() + duration));
}
private void eventMoved(CalendarEvent e) {
calendar.updateEventToMonthGrid(e);
if (calendar.getEventMovedListener() != null) {
calendar.getEventMovedListener().eventMoved(e);
}
}
public void startCalendarEventDrag(MouseDownEvent event,
final MonthEventLabel w) {
moveRegistration = addMouseMoveHandler(this);
startX = event.getClientX();
startY = event.getClientY();
startYrelative = event.getRelativeY(w.getParent().getElement())
% getHeigth();
startXrelative = event.getRelativeX(w.getParent().getElement())
% getWidth();
CalendarEvent e = getEventByWidget(w);
dndSourceDateFrom = (Date) e.getStart().clone();
dndSourceDateTo = (Date) e.getEnd().clone();
dndSourceStartDateTime = (Date) e.getStartTime().clone();
dndSourceEndDateTime = (Date) e.getEndTime().clone();
Event.setCapture(getElement());
keyDownHandler = addKeyDownHandler(new KeyDownHandler() {
@Override
public void onKeyDown(KeyDownEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
cancelEventDrag(w);
}
}
});
focus();
GWT.log("Start drag");
}
protected void cancelEventDrag(MonthEventLabel w) {
if (moveRegistration != null) {
// reset position
if (moveEvent == null) {
moveEvent = getEventByWidget(w);
}
moveEvent.setStart(dndSourceDateFrom);
moveEvent.setEnd(dndSourceDateTo);
moveEvent.setStartTime(dndSourceStartDateTime);
moveEvent.setEndTime(dndSourceEndDateTime);
calendar.updateEventToMonthGrid(moveEvent);
// reset drag-related properties
Event.releaseCapture(getElement());
moveRegistration.removeHandler();
moveRegistration = null;
keyDownHandler.removeHandler();
keyDownHandler = null;
setFocus(false);
monthEventMouseDown = false;
startY = -1;
startX = -1;
moveEvent = null;
labelMouseDown = false;
clickedWidget = null;
}
}
public void updateDragPosition(MonthEventLabel w, int dayDiff,
int weekDiff) {
// Draw event to its new position only when position has changed
if (dayDiff == prevDayDiff && weekDiff == prevWeekDiff) {
return;
}
prevDayDiff = dayDiff;
prevWeekDiff = weekDiff;
if (moveEvent == null) {
moveEvent = getEventByWidget(w);
}
calendar.updateEventToMonthGrid(moveEvent);
}
public int getRow() {
return row;
}
public int getCell() {
return cell;
}
public int getHeigth() {
return intHeight + BORDERPADDINGSIZE;
}
public int getWidth() {
return getOffsetWidth() - BORDERPADDINGSIZE;
}
public void setToday(boolean today) {
if (today) {
addStyleDependentName("today");
} else {
removeStyleDependentName("today");
}
}
public boolean removeEvent(CalendarEvent targetEvent,
boolean reDrawImmediately) {
int slot = targetEvent.getSlotIndex();
if (slot < 0) {
return false;
}
CalendarEvent e = getCalendarEvent(slot);
if (targetEvent.equals(e)) {
events[slot] = null;
eventCount--;
if (reDrawImmediately) {
reDraw(moveEvent == null);
}
return true;
}
return false;
}
private CalendarEvent getEventByWidget(MonthEventLabel eventWidget) {
int index = getWidgetIndex(eventWidget);
return getCalendarEvent(index - 1);
}
public CalendarEvent getCalendarEvent(int i) {
return events[i];
}
public CalendarEvent[] getEvents() {
return events;
}
public int getEventCount() {
return eventCount;
}
public CalendarEvent getMoveEvent() {
return moveEvent;
}
public void addEmphasisStyle() {
addStyleDependentName("dragemphasis");
}
public void removeEmphasisStyle() {
removeStyleDependentName("dragemphasis");
}
}