1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.custom.schedule;
21
22 import java.io.IOException;
23 import java.io.Serializable;
24 import java.text.DateFormat;
25 import java.util.Calendar;
26 import java.util.Date;
27 import java.util.Iterator;
28 import java.util.TimeZone;
29 import java.util.TreeSet;
30
31 import javax.faces.component.UIComponent;
32 import javax.faces.context.FacesContext;
33 import javax.faces.context.ResponseWriter;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.myfaces.custom.schedule.model.HalfHourInterval;
38 import org.apache.myfaces.custom.schedule.model.Interval;
39 import org.apache.myfaces.custom.schedule.model.ScheduleDay;
40 import org.apache.myfaces.custom.schedule.model.ScheduleEntry;
41 import org.apache.myfaces.custom.schedule.util.ScheduleUtil;
42 import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
43 import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
44 import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
45
46
47
48
49
50
51
52
53
54
55
56 public class ScheduleDetailedDayRenderer extends AbstractScheduleRenderer
57 implements Serializable
58 {
59 private static final Log log = LogFactory.getLog(ScheduleDetailedDayRenderer.class);
60 private static final long serialVersionUID = -5103791076091317355L;
61
62
63
64 private final int defaultRowHeightInPixels = 22;
65
66
67
68
69
70
71
72 public void encodeBegin(FacesContext context, UIComponent component)
73 throws IOException
74 {
75 if (!component.isRendered())
76 {
77 return;
78 }
79
80 super.encodeBegin(context, component);
81
82 HtmlSchedule schedule = (HtmlSchedule) component;
83 ResponseWriter writer = context.getResponseWriter();
84 int rowHeight = getRowHeight(schedule);
85
86
87
88 int numberOfRows = ((getRenderedEndHour(schedule) - getRenderedStartHour(schedule)) * 2) + 1;
89
90
91
92 int gridHeight = (numberOfRows * rowHeight) + 3 + 10;
93
94
95 writer.startElement(HTML.DIV_ELEM, schedule);
96 writer.writeAttribute(HTML.CLASS_ATTR, "schedule-detailed-"
97 + schedule.getTheme(), null);
98 writer.writeAttribute(HTML.STYLE_ATTR, "height: "
99 + String.valueOf(gridHeight) + "px; overflow: hidden;", null);
100 writeBackgroundStart(context, schedule, writer);
101 writeForegroundStart(context, schedule, writer);
102 }
103
104
105
106
107
108 public void encodeChildren(FacesContext context, UIComponent component)
109 throws IOException
110 {
111 if (!component.isRendered())
112 {
113 return;
114 }
115
116 HtmlSchedule schedule = (HtmlSchedule) component;
117 ResponseWriter writer = context.getResponseWriter();
118 String clientId = schedule.getClientId(context);
119 FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context);
120 String formId = parentFormInfo == null ? null : parentFormInfo.getFormName();
121
122 for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator
123 .hasNext();)
124 {
125 ScheduleDay day = (ScheduleDay) dayIterator.next();
126 String dayBodyId = clientId + "_body_" + ScheduleUtil.getDateId(day.getDate(), schedule.getModel().getTimeZone());
127 writer.startElement(HTML.TD_ELEM, schedule);
128 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
129 "column"), null);
130 writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%;", null);
131 writer.startElement(HTML.DIV_ELEM, schedule);
132 writer.writeAttribute(HTML.ID_ATTR, dayBodyId, null);
133 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
134 "column"), null);
135 writer.writeAttribute(
136 HTML.STYLE_ATTR,
137 "position: relative; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 0;",
138 null);
139
140
141 if (!schedule.isReadonly() && schedule.isSubmitOnClick()) {
142 writer.writeAttribute(
143 HTML.ONMOUSEUP_ATTR,
144 "fireScheduleTimeClicked(this, event, '"
145 + formId + "', '"
146 + clientId
147 + "');",
148 null);
149 }
150 writeEntries(context, schedule, day, writer);
151 writer.endElement(HTML.DIV_ELEM);
152 writer.endElement(HTML.TD_ELEM);
153 }
154 }
155
156
157
158
159
160 public void encodeEnd(FacesContext context, UIComponent component)
161 throws IOException
162 {
163 if (!component.isRendered())
164 {
165 return;
166 }
167
168 ResponseWriter writer = context.getResponseWriter();
169
170 writeForegroundEnd(writer);
171 writeBackgroundEnd(writer);
172 writer.endElement(HTML.DIV_ELEM);
173 }
174
175 protected String getCellClass(HtmlSchedule schedule, ScheduleDay day, boolean even, int hour)
176 {
177 String cellClass = "free";
178 if (!day.isWorkingDay())
179 {
180 return getStyleClass(schedule, cellClass);
181 }
182
183 if (hour >= schedule.getWorkingStartHour()
184 && hour < schedule.getWorkingEndHour())
185 {
186 cellClass = even ? "even" : "uneven";
187 }
188
189 return getStyleClass(schedule, cellClass);
190 }
191
192 protected boolean isSelected(HtmlSchedule schedule, EntryWrapper entry)
193 {
194 ScheduleEntry selectedEntry = schedule.getModel().getSelectedEntry();
195
196 if (selectedEntry == null)
197 {
198 return false;
199 }
200
201 boolean returnboolean = selectedEntry.getId().equals(
202 entry.entry.getId());
203
204 return returnboolean;
205 }
206
207 protected void maximizeEntries(EntryWrapper[] entries, int numberOfColumns)
208 {
209 for (int i = 0; i < entries.length; i++)
210 {
211 EntryWrapper entry = entries[i];
212
213
214 while (((entry.column + entry.colspan) < numberOfColumns)
215 && entry.canFitInColumn(entry.column + entry.colspan))
216 {
217 entry.colspan++;
218 }
219 }
220 }
221
222 protected void scanEntries(EntryWrapper[] entries, int index)
223 {
224 if (entries.length <= 0)
225 {
226 return;
227 }
228
229 EntryWrapper entry = entries[index];
230 entry.column = 0;
231
232
233 for (int i = 0; i < index; i++)
234 {
235 if (entry.overlaps(entries[i]))
236 {
237 entry.overlappingEntries.add(entries[i]);
238 entries[i].overlappingEntries.add(entry);
239 }
240 }
241
242
243 while (!entry.canFitInColumn(entry.column))
244 {
245 entry.column++;
246 }
247
248
249 if (++index < entries.length)
250 {
251 scanEntries(entries, index);
252 }
253 }
254
255 protected void writeGutter(FacesContext context, HtmlSchedule schedule,
256 ResponseWriter writer, boolean useIntervalLabels) throws IOException
257 {
258 final int rowHeight = getRowHeight(schedule);
259 final int headerHeight = rowHeight + 9;
260
261 int startHour = getRenderedStartHour(schedule);
262 int endHour = getRenderedEndHour(schedule);
263
264 DateFormat hourFormater = getDateFormat(context, schedule,
265 HtmlSchedule.HOUR_NOTATION_12.equals(schedule.getHourNotation()) ? "h" : "HH");
266 DateFormat minuteFormater = getDateFormat(context, schedule,
267 HtmlSchedule.HOUR_NOTATION_12.equals(schedule.getHourNotation()) ? "':'mma" : "mm");
268 DateFormat shortMinuteFormater = getDateFormat(context, schedule,
269 HtmlSchedule.HOUR_NOTATION_12.equals(schedule.getHourNotation()) ? "a" : "mm");
270
271 ScheduleDay day = (ScheduleDay) schedule.getModel().iterator().next();
272
273 writer.startElement(HTML.TABLE_ELEM, schedule);
274 writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
275 writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null);
276 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "background"), null);
277 writer.writeAttribute(HTML.STYLE_ATTR, "height: 100%", null);
278 writer.startElement(HTML.TBODY_ELEM, schedule);
279
280 writer.startElement(HTML.TR_ELEM, schedule);
281
282
283 writer.startElement(HTML.TD_ELEM, schedule);
284 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "gutter"), null);
285 writer.writeAttribute(
286 HTML.STYLE_ATTR,
287 "height: "
288 + headerHeight
289 + "px; border-style: none; border-width: 0px; overflow: hidden; padding: 0px",
290 null);
291 writer.startElement(HTML.DIV_ELEM, schedule);
292 writer.writeAttribute(HTML.STYLE_ATTR, "height: 1px; width: 56px", null);
293 writer.endElement(HTML.DIV_ELEM);
294 writer.endElement(HTML.TD_ELEM);
295 writer.endElement(HTML.TR_ELEM);
296
297
298 Iterator intervalIt = day.getIntervals(startHour, endHour).iterator();
299
300 boolean renderGutter = true;
301
302 while (intervalIt.hasNext())
303 {
304 Interval interval = (Interval) intervalIt.next();
305 int intervalHeight = calcRowHeight(rowHeight, interval.getDuration()) - 1;
306
307
308 if (intervalHeight <= 0)
309 {
310 continue;
311 }
312
313 if (!renderGutter)
314 {
315 renderGutter = true;
316 continue;
317 }
318
319 writer.startElement(HTML.TR_ELEM, schedule);
320
321 int gutterHeight = intervalHeight;
322
323 if (day.getIntervals() == null && interval.getStartMinutes(getTimeZone(schedule)) == 0)
324 {
325 gutterHeight = (gutterHeight * 2) + 1;
326 renderGutter = false;
327 }
328
329
330
331 writer.startElement(HTML.TD_ELEM, schedule);
332 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "gutter"), null);
333 writer.writeAttribute(
334 HTML.STYLE_ATTR,
335 "height: " + gutterHeight
336 + "px; border-style: none; border-width: 0px; overflow: hidden; padding: 0px",
337 null);
338 if (interval.getDuration() >= HalfHourInterval.HALF_HOUR)
339 {
340 if (!useIntervalLabels || interval.getLabel() == null)
341 {
342 writer.startElement(HTML.SPAN_ELEM, schedule);
343 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
344 renderGutter ? "minutes" : "hours"), null);
345 writer.writeAttribute(HTML.STYLE_ATTR,
346 "vertical-align: top; height: 100%; padding: 0px;",
347 null);
348 writer.writeText(hourFormater.format(interval.getStartTime()), null);
349 writer.endElement(HTML.SPAN_ELEM);
350 }
351 writer.startElement(HTML.SPAN_ELEM, schedule);
352 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
353 "minutes"), null);
354 writer.writeAttribute(HTML.STYLE_ATTR,
355 "vertical-align: top; height: 100%; padding: 0px;",
356 null);
357 if (useIntervalLabels && interval.getLabel() != null)
358 {
359 writer.writeText(interval.getLabel(), null);
360 }
361 else
362 {
363 writer.writeText((renderGutter ? minuteFormater : shortMinuteFormater).format(interval.getStartTime()), null);
364 }
365 writer.endElement(HTML.SPAN_ELEM);
366 }
367 writer.endElement(HTML.TD_ELEM);
368 writer.endElement(HTML.TR_ELEM);
369 }
370
371 writer.endElement(HTML.TBODY_ELEM);
372 writer.endElement(HTML.TABLE_ELEM);
373 }
374
375 protected void writeBackgroundStart(FacesContext context, HtmlSchedule schedule,
376 ResponseWriter writer) throws IOException
377 {
378 boolean repeatedIntervals = schedule.getModel().containsRepeatedIntervals();
379
380 writer.startElement(HTML.TABLE_ELEM, schedule);
381 writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
382 writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
383 writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%", null);
384 writer.startElement(HTML.TBODY_ELEM, schedule);
385 writer.startElement(HTML.TR_ELEM, schedule);
386 writer.startElement(HTML.TD_ELEM, schedule);
387 writer.writeAttribute(HTML.STYLE_ATTR, "width: 56px; vertical-align: top;", null);
388
389
390 writeGutter(context, schedule, writer, repeatedIntervals);
391
392 writer.endElement(HTML.TD_ELEM);
393 writer.startElement(HTML.TD_ELEM, schedule);
394 writer.writeAttribute(HTML.STYLE_ATTR, "width: 99%; vertical-align: top;", null);
395
396 final String clientId = schedule.getClientId(context);
397 FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context);
398 String formId = parentFormInfo == null ? null : parentFormInfo.getFormName();
399
400 final int rowHeight = getRowHeight(schedule);
401 final int headerHeight = rowHeight + 9;
402 writer.startElement(HTML.DIV_ELEM, schedule);
403 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
404 "background"), null);
405 writer
406 .writeAttribute(
407 HTML.STYLE_ATTR,
408 "position: relative; width: 100%; height: 100%; z-index: 0;",
409 null);
410
411
412 writer.startElement(HTML.TABLE_ELEM, schedule);
413 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
414 "background"), null);
415 writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
416 writer.writeAttribute(HTML.CELLSPACING_ATTR, "0", null);
417 writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%",
418 null);
419 writer.startElement(HTML.TBODY_ELEM, schedule);
420 writer.startElement(HTML.TR_ELEM, schedule);
421
422 float columnWidth = (schedule.getModel().size() == 0) ? 100
423 : (100 / schedule.getModel().size());
424
425 int startHour = getRenderedStartHour(schedule);
426 int endHour = getRenderedEndHour(schedule);
427
428 ScheduleDay day = null;
429
430 for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator.hasNext();)
431 {
432 writer.startElement(HTML.TD_ELEM, schedule);
433
434 writer.writeAttribute(HTML.STYLE_ATTR, "width: " + String.valueOf(columnWidth)+ "%", null);
435
436 day = (ScheduleDay) dayIterator.next();
437
438 writer.startElement(HTML.TABLE_ELEM, schedule);
439 writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
440 writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null);
441 writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%",
442 null);
443 writer.startElement(HTML.TBODY_ELEM, schedule);
444
445 writer.startElement(HTML.TR_ELEM, schedule);
446
447
448 final String dayHeaderId = clientId + "_header_" + ScheduleUtil.getDateId(day.getDate(), schedule.getModel().getTimeZone());
449
450 writer.startElement(HTML.TH_ELEM, schedule);
451 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
452 "header"), null);
453 writer
454 .writeAttribute(
455 HTML.STYLE_ATTR,
456 "height: " + headerHeight + "px; vertical-align: top; border-style: none; border-width: 0px; overflow: hidden;",
457 null);
458
459 boolean isToday = ScheduleUtil.isSameDay(day.getDate(), new Date(), schedule.getModel().getTimeZone());
460
461
462 writer.startElement(HTML.ANCHOR_ELEM, schedule);
463 writer.writeAttribute(HTML.ID_ATTR, dayHeaderId, null);
464 writer.writeAttribute(HTML.HREF_ATTR, "#", null);
465 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule, "date")
466 + (isToday ? " today" : ""), null);
467 writer
468 .writeAttribute(
469 HTML.STYLE_ATTR,
470 "display: block; height: 50%; width: 100%; overflow: hidden; white-space: nowrap;",
471 null);
472
473
474
475 if (!schedule.isReadonly() && schedule.isSubmitOnClick()) {
476 writer.writeAttribute(
477 HTML.ONCLICK_ATTR,
478 "fireScheduleDateClicked(this, event, '"
479 + formId + "', '"
480 + clientId
481 + "');",
482 null);
483 }
484
485 writer.writeText(getDateString(context, schedule, day.getDate()),
486 null);
487 writer.endElement(HTML.ANCHOR_ELEM);
488
489
490 if ((day.getSpecialDayName() != null)
491 && (day.getSpecialDayName().length() > 0))
492 {
493 writer.startElement(HTML.SPAN_ELEM, schedule);
494 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
495 "holiday"), null);
496 writer
497 .writeAttribute(
498 HTML.STYLE_ATTR,
499 "height: 50%; width: 100%; overflow: hidden; white-space: nowrap;",
500 null);
501 writer.writeText(day.getSpecialDayName(), null);
502 writer.endElement(HTML.SPAN_ELEM);
503 }
504
505 writer.endElement(HTML.TH_ELEM);
506 writer.endElement(HTML.TR_ELEM);
507
508
509 Iterator intervalIt = day.getIntervals(startHour, endHour).iterator();
510 boolean even = false;
511
512 while (intervalIt.hasNext())
513 {
514 Interval interval = (Interval) intervalIt.next();
515 int intervalHeight = calcRowHeight(rowHeight, interval.getDuration()) - 1;
516
517
518 if (intervalHeight <= 0)
519 {
520 continue;
521 }
522
523 writer.startElement(HTML.TR_ELEM, schedule);
524
525 writer.startElement(HTML.TD_ELEM, schedule);
526 writer.writeAttribute(HTML.CLASS_ATTR, getCellClass(schedule,
527 day, even, interval.getStartHours(getTimeZone(schedule))), null);
528 writer.writeAttribute(HTML.STYLE_ATTR,
529 "overflow: hidden; height: " + intervalHeight + "px;", null);
530 if (!repeatedIntervals && interval.getLabel() != null)
531 {
532 writer.write(interval.getLabel());
533 }
534 writer.endElement(HTML.TD_ELEM);
535
536 writer.endElement(HTML.TR_ELEM);
537
538 even = !even;
539 }
540
541 writer.endElement(HTML.TBODY_ELEM);
542 writer.endElement(HTML.TABLE_ELEM);
543
544 writer.endElement(HTML.TD_ELEM);
545 }
546
547 writer.endElement(HTML.TR_ELEM);
548 writer.endElement(HTML.TBODY_ELEM);
549 writer.endElement(HTML.TABLE_ELEM);
550 }
551
552 protected void writeBackgroundEnd(ResponseWriter writer) throws IOException
553 {
554 writer.endElement(HTML.DIV_ELEM);
555
556 writer.endElement(HTML.TD_ELEM);
557 writer.endElement(HTML.TR_ELEM);
558 writer.endElement(HTML.TBODY_ELEM);
559 writer.endElement(HTML.TABLE_ELEM);
560 }
561
562
563
564
565
566
567
568
569 private int calcRowHeight(int halfHourHeight, long duration) {
570
571 return duration == HalfHourInterval.HALF_HOUR ? halfHourHeight :
572 (int)((halfHourHeight / (float)(30 * 60 * 1000)) * duration);
573 }
574
575 protected int getRenderedStartHour(HtmlSchedule schedule)
576 {
577 int startHour = schedule.getVisibleStartHour();
578
579
580
581 if (!expandToFitEntries(schedule)) return startHour;
582
583 for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator.hasNext();)
584 {
585 ScheduleDay day = (ScheduleDay) dayIterator.next();
586 int dayStart = day.getFirstEventHour();
587
588 if (dayStart < startHour) {
589 startHour = dayStart;
590 }
591 }
592
593 return startHour;
594 }
595
596 protected int getRenderedEndHour(HtmlSchedule schedule)
597 {
598 int endHour = schedule.getVisibleEndHour();
599
600
601
602 if (!expandToFitEntries(schedule)) return endHour;
603
604 for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator.hasNext();)
605 {
606 ScheduleDay day = (ScheduleDay) dayIterator.next();
607 int dayEnd = day.getLastEventHour();
608
609 if (dayEnd > endHour) {
610 endHour = dayEnd;
611 }
612 }
613
614 return endHour;
615 }
616
617 protected void writeEntries(FacesContext context, HtmlSchedule schedule,
618 ScheduleDay day, ResponseWriter writer) throws IOException
619 {
620 final String clientId = schedule.getClientId(context);
621 FormInfo parentFormInfo = RendererUtils.findNestingForm(schedule, context);
622 String formId = parentFormInfo == null ? null : parentFormInfo.getFormName();
623
624 TreeSet entrySet = new TreeSet();
625
626 for (Iterator entryIterator = day.iterator(); entryIterator.hasNext();)
627 {
628 entrySet.add(new EntryWrapper((ScheduleEntry) entryIterator.next(),
629 day));
630 }
631
632 EntryWrapper[] entries = (EntryWrapper[]) entrySet
633 .toArray(new EntryWrapper[entrySet.size()]);
634
635
636 scanEntries(entries, 0);
637
638
639 int maxColumn = 0;
640
641 for (Iterator entryIterator = entrySet.iterator(); entryIterator
642 .hasNext();)
643 {
644 EntryWrapper wrapper = (EntryWrapper) entryIterator.next();
645 maxColumn = Math.max(wrapper.column, maxColumn);
646 }
647
648 int numberOfColumns = maxColumn + 1;
649
650
651 maximizeEntries(entries, numberOfColumns);
652
653
654 float columnWidth = 100 / numberOfColumns;
655
656
657 for (Iterator entryIterator = entrySet.iterator(); entryIterator
658 .hasNext();)
659 {
660 EntryWrapper wrapper = (EntryWrapper) entryIterator.next();
661 boolean selected = isSelected(schedule, wrapper);
662
663 StringBuffer entryStyle = new StringBuffer();
664 entryStyle.append(wrapper.getBounds(schedule, columnWidth));
665 String entryBorderColor = getEntryRenderer(schedule).getColor(
666 context, schedule, wrapper.entry, selected);
667 if (entryBorderColor != null)
668 {
669 entryStyle.append(" border-color: ");
670 entryStyle.append(entryBorderColor);
671 entryStyle.append(";");
672 }
673
674 if (selected)
675 {
676 writer.startElement(HTML.DIV_ELEM, schedule);
677 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
678 "entry-selected"), null);
679 writer.writeAttribute(HTML.STYLE_ATTR, entryStyle.toString(),
680 null);
681
682
683 if (schedule.isTooltip())
684 {
685 getEntryRenderer(schedule).renderToolTip(context, writer,
686 schedule, wrapper.entry, selected);
687 }
688
689
690 getEntryRenderer(schedule).renderContent(context, writer,
691 schedule, day, wrapper.entry, false, selected);
692 writer.endElement(HTML.DIV_ELEM);
693 }
694 else
695 {
696
697
698 writer.startElement(
699 schedule.isReadonly() ? HTML.DIV_ELEM : HTML.ANCHOR_ELEM, schedule);
700
701
702 if (schedule.isTooltip())
703 {
704 getEntryRenderer(schedule).renderToolTip(context, writer,
705 schedule, wrapper.entry, selected);
706 }
707
708 if (!schedule.isReadonly())
709 {
710 writer.writeAttribute(HTML.HREF_ATTR, "#", null);
711
712 writer.writeAttribute(
713 HTML.ONCLICK_ATTR,
714 "fireEntrySelected('"
715 + formId + "', '"
716 + clientId + "', '"
717 + wrapper.entry.getId()
718 + "');",
719 null);
720 }
721
722 writer.writeAttribute(HTML.CLASS_ATTR, getEntryRenderer(schedule).getEntryClass(schedule, wrapper.entry), null);
723 writer.writeAttribute(HTML.STYLE_ATTR, entryStyle.toString(),
724 null);
725
726
727 getEntryRenderer(schedule).renderContent(context, writer,
728 schedule, day, wrapper.entry, false, selected);
729
730 writer.endElement(schedule.isReadonly() ? HTML.DIV_ELEM : HTML.ANCHOR_ELEM);
731 }
732 }
733 }
734
735 protected void writeForegroundEnd(ResponseWriter writer) throws IOException
736 {
737 writer.endElement(HTML.TR_ELEM);
738 writer.endElement(HTML.TABLE_ELEM);
739 writer.endElement(HTML.DIV_ELEM);
740 }
741
742 protected void writeForegroundStart(FacesContext context,
743 HtmlSchedule schedule, ResponseWriter writer) throws IOException
744 {
745 final int rowHeight = getRowHeight(schedule) - 1;
746 final int headerHeight = rowHeight + 10;
747
748 writer.startElement(HTML.DIV_ELEM, schedule);
749 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
750 "foreground"), null);
751 writer.writeAttribute(
752 HTML.STYLE_ATTR,
753 "position: absolute; left: 0px; top: " + headerHeight + "px; width: 100%; height: 100%; z-index: 2;",
754 null);
755
756 writer.startElement(HTML.TABLE_ELEM, schedule);
757 writer.writeAttribute(HTML.CLASS_ATTR, getStyleClass(schedule,
758 "foreground"), null);
759 writer.writeAttribute(HTML.CELLSPACING_ATTR, "1", null);
760 writer.writeAttribute(HTML.CELLPADDING_ATTR, "0", null);
761 writer.writeAttribute(HTML.STYLE_ATTR, "width: 100%; height: 100%",
762 null);
763
764 float columnWidth = (schedule.getModel().size() == 0) ? 100
765 : (100 / schedule.getModel().size());
766
767 for (Iterator dayIterator = schedule.getModel().iterator(); dayIterator
768 .hasNext();)
769 {
770 dayIterator.next();
771 writer.startElement("col", schedule);
772
773 writer.writeAttribute(HTML.STYLE_ATTR,
774 "width: " + String.valueOf(columnWidth) + "%;", null);
775 writer.endElement("col");
776 }
777
778 writer.startElement(HTML.TR_ELEM, schedule);
779 }
780
781
782
783 protected int getDefaultRowHeight()
784 {
785 return defaultRowHeightInPixels;
786 }
787
788
789
790
791
792 protected Date determineLastClickedDate(HtmlSchedule schedule, String dateId, String yPos) {
793
794 String day = dateId.substring(dateId.lastIndexOf("_") + 1);
795 Date date = ScheduleUtil.getDateFromId(day, schedule.getModel().getTimeZone());
796
797 Calendar cal = getCalendarInstance(schedule, date != null ? date : new Date());
798 cal.set(Calendar.HOUR_OF_DAY, getRenderedStartHour(schedule));
799
800 try {
801 int y = Integer.parseInt(yPos);
802 int halfHourHeight = getRowHeight(schedule);
803 int minutes = y * 30 / halfHourHeight;
804 cal.add(Calendar.MINUTE, minutes);
805 } catch (NumberFormatException nfe) {
806 log.debug("y position is not a number");
807 }
808 log.debug("last clicked datetime: " + cal.getTime());
809 return cal.getTime();
810 }
811
812
813
814
815
816
817
818
819
820
821 protected boolean renderZeroLengthEntries(UIComponent component) {
822 if (component instanceof UIScheduleBase)
823 {
824 UIScheduleBase schedule = (UIScheduleBase) component;
825 return schedule.isRenderZeroLengthEntries();
826 } else {
827 return false;
828 }
829 }
830
831
832
833
834
835
836
837
838
839
840 protected boolean expandToFitEntries(UIComponent component) {
841 if (component instanceof HtmlSchedule)
842 {
843 HtmlSchedule schedule = (HtmlSchedule) component;
844 return schedule.isExpandToFitEntries();
845 }
846 return false;
847 }
848
849
850 protected class EntryWrapper implements Comparable
851 {
852
853
854 private static final int HALF_HOUR = 1000 * 60 * 30;
855
856
857
858 private final ScheduleDay day;
859 private final ScheduleEntry entry;
860 private final TreeSet overlappingEntries;
861 private int colspan;
862 private int column;
863
864
865
866 EntryWrapper(ScheduleEntry entry, ScheduleDay day)
867 {
868 this.entry = entry;
869 this.day = day;
870 this.column = 0;
871 this.colspan = 1;
872 this.overlappingEntries = new TreeSet();
873 }
874
875
876
877
878
879
880 public int compareTo(Object o)
881 {
882 return comparator.compare(entry, o);
883 }
884
885
886
887
888 public boolean equals(Object o)
889 {
890 if (o instanceof EntryWrapper)
891 {
892 EntryWrapper other = (EntryWrapper) o;
893
894 boolean returnboolean = (entry.getStartTime()
895 .equals(other.entry.getStartTime()))
896 && (entry.getEndTime().equals(other.entry.getEndTime()))
897 && (entry.getId().equals(other.entry.getId()))
898 && (day.equals(other.day));
899
900
901
902
903
904
905
906
907 return returnboolean;
908 }
909
910 return false;
911 }
912
913
914
915
916 public int hashCode()
917 {
918 int returnint = entry.getStartTime().hashCode()
919 ^ entry.getEndTime().hashCode() ^ entry.getId().hashCode();
920
921 return returnint;
922 }
923
924
925
926
927
928
929
930
931
932
933
934 String getBounds(HtmlSchedule schedule, float columnWidth)
935 {
936 int rowHeight = getRowHeight(schedule);
937 float width = (columnWidth * colspan) - 0.5f;
938 float left = column * columnWidth;
939 Calendar cal = getCalendarInstance(schedule, day.getDate());
940
941 int curyear = cal.get(Calendar.YEAR);
942 int curmonth = cal.get(Calendar.MONTH);
943 int curday = cal.get(Calendar.DATE);
944
945 cal.setTime(entry.getStartTime());
946 cal.set(curyear, curmonth, curday);
947
948 long startMillis = cal.getTimeInMillis();
949 cal.set(Calendar.HOUR_OF_DAY, getRenderedStartHour(schedule));
950 cal.set(Calendar.MINUTE, 0);
951 cal.set(Calendar.SECOND, 0);
952 cal.set(Calendar.MILLISECOND, 0);
953
954 long visibleStartMillis = cal.getTimeInMillis();
955 startMillis = day.equalsDate(entry.getStartTime()) ? Math.max(
956 startMillis, visibleStartMillis) : visibleStartMillis;
957 cal.setTime(entry.getEndTime());
958 cal.set(curyear, curmonth, curday);
959
960 long endMillis = cal.getTimeInMillis();
961 cal.set(Calendar.HOUR_OF_DAY, getRenderedEndHour(schedule));
962 cal.set(Calendar.MINUTE, 0);
963 cal.set(Calendar.SECOND, 0);
964 cal.set(Calendar.MILLISECOND, 0);
965
966 long visibleEndMillis = cal.getTimeInMillis();
967 endMillis = day.equalsDate(entry.getEndTime()) ? Math.min(
968 endMillis, visibleEndMillis) : visibleEndMillis;
969
970 int top = (int) (((startMillis - visibleStartMillis) * rowHeight) / HALF_HOUR);
971 int height = (int) (((endMillis - startMillis) * rowHeight) / HALF_HOUR);
972 StringBuffer buffer = new StringBuffer();
973
974 boolean entryVisible = height > 0 || renderZeroLengthEntries(schedule);
975
976 if (!entryVisible)
977 {
978 buffer.append("visibility: hidden; ");
979 }
980 buffer.append("position: absolute; height: ");
981 if (height > 2)
982 {
983
984 buffer.append((height - 2) + "px");
985 } else if (height > 0)
986 {
987 buffer.append(height + "px");
988 } else if (entryVisible)
989 {
990 buffer.append("auto");
991 } else
992 {
993 buffer.append("0px");
994 }
995 buffer.append("; top: ");
996 buffer.append(top);
997 buffer.append("px; left: ");
998 buffer.append(left);
999 buffer.append("%; width: ");
1000 buffer.append(width);
1001 buffer
1002 .append("%; padding: 0px; overflow: hidden; border-width: 1.0px; border-style:solid;");
1003
1004 return buffer.toString();
1005 }
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016 boolean canFitInColumn(int column)
1017 {
1018 for (Iterator overlapIterator = overlappingEntries.iterator(); overlapIterator
1019 .hasNext();)
1020 {
1021 EntryWrapper overlap = (EntryWrapper) overlapIterator.next();
1022
1023 if (overlap.column == column)
1024 {
1025 return false;
1026 }
1027 }
1028
1029 return true;
1030 }
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042 Date minimumEndTime() {
1043 Date start = entry.getStartTime();
1044 Date end = entry.getEndTime();
1045
1046 return end != null ?
1047 (end.after(start) ? end : new Date(start.getTime() + HALF_HOUR))
1048 : null;
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060 boolean overlaps(EntryWrapper other)
1061 {
1062 Date start = entry.getStartTime();
1063 Date end = minimumEndTime();
1064
1065 if ((start == null) || (end == null))
1066 {
1067 return false;
1068 }
1069
1070 boolean returnboolean = (start.before(
1071 other.minimumEndTime()) && end.after(
1072 other.entry.getStartTime()));
1073
1074 return returnboolean;
1075 }
1076 }
1077
1078
1079 protected int getRowHeight(UIScheduleBase schedule)
1080 {
1081 if (schedule != null) {
1082 int height = schedule.getDetailedRowHeight();
1083 return height <= 0 ? getDefaultRowHeight() : height;
1084 }
1085 return getDefaultRowHeight();
1086 }
1087
1088 private TimeZone getTimeZone(UIScheduleBase schedule)
1089 {
1090
1091 return schedule != null && schedule.getModel().getTimeZone() != null ?
1092 schedule.getModel().getTimeZone()
1093 : TimeZone.getDefault();
1094 }
1095 }
1096