com.bradrydzewski.gwt.calendar.client.monthview.MonthView.java Source code

Java tutorial

Introduction

Here is the source code for com.bradrydzewski.gwt.calendar.client.monthview.MonthView.java

Source

/*
 * 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 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.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> {

    private List<Widget> dragControlledWidgets = new LinkedList<Widget>();

    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

        for (Widget dragControlledWidget : dragControlledWidgets) {
            dragController.makeNotDraggable(dragControlledWidget);
        }
        dragControlledWidgets.clear();

        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);
            dragControlledWidgets.add(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 + "%");
    }
}