com.areahomeschoolers.baconbits.client.content.calendar.dayview.DayView.java Source code

Java tutorial

Introduction

Here is the source code for com.areahomeschoolers.baconbits.client.content.calendar.dayview.DayView.java

Source

/*
 * 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.areahomeschoolers.baconbits.client.content.calendar.dayview;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
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.areahomeschoolers.baconbits.client.HistoryToken;
import com.areahomeschoolers.baconbits.client.content.calendar.Appointment;
import com.areahomeschoolers.baconbits.client.content.calendar.CalendarSettings.Click;
import com.areahomeschoolers.baconbits.client.content.calendar.CalendarView;
import com.areahomeschoolers.baconbits.client.content.calendar.CalendarWidget;
import com.areahomeschoolers.baconbits.client.content.calendar.DateUtils;
import com.areahomeschoolers.baconbits.client.content.calendar.event.DaySelectionHandler;
import com.areahomeschoolers.baconbits.client.content.calendar.event.WeekSelectionHandler;
import com.areahomeschoolers.baconbits.client.content.calendar.theme.google.client.GoogleDayViewStyleManager;
import com.areahomeschoolers.baconbits.client.content.calendar.util.AppointmentUtil;
import com.areahomeschoolers.baconbits.client.util.PageUrl;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
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.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Widget;

@SuppressWarnings("deprecation")
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 List<Widget> resizeControlledWidgets = new LinkedList<Widget>();
    private List<Widget> dragControlledWidgets = new LinkedList<Widget>();

    private final DayViewStyleManager styleManager = GWT.create(GoogleDayViewStyleManager.class);

    private DayViewResizeController resizeController = null;

    private DayViewDropController dropController = null;

    private PickupDragController dragController = null;

    private DayViewResizeController proxyResizeController = null;

    public DayView() {
        super();
    }

    @Override
    public HandlerRegistration addDaySelectionHandler(DaySelectionHandler<Date> handler) {
        return dayViewHeader.addDaySelectionHandler(handler);
    }

    @Override
    public HandlerRegistration addWeekSelectionHandler(WeekSelectionHandler<Date> handler) {
        return dayViewHeader.addWeekSelectionHandler(handler);
    }

    @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();
    }

    @Override
    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());

        for (Widget widget : resizeControlledWidgets) {
            resizeController.makeNotDraggable(widget);
        }
        resizeControlledWidgets.clear();
        for (Widget widget : dragControlledWidgets) {
            dragController.makeNotDraggable(widget);
        }
        dragControlledWidgets.clear();

        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()) {
            if (getSettings().getMultiDayMaxPixelsHeight() > 0) {
                desiredHeight = Math.min(desiredHeight, getSettings().getMultiDayMaxPixelsHeight());
            }
            multiViewBody.setViewHeight(desiredHeight + "px");

            addAppointmentsToGrid(adapterList, true);
        }
    }

    @Override
    public void doSizing() {
        if (calendarWidget.getOffsetHeight() > 0) {
            int height = 0;
            if (getSettings().isMultidayVisible()) {
                height = calendarWidget.getOffsetHeight() - 2 - dayViewHeader.getOffsetHeight()
                        - multiViewBody.getOffsetHeight();
            } else {
                height = calendarWidget.getOffsetHeight() - 2 - dayViewHeader.getOffsetHeight();
            }
            if (height < 0) {
                height = 0;
            }
            dayViewBody.setHeight(height + "px");
        }
    }

    @Override
    public String getStyleName() {
        return "gwt-cal";
    }

    @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());
            }
        }
    }

    @Override
    public void onDeleteKeyPressed() {
        if (calendarWidget.getSelectedAppointment() != null) {
            calendarWidget.fireDeleteEvent(calendarWidget.getSelectedAppointment());
        }
    }

    @Override
    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);
        }
    }

    @Override
    public void onDownArrowKeyPressed() {
        calendarWidget.selectNextAppointment();
    }

    @Override
    public void onLeftArrowKeyPressed() {
        calendarWidget.selectPreviousAppointment();
    }

    @Override
    public void onMouseOver(final Element element, final Event event) {
        Appointment appointment = findAppointmentByElement(element);
        calendarWidget.fireMouseOverEvent(appointment, element);
    }

    @Override
    public void onRightArrowKeyPressed() {
        calendarWidget.selectNextAppointment();
    }

    @Override
    public void onSingleClick(final Element element, final Event event) {

        // Ignore the scroll panel
        if (dayViewBody.getScrollPanel().getElement().equals(element)) {
            return;
        }

        Appointment appointment = findAppointmentByElement(element);

        HistoryToken.set(PageUrl.event(Integer.parseInt(appointment.getId())));

        // 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);
        // }
    }

    @Override
    public void onUpArrowKeyPressed() {
        calendarWidget.selectPreviousAppointment();
    }

    @Override
    public void scrollToHour(final int hour) {
        dayViewBody.getScrollPanel().setVerticalScrollPosition((hour - getSettings().getDayStartsAt())
                * getSettings().getIntervalsPerHour() * getSettings().getPixelsPerInterval());
    }

    /**
     * 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.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());
                    resizeControlledWidgets.add(panel.getResizeHandle());
                    dragController.makeDraggable(panel, panel.getMoveHandle());
                    dragControlledWidgets.add(panel);
                }
            }
        }
    }

    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() {

                @Override
                public void onDragEnd(DragEndEvent event) {
                    Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
                    calendarWidget.setCommittedAppointment(appt);
                    calendarWidget.fireUpdateEvent(appt);
                }

                @Override
                public void onDragStart(DragStartEvent event) {
                    Appointment appt = ((AppointmentWidget) event.getContext().draggable).getAppointment();
                    calendarWidget.setRollbackAppointment(appt.cloneAppointment());
                    ((DayViewPickupDragController) dragController).setMaxProxyHeight(getMaxProxyHeight());
                }

                @Override
                public void onPreviewDragEnd(DragEndEvent event) throws VetoDragException {
                }

                @Override
                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() {

                @Override
                public void onDragEnd(DragEndEvent event) {
                    Appointment appt = ((AppointmentWidget) event.getContext().draggable.getParent())
                            .getAppointment();
                    calendarWidget.setCommittedAppointment(appt);
                    calendarWidget.fireUpdateEvent(appt);
                }

                @Override
                public void onDragStart(DragStartEvent event) {
                    calendarWidget
                            .setRollbackAppointment(((AppointmentWidget) event.getContext().draggable.getParent())
                                    .getAppointment().cloneAppointment());
                }

                @Override
                public void onPreviewDragEnd(DragEndEvent event) throws VetoDragException {
                }

                @Override
                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;

                @Override
                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);
                    }
                }

                @Override
                public void onDragStart(DragStartEvent event) {
                    startTime = System.currentTimeMillis();
                    initialX = event.getContext().mouseX;
                    initialY = event.getContext().mouseY;
                    startDate = getCoordinatesDate(initialX, initialY);
                    calendarWidget.setRollbackAppointment(null);
                }

                @Override
                public void onPreviewDragEnd(DragEndEvent event) throws VetoDragException {
                }

                @Override
                public void onPreviewDragStart(DragStartEvent event) throws VetoDragException {
                }
            });
        }
    }

    /**
     * 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;
    }

    private List<AppointmentWidget> findAppointmentWidgetsByElement(Element element) {
        return findAppointmentWidget(findAppointmentByElement(element));
    }

    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(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 int getMaxProxyHeight() {
        // For a comfortable use, the Proxy should be, top 2/3 (66%) of the view
        return (2 * (dayViewBody.getScrollPanel().getOffsetHeight() / 3));
    }

    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(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);
        }
    }

}