OSDN Git Service

Automatically align underspecified tasks boundaries on project boundaries when possible.
[tjqt4port/tj2qt4.git] / taskjuggler / HTMLWeeklyCalendarElement.cpp
1 /*
2  * HTMLWeeklyCalendarElement.cpp - TaskJuggler
3  *
4  * Copyright (c) 2002, 2003, 2004 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 "HTMLWeeklyCalendarElement.h"
14 #include "tjlib-internal.h"
15 #include "Project.h"
16 #include "ExpressionTree.h"
17 #include "Operation.h"
18 #include "Resource.h"
19 #include "TableLineInfo.h"
20 #include "HTMLReport.h"
21
22 HTMLWeeklyCalendarElement::HTMLWeeklyCalendarElement(Report* r,
23                                                      const QString& df,
24                                                      int dl) :
25     HTMLReportElement(r, df, dl),
26     daysToShow(),
27     numberOfDays(7),
28     taskReport(true)
29 {
30     uint sc = r->getProject()->getMaxScenarios();
31     columns.append(new TableColumnInfo(sc, "name"));
32
33     // show all tasks
34     setHideTask(new ExpressionTree(new Operation(0)));
35     // hide all resources
36     setHideResource(new ExpressionTree(new Operation(1)));
37
38     taskSortCriteria[0] = CoreAttributesList::TreeMode;
39     taskSortCriteria[1] = CoreAttributesList::StartUp;
40     taskSortCriteria[2] = CoreAttributesList::EndUp;
41
42     resourceSortCriteria[0] = CoreAttributesList::TreeMode;
43     resourceSortCriteria[1] = CoreAttributesList::NameUp;
44     resourceSortCriteria[2] = CoreAttributesList::IdUp;
45
46     daysToShow.resize(7);
47     for (unsigned int i = 0; i < 7; ++i)
48         daysToShow.setBit(i);
49 }
50
51 HTMLWeeklyCalendarElement::~HTMLWeeklyCalendarElement()
52 {
53 }
54
55 void
56 HTMLWeeklyCalendarElement::setDaysToShow(QBitArray& days)
57 {
58     daysToShow = days;
59     numberOfDays = 0;
60     for (int i = days.size() - 1; i >= 0; i--)
61         if (days[i])
62             numberOfDays++;
63 }
64
65 void
66 HTMLWeeklyCalendarElement::generateTableHeader(bool weekStartsMonday)
67 {
68     s() << " <thead>" << endl
69         << "   <tr style=\"background-color:"
70         << colors.getColorName("header")
71         << "; text-align:center\">" << endl;
72     time_t wd = beginOfWeek(start, weekStartsMonday);
73     QString cellWidth;
74     cellWidth.sprintf("%.1f", 100.0 / numberOfDays);
75     for (int day = 0; day < 7; ++day, wd = sameTimeNextDay(wd))
76     {
77         if (!showThisDay(day, weekStartsMonday))
78             continue;
79
80         s() << "   <th width=\"" << cellWidth << "%\" style=\"font-size:110%; ";
81         if (isWeekend(wd))
82             s() << "background-color:"
83                 << colors.getColor("header").dark(130).name();
84         s() << "\">"
85             << htmlFilter(dayOfWeekName(wd)) << "</th>" << endl;
86     }
87     s() << "  </tr>" << endl
88         << " </thead>" << endl;
89 }
90
91 void
92 HTMLWeeklyCalendarElement::generateWeekHeader(bool weekStartsMonday,
93                                               time_t week)
94 {
95     /* Generate table row that contains the day of the month, the month
96      * and the year. The first column of the row also has the number of
97      * the week. */
98     time_t wd = week;
99     s() << "  <tr style=\"background-color:"
100         << colors.getColorName("header")
101         << "; text-align:center\">" << endl;
102     QString lastMAY;
103     QString cellWidth;
104     cellWidth.sprintf("%.1f", 100.0 / numberOfDays);
105     for (int day = 0; day < 7; ++day, wd = sameTimeNextDay(wd))
106     {
107         if (!showThisDay(day, weekStartsMonday))
108             continue;
109
110         s() << "   <td width=\"" << cellWidth << "%\"";
111         if (isSameDay(report->getProject()->getNow(), wd))
112             s() << " style=\"background-color:"
113                 << colors.getColorName("today") << "\"";
114         else if (isWeekend(wd))
115             s() << " style=\"background-color:"
116                 << colors.getColor("header").dark(130).name() << "\"";
117         s() << ">" << endl
118             << "   <table width=\"100%\">"
119             << endl
120             << "    <tr>" << endl
121             << "     <td width=\"30%\" rowspan=\"2\" "
122             "style=\"font-size:200%; text-align:center\">"
123             << QString().sprintf("%d", dayOfMonth(wd)) << "</td>" << endl
124             << "     <td width=\"70%\" style=\"font-size:60%\">";
125         if (day == 0)
126             s() << htmlFilter(i18n("Week")) << " "
127                 << QString("%1").arg(weekOfYear(wd, weekStartsMonday));
128         else
129             s() << "<p></p>";
130         s() << "     </td>" << endl
131             << "    </tr>" << endl
132             << "    <tr>" << endl;
133         QString mAY = monthAndYear(wd);
134         if (mAY != lastMAY)
135         {
136             s() << "     <td style=\"font-size:90%\">"
137                 << htmlFilter(mAY) << "</td>" << endl;
138             lastMAY = mAY;
139         }
140         s() << "    </tr>" << endl;
141         if (report->getProject()->isVacation(wd))
142             s() << "    <tr><td colspan=\"2\" style=\"font-size:80%\">"
143                 << htmlFilter(report->getProject()->vacationName(wd))
144                 << "</td></tr>" << endl;
145         s() << "   </table></td>" << endl;
146     }
147     s() << "  </tr>" << endl;
148 }
149
150 bool
151 HTMLWeeklyCalendarElement::generateTaksPerDay(
152    time_t& wd, TaskList& filteredTaskList, ResourceList& filteredResourceList,
153    bool weekStartsMonday)
154 {
155     // Generate a row with lists the tasks for each day.
156     s() << "  <tr style=\"background-color:"
157         << colors.getColorName("default") << "\">" << endl
158         << endl;
159     QString cellWidth;
160     cellWidth.sprintf("%.1f", 100.0 / numberOfDays);
161     for (int day = 0; day < 7; ++day, wd = sameTimeNextDay(wd))
162     {
163         if (!showThisDay(day, weekStartsMonday))
164             continue;
165
166         /* Misuse the class member start and end to limit the scope of
167          * the information listed. */
168         time_t savedStart = start;
169         time_t savedEnd = end;
170         start = wd;
171         end = sameTimeNextDay(wd);
172
173         s() << "   <td width=\"" << cellWidth
174             << "\" style=\"vertical-align:top\">" << endl;
175         bool first = true;
176         int no = 1;
177         for (TaskListIterator tli(filteredTaskList); *tli != 0;
178              ++tli, ++no)
179         {
180             if ((*tli)->getLoad(scenarios[0], Interval(start, end)) == 0.0)
181                 continue;
182             if (!(*tli)->isActive(scenarios[0],
183                                   Interval(wd, sameTimeNextDay(wd))))
184                 continue;
185
186             if (first)
187             {
188                 s() << "     <table width=\"100%\">" << endl;
189                 first = false;
190             }
191             TableLineInfo tli1;
192             tli1.ca1 = tli1.task = *tli;
193             tli1.idxNo = no;
194             tli1.fontFactor = 40;
195             generateLine(&tli1, 2);
196
197             if (!filterResourceList(filteredResourceList, *tli,
198                                     getHideResource(), getRollUpResource()))
199                 return false;
200             sortResourceList(filteredResourceList);
201             int rNo = 1;
202             for (ResourceListIterator rli(filteredResourceList); *rli != 0;
203                  ++rli, ++rNo)
204             {
205                 TableLineInfo tli2;
206                 tli2.ca1 = tli2.resource = *rli;
207                 tli2.ca2 = tli2.task = *tli;
208                 for (uint sc = 0; sc < scenarios.count(); ++sc)
209                 {
210                     tli2.row = sc;
211                     tli2.sc = scenarios[sc];
212                     tli2.idxNo = rNo;
213                     tli2.fontFactor = 30;
214                     generateLine(&tli2, sc == 0 ? 4 : 5);
215                 }
216             }
217         }
218         if (!first)
219             s() << "     </table>" << endl;
220         s() << "   </td>" << endl;
221         start = savedStart;
222         end = savedEnd;
223     }
224     s() << "  </tr>" << endl;
225
226     return true;
227 }
228
229 bool
230 HTMLWeeklyCalendarElement::generateResourcesPerDay
231     (time_t& wd, ResourceList& filteredResourceList,
232      TaskList& filteredTaskList, bool weekStartsMonday)
233 {
234     // Generate a table row which lists the resources for each day.
235     s() << "  <tr style=\"background-color:"
236         << colors.getColorName("default") << "\">" << endl
237         << endl;
238     QString cellWidth;
239     cellWidth.sprintf("%.1f", 100.0 / numberOfDays);
240     for (int day = 0; day < 7; ++day, wd = sameTimeNextDay(wd))
241     {
242         if (!showThisDay(day, weekStartsMonday))
243             continue;
244
245         /* Misuse the class member start and end to limit the scope of
246          * the information listed. */
247         time_t savedStart = start;
248         time_t savedEnd = end;
249         start = wd;
250         end = sameTimeNextDay(wd);
251         s() << "   <td width=\"" << cellWidth
252             << "\" style=\"vertical-align:top\">"
253             << endl;
254         bool first = true;
255         int no = 1;
256         for (ResourceListIterator rli(filteredResourceList);
257              *rli != 0; ++rli, ++no)
258         {
259             if ((*rli)->getEffectiveLoad
260                 (scenarios[0], Interval(wd, sameTimeNextDay(wd))) <= 0.0)
261                 continue;
262
263             if (first)
264             {
265                 s() << "     <table width=\"100%\">" << endl;
266                 first = false;
267             }
268             TableLineInfo tli2;
269             tli2.ca1 = tli2.resource = *rli;
270             tli2.idxNo = no;
271             tli2.fontFactor = 40;
272             generateLine(&tli2, 4);
273
274             /* We only want to show the nested task list for leaf resources.
275              * Leaf in this case means "task has no visible childs". */
276             bool hasVisibleChilds = false;
277             for (ResourceListIterator cli((*rli)->getSubListIterator());
278                  *cli; ++cli)
279                 if (filteredResourceList.findRef(*cli) >= 0)
280                 {
281                     hasVisibleChilds = true;
282                     break;
283                 }
284
285             if (hasVisibleChilds)
286                 continue;
287
288             if (!filterTaskList(filteredTaskList, *rli, hideTask, rollUpTask))
289                 return false;
290             sortTaskList(filteredTaskList);
291
292             int tNo = 1;
293             for (TaskListIterator tli(filteredTaskList); *tli != 0; ++tli,
294                  ++tNo)
295             {
296                 TableLineInfo tli2;
297                 tli2.ca1 = tli2.task = *tli;
298                 tli2.ca2 = tli2.resource = *rli;
299                 for (uint sc = 0; sc < scenarios.count(); ++sc)
300                 {
301                     tli2.row = sc;
302                     tli2.sc = scenarios[sc];
303                     tli2.idxNo = tNo;
304                     tli2.fontFactor = 30;
305                     generateLine(&tli2, sc == 0 ? 2 : 3);
306                 }
307             }
308         }
309         if (!first)
310             s() << "     </table>" << endl;
311         s() << "   </td>" << endl;
312         start = savedStart;
313         end = savedEnd;
314     }
315     s() << "  </tr>" << endl;
316
317     return true;
318 }
319
320
321 bool
322 HTMLWeeklyCalendarElement::generate()
323 {
324     generateHeader();
325
326     TaskList filteredTaskList;
327     if (!filterTaskList(filteredTaskList, 0, hideTask, rollUpTask))
328         return false;
329     sortTaskList(filteredTaskList);
330     maxDepthTaskList = filteredTaskList.maxDepth();
331
332     ResourceList filteredResourceList;
333     if (!filterResourceList(filteredResourceList, 0, hideResource,
334                             rollUpResource))
335         return false;
336     sortResourceList(filteredResourceList);
337     maxDepthResourceList = filteredResourceList.maxDepth();
338
339     bool weekStartsMonday = report->getProject()->getWeekStartsMonday();
340     s() << "<table align=\"center\" cellpadding=\"2\" "
341         << "style=\"background-color:#000000\"";
342     if (static_cast<HTMLReport*>(report)->hasStyleSheet())
343         s() << " class=\"tj_table\"";
344     s() << ">" << endl;
345
346     generateTableHeader(weekStartsMonday);
347
348     s()    << " <tbody>" << endl;
349
350     for (time_t week = beginOfWeek(start, weekStartsMonday);
351          week <= sameTimeNextWeek(beginOfWeek(end, weekStartsMonday)) - 1; )
352     {
353         generateWeekHeader(weekStartsMonday, week);
354
355         if (taskReport)
356         {
357             if (!filteredTaskList.isEmpty())
358             {
359                 if (!generateTaksPerDay(week, filteredTaskList,
360                                         filteredResourceList,
361                                         weekStartsMonday))
362                     return false;
363             }
364             else
365                 week = sameTimeNextWeek(week);
366         }
367         else
368         {
369             if (!filteredResourceList.isEmpty())
370             {
371                 if (!generateResourcesPerDay(week, filteredResourceList,
372                                              filteredTaskList,
373                                              weekStartsMonday))
374                     return false;
375             }
376             else
377                 week = sameTimeNextWeek(week);
378         }
379     }
380
381     s() << " </tbody>" << endl << "</table>" << endl;
382
383     generateFooter();
384
385     return true;
386 }
387
388 bool
389 HTMLWeeklyCalendarElement::showThisDay(int dayIndex, bool weekStartsMonday)
390 {
391     /* The dayIndex may start with Sunday or Monday depending of the setting
392      * of weekStartsMonday. daysToShow[0] is always Sunday. */
393
394     return daysToShow[(dayIndex + (weekStartsMonday ? 1 : 0)) % 7];
395 }
396