OSDN Git Service

Automatically align underspecified tasks boundaries on project boundaries when possible.
[tjqt4port/tj2qt4.git] / taskjuggler / HTMLReportElement.cpp
1 /*
2  * HTMLReportElement.cpp - TaskJuggler
3  *
4  * Copyright (c) 2001, 2002, 2003, 2004, 2005 by Chris Schlaeger <cs@kde.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of version 2 of the GNU General Public License as
8  * published by the Free Software Foundation.
9  *
10  * $Id$
11  */
12
13 #include "HTMLReportElement.h"
14
15 #include <assert.h>
16
17 #include "TjMessageHandler.h"
18 #include "tjlib-internal.h"
19 #include "Project.h"
20 #include "Resource.h"
21 #include "Account.h"
22 #include "BookingList.h"
23 #include "Utility.h"
24 #include "MacroTable.h"
25 #include "TableLineInfo.h"
26 #include "TableCellInfo.h"
27 #include "ReferenceAttribute.h"
28 #include "TextAttribute.h"
29 #include "HTMLReport.h"
30 #include "UsageLimits.h"
31
32 HTMLReportElement::HTMLReportElement(Report* r, const QString& df, int dl) :
33     ReportElement(r, df, dl),
34     HTMLPrimitives()
35 {
36 }
37
38 void
39 HTMLReportElement::generateHeader()
40 {
41     if (!rawHead.isEmpty())
42     {
43         puts(rawHead);
44         puts("\n");
45     }
46     if (!headline.isEmpty())
47     {
48         puts("<h3>");
49         puts(htmlFilter(headline));
50         puts("</h3>\n");
51     }
52     if (!caption.isEmpty())
53     {
54         puts("<p>");
55         puts(htmlFilter(caption));
56         puts("</p>\n");
57     }
58 }
59
60 void
61 HTMLReportElement::generateFooter()
62 {
63     if (!rawTail.isEmpty())
64     {
65         puts(rawTail);
66         puts("\n");
67     }
68 }
69
70 void
71 HTMLReportElement::generateTableHeader()
72 {
73     // Header line 1
74     s() << "<table align=\"center\" cellpadding=\"2\" "
75         << "style=\"background-color:#000000\"";
76     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
77         s() << " class=\"tj_table\"";
78     s() << ">" << endl;
79     s() << " <thead>" << endl
80         << "  <tr valign=\"middle\""
81         << " style=\"background-color:" << colors.getColorName("header") << "; "
82         << "font-size:110%; font-weight:bold; text-align:center\"";
83     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
84         s() << " class=\"tj_header_row\"";
85     s() << ">" << endl;
86     for (QPtrListIterator<TableColumnInfo> it(columns); it; ++it )
87     {
88         if (columnFormat[(*it)->getName()])
89         {
90             TableCellInfo tci(columnFormat[(*it)->getName()], 0, *it);
91             (*this.*(columnFormat[(*it)->getName()]->genHeadLine1))
92                 (&tci);
93         }
94         else if ((*it)->getName() == "costs")
95         {
96             TJMH.errorMessage
97                 (i18n("'costs' has been deprecated. Use 'cost' instead."));
98             return;
99         }
100         else
101         {
102             TJMH.errorMessage
103                 (i18n("Unknown Column '%1' for HTML Report")
104                  .arg((*it)->getName()));
105             return;
106         }
107     }
108     s() << "  </tr>" << endl;
109
110     // Header line 2
111     bool first = true;
112     for (QPtrListIterator<TableColumnInfo> it(columns); it; ++it )
113     {
114         if (columnFormat[(*it)->getName()])
115             if (columnFormat[(*it)->getName()]->genHeadLine2)
116             {
117                 if (first)
118                 {
119                     s() << "  <tr";
120                     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
121                         s() << " class=\"tj_header_row\"";
122                     s() << ">" << endl;
123                     first = false;
124                 }
125
126                 TableCellInfo tci(columnFormat[(*it)->getName()], 0, *it);
127                 (*this.*(columnFormat[(*it)->getName()]->genHeadLine2))
128                     (&tci);
129             }
130     }
131     if (!first)
132         s() << "  </tr>" << endl;
133
134     s() << " </thead>\n" << endl;
135 }
136
137 void
138 HTMLReportElement::generateLine(TableLineInfo* tli, int funcSel)
139 {
140     setMacros(tli);
141
142     puts("  <tr valign=\"middle\"");
143     if (tli->bgCol.isValid() || tli->boldText || tli->fontFactor != 100)
144     {
145        puts(" style=\"");
146        if (tli->bgCol.isValid())
147        {
148            puts("background-color:");
149            puts(tli->bgCol.name());
150            puts("; ");
151        }
152        if (tli->boldText)
153            puts("font-weight:bold; ");
154        if (tli->fontFactor != 100)
155        {
156            puts("font-size:");
157            puts(QString("%1").arg(tli->fontFactor));
158            puts("%; ");
159        }
160        puts("\"");
161     }
162     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
163         puts(" class=\"tj_row\"");
164     puts(">\n");
165
166     for (QPtrListIterator<TableColumnInfo> it(columns); it; ++it )
167     {
168         TableCellInfo tci(columnFormat[(*it)->getName()], tli, *it);
169         if (columnFormat[(*it)->getName()])
170         {
171             GenCellPtr gcf = 0;
172             switch (funcSel)
173             {
174                 case 0:
175                     gcf = columnFormat[(*it)->getName()]->genHeadLine1;
176                     break;
177                 case 1:
178                     gcf = columnFormat[(*it)->getName()]->genHeadLine2;
179                     break;
180                 case 2:
181                     gcf = columnFormat[(*it)->getName()]->genTaskLine1;
182                     break;
183                 case 3:
184                     gcf = columnFormat[(*it)->getName()]->genTaskLine2;
185                     break;
186                 case 4:
187                     gcf = columnFormat[(*it)->getName()]->genResourceLine1;
188                     break;
189                 case 5:
190                     gcf = columnFormat[(*it)->getName()]->genResourceLine2;
191                     break;
192                 case 6:
193                     gcf = columnFormat[(*it)->getName()]->genAccountLine1;
194                     break;
195                 case 7:
196                     gcf = columnFormat[(*it)->getName()]->genAccountLine2;
197                     break;
198                 case 8:
199                     gcf = columnFormat[(*it)->getName()]->genSummaryLine1;
200                     break;
201                 case 9:
202                     gcf = columnFormat[(*it)->getName()]->genSummaryLine2;
203                     break;
204                 default:
205                     qFatal("Unknown function selector: %d", funcSel);
206             }
207             if (gcf)
208             {
209                 (*this.*(gcf))(&tci);
210             }
211         }
212     }
213     puts("  </tr>\n");
214 }
215
216 void
217 HTMLReportElement::genCell(const QString& text, TableCellInfo* tci,
218                            bool multi, bool filter)
219 {
220     if (!multi)
221         tci->setFontFactor(90);
222
223     puts("   <td");
224     if (tci->tcf->noWrap)
225         puts(" nowrap=\"nowrap\"");
226     if (tci->getRows() != 1 || (multi && scenarios.count() > 1))
227         puts(" rowspan=\"" + QString("%1")
228              .arg(tci->getRows() != 1 ?
229                   tci->getRows() : scenarios.count()) + "\"");
230     if (tci->getColumns() != 1)
231     {
232         puts(" colspan=\"");
233         puts(QString("%1").arg(tci->getColumns()));
234         puts("\"");
235     }
236     if (!tci->getStatusText().isEmpty())
237     {
238         puts(" onmouseover=\"status='");
239         puts(tci->getStatusText());
240         puts("';return true;\"");
241     }
242     if (tci->tcf->hAlign != TableColumnFormat::center ||
243         !tci->getHAlign().isEmpty() ||
244         tci->getLeftPadding() > 0 ||
245         tci->getRightPadding() > 0 ||
246         tci->getBgColor().isValid() ||
247         tci->getFontFactor() != 100 ||
248         tci->getBoldText() ||
249         tci->tcf->fontFactor != 100)
250     {
251         puts(" style=\"");
252         if (tci->getBgColor().isValid())
253         {
254             puts("background-color:");
255             int r, g, b;
256             tci->getBgColor().rgb(&r, &g, &b);
257             char buf[10];
258             sprintf(buf, "#%02x%02x%02x; ", r, g, b);
259             puts(buf);
260         }
261         if (!tci->getHAlign().isEmpty())
262         {
263             puts("text-align:");
264             puts(tci->getHAlign());
265             puts("; ");
266         }
267         else if (tci->tcf->hAlign != TableColumnFormat::center)
268         {
269             puts("text-align:");
270             switch(tci->tcf->hAlign)
271             {
272                 case TableColumnFormat::center:
273                     puts("center");
274                     break;
275                 case TableColumnFormat::left:
276                     puts("left");
277                     break;
278                 case TableColumnFormat::right:
279                     puts("right");
280                     break;
281             }
282             puts("; ");
283         }
284         if (tci->getLeftPadding() > 0)
285         {
286             puts("padding-left:");
287             puts(QString("%1").arg(tci->getLeftPadding()));
288             puts("px; ");
289         }
290         if (tci->getRightPadding() > 0)
291         {
292             puts("padding-right:");
293             puts(QString("%1").arg(tci->getRightPadding()));
294             puts("px; ");
295         }
296         if (tci->getBoldText())
297             puts("font-weight:bold; ");
298         if (tci->getFontFactor() != 100 || tci->tcf->fontFactor != 100)
299         {
300             puts("font-size:");
301             puts(QString("%1").arg(tci->getFontFactor() *
302                                    tci->tcf->fontFactor / 100));
303             puts("%; ");
304         }
305         puts("\"");
306     }
307     QString cellText;
308     if (tci->tli->ca1 == 0 ||
309         !isHidden(tci->tli->ca1, tci->tci->getHideCellText()))
310     {
311         cellText = filter ? htmlFilter(text) : text;
312         if (tci->tli->ca1 && !tci->tci->getCellText().isEmpty())
313         {
314             QStringList sl(text);
315             cellText = mt.expandReportVariable(tci->tci->getCellText(), &sl);
316         }
317     }
318     if (!tci->tci->getCellURL().isEmpty() && (tci->tli->ca1 == 0 ||
319         !isHidden(tci->tli->ca1, tci->tci->getHideCellURL())))
320     {
321         QStringList sl(text);
322         QString cellURL = mt.expandReportVariable(tci->tci->getCellURL(), &sl);
323         if (!cellURL.isEmpty())
324         {
325             cellText = QString("<a href=\"") + cellURL
326                         + "\">" + cellText + "</a>";
327         }
328     }
329     if (cellText.isEmpty())
330         cellText = "&#160;";
331     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
332         puts(" class=\"tj_cell\"");
333     puts(">");
334     if (!tci->getToolTipText().isEmpty())
335     {
336         puts("<div id=\"");
337         puts(tci->getToolTipID());
338         puts("\" class=\"tj_tooltip\" style=\"visibility:hidden\">");
339         puts(tci->getToolTipText());
340         puts("</div>");
341     }
342
343     puts(cellText);
344     puts("</td>\n");
345 }
346
347 void
348 HTMLReportElement::reportTaskLoad(double load, TableCellInfo* tci,
349                                   const Interval& period)
350 {
351     QString text;
352     if (tci->tli->task->isActive(tci->tli->sc, period))
353     {
354         if (tci->tli->task->isContainer())
355         {
356             QString pre, post;
357             if (period.contains(tci->tli->task->getStart(tci->tli->sc)))
358                 pre = "v=";
359             if (period.contains(tci->tli->task->getEnd(tci->tli->sc)))
360                 post += "=v";
361             if (load > 0.0 && barLabels != BLT_EMPTY)
362                 text = scaledLoad(load, tci->tcf->realFormat);
363             else if (pre.isEmpty() && post.isEmpty())
364                 text = "==";
365             else if (!pre.isEmpty() && !post.isEmpty())
366             {
367                 pre = post = "v";
368                 text = "=";
369             }
370             text = pre + text + post;
371             tci->setBoldText(true);
372         }
373         else
374         {
375             if (tci->tli->task->isMilestone())
376             {
377                 text += "<>";
378                 tci->setBoldText(true);
379             }
380             else
381             {
382                 QString pre, post;
383                 if (period.contains(tci->tli->task->
384                                     getStart(tci->tli->sc)))
385                     pre = "[=";
386                 if (period.contains(tci->tli->task->
387                                     getEnd(tci->tli->sc)))
388                     post = "=]";
389                 if (!pre.isEmpty() && !post.isEmpty())
390                 {
391                     pre = "[";
392                     post = "]";
393                 }
394                 if (load > 0.0 && barLabels != BLT_EMPTY)
395                     text = scaledLoad(load, tci->tcf->realFormat);
396                 else if (pre.isEmpty() && post.isEmpty())
397                     text = "==";
398                 else if (pre == "[")
399                    text = "=";
400                 text = pre + text + post;
401             }
402         }
403         tci->setHAlign("center");
404         tci->setStatusText(time2user(period.getStart(), "%Y-%m-%d / [") +
405                            tci->tli->task->getId() + "] " +
406                            htmlFilter(tci->tli->task->getName()));
407     }
408     else
409     {
410         tci->setStatusText("");
411     }
412     genCell(text, tci, false);
413 }
414
415 void
416 HTMLReportElement::reportResourceLoad(double load, TableCellInfo* tci,
417                                       const Interval& period)
418 {
419     QString text;
420     if (load > 0.0)
421     {
422         if (barLabels != BLT_EMPTY)
423             text += scaledLoad(load, tci->tcf->realFormat);
424         if (tci->tli->resource->hasSubs())
425             tci->setBoldText(true);
426         tci->setHAlign("center");
427         tci->setStatusText(time2user(period.getStart(), "%Y-%m-%d / [") +
428                            tci->tli->resource->getId() + "] " +
429                            htmlFilter(tci->tli->resource->getName()));
430     }
431     else
432     {
433         tci->setStatusText("");
434     }
435     genCell(text, tci, false);
436 }
437
438 void
439 HTMLReportElement::reportCurrency(double value, TableCellInfo* tci,
440                                   time_t iv_start)
441 {
442     tci->setStatusText(time2user(iv_start, "%Y-%m-%d / [") +
443                        tci->tli->account->getId() + "] " +
444                        htmlFilter(tci->tli->account->getName()));
445     genCell(tci->tcf->realFormat.format(value, tci), tci, false);
446 }
447
448 void
449 HTMLReportElement::generateTitle(TableCellInfo* tci, const QString& str)
450 {
451     QStringList sl(str);
452     QString cellText;
453     if (!tci->tci->getTitle().isEmpty())
454         cellText = mt.expandReportVariable(tci->tci->getTitle(), &sl);
455     else
456         cellText = str;
457     cellText = htmlFilter(cellText);
458     QString cellURL = mt.expandReportVariable(tci->tci->getTitleURL(), &sl);
459     if (!cellURL.isEmpty())
460         cellText = QString("<a href=\"") + cellURL
461             + "\">" + cellText + "</a>";
462
463     puts(cellText);
464 }
465
466 void
467 HTMLReportElement::generateSubTitle(TableCellInfo* tci, const QString& str)
468 {
469     QStringList sl(str);
470     QString cellText;
471     if (!tci->tci->getSubTitle().isEmpty())
472         cellText = mt.expandReportVariable(tci->tci->getSubTitle(), &sl);
473     else
474         cellText = str;
475     cellText = htmlFilter(cellText);
476     QString cellURL = mt.expandReportVariable(tci->tci->getSubTitleURL(), &sl);
477     if (!cellURL.isEmpty())
478         cellText = QString("<a href=\"") + cellURL
479             + "\">" + cellText + "</a>";
480
481     puts(cellText);
482
483     tci->tci->increaseSubColumns();
484 }
485
486 void
487 HTMLReportElement::generateRightIndented(TableCellInfo* tci, const QString& str)
488 {
489     int topIndent = 0, subIndent = 0, maxDepth = 0;
490     if (tci->tli->ca1->getType() == CA_Task)
491     {
492         if (taskSortCriteria[0] == CoreAttributesList::TreeMode)
493             subIndent = tci->tli->ca2 == 0 ? 8 : 5;
494         if (resourceSortCriteria[0] == CoreAttributesList::TreeMode)
495             topIndent = (tci->tli->ca2 != 0 ? 0 : 5) * maxDepthResourceList;
496         maxDepth = maxDepthTaskList;
497     }
498     else if (tci->tli->ca1->getType() == CA_Resource)
499     {
500         if (resourceSortCriteria[0] == CoreAttributesList::TreeMode)
501             subIndent = tci->tli->ca2 == 0 ? 8 : 5;
502         if (taskSortCriteria[0] == CoreAttributesList::TreeMode)
503             topIndent = (tci->tli->ca2 != 0 ? 0 : 5) * maxDepthTaskList;
504         maxDepth = maxDepthResourceList;
505     }
506
507     tci->setRightPadding(2 + topIndent +
508                          (maxDepth - 1 - tci->tli->ca1->treeLevel()) *
509                          subIndent);
510     genCell(str, tci, false);
511 }
512
513 void
514 HTMLReportElement::genHeadDefault(TableCellInfo* tci)
515 {
516     puts("   <td rowspan=\"2\"");
517     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
518         puts(" class=\"tj_header_cell\"");
519     puts(">");
520     generateTitle(tci, tci->tcf->getTitle());
521     puts("</td>\n");
522 }
523
524 void
525 HTMLReportElement::genHeadCurrency(TableCellInfo* tci)
526 {
527     puts("   <td rowspan=\"2\"");
528     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
529         puts(" class=\"tj_header_cell\"");
530     puts(">");
531     generateTitle(tci, tci->tcf->getTitle() +
532                   (!report->getProject()->getCurrency().isEmpty() ?
533                    QString(" ") + report->getProject()->getCurrency() :
534                    QString()));
535     puts("</td>\n");
536 }
537
538 void
539 HTMLReportElement::genHeadDaily1(TableCellInfo* tci)
540 {
541     // Generates the 1st header line for daily calendar views.
542     bool weekStartsMonday = report->getWeekStartsMonday();
543     for (time_t day = midnight(start); day < end;
544          day = sameTimeNextMonth(beginOfMonth(day)))
545     {
546         int left = daysLeftInMonth(day);
547         if (left > daysBetween(day, end))
548             left = daysBetween(day, end);
549         s() << "   <td colspan=\""
550             << QString().sprintf("%d", left) << "\"";
551         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
552             s() << " class=\"tj_header_cell\"";
553         s() << ">";
554         mt.setMacro(new Macro(KW("day"), "01",
555                               defFileName, defFileLine));
556         mt.setMacro(new Macro(KW("month"),
557                               QString().sprintf("%02d", monthOfYear(day)),
558                               defFileName, defFileLine));
559         mt.setMacro(new Macro(KW("quarter"),
560                               QString().sprintf
561                               ("%02d", quarterOfYear(day)),
562                               defFileName, defFileLine));
563         mt.setMacro(new Macro(KW("week"),
564                               QString().sprintf
565                               ("%02d", weekOfYear(day, weekStartsMonday)),
566                               defFileName, defFileLine));
567         mt.setMacro(new Macro(KW("year"),
568                               QString().sprintf("%04d", year(day)),
569                               defFileName, defFileLine));
570         generateTitle(tci, monthAndYear(day));
571         s() << "</td>" << endl;
572     }
573 }
574
575 void
576 HTMLReportElement::genHeadDaily2(TableCellInfo* tci)
577 {
578     // Generates the 2nd header line for daily calendar views.
579     bool weekStartsMonday = report->getWeekStartsMonday();
580     for (time_t day = midnight(start); day < end; day = sameTimeNextDay(day))
581     {
582         int dom = dayOfMonth(day);
583         s() << "   <td style=\"";
584         QColor bgCol = colors.getColor("header");
585         if (isWeekend(day))
586             bgCol = bgCol.dark(130);
587         if (isSameDay(report->getProject()->getNow(), day))
588             bgCol = colors.getColor("today");
589         s() << "background-color:" << bgCol.name() << "; "
590             << "font-size:80%; text-align:center\"";
591         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
592             s() << " class=\"tj_header_cell\"";
593         s() << ">";
594         mt.setMacro(new Macro(KW("day"), QString().sprintf("%02d", dom),
595                               defFileName, defFileLine));
596         mt.setMacro(new Macro(KW("month"),
597                               QString().sprintf("%02d", monthOfYear(day)),
598                               defFileName, defFileLine));
599         mt.setMacro(new Macro(KW("quarter"),
600                               QString().sprintf
601                               ("%02d", quarterOfYear(day)),
602                               defFileName, defFileLine));
603         mt.setMacro(new Macro(KW("week"),
604                               QString().sprintf
605                               ("%02d", weekOfYear(day, weekStartsMonday)),
606                               defFileName, defFileLine));
607         mt.setMacro(new Macro(KW("year"),
608                               QString().sprintf("%04d", year(day)),
609                               defFileName, defFileLine));
610         if (dom < 10)
611             s() << "&#160;";
612         generateSubTitle(tci, QString().sprintf("%d", dom));
613         s() << "</td>" << endl;
614     }
615 }
616
617 void
618 HTMLReportElement::genHeadWeekly1(TableCellInfo* tci)
619 {
620     // Generates the 1st header line for weekly calendar views.
621     bool weekStartsMonday = report->getWeekStartsMonday();
622     for (time_t week = beginOfWeek(start, weekStartsMonday); week < end; )
623     {
624         int currMonth = monthOfWeek(week, weekStartsMonday);
625         int left;
626         time_t wi = sameTimeNextWeek(week);
627         for (left = 1 ; wi < end &&
628              monthOfWeek(wi, weekStartsMonday) == currMonth;
629              wi = sameTimeNextWeek(wi))
630             left++;
631
632         s() << "   <td colspan=\""
633           << QString().sprintf("%d", left) << "\"";
634         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
635             s() << " class=\"tj_header_cell\"";
636         s() << ">";
637         mt.setMacro(new Macro(KW("day"), QString().sprintf
638                               ("%02d", dayOfMonth(week)),
639                               defFileName, defFileLine));
640         mt.setMacro(new Macro(KW("month"),
641                               QString().sprintf
642                               ("%02d", monthOfWeek(week, weekStartsMonday)),
643                               defFileName, defFileLine));
644         mt.setMacro(new Macro(KW("quarter"),
645                               QString().sprintf
646                               ("%02d", quarterOfYear(week)),
647                               defFileName, defFileLine));
648         mt.setMacro(new Macro(KW("week"),
649                               QString().sprintf
650                               ("%02d", weekOfYear(week, weekStartsMonday)),
651                               defFileName, defFileLine));
652         mt.setMacro(new Macro(KW("year"),
653                               QString().sprintf
654                               ("%04d", yearOfWeek(week, weekStartsMonday)),
655                               defFileName, defFileLine));
656         generateTitle(tci,
657                       QString("%1 %2").arg(shortMonthName(monthOfWeek(week,
658                                                           weekStartsMonday)
659                                                           - 1)).
660                       arg(yearOfWeek(week, weekStartsMonday)));
661         s() << "</td>" << endl;
662         week = wi;
663     }
664 }
665
666 void
667 HTMLReportElement::genHeadWeekly2(TableCellInfo* tci)
668 {
669     // Generates the 2nd header line for weekly calendar views.
670     bool wsm = report->getWeekStartsMonday();
671     for (time_t week = beginOfWeek(start, wsm); week < end;
672          week = sameTimeNextWeek(week))
673     {
674         int woy = weekOfYear(week, wsm);
675         s() << "   <td style=\"";
676         QColor bgCol;
677         if (isSameWeek(report->getProject()->getNow(), week, wsm))
678             bgCol = colors.getColor("today");
679         else
680             bgCol = colors.getColor("header");
681         s() << "background-color:" << bgCol.name() << "; "
682             << "font-size:80%; text-align:center\"";
683         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
684             s() << " class=\"tj_header_cell\"";
685         s() << ">";
686         if (woy < 10)
687             s() << "&#160;";
688         mt.setMacro(new Macro(KW("day"), QString().sprintf
689                               ("%02d", dayOfMonth(week)),
690                               defFileName, defFileLine));
691         mt.setMacro(new Macro(KW("month"),
692                               QString().sprintf("%02d",
693                                                 monthOfWeek(week, wsm)),
694                               defFileName, defFileLine));
695         mt.setMacro(new Macro(KW("quarter"),
696                               QString().sprintf
697                               ("%02d", quarterOfYear(week)),
698                               defFileName, defFileLine));
699         mt.setMacro(new Macro(KW("week"),
700                               QString().sprintf("%02d", woy),
701                               defFileName, defFileLine));
702         mt.setMacro(new Macro(KW("year"),
703                               QString().sprintf("%04d", yearOfWeek(week, wsm)),
704                               defFileName, defFileLine));
705         generateSubTitle(tci, QString().sprintf("%d", woy));
706         s() << "</td>" << endl;
707     }
708 }
709
710 void
711 HTMLReportElement::genHeadMonthly1(TableCellInfo* tci)
712 {
713     // Generates 1st header line of monthly calendar view.
714     for (time_t year = beginOfMonth(start); year < end;
715          year = sameTimeNextYear(beginOfYear(year)))
716     {
717         int left = monthLeftInYear(year);
718         if (left > monthsBetween(year, end))
719             left = monthsBetween(year, end);
720         s() << "   <td colspan=\""
721           << QString().sprintf("%d", left) << "\"";
722         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
723             s() << " class=\"tj_header_cell\"";
724         s() << ">";
725         mt.setMacro(new Macro(KW("day"), "01",
726                               defFileName, defFileLine));
727         mt.setMacro(new Macro(KW("month"), "01",
728                               defFileName, defFileLine));
729         mt.setMacro(new Macro(KW("quarter"), "1",
730                               defFileName, defFileLine));
731         mt.setMacro(new Macro(KW("week"), "01",
732                               defFileName, defFileLine));
733         mt.setMacro(new Macro(KW("year"),
734                               QString().sprintf("%04d", ::year(year)),
735                               defFileName, defFileLine));
736         generateTitle(tci, QString().sprintf("%d", ::year(year)));
737         s() << "</td>" << endl;
738     }
739 }
740
741 void
742 HTMLReportElement::genHeadMonthly2(TableCellInfo* tci)
743 {
744     // Generates 2nd header line of monthly calendar view.
745     bool weekStartsMonday = report->getWeekStartsMonday();
746     for (time_t month = beginOfMonth(start); month < end;
747          month = sameTimeNextMonth(month))
748     {
749         int moy = monthOfYear(month);
750         s() << "   <td style=\"";
751         QColor bgCol;
752         if (isSameMonth(report->getProject()->getNow(), month))
753             bgCol = colors.getColor("today");
754         else
755             bgCol = colors.getColor("header");
756         s() << "background-color:" << bgCol.name() << "; "
757             << "font-size:80%; text-align:center\"";
758         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
759             s() << " class=\"tj_header_cell\"";
760         s() << ">";
761         if (month < 10)
762             s() << "&#160;";
763         mt.setMacro(new Macro(KW("day"), "01",
764                               defFileName, defFileLine));
765         mt.setMacro(new Macro(KW("month"),
766                               QString().sprintf("%02d", moy),
767                               defFileName, defFileLine));
768         mt.setMacro(new Macro(KW("quarter"),
769                               QString().sprintf
770                               ("%02d", quarterOfYear(month)),
771                               defFileName, defFileLine));
772         mt.setMacro(new Macro(KW("week"),
773                               QString().sprintf
774                               ("%02d", weekOfYear(month, weekStartsMonday)),
775                               defFileName, defFileLine));
776         mt.setMacro(new Macro(KW("year"),
777                               QString().sprintf("%04d", year(month)),
778                               defFileName, defFileLine));
779         generateSubTitle(tci, shortMonthName(moy - 1));
780         s() << "</td>" << endl;
781     }
782 }
783
784 void
785 HTMLReportElement::genHeadQuarterly1(TableCellInfo* tci)
786 {
787     // Generates 1st header line of quarterly calendar view.
788     for (time_t year = beginOfQuarter(start); year < end;
789          year = sameTimeNextYear(beginOfYear(year)))
790     {
791         int left = quartersLeftInYear(year);
792         if (left > quartersBetween(year, end))
793             left = quartersBetween(year, end);
794         s() << "   <td colspan=\""
795           << QString().sprintf("%d", left) << "\"";
796         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
797             s() << " class=\"tj_header_cell\"";
798         s() << ">";
799         mt.setMacro(new Macro(KW("day"), "01",
800                               defFileName, defFileLine));
801         mt.setMacro(new Macro(KW("month"), "01",
802                               defFileName, defFileLine));
803         mt.setMacro(new Macro(KW("quarter"), "1",
804                               defFileName, defFileLine));
805         mt.setMacro(new Macro(KW("week"), "01",
806                               defFileName, defFileLine));
807         mt.setMacro(new Macro(KW("year"),
808                               QString().sprintf("%04d", ::year(year)),
809                               defFileName, defFileLine));
810         generateTitle(tci, QString().sprintf("%d", ::year(year)));
811         s() << "</td>" << endl;
812     }
813 }
814
815 void
816 HTMLReportElement::genHeadQuarterly2(TableCellInfo* tci)
817 {
818     // Generates 2nd header line of quarterly calendar view.
819     static const char* qnames[] =
820     {
821         I18N_NOOP("1st Quarter"), I18N_NOOP("2nd Quarter"),
822         I18N_NOOP("3rd Quarter"), I18N_NOOP("4th Quarter")
823     };
824
825     bool weekStartsMonday = report->getWeekStartsMonday();
826     for (time_t quarter = beginOfQuarter(start); quarter < end;
827          quarter = sameTimeNextQuarter(quarter))
828     {
829         int qoy = quarterOfYear(quarter);
830         s() << "   <td style=\"";
831         QColor bgCol;
832         if (isSameQuarter(report->getProject()->getNow(), quarter))
833             bgCol = colors.getColor("today");
834         else
835             bgCol = colors.getColor("header");
836         s() << "background-color:" << bgCol.name() << "; "
837             << "font-size:80%; text-align:center\"";
838         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
839             s() << " class=\"tj_header_cell\"";
840         s() << ">";
841         mt.setMacro(new Macro(KW("day"), QString().sprintf("%02d",
842                                                            dayOfMonth(quarter)),
843                               defFileName, defFileLine));
844         mt.setMacro(new Macro(KW("month"),
845                               QString().sprintf("%02d", monthOfYear(quarter)),
846                               defFileName, defFileLine));
847         mt.setMacro(new Macro(KW("quarter"),
848                               QString().sprintf("%d", qoy),
849                               defFileName, defFileLine));
850         mt.setMacro(new Macro(KW("week"),
851                               QString().sprintf
852                               ("%02d", weekOfYear(quarter, weekStartsMonday)),
853                               defFileName, defFileLine));
854         mt.setMacro(new Macro(KW("year"),
855                               QString().sprintf("%04d", year(quarter)),
856                               defFileName, defFileLine));
857         generateSubTitle(tci, i18n(qnames[qoy - 1]));
858         s() << "</td>" << endl;
859     }
860 }
861
862 void
863 HTMLReportElement::genHeadYear(TableCellInfo* tci)
864 {
865     // Generates 1st header line of monthly calendar view.
866     for (time_t year = beginOfYear(start); year < end;
867          year = sameTimeNextYear(year))
868     {
869         s() << "   <td rowspan=\"2\"";
870         if (static_cast<HTMLReport*>(report)->hasStyleSheet())
871             s() << " class=\"tj_header_cell\"";
872         s() << ">";
873         mt.setMacro(new Macro(KW("day"), QString().sprintf("%02d",
874                                                            dayOfMonth(year)),
875                               defFileName, defFileLine));
876         mt.setMacro(new Macro(KW("month"),
877                               QString().sprintf("%02d", monthOfYear(year)),
878                               defFileName, defFileLine));
879         mt.setMacro(new Macro(KW("quarter"), "1",
880                               defFileName, defFileLine));
881         mt.setMacro(new Macro(KW("week"), "01",
882                               defFileName, defFileLine));
883         mt.setMacro(new Macro(KW("year"),
884                               QString().sprintf("%04d", ::year(year)),
885                               defFileName, defFileLine));
886         generateTitle(tci, QString().sprintf("%d", ::year(year)));
887         s() << "</td>" << endl;
888     }
889 }
890
891 void
892 HTMLReportElement::genCellEmpty(TableCellInfo* tci)
893 {
894     genCell("", tci, true);
895 }
896
897 void
898 HTMLReportElement::genCellAccounts(TableCellInfo* tci)
899 {
900     genCell(QString().sprintf("%s", tci->tli->task->getAccount() ?
901                               tci->tli->task->getAccount()->getId().latin1() :
902                               ""), tci, true);
903 }
904
905 void
906 HTMLReportElement::genCellSequenceNo(TableCellInfo* tci)
907 {
908     genCell(tci->tli->ca2 == 0 ?
909             QString().sprintf("%d.", tci->tli->ca1->getSequenceNo()) :
910             QString::null, tci, true);
911 }
912
913 void
914 HTMLReportElement::genCellNo(TableCellInfo* tci)
915 {
916     genCell(tci->tli->ca2 == 0 ? QString().sprintf("%d.", tci->tli->idxNo) :
917             QString::null, tci, true);
918 }
919
920 void
921 HTMLReportElement::genCellHierarchNo(TableCellInfo* tci)
922 {
923     genCell(tci->tli->ca2 == 0 ?
924             tci->tli->ca1->getHierarchNo() : QString::null, tci, true);
925 }
926
927 void
928 HTMLReportElement::genCellIndex(TableCellInfo* tci)
929 {
930     genCell(tci->tli->ca2 == 0 ?
931             QString().sprintf("%d.", tci->tli->ca1->getIndex()) :
932             QString::null, tci, true);
933 }
934
935 void
936 HTMLReportElement::genCellHierarchIndex(TableCellInfo* tci)
937 {
938     genCell(tci->tli->ca2 == 0 ?
939             tci->tli->ca1->getHierarchIndex() : QString::null, tci, true);
940 }
941
942 void
943 HTMLReportElement::genCellHierarchLevel(TableCellInfo* tci)
944 {
945     genCell(tci->tli->ca2 == 0 ?
946             tci->tli->ca1->getHierarchLevel() : QString::null, tci, true);
947 }
948
949 void
950 HTMLReportElement::genCellId(TableCellInfo* tci)
951 {
952     genCell(tci->tli->ca1->getId(), tci, true);
953 }
954
955 void
956 HTMLReportElement::genCellName(TableCellInfo* tci)
957 {
958     int lPadding = 0;
959     int fontSize = tci->tli->ca2 == 0 ? 100 : 90;
960     if ((tci->tli->ca2 && tci->tli->ca2->getType() == CA_Resource &&
961           resourceSortCriteria[0] == CoreAttributesList::TreeMode) ||
962         (tci->tli->ca2 && tci->tli->ca2->getType() == CA_Task &&
963          taskSortCriteria[0] == CoreAttributesList::TreeMode) ||
964         (tci->tli->ca2 && tci->tli->ca2->getType() == CA_Account &&
965           accountSortCriteria[0] == CoreAttributesList::TreeMode))
966         for (const CoreAttributes* cp = tci->tli->ca2 ; cp != 0;
967              cp = cp->getParent())
968             lPadding++;
969
970     QString text;
971     if (tci->tli->specialName.isNull())
972     {
973         if (tci->tli->task)
974             mt.setMacro(new Macro(KW("taskid"), tci->tli->task->getId(),
975                                   defFileName, defFileLine));
976         if (tci->tli->resource)
977             mt.setMacro(new Macro(KW("resourceid"), tci->tli->resource->getId(),
978                                   defFileName, defFileLine));
979         if (tci->tli->account)
980             mt.setMacro(new Macro(KW("accountid"), tci->tli->account->getId(),
981                                   defFileName, defFileLine));
982
983         if ((tci->tli->ca1->getType() == CA_Resource &&
984              resourceSortCriteria[0] == CoreAttributesList::TreeMode) ||
985             (tci->tli->ca1->getType() == CA_Task &&
986              taskSortCriteria[0] == CoreAttributesList::TreeMode) ||
987             (tci->tli->ca1->getType() == CA_Account &&
988              accountSortCriteria[0] == CoreAttributesList::TreeMode))
989         {
990             lPadding += tci->tli->ca1->treeLevel();
991             tci->setFontFactor(fontSize + 5 * (maxDepthTaskList - 1 -
992                                                tci->tli->ca1->treeLevel()));
993         }
994         tci->setLeftPadding(2 + lPadding * 15);
995         text = tci->tli->ca1->getName();
996     }
997     else
998         text = tci->tli->specialName;
999     genCell(text, tci, true);
1000 }
1001
1002 void
1003 HTMLReportElement::genCellStart(TableCellInfo* tci)
1004 {
1005     if (!tci->tli->task->isStartOk(tci->tli->sc))
1006         tci->setBgColor(colors.getColor("error"));
1007     genCell(time2user(tci->tli->task->getStart(tci->tli->sc), timeFormat),
1008             tci, false);
1009 }
1010
1011 void
1012 HTMLReportElement::genCellEnd(TableCellInfo* tci)
1013 {
1014     if (!tci->tli->task->isEndOk(tci->tli->sc))
1015         tci->setBgColor(colors.getColor("error"));
1016     genCell(time2user(tci->tli->task->getEnd(tci->tli->sc) + 1, timeFormat),
1017             tci, false);
1018 }
1019
1020 #define GCMMSE(a, b) \
1021 void \
1022 HTMLReportElement::genCell##a(TableCellInfo* tci) \
1023 { \
1024     genCell(tci->tli->task->get##a(tci->tli->sc) == 0 ? QString() : \
1025             time2user(tci->tli->task->get##a(tci->tli->sc) + b, timeFormat), \
1026             tci, false); \
1027 }
1028
1029 GCMMSE(MinStart, 0)
1030 GCMMSE(MaxStart, 0)
1031 GCMMSE(MinEnd, 1)
1032 GCMMSE(MaxEnd, 1)
1033
1034 #define GCSEBUFFER(a) \
1035 void \
1036 HTMLReportElement::genCell##a##Buffer(TableCellInfo* tci) \
1037 { \
1038     genCell(QString().sprintf \
1039             ("%3.0f", tci->tli->task->get##a##Buffer(tci->tli->sc)), \
1040             tci, false); \
1041 }
1042
1043 GCSEBUFFER(Start)
1044 GCSEBUFFER(End)
1045
1046 void
1047 HTMLReportElement::genCellStartBufferEnd(TableCellInfo* tci)
1048 {
1049     genCell(time2user(tci->tli->task->getStartBufferEnd
1050                          (tci->tli->sc), timeFormat), tci, false);
1051 }
1052
1053 void
1054 HTMLReportElement::genCellEndBufferStart(TableCellInfo* tci)
1055 {
1056     genCell(time2user(tci->tli->task->getStartBufferEnd
1057                       (tci->tli->sc) + 1, timeFormat), tci, false);
1058 }
1059
1060 void
1061 HTMLReportElement::genCellDuration(TableCellInfo* tci)
1062 {
1063     genCell(scaledDuration(tci->tli->task->getCalcDuration(tci->tli->sc),
1064                            tci->tcf->realFormat),
1065             tci, false);
1066 }
1067
1068 void
1069 HTMLReportElement::genCellEffort(TableCellInfo* tci)
1070 {
1071     double val = 0.0;
1072     if (tci->tli->ca1->getType() == CA_Task)
1073     {
1074         val = tci->tli->task->getLoad(tci->tli->sc, Interval(start, end),
1075                                       tci->tli->resource);
1076     }
1077     else if (tci->tli->ca1->getType() == CA_Resource)
1078     {
1079         val = tci->tli->resource->getEffectiveLoad
1080             (tci->tli->sc, Interval(start, end), AllAccounts, tci->tli->task);
1081     }
1082
1083     generateRightIndented(tci, scaledLoad(val, tci->tcf->realFormat));
1084 }
1085
1086 void
1087 HTMLReportElement::genCellFreeLoad(TableCellInfo* tci)
1088 {
1089     double val = 0.0;
1090     if (tci->tli->ca1->getType() == CA_Resource)
1091     {
1092         val = tci->tli->resource->getEffectiveFreeLoad
1093             (tci->tli->sc, Interval(start, end));
1094     }
1095
1096     generateRightIndented(tci, scaledLoad(val, tci->tcf->realFormat));
1097 }
1098
1099 void
1100 HTMLReportElement::genCellUtilization(TableCellInfo* tci)
1101 {
1102     double val = 0.0;
1103     if (tci->tli->ca1->getType() == CA_Resource)
1104     {
1105         double load = tci->tli->resource->getEffectiveLoad
1106             (tci->tli->sc, Interval(start, end));
1107         if (load > 0.0)
1108         {
1109             double availableLoad =
1110                 tci->tli->resource->getEffectiveFreeLoad
1111                 (tci->tli->sc, Interval(start, end));
1112
1113             val = 100.0 / (1.0 + availableLoad / load);
1114         }
1115     }
1116
1117     generateRightIndented(tci, QString().sprintf("%.1f%%", val));
1118 }
1119
1120 void
1121 HTMLReportElement::genCellCriticalness(TableCellInfo* tci)
1122 {
1123     generateRightIndented
1124         (tci, scaledLoad(tci->tli->task->getCriticalness(tci->tli->sc),
1125                          tci->tcf->realFormat));
1126 }
1127
1128 void
1129 HTMLReportElement::genCellPathCriticalness(TableCellInfo* tci)
1130 {
1131     generateRightIndented
1132         (tci, scaledLoad(tci->tli->task->getPathCriticalness(tci->tli->sc),
1133                          tci->tcf->realFormat));
1134 }
1135
1136 void
1137 HTMLReportElement::genCellProjectId(TableCellInfo* tci)
1138 {
1139     genCell(tci->tli->task->getProjectId() + " (" +
1140             report->getProject()->getIdIndex(tci->tli->task->
1141                                              getProjectId()) + ")", tci,
1142             true);
1143 }
1144
1145 void
1146 HTMLReportElement::genCellProjectIDs(TableCellInfo* tci)
1147 {
1148     genCell(tci->tli->resource->getProjectIDs(tci->tli->sc,
1149                                               Interval(start, end)), tci, true);
1150 }
1151
1152 void
1153 HTMLReportElement::genCellResources(TableCellInfo* tci)
1154 {
1155     QString text;
1156     for (ResourceListIterator rli(tci->tli->task->
1157                                   getBookedResourcesIterator(tci->tli->sc));
1158         *rli != 0; ++rli)
1159     {
1160         if (!text.isEmpty())
1161             text += ", ";
1162
1163         text += (*rli)->getName();
1164     }
1165     genCell(text, tci, false);
1166 }
1167
1168 void
1169 HTMLReportElement::genCellResponsible(TableCellInfo* tci)
1170 {
1171     if (tci->tli->task->getResponsible())
1172         genCell(tci->tli->task->getResponsible()->getName(),
1173                 tci, true);
1174     else
1175         genCell("", tci, true);
1176 }
1177
1178 void
1179 HTMLReportElement::genCellText(TableCellInfo* tci)
1180 {
1181     if (tci->tcf->getId() == "note")
1182     {
1183         if (tci->tli->task->getNote().isEmpty())
1184             genCell("", tci, true);
1185         else
1186             genCell(tci->tli->task->getNote(), tci, true);
1187         return;
1188     }
1189
1190     const TextAttribute* ta = static_cast<const TextAttribute*>
1191         (tci->tli->ca1->getCustomAttribute(tci->tcf->getId()));
1192     if (!ta || ta->getText().isEmpty())
1193         genCell("", tci, true);
1194     else
1195         genCell(ta->getText(), tci, true);
1196 }
1197
1198 void
1199 HTMLReportElement::genCellStatusNote(TableCellInfo* tci)
1200 {
1201     if (tci->tli->task->getStatusNote(tci->tli->sc).isEmpty())
1202         genCell("", tci, true);
1203     else
1204         genCell(tci->tli->task->getStatusNote(tci->tli->sc),
1205                 tci, true);
1206 }
1207
1208 void
1209 HTMLReportElement::genCellCost(TableCellInfo* tci)
1210 {
1211     double val = 0.0;
1212     if (tci->tli->ca1->getType() == CA_Task)
1213     {
1214         val = tci->tli->task->getCredits(tci->tli->sc, Interval(start, end),
1215                                        Cost, tci->tli->resource);
1216     }
1217     else if (tci->tli->ca1->getType() == CA_Resource)
1218     {
1219         val = tci->tli->resource->getCredits(tci->tli->sc, Interval(start, end),
1220                                         Cost, tci->tli->task);
1221     }
1222     generateRightIndented(tci, tci->tcf->realFormat.format(val, tci));
1223 }
1224
1225 void
1226 HTMLReportElement::genCellRevenue(TableCellInfo* tci)
1227 {
1228     double val = 0.0;
1229     if (tci->tli->ca1->getType() == CA_Task)
1230     {
1231         val = tci->tli->task->getCredits(tci->tli->sc, Interval(start, end),
1232                                        Revenue, tci->tli->resource);
1233     }
1234     else if (tci->tli->ca1->getType() == CA_Resource)
1235     {
1236         val = tci->tli->resource->getCredits(tci->tli->sc, Interval(start, end),
1237                                         Revenue, tci->tli->task);
1238     }
1239     generateRightIndented(tci, tci->tcf->realFormat.format(val, tci));
1240 }
1241
1242 void
1243 HTMLReportElement::genCellProfit(TableCellInfo* tci)
1244 {
1245     double val = 0.0;
1246     if (tci->tli->ca1->getType() == CA_Task)
1247     {
1248         val = tci->tli->task->getCredits(tci->tli->sc, Interval(start, end),
1249                                     Revenue, tci->tli->resource) -
1250             tci->tli->task->getCredits(tci->tli->sc, Interval(start, end),
1251                                   Cost, tci->tli->resource);
1252     }
1253     else if (tci->tli->ca1->getType() == CA_Resource)
1254     {
1255         val = tci->tli->resource->getCredits(tci->tli->sc, Interval(start, end),
1256                                         Revenue, tci->tli->task) -
1257             tci->tli->resource->getCredits(tci->tli->sc, Interval(start, end),
1258                                       Cost, tci->tli->task);
1259     }
1260     generateRightIndented(tci, tci->tcf->realFormat.format(val, tci));
1261 }
1262
1263 void
1264 HTMLReportElement::genCellPriority(TableCellInfo* tci)
1265 {
1266     genCell(QString().sprintf("%d", tci->tli->task->getPriority()),
1267             tci, true);
1268 }
1269
1270 void
1271 HTMLReportElement::genCellFlags(TableCellInfo* tci)
1272 {
1273     FlagList allFlags = tci->tli->ca1->getFlagList();
1274     QString flagStr;
1275     for (QStringList::Iterator it = allFlags.begin();
1276          it != allFlags.end(); ++it)
1277     {
1278         if (it != allFlags.begin())
1279             flagStr += ", ";
1280         flagStr += *it;
1281     }
1282     genCell(flagStr, tci, true);
1283 }
1284
1285 void
1286 HTMLReportElement::genCellCompleted(TableCellInfo* tci)
1287 {
1288     double calcedCompletionDegree =
1289         tci->tli->task->getCalcedCompletionDegree(tci->tli->sc);
1290     double providedCompletionDegree =
1291         tci->tli->task->getCompletionDegree(tci->tli->sc);
1292
1293     if (calcedCompletionDegree < 0)
1294     {
1295         if (calcedCompletionDegree == providedCompletionDegree)
1296         {
1297             genCell(QString(i18n("in progress")), tci, false);
1298         }
1299         else
1300         {
1301             genCell(QString(i18n("%1% (in progress)"))
1302                     .arg((int) providedCompletionDegree),
1303                     tci, false);
1304         }
1305     }
1306     else
1307     {
1308         if (calcedCompletionDegree == providedCompletionDegree)
1309         {
1310             genCell(QString("%1%").arg((int) providedCompletionDegree),
1311                     tci, false);
1312         }
1313         else
1314         {
1315             genCell(QString("%1% (%2%)")
1316                     .arg((int) providedCompletionDegree)
1317                     .arg((int) calcedCompletionDegree),
1318                     tci, false);
1319         }
1320     }
1321 }
1322
1323 void
1324 HTMLReportElement::genCellCompletedEffort(TableCellInfo* tci)
1325 {
1326     double val = 0.0;
1327
1328     if (!tci->tli->resource && tci->tli->task->isLeaf())
1329     {
1330         // Task line, no resource.
1331         val = tci->tli->task->getCompletedLoad(tci->tli->sc);
1332     }
1333     else if (tci->tli->ca2 && tci->tli->ca2->getType() == CA_Resource &&
1334              tci->tli->task && tci->tli->task->isLeaf())
1335     {
1336         // Task line, nested into a resource
1337         const Project* project = report->getProject();
1338         time_t now = project->getNow();
1339         if (now < project->getStart())
1340             now = project->getStart();
1341         if (now > project->getEnd())
1342             now = project->getEnd();
1343         Interval iv = Interval(project->getStart(), now);
1344         val = tci->tli->task->getLoad(tci->tli->sc, iv, tci->tli->resource);
1345     }
1346     else
1347     {
1348         genCell("", tci, false);
1349         return;
1350     }
1351     generateRightIndented(tci, scaledLoad(val, tci->tcf->realFormat));
1352 }
1353
1354 void
1355 HTMLReportElement::genCellRemainingEffort(TableCellInfo* tci)
1356 {
1357     double val = 0.0;
1358
1359     if (!tci->tli->resource && tci->tli->task->isLeaf())
1360     {
1361         // Task line, no resource.
1362         val = tci->tli->task->getRemainingLoad(tci->tli->sc);
1363     }
1364     else if (tci->tli->ca2 && tci->tli->ca2->getType() == CA_Resource &&
1365              tci->tli->task && tci->tli->task->isLeaf())
1366     {
1367         // Task line, nested into a resource
1368         const Project* project = report->getProject();
1369         time_t now = project->getNow();
1370         if (now < project->getStart())
1371             now = project->getStart();
1372         if (now > project->getEnd())
1373             now = project->getEnd();
1374         Interval iv = Interval(now, project->getEnd());
1375         val = tci->tli->task->getLoad(tci->tli->sc, iv, tci->tli->resource);
1376     }
1377     else
1378     {
1379         genCell("", tci, false);
1380         return;
1381     }
1382
1383     generateRightIndented(tci, scaledLoad(val, tci->tcf->realFormat));
1384 }
1385
1386 void
1387 HTMLReportElement::genCellStatus(TableCellInfo* tci)
1388 {
1389     genCell(tci->tli->task->getStatusText(tci->tli->sc), tci, false);
1390 }
1391
1392 void
1393 HTMLReportElement::genCellReference(TableCellInfo* tci)
1394 {
1395     if (tci->tcf->getId() == "reference")
1396     {
1397         if (tci->tli->task->getReference().isEmpty())
1398             genCell("", tci, true);
1399         else
1400         {
1401             QString text ="<a href=\"" + tci->tli->task->getReference() + "\">";
1402             if (tci->tli->task->getReferenceLabel().isEmpty())
1403                 text += htmlFilter(tci->tli->task->getReference());
1404             else
1405                 text += htmlFilter(tci->tli->task->getReferenceLabel());
1406             text += "</a>";
1407             genCell(text, tci, true, false);
1408         }
1409         return;
1410     }
1411
1412     const ReferenceAttribute* ra = static_cast<const ReferenceAttribute*>
1413         (tci->tli->ca1->getCustomAttribute(tci->tcf->getId()));
1414     if (!ra || ra->getURL().isEmpty())
1415         genCell("", tci, true);
1416     else
1417     {
1418         QString text ="<a href=\"" + ra->getURL() + "\">";
1419         if (ra->getLabel().isEmpty())
1420             text += htmlFilter(ra->getURL());
1421         else
1422             text += htmlFilter(ra->getLabel());
1423         text += "</a>";
1424         genCell(text, tci, true, false);
1425     }
1426 }
1427
1428 void
1429 HTMLReportElement::genCellScenario(TableCellInfo* tci)
1430 {
1431     genCell(report->getProject()->getScenarioName(tci->tli->sc), tci, false);
1432 }
1433
1434 #define GCDEPFOL(a, b) \
1435 void \
1436 HTMLReportElement::genCell##a(TableCellInfo* tci) \
1437 { \
1438     QString text; \
1439     for (TaskListIterator it(tci->tli->task->get##b##Iterator()); *it != 0; \
1440          ++it) \
1441     { \
1442         if (!text.isEmpty()) \
1443             text += ", "; \
1444         text += (*it)->getId(); \
1445     } \
1446     genCell(text, tci, true); \
1447 }
1448
1449 GCDEPFOL(Depends, Previous)
1450 GCDEPFOL(Follows, Followers)
1451
1452 QColor
1453 HTMLReportElement::selectTaskBgColor(TableCellInfo* tci, double load,
1454                                      const Interval& period, bool daily)
1455 {
1456     QColor bgCol;
1457     if (tci->tli->task->isActive(tci->tli->sc, period) &&
1458         ((tci->tli->resource != 0 && load > 0.0) || tci->tli->resource == 0))
1459     {
1460         if (tci->tli->task->isCompleted(tci->tli->sc, period.getEnd()))
1461         {
1462             if (tci->tli->ca2 == 0)
1463                 bgCol = colors.getColor("completed");
1464             else
1465                 bgCol = colors.getColor("completed").light(130);
1466         }
1467         else
1468         {
1469             if (tci->tli->ca2 == 0 &&
1470                 !tci->tli->task->isBuffer(tci->tli->sc, period))
1471             {
1472                 bgCol = colors.getColor("booked");
1473             }
1474             else
1475             {
1476                 bgCol = colors.getColor("booked").light(130);
1477             }
1478         }
1479     }
1480     else if (period.contains(report->getProject()->getNow()))
1481     {
1482         bgCol = colors.getColor("today");
1483     }
1484     else if (daily && (isWeekend(period.getStart()) ||
1485                        report->getProject()->isVacation(period.getStart())))
1486     {
1487             bgCol = colors.getColor("vacation");
1488     }
1489
1490     return bgCol;
1491 }
1492
1493 QColor
1494 HTMLReportElement::selectResourceBgColor(TableCellInfo* tci, double load,
1495                                          const Interval& period, bool)
1496 {
1497     QColor bgCol;
1498     if ((load > tci->tli->resource->getMinEffort() *
1499             tci->tli->resource->getEfficiency()) ||
1500         (load == 0.0 && tci->tli->resource->isAllocated(tci->tli->sc, period)))
1501     {
1502         if (tci->tli->ca2 == 0)
1503         {
1504             bgCol = colors.getColor("booked");
1505         }
1506         else
1507         {
1508             if (tci->tli->task->isCompleted(tci->tli->sc, period.getEnd()))
1509                 bgCol = colors.getColor("completed").light(130);
1510             else
1511                 bgCol = colors.getColor("booked").light(130);
1512         }
1513     }
1514     else if (period.contains(report->getProject()->getNow()))
1515     {
1516         bgCol = colors.getColor("today");
1517     }
1518     else if (tci->tli->resource->getEffectiveLoad
1519              (tci->tli->sc, period) == 0.0 &&
1520              tci->tli->resource->getEffectiveFreeLoad(tci->tli->sc, period) ==
1521              0.0)
1522     {
1523             bgCol = colors.getColor("vacation");
1524     }
1525
1526     return bgCol;
1527 }
1528
1529 void
1530 HTMLReportElement::genCellTaskFunc(TableCellInfo* tci, bool daily,
1531                                    time_t (*beginOfT)(time_t),
1532                                    time_t (*sameTimeNextT)(time_t))
1533 {
1534     for (time_t t = (*beginOfT)(start); t < end; t = (*sameTimeNextT)(t))
1535     {
1536         Interval period = Interval(t, sameTimeNextT(t) - 1);
1537         double load = tci->tli->task->getLoad(tci->tli->sc, period,
1538                                               tci->tli->resource);
1539         QColor bgCol = selectTaskBgColor(tci, load, period, daily);
1540
1541         int runLength = 1;
1542         if (!tci->tli->task->isActive(tci->tli->sc, period))
1543         {
1544             time_t lastEndT = t;
1545             for (time_t endT = sameTimeNextT(t); endT < end;
1546                  endT = sameTimeNextT(endT))
1547             {
1548                 Interval periodProbe = Interval(endT, sameTimeNextT(endT) - 1);
1549                 double loadProbe = tci->tli->task->getLoad(tci->tli->sc,
1550                                                            periodProbe,
1551                                                            tci->tli->resource);
1552                 QColor bgColProbe = selectTaskBgColor(tci, loadProbe,
1553                                                       periodProbe, daily);
1554                 if (load != loadProbe || bgCol != bgColProbe)
1555                     break;
1556                 lastEndT = endT;
1557                 runLength++;
1558             }
1559             t = lastEndT;
1560         }
1561         tci->setColumns(runLength);
1562         tci->setBgColor(bgCol);
1563
1564         reportTaskLoad(load, tci, period);
1565     }
1566 }
1567
1568 void
1569 HTMLReportElement::genCellResourceFunc(TableCellInfo* tci, bool daily,
1570                                    time_t (*beginOfT)(time_t),
1571                                    time_t (*sameTimeNextT)(time_t))
1572 {
1573     for (time_t t = beginOfT(start); t < end; t = sameTimeNextT(t))
1574     {
1575         Interval period = Interval(t, sameTimeNextT(t) - 1);
1576         double load = tci->tli->resource->getEffectiveLoad
1577             (tci->tli->sc, period, AllAccounts, tci->tli->task);
1578         QColor bgCol = selectResourceBgColor(tci, load, period, daily);
1579
1580         int runLength = 1;
1581         if (load == 0.0)
1582         {
1583             time_t lastEndT = t;
1584             for (time_t endT = sameTimeNextT(t); endT < end;
1585                  endT = sameTimeNextT(endT))
1586             {
1587                 Interval periodProbe = Interval(endT, sameTimeNextT(endT) - 1);
1588                 double loadProbe = tci->tli->resource->getEffectiveLoad
1589                     (tci->tli->sc, periodProbe, AllAccounts, tci->tli->task);
1590                 QColor bgColProbe = selectResourceBgColor(tci, loadProbe,
1591                                                           periodProbe, daily);
1592
1593                 if (load != loadProbe || bgCol != bgColProbe)
1594                     break;
1595                 lastEndT = endT;
1596                 runLength++;
1597             }
1598             t = lastEndT;
1599         }
1600         tci->setColumns(runLength);
1601         tci->setBgColor(bgCol);
1602
1603         reportResourceLoad(load, tci, period);
1604     }
1605 }
1606
1607 void
1608 HTMLReportElement::genCellAccountFunc(TableCellInfo* tci,
1609                                       time_t (*beginOfT)(time_t),
1610                                       time_t (*sameTimeNextT)(time_t))
1611 {
1612     tci->tcf->realFormat = currencyFormat;
1613     for (time_t t = beginOfT(start); t < end; t = sameTimeNextT(t))
1614     {
1615         double volume = tci->tli->account->
1616             getVolume(tci->tli->sc, Interval(t, sameTimeNextT(t) - 1));
1617         if ((accountSortCriteria[0] == CoreAttributesList::TreeMode &&
1618              tci->tli->account->isRoot()) ||
1619             (accountSortCriteria[0] != CoreAttributesList::TreeMode))
1620             tci->tci->addToSum(tci->tli->sc, time2ISO(t), volume);
1621         reportCurrency(volume, tci, t);
1622     }
1623 }
1624
1625 void
1626 HTMLReportElement::genCellDailyTask(TableCellInfo* tci)
1627 {
1628     genCellTaskFunc(tci, true, midnight, sameTimeNextDay);
1629 }
1630
1631 void
1632 HTMLReportElement::genCellDailyResource(TableCellInfo* tci)
1633 {
1634     genCellResourceFunc(tci, true, midnight, sameTimeNextDay);
1635 }
1636
1637 void
1638 HTMLReportElement::genCellDailyAccount(TableCellInfo* tci)
1639 {
1640     genCellAccountFunc(tci, midnight, sameTimeNextDay);
1641 }
1642
1643 void
1644 HTMLReportElement::genCellWeeklyTask(TableCellInfo* tci)
1645 {
1646     bool weekStartsMonday = report->getWeekStartsMonday();
1647     for (time_t week = beginOfWeek(start, weekStartsMonday); week < end;
1648          week = sameTimeNextWeek(week))
1649     {
1650         Interval period = Interval(week, sameTimeNextWeek(week) - 1);
1651         double load = tci->tli->task->getLoad(tci->tli->sc, period,
1652                                               tci->tli->resource);
1653         QColor bgCol = selectTaskBgColor(tci, load, period, false);
1654
1655         int runLength = 1;
1656         if (!tci->tli->task->isActive(tci->tli->sc, period))
1657         {
1658             time_t lastEndWeek = week;
1659             for (time_t endWeek = sameTimeNextWeek(week); endWeek < end;
1660                  endWeek = sameTimeNextWeek(endWeek))
1661             {
1662                 Interval periodProbe = Interval(endWeek)
1663                     .firstWeek(weekStartsMonday);
1664                 double loadProbe = tci->tli->task->getLoad(tci->tli->sc,
1665                                                            periodProbe,
1666                                                            tci->tli->resource);
1667                 QColor bgColProbe = selectTaskBgColor(tci, loadProbe,
1668                                                       periodProbe, false);
1669                 if (load != loadProbe || bgCol != bgColProbe)
1670                     break;
1671                 lastEndWeek = endWeek;
1672                 runLength++;
1673             }
1674             week = lastEndWeek;
1675         }
1676         tci->setColumns(runLength);
1677         tci->setBgColor(bgCol);
1678
1679         reportTaskLoad(load, tci, period);
1680     }
1681 }
1682
1683 void
1684 HTMLReportElement::genCellWeeklyResource(TableCellInfo* tci)
1685 {
1686     bool weekStartsMonday = report->getWeekStartsMonday();
1687     for (time_t week = beginOfWeek(start, weekStartsMonday); week < end;
1688          week = sameTimeNextWeek(week))
1689     {
1690         Interval period = Interval(week, sameTimeNextWeek(week) - 1);
1691         double load = tci->tli->resource->getEffectiveLoad
1692             (tci->tli->sc, period, AllAccounts, tci->tli->task);
1693         QColor bgCol = selectResourceBgColor(tci, load, period, false);
1694
1695         int runLength = 1;
1696         if (load == 0.0)
1697         {
1698             time_t lastEndWeek = week;
1699             for (time_t endWeek = sameTimeNextWeek(week); endWeek < end;
1700                  endWeek = sameTimeNextWeek(endWeek))
1701             {
1702                 Interval periodProbe = Interval(endWeek)
1703                     .firstWeek(weekStartsMonday);
1704                 double loadProbe = tci->tli->resource->getEffectiveLoad
1705                     (tci->tli->sc, periodProbe, AllAccounts, tci->tli->task);
1706                 QColor bgColProbe = selectResourceBgColor(tci, loadProbe,
1707                                                           periodProbe, false);
1708                 if (load != loadProbe || bgCol != bgColProbe)
1709                     break;
1710                 lastEndWeek = endWeek;
1711                 runLength++;
1712             }
1713             week = lastEndWeek;
1714         }
1715         tci->setColumns(runLength);
1716         tci->setBgColor(bgCol);
1717
1718         reportResourceLoad(load, tci, period);
1719     }
1720 }
1721
1722 void
1723 HTMLReportElement::genCellWeeklyAccount(TableCellInfo* tci)
1724 {
1725     bool weekStartsMonday = report->getWeekStartsMonday();
1726     for (time_t week = beginOfWeek(start, weekStartsMonday); week < end;
1727          week = sameTimeNextWeek(week))
1728     {
1729         double volume = tci->tli->account->
1730             getVolume(tci->tli->sc, Interval(week, sameTimeNextWeek(week) - 1));
1731         if ((accountSortCriteria[0] == CoreAttributesList::TreeMode &&
1732              tci->tli->account->isRoot()) ||
1733             (accountSortCriteria[0] != CoreAttributesList::TreeMode))
1734             tci->tci->addToSum(tci->tli->sc, time2ISO(week), volume);
1735         reportCurrency(volume, tci, week);
1736     }
1737 }
1738
1739 void
1740 HTMLReportElement::genCellMonthlyTask(TableCellInfo* tci)
1741 {
1742     genCellTaskFunc(tci, false, beginOfMonth, sameTimeNextMonth);
1743 }
1744
1745 void
1746 HTMLReportElement::genCellMonthlyResource(TableCellInfo* tci)
1747 {
1748     genCellResourceFunc(tci, false, beginOfMonth, sameTimeNextMonth);
1749 }
1750
1751 void
1752 HTMLReportElement::genCellMonthlyAccount(TableCellInfo* tci)
1753 {
1754     genCellAccountFunc(tci, beginOfMonth, sameTimeNextMonth);
1755 }
1756
1757 void
1758 HTMLReportElement::genCellQuarterlyTask(TableCellInfo* tci)
1759 {
1760     genCellTaskFunc(tci, false, beginOfQuarter, sameTimeNextQuarter);
1761 }
1762
1763 void
1764 HTMLReportElement::genCellQuarterlyResource(TableCellInfo* tci)
1765 {
1766     genCellResourceFunc(tci, false, beginOfQuarter, sameTimeNextQuarter);
1767 }
1768
1769 void
1770 HTMLReportElement::genCellQuarterlyAccount(TableCellInfo* tci)
1771 {
1772     genCellAccountFunc(tci, beginOfQuarter, sameTimeNextQuarter);
1773 }
1774
1775 void
1776 HTMLReportElement::genCellYearlyTask(TableCellInfo* tci)
1777 {
1778     genCellTaskFunc(tci, false, beginOfYear, sameTimeNextYear);
1779 }
1780
1781 void
1782 HTMLReportElement::genCellYearlyResource(TableCellInfo* tci)
1783 {
1784     genCellResourceFunc(tci, false, beginOfYear, sameTimeNextYear);
1785 }
1786
1787 void
1788 HTMLReportElement::genCellYearlyAccount(TableCellInfo* tci)
1789 {
1790     genCellAccountFunc(tci, beginOfYear, sameTimeNextYear);
1791 }
1792
1793 void
1794 HTMLReportElement::genCellResponsibilities(TableCellInfo* tci)
1795 {
1796     QString text;
1797     for (TaskListIterator it(report->getProject()->getTaskListIterator());
1798          *it != 0; ++it)
1799     {
1800         if ((*it)->getResponsible() == tci->tli->resource)
1801         {
1802             if (!text.isEmpty())
1803                 text += ", ";
1804             text += (*it)->getId();
1805         }
1806     }
1807     genCell(text, tci, true);
1808 }
1809
1810 void
1811 HTMLReportElement::genCellScheduling(TableCellInfo* tci)
1812 {
1813     genCell(tci->tli->task->getSchedulingText(), tci, true);
1814 }
1815
1816 void
1817 HTMLReportElement::genCellSchedule(TableCellInfo* tci)
1818 {
1819     s() << "   <td>" << endl;
1820
1821     if (tci->tli->resource)
1822     {
1823         BookingList jobs = tci->tli->resource->getJobs(tci->tli->sc);
1824         jobs.setAutoDelete(true);
1825         time_t prevTime = 0;
1826         Interval reportPeriod(start, end);
1827         s() << "    <table style=\"width:150px; font-size:100%; "
1828            << "text-align:left\">" << endl
1829            << "      <tr>" << endl
1830            << "       <th style=\"width:35%\"></th>" << endl
1831            << "       <th style=\"width:65%\"></th>" << endl
1832            << "      </tr>" << endl;
1833         for (BookingList::Iterator bli(jobs); *bli != 0; ++bli)
1834         {
1835             if ((tci->tli->ca2 == 0 || tci->tli->task == (*bli)->getTask()) &&
1836                 reportPeriod.overlaps(Interval((*bli)->getStart(),
1837                                                (*bli)->getEnd())))
1838             {
1839                 /* If the reporting interval is not more than a day, we
1840                  * do not print the day since this information is most
1841                  * likely given by the context of the report. */
1842                 if (!isSameDay(prevTime, (*bli)->getStart()) &&
1843                     !isSameDay(start, end - 1))
1844                 {
1845                     s() << "      <tr>" << endl
1846                         << "       <td colspan=\"2\" style=\"font-size:120%\">"
1847                         << time2weekday((*bli)->getStart()) << ", "
1848                         << time2date((*bli)->getStart()) << "</td>" << endl
1849                         << "      </tr>"
1850                         << endl;
1851                 }
1852                 s() << "       <tr>" << endl
1853                     << "        <td>";
1854                 Interval workPeriod((*bli)->getStart(), (*bli)->getEnd());
1855                 workPeriod.overlap(reportPeriod);
1856                 s() << time2user(workPeriod.getStart(), shortTimeFormat)
1857                     << "&#160;-&#160;"
1858                     << time2user(workPeriod.getEnd() + 1, shortTimeFormat);
1859                 s() << "</td>" << endl
1860                     << "       <td>";
1861                 if (tci->tli->ca2 == 0)
1862                     s() << " " << htmlFilter((*bli)->getTask()->getName());
1863                 s() << "       </td>" << endl;
1864                 prevTime = (*bli)->getStart();
1865                 s() << "      </tr>" << endl;
1866             }
1867         }
1868         s() << "     </table>" << endl;
1869     }
1870     else
1871         s() << "&#160;";
1872
1873     s() << "   </td>" << endl;
1874 }
1875
1876 #define GCEFFORT(a) \
1877 void \
1878 HTMLReportElement::genCell##a##Effort(TableCellInfo* tci) \
1879 { \
1880     genCell(tci->tcf->realFormat.format \
1881             (tci->tli->resource->get##a##Effort(), false), \
1882             tci, true); \
1883 }
1884
1885 GCEFFORT(Min)
1886
1887 void
1888 HTMLReportElement::genCellMaxEffort(TableCellInfo* tci)
1889 {
1890     genCell(tci->tcf->realFormat.format
1891             (tci->tli->resource->getLimits() ?
1892              tci->tli->resource->getLimits()->getDailyMax() : 0, false),
1893             tci, true);
1894 }
1895
1896 void
1897 HTMLReportElement::genCellEfficiency(TableCellInfo* tci)
1898 {
1899     genCell(tci->tcf->realFormat.format(tci->tli->resource->getEfficiency(),
1900                                         tci), tci, true);
1901 }
1902
1903 void
1904 HTMLReportElement::genCellRate(TableCellInfo* tci)
1905 {
1906     genCell(tci->tcf->realFormat.format(tci->tli->resource->getRate(), tci),
1907             tci, true);
1908 }
1909
1910 void
1911 HTMLReportElement::genCellTotal(TableCellInfo* tci)
1912 {
1913     double value = tci->tli->account->getVolume(tci->tli->sc,
1914                                                 Interval(start, end));
1915     if (tci->tli->account->isLeaf())
1916         tci->tci->addToSum(tci->tli->sc, "total", value);
1917     genCell(tci->tcf->realFormat.format(value, tci), tci, false);
1918 }
1919
1920 void
1921 HTMLReportElement::genCellSummary(TableCellInfo* tci)
1922 {
1923     QMap<QString, double>::ConstIterator it;
1924     const QMap<QString, double>* sum = tci->tci->getSum();
1925     assert(sum != 0);
1926
1927     uint sc = tci->tli->sc;
1928     double val = 0.0;
1929     if (sum[sc].begin() != sum[sc].end())
1930     {
1931         for (it = sum[sc].begin(); it != sum[sc].end(); ++it)
1932         {
1933             if (accumulate)
1934                 val += *it;
1935             else
1936                 val = *it;
1937             genCell(tci->tcf->realFormat.format(val, tci), tci, false);
1938         }
1939     }
1940     else
1941     {
1942         // The column counter is not set in all cases. These are always single
1943         // column cases.
1944         if (tci->tci->getSubColumns() > 0)
1945             for (uint col = 0; col < tci->tci->getSubColumns(); ++col)
1946                 genCell(tci->tcf->realFormat.format(0.0, tci), tci, false);
1947         else
1948             genCell(tci->tcf->realFormat.format(0.0, tci), tci, false);
1949     }
1950 }
1951