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

org.apache.myfaces.custom.schedule.ScheduleDetailedDayRenderer Maven / Gradle / Ivy

Go to download

JSF components and utilities that can be used with any JSF implementation. This library is based on the JSF1.1 version of Tomahawk, but with minor source code and build changes to take advantage of JSF2.1 features. A JSF2.1 implementation is required to use this version of the Tomahawk library.

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.myfaces.custom.schedule;

import java.io.IOException;
import java.io.Serializable;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.TimeZone;
import java.util.TreeSet;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.custom.schedule.model.HalfHourInterval;
import org.apache.myfaces.custom.schedule.model.Interval;
import org.apache.myfaces.custom.schedule.model.ScheduleDay;
import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
import org.apache.myfaces.custom.schedule.util.ScheduleUtil;
import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;

/**
 * 

* Renderer for the day and workweek views of the Schedule component *

* * @since 1.1.7 * @author Jurgen Lust (latest modification by $Author: jlust $) * @author Bruno Aranda (adaptation of Jurgen's code to myfaces) * @version $Revision: 392301 $ */ public class ScheduleDetailedDayRenderer extends AbstractScheduleRenderer implements Serializable { private static final Log log = LogFactory.getLog(ScheduleDetailedDayRenderer.class); private static final long serialVersionUID = -5103791076091317355L; //~ Instance fields -------------------------------------------------------- private final int defaultRowHeightInPixels = 22; //~ Methods ---------------------------------------------------------------- /** * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, * javax.faces.component.UIComponent) */ public void encodeBegin(FacesContext context, UIComponent component) throws IOException { if (!component.isRendered()) { return; } super.encodeBegin(context, component); HtmlSchedule schedule = (HtmlSchedule) component; ResponseWriter writer = context.getResponseWriter(); int rowHeight = getRowHeight(schedule); //the number of rows in the grid is the number of half hours between //visible start hour and visible end hour, plus 1 for the header int numberOfRows = ((getRenderedEndHour(schedule) - getRenderedStartHour(schedule)) * 2) + 1; //the grid height = 22 pixels times the number of rows + 3, for the //table border and the cellpadding int gridHeight = (numberOfRows * rowHeight) + 3 + 10; //container div for the schedule grid writer.startElement(HTML.DIV_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, "schedule-detailed-" + schedule.getTheme(), null); writer.writeAttribute(HTML.STYLE_ATTR, "height: " + String.valueOf(gridHeight) + "px; overflow: hidden;", null); writeBackgroundStart(context, schedule, writer); writeForegroundStart(context, schedule, writer); } /** * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext, * javax.faces.component.UIComponent) */ public void encodeChildren(FacesContext context, UIComponent component) throws IOException { if (!component.isRendered()) { return; } HtmlSchedule schedule = (HtmlSchedule) component; ResponseWriter writer = context.getResponseWriter(); String clientId = schedule.getClientId(context); FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context); String formId = parentFormInfo == null ? null : parentFormInfo.getFormName(); for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator .hasNext();) { ScheduleDay day = (ScheduleDay) dayIterator.next(); String dayBodyId = clientId + "_body_" + ScheduleUtil.getDateId(day.getDate(), schedule.getModel().getTimeZone()); writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "column"), null); writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%;", null); writer.startElement(HTML.DIV_ELEM, schedule); writer.writeAttribute(HTML.ID_ATTR, dayBodyId, null); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "column"), null); writer.writeAttribute( HTML.STYLE_ATTR, "position: relative; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 0;", null); //register an onclick event listener to a column which will capture //the y coordinate of the mouse, to determine the hour of day if (!schedule.isReadonly() && schedule.isSubmitOnClick()) { writer.writeAttribute( HTML.ONMOUSEUP_ATTR, "fireScheduleTimeClicked(this, event, '" + formId + "', '" + clientId + "');", null); } writeEntries(context, schedule, day, writer); writer.endElement(HTML.DIV_ELEM); writer.endElement(HTML.TD_ELEM); } } /** * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext, * javax.faces.component.UIComponent) */ public void encodeEnd(FacesContext context, UIComponent component) throws IOException { if (!component.isRendered()) { return; } ResponseWriter writer = context.getResponseWriter(); writeForegroundEnd(writer); writeBackgroundEnd(writer); writer.endElement(HTML.DIV_ELEM); } protected String getCellClass(HtmlSchedule schedule, ScheduleDay day, boolean even, int hour) { String cellClass = "free"; if (!day.isWorkingDay()) { return getStyleClass(schedule, cellClass); } if (hour >= schedule.getWorkingStartHour() && hour < schedule.getWorkingEndHour()) { cellClass = even ? "even" : "uneven"; } return getStyleClass(schedule, cellClass); } protected boolean isSelected(HtmlSchedule schedule, EntryWrapper entry) { ScheduleEntry selectedEntry = schedule.getModel().getSelectedEntry(); if (selectedEntry == null) { return false; } boolean returnboolean = selectedEntry.getId().equals( entry.entry.getId()); return returnboolean; } protected void maximizeEntries(EntryWrapper[] entries, int numberOfColumns) { for (int i = 0; i < entries.length; i++) { EntryWrapper entry = entries[i]; //now see if we can expand the entry to the columns on the right while (((entry.column + entry.colspan) < numberOfColumns) && entry.canFitInColumn(entry.column + entry.colspan)) { entry.colspan++; } } } protected void scanEntries(EntryWrapper[] entries, int index) { if (entries.length <= 0) { return; } EntryWrapper entry = entries[index]; entry.column = 0; //see what columns are already taken for (int i = 0; i < index; i++) { if (entry.overlaps(entries[i])) { entry.overlappingEntries.add(entries[i]); entries[i].overlappingEntries.add(entry); } } //find an available column while (!entry.canFitInColumn(entry.column)) { entry.column++; } //recursively scan the remaining entries for overlaps if (++index < entries.length) { scanEntries(entries, index); } } protected void writeGutter(FacesContext context, HtmlSchedule schedule, ResponseWriter writer, boolean useIntervalLabels) throws IOException { final int rowHeight = getRowHeight(schedule); final int headerHeight = rowHeight + 9; int startHour = getRenderedStartHour(schedule); int endHour = getRenderedEndHour(schedule); DateFormat hourFormater = getDateFormat(context, schedule, HtmlSchedule.HOUR_NOTATION_12.equals(schedule.getHourNotation()) ? "h" : "HH"); DateFormat minuteFormater = getDateFormat(context, schedule, HtmlSchedule.HOUR_NOTATION_12.equals(schedule.getHourNotation()) ? "':'mma" : "mm"); DateFormat shortMinuteFormater = getDateFormat(context, schedule, HtmlSchedule.HOUR_NOTATION_12.equals(schedule.getHourNotation()) ? "a" : "mm"); ScheduleDay day = (ScheduleDay) schedule.getModel().iterator().next(); writer.startElement(HTML.TABLE_ELEM, schedule); writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null); writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "background"), null); writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%", null); writer.startElement(HTML.TBODY_ELEM, schedule); writer.startElement(HTML.TR_ELEM, schedule); // the header gutter writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "gutter"), null); writer.writeAttribute( HTML.STYLE_ATTR, "height: " + headerHeight + "px; border-style: none; border-width: 0px; overflow: hidden; padding: 0px", null); writer.startElement(HTML.DIV_ELEM, schedule); writer.writeAttribute(HTML.STYLE_ATTR, "height: 1px; width: 56px", null); writer.endElement(HTML.DIV_ELEM); writer.endElement(HTML.TD_ELEM); writer.endElement(HTML.TR_ELEM); // the intervals Iterator intervalIt = day.getIntervals(startHour, endHour).iterator(); boolean renderGutter = true; while (intervalIt.hasNext()) { Interval interval = (Interval) intervalIt.next(); int intervalHeight = calcRowHeight(rowHeight, interval.getDuration()) - 1; // Don't render rows where the timespan is too small if (intervalHeight <= 0) { continue; } if (!renderGutter) { renderGutter = true; continue; } writer.startElement(HTML.TR_ELEM, schedule); int gutterHeight = intervalHeight; if (day.getIntervals() == null && interval.getStartMinutes(getTimeZone(schedule)) == 0) { gutterHeight = (gutterHeight * 2) + 1; renderGutter = false; } //write the hours of the day on the left //this only happens on even rows, or every hour writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "gutter"), null); writer.writeAttribute( HTML.STYLE_ATTR, "height: " + gutterHeight + "px; border-style: none; border-width: 0px; overflow: hidden; padding: 0px", null); if (interval.getDuration() >= HalfHourInterval.HALF_HOUR) { if (!useIntervalLabels || interval.getLabel() == null) { writer.startElement(HTML.SPAN_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, renderGutter ? "minutes" : "hours"), null); writer.writeAttribute(HTML.STYLE_ATTR, "vertical-align: top; height: 100%; padding: 0px;", null); writer.writeText(hourFormater.format(interval.getStartTime()), null); writer.endElement(HTML.SPAN_ELEM); } writer.startElement(HTML.SPAN_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "minutes"), null); writer.writeAttribute(HTML.STYLE_ATTR, "vertical-align: top; height: 100%; padding: 0px;", null); if (useIntervalLabels && interval.getLabel() != null) { writer.writeText(interval.getLabel(), null); } else { writer.writeText((renderGutter ? minuteFormater : shortMinuteFormater).format(interval.getStartTime()), null); } writer.endElement(HTML.SPAN_ELEM); } writer.endElement(HTML.TD_ELEM); writer.endElement(HTML.TR_ELEM); } writer.endElement(HTML.TBODY_ELEM); writer.endElement(HTML.TABLE_ELEM); } protected void writeBackgroundStart(FacesContext context, HtmlSchedule schedule, ResponseWriter writer) throws IOException { boolean repeatedIntervals = schedule.getModel().containsRepeatedIntervals(); writer.startElement(HTML.TABLE_ELEM, schedule); writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null); writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null); writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%", null); writer.startElement(HTML.TBODY_ELEM, schedule); writer.startElement(HTML.TR_ELEM, schedule); writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.STYLE_ATTR, "width: 56px; vertical-align: top;", null); // Render gutter outside background, to allow it to have a flexible width writeGutter(context, schedule, writer, repeatedIntervals); writer.endElement(HTML.TD_ELEM); writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.STYLE_ATTR, "width: 99%; vertical-align: top;", null); final String clientId = schedule.getClientId(context); FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context); String formId = parentFormInfo == null ? null : parentFormInfo.getFormName(); final int rowHeight = getRowHeight(schedule); final int headerHeight = rowHeight + 9; writer.startElement(HTML.DIV_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "background"), null); writer .writeAttribute( HTML.STYLE_ATTR, "position: relative; width: 100%; height: 100%; z-index: 0;", null); //background table for the schedule grid writer.startElement(HTML.TABLE_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "background"), null); writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null); writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null); writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%", null); writer.startElement(HTML.TBODY_ELEM, schedule); writer.startElement(HTML.TR_ELEM, schedule); float columnWidth = (schedule.getModel().size() == 0) ? 100 : (100 / schedule.getModel().size()); int startHour = getRenderedStartHour(schedule); int endHour = getRenderedEndHour(schedule); ScheduleDay day = null; for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator.hasNext();) { writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.STYLE_ATTR, "width: " + String.valueOf(columnWidth)+ "%", null); day = (ScheduleDay) dayIterator.next(); writer.startElement(HTML.TABLE_ELEM, schedule); writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null); writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null); writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%", null); writer.startElement(HTML.TBODY_ELEM, schedule); writer.startElement(HTML.TR_ELEM, schedule); // the header final String dayHeaderId = clientId + "_header_" + ScheduleUtil.getDateId(day.getDate(), schedule.getModel().getTimeZone()); writer.startElement(HTML.TH_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "header"), null); writer .writeAttribute( HTML.STYLE_ATTR, "height: " + headerHeight + "px; vertical-align: top; border-style: none; border-width: 0px; overflow: hidden;", null); boolean isToday = ScheduleUtil.isSameDay(day.getDate(), new Date(), schedule.getModel().getTimeZone()); // write the date writer.startElement(HTML.ANCHOR_ELEM, schedule); writer.writeAttribute(HTML.ID_ATTR, dayHeaderId, null); writer.writeAttribute(HTML.HREF_ATTR, "#", null); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "date") + (isToday ? " today" : ""), null); writer .writeAttribute( HTML.STYLE_ATTR, "display: block; height: 50%; width: 100%; overflow: hidden; white-space: nowrap;", null); //register an onclick event listener to a column header which will //be used to determine the date if (!schedule.isReadonly() && schedule.isSubmitOnClick()) { writer.writeAttribute( HTML.ONCLICK_ATTR, "fireScheduleDateClicked(this, event, '" + formId + "', '" + clientId + "');", null); } writer.writeText(getDateString(context, schedule, day.getDate()), null); writer.endElement(HTML.ANCHOR_ELEM); // write the name of the holiday, if there is one if ((day.getSpecialDayName() != null) && (day.getSpecialDayName().length() > 0)) { writer.startElement(HTML.SPAN_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "holiday"), null); writer .writeAttribute( HTML.STYLE_ATTR, "height: 50%; width: 100%; overflow: hidden; white-space: nowrap;", null); writer.writeText(day.getSpecialDayName(), null); writer.endElement(HTML.SPAN_ELEM); } writer.endElement(HTML.TH_ELEM); writer.endElement(HTML.TR_ELEM); // the intervals Iterator intervalIt = day.getIntervals(startHour, endHour).iterator(); boolean even = false; while (intervalIt.hasNext()) { Interval interval = (Interval) intervalIt.next(); int intervalHeight = calcRowHeight(rowHeight, interval.getDuration()) - 1; // Don't render rows where the timespan is too small if (intervalHeight <= 0) { continue; } writer.startElement(HTML.TR_ELEM, schedule); writer.startElement(HTML.TD_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getCellClass(schedule, day, even, interval.getStartHours(getTimeZone(schedule))), null); writer.writeAttribute(HTML.STYLE_ATTR, "overflow: hidden; height: " + intervalHeight + "px;", null); if (!repeatedIntervals && interval.getLabel() != null) { writer.write(interval.getLabel()); } writer.endElement(HTML.TD_ELEM); writer.endElement(HTML.TR_ELEM); even = !even; } writer.endElement(HTML.TBODY_ELEM); writer.endElement(HTML.TABLE_ELEM); writer.endElement(HTML.TD_ELEM); } writer.endElement(HTML.TR_ELEM); writer.endElement(HTML.TBODY_ELEM); writer.endElement(HTML.TABLE_ELEM); } protected void writeBackgroundEnd(ResponseWriter writer) throws IOException { writer.endElement(HTML.DIV_ELEM); writer.endElement(HTML.TD_ELEM); writer.endElement(HTML.TR_ELEM); writer.endElement(HTML.TBODY_ELEM); writer.endElement(HTML.TABLE_ELEM); } /** * Calculate an actual row height, given a specified height for a half hour duration. * * @param halfHourHeight The height for a half hour duration * @param duration The actual interval duration * @return The height for the actual interval duration */ private int calcRowHeight(int halfHourHeight, long duration) { return duration == HalfHourInterval.HALF_HOUR ? halfHourHeight : (int)((halfHourHeight / (float)(30 * 60 * 1000)) * duration); } protected int getRenderedStartHour(HtmlSchedule schedule) { int startHour = schedule.getVisibleStartHour(); //default behaviour: do not auto-expand the schedule to display all //entries if (!expandToFitEntries(schedule)) return startHour; for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator.hasNext();) { ScheduleDay day = (ScheduleDay) dayIterator.next(); int dayStart = day.getFirstEventHour(); if (dayStart < startHour) { startHour = dayStart; } } return startHour; } protected int getRenderedEndHour(HtmlSchedule schedule) { int endHour = schedule.getVisibleEndHour(); //default behaviour: do not auto-expand the schedule to display all //entries if (!expandToFitEntries(schedule)) return endHour; for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator.hasNext();) { ScheduleDay day = (ScheduleDay) dayIterator.next(); int dayEnd = day.getLastEventHour(); if (dayEnd > endHour) { endHour = dayEnd; } } return endHour; } protected void writeEntries(FacesContext context, HtmlSchedule schedule, ScheduleDay day, ResponseWriter writer) throws IOException { final String clientId = schedule.getClientId(context); FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context); String formId = parentFormInfo == null ? null : parentFormInfo.getFormName(); TreeSet entrySet = new TreeSet(); for (Iterator entryIterator = day.iterator(); entryIterator.hasNext();) { entrySet.add(new EntryWrapper((ScheduleEntry) entryIterator.next(), day)); } EntryWrapper[] entries = (EntryWrapper[]) entrySet .toArray(new EntryWrapper[entrySet.size()]); //determine overlaps scanEntries(entries, 0); //determine the number of columns within this day int maxColumn = 0; for (Iterator entryIterator = entrySet.iterator(); entryIterator .hasNext();) { EntryWrapper wrapper = (EntryWrapper) entryIterator.next(); maxColumn = Math.max(wrapper.column, maxColumn); } int numberOfColumns = maxColumn + 1; //make sure the entries take up all available space horizontally maximizeEntries(entries, numberOfColumns); //now determine the width in percent of 1 column float columnWidth = 100 / numberOfColumns; //and now draw the entries in the columns for (Iterator entryIterator = entrySet.iterator(); entryIterator .hasNext();) { EntryWrapper wrapper = (EntryWrapper) entryIterator.next(); boolean selected = isSelected(schedule, wrapper); //compose the CSS style for the entry box StringBuffer entryStyle = new StringBuffer(); entryStyle.append(wrapper.getBounds(schedule, columnWidth)); String entryBorderColor = getEntryRenderer(schedule).getColor( context, schedule, wrapper.entry, selected); if (entryBorderColor != null) { entryStyle.append(" border-color: "); entryStyle.append(entryBorderColor); entryStyle.append(";"); } if (selected) { writer.startElement(HTML.DIV_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "entry-selected"), null); writer.writeAttribute(HTML.STYLE_ATTR, entryStyle.toString(), null); //draw the tooltip if (schedule.isTooltip()) { getEntryRenderer(schedule).renderToolTip(context, writer, schedule, wrapper.entry, selected); } //draw the content getEntryRenderer(schedule).renderContent(context, writer, schedule, day, wrapper.entry, false, selected); writer.endElement(HTML.DIV_ELEM); } else { //if the schedule is read-only, the entries should not be //hyperlinks writer.startElement( schedule.isReadonly() ? HTML.DIV_ELEM : HTML.ANCHOR_ELEM, schedule); //draw the tooltip if (schedule.isTooltip()) { getEntryRenderer(schedule).renderToolTip(context, writer, schedule, wrapper.entry, selected); } if (!schedule.isReadonly()) { writer.writeAttribute(HTML.HREF_ATTR, "#", null); writer.writeAttribute( HTML.ONCLICK_ATTR, "fireEntrySelected('" + formId + "', '" + clientId + "', '" + wrapper.entry.getId() + "');", null); } writer.writeAttribute(HTML.CLASS_ATTR, getEntryRenderer(schedule).getEntryClass(schedule, wrapper.entry), null); writer.writeAttribute(HTML.STYLE_ATTR, entryStyle.toString(), null); //draw the content getEntryRenderer(schedule).renderContent(context, writer, schedule, day, wrapper.entry, false, selected); writer.endElement(schedule.isReadonly() ? HTML.DIV_ELEM : HTML.ANCHOR_ELEM); } } } protected void writeForegroundEnd(ResponseWriter writer) throws IOException { writer.endElement(HTML.TR_ELEM); writer.endElement(HTML.TABLE_ELEM); writer.endElement(HTML.DIV_ELEM); } protected void writeForegroundStart(FacesContext context, HtmlSchedule schedule, ResponseWriter writer) throws IOException { final int rowHeight = getRowHeight(schedule) - 1; final int headerHeight = rowHeight + 10; writer.startElement(HTML.DIV_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "foreground"), null); writer.writeAttribute( HTML.STYLE_ATTR, "position: absolute; left: 0px; top: " + headerHeight + "px; width: 100%; height: 100%; z-index: 2;", null); writer.startElement(HTML.TABLE_ELEM, schedule); writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "foreground"), null); writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null); writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null); writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%", null); float columnWidth = (schedule.getModel().size() == 0) ? 100 : (100 / schedule.getModel().size()); for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator .hasNext();) { dayIterator.next(); writer.startElement("col", schedule); writer.writeAttribute(HTML.STYLE_ATTR, "width: " + String.valueOf(columnWidth) + "%;", null); writer.endElement("col"); } writer.startElement(HTML.TR_ELEM, schedule); } //~ Inner Classes ---------------------------------------------------------- protected int getDefaultRowHeight() { return defaultRowHeightInPixels; } /** * In the detailed day renderer, we take the y coordinate of the mouse * into account when determining the last clicked date. */ protected Date determineLastClickedDate(HtmlSchedule schedule, String dateId, String yPos) { //the dateId is the schedule client id + "_" + yyyyMMdd String day = dateId.substring(dateId.lastIndexOf("_") + 1); Date date = ScheduleUtil.getDateFromId(day, schedule.getModel().getTimeZone()); Calendar cal = getCalendarInstance(schedule, date != null ? date : new Date()); cal.set(Calendar.HOUR_OF_DAY, getRenderedStartHour(schedule)); //OK, we have the date, let's determine the time try { int y = Integer.parseInt(yPos); int halfHourHeight = getRowHeight(schedule); int minutes = y * 30 / halfHourHeight; cal.add(Calendar.MINUTE, minutes); } catch (NumberFormatException nfe) { log.debug("y position is not a number"); } log.debug("last clicked datetime: " + cal.getTime()); return cal.getTime(); } /** *

* When the start- and endtime of an entry are the same, should the entry * be rendered, fitting the entry box to the text? *

* * @param component the component * @return whether or not zero length entries should be rendered */ protected boolean renderZeroLengthEntries(UIComponent component) { if (component instanceof UIScheduleBase) { UIScheduleBase schedule = (UIScheduleBase) component; return schedule.isRenderZeroLengthEntries(); } else { return false; } } /** *

* When the start- and endtime of an entry are the same, should the entry * be rendered, fitting the entry box to the text? *

* * @param component the component * @return whether or not zero length entries should be rendered */ protected boolean expandToFitEntries(UIComponent component) { if (component instanceof HtmlSchedule) { HtmlSchedule schedule = (HtmlSchedule) component; return schedule.isExpandToFitEntries(); } return false; } protected class EntryWrapper implements Comparable { //~ Static fields/initializers ----------------------------------------- private static final int HALF_HOUR = 1000 * 60 * 30; //~ Instance fields ---------------------------------------------------- private final ScheduleDay day; private final ScheduleEntry entry; private final TreeSet overlappingEntries; private int colspan; private int column; //~ Constructors ------------------------------------------------------- EntryWrapper(ScheduleEntry entry, ScheduleDay day) { this.entry = entry; this.day = day; this.column = 0; this.colspan = 1; this.overlappingEntries = new TreeSet(); } //~ Methods ------------------------------------------------------------ /** * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(Object o) { return comparator.compare(entry, o); } /** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object o) { if (o instanceof EntryWrapper) { EntryWrapper other = (EntryWrapper) o; boolean returnboolean = (entry.getStartTime() .equals(other.entry.getStartTime())) && (entry.getEndTime().equals(other.entry.getEndTime())) && (entry.getId().equals(other.entry.getId())) && (day.equals(other.day)); /* new EqualsBuilder().append( entry.getStartTime(), other.entry.getStartTime() ).append(entry.getEndTime(), other.entry.getEndTime()) .append( entry.getId(), other.entry.getId() ).append(day, other.day).isEquals(); */ return returnboolean; } return false; } /** * @see java.lang.Object#hashCode() */ public int hashCode() { int returnint = entry.getStartTime().hashCode() ^ entry.getEndTime().hashCode() ^ entry.getId().hashCode(); return returnint; } /** *

* Determine the bounds of this entry, in CSS position attributes *

* * @param schedule the schedule * @param columnWidth the width of a column * * @return the bounds */ String getBounds(HtmlSchedule schedule, float columnWidth) { int rowHeight = getRowHeight(schedule); float width = (columnWidth * colspan) - 0.5f; float left = column * columnWidth; Calendar cal = getCalendarInstance(schedule, day.getDate()); int curyear = cal.get(Calendar.YEAR); int curmonth = cal.get(Calendar.MONTH); int curday = cal.get(Calendar.DATE); cal.setTime(entry.getStartTime()); cal.set(curyear, curmonth, curday); long startMillis = cal.getTimeInMillis(); cal.set(Calendar.HOUR_OF_DAY, getRenderedStartHour(schedule)); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); long visibleStartMillis = cal.getTimeInMillis(); startMillis = day.equalsDate(entry.getStartTime()) ? Math.max( startMillis, visibleStartMillis) : visibleStartMillis; cal.setTime(entry.getEndTime()); cal.set(curyear, curmonth, curday); long endMillis = cal.getTimeInMillis(); cal.set(Calendar.HOUR_OF_DAY, getRenderedEndHour(schedule)); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); long visibleEndMillis = cal.getTimeInMillis(); endMillis = day.equalsDate(entry.getEndTime()) ? Math.min( endMillis, visibleEndMillis) : visibleEndMillis; int top = (int) (((startMillis - visibleStartMillis) * rowHeight) / HALF_HOUR); int height = (int) (((endMillis - startMillis) * rowHeight) / HALF_HOUR); StringBuffer buffer = new StringBuffer(); boolean entryVisible = height > 0 || renderZeroLengthEntries(schedule); if (!entryVisible) { buffer.append("visibility: hidden; "); } buffer.append("position: absolute; height: "); if (height > 2) { // Adjust for the width of the border buffer.append((height - 2) + "px"); } else if (height > 0) { buffer.append(height + "px"); } else if (entryVisible) { buffer.append("auto"); } else { buffer.append("0px"); } buffer.append("; top: "); buffer.append(top); buffer.append("px; left: "); buffer.append(left); buffer.append("%; width: "); buffer.append(width); buffer .append("%; padding: 0px; overflow: hidden; border-width: 1.0px; border-style:solid;"); return buffer.toString(); } /** *

* Can this entry fit in the specified column? *

* * @param column the column * * @return whether the entry fits */ boolean canFitInColumn(int column) { for (Iterator overlapIterator = overlappingEntries.iterator(); overlapIterator .hasNext();) { EntryWrapper overlap = (EntryWrapper) overlapIterator.next(); if (overlap.column == column) { return false; } } return true; } /** *

* What is the minimum end time allocated to this event? * Where the event has a duration, the end time of the event * is the minimum end time.
* Where the event has no duration, a minimum end time of half * and hour after the start is implemented. *

* @return The minimum end time of the event */ Date minimumEndTime() { Date start = entry.getStartTime(); Date end = entry.getEndTime(); return end != null ? (end.after(start) ? end : new Date(start.getTime() + HALF_HOUR)) : null; } /** *

* Does this entry overlap with another? *

* * @param other the other entry * * @return whether the entries overlap */ boolean overlaps(EntryWrapper other) { Date start = entry.getStartTime(); Date end = minimumEndTime(); if ((start == null) || (end == null)) { return false; } boolean returnboolean = (start.before( other.minimumEndTime()) && end.after( other.entry.getStartTime())); return returnboolean; } } protected int getRowHeight(UIScheduleBase schedule) { if (schedule != null) { int height = schedule.getDetailedRowHeight(); return height <= 0 ? getDefaultRowHeight() : height; } return getDefaultRowHeight(); } private TimeZone getTimeZone(UIScheduleBase schedule) { return schedule != null && schedule.getModel().getTimeZone() != null ? schedule.getModel().getTimeZone() : TimeZone.getDefault(); } } //The End




© 2015 - 2024 Weber Informatics LLC | Privacy Policy