View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.custom.schedule;
21  
22  import java.io.IOException;
23  import java.io.Serializable;
24  import java.util.Calendar;
25  import java.util.Date;
26  import java.util.Iterator;
27  import java.util.TreeSet;
28  
29  import javax.faces.component.UIComponent;
30  import javax.faces.context.FacesContext;
31  import javax.faces.context.ResponseWriter;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.myfaces.custom.schedule.model.ScheduleDay;
36  import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
37  import org.apache.myfaces.custom.schedule.util.ScheduleUtil;
38  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
39  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
40  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
41  
42  /**
43   * <p>
44   * Abstract superclass for the week and month view renderers.
45   * </p>
46   * 
47   * @since 1.1.7
48   * @author Jurgen Lust (latest modification by $Author: jlust $)
49   * @author Bruno Aranda (adaptation of Jurgen's code to myfaces)
50   * @version $Revision: 398348 $
51   */
52  public abstract class AbstractCompactScheduleRenderer extends
53          AbstractScheduleRenderer implements Serializable
54  {
55      private static final Log log = LogFactory.getLog(AbstractCompactScheduleRenderer.class);
56  
57      // ~ Methods
58      // ----------------------------------------------------------------
59  
60      /**
61       * @see javax.faces.render.Renderer#encodeChildren(javax.faces.context.FacesContext,
62       *      javax.faces.component.UIComponent)
63       */
64      public void encodeChildren(FacesContext context, UIComponent component)
65              throws IOException
66      {
67          // the children are rendered in the encodeBegin phase
68      }
69  
70      /**
71       * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext,
72       *      javax.faces.component.UIComponent)
73       */
74      public void encodeEnd(FacesContext context, UIComponent component)
75              throws IOException
76      {
77          // all rendering is done in the begin phase
78      }
79  
80      /**
81       * @return The default height, in pixels, of one row in the schedule grid
82       */
83      protected abstract int getDefaultRowHeight();
84  
85      /**
86       * <p>
87       * Draw one day in the schedule
88       * </p>
89       * 
90       * @param context
91       *            the FacesContext
92       * @param writer
93       *            the ResponseWriter
94       * @param schedule
95       *            the schedule
96       * @param day
97       *            the day that should be drawn
98       * @param cellWidth
99       *            the width of the cell
100      * @param dayOfWeek
101      *            the day of the week
102      * @param dayOfMonth
103      *            the day of the month
104      * @param isWeekend
105      *            is it a weekend day?
106      * @param isCurrentMonth
107      *            is the day in the currently selected month?
108      * @param rowspan
109      *            the rowspan for the table cell
110      * 
111      * @throws IOException
112      *             when the cell could not be drawn
113      */
114     protected void writeDayCell(FacesContext context, ResponseWriter writer,
115                                 HtmlSchedule schedule, ScheduleDay day, float cellWidth,
116                                 int dayOfWeek, int dayOfMonth, boolean isWeekend,
117                                 boolean isCurrentMonth, int rowspan) throws IOException
118     {
119         final String clientId = schedule.getClientId(context);
120         final FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context);
121         final String formId = parentFormInfo == null ? null : parentFormInfo.getFormName();
122         final String dayHeaderId = clientId + "_header_" + ScheduleUtil.getDateId(day.getDate(), schedule.getModel().getTimeZone());
123         final String dayBodyId = clientId + "_body_" + ScheduleUtil.getDateId(day.getDate(), schedule.getModel().getTimeZone());
124         writer.startElement(HTML.TD_ELEM, schedule);
125 
126         writer.writeAttribute("rowspan", String.valueOf(rowspan), null);
127 
128         boolean isToday = ScheduleUtil.isSameDay(day.getDate(), new Date(), schedule.getModel().getTimeZone());
129         
130         String dayClass = getStyleClass(schedule, isCurrentMonth ? "day" : "inactive-day") + 
131                 " " + getStyleClass(schedule, isWeekend ? "weekend" : "workday") + " " + (isToday ? getStyleClass(schedule, "today") : "");
132         
133         writer.writeAttribute(HTML.CLASS_ATTR, dayClass, null);
134 
135         // determine the height of the day in pixels
136         StringBuffer styleBuffer = new StringBuffer();
137 
138         int rowHeight = getRowHeight(schedule);
139         String myRowHeight = "height: ";
140         String myContentHeight = "height: ";
141 
142         if (rowHeight > 0)
143         {
144             if (isWeekend && schedule.isSplitWeekend())
145             {
146                 myRowHeight += (rowHeight / 2) + "px;";
147                 myContentHeight += ((rowHeight / 2) - 19) + "px;";
148             }
149             else
150             {
151                 myRowHeight += (rowHeight + (schedule.isSplitWeekend() ? 1 : 0)) + "px;"; //need to add 1 to get the weekends right
152                 myContentHeight += ((rowHeight + (schedule.isSplitWeekend() ? 1 : 0)) - 18) + "px;"; //18 instead of 19, to get the weekends right
153             }
154          }
155         else
156         {
157             myRowHeight += "100%;";
158             myContentHeight += "100%;";
159         }
160 
161         styleBuffer.append(myRowHeight);
162         styleBuffer.append("width: " + cellWidth + "%;");
163         styleBuffer.append("vertical-align: top;");
164 
165         writer.writeAttribute(HTML.STYLE_ATTR, styleBuffer.toString(), null);
166 
167         writer.startElement(HTML.DIV_ELEM, schedule);
168 
169         writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "day"),
170                               null);
171 
172         // day header
173         writer.startElement(HTML.DIV_ELEM, schedule);
174         writer.writeAttribute(HTML.CLASS_ATTR,
175                               getStyleClass(schedule, "header"), null);
176         writer.writeAttribute(HTML.STYLE_ATTR,
177                               "height: 18px; overflow: hidden", null);
178 
179         writer.startElement(HTML.ANCHOR_ELEM, schedule);
180         writer.writeAttribute(HTML.ID_ATTR, dayHeaderId, null);
181         writer.writeAttribute(HTML.HREF_ATTR, "#", null);
182 
183         //register an onclick event listener to a day header which will capture
184         //the date
185         if (!schedule.isReadonly() && schedule.isSubmitOnClick()) {
186             writer.writeAttribute(
187                     HTML.ONCLICK_ATTR,
188                     "fireScheduleDateClicked(this, event, '"
189                     + formId + "', '"
190                     + clientId
191                     + "');",
192                     null);
193         }
194 
195         writer.writeText(getDateString(context, schedule, day.getDate()), null);
196 
197         writer.endElement(HTML.ANCHOR_ELEM);
198         writer.endElement(HTML.DIV_ELEM);
199 
200         // day content
201         writer.startElement(HTML.DIV_ELEM, schedule);
202 
203         writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
204                                                              "content"), null);
205 
206         // determine the height of the day content in pixels
207         writer.writeAttribute(HTML.STYLE_ATTR, myContentHeight + " overflow: auto; vertical-align: top;", null);
208 
209         //this extra div is required, because when a scrollbar is visible and
210         //it is clicked, the fireScheduleTimeClicked() method is fired.
211         writer.startElement(HTML.DIV_ELEM, schedule);
212         writer
213         .writeAttribute(
214                 HTML.STYLE_ATTR,
215                 "height: 100%; vertical-align: top;",
216                 null);
217 
218         writer.writeAttribute(HTML.ID_ATTR, dayBodyId, null);
219         
220         //register an onclick event listener to a day cell which will capture
221         //the date
222         if (!schedule.isReadonly() && schedule.isSubmitOnClick()) {
223             writer.writeAttribute(
224                     HTML.ONCLICK_ATTR,
225                     "fireScheduleTimeClicked(this, event, '"
226                     + formId + "', '"
227                     + clientId
228                     + "');",
229                     null);
230         }
231 
232         writeEntries(context, schedule, day, writer);
233 
234         writer.endElement(HTML.DIV_ELEM);
235         writer.endElement(HTML.DIV_ELEM);
236         writer.endElement(HTML.DIV_ELEM);
237         writer.endElement(HTML.TD_ELEM);
238     }
239 
240     /**
241      * <p>
242      * Draw the schedule entries in the specified day cell
243      * </p>
244      * 
245      * @param context
246      *            the FacesContext
247      * @param schedule
248      *            the schedule
249      * @param day
250      *            the day
251      * @param writer
252      *            the ResponseWriter
253      * 
254      * @throws IOException
255      *             when the entries could not be drawn
256      */
257     protected void writeEntries(FacesContext context, HtmlSchedule schedule,
258             ScheduleDay day, ResponseWriter writer) throws IOException
259     {
260         final String clientId = schedule.getClientId(context);
261         final FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context);
262         final String formId = parentFormInfo == null ? null : parentFormInfo.getFormName();
263         final TreeSet entrySet = new TreeSet(comparator);
264 
265         for (Iterator entryIterator = day.iterator(); entryIterator.hasNext();)
266         {
267             ScheduleEntry entry = (ScheduleEntry) entryIterator.next();
268             entrySet.add(entry);
269         }
270 
271         if (entrySet.size() > 0)
272         {
273             writer.startElement(HTML.TABLE_ELEM, schedule);
274             writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
275             writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
276             writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%;", null);           
277 
278             for (Iterator entryIterator = entrySet.iterator(); entryIterator
279             .hasNext();)
280             {
281                 ScheduleEntry entry = (ScheduleEntry) entryIterator.next();
282                 writer.startElement(HTML.TR_ELEM, schedule);
283                 writer.startElement(HTML.TD_ELEM, schedule);
284 
285                 if (isSelected(schedule, entry))
286                 {
287                     writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
288                             "selected"), null);
289                 }
290 
291                 //compose the CSS style for the entry box
292                 StringBuffer entryStyle = new StringBuffer();
293                 entryStyle.append("width: 100%;");
294                 String entryColor = getEntryRenderer(schedule).getColor(context, schedule, entry, isSelected(schedule, entry));
295                 if (isSelected(schedule, entry) && entryColor != null) {
296                     entryStyle.append(" background-color: ");
297                     entryStyle.append(entryColor);
298                     entryStyle.append(";");
299                     entryStyle.append(" border-color: ");
300                     entryStyle.append(entryColor);
301                     entryStyle.append(";");
302                 }
303 
304                 writer.writeAttribute(HTML.STYLE_ATTR, entryStyle.toString(), null);
305 
306                 // draw the tooltip
307                 if (schedule.isTooltip())
308                 {
309                     getEntryRenderer(schedule).renderToolTip(context, writer,
310                             schedule, entry, isSelected(schedule, entry));
311                 }
312 
313                 if (!isSelected(schedule, entry) && !schedule.isReadonly())
314                 {
315                     writer.startElement(HTML.ANCHOR_ELEM, schedule);
316                     writer.writeAttribute(HTML.HREF_ATTR, "#", null);
317 
318                     writer.writeAttribute(
319                             HTML.ONCLICK_ATTR,
320                             "fireEntrySelected('"
321                             + formId + "', '"
322                             + clientId + "', '"
323                             + entry.getId()
324                             + "');",
325                             null);
326                 }
327 
328                 // draw the content
329                 getEntryRenderer(schedule).renderContent(context, writer, schedule,
330                         day, entry, true, isSelected(schedule, entry));
331 
332                 if (!isSelected(schedule, entry) && !schedule.isReadonly())
333                 {
334                     writer.endElement(HTML.ANCHOR_ELEM);
335                 }
336 
337                 writer.endElement(HTML.TD_ELEM);
338                 writer.endElement(HTML.TR_ELEM);
339             }
340             writer.endElement(HTML.TABLE_ELEM);
341         }
342     }
343 
344     protected boolean isSelected(HtmlSchedule schedule, ScheduleEntry entry)
345     {
346         ScheduleEntry selectedEntry = schedule.getModel().getSelectedEntry();
347 
348         if (selectedEntry == null)
349         {
350             return false;
351         }
352 
353         return selectedEntry.getId().equals(entry.getId());
354     }
355 
356     /**
357      * In the compact renderer, we don't take the y coordinate of the mouse
358      * into account when determining the last clicked date.
359      */
360     protected Date determineLastClickedDate(HtmlSchedule schedule, String dateId, String yPos) {
361         //the dateId is the schedule client id + "_" + yyyyMMdd
362         String day = dateId.substring(dateId.lastIndexOf("_") + 1);
363         Date date = ScheduleUtil.getDateFromId(day, schedule.getModel().getTimeZone());
364 
365         Calendar cal = getCalendarInstance(schedule, date != null ? date : new Date());
366         cal.set(Calendar.HOUR_OF_DAY, schedule.getVisibleStartHour());
367         cal.set(Calendar.MINUTE, 0);
368         cal.set(Calendar.SECOND, 0);
369         cal.set(Calendar.MILLISECOND, 0);
370         log.debug("last clicked datetime: " + cal.getTime());
371         return cal.getTime();
372     }
373 
374 }
375 // The End