All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.v7.client.ui.calendar.schedule.SimpleDayCell Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * 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");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy