You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by mm...@apache.org on 2005/07/13 17:33:25 UTC

svn commit: r216175 [3/4] - in /myfaces: build/trunk/ examples/trunk/sandbox/ examples/trunk/sandbox/WEB-INF/ examples/trunk/sandbox/src/java/org/apache/myfaces/examples/schedule/ sandbox/trunk/conf/ sandbox/trunk/src/java/org/apache/myfaces/custom/sch...

Added: myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/PlannerRenderer.java
URL: http://svn.apache.org/viewcvs/myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/PlannerRenderer.java?rev=216175&view=auto
==============================================================================
--- myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/PlannerRenderer.java (added)
+++ myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/PlannerRenderer.java Wed Jul 13 08:33:22 2005
@@ -0,0 +1,955 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.renderer;
+
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIForm;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.render.Renderer;
+
+import org.apache.myfaces.component.html.util.AddResource;
+import org.apache.myfaces.custom.schedule.HtmlPlanner;
+import org.apache.myfaces.custom.schedule.HtmlSchedule;
+import org.apache.myfaces.custom.schedule.model.Day;
+import org.apache.myfaces.custom.schedule.model.PlannerEntity;
+import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
+import org.apache.myfaces.custom.schedule.util.ScheduleEntryComparator;
+import org.apache.myfaces.renderkit.html.HTML;
+
+
+/**
+ * <p>
+ * Renderer for the UIPlanner component
+ * </p>
+ *
+ * @author Jurgen Lust (latest modification by $Author$)
+ * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
+ * @version $Revision$
+ */
+public class PlannerRenderer
+    extends Renderer
+{
+    //~ Static fields/initializers ---------------------------------------------
+
+    private static final ScheduleEntryComparator comparator =
+        new ScheduleEntryComparator();
+
+    //~ Instance fields --------------------------------------------------------
+
+    private final int ROW_HEIGHT_IN_PIXELS = 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;
+        }
+        
+        AddResource.addStyleSheet(HtmlSchedule.class, "css/schedule.css", context);
+
+        HtmlPlanner planner = (HtmlPlanner) component;
+        Map attributes = planner.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        int numberOfRows = planner.getModel().numberOfEntities();
+
+        int gridHeight = (numberOfRows * 22) + 3 + 47;
+
+        writer.startElement(HTML.DIV_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "planner", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "height: " + String.valueOf(gridHeight) + "px; overflow: hidden;",
+            null
+        );
+        writeBackground(context, planner, writer);
+        writeForegroundStart(context, planner, 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;
+        }
+
+        HtmlPlanner planner = (HtmlPlanner) component;
+        Map attributes = planner.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        for (
+            Iterator entityIterator = planner.getModel().entityIterator();
+                entityIterator.hasNext();
+        ) {
+            PlannerEntity entity = (PlannerEntity) entityIterator.next();
+            writer.startElement(HTML.TR_ELEM, planner);
+            writer.startElement(HTML.TD_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "gutter", null);
+            writer.startElement(HTML.DIV_ELEM, planner);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "height: 1px; width: " +
+                String.valueOf(getGutterWidth(planner.getAttributes())) + "px",
+                null
+            );
+            writer.endElement(HTML.DIV_ELEM);
+            writer.endElement(HTML.TD_ELEM);
+
+            int numberOfDays = planner.getModel().numberOfDays();
+            float columnWidth =
+                (numberOfDays == 0) ? 100f : (100 / numberOfDays);
+
+            for (
+                Iterator dayIterator = planner.getModel().dayIterator();
+                    dayIterator.hasNext();
+            ) {
+                Day day = (Day) dayIterator.next();
+                writer.startElement(HTML.TD_ELEM, planner);
+                writer.writeAttribute(HTML.CLASS_ATTR, "row", null);
+                writer.writeAttribute(
+                		HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+                );
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "height: 21px; border-left-style: solid; border-left-width: 1px; border-top-style: none; border-right-style: none; border-bottom-style: none;",
+                    null
+                );
+                writer.startElement(HTML.DIV_ELEM, null);
+                writer.writeAttribute(HTML.CLASS_ATTR, "row", null);
+                writer.writeAttribute(
+                	HTML.STYLE_CLASS_ATTR,
+                    "position: relative; top: 0px; left: 0px; width: 100%; height: 100%; padding: 0px; z-index: 0; vertical-align: middle;",
+                    null
+                );
+                writeEntries(context, planner, entity, day, writer);
+                writer.endElement(HTML.DIV_ELEM);
+                writer.endElement(HTML.TD_ELEM);
+            }
+
+            writer.endElement(HTML.TR_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;
+        }
+
+        HtmlPlanner planner = (HtmlPlanner) component;
+        Map attributes = planner.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+        writeForegroundEnd(context, planner, writer);
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    /**
+     * <p>
+     * Determine the width of the left gutter
+     * </p>
+     *
+     * @param attributes the attributes
+     *
+     * @return the gutter width in pixels
+     */
+    protected int getGutterWidth(Map attributes)
+    {
+        try {
+            Integer width =
+                Integer.valueOf((String) attributes.get("gutterWidthInPixels"));
+
+            return width.intValue();
+        } catch (NumberFormatException nfe) {
+            return 200;
+        }
+    }
+
+    /**
+     * <p>
+     * The date format that is used in the planner header
+     * </p>
+     *
+     * @param attributes the attributes
+     *
+     * @return Returns the headerDateFormat.
+     */
+    protected String getHeaderDateFormat(Map attributes)
+    {
+        return (String) attributes.get("headerDateFormat");
+    }
+
+    /**
+     * <p>
+     * Get the parent form of the planner component
+     * </p>
+     *
+     * @param component the component
+     *
+     * @return the parent form
+     */
+    protected UIForm getParentForm(UIComponent component)
+    {
+        UIComponent parent = component.getParent();
+
+        while (parent != null) {
+            if (parent instanceof UIForm) {
+                break;
+            }
+
+            parent = parent.getParent();
+        }
+
+        return (UIForm) parent;
+    }
+
+    /**
+     * <p>
+     * Should the legend be rendered?
+     * </p>
+     *
+     * TODO the rendering of the legend has not been implemented yet
+     *
+     * @param attributes the attributes
+     *
+     * @return whether to render the legend
+     */
+    protected boolean showLegend(Map attributes)
+    {
+        return Boolean.valueOf((String) attributes.get("legend")).booleanValue();
+    }
+
+    /**
+     * <p>
+     * Should the tooltip be rendered?
+     * </p>
+     *
+     * @param attributes the attributes
+     *
+     * @return whether or not tooltips should be rendered
+     */
+    protected boolean showTooltip(Map attributes)
+    {
+        return Boolean.valueOf((String) attributes.get("tooltip")).booleanValue();
+    }
+
+    private String getCellClass(
+        HtmlPlanner planner,
+        Day day,
+        int hour
+    )
+    {
+        if (!day.isWorkingDay()) {
+            return "free";
+        }
+
+        if (
+            (hour >= planner.getWorkingStartHour()) &&
+                (hour < planner.getWorkingEndHour())
+        ) {
+            return ((hour % 2) == 0) ? "even" : "uneven";
+        }
+
+        return "free";
+    }
+
+    private String getDateString(
+        FacesContext context,
+        Map attributes,
+        Date date
+    )
+    {
+        DateFormat format;
+        String pattern = getHeaderDateFormat(attributes);
+
+        if ((pattern != null) && (pattern.length() > 0)) {
+            format = new SimpleDateFormat(pattern);
+        } else {
+            if (context.getApplication().getDefaultLocale() != null) {
+                format =
+                    DateFormat.getDateInstance(
+                        DateFormat.MEDIUM,
+                        context.getApplication().getDefaultLocale()
+                    );
+            } else {
+                format = DateFormat.getDateInstance(DateFormat.MEDIUM);
+            }
+        }
+
+        return format.format(date);
+    }
+
+    private String getTooltipText(
+        ScheduleEntry entry,
+        Map attributes
+    )
+    {
+        if (!showTooltip(attributes)) {
+            return null;
+        }
+        
+        //TODO enable tooltips again
+        return "";
+
+       /*
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(
+            "return makeTrue(domTT_activate(this, event, 'caption', '"
+        );
+
+        if (entry.getTitle() != null) {
+            buffer.append(escape(entry.getTitle()));
+        }
+
+        buffer.append("', 'content', '<i>");
+
+        if (entry.getSubtitle() != null) {
+            buffer.append(escape(entry.getSubtitle()));
+        }
+
+        buffer.append("</i>");
+
+        if (entry.getDescription() != null) {
+            buffer.append("<br/>");
+            buffer.append(escape(entry.getDescription()));
+        }
+
+        buffer.append("', 'trail', true));");
+
+        return buffer.toString();
+        */
+    }
+
+    private boolean containsEntry(
+        HtmlPlanner planner,
+        Day day,
+        ScheduleEntry entry
+    )
+    {
+        if ((day == null) || (entry == null)) {
+            return false;
+        }
+
+        Calendar cal = GregorianCalendar.getInstance();
+        cal.setTime(day.getDate());
+        cal.set(Calendar.HOUR_OF_DAY, planner.getVisibleStartHour());
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+
+        Date dayStart = cal.getTime();
+        cal.set(Calendar.HOUR_OF_DAY, planner.getVisibleEndHour());
+
+        Date dayEnd = cal.getTime();
+
+        return entry.getEndTime().after(dayStart) &&
+        entry.getStartTime().before(dayEnd);
+    }
+
+    private String escape(String text)
+    {
+        if (text == null) {
+            return null;
+        }
+
+        return text.replaceAll("'", "&quot;");
+    }
+
+    /**
+     * <p>
+     * Draw the planner background, i.e. the grid
+     * </p>
+     *
+     * @param context the FacesContext
+     * @param planner the Planner component
+     * @param writer the ResponseWriter
+     *
+     * @throws IOException when the background cannot be drawn
+     * @throws NullPointerException
+     */
+    private void writeBackground(
+        FacesContext context,
+        HtmlPlanner planner,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        //a planner component should always be inside a UIForm
+        UIForm parentForm = getParentForm(planner);
+
+        if (parentForm == null) {
+            throw new NullPointerException("No parent UIForm found");
+        }
+
+        writer.startElement(HTML.DIV_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "background", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 0;",
+            null
+        );
+
+        //background table for the schedule grid
+        writer.startElement(HTML.TABLE_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "background", null);
+        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, planner);
+
+        //header row, containing the day names
+        writer.startElement(HTML.TR_ELEM, planner);
+        writer.startElement(HTML.TD_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "gutter", null);
+        writer.writeAttribute("rowspan", "2", null);
+        writer.writeAttribute(
+        	HTML.STYLE_ATTR,
+            "padding: 0px; vertical-align: middle; height: 21px; border-style: none; border-width: 0px; overflow: hidden; whitespace: nowrap;",
+            null
+        );
+        writer.startElement(HTML.DIV_ELEM, planner);
+        writer.writeAttribute(
+        	HTML.STYLE_ATTR,
+            "height: 1px; width: " +
+            String.valueOf(getGutterWidth(planner.getAttributes())) + "px", null
+        );
+        writer.endElement(HTML.DIV_ELEM);
+        writer.endElement(HTML.TD_ELEM);
+
+        int numberOfDays = planner.getModel().numberOfDays();
+        float columnWidth = (numberOfDays == 0) ? 100f : (100 / numberOfDays);
+
+        for (
+            Iterator dayIterator = planner.getModel().dayIterator();
+                dayIterator.hasNext();
+        ) {
+            Day day = (Day) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "header", null);
+            writer.writeAttribute(
+                HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+            );
+            writer.writeAttribute(
+                HTML.STYLE_ATTR, "overflow: hidden; height: 31px;", null
+            );
+            writer.startElement(HTML.DIV_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "header", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "position: relative; left: 0px; top: 0px; width: 100%; height: 100%;",
+                null
+            );
+            writer.startElement(HTML.SPAN_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "date", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "position: absolute; left: 0px; top: 0px; width: 100%; height: 15px; overflow: hidden; white-space: nowrap;",
+                null
+            );
+            writer.writeText(
+                getDateString(context, planner.getAttributes(), day.getDate()),
+                null
+            );
+            writer.endElement(HTML.SPAN_ELEM);
+
+            //write the name of the holiday, if there is one
+            if (
+                (day.getSpecialDayName() != null) &&
+                    (day.getSpecialDayName().length() > 0)
+            ) {
+                writer.startElement(HTML.SPAN_ELEM, planner);
+                writer.writeAttribute(HTML.CLASS_ATTR, "holiday", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "position: absolute; left: 0px; top: 15px; width: 100%; overflow: hidden; white-space: nowrap;",
+                    null
+                );
+                writer.writeText(day.getSpecialDayName(), null);
+                writer.endElement(HTML.SPAN_ELEM);
+            }
+
+            writer.endElement(HTML.DIV_ELEM);
+            writer.endElement(HTML.TD_ELEM);
+        }
+
+        writer.endElement(HTML.TR_ELEM);
+
+        //second header row, containing the hours
+        writer.startElement(HTML.TR_ELEM, planner);
+
+        int numberOfHours =
+            planner.getVisibleEndHour() - planner.getVisibleStartHour();
+        float hourWidth = (numberOfHours <= 0) ? 100f : (100 / numberOfHours);
+
+        for (
+            Iterator dayIterator = planner.getModel().dayIterator();
+                dayIterator.hasNext();
+        ) {
+            Day day = (Day) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, planner);
+            writer.writeAttribute(
+                HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+            );
+            writer.startElement(HTML.TABLE_ELEM, planner);
+            writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%; width: 100%", null);
+            writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
+            writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
+            writer.writeAttribute("border", "0", null);
+            writer.startElement(HTML.TR_ELEM, planner);
+
+            for (
+                int hcol = planner.getVisibleStartHour();
+                    hcol < planner.getVisibleEndHour(); hcol++
+            ) {
+                writer.startElement(HTML.TD_ELEM, planner);
+                writer.writeAttribute(HTML.CLASS_ATTR, "hours", null);
+                writer.writeAttribute(
+                    HTML.WIDTH_ATTR, String.valueOf(hourWidth) + "%", null
+                );
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "overflow: hidden; height: 15px; border-left-style: solid; border-left-width: 1px; border-top-style: none; border-right-style: none; border-bottom-style: none;",
+                    null
+                );
+                writer.startElement(HTML.DIV_ELEM, planner);
+                writer.writeAttribute(HTML.CLASS_ATTR, "header", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "position: relative; left: 0px; top: 0px; width: 100%; height: 100%;",
+                    null
+                );
+                writer.startElement(HTML.SPAN_ELEM, planner);
+                writer.writeAttribute(HTML.CLASS_ATTR, "hours", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; overflow: hidden; white-space: nowrap;",
+                    null
+                );
+                writer.writeText(String.valueOf(hcol) + ":00", null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.endElement(HTML.DIV_ELEM);
+                writer.endElement(HTML.TD_ELEM);
+            }
+
+            writer.endElement(HTML.TR_ELEM);
+            writer.endElement(HTML.TABLE_ELEM);
+            writer.endElement(HTML.TD_ELEM);
+        }
+
+        writer.endElement(HTML.TR_ELEM);
+
+        //the actual entity rows start here
+        for (
+            Iterator entityIterator = planner.getModel().entityIterator();
+                entityIterator.hasNext();
+        ) {
+            PlannerEntity entity = (PlannerEntity) entityIterator.next();
+            writer.startElement(HTML.TR_ELEM, planner);
+            writer.startElement(HTML.TD_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "gutter", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "padding: 0px; vertical-align: middle; height: 21px; border-style: none; border-width: 0px; overflow: hidden; whitespace: nowrap;",
+                null
+            );
+            writer.startElement(HTML.SPAN_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "title", null);
+            writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%;", null);
+            writer.writeText(entity.getName(), null);
+            writer.endElement(HTML.SPAN_ELEM);
+            writer.endElement(HTML.TD_ELEM);
+
+            for (
+                Iterator dayIterator = planner.getModel().dayIterator();
+                    dayIterator.hasNext();
+            ) {
+                Day day = (Day) dayIterator.next();
+                writer.startElement(HTML.TD_ELEM, planner);
+                writer.writeAttribute(
+                    HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+                );
+                writer.startElement(HTML.TABLE_ELEM, planner);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, "height: 100%; width: 100%", null
+                );
+                writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
+                writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
+                writer.writeAttribute("border", "0", null);
+                writer.startElement(HTML.TR_ELEM, planner);
+
+                for (
+                    int hcol = planner.getVisibleStartHour();
+                        hcol < planner.getVisibleEndHour(); hcol++
+                ) {
+                    writer.startElement(HTML.TD_ELEM, planner);
+                    writer.writeAttribute(
+                        HTML.CLASS_ATTR, getCellClass(planner, day, hcol), null
+                    );
+                    writer.writeAttribute(
+                        HTML.WIDTH_ATTR, String.valueOf(hourWidth) + "%", null
+                    );
+                    writer.writeAttribute(
+                        HTML.STYLE_ATTR,
+                        "height: 21px; overflow: hidden; border-left-style: solid; border-left-width: 1px; border-top-style: none; border-right-style: none; border-bottom-style: none;",
+                        null
+                    );
+                    writer.write(HTML.NBSP_ENTITY);
+                    writer.endElement(HTML.TD_ELEM);
+                }
+
+                writer.endElement(HTML.TR_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);
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    /**
+     * <p>
+     * Draw the entries on the planner
+     * </p>
+     *
+     * @param context the FacesContext
+     * @param planner the Planner component
+     * @param entity the planner entity
+     * @param day the day
+     * @param writer the responsewriter
+     *
+     * @throws IOException when the entries cannot be drawn
+     */
+    private void writeEntries(
+        FacesContext context,
+        HtmlPlanner planner,
+        PlannerEntity entity,
+        Day day,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        for (Iterator entryIterator = entity.iterator();
+                entryIterator.hasNext();
+        ) {
+            ScheduleEntry entry = (ScheduleEntry) entryIterator.next();
+
+            if (containsEntry(planner, day, entry)) {
+                EntryWrapper wrapper = new EntryWrapper(entry, day, entity);
+                writer.startElement(HTML.DIV_ELEM, planner);
+
+                //draw the tooltip
+                if (showTooltip(planner.getAttributes())) {
+                    writer.writeAttribute(
+                        "onmouseover",
+                        getTooltipText(wrapper.entry, planner.getAttributes()),
+                        null
+                    );
+                }
+
+                writer.writeAttribute(HTML.CLASS_ATTR, "entry", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, wrapper.getBounds(planner), null
+                );
+                writer.endElement(HTML.DIV_ELEM);
+            }
+        }
+    }
+
+    private void writeForegroundEnd(
+        FacesContext context,
+        HtmlPlanner planner,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        writer.endElement(HTML.TBODY_ELEM);
+        writer.endElement(HTML.TABLE_ELEM);
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    private void writeForegroundStart(
+        FacesContext context,
+        HtmlPlanner planner,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        writer.startElement(HTML.DIV_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "foreground", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "position: absolute; left: 0px; top: 0px; width: 100%; height: 100%; z-index: 1;",
+            null
+        );
+
+        //foreground table for the planner grid
+        writer.startElement(HTML.TABLE_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "foreground", null);
+        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, planner);
+
+        //header row containing the day names
+        writer.startElement(HTML.TR_ELEM, planner);
+        writer.startElement(HTML.TD_ELEM, planner);
+        writer.writeAttribute(HTML.CLASS_ATTR, "gutter", null);
+        writer.writeAttribute("rowspan", "2", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "padding: 0px; vertical-align: middle; height: 21px; border-style: none; border-width: 0px; overflow: hidden; whitespace: nowrap;",
+            null
+        );
+        writer.startElement(HTML.DIV_ELEM, planner);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "height: 1px; width: " +
+            String.valueOf(getGutterWidth(planner.getAttributes())) + "px", null
+        );
+        writer.endElement(HTML.DIV_ELEM);
+        writer.endElement(HTML.TD_ELEM);
+
+        int numberOfDays = planner.getModel().numberOfDays();
+        float columnWidth = (numberOfDays == 0) ? 100f : (100 / numberOfDays);
+
+        for (
+            Iterator dayIterator = planner.getModel().dayIterator();
+                dayIterator.hasNext();
+        ) {
+            Day day = (Day) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "header", null);
+            writer.writeAttribute(
+                HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+            );
+            writer.writeAttribute(
+                HTML.STYLE_ATTR, "overflow: hidden; height: 31px;", null
+            );
+            writer.endElement(HTML.TD_ELEM);
+        }
+
+        writer.endElement(HTML.TR_ELEM);
+
+        //header row containing the hours
+        writer.startElement(HTML.TR_ELEM, planner);
+
+        for (
+            Iterator dayIterator = planner.getModel().dayIterator();
+                dayIterator.hasNext();
+        ) {
+            Day day = (Day) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, planner);
+            writer.writeAttribute(HTML.CLASS_ATTR, "hours", null);
+            writer.writeAttribute(
+                HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+            );
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "overflow: hidden; height: 15px; border-style: none; border-width: 0px",
+                null
+            );
+            writer.endElement(HTML.TD_ELEM);
+        }
+
+        writer.endElement(HTML.TR_ELEM);
+    }
+
+    //~ Inner Classes ----------------------------------------------------------
+
+    /**
+     *
+     */
+    private class EntryWrapper
+        implements Comparable
+    {
+        //~ Static fields/initializers -----------------------------------------
+
+        private static final int HOUR = 1000 * 3600;
+
+        //~ Instance fields ----------------------------------------------------
+
+        private final Day day;
+        private final PlannerEntity entity;
+        private final ScheduleEntry entry;
+
+        //~ Constructors -------------------------------------------------------
+
+        EntryWrapper(
+            ScheduleEntry entry,
+            Day day,
+            PlannerEntity entity
+        )
+        {
+            this.entry = entry;
+            this.day = day;
+            this.entity = entity;
+        }
+
+        //~ 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;
+        }
+
+        /**
+         * <p>
+         * Determine the bounds of the entry
+         * </p>
+         *
+         * @param planner the planner component
+         *
+         * @return the bounds, as CSS position attributes
+         */
+        String getBounds(HtmlPlanner planner)
+        {
+            Calendar cal = GregorianCalendar.getInstance();
+            cal.setTime(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, planner.getVisibleStartHour());
+            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, planner.getVisibleEndHour());
+            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;
+
+            float left =
+                (float) ((startMillis - visibleStartMillis) * 100) / (float) (visibleEndMillis -
+                visibleStartMillis);
+            float width =
+                (float) ((endMillis - startMillis) * 100) / (float) (visibleEndMillis -
+                visibleStartMillis);
+            StringBuffer buffer = new StringBuffer();
+            buffer.append(
+                "position: absolute; padding: 0px; top: 0px; height: 21px; left: "
+            );
+            buffer.append(String.valueOf(left));
+            buffer.append("%; width: ");
+            buffer.append(String.valueOf(width));
+            buffer.append(
+                "%; overflow: hidden; border-style-none; border-width: 0px;"
+            );
+
+            return buffer.toString();
+        }
+    }
+}
+//The End

Added: myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactMonthRenderer.java
URL: http://svn.apache.org/viewcvs/myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactMonthRenderer.java?rev=216175&view=auto
==============================================================================
--- myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactMonthRenderer.java (added)
+++ myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactMonthRenderer.java Wed Jul 13 08:33:22 2005
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.renderer;
+
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.apache.myfaces.custom.schedule.HtmlSchedule;
+import org.apache.myfaces.custom.schedule.model.ScheduleDay;
+import org.apache.myfaces.renderkit.html.HTML;
+
+
+/**
+ * <p>
+ * Renderer for the month view of the Schedule component
+ * </p>
+ *
+ * @author Jurgen Lust (latest modification by $Author$)
+ * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
+ * @version $Revision$
+ */
+public class ScheduleCompactMonthRenderer
+    extends AbstractCompactScheduleRenderer
+{
+    //~ 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;
+        Map attributes = schedule.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        //container div for the schedule grid
+        writer.startElement(HTML.DIV_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "schedule-compact", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR, "border-style: none; overflow: hidden;", null
+        );
+
+        writer.startElement(HTML.TABLE_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "month", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR, "position: relative; left: 0px; top: 0px; width: 100%;",
+            null
+        );
+        writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
+        writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null);
+        writer.writeAttribute("border", "0", null);
+        writer.writeAttribute(HTML.WIDTH_ATTR, "100%", null);
+        writer.startElement(HTML.TBODY_ELEM, schedule);
+
+        Calendar cal = GregorianCalendar.getInstance();
+
+        for (
+            Iterator dayIterator = schedule.getModel().iterator();
+                dayIterator.hasNext();
+        ) {
+            ScheduleDay day = (ScheduleDay) dayIterator.next();
+            cal.setTime(day.getDate());
+
+            int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
+            int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
+            boolean isWeekend =
+                (dayOfWeek == Calendar.SATURDAY) ||
+                (dayOfWeek == Calendar.SUNDAY);
+
+            cal.setTime(day.getDate());
+
+            if (dayOfMonth == 1) { //fill the cells of the previous month
+
+                TreeSet previousMonth = new TreeSet();
+
+                while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
+                    cal.add(Calendar.DATE, -1);
+                    previousMonth.add(new ScheduleDay(cal.getTime()));
+                }
+
+                for (
+                    Iterator prevMonthIterator = previousMonth.iterator();
+                        prevMonthIterator.hasNext();
+                ) {
+                    ScheduleDay d = (ScheduleDay) prevMonthIterator.next();
+                    cal.setTime(d.getDate());
+
+                    int dow = cal.get(Calendar.DAY_OF_WEEK);
+                    int dom = cal.get(Calendar.DAY_OF_MONTH);
+                    boolean w =
+                        (dow == Calendar.SATURDAY) || (dow == Calendar.SUNDAY);
+                    writeDayCell(
+                        context, writer, schedule, d, dow, dom, w, false,
+                        w ? 1 : 2
+                    );
+                }
+            }
+
+            writeDayCell(
+                context, writer, schedule, day, dayOfWeek, dayOfMonth, isWeekend,
+                true, isWeekend ? 1 : 2
+            );
+
+            if (!dayIterator.hasNext()) { //fill the empty cells of the next month
+
+                while (cal.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
+                    cal.add(Calendar.DATE, 1);
+
+                    ScheduleDay d = new ScheduleDay(cal.getTime());
+                    int dow = cal.get(Calendar.DAY_OF_WEEK);
+                    int dom = cal.get(Calendar.DAY_OF_MONTH);
+                    boolean w =
+                        (dow == Calendar.SATURDAY) || (dow == Calendar.SUNDAY);
+                    writeDayCell(
+                        context, writer, schedule, d, dow, dom, w, false,
+                        w ? 1 : 2
+                    );
+                }
+            }
+        }
+
+        writer.endElement(HTML.TBODY_ELEM);
+        writer.endElement(HTML.TABLE_ELEM);
+
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    /**
+     * @see org.apache.myfaces.custom.schedule.render.AbstractCompactScheduleRenderer#getDefaultRowHeight()
+     */
+    protected int getDefaultRowHeight()
+    {
+        return 120;
+    }
+
+    /**
+     * @see org.apache.myfaces.custom.schedule.render.AbstractCompactScheduleRenderer#getRowHeightProperty()
+     */
+    protected String getRowHeightProperty()
+    {
+        return "compactMonthRowHeight";
+    }
+
+    /**
+     * @see org.apache.myfaces.custom.schedule.render.AbstractCompactScheduleRenderer#writeDayCell(javax.faces.context.FacesContext,
+     *      javax.faces.context.ResponseWriter,
+     *      org.apache.myfaces.custom.schedule.HtmlSchedule,
+     *      org.apache.myfaces.custom.schedule.model.ScheduleDay, int, int, int,
+     *      boolean, boolean, int)
+     */
+    protected void writeDayCell(
+        FacesContext context,
+        ResponseWriter writer,
+        HtmlSchedule schedule,
+        ScheduleDay day,
+        int dayOfWeek,
+        int dayOfMonth,
+        boolean isWeekend,
+        boolean isCurrentMonth,
+        int rowspan
+    )
+        throws IOException
+    {
+        if ((dayOfWeek == Calendar.MONDAY) || (dayOfWeek == Calendar.SUNDAY)) {
+            writer.startElement(HTML.TR_ELEM, schedule);
+        }
+
+        super.writeDayCell(
+            context, writer, schedule, day, 100f / 6, dayOfWeek, dayOfMonth,
+            isWeekend, isCurrentMonth, rowspan
+        );
+
+        if ((dayOfWeek == Calendar.SATURDAY) || (dayOfWeek == Calendar.SUNDAY)) {
+            writer.endElement(HTML.TR_ELEM);
+        }
+    }
+}
+//The End

Added: myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactWeekRenderer.java
URL: http://svn.apache.org/viewcvs/myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactWeekRenderer.java?rev=216175&view=auto
==============================================================================
--- myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactWeekRenderer.java (added)
+++ myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleCompactWeekRenderer.java Wed Jul 13 08:33:22 2005
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.renderer;
+
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.apache.myfaces.custom.schedule.HtmlSchedule;
+import org.apache.myfaces.custom.schedule.model.ScheduleDay;
+import org.apache.myfaces.renderkit.html.HTML;
+
+
+/**
+ * <p>
+ * Renderer for the week view of the UISchedule component
+ * </p>
+ *
+ * @author Jurgen Lust (latest modification by $Author$)
+ * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
+ * @version $Revision$
+ */
+public class ScheduleCompactWeekRenderer
+    extends AbstractCompactScheduleRenderer
+{
+    //~ 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;
+        Map attributes = schedule.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        //container div for the schedule grid
+        writer.startElement(HTML.DIV_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "schedule-compact", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR, "border-style: none; overflow: hidden;", null
+        );
+
+        writer.startElement(HTML.TABLE_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "week", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR, "position: relative; left: 0px; top: 0px; width: 100%;",
+            null
+        );
+        writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
+        writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null);
+        writer.writeAttribute("border", "0", null);
+        writer.writeAttribute(HTML.WIDTH_ATTR, "100%", null);
+        writer.startElement(HTML.TBODY_ELEM, schedule);
+
+        Calendar cal = GregorianCalendar.getInstance();
+
+        for (
+            Iterator dayIterator = schedule.getModel().iterator();
+                dayIterator.hasNext();
+        ) {
+            ScheduleDay day = (ScheduleDay) dayIterator.next();
+            cal.setTime(day.getDate());
+
+            int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
+            int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
+            boolean isWeekend =
+                (dayOfWeek == Calendar.SATURDAY) ||
+                (dayOfWeek == Calendar.SUNDAY);
+
+            if (
+                (dayOfWeek == Calendar.MONDAY) ||
+                    (dayOfWeek == Calendar.WEDNESDAY) ||
+                    (dayOfWeek == Calendar.FRIDAY) ||
+                    (dayOfWeek == Calendar.SUNDAY)
+            ) {
+                writer.startElement(HTML.TR_ELEM, schedule);
+            }
+
+            writeDayCell(
+                context, writer, schedule, day, 50f, dayOfWeek, dayOfMonth,
+                isWeekend, true, (dayOfWeek == Calendar.FRIDAY) ? 2 : 1
+            );
+
+            if (
+                (dayOfWeek == Calendar.TUESDAY) ||
+                    (dayOfWeek == Calendar.THURSDAY) ||
+                    (dayOfWeek == Calendar.SATURDAY) ||
+                    (dayOfWeek == Calendar.SUNDAY)
+            ) {
+                writer.endElement(HTML.TR_ELEM);
+            }
+        }
+
+        writer.endElement(HTML.TBODY_ELEM);
+        writer.endElement(HTML.TABLE_ELEM);
+
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    /**
+     * @see org.apache.myfaces.custom.schedule.render.AbstractCompactScheduleRenderer#getDefaultRowHeight()
+     */
+    protected int getDefaultRowHeight()
+    {
+        return 200;
+    }
+
+    /**
+     * @see org.apache.myfaces.custom.schedule.render.AbstractCompactScheduleRenderer#getRowHeightProperty()
+     */
+    protected String getRowHeightProperty()
+    {
+        return "compactWeekRowHeight";
+    }
+}
+//The End

Added: myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDelegatingRenderer.java
URL: http://svn.apache.org/viewcvs/myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDelegatingRenderer.java?rev=216175&view=auto
==============================================================================
--- myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDelegatingRenderer.java (added)
+++ myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDelegatingRenderer.java Wed Jul 13 08:33:22 2005
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.renderer;
+
+
+import java.io.IOException;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import javax.faces.render.Renderer;
+
+import org.apache.myfaces.component.html.util.AddResource;
+import org.apache.myfaces.custom.schedule.HtmlSchedule;
+import org.apache.myfaces.custom.schedule.model.ScheduleModel;
+
+
+/**
+ * <p>
+ * Renderer for the Schedule component that delegates the actual rendering
+ * to a compact or detailed renderer, depending on the mode of the ScheduleModel
+ * </p>
+ *
+ * @author Jurgen Lust (latest modification by $Author$)
+ * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
+ * @version $Revision$
+ */
+public class ScheduleDelegatingRenderer
+    extends Renderer
+{
+    //~ Instance fields --------------------------------------------------------
+
+    private final ScheduleCompactMonthRenderer monthDelegate =
+        new ScheduleCompactMonthRenderer();
+    private final ScheduleCompactWeekRenderer weekDelegate =
+        new ScheduleCompactWeekRenderer();
+    private final ScheduleDetailedDayRenderer dayDelegate =
+        new ScheduleDetailedDayRenderer();
+
+    //~ Methods ----------------------------------------------------------------
+
+    /**
+     * @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext,
+     *      javax.faces.component.UIComponent)
+     */
+    public void decode(
+        FacesContext context,
+        UIComponent component
+    )
+    {
+        getDelegateRenderer(component).decode(context, component);
+    }
+
+    /**
+     * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext,
+     *      javax.faces.component.UIComponent)
+     */
+    public void encodeBegin(
+        FacesContext context,
+        UIComponent component
+    )
+        throws IOException
+    {
+    	AddResource.addStyleSheet(HtmlSchedule.class, "css/schedule.css", context);
+        getDelegateRenderer(component).encodeBegin(context, component);
+    }
+
+    /**
+     * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext,
+     *      javax.faces.component.UIComponent)
+     */
+    public void encodeChildren(
+        FacesContext context,
+        UIComponent component
+    )
+        throws IOException
+    {
+        getDelegateRenderer(component).encodeChildren(context, component);
+    }
+
+    /**
+     * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext,
+     *      javax.faces.component.UIComponent)
+     */
+    public void encodeEnd(
+        FacesContext context,
+        UIComponent component
+    )
+        throws IOException
+    {
+        getDelegateRenderer(component).encodeEnd(context, component);
+    }
+
+    private Renderer getDelegateRenderer(UIComponent component)
+    {
+        HtmlSchedule schedule = (HtmlSchedule) component;
+
+        if ((schedule == null) || (schedule.getModel() == null)) {
+            return dayDelegate;
+        }
+
+        switch (schedule.getModel().getMode()) {
+            case ScheduleModel.WEEK:
+                return weekDelegate;
+
+            case ScheduleModel.MONTH:
+                return monthDelegate;
+
+            default:
+                return dayDelegate;
+        }
+    }
+}
+//The End

Added: myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDetailedDayRenderer.java
URL: http://svn.apache.org/viewcvs/myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDetailedDayRenderer.java?rev=216175&view=auto
==============================================================================
--- myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDetailedDayRenderer.java (added)
+++ myfaces/sandbox/trunk/src/java/org/apache/myfaces/custom/schedule/renderer/ScheduleDetailedDayRenderer.java Wed Jul 13 08:33:22 2005
@@ -0,0 +1,832 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed 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.renderer;
+
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIForm;
+import javax.faces.context.FacesContext;
+import javax.faces.context.ResponseWriter;
+
+import org.apache.myfaces.custom.schedule.HtmlSchedule;
+import org.apache.myfaces.custom.schedule.model.ScheduleDay;
+import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
+import org.apache.myfaces.renderkit.html.HTML;
+
+
+/**
+ * <p>
+ * Renderer for the day and workweek views of the Schedule component
+ * </p>
+ *
+ * @author Jurgen Lust (latest modification by $Author$)
+ * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
+ * @version $Revision$
+ */
+public class ScheduleDetailedDayRenderer
+    extends AbstractScheduleRenderer
+{
+    //~ Instance fields --------------------------------------------------------
+
+    private final int rowHeightInPixels = 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;
+        Map attributes = schedule.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        //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 =
+            ((schedule.getVisibleEndHour() - schedule.getVisibleStartHour()) * 2) +
+            1;
+
+        //the grid height = 22 pixels times the number of rows + 3, for the
+        //table border and the cellpadding
+        int gridHeight = (numberOfRows * 22) + 3 + 10;
+
+        //container div for the schedule grid
+        writer.startElement(HTML.DIV_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "schedule-detailed", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "height: " + String.valueOf(gridHeight) + "px; overflow: hidden;",
+            null
+        );
+        writeBackground(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;
+        Map attributes = schedule.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        for (
+            Iterator dayIterator = schedule.getModel().iterator();
+                dayIterator.hasNext();
+        ) {
+            ScheduleDay day = (ScheduleDay) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, schedule);
+            writer.writeAttribute(HTML.CLASS_ATTR, "column", null);
+            writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%;", null);
+            writer.startElement(HTML.DIV_ELEM, schedule);
+            writer.writeAttribute(HTML.CLASS_ATTR, "column", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "position: relative; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 0;",
+                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;
+        }
+
+        HtmlSchedule schedule = (HtmlSchedule) component;
+        Map attributes = schedule.getAttributes();
+        ResponseWriter writer = context.getResponseWriter();
+
+        writeForegroundEnd(context, schedule, writer);
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    private String getCellClass(
+        HtmlSchedule schedule,
+        int column,
+        int row
+    )
+    {
+        String cellClass = "free";
+        ScheduleDay day = (ScheduleDay) schedule.getModel().get(column);
+
+        if (!day.isWorkingDay()) {
+            return cellClass;
+        }
+
+        if (
+            ((schedule.getVisibleStartHour() + (row / 2)) >= schedule.getWorkingStartHour()) &&
+                ((schedule.getVisibleStartHour() + (row / 2)) < schedule.getWorkingEndHour())
+        ) {
+            cellClass = ((row % 2) == 0) ? "even" : "uneven";
+        }
+
+        return cellClass;
+    }
+
+    private 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;
+    }
+
+    private 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++;
+            }
+        }
+    }
+
+    private 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);
+        }
+    }
+
+    private void writeBackground(
+        FacesContext context,
+        HtmlSchedule schedule,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        //a calendar component should always be inside a UIForm
+        UIForm parentForm = getParentForm(schedule);
+
+        if (parentForm == null) {
+            throw new NullPointerException("No parent UIForm found");
+        }
+
+        writer.startElement(HTML.DIV_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "background", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "position: absolute;	left: 0px; top: 0px; 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, "background", null);
+        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);
+
+        //header row, containing the column names
+        writer.startElement(HTML.TR_ELEM, schedule);
+        writer.startElement(HTML.TD_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "gutter", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "height: 21px; 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);
+
+        float columnWidth =
+            (schedule.getModel().size() == 0) ? 100
+                                              : (100 / schedule.getModel().size());
+
+        for (
+            Iterator dayIterator = schedule.getModel().iterator();
+                dayIterator.hasNext();
+        ) {
+            ScheduleDay day = (ScheduleDay) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, schedule);
+            writer.writeAttribute(HTML.CLASS_ATTR, "header", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "height: 31px; border-style: none; border-width: 0px; overflow: hidden;",
+                null
+            );
+            writer.writeAttribute(
+                HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+            );
+            writer.startElement(HTML.DIV_ELEM, schedule);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "position: relative; left: 0px; top: 0px; width: 100%; height: 100%;",
+                null
+            );
+
+            //write the date
+            writer.startElement(HTML.SPAN_ELEM, schedule);
+            writer.writeAttribute(HTML.CLASS_ATTR, "date", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "position: absolute; left: 0px; top: 0px; height: 15px; width: 100%; vertical-align: top; overflow: hidden; white-space: nowrap;",
+                null
+            );
+            writer.writeText(
+                getDateString(context, schedule.getAttributes(), day.getDate()),
+                null
+            );
+            writer.endElement(HTML.SPAN_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, "holiday", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "position: absolute; left: 0px; top: 15px; width: 100%; vertical-align: top; overflow: hidden; white-space: nowrap;",
+                    null
+                );
+                writer.writeText(day.getSpecialDayName(), null);
+                writer.endElement(HTML.SPAN_ELEM);
+            }
+
+            writer.endElement(HTML.DIV_ELEM);
+            writer.endElement(HTML.TD_ELEM);
+        }
+
+        writer.endElement(HTML.TR_ELEM);
+
+        int numberOfRows =
+            (schedule.getVisibleEndHour() - schedule.getVisibleStartHour()) * 2;
+
+        for (int row = 0; row < numberOfRows; row++) {
+            writer.startElement(HTML.TR_ELEM, schedule);
+
+            //write the hours of the day on the left
+            //this only happens on even rows, or every hour
+            if ((row % 2) == 0) {
+                writer.startElement(HTML.TD_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "gutter", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR,
+                    "height: 21px; border-style: none; border-width: 0px; overflow: hidden; padding: 0px",
+                    null
+                );
+                writer.writeAttribute("rowspan", "2", null);
+                writer.startElement(HTML.SPAN_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "hours", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, "vertical-align: top; height: 100%; padding: 0px;",
+                    null
+                );
+                writer.writeText(
+                    String.valueOf(schedule.getVisibleStartHour() + (row / 2)),
+                    null
+                );
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.startElement(HTML.SPAN_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "minutes", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, "vertical-align: top; height: 100%; padding: 0px;",
+                    null
+                );
+                writer.writeText("00", null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.endElement(HTML.TD_ELEM);
+            }
+
+            //write the cells of the day columns on this row
+            for (int column = 0; column < schedule.getModel().size();
+                    column++
+            ) {
+                writer.startElement(HTML.TD_ELEM, schedule);
+                writer.writeAttribute(
+                    HTML.CLASS_ATTR, getCellClass(schedule, column, row), null
+                );
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, "overflow: hidden; height: 21px;", null
+                );
+                writer.writeAttribute(
+                    HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+                );
+                writer.write(HTML.NBSP_ENTITY);
+                writer.endElement(HTML.TD_ELEM);
+            }
+
+            writer.endElement(HTML.TR_ELEM);
+        }
+
+        writer.endElement(HTML.TBODY_ELEM);
+        writer.endElement(HTML.TABLE_ELEM);
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    private void writeEntries(
+        FacesContext context,
+        HtmlSchedule schedule,
+        ScheduleDay day,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        TreeSet entrySet = new TreeSet();
+        Map attributes = schedule.getAttributes();
+
+        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;
+
+        UIForm parentForm = getParentForm(schedule);
+
+        //and now draw the entries in the columns
+        for (
+            Iterator entryIterator = entrySet.iterator();
+                entryIterator.hasNext();
+        ) {
+            EntryWrapper wrapper = (EntryWrapper) entryIterator.next();
+
+            if (isSelected(schedule, wrapper)) {
+                writer.startElement(HTML.DIV_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "entry-selected", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, wrapper.getBounds(schedule, columnWidth), null
+                );
+
+                //draw the tooltip
+                if (showTooltip(schedule.getAttributes())) {
+                    writer.writeAttribute(
+                        "onmouseover",
+                        getTooltipText(wrapper.entry, schedule.getAttributes()),
+                        null
+                    );
+                }
+
+                //draw the contents of the selected entry
+                writer.startElement(HTML.DIV_ELEM, null);
+                writer.writeAttribute(HTML.CLASS_ATTR, "text", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, "height: 100%; width: 100%;", null
+                );
+                writer.startElement(HTML.SPAN_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "title", null);
+                writer.writeText(wrapper.entry.getTitle(), null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.startElement("br", schedule);
+                writer.endElement("br");
+                writer.startElement(HTML.SPAN_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "subtitle", null);
+                writer.writeText(wrapper.entry.getSubtitle(), null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.endElement(HTML.DIV_ELEM);
+                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 : "a", schedule
+                );
+
+                //draw the tooltip
+                if (showTooltip(schedule.getAttributes())) {
+                    writer.writeAttribute(
+                        "onmouseover",
+                        getTooltipText(wrapper.entry, schedule.getAttributes()),
+                        null
+                    );
+                }
+
+                if (!schedule.isReadonly()) {
+                    writer.writeAttribute("href", "#", null);
+
+                    String clientId = schedule.getClientId(context);
+                    StringBuffer mousedown = new StringBuffer();
+                    mousedown.append("document.forms['");
+                    mousedown.append(parentForm.getClientId(context));
+                    mousedown.append("']['");
+                    mousedown.append(clientId);
+                    mousedown.append("'].value='");
+                    mousedown.append(wrapper.entry.getId());
+                    mousedown.append("'; document.forms['");
+                    mousedown.append(parentForm.getClientId(context));
+                    mousedown.append("'].submit()");
+                    writer.writeAttribute(
+                        "onmousedown", mousedown.toString(), null
+                    );
+                }
+
+                writer.writeAttribute(HTML.CLASS_ATTR, "entry", null);
+                writer.writeAttribute(
+                    HTML.STYLE_ATTR, wrapper.getBounds(schedule, columnWidth), null
+                );
+                writer.startElement(HTML.SPAN_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "title", null);
+                writer.writeText(wrapper.entry.getTitle(), null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.startElement("br", schedule);
+                writer.endElement("br");
+                writer.startElement(HTML.SPAN_ELEM, schedule);
+                writer.writeAttribute(HTML.CLASS_ATTR, "subtitle", null);
+                writer.writeText(wrapper.entry.getSubtitle(), null);
+                writer.endElement(HTML.SPAN_ELEM);
+                writer.endElement(schedule.isReadonly() ? HTML.DIV_ELEM : "a");
+            }
+        }
+    }
+
+    private void writeForegroundEnd(
+        FacesContext context,
+        HtmlSchedule schedule,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        writer.endElement(HTML.TR_ELEM);
+        writer.endElement(HTML.TABLE_ELEM);
+        writer.endElement(HTML.DIV_ELEM);
+    }
+
+    private void writeForegroundStart(
+        FacesContext context,
+        HtmlSchedule schedule,
+        ResponseWriter writer
+    )
+        throws IOException
+    {
+        writer.startElement(HTML.DIV_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "foreground", null);
+        writer.writeAttribute(
+            HTML.STYLE_ATTR,
+            "position: absolute;	left: 0px; top: 0px; width: 100%; height: 100%;	z-index: 2;",
+            null
+        );
+
+        writer.startElement(HTML.TABLE_ELEM, schedule);
+        writer.writeAttribute(HTML.CLASS_ATTR, "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);
+        writer.startElement(HTML.TR_ELEM, schedule);
+        writer.startElement(HTML.TD_ELEM, schedule);
+        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);
+
+        float columnWidth =
+            (schedule.getModel().size() == 0) ? 100
+                                              : (100 / schedule.getModel().size());
+
+        for (
+            Iterator dayIterator = schedule.getModel().iterator();
+                dayIterator.hasNext();
+        ) {
+            ScheduleDay day = (ScheduleDay) dayIterator.next();
+            writer.startElement(HTML.TD_ELEM, schedule);
+            writer.writeAttribute(HTML.CLASS_ATTR, "header", null);
+            writer.writeAttribute(
+                HTML.STYLE_ATTR,
+                "height: 31px; border-style: none; border-width: 0px; overflow: hidden;",
+                null
+            );
+            writer.writeAttribute(
+                HTML.WIDTH_ATTR, String.valueOf(columnWidth) + "%", null
+            );
+
+            writer.endElement(HTML.TD_ELEM);
+        }
+
+        writer.endElement(HTML.TR_ELEM);
+
+        writer.startElement(HTML.TR_ELEM, schedule);
+        writer.startElement(HTML.TD_ELEM, schedule);
+        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);
+    }
+
+    //~ Inner Classes ----------------------------------------------------------
+
+    private 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;
+        }
+
+        /**
+         * <p>
+         * Determine the bounds of this entry, in CSS position attributes
+         * </p>
+         *
+         * @param schedule the schedule
+         * @param columnWidth the width of a column
+         *
+         * @return the bounds
+         */
+        String getBounds(
+            HtmlSchedule schedule,
+            float columnWidth
+        )
+        {
+            float width = (columnWidth * colspan) - 0.5f;
+            float left = column * columnWidth;
+            Calendar cal = GregorianCalendar.getInstance();
+            cal.setTime(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, schedule.getVisibleStartHour());
+            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, schedule.getVisibleEndHour());
+            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) * rowHeightInPixels) / HALF_HOUR);
+            int height =
+                (int) (((endMillis - startMillis) * rowHeightInPixels) / HALF_HOUR);
+            StringBuffer buffer = new StringBuffer();
+            buffer.append("position: absolute; height: ");
+            buffer.append(height);
+            buffer.append("px; 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();
+        }
+
+        /**
+         * <p>
+         * Can this entry fit in the specified column?
+         * </p>
+         *
+         * @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;
+        }
+
+        /**
+         * <p>
+         * Does this entry overlap with another?
+         * </p>
+         *
+         * @param other the other entry
+         *
+         * @return whether the entries overlap
+         */
+        boolean overlaps(EntryWrapper other)
+        {
+            if ((entry.getStartTime() == null) || (entry.getEndTime() == null)) {
+                return false;
+            }
+
+            boolean returnboolean =
+                (entry.getStartTime().before(other.entry.getEndTime()) &&
+                entry.getEndTime().after(other.entry.getStartTime()));
+
+            return returnboolean;
+        }
+    }
+}
+//The End