1 /*
2 File timetableexport.cpp
3 */
4 
5 /***************************************************************************
6                           timetableexport.cpp  -  description
7                           -------------------
8     begin                : Tue Apr 22 2003
9     copyright            : (C) 2003 by Lalescu Liviu
10     email                : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
11  ***************************************************************************/
12 
13 /***************************************************************************
14  *                                                                         *
15  *   This program is free software: you can redistribute it and/or modify  *
16  *   it under the terms of the GNU Affero General Public License as        *
17  *   published by the Free Software Foundation, either version 3 of the    *
18  *   License, or (at your option) any later version.                       *
19  *                                                                         *
20  ***************************************************************************/
21 
22 //**********************************************************************************************************************/
23 //August 2007
24 //XHTML generation code by Volker Dirr (timetabling.de)
25 //Features:   - XHTML 1.0 strict valid
26 //            - using colspan and rowspan
27 //            - table of contents with hyperlinks
28 //            - CSS and JavaScript support
29 //            - index HTML file
30 //            - TIMETABLE_HTML_LEVEL
31 //            - days/time horizontal/vertical
32 //            - subgroups, groups, years, teachers, rooms, subjects, activities, activity tags timetable
33 //            - teachers free periods
34 //            - daily timetable
35 //            - activities with same starting time
36 //            - reorganized functions. now they can be also used for printing
37 //            - split times tables after X names (TIMETABLE_HTML_SPLIT?) and choose if activity tags should be printed (TIMETABLE_HTML_PRINT_ACTIVITY_TAGS)
38 //            - teachers and students statistics (gaps, free days, hours)
39 
40 //TODO: all must be internal here. so maybe also do daysOfTheWeek and hoursPerDay also internal
41 //maybe TODO: use back_odd and back_even (or back0 and back1, because easier to code!) like in printing. so don't use the table_odd and table_even anymore
42 //maybe TODO: make TIMETABLE_HTML_SPLIT? (similar to TIMETABLE_HTML_LEVEL)
43 //maybe TODO: rename augmentedYearsList into internalYearsList to have it similar to others?
44 //maybe TODO: some "stg" stuff can be replaced by gt.rules.internalGroupsList. I don't want to do that now, because id-s will change. That is not critical, but I want to diff tables with old release.
45 
46 #include "timetable_defs.h"
47 #include "timetable.h"
48 #include "timetableexport.h"
49 #include "solution.h"
50 
51 #include "matrix.h"
52 
53 #include <iostream>
54 using namespace std;
55 
56 #include <Qt>
57 
58 #include <QString>
59 #include <QTextStream>
60 #include <QFile>
61 
62 #include <QList>
63 
64 #include <QHash>
65 
66 #include "messageboxes.h"
67 
68 #include <QLocale>
69 #include <QTime>
70 #include <QDate>
71 
72 #include <QDir>
73 
74 //std::stable_sort
75 #include <algorithm>
76 
77 //Represents the current status of the simulation - running or stopped.
78 //extern bool simulation_running;
79 
80 extern bool students_schedule_ready;
81 extern bool teachers_schedule_ready;
82 extern bool rooms_schedule_ready;
83 
84 extern Solution best_solution;
85 extern bool LANGUAGE_STYLE_RIGHT_TO_LEFT;
86 extern QString LANGUAGE_FOR_HTML;
87 
88 extern Timetable gt;
89 extern Matrix3D<int> teachers_timetable_weekly;
90 extern Matrix3D<int> students_timetable_weekly;
91 extern Matrix3D<int> rooms_timetable_weekly;
92 extern Matrix3D<QList<int>> virtual_rooms_timetable_weekly;
93 
94 extern Matrix3D<QList<int>> teachers_free_periods_timetable_weekly;
95 
96 extern Matrix2D<bool> breakDayHour;
97 extern Matrix3D<bool> teacherNotAvailableDayHour;
98 extern Matrix2D<double> notAllowedRoomTimePercentages;
99 extern Matrix3D<bool> subgroupNotAvailableDayHour;
100 
101 static Matrix2D<QList<int>> activitiesForCurrentSubject;
102 static Matrix2D<QList<int>> activitiesForCurrentActivityTag;
103 
104 static Matrix2D<QList<int>> activitiesAtTime;
105 
106 extern Rules rules2;
107 
108 const QString STRING_EMPTY_SLOT="---";
109 
110 const QString STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES="???";
111 
112 const QString STRING_NOT_AVAILABLE_TIME_SLOT="-x-";
113 
114 const QString STRING_BREAK_SLOT="-X-";
115 
116 //these hashes are needed to get the IDs for html and css in timetableexport and statistics
117 static QHash<QString, QString> hashSubjectIDsTimetable;
118 static QHash<QString, QString> hashActivityTagIDsTimetable;
119 static QHash<QString, QString> hashStudentIDsTimetable;
120 static QHash<QString, QString> hashTeacherIDsTimetable;
121 static QHash<QString, QString> hashRoomIDsTimetable;
122 static QHash<QString, QString> hashDayIDsTimetable;
123 
124 //static QHash<QString, QString> hashColorStringIDsTimetable;
125 QHash<int, int> hashActivityColorBySubject;
126 QList<int> activeHashActivityColorBySubject;
127 QHash<int, int> hashActivityColorBySubjectAndStudents;
128 QList<int> activeHashActivityColorBySubjectAndStudents;
129 const int COLOR_BY_SUBJECT=1;
130 const int COLOR_BY_SUBJECT_STUDENTS=2;
131 
132 //this hash is needed to care about sctivities with same starting time
133 static QHash<int, QList<int>>activitiesWithSameStartingTime;
134 
135 //Now the filenames of the output files are following (for xml and all html tables)
136 const QString SUBGROUPS_TIMETABLE_FILENAME_XML="subgroups.xml";
137 const QString TEACHERS_TIMETABLE_FILENAME_XML="teachers.xml";
138 const QString ACTIVITIES_TIMETABLE_FILENAME_XML="activities.xml";
139 //const QString ROOMS_TIMETABLE_FILENAME_XML="rooms.xml";
140 
141 const QString CONFLICTS_FILENAME="soft_conflicts.txt";
142 const QString INDEX_HTML="index.html";
143 const QString STYLESHEET_CSS="stylesheet.css";
144 
145 const QString SUBGROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="subgroups_days_horizontal.html";
146 const QString SUBGROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="subgroups_days_vertical.html";
147 const QString SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="subgroups_time_horizontal.html";
148 const QString SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="subgroups_time_vertical.html";
149 
150 const QString GROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="groups_days_horizontal.html";
151 const QString GROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="groups_days_vertical.html";
152 const QString GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="groups_time_horizontal.html";
153 const QString GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="groups_time_vertical.html";
154 
155 const QString YEARS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="years_days_horizontal.html";
156 const QString YEARS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="years_days_vertical.html";
157 const QString YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="years_time_horizontal.html";
158 const QString YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="years_time_vertical.html";
159 
160 const QString TEACHERS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="teachers_days_horizontal.html";
161 const QString TEACHERS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="teachers_days_vertical.html";
162 const QString TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="teachers_time_horizontal.html";
163 const QString TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="teachers_time_vertical.html";
164 
165 const QString ROOMS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="rooms_days_horizontal.html";
166 const QString ROOMS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="rooms_days_vertical.html";
167 const QString ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="rooms_time_horizontal.html";
168 const QString ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="rooms_time_vertical.html";
169 
170 const QString SUBJECTS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="subjects_days_horizontal.html";
171 const QString SUBJECTS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="subjects_days_vertical.html";
172 const QString SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="subjects_time_horizontal.html";
173 const QString SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="subjects_time_vertical.html";
174 
175 const QString ACTIVITY_TAGS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="activity_tags_days_horizontal.html";
176 const QString ACTIVITY_TAGS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="activity_tags_days_vertical.html";
177 const QString ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="activity_tags_time_horizontal.html";
178 const QString ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="activity_tags_time_vertical.html";
179 
180 const QString ALL_ACTIVITIES_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="activities_days_horizontal.html";
181 const QString ALL_ACTIVITIES_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="activities_days_vertical.html";
182 const QString ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML="activities_time_horizontal.html";
183 const QString ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML="activities_time_vertical.html";
184 
185 const QString TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML="teachers_free_periods_days_horizontal.html";
186 const QString TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML="teachers_free_periods_days_vertical.html";
187 
188 const QString TEACHERS_STATISTICS_FILENAME_HTML="teachers_statistics.html";
189 const QString STUDENTS_STATISTICS_FILENAME_HTML="students_statistics.html";
190 
191 const QString MULTIPLE_TIMETABLE_DATA_RESULTS_FILE="data_and_timetable.fet";
192 
193 //now the XML tags used for identification of the output file (is that comment correct? it's the old comment)
194 const QString STUDENTS_TIMETABLE_TAG="Students_Timetable";
195 const QString TEACHERS_TIMETABLE_TAG="Teachers_Timetable";
196 const QString ACTIVITIES_TIMETABLE_TAG="Activities_Timetable";
197 const QString ROOMS_TIMETABLE_TAG="Rooms_Timetable";
198 
199 const QString RANDOM_SEED_FILENAME_BEFORE="random_seed_before.txt";
200 const QString RANDOM_SEED_FILENAME_AFTER="random_seed_after.txt";
201 
202 //extern int XX;
203 //extern int YY;
204 //extern MRG32k3a rng;
205 
206 QString generationLocalizedTime=QString(""); //to be used in timetableprintform.cpp
207 
208 //similar with the code from Marco Vassura, modified by Volker Dirr to avoid usage of QColor and QBrush, since these need QtGui.
209 //(the command-line version does not have access to QtGui.)
210 //slightly modified by Liviu Lalescu on 2021-03-01
stringToColor(const QString & s,int & r,int & g,int & b)211 void TimetableExport::stringToColor(const QString& s, int& r, int& g, int& b)
212 {
213 	// CRC-24 based on RFC 2440 Section 6.1
214 	unsigned long int crc = 0xB704CEUL;
215 	QByteArray ba=s.toUtf8();
216 	for(char c : qAsConst(ba)){
217 		unsigned char uc=(unsigned char)(c);
218 		crc ^= (uc & 0xFF) << 16;
219 		for (int i = 0; i < 8; i++) {
220 			crc <<= 1;
221 			if (crc & 0x1000000UL)
222 				crc ^= 0x1864CFBUL;
223 		}
224 	}
225 
226 	r = int((crc>>16) & 0xFF);
227 	g = int((crc>>8) & 0xFF);
228 	b = int(crc & 0xFF);
229 }
230 //similar with the code from Marco Vassura, modified by Volker Dirr
231 
writeAtLeastATimetable()232 bool writeAtLeastATimetable()
233 {
234 	bool t = WRITE_TIMETABLE_CONFLICTS ||
235 
236 	 (WRITE_TIMETABLES_STATISTICS &&
237 	 (WRITE_TIMETABLES_SUBGROUPS ||
238 	 WRITE_TIMETABLES_GROUPS ||
239 	 WRITE_TIMETABLES_YEARS ||
240 	 WRITE_TIMETABLES_TEACHERS)) ||
241 
242 	 (WRITE_TIMETABLES_XML &&
243 	 (WRITE_TIMETABLES_SUBGROUPS ||
244 	 WRITE_TIMETABLES_TEACHERS ||
245 	 WRITE_TIMETABLES_ACTIVITIES)) ||
246 
247 	 ((WRITE_TIMETABLES_DAYS_HORIZONTAL ||
248 	 WRITE_TIMETABLES_DAYS_VERTICAL ||
249 	 WRITE_TIMETABLES_TIME_HORIZONTAL ||
250 	 WRITE_TIMETABLES_TIME_VERTICAL) &&
251 	 (WRITE_TIMETABLES_SUBGROUPS ||
252 	 WRITE_TIMETABLES_GROUPS ||
253 	 WRITE_TIMETABLES_YEARS ||
254 	 WRITE_TIMETABLES_TEACHERS ||
255 	 WRITE_TIMETABLES_ROOMS ||
256 	 WRITE_TIMETABLES_SUBJECTS ||
257 	 WRITE_TIMETABLES_ACTIVITY_TAGS ||
258 	 WRITE_TIMETABLES_ACTIVITIES)) ||
259 
260 	 ((WRITE_TIMETABLES_DAYS_HORIZONTAL ||
261 	 WRITE_TIMETABLES_DAYS_VERTICAL) &&
262 	 WRITE_TIMETABLES_TEACHERS_FREE_PERIODS);
263 
264 	 return t;
265 }
266 
TimetableExport()267 TimetableExport::TimetableExport()
268 {
269 }
270 
~TimetableExport()271 TimetableExport::~TimetableExport()
272 {
273 }
274 
275 /*void TimetableExport::getStudentsTimetable(Solution &c){
276 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
277 
278 	c.getSubgroupsTimetable(gt.rules, students_timetable_weekly);
279 	best_solution.copy(gt.rules, c);
280 	students_schedule_ready=true;
281 }
282 
283 void TimetableExport::getTeachersTimetable(Solution &c){
284 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
285 
286 	c.getTeachersTimetable(gt.rules, teachers_timetable_weekly, teachers_free_periods_timetable_weekly);
287 	best_solution.copy(gt.rules, c);
288 	teachers_schedule_ready=true;
289 }
290 
291 void TimetableExport::getRoomsTimetable(Solution &c){
292 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
293 
294 	c.getRoomsTimetable(gt.rules, rooms_timetable_weekly, virtual_rooms_timetable_weekly);
295 	best_solution.copy(gt.rules, c);
296 	rooms_schedule_ready=true;
297 }*/
298 
getStudentsTeachersRoomsTimetable(Solution & c)299 void TimetableExport::getStudentsTeachersRoomsTimetable(Solution &c){
300 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
301 
302 	c.getSubgroupsTimetable(gt.rules, students_timetable_weekly);
303 	c.getTeachersTimetable(gt.rules, teachers_timetable_weekly, teachers_free_periods_timetable_weekly);
304 	c.getRoomsTimetable(gt.rules, rooms_timetable_weekly, virtual_rooms_timetable_weekly);
305 
306 	best_solution.copy(gt.rules, c);
307 
308 	students_schedule_ready=true;
309 	teachers_schedule_ready=true;
310 	rooms_schedule_ready=true;
311 }
312 
getNumberOfPlacedActivities(int & number1,int & number2)313 void TimetableExport::getNumberOfPlacedActivities(int& number1, int& number2)
314 {
315 	number1=0;
316 	for(int i=0; i<gt.rules.nInternalActivities; i++)
317 		if(best_solution.times[i]!=UNALLOCATED_TIME)
318 			number1++;
319 
320 	number2=0;
321 	for(int i=0; i<gt.rules.nInternalActivities; i++)
322 		if(best_solution.rooms[i]!=UNALLOCATED_SPACE)
323 			number2++;
324 }
325 
writeSimulationResults(QWidget * parent)326 void TimetableExport::writeSimulationResults(QWidget* parent){
327 	QList<int> subgroupsSortedOrder;
328 	QList<StudentsSubgroup*> lst;
329 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
330 		lst.append(gt.rules.internalSubgroupsList[subgroup]);
331 	if(TIMETABLES_SUBGROUPS_SORTED)
332 		std::stable_sort(lst.begin(), lst.end(), subgroupsAscending);
333 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
334 		subgroupsSortedOrder.append(lst.at(subgroup)->indexInInternalSubgroupsList);
335 
336 	QDir dir;
337 
338 	QString OUTPUT_DIR_TIMETABLES=OUTPUT_DIR+FILE_SEP+"timetables";
339 
340 	OUTPUT_DIR_TIMETABLES.append(FILE_SEP);
341 	if(INPUT_FILENAME_XML=="")
342 		OUTPUT_DIR_TIMETABLES.append("unnamed");
343 	else{
344 		OUTPUT_DIR_TIMETABLES.append(INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1));
345 		if(OUTPUT_DIR_TIMETABLES.right(4)==".fet")
346 			OUTPUT_DIR_TIMETABLES=OUTPUT_DIR_TIMETABLES.left(OUTPUT_DIR_TIMETABLES.length()-4);
347 		//else if(INPUT_FILENAME_XML!="")
348 		//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
349 	}
350 	OUTPUT_DIR_TIMETABLES.append("-single");
351 
352 	//make sure that the output directory exists
353 	if(!dir.exists(OUTPUT_DIR_TIMETABLES))
354 		dir.mkpath(OUTPUT_DIR_TIMETABLES);
355 
356 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
357 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
358 	assert(TIMETABLE_HTML_LEVEL>=0);
359 	assert(TIMETABLE_HTML_LEVEL<=7);
360 
361 	computeHashForIDsTimetable();
362 	computeActivitiesAtTime();
363 	computeActivitiesWithSameStartingTime();
364 
365 	QString s;
366 	QString bar;
367 	if(INPUT_FILENAME_XML=="")
368 		bar="";
369 	else
370 		bar="_";
371 	QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
372 	if(s2.right(4)==".fet")
373 		s2=s2.left(s2.length()-4);
374 	//else if(INPUT_FILENAME_XML!="")
375 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
376 
377 	//now write the solution in xml files
378 	//subgroups
379 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_FILENAME_XML;
380 	writeSubgroupsTimetableXml(parent, s, subgroupsSortedOrder);
381 	//teachers
382 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_FILENAME_XML;
383 	writeTeachersTimetableXml(parent, s);
384 	//activities
385 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITIES_TIMETABLE_FILENAME_XML;
386 	writeActivitiesTimetableXml(parent, s);
387 
388 	//now get the time. TODO: maybe write it in xml too? so do it a few lines earlier!
389 	QDate dat=QDate::currentDate();
390 	QTime tim=QTime::currentTime();
391 	QLocale loc(FET_LANGUAGE);
392 	QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
393 	generationLocalizedTime=sTime;
394 
395 	//now get the number of placed activities. TODO: maybe write it in xml too? so do it a few lines earlier!
396 	int na=0;
397 	int na2=0;
398 	getNumberOfPlacedActivities(na, na2);
399 
400 	if(na==gt.rules.nInternalActivities && na==na2){
401 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+MULTIPLE_TIMETABLE_DATA_RESULTS_FILE;
402 		if(VERBOSE){
403 			cout<<"Since the simulation is complete, FET will write also the timetable data file"<<endl;
404 		}
405 		writeTimetableDataFile(parent, s);
406 	}
407 
408 	//write the conflicts in txt mode
409 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+CONFLICTS_FILENAME;
410 	writeConflictsTxt(parent, s, sTime, na);
411 
412 	//now write the solution in html files
413 	if(TIMETABLE_HTML_LEVEL>=1){
414 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+STYLESHEET_CSS;
415 		writeStylesheetCss(parent, s, sTime, na);
416 	}
417 
418 	//indexHtml
419 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+INDEX_HTML;
420 	writeIndexHtml(parent, s, sTime, na);
421 
422 	//subgroups
423 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
424 	writeSubgroupsTimetableDaysHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
425 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
426 	writeSubgroupsTimetableDaysVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
427 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
428 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
429 		writeSubgroupsTimetableTimeHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
430 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
431 		writeSubgroupsTimetableTimeVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
432 	} else {
433 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
434 		writeSubgroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
435 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
436 		writeSubgroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
437 	}
438 	//groups
439 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
440 	writeGroupsTimetableDaysHorizontalHtml(parent, s, sTime, na);
441 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
442 	writeGroupsTimetableDaysVerticalHtml(parent, s, sTime, na);
443 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
444 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
445 		writeGroupsTimetableTimeHorizontalHtml(parent, s, sTime, na);
446 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
447 		writeGroupsTimetableTimeVerticalHtml(parent, s, sTime, na);
448 	} else {
449 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
450 		writeGroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
451 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
452 		writeGroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
453 	}
454 	//years
455 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
456 	writeYearsTimetableDaysHorizontalHtml(parent, s, sTime, na);
457 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
458 	writeYearsTimetableDaysVerticalHtml(parent, s, sTime, na);
459 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
460 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
461 		writeYearsTimetableTimeHorizontalHtml(parent, s, sTime, na);
462 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
463 		writeYearsTimetableTimeVerticalHtml(parent, s, sTime, na);
464 	} else {
465 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
466 		writeYearsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
467 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
468 		writeYearsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
469 	}
470 	//teachers
471 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
472 	writeTeachersTimetableDaysHorizontalHtml(parent, s, sTime, na);
473 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
474 	writeTeachersTimetableDaysVerticalHtml(parent, s, sTime, na);
475 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
476 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
477 		writeTeachersTimetableTimeHorizontalHtml(parent, s, sTime, na);
478 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
479 		writeTeachersTimetableTimeVerticalHtml(parent, s, sTime, na);
480 	} else {
481 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
482 		writeTeachersTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
483 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
484 		writeTeachersTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
485 	}
486 	//rooms
487 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
488 	writeRoomsTimetableDaysHorizontalHtml(parent, s, sTime, na);
489 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
490 	writeRoomsTimetableDaysVerticalHtml(parent, s, sTime, na);
491 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
492 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
493 		writeRoomsTimetableTimeHorizontalHtml(parent, s, sTime, na);
494 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
495 		writeRoomsTimetableTimeVerticalHtml(parent, s, sTime, na);
496 	} else {
497 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
498 		writeRoomsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
499 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
500 		writeRoomsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
501 	}
502 	//subjects
503 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
504 	writeSubjectsTimetableDaysHorizontalHtml(parent, s, sTime, na);
505 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
506 	writeSubjectsTimetableDaysVerticalHtml(parent, s, sTime, na);
507 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
508 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
509 		writeSubjectsTimetableTimeHorizontalHtml(parent, s, sTime, na);
510 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
511 		writeSubjectsTimetableTimeVerticalHtml(parent, s, sTime, na);
512 	} else {
513 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
514 		writeSubjectsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
515 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
516 		writeSubjectsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
517 	}
518 	//activty_tags
519 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
520 	writeActivityTagsTimetableDaysHorizontalHtml(parent, s, sTime, na);
521 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
522 	writeActivityTagsTimetableDaysVerticalHtml(parent, s, sTime, na);
523 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
524 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
525 		writeActivityTagsTimetableTimeHorizontalHtml(parent, s, sTime, na);
526 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
527 		writeActivityTagsTimetableTimeVerticalHtml(parent, s, sTime, na);
528 	} else {
529 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
530 		writeActivityTagsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
531 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
532 		writeActivityTagsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
533 	}
534 	//all activities
535 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
536 	writeAllActivitiesTimetableDaysHorizontalHtml(parent, s, sTime, na);
537 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
538 	writeAllActivitiesTimetableDaysVerticalHtml(parent, s, sTime, na);
539 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
540 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
541 		writeAllActivitiesTimetableTimeHorizontalHtml(parent, s, sTime, na);
542 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
543 		writeAllActivitiesTimetableTimeVerticalHtml(parent, s, sTime, na);
544 	} else {
545 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
546 		writeAllActivitiesTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
547 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
548 		writeAllActivitiesTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
549 	}
550 	//teachers free periods
551 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
552 	writeTeachersFreePeriodsTimetableDaysHorizontalHtml(parent, s, sTime, na);
553 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
554 	writeTeachersFreePeriodsTimetableDaysVerticalHtml(parent, s, sTime, na);
555 	//statistics
556 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_STATISTICS_FILENAME_HTML;
557 	writeTeachersStatisticsHtml(parent, s, sTime, na);
558 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+STUDENTS_STATISTICS_FILENAME_HTML;
559 	writeStudentsStatisticsHtml(parent, s, sTime, na);
560 
561 /*
562 	//needed for printing from the interface, so don't clear them!
563 	hashSubjectIDsTimetable.clear();
564 	hashActivityTagIDsTimetable.clear();
565 	hashStudentIDsTimetable.clear();
566 	hashTeacherIDsTimetable.clear();
567 	hashRoomIDsTimetable.clear();
568 	hashDayIDsTimetable.clear();
569 	hashActivityColorBySubject.clear();
570 	hashActivityColorBySubjectAndStudents.clear();
571 	activeHashActivityColorBySubject.clear();
572 	activeHashActivityColorBySubjectAndStudents.clear();
573 */
574 	if(VERBOSE){
575 		cout<<"Writing simulation results to disk completed successfully"<<endl;
576 	}
577 }
578 
writeHighestStageResults(QWidget * parent)579 void TimetableExport::writeHighestStageResults(QWidget* parent){
580 	QList<int> subgroupsSortedOrder;
581 	QList<StudentsSubgroup*> lst;
582 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
583 		lst.append(gt.rules.internalSubgroupsList[subgroup]);
584 	if(TIMETABLES_SUBGROUPS_SORTED)
585 		std::stable_sort(lst.begin(), lst.end(), subgroupsAscending);
586 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
587 		subgroupsSortedOrder.append(lst.at(subgroup)->indexInInternalSubgroupsList);
588 	QDir dir;
589 
590 	QString OUTPUT_DIR_TIMETABLES=OUTPUT_DIR+FILE_SEP+"timetables";
591 
592 	OUTPUT_DIR_TIMETABLES.append(FILE_SEP);
593 	if(INPUT_FILENAME_XML=="")
594 		OUTPUT_DIR_TIMETABLES.append("unnamed");
595 	else{
596 		OUTPUT_DIR_TIMETABLES.append(INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1));
597 		if(OUTPUT_DIR_TIMETABLES.right(4)==".fet")
598 			OUTPUT_DIR_TIMETABLES=OUTPUT_DIR_TIMETABLES.left(OUTPUT_DIR_TIMETABLES.length()-4);
599 		//else if(INPUT_FILENAME_XML!="")
600 		//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
601 	}
602 	OUTPUT_DIR_TIMETABLES.append("-highest");
603 
604 	//make sure that the output directory exists
605 	if(!dir.exists(OUTPUT_DIR_TIMETABLES))
606 		dir.mkpath(OUTPUT_DIR_TIMETABLES);
607 
608 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
609 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
610 	assert(TIMETABLE_HTML_LEVEL>=0);
611 	assert(TIMETABLE_HTML_LEVEL<=7);
612 
613 	computeHashForIDsTimetable();
614 	computeActivitiesAtTime();
615 	computeActivitiesWithSameStartingTime();
616 
617 	QString s;
618 	QString bar;
619 	if(INPUT_FILENAME_XML=="")
620 		bar="";
621 	else
622 		bar="_";
623 	QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
624 	if(s2.right(4)==".fet")
625 		s2=s2.left(s2.length()-4);
626 	//else if(INPUT_FILENAME_XML!="")
627 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
628 
629 	//now write the solution in xml files
630 	//subgroups
631 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_FILENAME_XML;
632 	writeSubgroupsTimetableXml(parent, s, subgroupsSortedOrder);
633 	//teachers
634 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_FILENAME_XML;
635 	writeTeachersTimetableXml(parent, s);
636 	//activities
637 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITIES_TIMETABLE_FILENAME_XML;
638 	writeActivitiesTimetableXml(parent, s);
639 
640 	//now get the time. TODO: maybe write it in xml too? so do it a few lines earlier!
641 	QDate dat=QDate::currentDate();
642 	QTime tim=QTime::currentTime();
643 	QLocale loc(FET_LANGUAGE);
644 	QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
645 	generationLocalizedTime=sTime;
646 
647 	//now get the number of placed activities. TODO: maybe write it in xml too? so do it a few lines earlier!
648 	int na=0;
649 	int na2=0;
650 	getNumberOfPlacedActivities(na, na2);
651 
652 	if(na==gt.rules.nInternalActivities && na==na2){
653 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+MULTIPLE_TIMETABLE_DATA_RESULTS_FILE;
654 		if(VERBOSE){
655 			cout<<"Since the simulation is complete, FET will write also the timetable data file"<<endl;
656 		}
657 		writeTimetableDataFile(parent, s);
658 	}
659 
660 	//write the conflicts in txt mode
661 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+CONFLICTS_FILENAME;
662 	writeConflictsTxt(parent, s, sTime, na);
663 
664 	//now write the solution in html files
665 	if(TIMETABLE_HTML_LEVEL>=1){
666 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+STYLESHEET_CSS;
667 		writeStylesheetCss(parent, s, sTime, na);
668 	}
669 
670 	//indexHtml
671 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+INDEX_HTML;
672 	writeIndexHtml(parent, s, sTime, na);
673 
674 	//subgroups
675 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
676 	writeSubgroupsTimetableDaysHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
677 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
678 	writeSubgroupsTimetableDaysVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
679 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
680 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
681 		writeSubgroupsTimetableTimeHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
682 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
683 		writeSubgroupsTimetableTimeVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
684 	} else {
685 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
686 		writeSubgroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
687 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
688 		writeSubgroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
689 	}
690 	//groups
691 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
692 	writeGroupsTimetableDaysHorizontalHtml(parent, s, sTime, na);
693 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
694 	writeGroupsTimetableDaysVerticalHtml(parent, s, sTime, na);
695 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
696 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
697 		writeGroupsTimetableTimeHorizontalHtml(parent, s, sTime, na);
698 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
699 		writeGroupsTimetableTimeVerticalHtml(parent, s, sTime, na);
700 	} else {
701 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
702 		writeGroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
703 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
704 		writeGroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
705 	}
706 	//years
707 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
708 	writeYearsTimetableDaysHorizontalHtml(parent, s, sTime, na);
709 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
710 	writeYearsTimetableDaysVerticalHtml(parent, s, sTime, na);
711 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
712 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
713 		writeYearsTimetableTimeHorizontalHtml(parent, s, sTime, na);
714 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
715 		writeYearsTimetableTimeVerticalHtml(parent, s, sTime, na);
716 	} else {
717 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
718 		writeYearsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
719 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
720 		writeYearsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
721 	}
722 	//teachers
723 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
724 	writeTeachersTimetableDaysHorizontalHtml(parent, s, sTime, na);
725 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
726 	writeTeachersTimetableDaysVerticalHtml(parent, s, sTime, na);
727 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
728 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
729 		writeTeachersTimetableTimeHorizontalHtml(parent, s, sTime, na);
730 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
731 		writeTeachersTimetableTimeVerticalHtml(parent, s, sTime, na);
732 	} else {
733 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
734 		writeTeachersTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
735 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
736 		writeTeachersTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
737 	}
738 	//rooms
739 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
740 	writeRoomsTimetableDaysHorizontalHtml(parent, s, sTime, na);
741 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
742 	writeRoomsTimetableDaysVerticalHtml(parent, s, sTime, na);
743 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
744 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
745 		writeRoomsTimetableTimeHorizontalHtml(parent, s, sTime, na);
746 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
747 		writeRoomsTimetableTimeVerticalHtml(parent, s, sTime, na);
748 	} else {
749 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
750 		writeRoomsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
751 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
752 		writeRoomsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
753 	}
754 	//subjects
755 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
756 	writeSubjectsTimetableDaysHorizontalHtml(parent, s, sTime, na);
757 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
758 	writeSubjectsTimetableDaysVerticalHtml(parent, s, sTime, na);
759 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
760 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
761 		writeSubjectsTimetableTimeHorizontalHtml(parent, s, sTime, na);
762 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
763 		writeSubjectsTimetableTimeVerticalHtml(parent, s, sTime, na);
764 	} else {
765 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
766 		writeSubjectsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
767 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
768 		writeSubjectsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
769 	}
770 	//activity_tags
771 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
772 	writeActivityTagsTimetableDaysHorizontalHtml(parent, s, sTime, na);
773 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
774 	writeActivityTagsTimetableDaysVerticalHtml(parent, s, sTime, na);
775 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
776 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
777 		writeActivityTagsTimetableTimeHorizontalHtml(parent, s, sTime, na);
778 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
779 		writeActivityTagsTimetableTimeVerticalHtml(parent, s, sTime, na);
780 	} else {
781 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
782 		writeActivityTagsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
783 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
784 		writeActivityTagsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
785 	}
786 	//all activities
787 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
788 	writeAllActivitiesTimetableDaysHorizontalHtml(parent, s, sTime, na);
789 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
790 	writeAllActivitiesTimetableDaysVerticalHtml(parent, s, sTime, na);
791 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
792 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
793 		writeAllActivitiesTimetableTimeHorizontalHtml(parent, s, sTime, na);
794 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
795 		writeAllActivitiesTimetableTimeVerticalHtml(parent, s, sTime, na);
796 	} else {
797 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
798 		writeAllActivitiesTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
799 		s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
800 		writeAllActivitiesTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
801 	}
802 	//teachers free periods
803 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
804 	writeTeachersFreePeriodsTimetableDaysHorizontalHtml(parent, s, sTime, na);
805 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
806 	writeTeachersFreePeriodsTimetableDaysVerticalHtml(parent, s, sTime, na);
807 	//statistics
808 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+TEACHERS_STATISTICS_FILENAME_HTML;
809 	writeTeachersStatisticsHtml(parent, s, sTime, na);
810 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+STUDENTS_STATISTICS_FILENAME_HTML;
811 	writeStudentsStatisticsHtml(parent, s, sTime, na);
812 
813 	//needed for printing from the interface, so don't clear them!
814 /*	hashSubjectIDsTimetable.clear();
815 	hashActivityTagIDsTimetable.clear();
816 	hashStudentIDsTimetable.clear();
817 	hashTeacherIDsTimetable.clear();
818 	hashRoomIDsTimetable.clear();
819 	hashDayIDsTimetable.clear();
820 	hashActivityColorBySubject.clear();
821 	hashActivityColorBySubjectAndStudents.clear();
822 	activeHashActivityColorBySubject.clear();
823 	activeHashActivityColorBySubjectAndStudents.clear();
824 */
825 	if(VERBOSE){
826 		cout<<"Writing highest stage results to disk completed successfully"<<endl;
827 	}
828 }
829 
writeRandomSeed(QWidget * parent,const MRG32k3a & rng,bool before)830 void TimetableExport::writeRandomSeed(QWidget* parent, const MRG32k3a& rng, bool before)
831 {
832 	QString RANDOM_SEED_FILENAME;
833 	if(before)
834 		RANDOM_SEED_FILENAME=RANDOM_SEED_FILENAME_BEFORE;
835 	else
836 		RANDOM_SEED_FILENAME=RANDOM_SEED_FILENAME_AFTER;
837 
838 	QDir dir;
839 
840 	QString OUTPUT_DIR_TIMETABLES=OUTPUT_DIR+FILE_SEP+"timetables";
841 
842 	OUTPUT_DIR_TIMETABLES.append(FILE_SEP);
843 	if(INPUT_FILENAME_XML=="")
844 		OUTPUT_DIR_TIMETABLES.append("unnamed");
845 	else{
846 		OUTPUT_DIR_TIMETABLES.append(INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1));
847 		if(OUTPUT_DIR_TIMETABLES.right(4)==".fet")
848 			OUTPUT_DIR_TIMETABLES=OUTPUT_DIR_TIMETABLES.left(OUTPUT_DIR_TIMETABLES.length()-4);
849 		//else if(INPUT_FILENAME_XML!="")
850 		//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
851 	}
852 	OUTPUT_DIR_TIMETABLES.append("-single");
853 
854 	//make sure that the output directory exists
855 	if(!dir.exists(OUTPUT_DIR_TIMETABLES))
856 		dir.mkpath(OUTPUT_DIR_TIMETABLES);
857 
858 	QString s;
859 	QString bar;
860 	if(INPUT_FILENAME_XML=="")
861 		bar="";
862 	else
863 		bar="_";
864 	QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
865 	if(s2.right(4)==".fet")
866 		s2=s2.left(s2.length()-4);
867 
868 	s=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+bar+RANDOM_SEED_FILENAME;
869 
870 	writeRandomSeedFile(parent, rng, s, before);
871 }
872 
writeRandomSeedFile(QWidget * parent,const MRG32k3a & rng,const QString & filename,bool before)873 void TimetableExport::writeRandomSeedFile(QWidget* parent, const MRG32k3a& rng, const QString& filename, bool before)
874 {
875 	QString s=filename;
876 
877 	QFile file(s);
878 	if(!file.open(QIODevice::WriteOnly)){
879 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
880 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(s));
881 		return;
882 	}
883 	QTextStream tos(&file);
884 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
885 	tos.setEncoding(QStringConverter::Utf8);
886 #else
887 	tos.setCodec("UTF-8");
888 #endif
889 	tos.setGenerateByteOrderMark(true);
890 
891 	QDate dat=QDate::currentDate();
892 	QTime tim=QTime::currentTime();
893 	QLocale loc(FET_LANGUAGE);
894 	QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
895 
896 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
897 	if(before){
898 		tos<<tr("Generation started on: %1", "%1 is the time").arg(sTime);
899 		tos<<Qt::endl<<Qt::endl;
900 		tos<<tr("The random seed at the start of generation is:", "The random seed has 6 components, to follow on the next 2 lines (3 values on each line).");
901 		tos<<Qt::endl;
902 		tos<<QString("\ts10=%1, s11=%2, s12=%3,").arg(rng.s10).arg(rng.s11).arg(rng.s12);
903 		tos<<Qt::endl;
904 		tos<<QString("\ts20=%1, s21=%2, s22=%3.").arg(rng.s20).arg(rng.s21).arg(rng.s22);
905 		tos<<Qt::endl<<Qt::endl;
906 		tos<<tr("This file was automatically generated by FET %1.").arg(FET_VERSION);
907 		tos<<Qt::endl;
908 	}
909 	else{
910 		tos<<tr("Generation ended on: %1", "%1 is the time").arg(sTime);
911 		tos<<Qt::endl<<Qt::endl;
912 		tos<<tr("The random seed at the end of generation is:", "The random seed has 6 components, to follow on the next 2 lines (3 values on each line).");
913 		tos<<Qt::endl;
914 		tos<<QString("\ts10=%1, s11=%2, s12=%3,").arg(rng.s10).arg(rng.s11).arg(rng.s12);
915 		tos<<Qt::endl;
916 		tos<<QString("\ts20=%1, s21=%2, s22=%3.").arg(rng.s20).arg(rng.s21).arg(rng.s22);
917 		tos<<Qt::endl<<Qt::endl;
918 		tos<<tr("This file was automatically generated by FET %1.").arg(FET_VERSION);
919 		tos<<Qt::endl;
920 	}
921 #else
922 	if(before){
923 		tos<<tr("Generation started on: %1", "%1 is the time").arg(sTime);
924 		tos<<endl<<endl;
925 		tos<<tr("The random seed at the start of generation is:", "The random seed has 6 components, to follow on the next 2 lines (3 values on each line).");
926 		tos<<endl;
927 		tos<<QString("\ts10=%1, s11=%2, s12=%3,").arg(rng.s10).arg(rng.s11).arg(rng.s12);
928 		tos<<endl;
929 		tos<<QString("\ts20=%1, s21=%2, s22=%3.").arg(rng.s20).arg(rng.s21).arg(rng.s22);
930 		tos<<endl<<endl;
931 		tos<<tr("This file was automatically generated by FET %1.").arg(FET_VERSION);
932 		tos<<endl;
933 	}
934 	else{
935 		tos<<tr("Generation ended on: %1", "%1 is the time").arg(sTime);
936 		tos<<endl<<endl;
937 		tos<<tr("The random seed at the end of generation is:", "The random seed has 6 components, to follow on the next 2 lines (3 values on each line).");
938 		tos<<endl;
939 		tos<<QString("\ts10=%1, s11=%2, s12=%3,").arg(rng.s10).arg(rng.s11).arg(rng.s12);
940 		tos<<endl;
941 		tos<<QString("\ts20=%1, s21=%2, s22=%3.").arg(rng.s20).arg(rng.s21).arg(rng.s22);
942 		tos<<endl<<endl;
943 		tos<<tr("This file was automatically generated by FET %1.").arg(FET_VERSION);
944 		tos<<endl;
945 	}
946 #endif
947 
948 	if(file.error()>0){
949 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
950 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(s).arg(file.error()));
951 	}
952 	file.close();
953 }
954 
writeTimetableDataFile(QWidget * parent,const QString & filename)955 void TimetableExport::writeTimetableDataFile(QWidget* parent, const QString& filename){
956 	if(!students_schedule_ready || !teachers_schedule_ready || !rooms_schedule_ready){
957 		IrreconcilableCriticalMessage::critical(parent, tr("FET - Critical"), tr("Timetable not generated - cannot save it - this should not happen (please report bug)"));
958 		return;
959 	}
960 
961 	Solution* tc=&best_solution;
962 
963 	for(int ai=0; ai<gt.rules.nInternalActivities; ai++){
964 		//Activity* act=&gt.rules.internalActivitiesList[ai];
965 		int time=tc->times[ai];
966 		if(time==UNALLOCATED_TIME){
967 			IrreconcilableCriticalMessage::critical(parent, tr("FET - Critical"), tr("Incomplete timetable - this should not happen - please report bug"));
968 			return;
969 		}
970 
971 		int ri=tc->rooms[ai];
972 		if(ri==UNALLOCATED_SPACE){
973 			IrreconcilableCriticalMessage::critical(parent, tr("FET - Critical"), tr("Incomplete timetable - this should not happen - please report bug"));
974 			return;
975 		}
976 	}
977 
978 	rules2.initialized=true;
979 
980 	rules2.mode=gt.rules.mode;
981 
982 	rules2.institutionName=gt.rules.institutionName;
983 	rules2.comments=gt.rules.comments;
984 
985 	rules2.nTerms=gt.rules.nTerms;
986 	rules2.nDaysPerTerm=gt.rules.nDaysPerTerm;
987 
988 	rules2.nHoursPerDay=gt.rules.nHoursPerDay;
989 	rules2.hoursOfTheDay=gt.rules.hoursOfTheDay;
990 	//for(int i=0; i<gt.rules.nHoursPerDay; i++)
991 	//	rules2.hoursOfTheDay[i]=gt.rules.hoursOfTheDay[i];
992 
993 	rules2.nDaysPerWeek=gt.rules.nDaysPerWeek;
994 	rules2.daysOfTheWeek=gt.rules.daysOfTheWeek;
995 	//for(int i=0; i<gt.rules.nDaysPerWeek; i++)
996 	//	rules2.daysOfTheWeek[i]=gt.rules.daysOfTheWeek[i];
997 
998 	rules2.yearsList=gt.rules.yearsList;
999 
1000 	rules2.teachersList=gt.rules.teachersList;
1001 
1002 	rules2.subjectsList=gt.rules.subjectsList;
1003 
1004 	rules2.activityTagsList=gt.rules.activityTagsList;
1005 
1006 	rules2.activitiesList=gt.rules.activitiesList;
1007 
1008 	rules2.buildingsList=gt.rules.buildingsList;
1009 
1010 	rules2.roomsList=gt.rules.roomsList;
1011 
1012 	rules2.timeConstraintsList=gt.rules.timeConstraintsList;
1013 
1014 	rules2.spaceConstraintsList=gt.rules.spaceConstraintsList;
1015 
1016 	rules2.apstHash=gt.rules.apstHash;
1017 	rules2.aprHash=gt.rules.aprHash;
1018 
1019 	rules2.groupActivitiesInInitialOrderList=gt.rules.groupActivitiesInInitialOrderList;
1020 
1021 	//add locking constraints
1022 	TimeConstraintsList lockTimeConstraintsList;
1023 	SpaceConstraintsList lockSpaceConstraintsList;
1024 
1025 	//bool report=false;
1026 
1027 	int addedTime=0, duplicatesTime=0;
1028 	int addedSpace=0, duplicatesSpace=0;
1029 
1030 	//lock selected activities
1031 	for(int ai=0; ai<gt.rules.nInternalActivities; ai++){
1032 		Activity* act=&gt.rules.internalActivitiesList[ai];
1033 		int time=tc->times[ai];
1034 		if(time>=0 && time<gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay){
1035 			int hour=time/gt.rules.nDaysPerWeek;
1036 			int day=time%gt.rules.nDaysPerWeek;
1037 
1038 			ConstraintActivityPreferredStartingTime* ctr=new ConstraintActivityPreferredStartingTime(100.0, act->id, day, hour, false); //permanently locked is false
1039 			bool t=rules2.addTimeConstraint(ctr);
1040 
1041 			if(t){
1042 				addedTime++;
1043 				lockTimeConstraintsList.append(ctr);
1044 			}
1045 			else
1046 				duplicatesTime++;
1047 
1048 			QString s;
1049 
1050 			if(t)
1051 				s=tr("Added the following constraint to saved file:")+"\n"+ctr->getDetailedDescription(gt.rules);
1052 			else{
1053 				s=tr("Constraint\n%1 NOT added to saved file - duplicate").arg(ctr->getDetailedDescription(gt.rules));
1054 				delete ctr;
1055 			}
1056 
1057 			/*if(report){
1058 				int k;
1059 				if(t)
1060 					k=TimetableExportMessage::information(parent, tr("FET information"), s,
1061 				 	 tr("Skip information"), tr("See next"), QString(), 1, 0 );
1062 				else
1063 					k=TimetableExportMessage::warning(parent, tr("FET warning"), s,
1064 				 	 tr("Skip information"), tr("See next"), QString(), 1, 0 );
1065 		 		if(k==0)
1066 					report=false;
1067 			}*/
1068 		}
1069 
1070 		int ri=tc->rooms[ai];
1071 		if(ri!=UNALLOCATED_SPACE && ri!=UNSPECIFIED_ROOM && ri>=0 && ri<gt.rules.nInternalRooms){
1072 			QStringList tl;
1073 			if(gt.rules.internalRoomsList[ri]->isVirtual==false)
1074 				assert(tc->realRoomsList[ai].isEmpty());
1075 			else
1076 				for(int rr : qAsConst(tc->realRoomsList[ai]))
1077 					tl.append(gt.rules.internalRoomsList[rr]->name);
1078 
1079 			ConstraintActivityPreferredRoom* ctr=new ConstraintActivityPreferredRoom(100, act->id, (gt.rules.internalRoomsList[ri])->name, tl, false); //permanently locked is false
1080 			bool t=rules2.addSpaceConstraint(ctr);
1081 
1082 			QString s;
1083 
1084 			if(t){
1085 				addedSpace++;
1086 				lockSpaceConstraintsList.append(ctr);
1087 			}
1088 			else
1089 				duplicatesSpace++;
1090 
1091 			if(t)
1092 				s=tr("Added the following constraint to saved file:")+"\n"+ctr->getDetailedDescription(gt.rules);
1093 			else{
1094 				s=tr("Constraint\n%1 NOT added to saved file - duplicate").arg(ctr->getDetailedDescription(gt.rules));
1095 				delete ctr;
1096 			}
1097 
1098 			/*if(report){
1099 				int k;
1100 				if(t)
1101 					k=TimetableExportMessage::information(parent, tr("FET information"), s,
1102 				 	 tr("Skip information"), tr("See next"), QString(), 1, 0 );
1103 				else
1104 					k=TimetableExportMessage::warning(parent, tr("FET warning"), s,
1105 					 tr("Skip information"), tr("See next"), QString(), 1, 0 );
1106 				if(k==0)
1107 					report=false;
1108 			}*/
1109 		}
1110 	}
1111 
1112 	//QMessageBox::information(parent, tr("FET information"), tr("Added %1 locking time constraints and %2 locking space constraints to saved file,"
1113 	// " ignored %3 activities which were already fixed in time and %4 activities which were already fixed in space").arg(addedTime).arg(addedSpace).arg(duplicatesTime).arg(duplicatesSpace));
1114 
1115 	bool result=rules2.write(parent, filename);
1116 
1117 	for(TimeConstraint* tc : qAsConst(lockTimeConstraintsList))
1118 		delete tc;
1119 	lockTimeConstraintsList.clear();
1120 	for(SpaceConstraint* sc : qAsConst(lockSpaceConstraintsList))
1121 		delete sc;
1122 	lockSpaceConstraintsList.clear();
1123 	//while(!lockTimeConstraintsList.isEmpty())
1124 	//	delete lockTimeConstraintsList.takeFirst();
1125 	//while(!lockSpaceConstraintsList.isEmpty())
1126 	//	delete lockSpaceConstraintsList.takeFirst();
1127 
1128 	//if(result)
1129 	//	QMessageBox::information(parent, tr("FET information"),
1130 	//		tr("File saved successfully. You can see it on the hard disk. Current data file remained untouched (of locking constraints),"
1131 	//		" so you can save it also, or generate different timetables."));
1132 
1133 	rules2.nHoursPerDay=0;
1134 	rules2.hoursOfTheDay.clear();
1135 	rules2.nDaysPerWeek=0;
1136 	rules2.daysOfTheWeek.clear();
1137 
1138 	rules2.yearsList.clear();
1139 
1140 	rules2.teachersList.clear();
1141 
1142 	rules2.subjectsList.clear();
1143 
1144 	rules2.activityTagsList.clear();
1145 
1146 	rules2.activitiesList.clear();
1147 
1148 	rules2.buildingsList.clear();
1149 
1150 	rules2.roomsList.clear();
1151 
1152 	rules2.timeConstraintsList.clear();
1153 
1154 	rules2.spaceConstraintsList.clear();
1155 
1156 	rules2.apstHash.clear();
1157 	rules2.aprHash.clear();
1158 
1159 	rules2.groupActivitiesInInitialOrderList.clear();
1160 
1161 	if(!result){
1162 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"), tr("Could not save the data + timetable file on the hard disk - maybe hard disk is full"));
1163 	}
1164 }
1165 
writeSimulationResults(QWidget * parent,int n,bool highest)1166 void TimetableExport::writeSimulationResults(QWidget* parent, int n, bool highest){
1167 	QList<int> subgroupsSortedOrder;
1168 	QList<StudentsSubgroup*> lst;
1169 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
1170 		lst.append(gt.rules.internalSubgroupsList[subgroup]);
1171 	if(TIMETABLES_SUBGROUPS_SORTED)
1172 		std::stable_sort(lst.begin(), lst.end(), subgroupsAscending);
1173 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
1174 		subgroupsSortedOrder.append(lst.at(subgroup)->indexInInternalSubgroupsList);
1175 	QDir dir;
1176 
1177 	QString OUTPUT_DIR_TIMETABLES=OUTPUT_DIR+FILE_SEP+"timetables";
1178 
1179 	//make sure that the output directory exists
1180 	if(!dir.exists(OUTPUT_DIR_TIMETABLES))
1181 		dir.mkpath(OUTPUT_DIR_TIMETABLES);
1182 
1183 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
1184 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
1185 	assert(TIMETABLE_HTML_LEVEL>=0);
1186 	assert(TIMETABLE_HTML_LEVEL<=7);
1187 
1188 	computeHashForIDsTimetable();
1189 	computeActivitiesAtTime();
1190 	computeActivitiesWithSameStartingTime();
1191 
1192 	QString s;
1193 	QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1194 	if(s2.right(4)==".fet")
1195 		s2=s2.left(s2.length()-4);
1196 	//else if(INPUT_FILENAME_XML!="")
1197 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1198 
1199 	QString destDir=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+"-multi";
1200 
1201 	if(!dir.exists(destDir))
1202 		dir.mkpath(destDir);
1203 
1204 	QString finalDestDir=destDir+FILE_SEP+CustomFETString::number(n);
1205 
1206 	if(highest)
1207 		finalDestDir+=QString("-highest");
1208 
1209 	if(!dir.exists(finalDestDir))
1210 		dir.mkpath(finalDestDir);
1211 
1212 	finalDestDir+=FILE_SEP;
1213 
1214 	QString s3=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1215 
1216 	if(s3.right(4)==".fet")
1217 		s3=s3.left(s3.length()-4);
1218 	//else if(INPUT_FILENAME_XML!="")
1219 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1220 
1221 	finalDestDir+=s3+"_";
1222 
1223 	//write data+timetable in .fet format
1224 	if(!highest)
1225 		writeTimetableDataFile(parent, finalDestDir+MULTIPLE_TIMETABLE_DATA_RESULTS_FILE);
1226 
1227 	//now write the solution in xml files
1228 	//subgroups
1229 	s=finalDestDir+SUBGROUPS_TIMETABLE_FILENAME_XML;
1230 	writeSubgroupsTimetableXml(parent, s, subgroupsSortedOrder);
1231 	//teachers
1232 	s=finalDestDir+TEACHERS_TIMETABLE_FILENAME_XML;
1233 	writeTeachersTimetableXml(parent, s);
1234 	//activities
1235 	s=finalDestDir+ACTIVITIES_TIMETABLE_FILENAME_XML;
1236 	writeActivitiesTimetableXml(parent, s);
1237 
1238 	//now get the time. TODO: maybe write it in xml too? so do it a few lines earlier!
1239 	QDate dat=QDate::currentDate();
1240 	QTime tim=QTime::currentTime();
1241 	QLocale loc(FET_LANGUAGE);
1242 	QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
1243 	generationLocalizedTime=sTime;
1244 
1245 	//now get the number of placed activities. TODO: maybe write it in xml too? so do it a few lines earlier!
1246 	int na=0;
1247 	int na2=0;
1248 	getNumberOfPlacedActivities(na, na2);
1249 
1250 	//write the conflicts in txt mode
1251 	s=finalDestDir+CONFLICTS_FILENAME;
1252 	writeConflictsTxt(parent, s, sTime, na);
1253 
1254 	//now write the solution in html files
1255 	if(TIMETABLE_HTML_LEVEL>=1){
1256 		s=finalDestDir+STYLESHEET_CSS;
1257 		writeStylesheetCss(parent, s, sTime, na);
1258 	}
1259 	//indexHtml
1260 	s=finalDestDir+INDEX_HTML;
1261 	writeIndexHtml(parent, s, sTime, na);
1262 	//subgroups
1263 	s=finalDestDir+SUBGROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1264 	writeSubgroupsTimetableDaysHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1265 	s=finalDestDir+SUBGROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1266 	writeSubgroupsTimetableDaysVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1267 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1268 		s=finalDestDir+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1269 		writeSubgroupsTimetableTimeHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1270 		s=finalDestDir+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1271 		writeSubgroupsTimetableTimeVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1272 	} else {
1273 		s=finalDestDir+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1274 		writeSubgroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
1275 		s=finalDestDir+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1276 		writeSubgroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
1277 	}
1278 	//groups
1279 	s=finalDestDir+GROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1280 	writeGroupsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1281 	s=finalDestDir+GROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1282 	writeGroupsTimetableDaysVerticalHtml(parent, s, sTime, na);
1283 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1284 		s=finalDestDir+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1285 		writeGroupsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1286 		s=finalDestDir+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1287 		writeGroupsTimetableTimeVerticalHtml(parent, s, sTime, na);
1288 	} else {
1289 		s=finalDestDir+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1290 		writeGroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1291 		s=finalDestDir+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1292 		writeGroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1293 	}
1294 	//years
1295 	s=finalDestDir+YEARS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1296 	writeYearsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1297 	s=finalDestDir+YEARS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1298 	writeYearsTimetableDaysVerticalHtml(parent, s, sTime, na);
1299 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1300 		s=finalDestDir+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1301 		writeYearsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1302 		s=finalDestDir+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1303 		writeYearsTimetableTimeVerticalHtml(parent, s, sTime, na);
1304 	} else {
1305 		s=finalDestDir+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1306 		writeYearsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1307 		s=finalDestDir+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1308 		writeYearsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1309 	}
1310 	//teachers
1311 	s=finalDestDir+TEACHERS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1312 	writeTeachersTimetableDaysHorizontalHtml(parent, s, sTime, na);
1313 	s=finalDestDir+TEACHERS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1314 	writeTeachersTimetableDaysVerticalHtml(parent, s, sTime, na);
1315 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1316 		s=finalDestDir+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1317 		writeTeachersTimetableTimeHorizontalHtml(parent, s, sTime, na);
1318 		s=finalDestDir+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1319 		writeTeachersTimetableTimeVerticalHtml(parent, s, sTime, na);
1320 	} else {
1321 		s=finalDestDir+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1322 		writeTeachersTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1323 		s=finalDestDir+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1324 		writeTeachersTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1325 	}
1326 	//rooms
1327 	s=finalDestDir+ROOMS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1328 	writeRoomsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1329 	s=finalDestDir+ROOMS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1330 	writeRoomsTimetableDaysVerticalHtml(parent, s, sTime, na);
1331 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1332 		s=finalDestDir+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1333 		writeRoomsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1334 		s=finalDestDir+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1335 		writeRoomsTimetableTimeVerticalHtml(parent, s, sTime, na);
1336 	} else {
1337 		s=finalDestDir+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1338 		writeRoomsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1339 		s=finalDestDir+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1340 		writeRoomsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1341 	}
1342 	//subjects
1343 	s=finalDestDir+SUBJECTS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1344 	writeSubjectsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1345 	s=finalDestDir+SUBJECTS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1346 	writeSubjectsTimetableDaysVerticalHtml(parent, s, sTime, na);
1347 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1348 		s=finalDestDir+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1349 		writeSubjectsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1350 		s=finalDestDir+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1351 		writeSubjectsTimetableTimeVerticalHtml(parent, s, sTime, na);
1352 	} else {
1353 		s=finalDestDir+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1354 		writeSubjectsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1355 		s=finalDestDir+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1356 		writeSubjectsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1357 	}
1358 	//activity_tags
1359 	s=finalDestDir+ACTIVITY_TAGS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1360 	writeActivityTagsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1361 	s=finalDestDir+ACTIVITY_TAGS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1362 	writeActivityTagsTimetableDaysVerticalHtml(parent, s, sTime, na);
1363 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1364 		s=finalDestDir+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1365 		writeActivityTagsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1366 		s=finalDestDir+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1367 		writeActivityTagsTimetableTimeVerticalHtml(parent, s, sTime, na);
1368 	} else {
1369 		s=finalDestDir+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1370 		writeActivityTagsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1371 		s=finalDestDir+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1372 		writeActivityTagsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1373 	}
1374 	//all activities
1375 	s=finalDestDir+ALL_ACTIVITIES_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1376 	writeAllActivitiesTimetableDaysHorizontalHtml(parent, s, sTime, na);
1377 	s=finalDestDir+ALL_ACTIVITIES_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1378 	writeAllActivitiesTimetableDaysVerticalHtml(parent, s, sTime, na);
1379 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1380 		s=finalDestDir+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1381 		writeAllActivitiesTimetableTimeHorizontalHtml(parent, s, sTime, na);
1382 		s=finalDestDir+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1383 		writeAllActivitiesTimetableTimeVerticalHtml(parent, s, sTime, na);
1384 	} else {
1385 		s=finalDestDir+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1386 		writeAllActivitiesTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1387 		s=finalDestDir+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1388 		writeAllActivitiesTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1389 	}
1390 	//teachers free periods
1391 	s=finalDestDir+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1392 	writeTeachersFreePeriodsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1393 	s=finalDestDir+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1394 	writeTeachersFreePeriodsTimetableDaysVerticalHtml(parent, s, sTime, na);
1395 	//statistics
1396 	s=finalDestDir+TEACHERS_STATISTICS_FILENAME_HTML;
1397 	writeTeachersStatisticsHtml(parent, s, sTime, na);
1398 	s=finalDestDir+STUDENTS_STATISTICS_FILENAME_HTML;
1399 	writeStudentsStatisticsHtml(parent, s, sTime, na);
1400 
1401 	//needed for printing from the interface, so don't clear them!
1402 /*	hashSubjectIDsTimetable.clear();
1403 	hashActivityTagIDsTimetable.clear();
1404 	hashStudentIDsTimetable.clear();
1405 	hashTeacherIDsTimetable.clear();
1406 	hashRoomIDsTimetable.clear();
1407 	hashDayIDsTimetable.clear();
1408 	hashActivityColorBySubject.clear();
1409 	hashActivityColorBySubjectAndStudents.clear();
1410 	activeHashActivityColorBySubject.clear();
1411 	activeHashActivityColorBySubjectAndStudents.clear();
1412 */
1413 	if(VERBOSE){
1414 		cout<<"Writing multiple simulation results to disk completed successfully"<<endl;
1415 	}
1416 }
1417 
writeRandomSeed(QWidget * parent,const MRG32k3a & rng,int n,bool before)1418 void TimetableExport::writeRandomSeed(QWidget* parent, const MRG32k3a& rng, int n, bool before){
1419 	QString RANDOM_SEED_FILENAME;
1420 	if(before)
1421 		RANDOM_SEED_FILENAME=RANDOM_SEED_FILENAME_BEFORE;
1422 	else
1423 		RANDOM_SEED_FILENAME=RANDOM_SEED_FILENAME_AFTER;
1424 
1425 	QDir dir;
1426 
1427 	QString OUTPUT_DIR_TIMETABLES=OUTPUT_DIR+FILE_SEP+"timetables";
1428 
1429 	//make sure that the output directory exists
1430 	if(!dir.exists(OUTPUT_DIR_TIMETABLES))
1431 		dir.mkpath(OUTPUT_DIR_TIMETABLES);
1432 
1433 	QString s;
1434 	QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1435 	if(s2.right(4)==".fet")
1436 		s2=s2.left(s2.length()-4);
1437 	//else if(INPUT_FILENAME_XML!="")
1438 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1439 
1440 	QString destDir=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+"-multi";
1441 
1442 	if(!dir.exists(destDir))
1443 		dir.mkpath(destDir);
1444 
1445 	QString finalDestDir=destDir+FILE_SEP+CustomFETString::number(n);
1446 
1447 	if(!dir.exists(finalDestDir))
1448 		dir.mkpath(finalDestDir);
1449 
1450 	finalDestDir+=FILE_SEP;
1451 
1452 	QString s3=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1453 
1454 	if(s3.right(4)==".fet")
1455 		s3=s3.left(s3.length()-4);
1456 	//else if(INPUT_FILENAME_XML!="")
1457 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1458 
1459 	finalDestDir+=s3+"_";
1460 
1461 	s=finalDestDir+RANDOM_SEED_FILENAME;
1462 
1463 	writeRandomSeedFile(parent, rng, s, before);
1464 }
1465 
writeReportForMultiple(QWidget * parent,const QString & description,bool begin)1466 void TimetableExport::writeReportForMultiple(QWidget* parent, const QString& description, bool begin)
1467 {
1468 	QDir dir;
1469 
1470 	QString OUTPUT_DIR_TIMETABLES=OUTPUT_DIR+FILE_SEP+"timetables";
1471 
1472 	//make sure that the output directory exists
1473 	if(!dir.exists(OUTPUT_DIR_TIMETABLES))
1474 		dir.mkpath(OUTPUT_DIR_TIMETABLES);
1475 
1476 	QString s;
1477 	QString s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1478 	if(s2.right(4)==".fet")
1479 		s2=s2.left(s2.length()-4);
1480 	//else if(INPUT_FILENAME_XML!="")
1481 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1482 
1483 	QString destDir=OUTPUT_DIR_TIMETABLES+FILE_SEP+s2+"-multi";
1484 
1485 	if(!dir.exists(destDir))
1486 		dir.mkpath(destDir);
1487 
1488 	QString filename=destDir+FILE_SEP+QString("report.txt");
1489 
1490 	QFile file(filename);
1491 	if(!file.open(QIODevice::Append)){
1492 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1493 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(filename));
1494 		return;
1495 	}
1496 	QTextStream tos(&file);
1497 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
1498 	tos.setEncoding(QStringConverter::Utf8);
1499 #else
1500 	tos.setCodec("UTF-8");
1501 #endif
1502 	if(begin){
1503 		tos.setGenerateByteOrderMark(true);
1504 	}
1505 
1506 #if QT_VERSION >= QT_VERSION_CHECK(5,15,0)
1507 	tos<<description<<Qt::endl;
1508 #else
1509 	tos<<description<<endl;
1510 #endif
1511 
1512 	if(file.error()>0){
1513 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1514 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(filename).arg(file.error()));
1515 	}
1516 	file.close();
1517 }
1518 
writeSimulationResultsCommandLine(QWidget * parent,const QString & outputDirectory)1519 void TimetableExport::writeSimulationResultsCommandLine(QWidget* parent, const QString& outputDirectory){ //outputDirectory contains trailing FILE_SEP
1520 	QList<int> subgroupsSortedOrder;
1521 	QList<StudentsSubgroup*> lst;
1522 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
1523 		lst.append(gt.rules.internalSubgroupsList[subgroup]);
1524 	if(TIMETABLES_SUBGROUPS_SORTED)
1525 		std::stable_sort(lst.begin(), lst.end(), subgroupsAscending);
1526 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++)
1527 		subgroupsSortedOrder.append(lst.at(subgroup)->indexInInternalSubgroupsList);
1528 	QString add=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1529 	if(add.right(4)==".fet")
1530 		add=add.left(add.length()-4);
1531 	//else if(INPUT_FILENAME_XML!="")
1532 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1533 
1534 	if(add!="")
1535 		add.append("_");
1536 
1537 	/////////
1538 
1539 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
1540 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
1541 	assert(TIMETABLE_HTML_LEVEL>=0);
1542 	assert(TIMETABLE_HTML_LEVEL<=7);
1543 
1544 	computeHashForIDsTimetable();
1545 	computeActivitiesAtTime();
1546 	computeActivitiesWithSameStartingTime();
1547 
1548 	TimetableExport::writeSubgroupsTimetableXml(parent, outputDirectory+add+SUBGROUPS_TIMETABLE_FILENAME_XML, subgroupsSortedOrder);
1549 	TimetableExport::writeTeachersTimetableXml(parent, outputDirectory+add+TEACHERS_TIMETABLE_FILENAME_XML);
1550 	TimetableExport::writeActivitiesTimetableXml(parent, outputDirectory+add+ACTIVITIES_TIMETABLE_FILENAME_XML);
1551 
1552 	//get the time
1553 	QDate dat=QDate::currentDate();
1554 	QTime tim=QTime::currentTime();
1555 	QLocale loc(FET_LANGUAGE);
1556 	QString sTime=loc.toString(dat, QLocale::ShortFormat)+" "+loc.toString(tim, QLocale::ShortFormat);
1557 	generationLocalizedTime=sTime; //really unneeded, but just to be similar to the other parts
1558 
1559 	//now get the number of placed activities. TODO: maybe write it in xml too? so do it a few lines earlier!
1560 	int na=0;
1561 	int na2=0;
1562 	getNumberOfPlacedActivities(na, na2);
1563 
1564 	if(na==gt.rules.nInternalActivities && na==na2){
1565 		QString s=outputDirectory+add+MULTIPLE_TIMETABLE_DATA_RESULTS_FILE;
1566 		if(VERBOSE){
1567 			cout<<"Since the simulation is complete, FET will write also the timetable data file"<<endl;
1568 		}
1569 		writeTimetableDataFile(parent, s);
1570 	}
1571 
1572 	//write the conflicts in txt mode
1573 	QString s=add+CONFLICTS_FILENAME;
1574 	s.prepend(outputDirectory);
1575 	TimetableExport::writeConflictsTxt(parent, s, sTime, na);
1576 
1577 	//now write the solution in html files
1578 	if(TIMETABLE_HTML_LEVEL>=1){
1579 		s=add+STYLESHEET_CSS;
1580 		s.prepend(outputDirectory);
1581 		TimetableExport::writeStylesheetCss(parent, s, sTime, na);
1582 	}
1583 	//indexHtml
1584 	s=add+INDEX_HTML;
1585 	s.prepend(outputDirectory);
1586 	writeIndexHtml(parent, s, sTime, na);
1587 	//subgroups
1588 	s=add+SUBGROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1589 	s.prepend(outputDirectory);
1590 	TimetableExport::writeSubgroupsTimetableDaysHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1591 	s=add+SUBGROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1592 	s.prepend(outputDirectory);
1593 	TimetableExport::writeSubgroupsTimetableDaysVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1594 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1595 		s=add+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1596 		s.prepend(outputDirectory);
1597 		TimetableExport::writeSubgroupsTimetableTimeHorizontalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1598 		s=add+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1599 		s.prepend(outputDirectory);
1600 		TimetableExport::writeSubgroupsTimetableTimeVerticalHtml(parent, s, sTime, na, subgroupsSortedOrder);
1601 	} else {
1602 		s=add+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1603 		s.prepend(outputDirectory);
1604 		TimetableExport::writeSubgroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
1605 		s=add+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1606 		s.prepend(outputDirectory);
1607 		TimetableExport::writeSubgroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na, subgroupsSortedOrder);
1608 	}
1609 	//groups
1610 	s=add+GROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1611 	s.prepend(outputDirectory);
1612 	TimetableExport::writeGroupsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1613 	s=add+GROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1614 	s.prepend(outputDirectory);
1615 	TimetableExport::writeGroupsTimetableDaysVerticalHtml(parent, s, sTime, na);
1616 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1617 		s=add+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1618 		s.prepend(outputDirectory);
1619 		TimetableExport::writeGroupsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1620 		s=add+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1621 		s.prepend(outputDirectory);
1622 		TimetableExport::writeGroupsTimetableTimeVerticalHtml(parent, s, sTime, na);
1623 	} else {
1624 		s=add+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1625 		s.prepend(outputDirectory);
1626 		TimetableExport::writeGroupsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1627 		s=add+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1628 		s.prepend(outputDirectory);
1629 		TimetableExport::writeGroupsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1630 	}
1631 	//years
1632 	s=add+YEARS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1633 	s.prepend(outputDirectory);
1634 	TimetableExport::writeYearsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1635 	s=add+YEARS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1636 	s.prepend(outputDirectory);
1637 	TimetableExport::writeYearsTimetableDaysVerticalHtml(parent, s, sTime, na);
1638 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1639 		s=add+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1640 		s.prepend(outputDirectory);
1641 		TimetableExport::writeYearsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1642 		s=add+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1643 		s.prepend(outputDirectory);
1644 		TimetableExport::writeYearsTimetableTimeVerticalHtml(parent, s, sTime, na);
1645 	} else {
1646 		s=add+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1647 		s.prepend(outputDirectory);
1648 		TimetableExport::writeYearsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1649 		s=add+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1650 		s.prepend(outputDirectory);
1651 		TimetableExport::writeYearsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1652 	}
1653 	//teachers
1654 	s=add+TEACHERS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1655 	s.prepend(outputDirectory);
1656 	TimetableExport::writeTeachersTimetableDaysHorizontalHtml(parent, s, sTime, na);
1657 	s=add+TEACHERS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1658 	s.prepend(outputDirectory);
1659 	TimetableExport::writeTeachersTimetableDaysVerticalHtml(parent, s, sTime, na);
1660 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1661 		s=add+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1662 		s.prepend(outputDirectory);
1663 		TimetableExport::writeTeachersTimetableTimeHorizontalHtml(parent, s, sTime, na);
1664 		s=add+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1665 		s.prepend(outputDirectory);
1666 		TimetableExport::writeTeachersTimetableTimeVerticalHtml(parent, s, sTime, na);
1667 	} else {
1668 		s=add+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1669 		s.prepend(outputDirectory);
1670 		TimetableExport::writeTeachersTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1671 		s=add+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1672 		s.prepend(outputDirectory);
1673 		TimetableExport::writeTeachersTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1674 	}
1675 	//rooms
1676 	s=add+ROOMS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1677 	s.prepend(outputDirectory);
1678 	TimetableExport::writeRoomsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1679 	s=add+ROOMS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1680 	s.prepend(outputDirectory);
1681 	TimetableExport::writeRoomsTimetableDaysVerticalHtml(parent, s, sTime, na);
1682 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1683 		s=add+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1684 		s.prepend(outputDirectory);
1685 		TimetableExport::writeRoomsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1686 		s=add+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1687 		s.prepend(outputDirectory);
1688 		TimetableExport::writeRoomsTimetableTimeVerticalHtml(parent, s, sTime, na);
1689 	} else {
1690 		s=add+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1691 		s.prepend(outputDirectory);
1692 		TimetableExport::writeRoomsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1693 		s=add+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1694 		s.prepend(outputDirectory);
1695 		TimetableExport::writeRoomsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1696 	}
1697 	//subjects
1698 	s=add+SUBJECTS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1699 	s.prepend(outputDirectory);
1700 	TimetableExport::writeSubjectsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1701 	s=add+SUBJECTS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1702 	s.prepend(outputDirectory);
1703 	TimetableExport::writeSubjectsTimetableDaysVerticalHtml(parent, s, sTime, na);
1704 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1705 		s=add+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1706 		s.prepend(outputDirectory);
1707 		TimetableExport::writeSubjectsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1708 		s=add+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1709 		s.prepend(outputDirectory);
1710 		TimetableExport::writeSubjectsTimetableTimeVerticalHtml(parent, s, sTime, na);
1711 	} else {
1712 		s=add+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1713 		s.prepend(outputDirectory);
1714 		TimetableExport::writeSubjectsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1715 		s=add+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1716 		s.prepend(outputDirectory);
1717 		TimetableExport::writeSubjectsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1718 	}
1719 	//activity_tags
1720 	s=add+ACTIVITY_TAGS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1721 	s.prepend(outputDirectory);
1722 	TimetableExport::writeActivityTagsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1723 	s=add+ACTIVITY_TAGS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1724 	s.prepend(outputDirectory);
1725 	TimetableExport::writeActivityTagsTimetableDaysVerticalHtml(parent, s, sTime, na);
1726 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1727 		s=add+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1728 		s.prepend(outputDirectory);
1729 		TimetableExport::writeActivityTagsTimetableTimeHorizontalHtml(parent, s, sTime, na);
1730 		s=add+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1731 		s.prepend(outputDirectory);
1732 		TimetableExport::writeActivityTagsTimetableTimeVerticalHtml(parent, s, sTime, na);
1733 	} else {
1734 		s=add+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1735 		s.prepend(outputDirectory);
1736 		TimetableExport::writeActivityTagsTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1737 		s=add+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1738 		s.prepend(outputDirectory);
1739 		TimetableExport::writeActivityTagsTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1740 	}
1741 	//all activities
1742 	s=add+ALL_ACTIVITIES_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1743 	s.prepend(outputDirectory);
1744 	TimetableExport::writeAllActivitiesTimetableDaysHorizontalHtml(parent, s, sTime, na);
1745 	s=add+ALL_ACTIVITIES_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1746 	s.prepend(outputDirectory);
1747 	TimetableExport::writeAllActivitiesTimetableDaysVerticalHtml(parent, s, sTime, na);
1748 	if(!DIVIDE_HTML_TIMETABLES_WITH_TIME_AXIS_BY_DAYS){
1749 		s=add+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1750 		s.prepend(outputDirectory);
1751 		TimetableExport::writeAllActivitiesTimetableTimeHorizontalHtml(parent, s, sTime, na);
1752 		s=add+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1753 		s.prepend(outputDirectory);
1754 		TimetableExport::writeAllActivitiesTimetableTimeVerticalHtml(parent, s, sTime, na);
1755 	} else {
1756 		s=add+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML;
1757 		s.prepend(outputDirectory);
1758 		TimetableExport::writeAllActivitiesTimetableTimeHorizontalDailyHtml(parent, s, sTime, na);
1759 		s=add+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML;
1760 		s.prepend(outputDirectory);
1761 		TimetableExport::writeAllActivitiesTimetableTimeVerticalDailyHtml(parent, s, sTime, na);
1762 	}
1763 	//teachers free periods
1764 	s=add+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML;
1765 	s.prepend(outputDirectory);
1766 	TimetableExport::writeTeachersFreePeriodsTimetableDaysHorizontalHtml(parent, s, sTime, na);
1767 	s=add+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML;
1768 	s.prepend(outputDirectory);
1769 	TimetableExport::writeTeachersFreePeriodsTimetableDaysVerticalHtml(parent, s, sTime, na);
1770 	//statistics
1771 	s=add+TEACHERS_STATISTICS_FILENAME_HTML;
1772 	s.prepend(outputDirectory);
1773 	TimetableExport::writeTeachersStatisticsHtml(parent, s, sTime, na);
1774 	s=add+STUDENTS_STATISTICS_FILENAME_HTML;
1775 	s.prepend(outputDirectory);
1776 	TimetableExport::writeStudentsStatisticsHtml(parent, s, sTime, na);
1777 
1778 	//we can keep it, since it is for the command line version (but in fact we can also clear or delete these lines, since command line doesn't need interface printing)
1779 	/*hashSubjectIDsTimetable.clear();
1780 	hashActivityTagIDsTimetable.clear();
1781 	hashStudentIDsTimetable.clear();
1782 	hashTeacherIDsTimetable.clear();
1783 	hashRoomIDsTimetable.clear();
1784 	hashDayIDsTimetable.clear();
1785 	hashActivityColorBySubject.clear();
1786 	hashActivityColorBySubjectAndStudents.clear();
1787 	activeHashActivityColorBySubject.clear();
1788 	activeHashActivityColorBySubjectAndStudents.clear();*/
1789 }
1790 
writeRandomSeedCommandLine(QWidget * parent,const MRG32k3a & rng,const QString & outputDirectory,bool before)1791 void TimetableExport::writeRandomSeedCommandLine(QWidget* parent, const MRG32k3a& rng, const QString& outputDirectory, bool before){ //outputDirectory contains trailing FILE_SEP
1792 	QString RANDOM_SEED_FILENAME;
1793 	if(before)
1794 		RANDOM_SEED_FILENAME=RANDOM_SEED_FILENAME_BEFORE;
1795 	else
1796 		RANDOM_SEED_FILENAME=RANDOM_SEED_FILENAME_AFTER;
1797 
1798 	QString add=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1799 	if(add.right(4)==".fet")
1800 		add=add.left(add.length()-4);
1801 	//else if(INPUT_FILENAME_XML!="")
1802 	//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
1803 
1804 	if(add!="")
1805 		add.append("_");
1806 
1807 	QString s=add+RANDOM_SEED_FILENAME;
1808 	s.prepend(outputDirectory);
1809 
1810 	writeRandomSeedFile(parent, rng, s, before);
1811 }
1812 
1813 //by Volker Dirr (timetabling.de)
writeConflictsTxt(QWidget * parent,const QString & filename,const QString & saveTime,int placedActivities)1814 void TimetableExport::writeConflictsTxt(QWidget* parent, const QString& filename, const QString& saveTime, int placedActivities){
1815 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
1816 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
1817 
1818 	if(!WRITE_TIMETABLE_CONFLICTS){
1819 		if(QFile::exists(filename))
1820 			QFile::remove(filename);
1821 
1822 		return;
1823 	}
1824 
1825 	QFile file(filename);
1826 	if(!file.open(QIODevice::WriteOnly)){
1827 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1828 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(filename));
1829 		return;
1830 	}
1831 	QTextStream tos(&file);
1832 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
1833 	tos.setEncoding(QStringConverter::Utf8);
1834 #else
1835 	tos.setCodec("UTF-8");
1836 #endif
1837 	tos.setGenerateByteOrderMark(true);
1838 
1839 	if(placedActivities==gt.rules.nInternalActivities){
1840 		QString tt=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1841 		if(INPUT_FILENAME_XML=="")
1842 			tt=tr("unnamed");
1843 		tos<<TimetableExport::tr("Soft conflicts of %1", "%1 is the file name").arg(tt);
1844 		tos<<"\n";
1845 		tos<<TimetableExport::tr("Generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)<<"\n\n";
1846 
1847 		tos<<TimetableExport::tr("Number of broken soft constraints: %1").arg(best_solution.conflictsWeightList.count())<<"\n";
1848 		tos<<TimetableExport::tr("Total soft conflicts: %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(best_solution.conflictsTotal))<<"\n\n";
1849 		tos<<TimetableExport::tr("Soft conflicts list (in decreasing order):")<<"\n\n";
1850 		for(const QString& t : qAsConst(best_solution.conflictsDescriptionList))
1851 			tos<<t<<"\n";
1852 		tos<<"\n"<<TimetableExport::tr("End of file.")<<"\n";
1853 	}
1854 	else{
1855 		QString tt=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
1856 		if(INPUT_FILENAME_XML=="")
1857 			tt=tr("unnamed");
1858 		tos<<TimetableExport::tr("Conflicts of %1").arg(tt);
1859 		tos<<"\n";
1860 		tos<<TimetableExport::tr("Warning! Only %1 out of %2 activities placed!").arg(placedActivities).arg(gt.rules.nInternalActivities)<<"\n";
1861 		tos<<TimetableExport::tr("Generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)<<"\n\n";
1862 
1863 		tos<<TimetableExport::tr("Number of broken constraints: %1").arg(best_solution.conflictsWeightList.count())<<"\n";
1864 		tos<<TimetableExport::tr("Total conflicts: %1").arg(CustomFETString::numberPlusTwoDigitsPrecision(best_solution.conflictsTotal))<<"\n\n";
1865 		tos<<TimetableExport::tr("Conflicts list (in decreasing order):")<<"\n\n";
1866 		for(const QString& t : qAsConst(best_solution.conflictsDescriptionList))
1867 			tos<<t<<"\n";
1868 		tos<<"\n"<<TimetableExport::tr("End of file.")<<"\n";
1869 	}
1870 
1871 	if(file.error()>0){
1872 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1873 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(filename).arg(file.error()));
1874 	}
1875 	file.close();
1876 }
1877 
writeSubgroupsTimetableXml(QWidget * parent,const QString & xmlfilename,const QList<int> & subgroupsSortedOrder)1878 void TimetableExport::writeSubgroupsTimetableXml(QWidget* parent, const QString& xmlfilename, const QList<int>& subgroupsSortedOrder){
1879 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
1880 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
1881 
1882 	if(!WRITE_TIMETABLES_XML || !WRITE_TIMETABLES_SUBGROUPS){
1883 		if(QFile::exists(xmlfilename))
1884 			QFile::remove(xmlfilename);
1885 
1886 		return;
1887 	}
1888 
1889 	//Now we print the results to an XML file
1890 	QFile file(xmlfilename);
1891 	if(!file.open(QIODevice::WriteOnly)){
1892 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1893 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(xmlfilename));
1894 		return;
1895 	}
1896 	QTextStream tos(&file);
1897 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
1898 	tos.setEncoding(QStringConverter::Utf8);
1899 #else
1900 	tos.setCodec("UTF-8");
1901 #endif
1902 	tos.setGenerateByteOrderMark(true);
1903 	tos<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1904 	tos<<"<"<<protect(STUDENTS_TIMETABLE_TAG)<<">\n";
1905 
1906 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
1907 		int realSubgroup;
1908 		if(subgroupsSortedOrder!=QList<int>())
1909 			realSubgroup=subgroupsSortedOrder.at(subgroup);
1910 		else
1911 			realSubgroup=subgroup;
1912 
1913 		tos<< "  <Subgroup name=\"";
1914 		QString subgroup_name = gt.rules.internalSubgroupsList[realSubgroup]->name;
1915 		tos<< protect(subgroup_name) << "\">\n";
1916 
1917 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
1918 			tos<<"   <Day name=\""<<protect(gt.rules.daysOfTheWeek[day])<<"\">\n";
1919 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
1920 				tos << "    <Hour name=\"" << protect(gt.rules.hoursOfTheDay[hour]) << "\">\n";
1921 				tos<<"     ";
1922 				int ai=students_timetable_weekly[realSubgroup][day][hour]; //activity index
1923 				if(ai!=UNALLOCATED_ACTIVITY){
1924 					//Activity* act=gt.rules.activitiesList.at(ai);
1925 					Activity* act=&gt.rules.internalActivitiesList[ai];
1926 					for(QStringList::const_iterator it=act->teachersNames.constBegin(); it!=act->teachersNames.constEnd(); it++)
1927 						tos<<"<Teacher name=\""<<protect(*it)<<"\"></Teacher>";
1928 					tos<<"<Subject name=\""<<protect(act->subjectName)<<"\"></Subject>";
1929 					for(const QString& atn : qAsConst(act->activityTagsNames))
1930 						tos<<"<Activity_Tag name=\""<<protect(atn)<<"\"></Activity_Tag>";
1931 
1932 					int r=best_solution.rooms[ai];
1933 					if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
1934 						tos<<"<Room name=\""<<protect(gt.rules.internalRoomsList[r]->name)<<"\"></Room>";
1935 						if(gt.rules.internalRoomsList[r]->isVirtual==true)
1936 							for(int rr : qAsConst(best_solution.realRoomsList[ai]))
1937 								tos<<"<Real_Room name=\""<<protect(gt.rules.internalRoomsList[rr]->name)<<"\"></Real_Room>";
1938 					}
1939 				}
1940 				tos<<"\n";
1941 				tos << "    </Hour>\n";
1942 			}
1943 			tos<<"   </Day>\n";
1944 		}
1945 		tos<<"  </Subgroup>\n";
1946 	}
1947 
1948 	tos << "</" << protect(STUDENTS_TIMETABLE_TAG) << ">\n";
1949 
1950 	if(file.error()>0){
1951 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1952 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(xmlfilename).arg(file.error()));
1953 	}
1954 	file.close();
1955 }
1956 
writeTeachersTimetableXml(QWidget * parent,const QString & xmlfilename)1957 void TimetableExport::writeTeachersTimetableXml(QWidget* parent, const QString& xmlfilename){
1958 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
1959 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
1960 
1961 	if(!WRITE_TIMETABLES_XML || !WRITE_TIMETABLES_TEACHERS){
1962 		if(QFile::exists(xmlfilename))
1963 			QFile::remove(xmlfilename);
1964 
1965 		return;
1966 	}
1967 
1968 	//Writing the timetable in xml format
1969 	QFile file(xmlfilename);
1970 	if(!file.open(QIODevice::WriteOnly)){
1971 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
1972 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(xmlfilename));
1973 		return;
1974 	}
1975 	QTextStream tos(&file);
1976 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
1977 	tos.setEncoding(QStringConverter::Utf8);
1978 #else
1979 	tos.setCodec("UTF-8");
1980 #endif
1981 	tos.setGenerateByteOrderMark(true);
1982 	tos<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
1983 	tos << "<" << protect(TEACHERS_TIMETABLE_TAG) << ">\n";
1984 
1985 	for(int i=0; i<gt.rules.nInternalTeachers; i++){
1986 		tos << "  <Teacher name=\"" << protect(gt.rules.internalTeachersList[i]->name) << "\">\n";
1987 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
1988 			tos << "   <Day name=\"" << protect(gt.rules.daysOfTheWeek[day]) << "\">\n";
1989 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
1990 				tos << "    <Hour name=\"" << protect(gt.rules.hoursOfTheDay[hour]) << "\">\n";
1991 
1992 				tos<<"     ";
1993 				int ai=teachers_timetable_weekly[i][day][hour]; //activity index
1994 				//Activity* act=gt.rules.activitiesList.at(ai);
1995 				if(ai!=UNALLOCATED_ACTIVITY){
1996 					Activity* act=&gt.rules.internalActivitiesList[ai];
1997 					tos<<"<Subject name=\""<<protect(act->subjectName)<<"\"></Subject>";
1998 					for(const QString& atn : qAsConst(act->activityTagsNames))
1999 						tos<<"<Activity_Tag name=\""<<protect(atn)<<"\"></Activity_Tag>";
2000 					for(QStringList::const_iterator it=act->studentsNames.constBegin(); it!=act->studentsNames.constEnd(); it++)
2001 						tos << "<Students name=\"" << protect(*it) << "\"></Students>";
2002 
2003 					int r=best_solution.rooms[ai];
2004 					if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
2005 						tos<<"<Room name=\""<<protect(gt.rules.internalRoomsList[r]->name)<<"\"></Room>";
2006 						if(gt.rules.internalRoomsList[r]->isVirtual==true)
2007 							for(int rr : qAsConst(best_solution.realRoomsList[ai]))
2008 								tos<<"<Real_Room name=\""<<protect(gt.rules.internalRoomsList[rr]->name)<<"\"></Real_Room>";
2009 					}
2010 				}
2011 				tos<<"\n";
2012 				tos << "    </Hour>\n";
2013 			}
2014 			tos << "   </Day>\n";
2015 		}
2016 		tos<<"  </Teacher>\n";
2017 	}
2018 
2019 	tos << "</" << protect(TEACHERS_TIMETABLE_TAG) << ">\n";
2020 
2021 	if(file.error()>0){
2022 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2023 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(xmlfilename).arg(file.error()));
2024 	}
2025 	file.close();
2026 }
2027 
writeActivitiesTimetableXml(QWidget * parent,const QString & xmlfilename)2028 void TimetableExport::writeActivitiesTimetableXml(QWidget* parent, const QString& xmlfilename){
2029 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2030 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2031 
2032 	if(!WRITE_TIMETABLES_XML || !WRITE_TIMETABLES_ACTIVITIES){
2033 		if(QFile::exists(xmlfilename))
2034 			QFile::remove(xmlfilename);
2035 
2036 		return;
2037 	}
2038 
2039 	//Writing the timetable in xml format
2040 	QFile file(xmlfilename);
2041 	if(!file.open(QIODevice::WriteOnly)){
2042 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2043 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(xmlfilename));
2044 		return;
2045 	}
2046 	QTextStream tos(&file);
2047 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2048 	tos.setEncoding(QStringConverter::Utf8);
2049 #else
2050 	tos.setCodec("UTF-8");
2051 #endif
2052 	tos.setGenerateByteOrderMark(true);
2053 	tos<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
2054 	tos << "<" << protect(ACTIVITIES_TIMETABLE_TAG) << ">\n";
2055 
2056 	for(int i=0; i<gt.rules.nInternalActivities; i++){
2057 		tos<<"<Activity>\n";
2058 
2059 		tos<<"	<Id>"<<gt.rules.internalActivitiesList[i].id<<"</Id>\n";
2060 
2061 		QString day="";
2062 		if(best_solution.times[i]!=UNALLOCATED_TIME){
2063 			int d=best_solution.times[i]%gt.rules.nDaysPerWeek;
2064 			day=gt.rules.daysOfTheWeek[d];
2065 		}
2066 		tos<<"	<Day>"<<protect(day)<<"</Day>\n";
2067 
2068 		QString hour="";
2069 		if(best_solution.times[i]!=UNALLOCATED_TIME){
2070 			int h=best_solution.times[i]/gt.rules.nDaysPerWeek;
2071 			hour=gt.rules.hoursOfTheDay[h];
2072 		}
2073 		tos<<"	<Hour>"<<protect(hour)<<"</Hour>\n";
2074 
2075 		QString room="";
2076 		if(best_solution.rooms[i]!=UNALLOCATED_SPACE && best_solution.rooms[i]!=UNSPECIFIED_ROOM){
2077 			int r=best_solution.rooms[i];
2078 			room=gt.rules.internalRoomsList[r]->name;
2079 		}
2080 		tos<<"	<Room>"<<protect(room)<<"</Room>\n";
2081 		if(best_solution.rooms[i]!=UNALLOCATED_SPACE && best_solution.rooms[i]!=UNSPECIFIED_ROOM){
2082 			int r=best_solution.rooms[i];
2083 			if(gt.rules.internalRoomsList[r]->isVirtual==true){
2084 				assert(gt.rules.internalRoomsList[r]->rrsl.count()==best_solution.realRoomsList[i].count());
2085 				for(int rr : qAsConst(best_solution.realRoomsList[i])){
2086 					assert(rr>=0 && rr<gt.rules.nInternalRooms);
2087 					tos<<"	<Real_Room>"<<protect(gt.rules.internalRoomsList[rr]->name)<<"</Real_Room>\n";
2088 				}
2089 			}
2090 		}
2091 
2092 		tos<<"</Activity>\n";
2093 	}
2094 
2095 	tos << "</" << protect(ACTIVITIES_TIMETABLE_TAG) << ">\n";
2096 
2097 	if(file.error()>0){
2098 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2099 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(xmlfilename).arg(file.error()));
2100 	}
2101 	file.close();
2102 }
2103 
2104 // writing the index html file by Volker Dirr.
writeIndexHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)2105 void TimetableExport::writeIndexHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
2106 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2107 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2108 
2109 	bool _writeAtLeastATimetable = writeAtLeastATimetable();
2110 
2111 	//Now we print the results to an HTML file
2112 	QFile file(htmlfilename);
2113 	if(!file.open(QIODevice::WriteOnly)){
2114 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2115 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2116 		return;
2117 	}
2118 	QTextStream tos(&file);
2119 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2120 	tos.setEncoding(QStringConverter::Utf8);
2121 #else
2122 	tos.setCodec("UTF-8");
2123 #endif
2124 	tos.setGenerateByteOrderMark(true);
2125 
2126 	tos<<writeHead(false, placedActivities, true);
2127 
2128 	QString bar;
2129 	QString s2="";
2130 	if(INPUT_FILENAME_XML=="")
2131 		bar="";
2132 	else{
2133 		bar="_";
2134 		s2=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
2135 
2136 		if(s2.right(4)==".fet")
2137 			s2=s2.left(s2.length()-4);
2138 		//else if(INPUT_FILENAME_XML!="")
2139 		//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
2140 	}
2141 	tos<<"    <p>\n";
2142 
2143 	if(!_writeAtLeastATimetable){
2144 		tos<<"      "<<TimetableExport::tr("No timetable was written, because from the settings you disabled writing any timetable.")<<"\n";
2145 		tos<<"      "<<TimetableExport::tr("The exception is that after each successful (complete) timetable generation the %1 file"
2146 		 " will be written.").arg("data_and_timetable.fet")<<"\n";
2147 		tos<<"    </p>\n";
2148 		tos<<"    <p>\n";
2149 		tos<<"      "<<TimetableExport::tr("File generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)<<"\n";
2150 		tos<<"    </p>\n";
2151 	}
2152 	else{
2153 		if(WRITE_TIMETABLE_CONFLICTS)
2154 			tos<<"      <a href=\""<<s2+bar+CONFLICTS_FILENAME<<"\">"<<tr("View the soft conflicts list.")<<"</a><br />\n";
2155 		else
2156 			tos<<"      "<<TimetableExport::tr("Soft conflicts list - disabled.")<<"<br />\n";
2157 
2158 		///////////////////////////
2159 
2160 		QString tmps1, tmps2;
2161 		if(WRITE_TIMETABLES_STATISTICS && (WRITE_TIMETABLES_YEARS || WRITE_TIMETABLES_GROUPS || WRITE_TIMETABLES_SUBGROUPS) )
2162 			tmps1="      <a href=\""+s2+bar+STUDENTS_STATISTICS_FILENAME_HTML+"\">"+tr("students")+"</a>";
2163 		else
2164 			tmps1=tr("students - disabled");
2165 
2166 		if(WRITE_TIMETABLES_STATISTICS && WRITE_TIMETABLES_TEACHERS)
2167 			tmps2="<a href=\""+s2+bar+TEACHERS_STATISTICS_FILENAME_HTML+"\">"+tr("teachers")+"</a>";
2168 		else
2169 			tmps2=tr("teachers - disabled");
2170 
2171 		QString tmps3=tr("View statistics: %1, %2.", "%1 and %2 are two files in HTML format, to show statistics for students and teachers. The user can click on one file to view it")
2172 		 .arg(tmps1).arg(tmps2);
2173 
2174 		tos<<"      "<<tmps3<<"<br />\n";
2175 
2176 		///////////////////////////
2177 
2178 		QString tmp1, tmp2, tmp3;
2179 		if(WRITE_TIMETABLES_XML && WRITE_TIMETABLES_SUBGROUPS)
2180 			tmp1="<a href=\""+s2+bar+SUBGROUPS_TIMETABLE_FILENAME_XML+"\">"+tr("subgroups")+"</a>";
2181 		else
2182 			tmp1=tr("subgroups - disabled", "It means the subgroups XML timetables are disabled");
2183 		if(WRITE_TIMETABLES_XML && WRITE_TIMETABLES_TEACHERS)
2184 			tmp2="<a href=\""+s2+bar+TEACHERS_TIMETABLE_FILENAME_XML+"\">"+tr("teachers")+"</a>";
2185 		else
2186 			tmp2=tr("teachers - disabled", "It means the teachers XML timetables are disabled");
2187 		if(WRITE_TIMETABLES_XML && WRITE_TIMETABLES_ACTIVITIES)
2188 			tmp3="<a href=\""+s2+bar+ACTIVITIES_TIMETABLE_FILENAME_XML+"\">"+tr("activities")+"</a>";
2189 		else
2190 			tmp3=tr("activities - disabled", "It means the activities XML timetables are disabled");
2191 		QString tmp4=TimetableExport::tr("View XML: %1, %2, %3.", "%1, %2 and %3 are three files in XML format, subgroups, teachers and activities timetables. The user can click on one file to view it").arg(tmp1).arg(tmp2).arg(tmp3);
2192 		tos<<"      "<<tmp4<<"\n";
2193 
2194 		tos<<"    </p>\n\n";
2195 
2196 		tos<<"    <table border=\"1\">\n";
2197 
2198 		tos<<"      <caption>"<<protect2(gt.rules.institutionName)<<"</caption>\n";
2199 
2200 		tos<<"      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\"4\">"+tr("Timetables")+"</th></tr>\n";
2201 		tos<<"        <tr>\n          <!-- span -->\n";
2202 		tos<<"          <th>"+tr("Days Horizontal")+"</th><th>"+tr("Days Vertical")+"</th><th>"+tr("Time Horizontal")+"</th><th>"+tr("Time Vertical")+"</th>";
2203 		tos<<"        </tr>\n";
2204 		tos<<"      </thead>\n";
2205 		tos<<"      <tbody>\n";
2206 
2207 		/* workaround
2208 		tos<<"      <tfoot><tr><td></td><td colspan=\"4\">"<<TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)<<"</td></tr></tfoot>\n";
2209 		*/
2210 
2211 		tos<<"        <tr>\n";
2212 		tos<<"          <th>"+tr("Subgroups")+"</th>\n";
2213 		if(WRITE_TIMETABLES_SUBGROUPS){
2214 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2215 				tos<<"          <td><a href=\""<<s2+bar+SUBGROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2216 			else
2217 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2218 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2219 				tos<<"          <td><a href=\""<<s2+bar+SUBGROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2220 			else
2221 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2222 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2223 				tos<<"          <td><a href=\""<<s2+bar+SUBGROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2224 			else
2225 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2226 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2227 				tos<<"          <td><a href=\""<<s2+bar+SUBGROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2228 			else
2229 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2230 		} else {
2231 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2232 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2233 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2234 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2235 		}
2236 		tos<<"        </tr>\n";
2237 		tos<<"        <tr>\n";
2238 		tos<<"          <th>"+tr("Groups")+"</th>\n";
2239 		if(WRITE_TIMETABLES_GROUPS){
2240 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2241 				tos<<"          <td><a href=\""<<s2+bar+GROUPS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2242 			else
2243 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2244 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2245 				tos<<"          <td><a href=\""<<s2+bar+GROUPS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2246 			else
2247 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2248 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2249 				tos<<"          <td><a href=\""<<s2+bar+GROUPS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2250 			else
2251 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2252 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2253 				tos<<"          <td><a href=\""<<s2+bar+GROUPS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2254 			else
2255 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2256 		} else {
2257 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2258 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2259 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2260 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2261 		}
2262 		tos<<"        </tr>\n";
2263 		tos<<"        <tr>\n";
2264 		tos<<"          <th>"+tr("Years")+"</th>\n";
2265 		if(WRITE_TIMETABLES_YEARS){
2266 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2267 				tos<<"          <td><a href=\""<<s2+bar+YEARS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2268 			else
2269 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2270 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2271 				tos<<"          <td><a href=\""<<s2+bar+YEARS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2272 			else
2273 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2274 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2275 				tos<<"          <td><a href=\""<<s2+bar+YEARS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2276 			else
2277 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2278 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2279 				tos<<"          <td><a href=\""<<s2+bar+YEARS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2280 			else
2281 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2282 		} else {
2283 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2284 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2285 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2286 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2287 		}
2288 		tos<<"        </tr>\n";
2289 		tos<<"        <tr>\n";
2290 		tos<<"          <th>"+tr("Teachers")+"</th>\n";
2291 		if(WRITE_TIMETABLES_TEACHERS){
2292 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2293 				tos<<"          <td><a href=\""<<s2+bar+TEACHERS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2294 			else
2295 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2296 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2297 				tos<<"          <td><a href=\""<<s2+bar+TEACHERS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2298 			else
2299 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2300 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2301 				tos<<"          <td><a href=\""<<s2+bar+TEACHERS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2302 			else
2303 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2304 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2305 				tos<<"          <td><a href=\""<<s2+bar+TEACHERS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2306 			else
2307 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2308 		} else {
2309 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2310 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2311 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2312 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2313 		}
2314 		tos<<"        </tr>\n";
2315 		tos<<"        <tr>\n";
2316 		tos<<"          <th>"+tr("Teachers' Free Periods")+"</th>\n";
2317 		if(WRITE_TIMETABLES_TEACHERS_FREE_PERIODS){
2318 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2319 				tos<<"          <td><a href=\""<<s2+bar+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2320 			else
2321 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2322 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2323 				tos<<"          <td><a href=\""<<s2+bar+TEACHERS_FREE_PERIODS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2324 			else
2325 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2326 		} else {
2327 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2328 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2329 		}
2330 		tos<<"          <td>"<<protect2(STRING_EMPTY_SLOT)<<"</td>\n";
2331 		tos<<"          <td>"<<protect2(STRING_EMPTY_SLOT)<<"</td>\n";
2332 		tos<<"        </tr>\n";
2333 		tos<<"        <tr>\n";
2334 		tos<<"          <th>"+tr("Rooms")+"</th>\n";
2335 		if(WRITE_TIMETABLES_ROOMS){
2336 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2337 				tos<<"          <td><a href=\""<<s2+bar+ROOMS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2338 			else
2339 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2340 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2341 				tos<<"          <td><a href=\""<<s2+bar+ROOMS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2342 			else
2343 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2344 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2345 				tos<<"          <td><a href=\""<<s2+bar+ROOMS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2346 			else
2347 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2348 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2349 				tos<<"          <td><a href=\""<<s2+bar+ROOMS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2350 			else
2351 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2352 		} else {
2353 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2354 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2355 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2356 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2357 		}
2358 		tos<<"        </tr>\n";
2359 		tos<<"        <tr>\n";
2360 		tos<<"          <th>"+tr("Subjects")+"</th>\n";
2361 		if(WRITE_TIMETABLES_SUBJECTS){
2362 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2363 				tos<<"          <td><a href=\""<<s2+bar+SUBJECTS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2364 			else
2365 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2366 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2367 				tos<<"          <td><a href=\""<<s2+bar+SUBJECTS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2368 			else
2369 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2370 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2371 				tos<<"          <td><a href=\""<<s2+bar+SUBJECTS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2372 			else
2373 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2374 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2375 				tos<<"          <td><a href=\""<<s2+bar+SUBJECTS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2376 			else
2377 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2378 		} else {
2379 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2380 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2381 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2382 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2383 		}
2384 		tos<<"        </tr>\n";
2385 		tos<<"        <tr>\n";
2386 		tos<<"          <th>"+tr("Activity Tags")+"</th>\n";
2387 		if(WRITE_TIMETABLES_ACTIVITY_TAGS){
2388 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2389 				tos<<"          <td><a href=\""<<s2+bar+ACTIVITY_TAGS_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2390 			else
2391 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2392 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2393 				tos<<"          <td><a href=\""<<s2+bar+ACTIVITY_TAGS_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2394 			else
2395 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2396 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2397 				tos<<"          <td><a href=\""<<s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2398 			else
2399 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2400 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2401 				tos<<"          <td><a href=\""<<s2+bar+ACTIVITY_TAGS_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2402 			else
2403 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2404 		} else {
2405 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2406 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2407 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2408 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2409 		}
2410 		tos<<"        </tr>\n";
2411 		tos<<"        <tr>\n";
2412 		tos<<"          <th>"+tr("Activities")+"</th>\n";
2413 		if(WRITE_TIMETABLES_ACTIVITIES){
2414 			if(WRITE_TIMETABLES_DAYS_HORIZONTAL)
2415 				tos<<"          <td><a href=\""<<s2+bar+ALL_ACTIVITIES_TIMETABLE_DAYS_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2416 			else
2417 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2418 			if(WRITE_TIMETABLES_DAYS_VERTICAL)
2419 				tos<<"          <td><a href=\""<<s2+bar+ALL_ACTIVITIES_TIMETABLE_DAYS_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2420 			else
2421 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2422 			if(WRITE_TIMETABLES_TIME_HORIZONTAL)
2423 				tos<<"          <td><a href=\""<<s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_HORIZONTAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2424 			else
2425 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2426 			if(WRITE_TIMETABLES_TIME_VERTICAL)
2427 				tos<<"          <td><a href=\""<<s2+bar+ALL_ACTIVITIES_TIMETABLE_TIME_VERTICAL_FILENAME_HTML<<"\">"+tr("view")+"</a></td>\n";
2428 			else
2429 				tos<<"          <td>"+tr("disabled")+"</td>\n";
2430 		} else {
2431 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2432 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2433 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2434 			tos<<"          <td>"+tr("disabled")+"</td>\n";
2435 		}
2436 		tos<<"        </tr>\n";
2437 		//workaround begin.
2438 		tos<<"      <tr class=\"foot\"><td></td><td colspan=\"4\">"<<TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)<<"</td></tr>\n";
2439 		//workaround end.
2440 		tos<<"      </tbody>\n";
2441 		tos<<"    </table>\n";
2442 	}
2443 
2444 	tos<<"  </body>\n</html>\n";
2445 
2446 	if(file.error()>0){
2447 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2448 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2449 	}
2450 	file.close();
2451 }
2452 
2453 // writing the stylesheet in css format to a file by Volker Dirr.
writeStylesheetCss(QWidget * parent,const QString & cssfilename,const QString & saveTime,int placedActivities)2454 void TimetableExport::writeStylesheetCss(QWidget* parent, const QString& cssfilename, const QString& saveTime, int placedActivities){
2455 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2456 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2457 
2458 	bool _writeAtLeastATimetable = writeAtLeastATimetable();
2459 
2460 	if(!_writeAtLeastATimetable){
2461 		if(QFile::exists(cssfilename))
2462 			QFile::remove(cssfilename);
2463 
2464 		return;
2465 	}
2466 
2467 	//get used students	//TODO: do it the same way in statistics.cpp
2468 	QSet<QString> usedStudents;
2469 	for(int i=0; i<gt.rules.nInternalActivities; i++){
2470 		for(const QString& st : qAsConst(gt.rules.internalActivitiesList[i].studentsNames)){
2471 			if(!usedStudents.contains(st))
2472 				usedStudents<<st;
2473 		}
2474 	}
2475 
2476 	//Now we print the results to a CSS file
2477 	QFile file(cssfilename);
2478 	if(!file.open(QIODevice::WriteOnly)){
2479 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2480 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(cssfilename));
2481 		return;
2482 	}
2483 	QTextStream tos(&file);
2484 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2485 	tos.setEncoding(QStringConverter::Utf8);
2486 #else
2487 	tos.setCodec("UTF-8");
2488 #endif
2489 	tos.setGenerateByteOrderMark(true);
2490 
2491 	tos<<"@charset \"UTF-8\";"<<"\n\n";
2492 
2493 	QString tt=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
2494 	if(INPUT_FILENAME_XML=="")
2495 		tt=tr("unnamed");
2496 	tos<<"/* "<<TimetableExport::tr("CSS Stylesheet of %1", "%1 is the file name").arg(tt);
2497 	tos<<"\n";
2498 	if(placedActivities!=gt.rules.nInternalActivities)
2499 		tos<<"   "<<TimetableExport::tr("Warning! Only %1 out of %2 activities placed!").arg(placedActivities).arg(gt.rules.nInternalActivities)<<"\n";
2500 	tos<<"   "<<TimetableExport::tr("Stylesheet generated with FET %1 on %2", "%1 is FET version, %2 is date and time").arg(FET_VERSION).arg(saveTime)<<" */\n\n";
2501 
2502 	tos<<"/* "<<TimetableExport::tr("To do a page-break only after every second timetable, delete \"page-break-before: always;\" in \"table.even_table\".",
2503 		"Please keep fields in quotes as they are, untranslated.")<<" */\n";
2504 	tos<<"/* "<<TimetableExport::tr("Delete \"page-break-before: always;\" in \"table.even_table\" and in \"table.odd_table\" to skip page-breaks.",
2505 		"Please keep fields in quotes as they are, untranslated.")<<" */\n";
2506 	tos<<"/* "<<TimetableExport::tr("To hide an element just write the following phrase into the element: %1 (without quotes).",
2507 		"%1 is a short phrase beginning and ending with quotes, and we want the user to be able to add it, but without quotes").arg("\"display: none;\"")<<" */\n\n";
2508 	tos<<"p.back {\n  margin-top: 4ex;\n  margin-bottom: 5ex;\n}\n\n";
2509 	tos<<"table {\n  text-align: center;\n  page-break-inside: avoid;\n}\n\n";
2510 	tos<<"table.odd_table {\n  page-break-before: always;\n}\n\n";
2511 	tos<<"table.even_table {\n  page-break-before: always;\n}\n\n";
2512 	tos<<"table.detailed {\n  margin-left: auto; margin-right: auto;\n  text-align: center;\n  border: 0px;\n  border-spacing: 0;\n  border-collapse: collapse;\n}\n\n";
2513 	tos<<"caption {\n\n}\n\n";
2514 	tos<<"thead {\n\n}\n\n";
2515 
2516 	//workaround begin.
2517 	tos<<"/* "<<TimetableExport::tr("Some programs import \"tfoot\" incorrectly. So we use \"tr.foot\" instead of \"tfoot\".",
2518 	 "Please keep tfoot and tr.foot untranslated, as they are in the original English phrase")<<" */\n\n";
2519 	//tos<<"tfoot {\n\n}\n\n";
2520 	tos<<"tr.foot {\n\n}\n\n";
2521 	//workaround end
2522 
2523 	tos<<"tbody {\n\n}\n\n";
2524 	tos<<"th {\n\n}\n\n";
2525 	tos<<"td {\n\n}\n\n";
2526 	tos<<"td.detailed {\n  border: 1px dashed silver;\n  border-bottom: 0;\n  border-top: 0;\n}\n\n";
2527 	if(TIMETABLE_HTML_LEVEL>=2){
2528 		tos<<"th.xAxis {\n/* width: 8em; */\n}\n\n";
2529 		tos<<"th.yAxis {\n  height: 8ex;\n}\n\n";
2530 	}
2531 
2532 	//By Liviu, with ideas from Volker
2533 	if(TIMETABLE_HTML_LEVEL==7){ //must be written before LEVEL 3, because LEVEL 3 should have higher priority
2534 		int cnt=0;
2535 		for(int i : qAsConst(activeHashActivityColorBySubject)){
2536 			Activity* act=&gt.rules.internalActivitiesList[i];
2537 
2538 			QString tmpString=act->subjectName;
2539 
2540 			//similar to the coloring by Marco Vassura (start)
2541 			int r,g,b;
2542 			stringToColor(tmpString, r, g, b);
2543 			tos << "td.c_"<<QString::number(cnt+1)<<" { /* Activity id: "<<QString::number(act->id)<<" (subject) */\n ";
2544 			tos<<"background-color: rgb("<<QString::number(r)<<", "<<QString::number(g)<<", "<<QString::number(b)<<");\n";
2545 			double brightness = double(r)*0.299 + double(g)*0.587 + double(b)*0.114;
2546 			if (brightness<127.5)
2547 				tos<<" color: white;\n";
2548 			else
2549 				tos<<" color: black;\n";
2550 			tos<<"}\n\n";
2551 			//similar to the coloring by Marco Vassura (end)
2552 			cnt++;
2553 		}
2554 		for(int i : qAsConst(activeHashActivityColorBySubjectAndStudents)){
2555 			Activity* act=&gt.rules.internalActivitiesList[i];
2556 
2557 			QString tmpString=act->subjectName+" "+act->studentsNames.join(", ");
2558 
2559 			//similar to the coloring by Marco Vassura (start)
2560 			int r,g,b;
2561 			stringToColor(tmpString, r, g, b);
2562 			tos << "td.c_"<<QString::number(cnt+1)<<" { /* Activity id: "<<QString::number(act->id)<<" (subject+students) */\n ";
2563 			tos<<"background-color: rgb("<<QString::number(r)<<", "<<QString::number(g)<<", "<<QString::number(b)<<");\n";
2564 			double brightness = double(r)*0.299 + double(g)*0.587 + double(b)*0.114;
2565 			if (brightness<127.5)
2566 				tos<<" color: white;\n";
2567 			else
2568 				tos<<" color: black;\n";
2569 			tos<<"}\n\n";
2570 			//similar to the coloring by Marco Vassura (end)
2571 			cnt++;
2572 		}
2573 	}
2574 //	if(TIMETABLE_HTML_LEVEL==7){ // must be written before LEVEL 3, because LEVEL 3 should have higher priority
2575 //		QHashIterator<QString, QString> i(hashColorStringIDsTimetable);
2576 //		while(i.hasNext()) {
2577 //			i.next();
2578 //			tos << "td.c_"<<i.value()<<" { /* "<<i.key()<<" */\n ";
2579 //
2580 //			//similar to the coloring by Marco Vassura (start)
2581 //			int r, g, b;
2582 //			stringToColor(i.key(), r, g, b);
2583 //			tos<<"background-color: rgb("<<QString::number(r)<<", "<<QString::number(g)<<", "<<QString::number(b)<<");\n";
2584 //			double brightness = double(r)*0.299 + double(g)*0.587 + double(b)*0.114;
2585 //			if (brightness<127.5)
2586 //				tos<<" color: white;\n";
2587 //			else
2588 //				tos<<" color: black;\n";
2589 //			//similar to the coloring by Marco Vassura (end)
2590 //			tos<<"}\n\n";
2591 //		}
2592 //	}
2593 	else if(TIMETABLE_HTML_LEVEL>=4){ // must be written before LEVEL 3, because LEVEL 3 should have higher priority
2594 		for(int i=0; i<gt.rules.nInternalSubjects; i++){
2595 			tos << "span.s_"<<hashSubjectIDsTimetable.value(gt.rules.internalSubjectsList[i]->name)<<" { /* subject "<<gt.rules.internalSubjectsList[i]->name<<" */\n\n}\n\n";
2596 		}
2597 		for(int i=0; i<gt.rules.nInternalActivityTags; i++){
2598 			if(gt.rules.internalActivityTagsList[i]->printable){
2599 				tos << "span.at_"<<hashActivityTagIDsTimetable.value(gt.rules.internalActivityTagsList[i]->name)<<" { /* activity tag "<<gt.rules.internalActivityTagsList[i]->name<<" */\n\n}\n\n";
2600 			}
2601 		}
2602 		for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
2603 			StudentsYear* sty=gt.rules.augmentedYearsList[i];
2604 			if(usedStudents.contains(sty->name))
2605 				tos << "span.ss_"<<hashStudentIDsTimetable.value(sty->name)<<" { /* students set "<<sty->name<<" */\n\n}\n\n";
2606 			for(int j=0; j<sty->groupsList.size(); j++){
2607 				StudentsGroup* stg=sty->groupsList[j];
2608 				if(usedStudents.contains(stg->name))
2609 					tos << "span.ss_"<<hashStudentIDsTimetable.value(stg->name)<<" { /* students set "<<stg->name<<" */\n\n}\n\n";
2610 				for(int k=0; k<stg->subgroupsList.size(); k++){
2611 					StudentsSubgroup* sts=stg->subgroupsList[k];
2612 					if(usedStudents.contains(sts->name))
2613 						tos << "span.ss_"<<hashStudentIDsTimetable.value(sts->name)<<" { /* students set "<<sts->name<<" */\n\n}\n\n";
2614 				}
2615 			}
2616 		}
2617 		for(int i=0; i<gt.rules.nInternalTeachers; i++){
2618 			tos << "span.t_"<<hashTeacherIDsTimetable.value(gt.rules.internalTeachersList[i]->name)<<" { /* teacher "<<gt.rules.internalTeachersList[i]->name<<" */\n\n}\n\n";
2619 		}
2620 		for(int room=0; room<gt.rules.nInternalRooms; room++){
2621 			tos << "span.r_"<<hashRoomIDsTimetable.value(gt.rules.internalRoomsList[room]->name)<<" { /* room "<<gt.rules.internalRoomsList[room]->name<<" */\n\n}\n\n";
2622 		}
2623 	}
2624 	if(TIMETABLE_HTML_LEVEL>=3){
2625 		tos<<"span.subject {\n\n}\n\n";
2626 		tos<<"span.activitytag {\n\n}\n\n";
2627 
2628 		tos<<"span.empty {\n  color: gray;\n}\n\n";
2629 		tos<<"td.empty {\n  border-color: silver;\n  border-right-style: none;\n  border-bottom-style: none;\n  border-left-style: dotted;\n  border-top-style: dotted;\n}\n\n";
2630 
2631 		tos<<"span.notAvailable {\n  color: gray;\n}\n\n";
2632 		tos<<"td.notAvailable {\n  border-color: silver;\n  border-right-style: none;\n  border-bottom-style: none;\n  border-left-style: dotted;\n  border-top-style: dotted;\n}\n\n";
2633 
2634 		tos<<"span.break {\n  color: gray;\n}\n\n";
2635 		tos<<"td.break {\n  border-color: silver;\n  border-right-style: none;\n  border-bottom-style: none;\n  border-left-style: dotted;\n  border-top-style: dotted;\n}\n\n";
2636 
2637 		tos<<"tr.studentsset, div.studentsset {\n\n}\n\n";
2638 		tos<<"tr.teacher, div.teacher {\n\n}\n\n";
2639 		tos<<"tr.room, div.room {\n\n}\n\n";
2640 		if(TIMETABLE_HTML_LEVEL!=7){
2641 			tos<<"tr.line0, div.line0 {\n  font-size: smaller;\n}\n\n";
2642 			tos<<"tr.line1, div.line1 {\n\n}\n\n";
2643 			tos<<"tr.line2, div.line2 {\n  font-size: smaller;\n  color: gray;\n}\n\n";
2644 			tos<<"tr.line3, div.line3 {\n  font-size: smaller;\n  color: silver;\n}\n\n";
2645 		} else {
2646 			tos<<"tr.line0, div.line0 {\n  font-size: smaller;\n}\n\n";
2647 			tos<<"tr.line1, div.line1 {\n\n}\n\n";
2648 			tos<<"tr.line2, div.line2 {\n  font-size: smaller;\n}\n\n";
2649 			tos<<"tr.line3, div.line3 {\n  font-size: smaller;\n}\n\n";
2650 		}
2651 	}
2652 	if(TIMETABLE_HTML_LEVEL==6){
2653 		tos<<"/* "<<TimetableExport::tr("Be careful. You might get mutual and ambiguous styles. CSS means that the last definition will be used.")<<" */\n\n";
2654 		for(int i=0; i<gt.rules.nInternalSubjects; i++){
2655 			tos << "td.s_"<<hashSubjectIDsTimetable.value(gt.rules.internalSubjectsList[i]->name)<<" { /* subject "<<gt.rules.internalSubjectsList[i]->name<<" */\n\n}\n\n";
2656 		}
2657 		for(int i=0; i<gt.rules.nInternalActivityTags; i++){
2658 			if(gt.rules.internalActivityTagsList[i]->printable){
2659 				tos << "td.at_"<<hashActivityTagIDsTimetable.value(gt.rules.internalActivityTagsList[i]->name)<<" { /* activity tag "<<gt.rules.internalActivityTagsList[i]->name<<" */\n\n}\n\n";
2660 			}
2661 		}
2662 		for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
2663 			StudentsYear* sty=gt.rules.augmentedYearsList[i];
2664 			if(usedStudents.contains(sty->name))
2665 				tos << "td.ss_"<<hashStudentIDsTimetable.value(sty->name)<<" { /* students set "<<sty->name<<" */\n\n}\n\n";
2666 			for(int j=0; j<sty->groupsList.size(); j++){
2667 				StudentsGroup* stg=sty->groupsList[j];
2668 				if(usedStudents.contains(stg->name))
2669 					tos << "td.ss_"<<hashStudentIDsTimetable.value(stg->name)<<" { /* students set "<<stg->name<<" */\n\n}\n\n";
2670 				for(int k=0; k<stg->subgroupsList.size(); k++){
2671 					StudentsSubgroup* sts=stg->subgroupsList[k];
2672 					if(usedStudents.contains(sts->name))
2673 						tos << "td.ss_"<<hashStudentIDsTimetable.value(sts->name)<<" { /* students set "<<sts->name<<" */\n\n}\n\n";
2674 				}
2675 			}
2676 		}
2677 		for(int i=0; i<gt.rules.nInternalTeachers; i++){
2678 			tos << "td.t_"<<hashTeacherIDsTimetable.value(gt.rules.internalTeachersList[i]->name)<<" { /* teacher "<<gt.rules.internalTeachersList[i]->name<<" */\n\n}\n\n";
2679 		}
2680 
2681 		//not included yet
2682 		//for(int room=0; room<gt.rules.nInternalRooms; room++){
2683 		//	tos << "span.r_"<<hashRoomIDsTimetable.value(gt.rules.internalRoomsList[room]->name)<<" { /* room "<<gt.rules.internalRoomsList[room]->name<<" */\n\n}\n\n";
2684 		//}
2685 	}
2686 	tos<<"/* "<<TimetableExport::tr("Style the teachers' free periods")<<" */\n\n";
2687 	if(TIMETABLE_HTML_LEVEL>=2){
2688 		tos<<"div.DESCRIPTION {\n  text-align: left;\n  font-size: smaller;\n}\n\n";
2689 	}
2690 	if(TIMETABLE_HTML_LEVEL>=3){
2691 		tos<<"div.TEACHER_HAS_SINGLE_GAP {\n  color: black;\n}\n\n";
2692 		tos<<"div.TEACHER_HAS_BORDER_GAP {\n  color: gray;\n}\n\n";
2693 		tos<<"div.TEACHER_HAS_BIG_GAP {\n  color: silver;\n}\n\n";
2694 		tos<<"div.TEACHER_MUST_COME_EARLIER {\n  color: purple;\n}\n\n";
2695 		tos<<"div.TEACHER_MUST_COME_MUCH_EARLIER {\n  font-size: smaller;\n  color: fuchsia;\n}\n\n";
2696 		tos<<"div.TEACHER_MUST_STAY_LONGER {\n  color: teal;\n}\n\n";
2697 		tos<<"div.TEACHER_MUST_STAY_MUCH_LONGER {\n  font-size: smaller;\n  color: aqua;\n}\n\n";
2698 		tos<<"div.TEACHER_HAS_A_FREE_DAY {\n  font-size: smaller;\n  color: red;\n}\n\n";
2699 		tos<<"div.TEACHER_IS_NOT_AVAILABLE {\n  font-size: smaller;\n  color: olive;\n}\n\n";
2700 	}
2701 	tos<<"/* "<<TimetableExport::tr("End of file.")<<" */\n";
2702 
2703 	if(file.error()>0){
2704 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2705 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(cssfilename).arg(file.error()));
2706 	}
2707 	file.close();
2708 }
2709 
2710 //XHTML generation code modified by Volker Dirr (timetabling.de) from old html generation code
writeSubgroupsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities,const QList<int> & subgroupsSortedOrder)2711 void TimetableExport::writeSubgroupsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities, const QList<int>& subgroupsSortedOrder){
2712 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2713 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2714 
2715 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_SUBGROUPS){
2716 		if(QFile::exists(htmlfilename))
2717 			QFile::remove(htmlfilename);
2718 
2719 		return;
2720 	}
2721 
2722 	//Now we print the results to an HTML file
2723 	QFile file(htmlfilename);
2724 	if(!file.open(QIODevice::WriteOnly)){
2725 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2726 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2727 		return;
2728 	}
2729 	QTextStream tos(&file);
2730 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2731 	tos.setEncoding(QStringConverter::Utf8);
2732 #else
2733 	tos.setCodec("UTF-8");
2734 #endif
2735 	tos.setGenerateByteOrderMark(true);
2736 
2737 	tos<<writeHead(true, placedActivities, true);
2738 
2739 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
2740 	tos<<"    <ul>\n";
2741 	for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
2742 		StudentsYear* sty=gt.rules.augmentedYearsList[i];
2743 		tos<<"      <li>\n        "<<TimetableExport::tr("Year")<<" "<<protect2(sty->name)<<"\n        <ul>\n";
2744 		for(int j=0; j<sty->groupsList.size(); j++){
2745 			StudentsGroup* stg=sty->groupsList[j];
2746 			tos<<"          <li>\n            "<<TimetableExport::tr("Group")<<" "<<protect2(stg->name)<<":\n";
2747 			for(int k=0; k<stg->subgroupsList.size(); k++){
2748 				StudentsSubgroup* sts=stg->subgroupsList[k];
2749 				tos<<"              <a href=\""<<"#table_"<<hashStudentIDsTimetable.value(sts->name)<<"\">"<<protect2(sts->name)<<"</a>\n";
2750 			}
2751 			tos<<"          </li>\n";
2752 		}
2753 		tos<<"        </ul>\n      </li>\n";
2754 	}
2755 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
2756 
2757 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
2758 		tos<<singleSubgroupsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, subgroup, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES, subgroupsSortedOrder);
2759 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
2760 	}
2761 	tos<<"  </body>\n</html>\n";
2762 
2763 	if(file.error()>0){
2764 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2765 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2766 	}
2767 	file.close();
2768 }
2769 
2770 //XHTML generation code modified by Volker Dirr (timetabling.de) from old html generation code
writeSubgroupsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities,const QList<int> & subgroupsSortedOrder)2771 void TimetableExport::writeSubgroupsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities, const QList<int>& subgroupsSortedOrder){
2772 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2773 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2774 
2775 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_SUBGROUPS){
2776 		if(QFile::exists(htmlfilename))
2777 			QFile::remove(htmlfilename);
2778 
2779 		return;
2780 	}
2781 
2782 	//Now we print the results to an HTML file
2783 	QFile file(htmlfilename);
2784 	if(!file.open(QIODevice::WriteOnly)){
2785 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2786 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2787 		return;
2788 	}
2789 	QTextStream tos(&file);
2790 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2791 	tos.setEncoding(QStringConverter::Utf8);
2792 #else
2793 	tos.setCodec("UTF-8");
2794 #endif
2795 	tos.setGenerateByteOrderMark(true);
2796 
2797 	tos<<writeHead(true, placedActivities, true);
2798 
2799 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
2800 	tos<<"    <ul>\n";
2801 	for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
2802 		StudentsYear* sty=gt.rules.augmentedYearsList[i];
2803 		tos<<"      <li>\n        "<<TimetableExport::tr("Year")<<" "<<protect2(sty->name)<<"\n        <ul>\n";
2804 		for(int j=0; j<sty->groupsList.size(); j++){
2805 			StudentsGroup* stg=sty->groupsList[j];
2806 			tos<<"          <li>\n            "<<TimetableExport::tr("Group")<<" "<<protect2(stg->name)<<":\n";
2807 			for(int k=0; k<stg->subgroupsList.size(); k++){
2808 				StudentsSubgroup* sts=stg->subgroupsList[k];
2809 				tos<<"              <a href=\""<<"#table_"<<hashStudentIDsTimetable.value(sts->name)<<"\">"<<protect2(sts->name)<<"</a>\n";
2810 			}
2811 			tos<<"          </li>\n";
2812 		}
2813 		tos<<"        </ul>\n      </li>\n";
2814 	}
2815 	tos<<"    </ul>\n    <p>&nbsp;</p>\n";
2816 
2817 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
2818 		tos<<singleSubgroupsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, subgroup, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES, subgroupsSortedOrder);
2819 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
2820 	}
2821 
2822 	tos<<"  </body>\n</html>\n";
2823 
2824 	if(file.error()>0){
2825 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2826 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2827 	}
2828 	file.close();
2829 }
2830 
2831 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubgroupsTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities,const QList<int> & subgroupsSortedOrder)2832 void TimetableExport::writeSubgroupsTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities, const QList<int>& subgroupsSortedOrder){
2833 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2834 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2835 
2836 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_SUBGROUPS){
2837 		if(QFile::exists(htmlfilename))
2838 			QFile::remove(htmlfilename);
2839 
2840 		return;
2841 	}
2842 
2843 	//Now we print the results to an HTML file
2844 	QFile file(htmlfilename);
2845 	if(!file.open(QIODevice::WriteOnly)){
2846 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2847 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2848 		return;
2849 	}
2850 	QTextStream tos(&file);
2851 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2852 	tos.setEncoding(QStringConverter::Utf8);
2853 #else
2854 	tos.setCodec("UTF-8");
2855 #endif
2856 	tos.setGenerateByteOrderMark(true);
2857 
2858 	tos<<writeHead(true, placedActivities, false);
2859 
2860 	QSet<int> tmp;
2861 	tos << singleSubgroupsTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalSubgroups, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES, subgroupsSortedOrder);
2862 
2863 	tos << "  </body>\n</html>\n";
2864 
2865 	if(file.error()>0){
2866 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2867 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2868 	}
2869 	file.close();
2870 }
2871 
2872 //XHTML generation code modified by Volker Dirr (timetabling.de) from old html generation code
writeSubgroupsTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities,const QList<int> & subgroupsSortedOrder)2873 void TimetableExport::writeSubgroupsTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities, const QList<int>& subgroupsSortedOrder){
2874 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2875 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2876 
2877 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_SUBGROUPS){
2878 		if(QFile::exists(htmlfilename))
2879 			QFile::remove(htmlfilename);
2880 
2881 		return;
2882 	}
2883 
2884 	//Now we print the results to an HTML file
2885 	QFile file(htmlfilename);
2886 	if(!file.open(QIODevice::WriteOnly)){
2887 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2888 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2889 		return;
2890 	}
2891 	QTextStream tos(&file);
2892 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2893 	tos.setEncoding(QStringConverter::Utf8);
2894 #else
2895 	tos.setCodec("UTF-8");
2896 #endif
2897 	tos.setGenerateByteOrderMark(true);
2898 
2899 	tos<<writeHead(true, placedActivities, false);
2900 
2901 	QSet<int> tmp;
2902 	tos << singleSubgroupsTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalSubgroups, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES, subgroupsSortedOrder);
2903 
2904 	tos << "  </body>\n</html>\n";
2905 
2906 	if(file.error()>0){
2907 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2908 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2909 	}
2910 	file.close();
2911 }
2912 
2913 // by Volker Dirr
writeSubgroupsTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities,const QList<int> & subgroupsSortedOrder)2914 void TimetableExport::writeSubgroupsTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities, const QList<int>& subgroupsSortedOrder){
2915 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2916 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2917 
2918 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_SUBGROUPS){
2919 		if(QFile::exists(htmlfilename))
2920 			QFile::remove(htmlfilename);
2921 
2922 		return;
2923 	}
2924 
2925 	//Now we print the results to an HTML file
2926 	QFile file(htmlfilename);
2927 	if(!file.open(QIODevice::WriteOnly)){
2928 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2929 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2930 		return;
2931 	}
2932 	QTextStream tos(&file);
2933 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2934 	tos.setEncoding(QStringConverter::Utf8);
2935 #else
2936 	tos.setCodec("UTF-8");
2937 #endif
2938 	tos.setGenerateByteOrderMark(true);
2939 
2940 	tos<<writeHead(true, placedActivities, true);
2941 	tos<<writeTOCDays();
2942 
2943 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
2944 		QSet<int> tmp;
2945 		tos<<singleSubgroupsTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalSubgroups, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES, subgroupsSortedOrder);
2946 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
2947 	}
2948 
2949 	tos << "  </body>\n</html>\n";
2950 
2951 	if(file.error()>0){
2952 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2953 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2954 	}
2955 	file.close();
2956 }
2957 
2958 // by Volker Dirr
writeSubgroupsTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities,const QList<int> & subgroupsSortedOrder)2959 void TimetableExport::writeSubgroupsTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities, const QList<int>& subgroupsSortedOrder){
2960 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
2961 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
2962 
2963 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_SUBGROUPS){
2964 		if(QFile::exists(htmlfilename))
2965 			QFile::remove(htmlfilename);
2966 
2967 		return;
2968 	}
2969 
2970 	//Now we print the results to an HTML file
2971 	QFile file(htmlfilename);
2972 	if(!file.open(QIODevice::WriteOnly)){
2973 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2974 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
2975 		return;
2976 	}
2977 	QTextStream tos(&file);
2978 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
2979 	tos.setEncoding(QStringConverter::Utf8);
2980 #else
2981 	tos.setCodec("UTF-8");
2982 #endif
2983 	tos.setGenerateByteOrderMark(true);
2984 
2985 	tos<<writeHead(true, placedActivities, true);
2986 	tos<<writeTOCDays();
2987 
2988 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
2989 		QSet<int> tmp;
2990 		tos<<singleSubgroupsTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalSubgroups, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES, subgroupsSortedOrder);
2991 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
2992 	}
2993 	tos << "  </body>\n</html>\n";
2994 
2995 	if(file.error()>0){
2996 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
2997 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
2998 	}
2999 	file.close();
3000 }
3001 
3002 //Now print the groups
3003 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeGroupsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3004 void TimetableExport::writeGroupsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3005 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3006 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3007 
3008 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_GROUPS){
3009 		if(QFile::exists(htmlfilename))
3010 			QFile::remove(htmlfilename);
3011 
3012 		return;
3013 	}
3014 
3015 	//Now we print the results to an HTML file
3016 	QFile file(htmlfilename);
3017 	if(!file.open(QIODevice::WriteOnly)){
3018 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3019 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3020 		return;
3021 	}
3022 	QTextStream tos(&file);
3023 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3024 	tos.setEncoding(QStringConverter::Utf8);
3025 #else
3026 	tos.setCodec("UTF-8");
3027 #endif
3028 	tos.setGenerateByteOrderMark(true);
3029 
3030 	tos<<writeHead(true, placedActivities, true);
3031 
3032 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
3033 	tos<<"    <ul>\n";
3034 	for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
3035 		StudentsYear* sty=gt.rules.augmentedYearsList[i];
3036 		tos<<"      <li>\n        "<<TimetableExport::tr("Year")<<" "<<protect2(sty->name)<<"\n        <ul>\n";
3037 		for(int j=0; j<sty->groupsList.size(); j++){
3038 			StudentsGroup* stg=sty->groupsList[j];
3039 			tos<<"          <li>\n            "<<TimetableExport::tr("Group");
3040 			tos<<" <a href=\""<<"#table_"<<hashStudentIDsTimetable.value(stg->name)<<"\">"<<protect2(stg->name)<<"</a>\n";
3041 			tos<<"          </li>\n";
3042 		}
3043 		tos<<"        </ul>\n      </li>\n";
3044 	}
3045 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
3046 
3047 	for(int group=0; group<gt.rules.internalGroupsList.size(); group++){
3048 		tos << singleGroupsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, group, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3049 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3050 	}
3051 
3052 	tos<<"  </body>\n</html>\n";
3053 
3054 	if(file.error()>0){
3055 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3056 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3057 	}
3058 	file.close();
3059 }
3060 
3061 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeGroupsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3062 void TimetableExport::writeGroupsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3063 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3064 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3065 
3066 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_GROUPS){
3067 		if(QFile::exists(htmlfilename))
3068 			QFile::remove(htmlfilename);
3069 
3070 		return;
3071 	}
3072 
3073 	//Now we print the results to an HTML file
3074 	QFile file(htmlfilename);
3075 	if(!file.open(QIODevice::WriteOnly)){
3076 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3077 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3078 		return;
3079 	}
3080 	QTextStream tos(&file);
3081 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3082 	tos.setEncoding(QStringConverter::Utf8);
3083 #else
3084 	tos.setCodec("UTF-8");
3085 #endif
3086 	tos.setGenerateByteOrderMark(true);
3087 
3088 	tos<<writeHead(true, placedActivities, true);
3089 
3090 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
3091 	tos<<"    <ul>\n";
3092 	for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
3093 		StudentsYear* sty=gt.rules.augmentedYearsList[i];
3094 		tos<<"      <li>\n        "<<TimetableExport::tr("Year")<<" "<<protect2(sty->name)<<"\n        <ul>\n";
3095 		for(int j=0; j<sty->groupsList.size(); j++){
3096 			StudentsGroup* stg=sty->groupsList[j];
3097 			tos<<"          <li>\n            "<<TimetableExport::tr("Group");
3098 			tos<<" <a href=\""<<"#table_"<<hashStudentIDsTimetable.value(stg->name)<<"\">"<<protect2(stg->name)<<"</a>\n";
3099 			tos<<"          </li>\n";
3100 		}
3101 		tos<<"        </ul>\n      </li>\n";
3102 	}
3103 	tos<<"    </ul>\n    <p>&nbsp;</p>\n";
3104 
3105 	for(int group=0; group<gt.rules.internalGroupsList.size(); group++){
3106 		tos<<singleGroupsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, group, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3107 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3108 	}
3109 
3110 	tos<<"  </body>\n</html>\n";
3111 
3112 	if(file.error()>0){
3113 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3114 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3115 	}
3116 	file.close();
3117 }
3118 
3119 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeGroupsTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3120 void TimetableExport::writeGroupsTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3121 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3122 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3123 
3124 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_GROUPS){
3125 		if(QFile::exists(htmlfilename))
3126 			QFile::remove(htmlfilename);
3127 
3128 		return;
3129 	}
3130 
3131 	//Now we print the results to an HTML file
3132 	QFile file(htmlfilename);
3133 	if(!file.open(QIODevice::WriteOnly)){
3134 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3135 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3136 		return;
3137 	}
3138 	QTextStream tos(&file);
3139 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3140 	tos.setEncoding(QStringConverter::Utf8);
3141 #else
3142 	tos.setCodec("UTF-8");
3143 #endif
3144 	tos.setGenerateByteOrderMark(true);
3145 
3146 	tos<<writeHead(true, placedActivities, false);
3147 
3148 	QSet<int> tmp;
3149 	tos<<singleGroupsTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.internalGroupsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3150 
3151 	tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3152 
3153 	tos << "  </body>\n</html>\n";
3154 
3155 	if(file.error()>0){
3156 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3157 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3158 	}
3159 	file.close();
3160 }
3161 
3162 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeGroupsTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3163 void TimetableExport::writeGroupsTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3164 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3165 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3166 
3167 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_GROUPS){
3168 		if(QFile::exists(htmlfilename))
3169 			QFile::remove(htmlfilename);
3170 
3171 		return;
3172 	}
3173 
3174 	//Now we print the results to an HTML file
3175 	QFile file(htmlfilename);
3176 	if(!file.open(QIODevice::WriteOnly)){
3177 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3178 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3179 		return;
3180 	}
3181 	QTextStream tos(&file);
3182 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3183 	tos.setEncoding(QStringConverter::Utf8);
3184 #else
3185 	tos.setCodec("UTF-8");
3186 #endif
3187 	tos.setGenerateByteOrderMark(true);
3188 
3189 	tos<<writeHead(true, placedActivities, false);
3190 
3191 	QSet<int> tmp;
3192 	tos<<singleGroupsTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.internalGroupsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3193 	tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3194 
3195 	tos << "  </body>\n</html>\n";
3196 
3197 	if(file.error()>0){
3198 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3199 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3200 	}
3201 	file.close();
3202 }
3203 
3204 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeGroupsTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3205 void TimetableExport::writeGroupsTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3206 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3207 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3208 
3209 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_GROUPS){
3210 		if(QFile::exists(htmlfilename))
3211 			QFile::remove(htmlfilename);
3212 
3213 		return;
3214 	}
3215 
3216 	//Now we print the results to an HTML file
3217 	QFile file(htmlfilename);
3218 	if(!file.open(QIODevice::WriteOnly)){
3219 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3220 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3221 		return;
3222 	}
3223 	QTextStream tos(&file);
3224 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3225 	tos.setEncoding(QStringConverter::Utf8);
3226 #else
3227 	tos.setCodec("UTF-8");
3228 #endif
3229 	tos.setGenerateByteOrderMark(true);
3230 
3231 	tos<<writeHead(true, placedActivities, true);
3232 	tos<<writeTOCDays();
3233 
3234 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
3235 		QSet<int> tmp;
3236 		tos<<singleGroupsTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.internalGroupsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3237 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3238 	}
3239 
3240 	tos << "  </body>\n</html>\n";
3241 
3242 	if(file.error()>0){
3243 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3244 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3245 	}
3246 	file.close();
3247 }
3248 
3249 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeGroupsTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3250 void TimetableExport::writeGroupsTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3251 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3252 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3253 
3254 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_GROUPS){
3255 		if(QFile::exists(htmlfilename))
3256 			QFile::remove(htmlfilename);
3257 
3258 		return;
3259 	}
3260 
3261 	//Now we print the results to an HTML file
3262 	QFile file(htmlfilename);
3263 	if(!file.open(QIODevice::WriteOnly)){
3264 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3265 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3266 		return;
3267 	}
3268 	QTextStream tos(&file);
3269 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3270 	tos.setEncoding(QStringConverter::Utf8);
3271 #else
3272 	tos.setCodec("UTF-8");
3273 #endif
3274 	tos.setGenerateByteOrderMark(true);
3275 
3276 	tos<<writeHead(true, placedActivities, true);
3277 	tos<<writeTOCDays();
3278 
3279 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
3280 		QSet<int> tmp;
3281 		tos<<singleGroupsTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.internalGroupsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3282 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3283 	}
3284 
3285 	tos << "  </body>\n</html>\n";
3286 
3287 	if(file.error()>0){
3288 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3289 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3290 	}
3291 	file.close();
3292 }
3293 
3294 //Now print the years
3295 
3296 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeYearsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3297 void TimetableExport::writeYearsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3298 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3299 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3300 
3301 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_YEARS){
3302 		if(QFile::exists(htmlfilename))
3303 			QFile::remove(htmlfilename);
3304 
3305 		return;
3306 	}
3307 
3308 	//Now we print the results to an HTML file
3309 	QFile file(htmlfilename);
3310 	if(!file.open(QIODevice::WriteOnly)){
3311 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3312 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3313 		return;
3314 	}
3315 	QTextStream tos(&file);
3316 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3317 	tos.setEncoding(QStringConverter::Utf8);
3318 #else
3319 	tos.setCodec("UTF-8");
3320 #endif
3321 	tos.setGenerateByteOrderMark(true);
3322 
3323 	tos<<writeHead(true, placedActivities, true);
3324 
3325 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
3326 	tos<<"    <ul>\n";
3327 	for(int year=0; year<gt.rules.augmentedYearsList.size(); year++){
3328 		StudentsYear* sty=gt.rules.augmentedYearsList[year];
3329 		tos<<"      <li>\n        "<<TimetableExport::tr("Year");
3330 		tos<<" <a href=\""<<"#table_"<<hashStudentIDsTimetable.value(sty->name)<<"\">"<<protect2(sty->name)<<"</a>\n";
3331 		tos<<"      </li>\n";
3332 	}
3333 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
3334 
3335 	for(int year=0; year<gt.rules.augmentedYearsList.size(); year++){
3336 		tos << singleYearsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, year, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3337 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3338 	}
3339 
3340 	tos<<"  </body>\n</html>\n";
3341 
3342 	if(file.error()>0){
3343 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3344 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3345 	}
3346 	file.close();
3347 }
3348 
3349 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeYearsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3350 void TimetableExport::writeYearsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3351 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3352 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3353 
3354 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_YEARS){
3355 		if(QFile::exists(htmlfilename))
3356 			QFile::remove(htmlfilename);
3357 
3358 		return;
3359 	}
3360 
3361 	//Now we print the results to an HTML file
3362 	QFile file(htmlfilename);
3363 	if(!file.open(QIODevice::WriteOnly)){
3364 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3365 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3366 		return;
3367 	}
3368 	QTextStream tos(&file);
3369 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3370 	tos.setEncoding(QStringConverter::Utf8);
3371 #else
3372 	tos.setCodec("UTF-8");
3373 #endif
3374 	tos.setGenerateByteOrderMark(true);
3375 
3376 	tos<<writeHead(true, placedActivities, true);
3377 
3378 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
3379 	tos<<"    <ul>\n";
3380 	for(int year=0; year<gt.rules.augmentedYearsList.size(); year++){
3381 		StudentsYear* sty=gt.rules.augmentedYearsList[year];
3382 		tos<<"      <li>\n        "<<TimetableExport::tr("Year");
3383 		tos<<" <a href=\""<<"#table_"<<hashStudentIDsTimetable.value(sty->name)<<"\">"<<protect2(sty->name)<<"</a>\n";
3384 		tos<<"      </li>\n";
3385 	}
3386 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
3387 
3388 	for(int year=0; year<gt.rules.augmentedYearsList.size(); year++){
3389 		tos << singleYearsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, year, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3390 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3391 	}
3392 
3393 	tos<<"  </body>\n</html>\n";
3394 
3395 	if(file.error()>0){
3396 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3397 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3398 	}
3399 	file.close();
3400 }
3401 
3402 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeYearsTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3403 void TimetableExport::writeYearsTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3404 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3405 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3406 
3407 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_YEARS){
3408 		if(QFile::exists(htmlfilename))
3409 			QFile::remove(htmlfilename);
3410 
3411 		return;
3412 	}
3413 
3414 	//Now we print the results to an HTML file
3415 	QFile file(htmlfilename);
3416 	if(!file.open(QIODevice::WriteOnly)){
3417 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3418 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3419 		return;
3420 	}
3421 	QTextStream tos(&file);
3422 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3423 	tos.setEncoding(QStringConverter::Utf8);
3424 #else
3425 	tos.setCodec("UTF-8");
3426 #endif
3427 	tos.setGenerateByteOrderMark(true);
3428 
3429 	tos<<writeHead(true, placedActivities, false);
3430 
3431 	QSet<int> tmp;
3432 	tos<<singleYearsTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.augmentedYearsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3433 
3434 	tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3435 
3436 	tos << "  </body>\n</html>\n";
3437 
3438 	if(file.error()>0){
3439 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3440 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3441 	}
3442 	file.close();
3443 }
3444 
3445 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeYearsTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3446 void TimetableExport::writeYearsTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3447 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3448 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3449 
3450 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_YEARS){
3451 		if(QFile::exists(htmlfilename))
3452 			QFile::remove(htmlfilename);
3453 
3454 		return;
3455 	}
3456 
3457 	//Now we print the results to an HTML file
3458 	QFile file(htmlfilename);
3459 	if(!file.open(QIODevice::WriteOnly)){
3460 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3461 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3462 		return;
3463 	}
3464 	QTextStream tos(&file);
3465 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3466 	tos.setEncoding(QStringConverter::Utf8);
3467 #else
3468 	tos.setCodec("UTF-8");
3469 #endif
3470 	tos.setGenerateByteOrderMark(true);
3471 
3472 	tos<<writeHead(true, placedActivities, false);
3473 
3474 	QSet<int> tmp;
3475 	tos<<singleYearsTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.augmentedYearsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3476 
3477 	tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3478 
3479 	tos << "  </body>\n</html>\n";
3480 
3481 	if(file.error()>0){
3482 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3483 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3484 	}
3485 	file.close();
3486 }
3487 
3488 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeYearsTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3489 void TimetableExport::writeYearsTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3490 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3491 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3492 
3493 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_YEARS){
3494 		if(QFile::exists(htmlfilename))
3495 			QFile::remove(htmlfilename);
3496 
3497 		return;
3498 	}
3499 
3500 	//Now we print the results to an HTML file
3501 	QFile file(htmlfilename);
3502 	if(!file.open(QIODevice::WriteOnly)){
3503 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3504 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3505 		return;
3506 	}
3507 	QTextStream tos(&file);
3508 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3509 	tos.setEncoding(QStringConverter::Utf8);
3510 #else
3511 	tos.setCodec("UTF-8");
3512 #endif
3513 	tos.setGenerateByteOrderMark(true);
3514 
3515 	tos<<writeHead(true, placedActivities, true);
3516 	tos<<writeTOCDays();
3517 
3518 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
3519 		QSet<int> tmp;
3520 		tos<<singleYearsTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.augmentedYearsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3521 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3522 	}
3523 
3524 	tos << "  </body>\n</html>\n";
3525 
3526 	if(file.error()>0){
3527 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3528 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3529 	}
3530 	file.close();
3531 }
3532 
3533 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeYearsTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3534 void TimetableExport::writeYearsTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3535 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3536 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3537 
3538 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_YEARS){
3539 		if(QFile::exists(htmlfilename))
3540 			QFile::remove(htmlfilename);
3541 
3542 		return;
3543 	}
3544 
3545 	//Now we print the results to an HTML file
3546 	QFile file(htmlfilename);
3547 	if(!file.open(QIODevice::WriteOnly)){
3548 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3549 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3550 		return;
3551 	}
3552 	QTextStream tos(&file);
3553 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3554 	tos.setEncoding(QStringConverter::Utf8);
3555 #else
3556 	tos.setCodec("UTF-8");
3557 #endif
3558 	tos.setGenerateByteOrderMark(true);
3559 
3560 	tos<<writeHead(true, placedActivities, true);
3561 	tos<<writeTOCDays();
3562 
3563 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
3564 		QSet<int> tmp;
3565 		tos<<singleYearsTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.augmentedYearsList.size(), tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, PRINT_DETAILED_HTML_TIMETABLES, TIMETABLE_HTML_REPEAT_NAMES);
3566 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3567 	}
3568 
3569 	tos << "  </body>\n</html>\n";
3570 
3571 	if(file.error()>0){
3572 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3573 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3574 	}
3575 	file.close();
3576 }
3577 
3578 //Print all activities
3579 
3580 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeAllActivitiesTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3581 void TimetableExport::writeAllActivitiesTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3582 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3583 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3584 
3585 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_ACTIVITIES){
3586 		if(QFile::exists(htmlfilename))
3587 			QFile::remove(htmlfilename);
3588 
3589 		return;
3590 	}
3591 
3592 	//Now we print the results to an HTML file
3593 	QFile file(htmlfilename);
3594 	if(!file.open(QIODevice::WriteOnly)){
3595 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3596 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3597 		return;
3598 	}
3599 	QTextStream tos(&file);
3600 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3601 	tos.setEncoding(QStringConverter::Utf8);
3602 #else
3603 	tos.setCodec("UTF-8");
3604 #endif
3605 	tos.setGenerateByteOrderMark(true);
3606 
3607 	tos<<writeHead(true, placedActivities, true);
3608 	tos<<singleAllActivitiesTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3609 	tos<<"  </body>\n</html>\n";
3610 
3611 	if(file.error()>0){
3612 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3613 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3614 	}
3615 	file.close();
3616 }
3617 
3618 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeAllActivitiesTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3619 void TimetableExport::writeAllActivitiesTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3620 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3621 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3622 
3623 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_ACTIVITIES){
3624 		if(QFile::exists(htmlfilename))
3625 			QFile::remove(htmlfilename);
3626 
3627 		return;
3628 	}
3629 
3630 	//Now we print the results to an HTML file
3631 	QFile file(htmlfilename);
3632 	if(!file.open(QIODevice::WriteOnly)){
3633 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3634 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3635 		return;
3636 	}
3637 	QTextStream tos(&file);
3638 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3639 	tos.setEncoding(QStringConverter::Utf8);
3640 #else
3641 	tos.setCodec("UTF-8");
3642 #endif
3643 	tos.setGenerateByteOrderMark(true);
3644 
3645 	tos<<writeHead(true, placedActivities, true);
3646 	tos<<singleAllActivitiesTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3647 	tos<<"  </body>\n</html>\n";
3648 
3649 	if(file.error()>0){
3650 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3651 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3652 	}
3653 	file.close();
3654 }
3655 
3656 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeAllActivitiesTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3657 void TimetableExport::writeAllActivitiesTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3658 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3659 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3660 
3661 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_ACTIVITIES){
3662 		if(QFile::exists(htmlfilename))
3663 			QFile::remove(htmlfilename);
3664 
3665 		return;
3666 	}
3667 
3668 	//Now we print the results to an HTML file
3669 	QFile file(htmlfilename);
3670 	if(!file.open(QIODevice::WriteOnly)){
3671 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3672 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3673 		return;
3674 	}
3675 	QTextStream tos(&file);
3676 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3677 	tos.setEncoding(QStringConverter::Utf8);
3678 #else
3679 	tos.setCodec("UTF-8");
3680 #endif
3681 	tos.setGenerateByteOrderMark(true);
3682 
3683 	tos<<writeHead(true, placedActivities, false);
3684 
3685 	tos<<singleAllActivitiesTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3686 
3687 	tos<<"  </body>\n</html>\n";
3688 
3689 	if(file.error()>0){
3690 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3691 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3692 	}
3693 	file.close();
3694 }
3695 
3696 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeAllActivitiesTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3697 void TimetableExport::writeAllActivitiesTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3698 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3699 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3700 
3701 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_ACTIVITIES){
3702 		if(QFile::exists(htmlfilename))
3703 			QFile::remove(htmlfilename);
3704 
3705 		return;
3706 	}
3707 
3708 	//Now we print the results to an HTML file
3709 	QFile file(htmlfilename);
3710 	if(!file.open(QIODevice::WriteOnly)){
3711 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3712 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3713 		return;
3714 	}
3715 	QTextStream tos(&file);
3716 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3717 	tos.setEncoding(QStringConverter::Utf8);
3718 #else
3719 	tos.setCodec("UTF-8");
3720 #endif
3721 	tos.setGenerateByteOrderMark(true);
3722 
3723 	tos<<writeHead(true, placedActivities, false);
3724 
3725 	tos<<singleAllActivitiesTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3726 
3727 	tos<<"  </body>\n</html>\n";
3728 
3729 	if(file.error()>0){
3730 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3731 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3732 	}
3733 	file.close();
3734 }
3735 
3736 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeAllActivitiesTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3737 void TimetableExport::writeAllActivitiesTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3738 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3739 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3740 
3741 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_ACTIVITIES){
3742 		if(QFile::exists(htmlfilename))
3743 			QFile::remove(htmlfilename);
3744 
3745 		return;
3746 	}
3747 
3748 	//Now we print the results to an HTML file
3749 	QFile file(htmlfilename);
3750 	if(!file.open(QIODevice::WriteOnly)){
3751 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3752 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3753 		return;
3754 	}
3755 	QTextStream tos(&file);
3756 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3757 	tos.setEncoding(QStringConverter::Utf8);
3758 #else
3759 	tos.setCodec("UTF-8");
3760 #endif
3761 	tos.setGenerateByteOrderMark(true);
3762 
3763 	tos<<writeHead(true, placedActivities, true);
3764 	tos<<writeTOCDays();
3765 
3766 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
3767 		tos<<singleAllActivitiesTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3768 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3769 	}
3770 	tos<<"  </body>\n</html>\n";
3771 
3772 	if(file.error()>0){
3773 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3774 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3775 	}
3776 	file.close();
3777 }
3778 
3779 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeAllActivitiesTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3780 void TimetableExport::writeAllActivitiesTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3781 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3782 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3783 
3784 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_ACTIVITIES){
3785 		if(QFile::exists(htmlfilename))
3786 			QFile::remove(htmlfilename);
3787 
3788 		return;
3789 	}
3790 
3791 	//Now we print the results to an HTML file
3792 	QFile file(htmlfilename);
3793 	if(!file.open(QIODevice::WriteOnly)){
3794 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3795 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3796 		return;
3797 	}
3798 	QTextStream tos(&file);
3799 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3800 	tos.setEncoding(QStringConverter::Utf8);
3801 #else
3802 	tos.setCodec("UTF-8");
3803 #endif
3804 	tos.setGenerateByteOrderMark(true);
3805 
3806 	tos<<writeHead(true, placedActivities, true);
3807 	tos<<writeTOCDays();
3808 
3809 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
3810 		tos<<singleAllActivitiesTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3811 
3812 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3813 	}
3814 
3815 	tos<<"  </body>\n</html>\n";
3816 
3817 	if(file.error()>0){
3818 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3819 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3820 	}
3821 	file.close();
3822 }
3823 
3824 //Print the teachers
3825 
3826 //XHTML generation code modified by Volker Dirr (timetabling.de) from old html generation code
writeTeachersTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3827 void TimetableExport::writeTeachersTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3828 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3829 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3830 
3831 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_TEACHERS){
3832 		if(QFile::exists(htmlfilename))
3833 			QFile::remove(htmlfilename);
3834 
3835 		return;
3836 	}
3837 
3838 	//Now we print the results to an HTML file
3839 	QFile file(htmlfilename);
3840 	if(!file.open(QIODevice::WriteOnly)){
3841 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3842 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3843 		return;
3844 	}
3845 	QTextStream tos(&file);
3846 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3847 	tos.setEncoding(QStringConverter::Utf8);
3848 #else
3849 	tos.setCodec("UTF-8");
3850 #endif
3851 	tos.setGenerateByteOrderMark(true);
3852 
3853 	tos<<writeHead(true, placedActivities, true);
3854 
3855 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
3856 	tos<<"    <ul>\n";
3857 	for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
3858 		QString teacher_name = gt.rules.internalTeachersList[teacher]->name;
3859 		tos<<"      <li><a href=\""<<"#table_"<<hashTeacherIDsTimetable.value(teacher_name)<<"\">"<<protect2(teacher_name)<<"</a></li>\n";
3860 	}
3861 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
3862 
3863 	for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
3864 		tos<<singleTeachersTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, teacher, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3865 
3866 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3867 	}
3868 	tos<<"  </body>\n</html>\n";
3869 
3870 	if(file.error()>0){
3871 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3872 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3873 	}
3874 	file.close();
3875 }
3876 
3877 //XHTML generation code modified by Volker Dirr (timetabling.de) from old html generation code
writeTeachersTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3878 void TimetableExport::writeTeachersTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3879 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3880 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3881 
3882 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_TEACHERS){
3883 		if(QFile::exists(htmlfilename))
3884 			QFile::remove(htmlfilename);
3885 
3886 		return;
3887 	}
3888 
3889 	//Now we print the results to an HTML file
3890 	QFile file(htmlfilename);
3891 	if(!file.open(QIODevice::WriteOnly)){
3892 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3893 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3894 		return;
3895 	}
3896 	QTextStream tos(&file);
3897 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3898 	tos.setEncoding(QStringConverter::Utf8);
3899 #else
3900 	tos.setCodec("UTF-8");
3901 #endif
3902 	tos.setGenerateByteOrderMark(true);
3903 
3904 	tos<<writeHead(true, placedActivities, true);
3905 
3906 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
3907 	tos<<"    <ul>\n";
3908 	for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
3909 		QString teacher_name = gt.rules.internalTeachersList[teacher]->name;
3910 		tos<<"      <li><a href=\""<<"#table_"<<hashTeacherIDsTimetable.value(teacher_name)<<"\">"<<protect2(teacher_name)<<"</a></li>\n";
3911 	}
3912 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
3913 
3914 	for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
3915 		tos<<singleTeachersTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, teacher, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3916 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
3917 	}
3918 	tos<<"  </body>\n</html>\n";
3919 
3920 	if(file.error()>0){
3921 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3922 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3923 	}
3924 	file.close();
3925 }
3926 
3927 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeTeachersTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3928 void TimetableExport::writeTeachersTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3929 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3930 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3931 
3932 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_TEACHERS){
3933 		if(QFile::exists(htmlfilename))
3934 			QFile::remove(htmlfilename);
3935 
3936 		return;
3937 	}
3938 
3939 	//Now we print the results to an HTML file
3940 	QFile file(htmlfilename);
3941 	if(!file.open(QIODevice::WriteOnly)){
3942 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3943 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3944 		return;
3945 	}
3946 	QTextStream tos(&file);
3947 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3948 	tos.setEncoding(QStringConverter::Utf8);
3949 #else
3950 	tos.setCodec("UTF-8");
3951 #endif
3952 	tos.setGenerateByteOrderMark(true);
3953 
3954 	tos<<writeHead(true, placedActivities, false);
3955 	QSet<int> tmp;
3956 	tos<<singleTeachersTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalTeachers, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3957 	tos << "  </body>\n</html>\n";
3958 
3959 	if(file.error()>0){
3960 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3961 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
3962 	}
3963 	file.close();
3964 }
3965 
3966 //XHTML generation code modified by Volker Dirr (timetabling.de) from old html generation code
writeTeachersTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)3967 void TimetableExport::writeTeachersTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
3968 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
3969 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
3970 
3971 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_TEACHERS){
3972 		if(QFile::exists(htmlfilename))
3973 			QFile::remove(htmlfilename);
3974 
3975 		return;
3976 	}
3977 
3978 	//Now we print the results to an HTML file
3979 	QFile file(htmlfilename);
3980 	if(!file.open(QIODevice::WriteOnly)){
3981 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
3982 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
3983 		return;
3984 	}
3985 	QTextStream tos(&file);
3986 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
3987 	tos.setEncoding(QStringConverter::Utf8);
3988 #else
3989 	tos.setCodec("UTF-8");
3990 #endif
3991 	tos.setGenerateByteOrderMark(true);
3992 
3993 	tos<<writeHead(true, placedActivities, false);
3994 	QSet<int> tmp;
3995 	tos<<singleTeachersTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalTeachers, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
3996 	tos << "  </body>\n</html>\n";
3997 
3998 	if(file.error()>0){
3999 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4000 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4001 	}
4002 	file.close();
4003 }
4004 
4005 //by Volker Dirr
writeTeachersTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4006 void TimetableExport::writeTeachersTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4007 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4008 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4009 
4010 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_TEACHERS){
4011 		if(QFile::exists(htmlfilename))
4012 			QFile::remove(htmlfilename);
4013 
4014 		return;
4015 	}
4016 
4017 	//Now we print the results to an HTML file
4018 	QFile file(htmlfilename);
4019 	if(!file.open(QIODevice::WriteOnly)){
4020 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4021 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4022 		return;
4023 	}
4024 	QTextStream tos(&file);
4025 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4026 	tos.setEncoding(QStringConverter::Utf8);
4027 #else
4028 	tos.setCodec("UTF-8");
4029 #endif
4030 	tos.setGenerateByteOrderMark(true);
4031 
4032 	tos<<writeHead(true, placedActivities, true);
4033 	tos<<writeTOCDays();
4034 
4035 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4036 		QSet<int> tmp;
4037 		tos<<singleTeachersTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalTeachers, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4038 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4039 	}
4040 	tos << "  </body>\n</html>\n";
4041 
4042 	if(file.error()>0){
4043 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4044 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4045 	}
4046 	file.close();
4047 }
4048 
4049 //by Volker Dirr
writeTeachersTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4050 void TimetableExport::writeTeachersTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4051 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4052 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4053 
4054 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_TEACHERS){
4055 		if(QFile::exists(htmlfilename))
4056 			QFile::remove(htmlfilename);
4057 
4058 		return;
4059 	}
4060 
4061 	//Now we print the results to an HTML file
4062 	QFile file(htmlfilename);
4063 	if(!file.open(QIODevice::WriteOnly)){
4064 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4065 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4066 		return;
4067 	}
4068 	QTextStream tos(&file);
4069 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4070 	tos.setEncoding(QStringConverter::Utf8);
4071 #else
4072 	tos.setCodec("UTF-8");
4073 #endif
4074 	tos.setGenerateByteOrderMark(true);
4075 
4076 	tos<<writeHead(true, placedActivities, true);
4077 	tos<<writeTOCDays();
4078 
4079 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4080 		QSet<int> tmp;
4081 		tos<<singleTeachersTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalTeachers, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4082 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4083 	}
4084 
4085 	tos << "  </body>\n</html>\n";
4086 
4087 	if(file.error()>0){
4088 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4089 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4090 	}
4091 	file.close();
4092 }
4093 
4094 //writing the rooms' timetable html format to a file by Volker Dirr
writeRoomsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4095 void TimetableExport::writeRoomsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4096 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4097 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4098 
4099 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_ROOMS){
4100 		if(QFile::exists(htmlfilename))
4101 			QFile::remove(htmlfilename);
4102 
4103 		return;
4104 	}
4105 
4106 	//Now we print the results to an HTML file
4107 	QFile file(htmlfilename);
4108 	if(!file.open(QIODevice::WriteOnly)){
4109 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4110 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4111 		return;
4112 	}
4113 	QTextStream tos(&file);
4114 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4115 	tos.setEncoding(QStringConverter::Utf8);
4116 #else
4117 	tos.setCodec("UTF-8");
4118 #endif
4119 	tos.setGenerateByteOrderMark(true);
4120 
4121 	tos<<writeHead(true, placedActivities, true);
4122 
4123 	if(gt.rules.nInternalRooms==0)
4124 		tos<<"    <h1>"<<TimetableExport::tr("No rooms recorded in FET for %1.", "%1 is the institution name").arg(protect2(gt.rules.institutionName))<<"</h1>\n";
4125 	else {
4126 		tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
4127 		tos<<"    <ul>\n";
4128 		for(int room=0; room<gt.rules.nInternalRooms; room++){
4129 			QString room_name = gt.rules.internalRoomsList[room]->name;
4130 			tos<<"      <li><a href=\""<<"#table_"<<hashRoomIDsTimetable.value(room_name)<<"\">"<<protect2(room_name)<<"</a></li>\n";
4131 		}
4132 		tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
4133 
4134 		for(int room=0; room<gt.rules.nInternalRooms; room++){
4135 			tos<<singleRoomsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, room, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4136 			tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4137 		}
4138 	}
4139 	tos<<"  </body>\n</html>\n";
4140 
4141 	if(file.error()>0){
4142 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4143 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4144 	}
4145 	file.close();
4146 }
4147 
4148 //writing the rooms' timetable html format to a file by Volker Dirr
writeRoomsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4149 void TimetableExport::writeRoomsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4150 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4151 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4152 
4153 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_ROOMS){
4154 		if(QFile::exists(htmlfilename))
4155 			QFile::remove(htmlfilename);
4156 
4157 		return;
4158 	}
4159 
4160 	//Now we print the results to an HTML file
4161 	QFile file(htmlfilename);
4162 	if(!file.open(QIODevice::WriteOnly)){
4163 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4164 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4165 		return;
4166 
4167 		assert(0);
4168 	}
4169 	QTextStream tos(&file);
4170 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4171 	tos.setEncoding(QStringConverter::Utf8);
4172 #else
4173 	tos.setCodec("UTF-8");
4174 #endif
4175 	tos.setGenerateByteOrderMark(true);
4176 
4177 	tos<<writeHead(true, placedActivities, true);
4178 
4179 	if(gt.rules.nInternalRooms==0)
4180 		tos<<"    <h1>"<<TimetableExport::tr("No rooms recorded in FET for %1.", "%1 is the institution name").arg(protect2(gt.rules.institutionName))<<"</h1>\n";
4181 	else {
4182 		tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
4183 		tos<<"    <ul>\n";
4184 		for(int room=0; room<gt.rules.nInternalRooms; room++){
4185 			QString room_name = gt.rules.internalRoomsList[room]->name;
4186 			tos<<"      <li><a href=\""<<"#table_"<<hashRoomIDsTimetable.value(room_name)<<"\">"<<protect2(room_name)<<"</a></li>\n";
4187 		}
4188 		tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
4189 
4190 		for(int room=0; room<gt.rules.nInternalRooms; room++){
4191 			tos<<singleRoomsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, room, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4192 			tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4193 		}
4194 	}
4195 	tos<<"  </body>\n</html>\n";
4196 
4197 	if(file.error()>0){
4198 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4199 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4200 	}
4201 	file.close();
4202 }
4203 
4204 //writing the rooms' timetable html format to a file by Volker Dirr
writeRoomsTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4205 void TimetableExport::writeRoomsTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4206 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4207 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4208 
4209 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_ROOMS){
4210 		if(QFile::exists(htmlfilename))
4211 			QFile::remove(htmlfilename);
4212 
4213 		return;
4214 	}
4215 
4216 	//Now we print the results to an HTML file
4217 	QFile file(htmlfilename);
4218 	if(!file.open(QIODevice::WriteOnly)){
4219 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4220 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4221 		return;
4222 	}
4223 	QTextStream tos(&file);
4224 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4225 	tos.setEncoding(QStringConverter::Utf8);
4226 #else
4227 	tos.setCodec("UTF-8");
4228 #endif
4229 	tos.setGenerateByteOrderMark(true);
4230 
4231 	tos<<writeHead(true, placedActivities, false);
4232 
4233 	if(gt.rules.nInternalRooms==0)
4234 		tos<<"    <h1>"<<TimetableExport::tr("No rooms recorded in FET for %1.", "%1 is the institution name").arg(protect2(gt.rules.institutionName))<<"</h1>\n";
4235 	else {
4236 		QSet<int> tmp;
4237 		tos<<singleRoomsTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalRooms, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4238 	}
4239 	tos << "  </body>\n</html>\n";
4240 
4241 	if(file.error()>0){
4242 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4243 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4244 	}
4245 	file.close();
4246 }
4247 
4248 // writing the rooms' timetable html format to a file by Volker Dirr
writeRoomsTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4249 void TimetableExport::writeRoomsTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4250 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4251 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4252 
4253 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_ROOMS){
4254 		if(QFile::exists(htmlfilename))
4255 			QFile::remove(htmlfilename);
4256 
4257 		return;
4258 	}
4259 
4260 	//Now we print the results to an HTML file
4261 	QFile file(htmlfilename);
4262 	if(!file.open(QIODevice::WriteOnly)){
4263 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4264 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4265 		return;
4266 	}
4267 	QTextStream tos(&file);
4268 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4269 	tos.setEncoding(QStringConverter::Utf8);
4270 #else
4271 	tos.setCodec("UTF-8");
4272 #endif
4273 	tos.setGenerateByteOrderMark(true);
4274 
4275 	tos<<writeHead(true, placedActivities, false);
4276 
4277 	if(gt.rules.nInternalRooms==0)
4278 		tos<<"    <h1>"<<TimetableExport::tr("No rooms recorded in FET for %1.", "%1 is the institution name").arg(protect2(gt.rules.institutionName))<<"</h1>\n";
4279 	else {
4280 		QSet<int> tmp;
4281 		tos<<singleRoomsTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalRooms, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4282 
4283 	}
4284 	tos << "  </body>\n</html>\n";
4285 
4286 	if(file.error()>0){
4287 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4288 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4289 	}
4290 	file.close();
4291 }
4292 
4293 //by Volker Dirr
writeRoomsTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4294 void TimetableExport::writeRoomsTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4295 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4296 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4297 
4298 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_ROOMS){
4299 		if(QFile::exists(htmlfilename))
4300 			QFile::remove(htmlfilename);
4301 
4302 		return;
4303 	}
4304 
4305 	//Now we print the results to an HTML file
4306 	QFile file(htmlfilename);
4307 	if(!file.open(QIODevice::WriteOnly)){
4308 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4309 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4310 		return;
4311 	}
4312 	QTextStream tos(&file);
4313 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4314 	tos.setEncoding(QStringConverter::Utf8);
4315 #else
4316 	tos.setCodec("UTF-8");
4317 #endif
4318 	tos.setGenerateByteOrderMark(true);
4319 
4320 	tos<<writeHead(true, placedActivities, true);
4321 	tos<<writeTOCDays();
4322 
4323 	if(gt.rules.nInternalRooms==0)
4324 		tos<<"    <h1>"<<TimetableExport::tr("No rooms recorded in FET for %1.", "%1 is the institution name").arg(protect2(gt.rules.institutionName))<<"</h1>\n";
4325 	else {
4326 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4327 			QSet<int> tmp;
4328 			tos<<singleRoomsTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalRooms, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4329 
4330 			tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4331 		}
4332 	}
4333 	tos << "  </body>\n</html>\n";
4334 
4335 	if(file.error()>0){
4336 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4337 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4338 	}
4339 	file.close();
4340 }
4341 
4342 //by Volker Dirr
writeRoomsTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4343 void TimetableExport::writeRoomsTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4344 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4345 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4346 
4347 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_ROOMS){
4348 		if(QFile::exists(htmlfilename))
4349 			QFile::remove(htmlfilename);
4350 
4351 		return;
4352 	}
4353 
4354 	//Now we print the results to an HTML file
4355 	QFile file(htmlfilename);
4356 	if(!file.open(QIODevice::WriteOnly)){
4357 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4358 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4359 		return;
4360 	}
4361 	QTextStream tos(&file);
4362 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4363 	tos.setEncoding(QStringConverter::Utf8);
4364 #else
4365 	tos.setCodec("UTF-8");
4366 #endif
4367 	tos.setGenerateByteOrderMark(true);
4368 
4369 	tos<<writeHead(true, placedActivities, true);
4370 	tos<<writeTOCDays();
4371 
4372 	if(gt.rules.nInternalRooms==0)
4373 		tos<<"    <h1>"<<TimetableExport::tr("No rooms recorded in FET for %1.", "%1 is the institution name").arg(protect2(gt.rules.institutionName))<<"</h1>\n";
4374 	else {
4375 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4376 			QSet<int> tmp;
4377 			tos<<singleRoomsTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalRooms, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4378 
4379 			tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4380 		}
4381 
4382 	}
4383 	tos << "  </body>\n</html>\n";
4384 
4385 	if(file.error()>0){
4386 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4387 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4388 	}
4389 	file.close();
4390 }
4391 
4392 //Print the subjects
4393 
4394 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubjectsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4395 void TimetableExport::writeSubjectsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4396 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4397 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4398 
4399 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_SUBJECTS){
4400 		if(QFile::exists(htmlfilename))
4401 			QFile::remove(htmlfilename);
4402 
4403 		return;
4404 	}
4405 
4406 	//Now we print the results to an HTML file
4407 	QFile file(htmlfilename);
4408 	if(!file.open(QIODevice::WriteOnly)){
4409 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4410 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4411 		return;
4412 	}
4413 	QTextStream tos(&file);
4414 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4415 	tos.setEncoding(QStringConverter::Utf8);
4416 #else
4417 	tos.setCodec("UTF-8");
4418 #endif
4419 	tos.setGenerateByteOrderMark(true);
4420 
4421 	tos<<writeHead(true, placedActivities, true);
4422 
4423 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
4424 	tos<<"    <ul>\n";
4425 	for(int i=0; i<gt.rules.nInternalSubjects; i++){
4426 		tos<<"      <li>\n        "<<TimetableExport::tr("Subject");
4427 		tos<<" <a href=\""<<"#table_"<<hashSubjectIDsTimetable.value(gt.rules.internalSubjectsList[i]->name)<<"\">"<<gt.rules.internalSubjectsList[i]->name<<"</a>\n";
4428 		tos<<"      </li>\n";
4429 	}
4430 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
4431 
4432 
4433 	for(int subject=0; subject<gt.rules.nInternalSubjects; subject++){
4434 		tos<<singleSubjectsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, subject, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4435 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4436 	}
4437 	tos<<"  </body>\n</html>\n";
4438 
4439 	if(file.error()>0){
4440 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4441 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4442 	}
4443 	file.close();
4444 }
4445 
4446 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubjectsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4447 void TimetableExport::writeSubjectsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4448 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4449 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4450 
4451 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_SUBJECTS){
4452 		if(QFile::exists(htmlfilename))
4453 			QFile::remove(htmlfilename);
4454 
4455 		return;
4456 	}
4457 
4458 	//Now we print the results to an HTML file
4459 	QFile file(htmlfilename);
4460 	if(!file.open(QIODevice::WriteOnly)){
4461 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4462 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4463 		return;
4464 	}
4465 	QTextStream tos(&file);
4466 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4467 	tos.setEncoding(QStringConverter::Utf8);
4468 #else
4469 	tos.setCodec("UTF-8");
4470 #endif
4471 	tos.setGenerateByteOrderMark(true);
4472 
4473 	tos<<writeHead(true, placedActivities, true);
4474 
4475 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
4476 	tos<<"    <ul>\n";
4477 	for(int i=0; i<gt.rules.nInternalSubjects; i++){
4478 		tos<<"      <li>\n        "<<TimetableExport::tr("Subject");
4479 		tos<<" <a href=\""<<"#table_"<<hashSubjectIDsTimetable.value(gt.rules.internalSubjectsList[i]->name)<<"\">"<<gt.rules.internalSubjectsList[i]->name<<"</a>\n";
4480 		tos<<"      </li>\n";
4481 	}
4482 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
4483 
4484 	for(int subject=0; subject<gt.rules.nInternalSubjects; subject++){
4485 		tos<<singleSubjectsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, subject, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4486 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4487 	}
4488 	tos << "  </body>\n</html>\n";
4489 
4490 	if(file.error()>0){
4491 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4492 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4493 	}
4494 	file.close();
4495 }
4496 
4497 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubjectsTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4498 void TimetableExport::writeSubjectsTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4499 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4500 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4501 
4502 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_SUBJECTS){
4503 		if(QFile::exists(htmlfilename))
4504 			QFile::remove(htmlfilename);
4505 
4506 		return;
4507 	}
4508 
4509 	//Now we print the results to an HTML file
4510 	QFile file(htmlfilename);
4511 	if(!file.open(QIODevice::WriteOnly)){
4512 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4513 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4514 		return;
4515 	}
4516 	QTextStream tos(&file);
4517 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4518 	tos.setEncoding(QStringConverter::Utf8);
4519 #else
4520 	tos.setCodec("UTF-8");
4521 #endif
4522 	tos.setGenerateByteOrderMark(true);
4523 
4524 	tos<<writeHead(true, placedActivities, false);
4525 
4526 	QSet<int> tmp;
4527 	tos<<singleSubjectsTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalSubjects, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4528 	tos << "  </body>\n</html>\n";
4529 
4530 	if(file.error()>0){
4531 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4532 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4533 	}
4534 	file.close();
4535 }
4536 
4537 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubjectsTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4538 void TimetableExport::writeSubjectsTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4539 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4540 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4541 
4542 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_SUBJECTS){
4543 		if(QFile::exists(htmlfilename))
4544 			QFile::remove(htmlfilename);
4545 
4546 		return;
4547 	}
4548 
4549 	//Now we print the results to an HTML file
4550 	QFile file(htmlfilename);
4551 	if(!file.open(QIODevice::WriteOnly)){
4552 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4553 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4554 		return;
4555 	}
4556 	QTextStream tos(&file);
4557 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4558 	tos.setEncoding(QStringConverter::Utf8);
4559 #else
4560 	tos.setCodec("UTF-8");
4561 #endif
4562 	tos.setGenerateByteOrderMark(true);
4563 
4564 	tos<<writeHead(true, placedActivities, false);
4565 
4566 	QSet<int> tmp;
4567 	tos<<singleSubjectsTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalSubjects, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4568 
4569 	tos << "  </body>\n</html>\n";
4570 
4571 	if(file.error()>0){
4572 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4573 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4574 	}
4575 	file.close();
4576 }
4577 
4578 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubjectsTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4579 void TimetableExport::writeSubjectsTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4580 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4581 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4582 
4583 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_SUBJECTS){
4584 		if(QFile::exists(htmlfilename))
4585 			QFile::remove(htmlfilename);
4586 
4587 		return;
4588 	}
4589 
4590 	//Now we print the results to an HTML file
4591 	QFile file(htmlfilename);
4592 	if(!file.open(QIODevice::WriteOnly)){
4593 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4594 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4595 		return;
4596 	}
4597 	QTextStream tos(&file);
4598 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4599 	tos.setEncoding(QStringConverter::Utf8);
4600 #else
4601 	tos.setCodec("UTF-8");
4602 #endif
4603 	tos.setGenerateByteOrderMark(true);
4604 
4605 	tos<<writeHead(true, placedActivities, true);
4606 	tos<<writeTOCDays();
4607 
4608 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4609 		QSet<int> tmp;
4610 		tos<<singleSubjectsTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalSubjects, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4611 
4612 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4613 	}
4614 
4615 	tos << "  </body>\n</html>\n";
4616 
4617 	if(file.error()>0){
4618 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4619 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4620 	}
4621 	file.close();
4622 }
4623 
4624 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeSubjectsTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4625 void TimetableExport::writeSubjectsTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4626 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4627 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4628 
4629 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_SUBJECTS){
4630 		if(QFile::exists(htmlfilename))
4631 			QFile::remove(htmlfilename);
4632 
4633 		return;
4634 	}
4635 
4636 	//Now we print the results to an HTML file
4637 	QFile file(htmlfilename);
4638 	if(!file.open(QIODevice::WriteOnly)){
4639 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4640 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4641 		return;
4642 	}
4643 	QTextStream tos(&file);
4644 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4645 	tos.setEncoding(QStringConverter::Utf8);
4646 #else
4647 	tos.setCodec("UTF-8");
4648 #endif
4649 	tos.setGenerateByteOrderMark(true);
4650 
4651 	tos<<writeHead(true, placedActivities, true);
4652 	tos<<writeTOCDays();
4653 
4654 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4655 		QSet<int> tmp;
4656 		tos<<singleSubjectsTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalSubjects, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4657 
4658 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4659 	}
4660 	tos << "  </body>\n</html>\n";
4661 
4662 	if(file.error()>0){
4663 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4664 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4665 	}
4666 	file.close();
4667 }
4668 
4669 //Print the activity tags
4670 
4671 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeActivityTagsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4672 void TimetableExport::writeActivityTagsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4673 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4674 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4675 
4676 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_ACTIVITY_TAGS){
4677 		if(QFile::exists(htmlfilename))
4678 			QFile::remove(htmlfilename);
4679 
4680 		return;
4681 	}
4682 
4683 	//Now we print the results to an HTML file
4684 	QFile file(htmlfilename);
4685 	if(!file.open(QIODevice::WriteOnly)){
4686 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4687 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4688 		return;
4689 	}
4690 	QTextStream tos(&file);
4691 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4692 	tos.setEncoding(QStringConverter::Utf8);
4693 #else
4694 	tos.setCodec("UTF-8");
4695 #endif
4696 	tos.setGenerateByteOrderMark(true);
4697 
4698 	tos<<writeHead(true, placedActivities, true);
4699 
4700 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
4701 	tos<<"    <ul>\n";
4702 	for(int i=0; i<gt.rules.nInternalActivityTags; i++){
4703 		if(gt.rules.internalActivityTagsList[i]->printable){
4704 			tos<<"      <li>\n        "<<TimetableExport::tr("Activity Tag");
4705 			tos<<" <a href=\""<<"#table_"<<hashActivityTagIDsTimetable.value(gt.rules.internalActivityTagsList[i]->name)<<"\">"<<gt.rules.internalActivityTagsList[i]->name<<"</a>\n";
4706 			tos<<"      </li>\n";
4707 		}
4708 	}
4709 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
4710 
4711 
4712 	for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags; activityTag++){
4713 		if(gt.rules.internalActivityTagsList[activityTag]->printable){
4714 			tos<<singleActivityTagsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, activityTag, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4715 			tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4716 		}
4717 	}
4718 	tos<<"  </body>\n</html>\n";
4719 
4720 	if(file.error()>0){
4721 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4722 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4723 	}
4724 	file.close();
4725 }
4726 
4727 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeActivityTagsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4728 void TimetableExport::writeActivityTagsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4729 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4730 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4731 
4732 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_ACTIVITY_TAGS){
4733 		if(QFile::exists(htmlfilename))
4734 			QFile::remove(htmlfilename);
4735 
4736 		return;
4737 	}
4738 
4739 	//Now we print the results to an HTML file
4740 	QFile file(htmlfilename);
4741 	if(!file.open(QIODevice::WriteOnly)){
4742 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4743 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4744 		return;
4745 	}
4746 	QTextStream tos(&file);
4747 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4748 	tos.setEncoding(QStringConverter::Utf8);
4749 #else
4750 	tos.setCodec("UTF-8");
4751 #endif
4752 	tos.setGenerateByteOrderMark(true);
4753 
4754 	tos<<writeHead(true, placedActivities, true);
4755 
4756 	tos<<"    <p><strong>"<<TimetableExport::tr("Table of contents")<<"</strong></p>\n";
4757 	tos<<"    <ul>\n";
4758 	for(int i=0; i<gt.rules.nInternalActivityTags; i++){
4759 		if(gt.rules.internalActivityTagsList[i]->printable){
4760 			tos<<"      <li>\n        "<<TimetableExport::tr("Activity Tag");
4761 			tos<<" <a href=\""<<"#table_"<<hashActivityTagIDsTimetable.value(gt.rules.internalActivityTagsList[i]->name)<<"\">"<<gt.rules.internalActivityTagsList[i]->name<<"</a>\n";
4762 			tos<<"      </li>\n";
4763 		}
4764 	}
4765 	tos<<"    </ul>\n    <p>&nbsp;</p>\n\n";
4766 
4767 	for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags; activityTag++){
4768 		if(gt.rules.internalActivityTagsList[activityTag]->printable){
4769 			tos<<singleActivityTagsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, activityTag, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4770 			tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4771 		}
4772 	}
4773 	tos << "  </body>\n</html>\n";
4774 
4775 	if(file.error()>0){
4776 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4777 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4778 	}
4779 	file.close();
4780 }
4781 
4782 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeActivityTagsTimetableTimeVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4783 void TimetableExport::writeActivityTagsTimetableTimeVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4784 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4785 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4786 
4787 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_ACTIVITY_TAGS){
4788 		if(QFile::exists(htmlfilename))
4789 			QFile::remove(htmlfilename);
4790 
4791 		return;
4792 	}
4793 
4794 	//Now we print the results to an HTML file
4795 	QFile file(htmlfilename);
4796 	if(!file.open(QIODevice::WriteOnly)){
4797 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4798 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4799 		return;
4800 	}
4801 	QTextStream tos(&file);
4802 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4803 	tos.setEncoding(QStringConverter::Utf8);
4804 #else
4805 	tos.setCodec("UTF-8");
4806 #endif
4807 	tos.setGenerateByteOrderMark(true);
4808 
4809 	tos<<writeHead(true, placedActivities, false);
4810 
4811 	QSet<int> tmp;
4812 	tos<<singleActivityTagsTimetableTimeVerticalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalActivityTags, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4813 	tos << "  </body>\n</html>\n";
4814 
4815 	if(file.error()>0){
4816 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4817 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4818 	}
4819 	file.close();
4820 }
4821 
4822 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeActivityTagsTimetableTimeHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4823 void TimetableExport::writeActivityTagsTimetableTimeHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4824 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4825 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4826 
4827 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_ACTIVITY_TAGS){
4828 		if(QFile::exists(htmlfilename))
4829 			QFile::remove(htmlfilename);
4830 
4831 		return;
4832 	}
4833 
4834 	//Now we print the results to an HTML file
4835 	QFile file(htmlfilename);
4836 	if(!file.open(QIODevice::WriteOnly)){
4837 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4838 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4839 		return;
4840 	}
4841 	QTextStream tos(&file);
4842 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4843 	tos.setEncoding(QStringConverter::Utf8);
4844 #else
4845 	tos.setCodec("UTF-8");
4846 #endif
4847 	tos.setGenerateByteOrderMark(true);
4848 
4849 	tos<<writeHead(true, placedActivities, false);
4850 
4851 	QSet<int> tmp;
4852 	tos<<singleActivityTagsTimetableTimeHorizontalHtml(TIMETABLE_HTML_LEVEL, gt.rules.nInternalActivityTags, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4853 
4854 	tos << "  </body>\n</html>\n";
4855 
4856 	if(file.error()>0){
4857 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4858 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4859 	}
4860 	file.close();
4861 }
4862 
4863 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeActivityTagsTimetableTimeVerticalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4864 void TimetableExport::writeActivityTagsTimetableTimeVerticalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4865 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4866 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4867 
4868 	if(!WRITE_TIMETABLES_TIME_VERTICAL || !WRITE_TIMETABLES_ACTIVITY_TAGS){
4869 		if(QFile::exists(htmlfilename))
4870 			QFile::remove(htmlfilename);
4871 
4872 		return;
4873 	}
4874 
4875 	//Now we print the results to an HTML file
4876 	QFile file(htmlfilename);
4877 	if(!file.open(QIODevice::WriteOnly)){
4878 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4879 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4880 		return;
4881 	}
4882 	QTextStream tos(&file);
4883 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4884 	tos.setEncoding(QStringConverter::Utf8);
4885 #else
4886 	tos.setCodec("UTF-8");
4887 #endif
4888 	tos.setGenerateByteOrderMark(true);
4889 
4890 	tos<<writeHead(true, placedActivities, true);
4891 	tos<<writeTOCDays();
4892 
4893 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4894 		QSet<int> tmp;
4895 		tos<<singleActivityTagsTimetableTimeVerticalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalActivityTags, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4896 
4897 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4898 	}
4899 
4900 	tos << "  </body>\n</html>\n";
4901 
4902 	if(file.error()>0){
4903 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4904 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4905 	}
4906 	file.close();
4907 }
4908 
4909 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeActivityTagsTimetableTimeHorizontalDailyHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4910 void TimetableExport::writeActivityTagsTimetableTimeHorizontalDailyHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4911 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4912 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4913 
4914 	if(!WRITE_TIMETABLES_TIME_HORIZONTAL || !WRITE_TIMETABLES_ACTIVITY_TAGS){
4915 		if(QFile::exists(htmlfilename))
4916 			QFile::remove(htmlfilename);
4917 
4918 		return;
4919 	}
4920 
4921 	//Now we print the results to an HTML file
4922 	QFile file(htmlfilename);
4923 	if(!file.open(QIODevice::WriteOnly)){
4924 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4925 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4926 		return;
4927 	}
4928 	QTextStream tos(&file);
4929 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4930 	tos.setEncoding(QStringConverter::Utf8);
4931 #else
4932 	tos.setCodec("UTF-8");
4933 #endif
4934 	tos.setGenerateByteOrderMark(true);
4935 
4936 	tos<<writeHead(true, placedActivities, true);
4937 	tos<<writeTOCDays();
4938 
4939 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
4940 		QSet<int> tmp;
4941 		tos<<singleActivityTagsTimetableTimeHorizontalDailyHtml(TIMETABLE_HTML_LEVEL, day, gt.rules.nInternalActivityTags, tmp, saveTime, TIMETABLE_HTML_PRINT_ACTIVITY_TAGS, TIMETABLE_HTML_REPEAT_NAMES);
4942 
4943 		tos<<"    <p class=\"back\"><a href=\""<<"#top\">"<<TimetableExport::tr("back to the top")<<"</a></p>\n\n";
4944 	}
4945 	tos << "  </body>\n</html>\n";
4946 
4947 	if(file.error()>0){
4948 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4949 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
4950 	}
4951 	file.close();
4952 }
4953 
4954 //Print the teachers free periods. Code by Volker Dirr (https://timetabling.de/)
writeTeachersFreePeriodsTimetableDaysHorizontalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)4955 void TimetableExport::writeTeachersFreePeriodsTimetableDaysHorizontalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
4956 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
4957 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
4958 
4959 	if(!WRITE_TIMETABLES_DAYS_HORIZONTAL || !WRITE_TIMETABLES_TEACHERS_FREE_PERIODS){
4960 		if(QFile::exists(htmlfilename))
4961 			QFile::remove(htmlfilename);
4962 
4963 		return;
4964 	}
4965 
4966 	//Now we print the results to an HTML file
4967 	QFile file(htmlfilename);
4968 	if(!file.open(QIODevice::WriteOnly)){
4969 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
4970 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
4971 		return;
4972 	}
4973 	QTextStream tos(&file);
4974 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
4975 	tos.setEncoding(QStringConverter::Utf8);
4976 #else
4977 	tos.setCodec("UTF-8");
4978 #endif
4979 	tos.setGenerateByteOrderMark(true);
4980 
4981 	tos<<writeHead(true, placedActivities, true);
4982 
4983 	tos<<"    <div class=\"TEACHER_HAS_SINGLE_GAP\">"<<TimetableExport::tr("Teacher has a single gap")<<"</div>\n";
4984 	tos<<"    <div class=\"TEACHER_HAS_BORDER_GAP\">"<<TimetableExport::tr("Teacher has a border gap")<<"</div>\n";
4985 	tos<<"    <div class=\"TEACHER_HAS_BIG_GAP\">"<<TimetableExport::tr("Teacher has a big gap")<<"</div>\n";
4986 	tos<<"    <div class=\"TEACHER_MUST_COME_EARLIER\">"<<TimetableExport::tr("Teacher must come earlier")<<"</div>\n";
4987 	tos<<"    <div class=\"TEACHER_MUST_COME_MUCH_EARLIER\">"<<TimetableExport::tr("Teacher must come much earlier")<<"</div>\n";
4988 	tos<<"    <div class=\"TEACHER_MUST_STAY_LONGER\">"<<TimetableExport::tr("Teacher must stay longer")<<"</div>\n";
4989 	tos<<"    <div class=\"TEACHER_MUST_STAY_MUCH_LONGER\">"<<TimetableExport::tr("Teacher must stay much longer")<<"</div>\n";
4990 	tos<<"    <div class=\"TEACHER_HAS_A_FREE_DAY\">"<<TimetableExport::tr("Teacher has a free day")<<"</div>\n";
4991 	tos<<"    <div class=\"TEACHER_IS_NOT_AVAILABLE\">"<<TimetableExport::tr("Teacher is not available")<<"</div>\n";
4992 
4993 	tos<<"    <p>&nbsp;</p>\n\n";
4994 
4995 	tos<<singleTeachersFreePeriodsTimetableDaysHorizontalHtml(TIMETABLE_HTML_LEVEL, saveTime, PRINT_DETAILED_HTML_TEACHERS_FREE_PERIODS, TIMETABLE_HTML_REPEAT_NAMES);
4996 
4997 	tos<<"  </body>\n</html>\n";
4998 
4999 	if(file.error()>0){
5000 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5001 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
5002 	}
5003 	file.close();
5004 }
5005 
5006 //XHTML generation code by Volker Dirr (https://timetabling.de/)
writeTeachersFreePeriodsTimetableDaysVerticalHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)5007 void TimetableExport::writeTeachersFreePeriodsTimetableDaysVerticalHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
5008 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5009 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5010 
5011 	if(!WRITE_TIMETABLES_DAYS_VERTICAL || !WRITE_TIMETABLES_TEACHERS_FREE_PERIODS){
5012 		if(QFile::exists(htmlfilename))
5013 			QFile::remove(htmlfilename);
5014 
5015 		return;
5016 	}
5017 
5018 	//Now we print the results to an HTML file
5019 	QFile file(htmlfilename);
5020 	if(!file.open(QIODevice::WriteOnly)){
5021 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5022 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
5023 		return;
5024 	}
5025 	QTextStream tos(&file);
5026 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
5027 	tos.setEncoding(QStringConverter::Utf8);
5028 #else
5029 	tos.setCodec("UTF-8");
5030 #endif
5031 	tos.setGenerateByteOrderMark(true);
5032 
5033 	tos<<writeHead(true, placedActivities, true);
5034 
5035 	tos<<"    <div class=\"TEACHER_HAS_SINGLE_GAP\">"<<TimetableExport::tr("Teacher has a single gap")<<"</div>\n";
5036 	tos<<"    <div class=\"TEACHER_HAS_BORDER_GAP\">"<<TimetableExport::tr("Teacher has a border gap")<<"</div>\n";
5037 	tos<<"    <div class=\"TEACHER_HAS_BIG_GAP\">"<<TimetableExport::tr("Teacher has a big gap")<<"</div>\n";
5038 	tos<<"    <div class=\"TEACHER_MUST_COME_EARLIER\">"<<TimetableExport::tr("Teacher must come earlier")<<"</div>\n";
5039 	tos<<"    <div class=\"TEACHER_MUST_COME_MUCH_EARLIER\">"<<TimetableExport::tr("Teacher must come much earlier")<<"</div>\n";
5040 	tos<<"    <div class=\"TEACHER_MUST_STAY_LONGER\">"<<TimetableExport::tr("Teacher must stay longer")<<"</div>\n";
5041 	tos<<"    <div class=\"TEACHER_MUST_STAY_MUCH_LONGER\">"<<TimetableExport::tr("Teacher must stay much longer")<<"</div>\n";
5042 	tos<<"    <div class=\"TEACHER_HAS_A_FREE_DAY\">"<<TimetableExport::tr("Teacher has a free day")<<"</div>\n";
5043 	tos<<"    <div class=\"TEACHER_IS_NOT_AVAILABLE\">"<<TimetableExport::tr("Teacher is not available")<<"</div>\n";
5044 
5045 	tos<<"    <p>&nbsp;</p>\n\n";
5046 
5047 	tos<<singleTeachersFreePeriodsTimetableDaysVerticalHtml(TIMETABLE_HTML_LEVEL, saveTime, PRINT_DETAILED_HTML_TEACHERS_FREE_PERIODS, TIMETABLE_HTML_REPEAT_NAMES);
5048 
5049 	tos<<"  </body>\n</html>\n";
5050 
5051 	if(file.error()>0){
5052 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5053 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
5054 	}
5055 	file.close();
5056 }
5057 
5058 //Code contributed by Volker Dirr (https://timetabling.de/)
writeTeachersStatisticsHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)5059 void TimetableExport::writeTeachersStatisticsHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
5060 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5061 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5062 
5063 	if(!WRITE_TIMETABLES_STATISTICS || !WRITE_TIMETABLES_TEACHERS){
5064 		if(QFile::exists(htmlfilename))
5065 			QFile::remove(htmlfilename);
5066 
5067 		return;
5068 	}
5069 
5070 	//Now we print the results to an HTML file
5071 	QFile file(htmlfilename);
5072 	if(!file.open(QIODevice::WriteOnly)){
5073 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5074 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
5075 		return;
5076 	}
5077 	QTextStream tos(&file);
5078 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
5079 	tos.setEncoding(QStringConverter::Utf8);
5080 #else
5081 	tos.setCodec("UTF-8");
5082 #endif
5083 	tos.setGenerateByteOrderMark(true);
5084 
5085 	tos<<writeHead(true, placedActivities, true);
5086 
5087 	bool PRINT_DETAILED=true;
5088 	tos<<singleTeachersStatisticsHtml(TIMETABLE_HTML_LEVEL, saveTime, PRINT_DETAILED, TIMETABLE_HTML_REPEAT_NAMES, true);
5089 	tos<<"  </body>\n</html>\n";
5090 
5091 	if(file.error()>0){
5092 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5093 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
5094 	}
5095 	file.close();
5096 }
5097 
5098 //Code contributed by Volker Dirr (https://timetabling.de/)
writeStudentsStatisticsHtml(QWidget * parent,const QString & htmlfilename,const QString & saveTime,int placedActivities)5099 void TimetableExport::writeStudentsStatisticsHtml(QWidget* parent, const QString& htmlfilename, const QString& saveTime, int placedActivities){
5100 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5101 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5102 
5103 	if(!WRITE_TIMETABLES_STATISTICS || !(WRITE_TIMETABLES_YEARS || WRITE_TIMETABLES_GROUPS || WRITE_TIMETABLES_SUBGROUPS) ){
5104 		if(QFile::exists(htmlfilename))
5105 			QFile::remove(htmlfilename);
5106 
5107 		return;
5108 	}
5109 
5110 	//Now we print the results to an HTML file
5111 	QFile file(htmlfilename);
5112 	if(!file.open(QIODevice::WriteOnly)){
5113 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5114 		 TimetableExport::tr("Cannot open file %1 for writing. Please check your disk's free space. Saving of %1 aborted.").arg(htmlfilename));
5115 		return;
5116 	}
5117 	QTextStream tos(&file);
5118 #if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
5119 	tos.setEncoding(QStringConverter::Utf8);
5120 #else
5121 	tos.setCodec("UTF-8");
5122 #endif
5123 	tos.setGenerateByteOrderMark(true);
5124 
5125 	tos<<writeHead(true, placedActivities, true);
5126 	bool PRINT_DETAILED=true;
5127 	tos<<singleStudentsStatisticsHtml(TIMETABLE_HTML_LEVEL, saveTime, PRINT_DETAILED, TIMETABLE_HTML_REPEAT_NAMES, true);
5128 
5129 	tos<<"  </body>\n</html>\n";
5130 
5131 	if(file.error()>0){
5132 		IrreconcilableCriticalMessage::critical(parent, tr("FET critical"),
5133 		 TimetableExport::tr("Writing %1 gave error code %2, which means saving is compromised. Please check your disk's free space.").arg(htmlfilename).arg(file.error()));
5134 	}
5135 	file.close();
5136 }
5137 
5138 //------------------------------------------------------------------
5139 //------------------------------------------------------------------
5140 
computeHashForIDsTimetable()5141 void TimetableExport::computeHashForIDsTimetable(){
5142 // by Volker Dirr
5143 
5144 //TODO This is unneeded if you use a relational data base, because we can use the primary key id of the database.
5145 //This is very similar to statistics compute hash. So always check it if you change something here!
5146 
5147 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5148 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5149 	hashStudentIDsTimetable.clear();
5150 	int cnt=1;
5151 	for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
5152 		StudentsYear* sty=gt.rules.augmentedYearsList[i];
5153 		if(!hashStudentIDsTimetable.contains(sty->name)){
5154 			hashStudentIDsTimetable.insert(sty->name, CustomFETString::number(cnt));
5155 			cnt++;
5156 		}
5157 		for(int j=0; j<sty->groupsList.size(); j++){
5158 			StudentsGroup* stg=sty->groupsList[j];
5159 			if(!hashStudentIDsTimetable.contains(stg->name)){
5160 				hashStudentIDsTimetable.insert(stg->name, CustomFETString::number(cnt));
5161 				cnt++;
5162 			}
5163 			for(int k=0; k<stg->subgroupsList.size(); k++){
5164 				StudentsSubgroup* sts=stg->subgroupsList[k];
5165 				if(!hashStudentIDsTimetable.contains(sts->name)){
5166 					hashStudentIDsTimetable.insert(sts->name, CustomFETString::number(cnt));
5167 					cnt++;
5168 				}
5169 			}
5170 		}
5171 	}
5172 
5173 	hashSubjectIDsTimetable.clear();
5174 	for(int i=0; i<gt.rules.nInternalSubjects; i++){
5175 		hashSubjectIDsTimetable.insert(gt.rules.internalSubjectsList[i]->name, CustomFETString::number(i+1));
5176 	}
5177 	hashActivityTagIDsTimetable.clear();
5178 	for(int i=0; i<gt.rules.nInternalActivityTags; i++){
5179 	//	if(gt.rules.internalActivityTagsList[i]->printable){
5180 			hashActivityTagIDsTimetable.insert(gt.rules.internalActivityTagsList[i]->name, CustomFETString::number(i+1));
5181 	//	}
5182 	}
5183 	hashTeacherIDsTimetable.clear();
5184 	for(int i=0; i<gt.rules.nInternalTeachers; i++){
5185 		hashTeacherIDsTimetable.insert(gt.rules.internalTeachersList[i]->name, CustomFETString::number(i+1));
5186 	}
5187 	hashRoomIDsTimetable.clear();
5188 	for(int room=0; room<gt.rules.nInternalRooms; room++){
5189 		hashRoomIDsTimetable.insert(gt.rules.internalRoomsList[room]->name, CustomFETString::number(room+1));
5190 	}
5191 	hashDayIDsTimetable.clear();
5192 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
5193 		hashDayIDsTimetable.insert(gt.rules.daysOfTheWeek[day], CustomFETString::number(day+1));
5194 	}
5195 	if(TIMETABLE_HTML_LEVEL==7){
5196 		computeHashActivityColorBySubject();
5197 		computeHashActivityColorBySubjectAndStudents();
5198 	}
5199 }
5200 
5201 //By Liviu, with ideas from Volker
computeHashActivityColorBySubject()5202 void TimetableExport::computeHashActivityColorBySubject(){
5203 	QHash<QString, int> tmpHash;
5204 
5205 	hashActivityColorBySubject.clear();
5206 	activeHashActivityColorBySubject.clear();
5207 
5208 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5209 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5210 
5211 	QSet<QString> alreadyAdded;
5212 
5213 	for(int i=0; i<gt.rules.nInternalActivities; i++){
5214 		if(best_solution.times[i]!=UNALLOCATED_TIME){
5215 			Activity* act=&gt.rules.internalActivitiesList[i];
5216 			QString tmpString=act->subjectName;
5217 			if(!alreadyAdded.contains(tmpString)){
5218 				alreadyAdded.insert(tmpString);
5219 				hashActivityColorBySubject.insert(i, alreadyAdded.count());
5220 				activeHashActivityColorBySubject.append(i);
5221 				tmpHash.insert(tmpString, alreadyAdded.count());
5222 			}
5223 			else{
5224 				assert(tmpHash.contains(tmpString));
5225 				int k=tmpHash.value(tmpString);
5226 				hashActivityColorBySubject.insert(i, k);
5227 			}
5228 		}
5229 	}
5230 
5231 	//cout<<"hashActivityColorBySubject.count()=="<<hashActivityColorBySubject.count()<<endl;
5232 }
5233 
5234 //By Liviu, with ideas from Volker
computeHashActivityColorBySubjectAndStudents()5235 void TimetableExport::computeHashActivityColorBySubjectAndStudents(){
5236 	QHash<QString, int> tmpHash;
5237 
5238 	hashActivityColorBySubjectAndStudents.clear();
5239 	activeHashActivityColorBySubjectAndStudents.clear();
5240 
5241 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5242 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5243 
5244 	QSet<QString> alreadyAdded;
5245 
5246 	for(int i=0; i<gt.rules.nInternalActivities; i++){
5247 		if(best_solution.times[i]!=UNALLOCATED_TIME){
5248 			Activity* act=&gt.rules.internalActivitiesList[i];
5249 
5250 			QString tmpString=act->subjectName+" "+act->studentsNames.join(", ");
5251 			if(!alreadyAdded.contains(tmpString)){
5252 				alreadyAdded.insert(tmpString);
5253 				hashActivityColorBySubjectAndStudents.insert(i, alreadyAdded.count());
5254 				activeHashActivityColorBySubjectAndStudents.append(i);
5255 				tmpHash.insert(tmpString, alreadyAdded.count());
5256 			}
5257 			else{
5258 				assert(tmpHash.contains(tmpString));
5259 				int k=tmpHash.value(tmpString);
5260 				hashActivityColorBySubjectAndStudents.insert(i, k);
5261 			}
5262 		}
5263 	}
5264 
5265 	//cout<<"hashActivityColorBySubjectAndStudents.count()=="<<hashActivityColorBySubjectAndStudents.count()<<endl;
5266 }
5267 
5268 /*void TimetableExport::computeHashForColors(QHash<QString, QString>& hashColorStringIDsTimetable){
5269 // by Volker Dirr
5270 	qWarning("compute hash for colors");
5271 	hashColorStringIDsTimetable.clear();
5272 	assert(gt.rules.initialized && gt.rules.internalStructureComputed);
5273 	assert(students_schedule_ready && teachers_schedule_ready && rooms_schedule_ready);
5274 	QSet<QString> alreadyAddedString;
5275 	for(int i=0; i<gt.rules.nInternalActivities; i++) {
5276 		Activity* act=&gt.rules.internalActivitiesList[i];
5277 		if(best_solution.times[i]!=UNALLOCATED_TIME) {
5278 			qWarning("add a hash");
5279 			//coloring for students
5280 			QString tmpString=act->subjectName;
5281 			if(!alreadyAddedString.contains(tmpString)){
5282 				alreadyAddedString<<tmpString;
5283 				hashColorStringIDsTimetable.insert(tmpString, CustomFETString::number(alreadyAddedString.count()));
5284 			}
5285 			//coloring for teachers
5286 			tmpString=act->subjectName+" "+act->studentsNames.join(", ");
5287 			if(!alreadyAddedString.contains(tmpString)){
5288 				alreadyAddedString<<tmpString;
5289 				hashColorStringIDsTimetable.insert(tmpString, CustomFETString::number(alreadyAddedString.count()));
5290 			}
5291 			//coloring for rooms
5292 //			it is similar to students
5293 //			tmpString=act->subjectName+" "+act->studentsNames.join(", ");
5294 //			if(!alreadyAddedString.contains(tmpString)){
5295 //				alreadyAddedString<<tmpString;
5296 //				hashColorStringIDsTimetable.insert(tmpString, CustomFETString::number(alreadyAddedString.count()));
5297 //			}
5298 		}
5299 	}
5300 }*/
5301 
computeActivitiesAtTime()5302 void TimetableExport::computeActivitiesAtTime(){		// by Liviu Lalescu
5303 	activitiesAtTime.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
5304 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
5305 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++)
5306 			activitiesAtTime[day][hour].clear();
5307 	for(int i=0; i<gt.rules.nInternalActivities; i++) {		//maybe TODO: maybe it is better to do this sorted by students or teachers?
5308 		Activity* act=&gt.rules.internalActivitiesList[i];
5309 		if(best_solution.times[i]!=UNALLOCATED_TIME) {
5310 			int hour=best_solution.times[i]/gt.rules.nDaysPerWeek;
5311 			int day=best_solution.times[i]%gt.rules.nDaysPerWeek;
5312 			for(int dd=0; dd < act->duration && hour+dd < gt.rules.nHoursPerDay; dd++)
5313 				activitiesAtTime[day][hour+dd].append(i);
5314 		}
5315 	}
5316 }
5317 
computeActivitiesWithSameStartingTime()5318 void TimetableExport::computeActivitiesWithSameStartingTime(){
5319 // by Volker Dirr
5320 	activitiesWithSameStartingTime.clear();
5321 
5322 	if(PRINT_ACTIVITIES_WITH_SAME_STARTING_TIME){
5323 		for(int i=0; i<gt.rules.nInternalTimeConstraints; i++){
5324 			TimeConstraint* tc=gt.rules.internalTimeConstraintsList[i];
5325 			if(tc->type==CONSTRAINT_ACTIVITIES_SAME_STARTING_TIME){ //not needed anymore:  && tc->weightPercentage==100
5326 				ConstraintActivitiesSameStartingTime* c=(ConstraintActivitiesSameStartingTime*) tc;
5327 				for(int a=0; a<c->_n_activities; a++){
5328 					//speed improvement
5329 					QList<int> & tmpList=activitiesWithSameStartingTime[c->_activities[a]];
5330 					for(int b=0; b<c->_n_activities; b++){
5331 						if(a!=b){
5332 							if(best_solution.times[c->_activities[a]]==best_solution.times[c->_activities[b]]){ 	//because constraint is maybe not with 100% weight and failed
5333 								if(!tmpList.contains(c->_activities[b])){
5334 									tmpList<<c->_activities[b];
5335 								}
5336 							}
5337 						}
5338 					}
5339 					/*
5340 					QList<int> tmpList;
5341 					if(activitiesWithSameStartingTime.contains(c->_activities[a]))
5342 						tmpList=activitiesWithSameStartingTime.value(c->_activities[a]);
5343 					for(int b=0; b<c->_n_activities; b++){
5344 						if(a!=b){
5345 							if(best_solution.times[c->_activities[a]]==best_solution.times[c->_activities[b]]){ 	//because constraint is maybe not with 100% weight and failed
5346 								if(!tmpList.contains(c->_activities[b])){
5347 									tmpList<<c->_activities[b];
5348 								}
5349 							}
5350 						}
5351 					}
5352 					activitiesWithSameStartingTime.insert(c->_activities[a], tmpList);
5353 					*/
5354 				}
5355 			}
5356 		}
5357 	}
5358 }
5359 
addActivitiesWithSameStartingTime(QList<int> & allActivities,int hour)5360 bool TimetableExport::addActivitiesWithSameStartingTime(QList<int>& allActivities, int hour){
5361 // by Volker Dirr
5362 	if(PRINT_ACTIVITIES_WITH_SAME_STARTING_TIME){
5363 		bool activitiesWithSameStartingtime=false;
5364 		QList<int> allActivitiesNew;
5365 		for(int tmpAct : qAsConst(allActivities)){
5366 			allActivitiesNew<<tmpAct;
5367 			if(activitiesWithSameStartingTime.contains(tmpAct)){
5368 				QList<int> sameTimeList=activitiesWithSameStartingTime.value(tmpAct);
5369 				for(int sameTimeAct : qAsConst(sameTimeList)){
5370 					if(!allActivitiesNew.contains(sameTimeAct) && !allActivities.contains(sameTimeAct)){
5371 						if(best_solution.times[sameTimeAct]!=UNALLOCATED_TIME){
5372 							Activity* act=&gt.rules.internalActivitiesList[sameTimeAct];
5373 							assert(best_solution.times[tmpAct]==best_solution.times[sameTimeAct]);//{
5374 								if((best_solution.times[sameTimeAct]/gt.rules.nDaysPerWeek+(act->duration-1))>=hour){
5375 									allActivitiesNew<<sameTimeAct;
5376 								}
5377 								activitiesWithSameStartingtime=true; //don't add this line in previous if command because of activities with different duration!
5378 							//}
5379 						}
5380 					}
5381 				}
5382 			}
5383 		}
5384 		//allActivities.clear();
5385 		allActivities=allActivitiesNew;
5386 		//allActivitiesNew.clear();
5387 		return activitiesWithSameStartingtime;
5388 	}
5389 	else
5390 		return false;
5391 }
5392 
5393 // by Volker Dirr
writeHead(bool java,int placedActivities,bool printInstitution)5394 QString TimetableExport::writeHead(bool java, int placedActivities, bool printInstitution){
5395 	QString tmp;
5396 	tmp+="<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n";
5397 	tmp+="  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n\n";
5398 
5399 	if(!LANGUAGE_STYLE_RIGHT_TO_LEFT)
5400 		tmp+="<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\""+LANGUAGE_FOR_HTML+"\" xml:lang=\""+LANGUAGE_FOR_HTML+"\">\n";
5401 	else
5402 		tmp+="<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\""+LANGUAGE_FOR_HTML+"\" xml:lang=\""+LANGUAGE_FOR_HTML+"\" dir=\"rtl\">\n";
5403 	tmp+="  <head>\n";
5404 	tmp+="    <title>"+protect2(gt.rules.institutionName)+"</title>\n";
5405 	tmp+="    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n";
5406 	if(TIMETABLE_HTML_LEVEL>=1){
5407 		QString cssfilename=INPUT_FILENAME_XML.right(INPUT_FILENAME_XML.length()-INPUT_FILENAME_XML.lastIndexOf(FILE_SEP)-1);
5408 
5409 		if(cssfilename.right(4)==".fet")
5410 			cssfilename=cssfilename.left(cssfilename.length()-4);
5411 		//else if(INPUT_FILENAME_XML!="")
5412 		//	cout<<"Minor problem - input file does not end in .fet extension - might be a problem when saving the timetables"<<" (file:"<<__FILE__<<", line:"<<__LINE__<<")"<<endl;
5413 
5414 		cssfilename+="_"+STYLESHEET_CSS;
5415 		if(INPUT_FILENAME_XML=="")
5416 			cssfilename=STYLESHEET_CSS;
5417 		tmp+="    <link rel=\"stylesheet\" media=\"all\" href=\""+cssfilename+"\" type=\"text/css\" />\n";
5418 	}
5419 	if(java){
5420 		if(TIMETABLE_HTML_LEVEL>=5 && TIMETABLE_HTML_LEVEL!=7){  // the following JavaScript code is pretty similar to an example of Les Richardson
5421 			tmp+="    <meta http-equiv=\"Content-Script-Type\" content=\"text/javascript\" />\n";
5422 			tmp+="    <script type=\"text/javascript\">\n";
5423 			tmp+="      function highlight(classval) {\n";
5424 			tmp+="        var spans = document.getElementsByTagName('span');\n";
5425 			tmp+="        for(var i=0; spans.length>i; i++) {\n";
5426 			tmp+="          if (spans[i].className == classval) {\n";
5427 			tmp+="            spans[i].style.backgroundColor = 'lime';\n";
5428 			tmp+="          } else {\n";
5429 			tmp+="            spans[i].style.backgroundColor = 'white';\n";
5430 			tmp+="          }\n";
5431 			tmp+="        }\n";
5432 			tmp+="      }\n";
5433 			tmp+="    </script>\n";
5434 		}
5435 	}
5436 	tmp+="  </head>\n\n";
5437 	tmp+="  <body id=\"top\">\n";
5438 	if(placedActivities!=gt.rules.nInternalActivities)
5439 		tmp+="    <h1>"+TimetableExport::tr("Warning! Only %1 out of %2 activities placed!").arg(placedActivities).arg(gt.rules.nInternalActivities)+"</h1>\n";
5440 	if(printInstitution){
5441 		tmp+="    <table>\n      <tr align=\"left\" valign=\"top\">\n        <th>"+TimetableExport::tr("Institution name")+":</th>\n        <td>"+protect2(gt.rules.institutionName)+"</td>\n      </tr>\n    </table>\n";
5442 		tmp+="    <table>\n      <tr align=\"left\" valign=\"top\">\n        <th>"+TimetableExport::tr("Comments")+":</th>\n        <td>"+protect2(gt.rules.comments).replace(QString("\n"), QString("<br />\n"))+"</td>\n      </tr>\n    </table>\n";
5443 	}
5444 	return tmp;
5445 }
5446 
5447 // by Volker Dirr
writeTOCDays()5448 QString TimetableExport::writeTOCDays(){
5449 	QString tmp;
5450 	tmp+="    <p><strong>"+TimetableExport::tr("Table of contents")+"</strong></p>\n";
5451 	tmp+="    <ul>\n";
5452 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
5453 		tmp+="      <li>\n        ";
5454 		tmp+=" <a href=\"#table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</a>\n";
5455 		tmp+="          </li>\n";
5456 	}
5457 	tmp+="    </ul>\n    <p>&nbsp;</p>\n";
5458 	return tmp;
5459 }
5460 
5461 // by Volker Dirr
writeStartTagTDofActivities(int htmlLevel,const Activity * act,bool detailed,bool colspan,bool rowspan,int colorBy)5462 QString TimetableExport::writeStartTagTDofActivities(int htmlLevel, const Activity* act, bool detailed, bool colspan, bool rowspan, int colorBy){
5463 	QString tmp;
5464 	assert(!(colspan && rowspan));
5465 	if(detailed)
5466 		assert(!colspan && !rowspan);
5467 	else
5468 		tmp+="          ";
5469 	tmp+="<td";
5470 	if(rowspan && act->duration>1)
5471 		tmp+=" rowspan=\""+CustomFETString::number(act->duration)+"\"";
5472 	if(colspan && act->duration>1)
5473 		tmp+=" colspan=\""+CustomFETString::number(act->duration)+"\"";
5474 	if(htmlLevel==6){
5475 		tmp+=" class=\"";
5476 		if(act->subjectName.size()>0){
5477 			tmp+="s_"+hashSubjectIDsTimetable.value(act->subjectName);
5478 		}
5479 		if(act->activityTagsNames.size()>0){
5480 			for(const QString& atn : qAsConst(act->activityTagsNames)){
5481 				assert(hashActivityTagIDsTimetable.contains(atn));
5482 				int id=hashActivityTagIDsTimetable.value(atn, "0").toInt()-1;
5483 				assert(id>=0);
5484 				assert(id<gt.rules.nInternalActivityTags);
5485 				if(gt.rules.internalActivityTagsList[id]->printable){
5486 					tmp+=" at_"+hashActivityTagIDsTimetable.value(atn);
5487 				}
5488 			}
5489 		}
5490 		if(act->studentsNames.size()>0){
5491 			for(const QString& st : qAsConst(act->studentsNames))
5492 				tmp+=" ss_"+hashStudentIDsTimetable.value(st);
5493 		}
5494 		if(act->teachersNames.size()>0){
5495 			for(const QString& t : qAsConst(act->teachersNames))
5496 				tmp+=" t_"+hashTeacherIDsTimetable.value(t);
5497 		}
5498 		//i need ai for this!!! so i need a parameter ai?! //TODO
5499 		/*int r=best_solution.rooms[ai];
5500 		if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
5501 			tmp+=" room_"+protect2id(gt.rules.internalRoomsList[r]->name);
5502 		}*/
5503 		if(detailed)
5504 			tmp+=" detailed";
5505 		tmp+="\"";
5506 	}
5507 	if(htmlLevel==7){
5508 		assert(gt.rules.activitiesHash.contains(act->id));
5509 		int index=gt.rules.activitiesHash.value(act->id);
5510 		switch(colorBy){
5511 			case COLOR_BY_SUBJECT_STUDENTS: tmp+=" class=\"c_"+QString::number(activeHashActivityColorBySubject.count()+hashActivityColorBySubjectAndStudents.value(index)); break;
5512 			case COLOR_BY_SUBJECT: tmp+=" class=\"c_"+QString::number(hashActivityColorBySubject.value(index)); break;
5513 			default: assert(0==1);
5514 		}
5515 
5516 		if(detailed)
5517 			tmp+=" detailed";
5518 		tmp+="\"";
5519 	}
5520 	if(detailed && htmlLevel>=1 && htmlLevel<=5)
5521 		tmp+=" class=\"detailed\"";
5522 	tmp+=">";
5523 	return tmp;
5524 }
5525 
5526 // by Volker Dirr
writeSubjectAndActivityTags(int htmlLevel,const Activity * act,const QString & startTag,const QString & startTagAttribute,bool activityTagsOnly,bool printActivityTags)5527 QString TimetableExport::writeSubjectAndActivityTags(int htmlLevel, const Activity* act, const QString& startTag, const QString& startTagAttribute, bool activityTagsOnly, bool printActivityTags){
5528 	QString tmp;
5529 	if(act->subjectName.size()>0||act->activityTagsNames.size()>0){
5530 		if(startTag=="div" && htmlLevel>=3)
5531 			tmp+="<"+startTag+startTagAttribute+">";
5532 		if(act->subjectName.size()>0 && !activityTagsOnly){
5533 			switch(htmlLevel){
5534 				case 3 : tmp+="<span class=\"subject\">"+protect2(act->subjectName)+"</span>"; break;
5535 				case 4 : tmp+="<span class=\"subject\"><span class=\"s_"+hashSubjectIDsTimetable.value(act->subjectName)+"\">"+protect2(act->subjectName)+"</span></span>"; break;
5536 				case 5 : ;
5537 				case 6 : tmp+="<span class=\"subject\"><span class=\"s_"+hashSubjectIDsTimetable.value(act->subjectName)+"\" onmouseover=\"highlight('s_"+hashSubjectIDsTimetable.value(act->subjectName)+"')\">"+protect2(act->subjectName)+"</span></span>"; break;
5538 				case 7 : tmp+="<span class=\"subject\">"+protect2(act->subjectName)+"</span>"; break;
5539 				default: tmp+=protect2(act->subjectName); break;
5540 			}
5541 			if(act->activityTagsNames.size()>0 && printActivityTags){
5542 				tmp+=" ";
5543 			}
5544 		}
5545 		if(act->activityTagsNames.size()>0 && printActivityTags){
5546 			bool writeTags=false;
5547 			for(const QString& atn : qAsConst(act->activityTagsNames)){
5548 				assert(hashActivityTagIDsTimetable.contains(atn));
5549 				int id=hashActivityTagIDsTimetable.value(atn, "0").toInt()-1;
5550 				assert(id>=0);
5551 				assert(id<gt.rules.nInternalActivityTags);
5552 				if(gt.rules.internalActivityTagsList[id]->printable){
5553 					writeTags=true;
5554 				}
5555 			}
5556 			if(writeTags){
5557 				if(!activityTagsOnly){
5558 					if(htmlLevel>=3){
5559 						tmp+="<span class=\"activitytag\">";
5560 					}
5561 				}
5562 				for(const QString& atn : qAsConst(act->activityTagsNames)){
5563 					assert(hashActivityTagIDsTimetable.contains(atn));
5564 					int id=hashActivityTagIDsTimetable.value(atn, "0").toInt()-1;
5565 					assert(id>=0);
5566 					assert(id<gt.rules.nInternalActivityTags);
5567 					if(gt.rules.internalActivityTagsList[id]->printable){
5568 						switch(htmlLevel){
5569 							case 3 : tmp+=protect2(atn); break;
5570 							case 4 : tmp+="<span class=\"at_"+hashActivityTagIDsTimetable.value(atn)+"\">"+protect2(atn)+"</span>"; break;
5571 							case 5 : ;
5572 							case 6 : tmp+="<span class=\"at_"+hashActivityTagIDsTimetable.value(atn)+"\" onmouseover=\"highlight('at_"+hashActivityTagIDsTimetable.value(atn)+"')\">"+protect2(atn)+"</span>"; break;
5573 							default: tmp+=protect2(atn); break;
5574 						}
5575 						tmp+=", ";
5576 					}
5577 				}
5578 				tmp.remove(tmp.size()-2, 2);
5579 				if(!activityTagsOnly){
5580 					if(htmlLevel>=3){
5581 						tmp+="</span>";
5582 					}
5583 				}
5584 			}
5585 		}
5586 		if(startTag=="div"){
5587 			if(htmlLevel>=3)
5588 				tmp+="</div>";
5589 			else tmp+="<br />";
5590 		}
5591 	}
5592 	return tmp;
5593 }
5594 
5595 // by Volker Dirr
writeStudents(int htmlLevel,const Activity * act,const QString & startTag,const QString & startTagAttribute)5596 QString TimetableExport::writeStudents(int htmlLevel, const Activity* act, const QString& startTag, const QString& startTagAttribute){
5597 	QString tmp;
5598 	if(act->studentsNames.size()>0){
5599 		if(startTag=="div" && htmlLevel>=3)
5600 			tmp+="<"+startTag+startTagAttribute+">";
5601 		for(const QString& st : qAsConst(act->studentsNames)){
5602 			switch(htmlLevel){
5603 				case 4 : tmp+="<span class=\"ss_"+hashStudentIDsTimetable.value(st)+"\">"+protect2(st)+"</span>"; break;
5604 				case 5 : ;
5605 				case 6 : tmp+="<span class=\"ss_"+hashStudentIDsTimetable.value(st)+"\" onmouseover=\"highlight('ss_"+hashStudentIDsTimetable.value(st)+"')\">"+protect2(st)+"</span>"; break;
5606 				default: tmp+=protect2(st); break;
5607 			}
5608 			tmp+=", ";
5609 		}
5610 		tmp.remove(tmp.size()-2, 2);
5611 		if(startTag=="div"){
5612 			if(htmlLevel>=3)
5613 				tmp+="</div>";
5614 			else tmp+="<br />";
5615 		}
5616 	}
5617 	return tmp;
5618 }
5619 
5620 // by Volker Dirr
writeTeachers(int htmlLevel,const Activity * act,const QString & startTag,const QString & startTagAttribute)5621 QString TimetableExport::writeTeachers(int htmlLevel, const Activity* act, const QString& startTag, const QString& startTagAttribute){
5622 	QString tmp;
5623 	if(act->teachersNames.size()>0){
5624 		if(startTag=="div" && htmlLevel>=3)
5625 			tmp+="<"+startTag+startTagAttribute+">";
5626 		for(const QString& t : qAsConst(act->teachersNames)){
5627 			switch(htmlLevel){
5628 				case 4 : tmp+="<span class=\"t_"+hashTeacherIDsTimetable.value(t)+"\">"+protect2(t)+"</span>"; break;
5629 				case 5 : ;
5630 				case 6 : tmp+="<span class=\"t_"+hashTeacherIDsTimetable.value(t)+"\" onmouseover=\"highlight('t_"+hashTeacherIDsTimetable.value(t)+"')\">"+protect2(t)+"</span>"; break;
5631 				default: tmp+=protect2(t); break;
5632 			}
5633 			tmp+=", ";
5634 		}
5635 		tmp.remove(tmp.size()-2, 2);
5636 		if(startTag=="div"){
5637 			if(htmlLevel>=3)
5638 				tmp+="</div>";
5639 			else tmp+="<br />";
5640 		}
5641 	}
5642 	return tmp;
5643 }
5644 
5645 // by Volker Dirr
writeRoom(int htmlLevel,int ai,const QString & startTag,const QString & startTagAttribute)5646 QString TimetableExport::writeRoom(int htmlLevel, int ai, const QString& startTag, const QString& startTagAttribute){
5647 	QString tmp;
5648 	int r=best_solution.rooms[ai];
5649 	if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
5650 		if(startTag=="div" && htmlLevel>=3)
5651 			tmp+="<"+startTag+startTagAttribute+">";
5652 
5653 		if(gt.rules.internalRoomsList[r]->isVirtual==false){
5654 			switch(htmlLevel){
5655 				case 4 : tmp+="<span class=\"r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[r]->name)+"\">"+protect2(gt.rules.internalRoomsList[r]->name)+"</span>"; break;
5656 				case 5 : ;
5657 				case 6 : tmp+="<span class=\"r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[r]->name)+"\" onmouseover=\"highlight('r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[r]->name)+"')\">"+protect2(gt.rules.internalRoomsList[r]->name)+"</span>"; break;
5658 				default: tmp+=protect2(gt.rules.internalRoomsList[r]->name); break;
5659 			}
5660 		} else {
5661 			QStringList rooms;
5662 			if(SHOW_VIRTUAL_ROOMS_IN_TIMETABLES){
5663 				switch(htmlLevel){
5664 					case 4 : tmp+="<span class=\"r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[r]->name)+"\">"+protect2(gt.rules.internalRoomsList[r]->name)+"</span>"; break;
5665 					case 5 : ;
5666 					case 6 : tmp+="<span class=\"r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[r]->name)+"\" onmouseover=\"highlight('r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[r]->name)+"')\">"+protect2(gt.rules.internalRoomsList[r]->name)+"</span>"; break;
5667 					default: tmp+=protect2(gt.rules.internalRoomsList[r]->name); break;
5668 				}
5669 				//tmp+="Virtual Room X";
5670 				tmp+=" (";
5671 			}
5672 			for(int rr : qAsConst(best_solution.realRoomsList[ai])){
5673 				QString room;
5674 				switch(htmlLevel){
5675 					case 4 : room+="<span class=\"r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[rr]->name)+"\">"+protect2(gt.rules.internalRoomsList[rr]->name)+"</span>"; break;
5676 					case 5 : ;
5677 					case 6 : room+="<span class=\"r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[rr]->name)+"\" onmouseover=\"highlight('r_"+hashRoomIDsTimetable.value(gt.rules.internalRoomsList[rr]->name)+"')\">"+protect2(gt.rules.internalRoomsList[rr]->name)+"</span>"; break;
5678 					default: room+=protect2(gt.rules.internalRoomsList[rr]->name); break;
5679 				}
5680 				rooms.append(room);
5681 			}
5682 			tmp+=rooms.join(", ");
5683 			if(SHOW_VIRTUAL_ROOMS_IN_TIMETABLES){
5684 				tmp+=")";
5685 			}
5686 		}
5687 
5688 		if(startTag=="div"){
5689 			if(htmlLevel>=3)
5690 				tmp+="</div>";
5691 			else tmp+="<br />";
5692 		}
5693 	}
5694 	return tmp;
5695 }
5696 
5697 // by Volker Dirr
writeNotAvailableSlot(int htmlLevel,const QString & weight)5698 QString TimetableExport::writeNotAvailableSlot(int htmlLevel, const QString& weight){
5699 	QString tmp;
5700 	//weight=" "+weight;
5701 	switch(htmlLevel){
5702 		case 3 : ;
5703 		case 4 : tmp="          <td class=\"notAvailable\"><span class=\"notAvailable\">"+protect2(STRING_NOT_AVAILABLE_TIME_SLOT)+weight+"</span></td>\n"; break;
5704 		case 5 : ;
5705 		case 6 : tmp="          <td class=\"notAvailable\"><span class=\"notAvailable\" onmouseover=\"highlight('notAvailable')\">"+protect2(STRING_NOT_AVAILABLE_TIME_SLOT)+weight+"</span></td>\n"; break;
5706 		case 7 : tmp="          <td class=\"notAvailable\"><span class=\"notAvailable\">"+protect2(STRING_NOT_AVAILABLE_TIME_SLOT)+weight+"</span></td>\n"; break;
5707 		default: tmp="          <td>"+protect2(STRING_NOT_AVAILABLE_TIME_SLOT)+weight+"</td>\n";
5708 	}
5709 	return tmp;
5710 }
5711 
5712 // by Volker Dirr
writeBreakSlot(int htmlLevel,const QString & weight)5713 QString TimetableExport::writeBreakSlot(int htmlLevel, const QString& weight){
5714 	QString tmp;
5715 	//weight=" "+weight;
5716 	switch(htmlLevel){
5717 		case 3 : ;
5718 		case 4 : tmp="          <td class=\"break\"><span class=\"break\">"+protect2(STRING_BREAK_SLOT)+weight+"</span></td>\n"; break;
5719 		case 5 : ;
5720 		case 6 : tmp="          <td class=\"break\"><span class=\"break\" onmouseover=\"highlight('break')\">"+protect2(STRING_BREAK_SLOT)+weight+"</span></td>\n"; break;
5721 		case 7 : tmp="          <td class=\"break\"><span class=\"break\">"+protect2(STRING_BREAK_SLOT)+weight+"</span></td>\n"; break;
5722 		default: tmp="          <td>"+protect2(STRING_BREAK_SLOT)+weight+"</td>\n";
5723 	}
5724 	return tmp;
5725 }
5726 
5727 // by Volker Dirr
writeEmpty(int htmlLevel)5728 QString TimetableExport::writeEmpty(int htmlLevel){
5729 	QString tmp;
5730 	switch(htmlLevel){
5731 		case 3 : ;
5732 		case 4 : tmp="          <td class=\"empty\"><span class=\"empty\">"+protect2(STRING_EMPTY_SLOT)+"</span></td>\n"; break;
5733 		case 5 : ;
5734 		case 6 : tmp="          <td class=\"empty\"><span class=\"empty\" onmouseover=\"highlight('empty')\">"+protect2(STRING_EMPTY_SLOT)+"</span></td>\n"; break;
5735 		case 7 : tmp="          <td class=\"empty\"><span class=\"empty\">"+protect2(STRING_EMPTY_SLOT)+"</span></td>\n"; break;
5736 		default: tmp="          <td>"+protect2(STRING_EMPTY_SLOT)+"</td>\n";
5737 	}
5738 	return tmp;
5739 }
5740 
5741 //by Volker Dirr
writeActivityStudents(int htmlLevel,int ai,int day,int hour,bool notAvailable,bool colspan,bool rowspan,bool printActivityTags,QString skipStudentsSet)5742 QString TimetableExport::writeActivityStudents(int htmlLevel, int ai, int day, int hour, bool notAvailable, bool colspan, bool rowspan, bool printActivityTags, QString skipStudentsSet){
5743 	QString tmp;
5744 	int currentTime=day+gt.rules.nDaysPerWeek*hour;
5745 	if(ai!=UNALLOCATED_ACTIVITY){
5746 		if(best_solution.times[ai]==currentTime){
5747 			Activity* act=&gt.rules.internalActivitiesList[ai];
5748 			tmp+=writeStartTagTDofActivities(htmlLevel, act, false, colspan, rowspan, COLOR_BY_SUBJECT);
5749 			//TODO line0
5750 			bool skipLine0=false;
5751 			if(act->studentsNames.size()==1){
5752 				if(act->studentsNames.at(0)==skipStudentsSet){
5753 					skipLine0=true;
5754 				}
5755 			}
5756 			if(!skipLine0){
5757 				tmp+=writeStudents(htmlLevel, act, "div", " class=\"studentsset line0\"");
5758 			}
5759 			tmp+=writeSubjectAndActivityTags(htmlLevel, act, "div", " class=\"line1\"", false, printActivityTags);
5760 			tmp+=writeTeachers(htmlLevel, act, "div", " class=\"teacher line2\"");
5761 			tmp+=writeRoom(htmlLevel, ai, "div", " class=\"room line3\"");
5762 			tmp+="</td>\n";
5763 		} else
5764 			tmp+="          <!-- span -->\n";
5765 	} else {
5766 		if(notAvailable && PRINT_NOT_AVAILABLE_TIME_SLOTS){
5767 			tmp+=writeNotAvailableSlot(htmlLevel, "");
5768 		}
5769 		else if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
5770 			tmp+=writeBreakSlot(htmlLevel, "");
5771 		}
5772 		else{
5773 			tmp+=writeEmpty(htmlLevel);
5774 		}
5775 	}
5776 	return tmp;
5777 }
5778 
5779 //by Volker Dirr
writeActivitiesStudents(int htmlLevel,const QList<int> & allActivities,bool printActivityTags)5780 QString TimetableExport::writeActivitiesStudents(int htmlLevel, const QList<int>& allActivities, bool printActivityTags){
5781 	QString tmp;
5782 	if(htmlLevel>=1)
5783 		tmp+="          <td><table class=\"detailed\">";
5784 	else
5785 		tmp+="          <td><table>";
5786 	if(htmlLevel>=3)
5787 		tmp+="<tr class=\"studentsset line0\">";
5788 	else	tmp+="<tr>";
5789 	for(int a=0; a<allActivities.size(); a++){
5790 		int ai=allActivities[a];
5791 		if(ai!=UNALLOCATED_ACTIVITY){
5792 			Activity* act=&gt.rules.internalActivitiesList[ai];
5793 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT)+writeStudents(htmlLevel, act, "", "")+"</td>";
5794 		}
5795 	}
5796 	tmp+="</tr>";
5797 	if(htmlLevel>=3)
5798 		tmp+="<tr class=\"line1\">";
5799 	else	tmp+="<tr>";
5800 	for(int a=0; a<allActivities.size(); a++){
5801 		int ai=allActivities[a];
5802 		if(ai!=UNALLOCATED_ACTIVITY){
5803 			Activity* act=&gt.rules.internalActivitiesList[ai];
5804 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT)+writeSubjectAndActivityTags(htmlLevel, act, "", "", false, printActivityTags)+"</td>";
5805 		}
5806 	}
5807 	tmp+="</tr>";
5808 	if(htmlLevel>=3)
5809 		tmp+="<tr class=\"teacher line2\">";
5810 	else	tmp+="<tr>";
5811 	for(int a=0; a<allActivities.size(); a++){
5812 		int ai=allActivities[a];
5813 		if(ai!=UNALLOCATED_ACTIVITY){
5814 			Activity* act=&gt.rules.internalActivitiesList[ai];
5815 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT)+writeTeachers(htmlLevel, act, "", "")+"</td>";
5816 		}
5817 	}
5818 	tmp+="</tr>";
5819 	if(htmlLevel>=3)
5820 		tmp+="<tr class=\"room line3\">";
5821 	else	tmp+="<tr>";
5822 	for(int a=0; a<allActivities.size(); a++){
5823 		int ai=allActivities[a];
5824 		if(ai!=UNALLOCATED_ACTIVITY){
5825 			Activity* act=&gt.rules.internalActivitiesList[ai];
5826 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT)+writeRoom(htmlLevel, ai, "", "")+"</td>";
5827 		}
5828 	}
5829 	tmp+="</tr>";
5830 	tmp+="</table></td>\n";
5831 	return tmp;
5832 }
5833 
5834 //by Volker Dirr
writeActivityTeacher(int htmlLevel,int teacher,int day,int hour,bool colspan,bool rowspan,bool printActivityTags,QString skipTeacher)5835 QString TimetableExport::writeActivityTeacher(int htmlLevel, int teacher, int day, int hour, bool colspan, bool rowspan, bool printActivityTags, QString skipTeacher){
5836 	QString tmp;
5837 	int ai=teachers_timetable_weekly[teacher][day][hour];
5838 	int currentTime=day+gt.rules.nDaysPerWeek*hour;
5839 	if(ai!=UNALLOCATED_ACTIVITY){
5840 		if(best_solution.times[ai]==currentTime){
5841 			Activity* act=&gt.rules.internalActivitiesList[ai];
5842 			tmp+=writeStartTagTDofActivities(htmlLevel, act, false, colspan, rowspan, COLOR_BY_SUBJECT_STUDENTS);
5843 			//TODO line0
5844 			bool skipLine0=false;
5845 			if(act->teachersNames.size()==1){
5846 				if(act->teachersNames.at(0)==skipTeacher){
5847 					skipLine0=true;
5848 				}
5849 			}
5850 			if(!skipLine0){
5851 				tmp+=writeTeachers(htmlLevel, act, "div", " class=\"teacher line0\"");
5852 			}
5853 			tmp+=writeStudents(htmlLevel, act, "div", " class=\"studentsset line1\"");
5854 			tmp+=writeSubjectAndActivityTags(htmlLevel, act, "div", " class=\"line2\"", false, printActivityTags);
5855 			tmp+=writeRoom(htmlLevel, ai, "div", " class=\"room line3\"");
5856 			tmp+="</td>\n";
5857 		} else
5858 			tmp+="          <!-- span -->\n";
5859 	} else {
5860 		if(teacherNotAvailableDayHour[teacher][day][hour] && PRINT_NOT_AVAILABLE_TIME_SLOTS){
5861 			tmp+=writeNotAvailableSlot(htmlLevel, "");
5862 		}
5863 		else if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
5864 			tmp+=writeBreakSlot(htmlLevel, "");
5865 		}
5866 		else{
5867 			tmp+=writeEmpty(htmlLevel);
5868 		}
5869 	}
5870 	return tmp;
5871 }
5872 
5873 //by Volker Dirr
writeActivitiesTeachers(int htmlLevel,const QList<int> & allActivities,bool printActivityTags)5874 QString TimetableExport::writeActivitiesTeachers(int htmlLevel, const QList<int>& allActivities, bool printActivityTags){
5875 	QString tmp;
5876 	if(htmlLevel>=1)
5877 		tmp+="          <td><table class=\"detailed\">";
5878 	else
5879 		tmp+="          <td><table>";
5880 	if(htmlLevel>=3)
5881 		tmp+="<tr class=\"teacher line0\">";
5882 	else	tmp+="<tr>";
5883 	for(int a=0; a<allActivities.size(); a++){
5884 		int ai=allActivities[a];
5885 		if(ai!=UNALLOCATED_ACTIVITY){
5886 			Activity* act=&gt.rules.internalActivitiesList[ai];
5887 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeTeachers(htmlLevel, act, "", "")+"</td>";
5888 		}
5889 	}
5890 	tmp+="</tr>";
5891 	if(htmlLevel>=3)
5892 		tmp+="<tr class=\"studentsset line1\">";
5893 	else	tmp+="<tr>";
5894 	for(int a=0; a<allActivities.size(); a++){
5895 		int ai=allActivities[a];
5896 		if(ai!=UNALLOCATED_ACTIVITY){
5897 			Activity* act=&gt.rules.internalActivitiesList[ai];
5898 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeStudents(htmlLevel, act, "", "")+"</td>";
5899 		}
5900 	}
5901 	tmp+="</tr>";
5902 	if(htmlLevel>=3)
5903 		tmp+="<tr class=\"line2\">";
5904 	else	tmp+="<tr>";
5905 	for(int a=0; a<allActivities.size(); a++){
5906 		int ai=allActivities[a];
5907 		if(ai!=UNALLOCATED_ACTIVITY){
5908 			Activity* act=&gt.rules.internalActivitiesList[ai];
5909 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeSubjectAndActivityTags(htmlLevel, act, "", "", false, printActivityTags)+"</td>";
5910 		}
5911 	}
5912 	tmp+="</tr>";
5913 
5914 	if(htmlLevel>=3)
5915 		tmp+="<tr class=\"room line3\">";
5916 	else	tmp+="<tr>";
5917 	for(int a=0; a<allActivities.size(); a++){
5918 		int ai=allActivities[a];
5919 		if(ai!=UNALLOCATED_ACTIVITY){
5920 			Activity* act=&gt.rules.internalActivitiesList[ai];
5921 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeRoom(htmlLevel, ai, "", "")+"</td>";
5922 		}
5923 	}
5924 	tmp+="</tr>";
5925 	tmp+="</table></td>\n";
5926 	return tmp;
5927 }
5928 
5929 //by Volker Dirr
writeActivityRoom(int htmlLevel,int room,int day,int hour,bool colspan,bool rowspan,bool printActivityTags)5930 QString TimetableExport::writeActivityRoom(int htmlLevel, int room, int day, int hour, bool colspan, bool rowspan, bool printActivityTags){
5931 	QString tmp;
5932 	int ai=rooms_timetable_weekly[room][day][hour];
5933 	int currentTime=day+gt.rules.nDaysPerWeek*hour;
5934 	if(ai!=UNALLOCATED_ACTIVITY){
5935 		if(best_solution.times[ai]==currentTime){
5936 			Activity* act=&gt.rules.internalActivitiesList[ai];
5937 			tmp+=writeStartTagTDofActivities(htmlLevel, act, false, colspan, rowspan, COLOR_BY_SUBJECT_STUDENTS);
5938 			//Each activity has only a single room. So there is no need for line0. Modify this as soon as FET supports multiple rooms per activity.
5939 			tmp+=writeStudents(htmlLevel, act, "div", " class=\"studentsset line1\"");
5940 			tmp+=writeTeachers(htmlLevel, act, "div", " class=\"teacher line2\"");
5941 			tmp+=writeSubjectAndActivityTags(htmlLevel, act, "div", " class=\"line3\"", false, printActivityTags);
5942 			tmp+="</td>\n";
5943 		} else
5944 			tmp+="          <!-- span -->\n";
5945 	} else {
5946 		if(notAllowedRoomTimePercentages[room][day+hour*gt.rules.nDaysPerWeek]>=0 && PRINT_NOT_AVAILABLE_TIME_SLOTS){
5947 			tmp+=writeNotAvailableSlot(htmlLevel, "");
5948 		}
5949 		else if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
5950 			tmp+=writeBreakSlot(htmlLevel, "");
5951 		}
5952 		else{
5953 			tmp+=writeEmpty(htmlLevel);
5954 		}
5955 	}
5956 	return tmp;
5957 }
5958 
5959 //by Volker Dirr
writeActivitiesRooms(int htmlLevel,const QList<int> & allActivities,bool printActivityTags)5960 QString TimetableExport::writeActivitiesRooms(int htmlLevel, const QList<int>& allActivities, bool printActivityTags){
5961 	QString tmp;
5962 	if(htmlLevel>=1)
5963 		tmp+="          <td><table class=\"detailed\">";
5964 	else
5965 		tmp+="          <td><table>";
5966 	if(htmlLevel>=3)
5967 		tmp+="<tr class=\"room line0\">";
5968 	else	tmp+="<tr>";
5969 	for(int a=0; a<allActivities.size(); a++){
5970 		int ai=allActivities[a];
5971 		if(ai!=UNALLOCATED_ACTIVITY){
5972 			Activity* act=&gt.rules.internalActivitiesList[ai];
5973 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeRoom(htmlLevel, ai, "", "")+"</td>";
5974 		}
5975 	}
5976 	tmp+="</tr>";
5977 	if(htmlLevel>=3)
5978 		tmp+="<tr class=\"studentsset line1\">";
5979 	else	tmp+="<tr>";
5980 	for(int a=0; a<allActivities.size(); a++){
5981 		int ai=allActivities[a];
5982 		if(ai!=UNALLOCATED_ACTIVITY){
5983 			Activity* act=&gt.rules.internalActivitiesList[ai];
5984 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeStudents(htmlLevel, act, "", "")+"</td>";
5985 		}
5986 	}
5987 	tmp+="</tr>";
5988 	if(htmlLevel>=3)
5989 		tmp+="<tr class=\"teacher line2\">";
5990 	else	tmp+="<tr>";
5991 	for(int a=0; a<allActivities.size(); a++){
5992 		int ai=allActivities[a];
5993 		if(ai!=UNALLOCATED_ACTIVITY){
5994 			Activity* act=&gt.rules.internalActivitiesList[ai];
5995 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeTeachers(htmlLevel, act, "", "")+"</td>";
5996 		}
5997 	}
5998 	tmp+="</tr>";
5999 	if(htmlLevel>=3)
6000 		tmp+="<tr class=\"line3\">";
6001 	else	tmp+="<tr>";
6002 	for(int a=0; a<allActivities.size(); a++){
6003 		int ai=allActivities[a];
6004 		if(ai!=UNALLOCATED_ACTIVITY){
6005 			Activity* act=&gt.rules.internalActivitiesList[ai];
6006 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeSubjectAndActivityTags(htmlLevel, act, "", "", false, printActivityTags)+"</td>";
6007 		}
6008 	}
6009 	tmp+="</tr>";
6010 
6011 	tmp+="</table></td>\n";
6012 	return tmp;
6013 }
6014 
6015 //by Volker Dirr
writeActivitiesSubjects(int htmlLevel,const QList<int> & allActivities,bool printActivityTags)6016 QString TimetableExport::writeActivitiesSubjects(int htmlLevel, const QList<int>& allActivities, bool printActivityTags){
6017 	QString tmp;
6018 	if(allActivities.isEmpty()){
6019 		tmp+=writeEmpty(htmlLevel);
6020 	} else {
6021 		if(htmlLevel>=1)
6022 			tmp+="          <td><table class=\"detailed\">";
6023 		else
6024 			tmp+="          <td><table>";
6025 		//Each activity has only a single subject. So there is no need for subjects in line0. Modify this as soon as FET supports multiple subjects per activity.
6026 		if(printActivityTags){
6027 			if(htmlLevel>=3)
6028 				tmp+="<tr class=\"line0 activitytag\">";
6029 			else	tmp+="<tr>";
6030 			for(int a=0; a<allActivities.size(); a++){
6031 				Activity* act=&gt.rules.internalActivitiesList[allActivities[a]];
6032 				Activity* act0=&gt.rules.internalActivitiesList[allActivities[0]];	//Because this is always the original subject. We don't need to repeat it, because it is displayed in the tables head
6033 				if(act->subjectName==act0->subjectName){
6034 					tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeSubjectAndActivityTags(htmlLevel, act, "", "", true, printActivityTags)+"</td>";
6035 				} else {
6036 					tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeSubjectAndActivityTags(htmlLevel, act, "", "", false, printActivityTags)+"</td>";
6037 				}
6038 			}
6039 			tmp+="</tr>";
6040 		}
6041 		if(htmlLevel>=3)
6042 			tmp+="<tr class=\"studentsset line1\">";
6043 		else	tmp+="<tr>";
6044 		for(int a=0; a<allActivities.size(); a++){
6045 			Activity* act=&gt.rules.internalActivitiesList[allActivities[a]];
6046 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeStudents(htmlLevel, act, "", "")+"</td>";
6047 		}
6048 		tmp+="</tr>";
6049 		if(htmlLevel>=3)
6050 			tmp+="<tr class=\"teacher line2\">";
6051 		else	tmp+="<tr>";
6052 		for(int a=0; a<allActivities.size(); a++){
6053 			Activity* act=&gt.rules.internalActivitiesList[allActivities[a]];
6054 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeTeachers(htmlLevel, act, "", "")+"</td>";
6055 		}
6056 		tmp+="</tr>";
6057 		if(htmlLevel>=3)
6058 			tmp+="<tr class=\"room line3\">";
6059 		else	tmp+="<tr>";
6060 		for(int a=0; a<allActivities.size(); a++){
6061 			int ai=allActivities[a];
6062 			Activity* act=&gt.rules.internalActivitiesList[ai];
6063 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeRoom(htmlLevel, ai, "", "")+"</td>";
6064 		}
6065 		tmp+="</tr>";
6066 		tmp+="</table></td>\n";
6067 	}
6068 	return tmp;
6069 }
6070 
6071 //by Volker Dirr
writeActivitiesActivityTags(int htmlLevel,const QList<int> & allActivities,bool printActivityTags)6072 QString TimetableExport::writeActivitiesActivityTags(int htmlLevel, const QList<int>& allActivities, bool printActivityTags){
6073 	QString tmp;
6074 	if(allActivities.isEmpty()){
6075 		tmp+=writeEmpty(htmlLevel);
6076 	} else {
6077 		if(htmlLevel>=1)
6078 			tmp+="          <td><table class=\"detailed\">";
6079 		else
6080 			tmp+="          <td><table>";
6081 
6082 		if(htmlLevel>=3)
6083 			tmp+="<tr class=\"line0\">";
6084 		else	tmp+="<tr>";
6085 		for(int a=0; a<allActivities.size(); a++){
6086 			int ai=allActivities[a];
6087 			if(ai!=UNALLOCATED_ACTIVITY){
6088 				Activity* act=&gt.rules.internalActivitiesList[ai];
6089 				tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeSubjectAndActivityTags(htmlLevel, act, "", "", false, printActivityTags)+"</td>";
6090 			}
6091 		}
6092 		tmp+="</tr>";
6093 
6094 		if(htmlLevel>=3)
6095 			tmp+="<tr class=\"studentsset line1\">";
6096 		else	tmp+="<tr>";
6097 		for(int a=0; a<allActivities.size(); a++){
6098 			Activity* act=&gt.rules.internalActivitiesList[allActivities[a]];
6099 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeStudents(htmlLevel, act, "", "")+"</td>";
6100 		}
6101 		tmp+="</tr>";
6102 		if(htmlLevel>=3)
6103 			tmp+="<tr class=\"teacher line2\">";
6104 		else	tmp+="<tr>";
6105 		for(int a=0; a<allActivities.size(); a++){
6106 			Activity* act=&gt.rules.internalActivitiesList[allActivities[a]];
6107 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeTeachers(htmlLevel, act, "", "")+"</td>";
6108 		}
6109 		tmp+="</tr>";
6110 		if(htmlLevel>=3)
6111 			tmp+="<tr class=\"room line3\">";
6112 		else	tmp+="<tr>";
6113 		for(int a=0; a<allActivities.size(); a++){
6114 			int ai=allActivities[a];
6115 			Activity* act=&gt.rules.internalActivitiesList[ai];
6116 			tmp+=writeStartTagTDofActivities(htmlLevel, act, true, false, false, COLOR_BY_SUBJECT_STUDENTS)+writeRoom(htmlLevel, ai, "", "")+"</td>";
6117 		}
6118 		tmp+="</tr>";
6119 		tmp+="</table></td>\n";
6120 	}
6121 	return tmp;
6122 }
6123 
6124 
6125 //the following functions return a single html table (needed for html file export and printing)
6126 
6127 //by Volker Dirr
singleSubgroupsTimetableDaysHorizontalHtml(int htmlLevel,int subgroup,const QString & saveTime,bool printActivityTags,bool repeatNames,const QList<int> & subgroupsSortedOrder)6128 QString TimetableExport::singleSubgroupsTimetableDaysHorizontalHtml(int htmlLevel, int subgroup, const QString& saveTime, bool printActivityTags, bool repeatNames, const QList<int>& subgroupsSortedOrder){
6129 	int realSubgroup;
6130 	if(subgroupsSortedOrder!=QList<int>())
6131 		realSubgroup=subgroupsSortedOrder.at(subgroup);
6132 	else
6133 		realSubgroup=subgroup;
6134 
6135 	assert(realSubgroup>=0);
6136 	assert(realSubgroup<gt.rules.nInternalSubgroups);
6137 	QString tmpString;
6138 	QString subgroup_name = gt.rules.internalSubgroupsList[realSubgroup]->name;
6139 	tmpString+="    <table id=\"table_"+hashStudentIDsTimetable.value(subgroup_name)+"\" border=\"1\"";
6140 	if(subgroup%2==0) tmpString+=" class=\"odd_table\"";
6141 	else tmpString+=" class=\"even_table\"";
6142 	tmpString+=">\n";
6143 
6144 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6145 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(subgroup_name)+"</th>";
6146 	if(repeatNames){
6147 		tmpString+="<td rowspan=\"2\"></td>";
6148 	}
6149 	tmpString+="</tr>\n";
6150 	tmpString+="        <tr>\n          <!-- span -->\n";
6151 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6152 		if(htmlLevel>=2)
6153 			tmpString+="          <th class=\"xAxis\">";
6154 		else
6155 			tmpString+="          <th>";
6156 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
6157 	}
6158 	tmpString+="        </tr>\n";
6159 	tmpString+="      </thead>\n";
6160 	/*workaround
6161 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6162 	*/
6163 	tmpString+="      <tbody>\n";
6164 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6165 		tmpString+="        <tr>\n";
6166 		if(htmlLevel>=2)
6167 			tmpString+="          <th class=\"yAxis\">";
6168 		else
6169 			tmpString+="          <th>";
6170 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6171 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6172 			QList<int> allActivities;
6173 			allActivities.clear();
6174 			allActivities<<students_timetable_weekly[realSubgroup][day][hour];
6175 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6176 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6177 				tmpString+=writeActivityStudents(htmlLevel, students_timetable_weekly[realSubgroup][day][hour], day, hour, subgroupNotAvailableDayHour[realSubgroup][day][hour], false, true, printActivityTags, subgroup_name);
6178 			} else{
6179 				tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6180 			}
6181 		}
6182 		if(repeatNames){
6183 			if(htmlLevel>=2)
6184 				tmpString+="          <th class=\"yAxis\">";
6185 			else
6186 				tmpString+="          <th>";
6187 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6188 		}
6189 		tmpString+="        </tr>\n";
6190 	}
6191 	//workaround begin.
6192 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6193 	if(repeatNames){
6194 		tmpString+="<td></td>";
6195 	}
6196 	tmpString+="</tr>\n";
6197 	//workaround end.
6198 	tmpString+="      </tbody>\n";
6199 	tmpString+="    </table>\n\n";
6200 	return tmpString;
6201 }
6202 
6203 //by Volker Dirr
singleSubgroupsTimetableDaysVerticalHtml(int htmlLevel,int subgroup,const QString & saveTime,bool printActivityTags,bool repeatNames,const QList<int> & subgroupsSortedOrder)6204 QString TimetableExport::singleSubgroupsTimetableDaysVerticalHtml(int htmlLevel, int subgroup, const QString& saveTime, bool printActivityTags, bool repeatNames, const QList<int>& subgroupsSortedOrder){
6205 	int realSubgroup;
6206 	if(subgroupsSortedOrder!=QList<int>())
6207 		realSubgroup=subgroupsSortedOrder.at(subgroup);
6208 	else
6209 		realSubgroup=subgroup;
6210 
6211 	assert(realSubgroup>=0);
6212 	assert(realSubgroup<gt.rules.nInternalSubgroups);
6213 	QString tmpString;
6214 	QString subgroup_name = gt.rules.internalSubgroupsList[realSubgroup]->name;
6215 	tmpString+="    <table id=\"table_"+hashStudentIDsTimetable.value(subgroup_name)+"\" border=\"1\"";
6216 	if(subgroup%2==0) tmpString+=" class=\"odd_table\"";
6217 	else tmpString+=" class=\"even_table\"";
6218 	tmpString+=">\n";
6219 
6220 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6221 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(subgroup_name)+"</th>";
6222 	if(repeatNames){
6223 		tmpString+="<td rowspan=\"2\"></td>";
6224 	}
6225 	tmpString+="</tr>\n";
6226 	tmpString+="        <tr>\n          <!-- span -->\n";
6227 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6228 		if(htmlLevel>=2)
6229 			tmpString+="          <th class=\"xAxis\">";
6230 		else
6231 			tmpString+="          <th>";
6232 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6233 	}
6234 	tmpString+="        </tr>\n";
6235 	tmpString+="      </thead>\n";
6236 	/*workaround
6237 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6238 	*/
6239 	tmpString+="      <tbody>\n";
6240 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6241 		tmpString+="        <tr>\n";
6242 		if(htmlLevel>=2)
6243 			tmpString+="          <th class=\"yAxis\">";
6244 		else
6245 			tmpString+="          <th>";
6246 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
6247 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6248 			QList<int> allActivities;
6249 			allActivities.clear();
6250 			allActivities<<students_timetable_weekly[realSubgroup][day][hour];
6251 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6252 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6253 				tmpString+=writeActivityStudents(htmlLevel, students_timetable_weekly[realSubgroup][day][hour], day, hour, subgroupNotAvailableDayHour[realSubgroup][day][hour], true, false, printActivityTags, subgroup_name);
6254 			} else{
6255 				tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6256 			}
6257 		}
6258 		if(repeatNames){
6259 			if(htmlLevel>=2)
6260 				tmpString+="          <th class=\"yAxis\">";
6261 			else
6262 				tmpString+="          <th>";
6263 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
6264 		}
6265 		tmpString+="        </tr>\n";
6266 	}
6267 	//workaround begin.
6268 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6269 	if(repeatNames){
6270 		tmpString+="<td></td>";
6271 	}
6272 	tmpString+="</tr>\n";
6273 	//workaround end.
6274 	tmpString+="      </tbody>\n";
6275 	tmpString+="    </table>\n\n";
6276 	return tmpString;
6277 }
6278 
6279 //by Volker Dirr
singleSubgroupsTimetableTimeVerticalHtml(int htmlLevel,int maxSubgroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames,const QList<int> & subgroupsSortedOrder)6280 QString TimetableExport::singleSubgroupsTimetableTimeVerticalHtml(int htmlLevel, int maxSubgroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames, const QList<int>& subgroupsSortedOrder){
6281 	QString tmpString;
6282 	tmpString+="    <table border=\"1\">\n";
6283 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6284 
6285 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
6286 
6287 	int currentCount=0;
6288 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups && currentCount<maxSubgroups; subgroup++){
6289 		int realSubgroup;
6290 		if(subgroupsSortedOrder!=QList<int>())
6291 			realSubgroup=subgroupsSortedOrder.at(subgroup);
6292 		else
6293 			realSubgroup=subgroup;
6294 
6295 		if(!excludedNames.contains(subgroup)){
6296 			currentCount++;
6297 			if(htmlLevel>=2)
6298 				tmpString+="          <th class=\"xAxis\">";
6299 			else
6300 				tmpString+="          <th>";
6301 			tmpString+=gt.rules.internalSubgroupsList[realSubgroup]->name+"</th>";
6302 		}
6303 	}
6304 	if(repeatNames){
6305 		tmpString+="<td colspan=\"2\"></td>";
6306 	}
6307 	tmpString+="</tr>\n      </thead>\n";
6308 	/*workaround
6309 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6310 	*/
6311 	tmpString+="      <tbody>\n";
6312 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6313 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6314 			tmpString+="        <tr>\n";
6315 			if(hour==0)
6316 				tmpString+="        <th rowspan=\"" +QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
6317 			else tmpString+="          <!-- span -->\n";
6318 
6319 			if(htmlLevel>=2)
6320 				tmpString+="          <th class=\"yAxis\">";
6321 			else
6322 				tmpString+="          <th>";
6323 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6324 			currentCount=0;
6325 			for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups && currentCount<maxSubgroups; subgroup++){
6326 				int realSubgroup;
6327 				if(subgroupsSortedOrder!=QList<int>())
6328 					realSubgroup=subgroupsSortedOrder.at(subgroup);
6329 				else
6330 					realSubgroup=subgroup;
6331 
6332 				if(!excludedNames.contains(subgroup)){
6333 					currentCount++;
6334 					if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
6335 						excludedNames<<subgroup;
6336 					QList<int> allActivities;
6337 					allActivities.clear();
6338 					allActivities<<students_timetable_weekly[realSubgroup][day][hour];
6339 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6340 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6341 						tmpString+=writeActivityStudents(htmlLevel, students_timetable_weekly[realSubgroup][day][hour], day, hour, subgroupNotAvailableDayHour[realSubgroup][day][hour], false, true, printActivityTags, gt.rules.internalSubgroupsList[realSubgroup]->name);
6342 					} else{
6343 						tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6344 					}
6345 				}
6346 			}
6347 			if(repeatNames){
6348 				if(htmlLevel>=2)
6349 					tmpString+="          <th class=\"yAxis\">";
6350 				else
6351 					tmpString+="          <th>";
6352 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6353 
6354 				if(hour==0)
6355 					tmpString+="        <th rowspan=\"" +QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
6356 				else tmpString+="          <!-- span -->\n";
6357 			}
6358 			tmpString+="        </tr>\n";
6359 		}
6360 	}
6361 	//workaround begin.
6362 	tmpString+="      <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6363 	if(repeatNames){
6364 		tmpString+="<td colspan=\"2\"></td>";
6365 	}
6366 	tmpString+="</tr>\n";
6367 	//workaround end.
6368 	tmpString+="      </tbody>\n    </table>\n";
6369 	return tmpString;
6370 }
6371 
6372 //by Volker Dirr
singleSubgroupsTimetableTimeHorizontalHtml(int htmlLevel,int maxSubgroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames,const QList<int> & subgroupsSortedOrder)6373 QString TimetableExport::singleSubgroupsTimetableTimeHorizontalHtml(int htmlLevel, int maxSubgroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames, const QList<int>& subgroupsSortedOrder){
6374 	QString tmpString;
6375 	tmpString+="    <table border=\"1\">\n";
6376 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6377 
6378 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
6379 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6380 		tmpString+="<th colspan=\"" +QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
6381 	}
6382 	if(repeatNames){
6383 		tmpString+="<td rowspan=\"2\"></td>";
6384 	}
6385 	tmpString+="        </tr>\n";
6386 	tmpString+="        <tr>\n          <!-- span -->\n";
6387 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6388 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6389 			if(htmlLevel>=2)
6390 				tmpString+="          <th class=\"xAxis\">";
6391 			else
6392 				tmpString+="          <th>";
6393 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6394 		}
6395 	}
6396 	tmpString+="        </tr>\n";
6397 	tmpString+="      </thead>\n";
6398 	/*workaround
6399 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6400 	*/
6401 	tmpString+="      <tbody>\n";
6402 
6403 	int currentCount=0;
6404 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups && currentCount<maxSubgroups; subgroup++){
6405 		int realSubgroup;
6406 		if(subgroupsSortedOrder!=QList<int>())
6407 			realSubgroup=subgroupsSortedOrder.at(subgroup);
6408 		else
6409 			realSubgroup=subgroup;
6410 
6411 		if(!excludedNames.contains(subgroup)){
6412 			currentCount++;
6413 			excludedNames<<subgroup;
6414 			tmpString+="        <tr>\n";
6415 			if(htmlLevel>=2)
6416 				tmpString+="          <th class=\"yAxis\">";
6417 			else
6418 				tmpString+="          <th>";
6419 			tmpString+=protect2(gt.rules.internalSubgroupsList[realSubgroup]->name)+"</th>\n";
6420 			for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6421 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6422 					QList<int> allActivities;
6423 					allActivities.clear();
6424 					allActivities<<students_timetable_weekly[realSubgroup][day][hour];
6425 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6426 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6427 						tmpString+=writeActivityStudents(htmlLevel, students_timetable_weekly[realSubgroup][day][hour], day, hour, subgroupNotAvailableDayHour[realSubgroup][day][hour], true, false, printActivityTags, gt.rules.internalSubgroupsList[realSubgroup]->name);
6428 					} else{
6429 						tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6430 					}
6431 				}
6432 			}
6433 			if(repeatNames){
6434 				if(htmlLevel>=2)
6435 					tmpString+="          <th class=\"yAxis\">";
6436 				else
6437 					tmpString+="          <th>";
6438 				tmpString+=protect2(gt.rules.internalSubgroupsList[realSubgroup]->name)+"</th>\n";
6439 			}
6440 			tmpString+="        </tr>\n";
6441 		}
6442 	}
6443 	//workaround begin.
6444 	tmpString+="      <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6445 	if(repeatNames){
6446 		tmpString+="<td></td>";
6447 	}
6448 	tmpString+="</tr>\n";
6449 	//workaround end.
6450 	tmpString+="      </tbody>\n    </table>\n";
6451 	return tmpString;
6452 }
6453 
6454 //by Volker Dirr
singleSubgroupsTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxSubgroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames,const QList<int> & subgroupsSortedOrder)6455 QString TimetableExport::singleSubgroupsTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxSubgroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames, const QList<int>& subgroupsSortedOrder){
6456 	assert(day>=0);
6457 	assert(day<gt.rules.nDaysPerWeek);
6458 
6459 	QString tmpString;
6460 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
6461 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6462 
6463 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
6464 	int currentCount=0;
6465 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups && currentCount<maxSubgroups; subgroup++){
6466 		int realSubgroup;
6467 		if(subgroupsSortedOrder!=QList<int>())
6468 			realSubgroup=subgroupsSortedOrder.at(subgroup);
6469 		else
6470 			realSubgroup=subgroup;
6471 
6472 		if(!excludedNames.contains(subgroup)){
6473 			currentCount++;
6474 			if(htmlLevel>=2)
6475 				tmpString+="          <th class=\"xAxis\">";
6476 			else
6477 				tmpString+="          <th>";
6478 			tmpString+=gt.rules.internalSubgroupsList[realSubgroup]->name+"</th>";
6479 		}
6480 	}
6481 	if(repeatNames){
6482 		tmpString+="<td colspan=\"2\"></td>";
6483 	}
6484 	tmpString+="</tr>\n      </thead>\n";
6485 	/*workaround
6486 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6487 	*/
6488 	tmpString+="      <tbody>\n";
6489 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6490 		tmpString+="        <tr>\n";
6491 		if(hour==0)
6492 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
6493 		else tmpString+="          <!-- span -->\n";
6494 		if(htmlLevel>=2)
6495 			tmpString+="          <th class=\"yAxis\">";
6496 		else
6497 			tmpString+="          <th>";
6498 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6499 		currentCount=0;
6500 		for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups && currentCount<maxSubgroups; subgroup++){
6501 			int realSubgroup;
6502 			if(subgroupsSortedOrder!=QList<int>())
6503 				realSubgroup=subgroupsSortedOrder.at(subgroup);
6504 			else
6505 				realSubgroup=subgroup;
6506 
6507 			if(!excludedNames.contains(subgroup)){
6508 				currentCount++;
6509 				if(hour+1==gt.rules.nHoursPerDay)
6510 					excludedNames<<subgroup;
6511 				QList<int> allActivities;
6512 				allActivities.clear();
6513 				allActivities<<students_timetable_weekly[realSubgroup][day][hour];
6514 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6515 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6516 					tmpString+=writeActivityStudents(htmlLevel, students_timetable_weekly[realSubgroup][day][hour], day, hour, subgroupNotAvailableDayHour[realSubgroup][day][hour], false, true, printActivityTags, gt.rules.internalSubgroupsList[realSubgroup]->name);
6517 				} else{
6518 					tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6519 				}
6520 			}
6521 		}
6522 		if(repeatNames){
6523 			if(htmlLevel>=2)
6524 				tmpString+="          <th class=\"yAxis\">";
6525 			else
6526 				tmpString+="          <th>";
6527 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6528 
6529 			if(hour==0)
6530 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
6531 			else tmpString+="          <!-- span -->\n";
6532 		}
6533 		tmpString+="        </tr>\n";
6534 	}
6535 	//workaround begin.
6536 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6537 	if(repeatNames){
6538 		tmpString+="<td colspan=\"2\"></td>";
6539 	}
6540 	tmpString+="</tr>\n";
6541 	//workaround end.
6542 	tmpString+="      </tbody>\n";
6543 	tmpString+="    </table>\n\n";
6544 	return tmpString;
6545 }
6546 
6547 //by Volker Dirr
singleSubgroupsTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxSubgroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames,const QList<int> & subgroupsSortedOrder)6548 QString TimetableExport::singleSubgroupsTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxSubgroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames, const QList<int>& subgroupsSortedOrder){
6549 	assert(day>=0);
6550 	assert(day<gt.rules.nDaysPerWeek);
6551 	QString tmpString;
6552 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
6553 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6554 
6555 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
6556 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
6557 	if(repeatNames){
6558 		tmpString+="<td rowspan=\"2\"></td>";
6559 	}
6560 	tmpString+="        </tr>\n";
6561 	tmpString+="        <tr>\n          <!-- span -->\n";
6562 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6563 		if(htmlLevel>=2)
6564 			tmpString+="          <th class=\"xAxis\">";
6565 		else
6566 			tmpString+="          <th>";
6567 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6568 	}
6569 	tmpString+="        </tr>\n";
6570 	tmpString+="      </thead>\n";
6571 	/*workaround
6572 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6573 	*/
6574 	tmpString+="      <tbody>\n";
6575 	int currentCount=0;
6576 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups && currentCount<maxSubgroups; subgroup++){
6577 		int realSubgroup;
6578 		if(subgroupsSortedOrder!=QList<int>())
6579 			realSubgroup=subgroupsSortedOrder.at(subgroup);
6580 		else
6581 			realSubgroup=subgroup;
6582 
6583 		if(!excludedNames.contains(subgroup)){
6584 			currentCount++;
6585 			excludedNames<<subgroup;
6586 
6587 			tmpString+="        <tr>\n";
6588 			if(htmlLevel>=2)
6589 				tmpString+="          <th class=\"yAxis\">";
6590 			else
6591 				tmpString+="          <th>";
6592 			tmpString+=gt.rules.internalSubgroupsList[realSubgroup]->name+"</th>\n";
6593 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6594 				QList<int> allActivities;
6595 				allActivities.clear();
6596 				allActivities<<students_timetable_weekly[realSubgroup][day][hour];
6597 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6598 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6599 					tmpString+=writeActivityStudents(htmlLevel, students_timetable_weekly[realSubgroup][day][hour], day, hour, subgroupNotAvailableDayHour[realSubgroup][day][hour], true, false, printActivityTags, gt.rules.internalSubgroupsList[realSubgroup]->name);
6600 				} else{
6601 					tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6602 				}
6603 			}
6604 			if(repeatNames){
6605 				if(htmlLevel>=2)
6606 					tmpString+="          <th class=\"yAxis\">";
6607 				else
6608 					tmpString+="          <th>";
6609 				tmpString+=gt.rules.internalSubgroupsList[realSubgroup]->name+"</th>\n";
6610 			}
6611 			tmpString+="        </tr>\n";
6612 		}
6613 	}
6614 	//workaround begin.
6615 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6616 	if(repeatNames){
6617 		tmpString+="<td></td>";
6618 	}
6619 	tmpString+="</tr>\n";
6620 	//workaround end.
6621 	tmpString+="      </tbody>\n";
6622 	tmpString+="    </table>\n\n";
6623 	return tmpString;
6624 }
6625 
6626 //by Volker Dirr
singleGroupsTimetableDaysHorizontalHtml(int htmlLevel,int group,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)6627 QString TimetableExport::singleGroupsTimetableDaysHorizontalHtml(int htmlLevel, int group, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
6628 	assert(group>=0);
6629 	assert(group<gt.rules.internalGroupsList.size());
6630 	QString tmpString;
6631 	tmpString+="    <table id=\"table_"+hashStudentIDsTimetable.value(gt.rules.internalGroupsList[group]->name);
6632 	tmpString+="\" border=\"1\"";
6633 	if(group%2) tmpString+=" class=\"even_table\"";
6634 	else tmpString+=" class=\"odd_table\"";
6635 	tmpString+=">\n";
6636 
6637 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6638 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(gt.rules.internalGroupsList[group]->name)+"</th>";
6639 	if(repeatNames){
6640 		tmpString+="<td rowspan=\"2\"></td>";
6641 	}
6642 	tmpString+="</tr>\n";
6643 	tmpString+="        <tr>\n          <!-- span -->\n";
6644 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6645 		if(htmlLevel>=2)
6646 			tmpString+="          <th class=\"xAxis\">";
6647 		else
6648 			tmpString+="          <th>";
6649 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
6650 	}
6651 	tmpString+="        </tr>\n";
6652 	tmpString+="      </thead>\n";
6653 	/*workaround
6654 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6655 	*/
6656 	tmpString+="      <tbody>\n";
6657 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6658 		tmpString+="        <tr>\n";
6659 		if(htmlLevel>=2)
6660 			tmpString+="          <th class=\"yAxis\">";
6661 		else
6662 			tmpString+="          <th>";
6663 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6664 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6665 			QList<int> allActivities;
6666 			allActivities.clear();
6667 			bool isNotAvailable=true;
6668 			for(int sg=0; sg<gt.rules.internalGroupsList[group]->subgroupsList.size(); sg++){
6669 				StudentsSubgroup* sts=gt.rules.internalGroupsList[group]->subgroupsList[sg];
6670 				int subgroup=sts->indexInInternalSubgroupsList;
6671 				if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
6672 					allActivities<<students_timetable_weekly[subgroup][day][hour];
6673 				if(!subgroupNotAvailableDayHour[subgroup][day][hour])
6674 					isNotAvailable=false;
6675 				}
6676 			assert(!allActivities.isEmpty());
6677 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6678 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6679 				tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, false, true, printActivityTags, gt.rules.internalGroupsList[group]->name);
6680 			} else{
6681 				if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
6682 				else{
6683 					tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6684 				}
6685 			}
6686 		}
6687 		if(repeatNames){
6688 			if(htmlLevel>=2)
6689 				tmpString+="          <th class=\"yAxis\">";
6690 			else
6691 				tmpString+="          <th>";
6692 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6693 		}
6694 		tmpString+="        </tr>\n";
6695 	}
6696 	//workaround begin.
6697 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6698 	if(repeatNames){
6699 		tmpString+="<td></td>";
6700 	}
6701 	tmpString+="</tr>\n";
6702 	//workaround end.
6703 	tmpString+="      </tbody>\n";
6704 	tmpString+="    </table>\n\n";
6705 	return tmpString;
6706 }
6707 
6708 //by Volker Dirr
singleGroupsTimetableDaysVerticalHtml(int htmlLevel,int group,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)6709 QString TimetableExport::singleGroupsTimetableDaysVerticalHtml(int htmlLevel, int group, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
6710 	assert(group>=0);
6711 	assert(group<gt.rules.internalGroupsList.size());
6712 	QString tmpString;
6713 	tmpString+="    <table id=\"table_"+hashStudentIDsTimetable.value(gt.rules.internalGroupsList.at(group)->name);
6714 	tmpString+="\" border=\"1\"";
6715 	if(group%2) tmpString+=" class=\"even_table\"";
6716 	else tmpString+=" class=\"odd_table\"";
6717 	tmpString+=">\n";
6718 
6719 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6720 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.internalGroupsList.at(group)->name)+"</th>";
6721 	if(repeatNames){
6722 		tmpString+="<td rowspan=\"2\"></td>";
6723 	}
6724 	tmpString+="</tr>\n";
6725 	tmpString+="        <tr>\n          <!-- span -->\n";
6726 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6727 		if(htmlLevel>=2)
6728 			tmpString+="          <th class=\"xAxis\">";
6729 		else
6730 			tmpString+="          <th>";
6731 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6732 	}
6733 	tmpString+="        </tr>\n";
6734 	tmpString+="      </thead>\n";
6735 	/*workaround
6736 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6737 	*/
6738 	tmpString+="      <tbody>\n";
6739 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6740 		tmpString+="        <tr>\n";
6741 		if(htmlLevel>=2)
6742 			tmpString+="          <th class=\"yAxis\">";
6743 		else
6744 			tmpString+="          <th>";
6745 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
6746 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6747 			QList<int> allActivities;
6748 			allActivities.clear();
6749 			bool isNotAvailable=true;
6750 			for(int sg=0; sg<gt.rules.internalGroupsList.at(group)->subgroupsList.size(); sg++){
6751 				StudentsSubgroup* sts=gt.rules.internalGroupsList.at(group)->subgroupsList[sg];
6752 				int subgroup=sts->indexInInternalSubgroupsList;
6753 				if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
6754 					allActivities<<students_timetable_weekly[subgroup][day][hour];
6755 				if(!subgroupNotAvailableDayHour[subgroup][day][hour])
6756 					isNotAvailable=false;
6757 			}
6758 			assert(!allActivities.isEmpty());
6759 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6760 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6761 				tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, true, false, printActivityTags, gt.rules.internalGroupsList[group]->name);
6762 			} else{
6763 				if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
6764 				else{
6765 					tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6766 				}
6767 			}
6768 		}
6769 		if(repeatNames){
6770 			if(htmlLevel>=2)
6771 				tmpString+="          <th class=\"yAxis\">";
6772 			else
6773 				tmpString+="          <th>";
6774 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
6775 		}
6776 		tmpString+="        </tr>\n";
6777 	}
6778 	//workaround begin.
6779 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6780 	if(repeatNames){
6781 		tmpString+="<td></td>";
6782 	}
6783 	tmpString+="</tr>\n";
6784 	//workaround end.
6785 	tmpString+="      </tbody>\n";
6786 	tmpString+="    </table>\n\n";
6787 	return tmpString;
6788 }
6789 
6790 //by Volker Dirr
singleGroupsTimetableTimeVerticalHtml(int htmlLevel,int maxGroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)6791 QString TimetableExport::singleGroupsTimetableTimeVerticalHtml(int htmlLevel, int maxGroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
6792 	QString tmpString;
6793 	tmpString+="    <table id=\"table";
6794 	tmpString+="\" border=\"1\">\n";
6795 		tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6796 		tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
6797 	int currentCount=0;
6798 	for(int group=0; group<gt.rules.internalGroupsList.size() && currentCount<maxGroups; group++){
6799 		if(!excludedNames.contains(group)){
6800 			currentCount++;
6801 			if(htmlLevel>=2)
6802 				tmpString+="          <th class=\"xAxis\">";
6803 			else
6804 				tmpString+="          <th>";
6805 			tmpString+=protect2(gt.rules.internalGroupsList.at(group)->name)+"</th>";
6806 		}
6807 	}
6808 	if(repeatNames){
6809 		tmpString+="<td colspan=\"2\"></td>";
6810 	}
6811 	tmpString+="</tr>\n      </thead>\n";
6812 	/*workaround
6813 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6814 	*/
6815 	tmpString+="      <tbody>\n";
6816 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6817 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6818 			currentCount=0;
6819 			tmpString+="        <tr>\n";
6820 			if(hour==0)
6821 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
6822 			else tmpString+="          <!-- span -->\n";
6823 			if(htmlLevel>=2)
6824 				tmpString+="          <th class=\"yAxis\">";
6825 			else
6826 				tmpString+="          <th>";
6827 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6828 			for(int group=0; group<gt.rules.internalGroupsList.size() && currentCount<maxGroups; group++){
6829 				if(!excludedNames.contains(group)){
6830 					currentCount++;
6831 					if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
6832 						excludedNames<<group;
6833 					QList<int> allActivities;
6834 					allActivities.clear();
6835 					bool isNotAvailable=true;
6836 					for(int sg=0; sg<gt.rules.internalGroupsList.at(group)->subgroupsList.size(); sg++){
6837 						StudentsSubgroup* sts=gt.rules.internalGroupsList.at(group)->subgroupsList[sg];
6838 						int subgroup=sts->indexInInternalSubgroupsList;
6839 						if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
6840 							allActivities<<students_timetable_weekly[subgroup][day][hour];
6841 						if(!subgroupNotAvailableDayHour[subgroup][day][hour])
6842 							isNotAvailable=false;
6843 					}
6844 					assert(!allActivities.isEmpty());
6845 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6846 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6847 						tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, false, true, printActivityTags, gt.rules.internalGroupsList[group]->name);
6848 					} else{
6849 						if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
6850 						else{
6851 							tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6852 						}
6853 					}
6854 				}
6855 			}
6856 			if(repeatNames){
6857 				if(htmlLevel>=2)
6858 					tmpString+="          <th class=\"yAxis\">";
6859 				else
6860 					tmpString+="          <th>";
6861 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6862 				if(hour==0)
6863 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
6864 				else tmpString+="          <!-- span -->\n";
6865 			}
6866 			tmpString+="        </tr>\n";
6867 		}
6868 	}
6869 	//workaround begin.
6870 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6871 	if(repeatNames){
6872 		tmpString+="<td colspan=\"2\"></td>";
6873 	}
6874 	tmpString+="</tr>\n";
6875 	//workaround end.
6876 	tmpString+="      </tbody>\n";
6877 	tmpString+="    </table>\n\n";
6878 	return tmpString;
6879 }
6880 
6881 //by Volker Dirr
singleGroupsTimetableTimeHorizontalHtml(int htmlLevel,int maxGroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)6882 QString TimetableExport::singleGroupsTimetableTimeHorizontalHtml(int htmlLevel, int maxGroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
6883 	QString tmpString;
6884 	tmpString+="    <table id=\"table";
6885 	tmpString+="\" border=\"1\">\n";
6886 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6887 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
6888 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
6889 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
6890 	if(repeatNames){
6891 		tmpString+="<td rowspan=\"2\"></td>";
6892 	}
6893 	tmpString+="</tr>\n";
6894 	tmpString+="        <tr>\n          <!-- span -->\n";
6895 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6896 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6897 			if(htmlLevel>=2)
6898 				tmpString+="          <th class=\"xAxis\">";
6899 			else
6900 				tmpString+="          <th>";
6901 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
6902 		}
6903 	}
6904 	tmpString+="        </tr>\n";
6905 	tmpString+="      </thead>\n";
6906 	/*workaround
6907 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6908 	*/
6909 	tmpString+="      <tbody>\n";
6910 
6911 	int currentCount=0;
6912 	for(int group=0; group<gt.rules.internalGroupsList.size() && currentCount<maxGroups; group++){
6913 		if(!excludedNames.contains(group)){
6914 			currentCount++;
6915 			excludedNames<<group;
6916 
6917 			tmpString+="        <tr>\n";
6918 			if(htmlLevel>=2)
6919 				tmpString+="          <th class=\"yAxis\">";
6920 			else
6921 				tmpString+="          <th>";
6922 			tmpString+=protect2(gt.rules.internalGroupsList.at(group)->name)+"</th>\n";
6923 			for(int day=0; day<gt.rules.nDaysPerWeek; day++){
6924 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6925 					QList<int> allActivities;
6926 					allActivities.clear();
6927 					bool isNotAvailable=true;
6928 					for(int sg=0; sg<gt.rules.internalGroupsList.at(group)->subgroupsList.size(); sg++){
6929 						StudentsSubgroup* sts=gt.rules.internalGroupsList.at(group)->subgroupsList[sg];
6930 						int subgroup=sts->indexInInternalSubgroupsList;
6931 						if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
6932 							allActivities<<students_timetable_weekly[subgroup][day][hour];
6933 						if(!subgroupNotAvailableDayHour[subgroup][day][hour])
6934 							isNotAvailable=false;
6935 					}
6936 					assert(!allActivities.isEmpty());
6937 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
6938 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
6939 						tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, true, false, printActivityTags, gt.rules.internalGroupsList[group]->name);
6940 					} else{
6941 						if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
6942 						else{
6943 							tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
6944 						}
6945 					}
6946 				}
6947 			}
6948 			if(repeatNames){
6949 				if(htmlLevel>=2)
6950 					tmpString+="          <th class=\"yAxis\">";
6951 				else
6952 					tmpString+="          <th>";
6953 				tmpString+=protect2(gt.rules.internalGroupsList.at(group)->name)+"</th>\n";
6954 			}
6955 		}
6956 		tmpString+="        </tr>\n";
6957 	}
6958 	//workaround begin.
6959 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
6960 	if(repeatNames){
6961 		tmpString+="<td></td>";
6962 	}
6963 	tmpString+="</tr>\n";
6964 	//workaround end.
6965 	tmpString+="      </tbody>\n";
6966 	tmpString+="    </table>\n\n";
6967 	return tmpString;
6968 }
6969 
6970 //by Volker Dirr
singleGroupsTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxGroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)6971 QString TimetableExport::singleGroupsTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxGroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
6972 	assert(day>=0);
6973 	assert(day<gt.rules.nDaysPerWeek);
6974 	QString tmpString;
6975 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day]);
6976 	tmpString+="\" border=\"1\">\n";
6977 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
6978 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
6979 	int currentCount=0;
6980 	for(int group=0; group<gt.rules.internalGroupsList.size() && currentCount<maxGroups; group++){
6981 		if(!excludedNames.contains(group)){
6982 			currentCount++;
6983 			if(htmlLevel>=2)
6984 				tmpString+="          <th class=\"xAxis\">";
6985 			else
6986 				tmpString+="          <th>";
6987 			tmpString+=gt.rules.internalGroupsList.at(group)->name+"</th>";
6988 		}
6989 	}
6990 	if(repeatNames){
6991 		tmpString+="<td colspan=\"2\"></td>";
6992 	}
6993 	tmpString+="</tr>\n      </thead>\n";
6994 	/*workaround
6995 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
6996 	*/
6997 	tmpString+="      <tbody>\n";
6998 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
6999 		currentCount=0;
7000 		tmpString+="        <tr>\n";
7001 		if(hour==0)
7002 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">" + protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7003 		else tmpString+="          <!-- span -->\n";
7004 		if(htmlLevel>=2)
7005 			tmpString+="          <th class=\"yAxis\">";
7006 		else
7007 			tmpString+="          <th>";
7008 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7009 		for(int group=0; group<gt.rules.internalGroupsList.size() && currentCount<maxGroups; group++){
7010 			if(!excludedNames.contains(group)){
7011 				currentCount++;
7012 				if(hour+1==gt.rules.nHoursPerDay)
7013 					excludedNames<<group;
7014 				QList<int> allActivities;
7015 				allActivities.clear();
7016 				bool isNotAvailable=true;
7017 				for(int sg=0; sg<gt.rules.internalGroupsList.at(group)->subgroupsList.size(); sg++){
7018 					StudentsSubgroup* sts=gt.rules.internalGroupsList.at(group)->subgroupsList[sg];
7019 					int subgroup=sts->indexInInternalSubgroupsList;
7020 					if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7021 						allActivities<<students_timetable_weekly[subgroup][day][hour];
7022 					if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7023 						isNotAvailable=false;
7024 				}
7025 				assert(!allActivities.isEmpty());
7026 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7027 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7028 					tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, false, true, printActivityTags, gt.rules.internalGroupsList[group]->name);
7029 				} else{
7030 					if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7031 					else{
7032 						tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7033 					}
7034 				}
7035 			}
7036 		}
7037 		if(repeatNames){
7038 			if(htmlLevel>=2)
7039 				tmpString+="          <th class=\"yAxis\">";
7040 			else
7041 				tmpString+="          <th>";
7042 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7043 			if(hour==0)
7044 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">" + protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7045 			else tmpString+="          <!-- span -->\n";
7046 		}
7047 		tmpString+="        </tr>\n";
7048 	}
7049 	//workaround begin.
7050 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7051 	if(repeatNames){
7052 		tmpString+="<td colspan=\"2\"></td>";
7053 	}
7054 	tmpString+="</tr>\n";
7055 	//workaround end.
7056 	tmpString+="      </tbody>\n";
7057 	tmpString+="    </table>\n\n";
7058 	return tmpString;
7059 }
7060 
7061 //by Volker Dirr
singleGroupsTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxGroups,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7062 QString TimetableExport::singleGroupsTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxGroups, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7063 	assert(day>=0);
7064 	assert(day<gt.rules.nDaysPerWeek);
7065 	QString tmpString;
7066 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day]);
7067 	tmpString+="\" border=\"1\">\n";
7068 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7069 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
7070 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
7071 	if(repeatNames){
7072 		tmpString+="<td rowspan=\"2\"></td>";
7073 	}
7074 	tmpString+="</tr>\n";
7075 	tmpString+="        <tr>\n          <!-- span -->\n";
7076 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7077 		if(htmlLevel>=2)
7078 			tmpString+="          <th class=\"xAxis\">";
7079 		else
7080 			tmpString+="          <th>";
7081 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7082 	}
7083 	tmpString+="        </tr>\n";
7084 	tmpString+="      </thead>\n";
7085 	/*workaround
7086 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7087 	*/
7088 	tmpString+="      <tbody>\n";
7089 
7090 	int currentCount=0;
7091 	for(int group=0; group<gt.rules.internalGroupsList.size() && currentCount<maxGroups; group++){
7092 		if(!excludedNames.contains(group)){
7093 			currentCount++;
7094 			excludedNames<<group;
7095 
7096 			tmpString+="        <tr>\n";
7097 			if(htmlLevel>=2)
7098 				tmpString+="          <th class=\"yAxis\">";
7099 			else
7100 				tmpString+="          <th>";
7101 			tmpString+=protect2(gt.rules.internalGroupsList.at(group)->name)+"</th>\n";
7102 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7103 				QList<int> allActivities;
7104 				allActivities.clear();
7105 				bool isNotAvailable=true;
7106 				for(int sg=0; sg<gt.rules.internalGroupsList.at(group)->subgroupsList.size(); sg++){
7107 					StudentsSubgroup* sts=gt.rules.internalGroupsList.at(group)->subgroupsList[sg];
7108 					int subgroup=sts->indexInInternalSubgroupsList;
7109 					if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7110 						allActivities<<students_timetable_weekly[subgroup][day][hour];
7111 					if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7112 						isNotAvailable=false;
7113 				}
7114 				assert(!allActivities.isEmpty());
7115 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7116 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7117 					tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, true, false, printActivityTags, gt.rules.internalGroupsList[group]->name);
7118 				} else{
7119 					if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7120 					else{
7121 						tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7122 					}
7123 				}
7124 			}
7125 			if(repeatNames){
7126 				if(htmlLevel>=2)
7127 					tmpString+="          <th class=\"yAxis\">";
7128 				else
7129 					tmpString+="          <th>";
7130 				tmpString+=protect2(gt.rules.internalGroupsList.at(group)->name)+"</th>\n";
7131 			}
7132 			tmpString+="        </tr>\n";
7133 		}
7134 	}
7135 	//workaround begin.
7136 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7137 	if(repeatNames){
7138 		tmpString+="<td></td>";
7139 	}
7140 	tmpString+="</tr>\n";
7141 	//workaround end.
7142 	tmpString+="      </tbody>\n";
7143 	tmpString+="    </table>\n\n";
7144 	return tmpString;
7145 }
7146 
7147 //by Volker Dirr
singleYearsTimetableDaysHorizontalHtml(int htmlLevel,int year,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7148 QString TimetableExport::singleYearsTimetableDaysHorizontalHtml(int htmlLevel, int year, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7149 	assert(year>=0);
7150 	assert(year<gt.rules.augmentedYearsList.size());
7151 	QString tmpString;
7152 	tmpString+="    <table id=\"table_"+hashStudentIDsTimetable.value(gt.rules.augmentedYearsList.at(year)->name);
7153 	tmpString+="\" border=\"1\"";
7154 	if(year%2)  tmpString+=" class=\"even_table\"";
7155 	else tmpString+=" class=\"odd_table\"";
7156 	tmpString+=">\n";
7157 
7158 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7159 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(gt.rules.augmentedYearsList.at(year)->name)+"</th>";
7160 	if(repeatNames){
7161 		tmpString+="<td rowspan=\"2\"></td>";
7162 	}
7163 	tmpString+="</tr>\n";
7164 	tmpString+="        <tr>\n          <!-- span -->\n";
7165 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7166 		if(htmlLevel>=2)
7167 			tmpString+="          <th class=\"xAxis\">";
7168 		else
7169 			tmpString+="          <th>";
7170 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
7171 	}
7172 	tmpString+="        </tr>\n";
7173 	tmpString+="      </thead>\n";
7174 	/*workaround
7175 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7176 	*/
7177 	tmpString+="      <tbody>\n";
7178 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7179 		tmpString+="        <tr>\n";
7180 		if(htmlLevel>=2)
7181 			tmpString+="          <th class=\"yAxis\">";
7182 		else
7183 			tmpString+="          <th>";
7184 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7185 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7186 			QList<int> allActivities;
7187 			allActivities.clear();
7188 			bool isNotAvailable=true;
7189 			for(int g=0; g<gt.rules.augmentedYearsList.at(year)->groupsList.size(); g++){
7190 				StudentsGroup* stg=gt.rules.augmentedYearsList.at(year)->groupsList[g];
7191 				for(int sg=0; sg<stg->subgroupsList.size(); sg++){
7192 					StudentsSubgroup* sts=stg->subgroupsList[sg];
7193 					int subgroup=sts->indexInInternalSubgroupsList;
7194 					if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7195 						allActivities<<students_timetable_weekly[subgroup][day][hour];
7196 					if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7197 						isNotAvailable=false;
7198 				}
7199 			}
7200 			assert(!allActivities.isEmpty());
7201 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7202 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7203 				tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, false, true, printActivityTags, gt.rules.augmentedYearsList.at(year)->name);
7204 			} else{
7205 				if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7206 				else{
7207 					tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7208 				}
7209 			}
7210 		}
7211 		if(repeatNames){
7212 			if(htmlLevel>=2)
7213 				tmpString+="          <th class=\"yAxis\">";
7214 			else
7215 				tmpString+="          <th>";
7216 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7217 		}
7218 		tmpString+="        </tr>\n";
7219 	}
7220 	//workaround begin.
7221 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7222 	if(repeatNames){
7223 		tmpString+="<td></td>";
7224 	}
7225 	tmpString+="</tr>\n";
7226 	//workaround end.
7227 	tmpString+="      </tbody>\n";
7228 	tmpString+="    </table>\n\n";
7229 	return tmpString;
7230 }
7231 
7232 //by Volker Dirr
singleYearsTimetableDaysVerticalHtml(int htmlLevel,int year,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7233 QString TimetableExport::singleYearsTimetableDaysVerticalHtml(int htmlLevel, int year, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7234 	assert(year>=0);
7235 	assert(year<gt.rules.augmentedYearsList.size());
7236 	QString tmpString;
7237 	tmpString+="    <table id=\"table_"+hashStudentIDsTimetable.value(gt.rules.augmentedYearsList.at(year)->name);
7238 	tmpString+="\" border=\"1\"";
7239 	if(year%2)  tmpString+=" class=\"even_table\"";
7240 	else tmpString+=" class=\"odd_table\"";
7241 	tmpString+=">\n";
7242 
7243 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7244 
7245 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.augmentedYearsList.at(year)->name)+"</th>";
7246 	if(repeatNames){
7247 		tmpString+="<td rowspan=\"2\"></td>";
7248 	}
7249 	tmpString+="</tr>\n";
7250 	tmpString+="        <tr>\n          <!-- span -->\n";
7251 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7252 		if(htmlLevel>=2)
7253 			tmpString+="          <th class=\"xAxis\">";
7254 		else
7255 			tmpString+="          <th>";
7256 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7257 	}
7258 	tmpString+="        </tr>\n";
7259 	tmpString+="      </thead>\n";
7260 	/*workaround
7261 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7262 	*/
7263 	tmpString+="      <tbody>\n";
7264 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7265 		tmpString+="        <tr>\n";
7266 		if(htmlLevel>=2)
7267 			tmpString+="          <th class=\"yAxis\">";
7268 		else
7269 			tmpString+="          <th>";
7270 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
7271 
7272 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7273 			QList<int> allActivities;
7274 			allActivities.clear();
7275 			bool isNotAvailable=true;
7276 			for(int g=0; g<gt.rules.augmentedYearsList.at(year)->groupsList.size(); g++){
7277 				StudentsGroup* stg=gt.rules.augmentedYearsList.at(year)->groupsList[g];
7278 				for(int sg=0; sg<stg->subgroupsList.size(); sg++){
7279 					StudentsSubgroup* sts=stg->subgroupsList[sg];
7280 					int subgroup=sts->indexInInternalSubgroupsList;
7281 					if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7282 						allActivities<<students_timetable_weekly[subgroup][day][hour];
7283 					if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7284 						isNotAvailable=false;
7285 				}
7286 			}
7287 			assert(!allActivities.isEmpty());
7288 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7289 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7290 				tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, true, false, printActivityTags, gt.rules.augmentedYearsList.at(year)->name);
7291 			} else{
7292 				if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7293 				else{
7294 					tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7295 				}
7296 			}
7297 		}
7298 		if(repeatNames){
7299 			if(htmlLevel>=2)
7300 				tmpString+="          <th class=\"yAxis\">";
7301 			else
7302 				tmpString+="          <th>";
7303 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
7304 		}
7305 		tmpString+="        </tr>\n";
7306 	}
7307 	//workaround begin.
7308 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7309 	if(repeatNames){
7310 		tmpString+="<td></td>";
7311 	}
7312 	tmpString+="</tr>\n";
7313 	//workaround end.
7314 	tmpString+="      </tbody>\n";
7315 	tmpString+="    </table>\n\n";
7316 	return tmpString;
7317 }
7318 
7319 //by Volker Dirr
singleYearsTimetableTimeVerticalHtml(int htmlLevel,int maxYears,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7320 QString TimetableExport::singleYearsTimetableTimeVerticalHtml(int htmlLevel, int maxYears, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7321 	QString tmpString;
7322 	tmpString+="    <table id=\"table";
7323 	tmpString+="\" border=\"1\">\n";
7324 
7325 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7326 
7327 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
7328 	int currentCount=0;
7329 	for(int year=0; year<gt.rules.augmentedYearsList.size() && currentCount<maxYears; year++){
7330 		if(!excludedNames.contains(year)){
7331 			currentCount++;
7332 			if(htmlLevel>=2)
7333 				tmpString+="          <th class=\"xAxis\">";
7334 			else
7335 				tmpString+="          <th>";
7336 			tmpString+=protect2(gt.rules.augmentedYearsList.at(year)->name)+"</th>";
7337 		}
7338 	}
7339 	if(repeatNames){
7340 		tmpString+="<td colspan=\"2\"></td>";
7341 	}
7342 	tmpString+="</tr>\n      </thead>\n";
7343 	/*workaround
7344 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7345 	*/
7346 	tmpString+="      <tbody>\n";
7347 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7348 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7349 			tmpString+="        <tr>\n";
7350 			if(hour==0)
7351 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7352 			else tmpString+="          <!-- span -->\n";
7353 			if(htmlLevel>=2)
7354 				tmpString+="          <th class=\"yAxis\">";
7355 			else
7356 				tmpString+="          <th>";
7357 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour]) + "</th>\n";
7358 			currentCount=0;
7359 			for(int year=0; year<gt.rules.augmentedYearsList.size() && currentCount<maxYears; year++){
7360 				if(!excludedNames.contains(year)){
7361 					currentCount++;
7362 					if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
7363 						excludedNames<<year;
7364 					QList<int> allActivities;
7365 					allActivities.clear();
7366 					bool isNotAvailable=true;
7367 					StudentsYear* sty=gt.rules.augmentedYearsList[year];
7368 					for(int g=0; g<sty->groupsList.size(); g++){
7369 						StudentsGroup* stg=sty->groupsList[g];
7370 						for(int sg=0; sg<stg->subgroupsList.size(); sg++){
7371 							StudentsSubgroup* sts=stg->subgroupsList[sg];
7372 							int subgroup=sts->indexInInternalSubgroupsList;
7373 							if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7374 								allActivities<<students_timetable_weekly[subgroup][day][hour];
7375 							if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7376 								isNotAvailable=false;
7377 						}
7378 					}
7379 					assert(!allActivities.isEmpty());
7380 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7381 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7382 						tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, false, true, printActivityTags, gt.rules.augmentedYearsList.at(year)->name);
7383 					} else{
7384 						if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7385 						else{
7386 							tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7387 						}
7388 					}
7389 				}
7390 			}
7391 			if(repeatNames){
7392 				if(htmlLevel>=2)
7393 					tmpString+="          <th class=\"yAxis\">";
7394 				else
7395 					tmpString+="          <th>";
7396 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour]) + "</th>\n";
7397 				if(hour==0)
7398 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7399 				else tmpString+="          <!-- span -->\n";
7400 			}
7401 			tmpString+="        </tr>\n";
7402 		}
7403 	}
7404 	//workaround begin.
7405 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7406 	if(repeatNames){
7407 		tmpString+="<td colspan=\"2\"></td>";
7408 	}
7409 	tmpString+="</tr>\n";
7410 	//workaround end.
7411 	tmpString+="      </tbody>\n";
7412 	tmpString+="    </table>\n\n";
7413 	return tmpString;
7414 }
7415 
7416 //by Volker Dirr
singleYearsTimetableTimeHorizontalHtml(int htmlLevel,int maxYears,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7417 QString TimetableExport::singleYearsTimetableTimeHorizontalHtml(int htmlLevel, int maxYears, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7418 	QString tmpString;
7419 	tmpString+="    <table id=\"table";
7420 	tmpString+="\" border=\"1\">\n";
7421 
7422 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7423 
7424 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
7425 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
7426 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
7427 	if(repeatNames){
7428 		tmpString+="<td rowspan=\"2\"></td>";
7429 	}
7430 	tmpString+="</tr>\n";
7431 	tmpString+="        <tr>\n          <!-- span -->\n";
7432 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7433 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7434 			if(htmlLevel>=2)
7435 				tmpString+="          <th class=\"xAxis\">";
7436 			else
7437 				tmpString+="          <th>";
7438 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7439 		}
7440 	}
7441 	tmpString+="        </tr>\n";
7442 	tmpString+="      </thead>\n";
7443 	/*workaround
7444 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7445 	*/
7446 	tmpString+="      <tbody>\n";
7447 
7448 	int currentCount=0;
7449 	for(int year=0; year<gt.rules.augmentedYearsList.size() && currentCount<maxYears; year++){
7450 		StudentsYear* sty=gt.rules.augmentedYearsList[year];
7451 		if(!excludedNames.contains(year)){
7452 			currentCount++;
7453 			excludedNames<<year;
7454 			tmpString+="        <tr>\n";
7455 			if(htmlLevel>=2)
7456 				tmpString+="          <th class=\"yAxis\">";
7457 			else
7458 				tmpString+="          <th>";
7459 			tmpString+=protect2(sty->name)+"</th>\n";
7460 			for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7461 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7462 					QList<int> allActivities;
7463 					allActivities.clear();
7464 					bool isNotAvailable=true;
7465 					for(int g=0; g<sty->groupsList.size(); g++){
7466 						StudentsGroup* stg=sty->groupsList[g];
7467 						for(int sg=0; sg<stg->subgroupsList.size(); sg++){
7468 							StudentsSubgroup* sts=stg->subgroupsList[sg];
7469 							int subgroup=sts->indexInInternalSubgroupsList;
7470 							if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7471 								allActivities<<students_timetable_weekly[subgroup][day][hour];
7472 							if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7473 								isNotAvailable=false;
7474 						}
7475 					}
7476 					assert(!allActivities.isEmpty());
7477 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7478 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7479 						tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, true, false, printActivityTags, gt.rules.augmentedYearsList.at(year)->name);
7480 					} else{
7481 						if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7482 						else{
7483 							tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7484 						}
7485 					}
7486 				}
7487 			}
7488 			if(repeatNames){
7489 				if(htmlLevel>=2)
7490 					tmpString+="          <th class=\"yAxis\">";
7491 				else
7492 					tmpString+="          <th>";
7493 				tmpString+=protect2(sty->name)+"</th>\n";
7494 			}
7495 			tmpString+="        </tr>\n";
7496 		}
7497 	}
7498 	//workaround begin.
7499 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7500 	if(repeatNames){
7501 		tmpString+="<td></td>";
7502 	}
7503 	tmpString+="</tr>\n";
7504 	//workaround end.
7505 	tmpString+="      </tbody>\n";
7506 	tmpString+="    </table>\n\n";
7507 	return tmpString;
7508 }
7509 
7510 //by Volker Dirr
singleYearsTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxYears,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7511 QString TimetableExport::singleYearsTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxYears, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7512 	assert(day>=0);
7513 	assert(day<gt.rules.nDaysPerWeek);
7514 	QString tmpString;
7515 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day]);
7516 	tmpString+="\" border=\"1\">\n";
7517 
7518 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7519 
7520 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
7521 	int currentCount=0;
7522 	for(int year=0; year<gt.rules.augmentedYearsList.size() && currentCount<maxYears; year++){
7523 		if(!excludedNames.contains(year)){
7524 			currentCount++;
7525 
7526 			if(htmlLevel>=2)
7527 				tmpString+="          <th class=\"xAxis\">";
7528 			else
7529 				tmpString+="          <th>";
7530 			tmpString+=protect2(gt.rules.augmentedYearsList.at(year)->name)+"</th>";
7531 		}
7532 	}
7533 	if(repeatNames){
7534 		tmpString+="<td colspan=\"2\"></td>";
7535 	}
7536 	tmpString+="</tr>\n      </thead>\n";
7537 	/*workaround
7538 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7539 	*/
7540 	tmpString+="      <tbody>\n";
7541 
7542 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7543 		tmpString+="        <tr>\n";
7544 		if(hour==0)
7545 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7546 		else tmpString+="          <!-- span -->\n";
7547 		if(htmlLevel>=2)
7548 			tmpString+="          <th class=\"yAxis\">";
7549 		else
7550 			tmpString+="          <th>";
7551 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7552 		currentCount=0;
7553 		for(int year=0; year<gt.rules.augmentedYearsList.size() && currentCount<maxYears; year++){
7554 			StudentsYear* sty=gt.rules.augmentedYearsList[year];
7555 			if(!excludedNames.contains(year)){
7556 				currentCount++;
7557 				if(hour+1==gt.rules.nHoursPerDay)
7558 					excludedNames<<year;
7559 				QList<int> allActivities;
7560 				allActivities.clear();
7561 				bool isNotAvailable=true;
7562 				for(int g=0; g<sty->groupsList.size(); g++){
7563 					StudentsGroup* stg=sty->groupsList[g];
7564 					for(int sg=0; sg<stg->subgroupsList.size(); sg++){
7565 						StudentsSubgroup* sts=stg->subgroupsList[sg];
7566 						int subgroup=sts->indexInInternalSubgroupsList;
7567 						if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7568 							allActivities<<students_timetable_weekly[subgroup][day][hour];
7569 						if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7570 							isNotAvailable=false;
7571 					}
7572 				}
7573 				assert(!allActivities.isEmpty());
7574 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7575 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7576 					tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, false, true, printActivityTags, gt.rules.augmentedYearsList.at(year)->name);
7577 				} else{
7578 					if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7579 					else{
7580 						tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7581 					}
7582 				}
7583 			}
7584 		}
7585 		if(repeatNames){
7586 			if(htmlLevel>=2)
7587 				tmpString+="          <th class=\"yAxis\">";
7588 			else
7589 				tmpString+="          <th>";
7590 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7591 			if(hour==0)
7592 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7593 			else tmpString+="          <!-- span -->\n";
7594 		}
7595 		tmpString+="        </tr>\n";
7596 	}
7597 	//workaround begin.
7598 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7599 	if(repeatNames){
7600 		tmpString+="<td colspan=\"2\"></td>";
7601 	}
7602 	tmpString+="</tr>\n";
7603 	//workaround end.
7604 	tmpString+="      </tbody>\n";
7605 	tmpString+="    </table>\n\n";
7606 	return tmpString;
7607 }
7608 
7609 //by Volker Dirr
singleYearsTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxYears,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool detailed,bool repeatNames)7610 QString TimetableExport::singleYearsTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxYears, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool detailed, bool repeatNames){
7611 	assert(day>=0);
7612 	assert(day<gt.rules.nDaysPerWeek);
7613 	QString tmpString;
7614 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day]);
7615 	tmpString+="\" border=\"1\">\n";
7616 
7617 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7618 
7619 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
7620 
7621 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
7622 	if(repeatNames){
7623 		tmpString+="<td rowspan=\"2\"></td>";
7624 	}
7625 	tmpString+="</tr>\n";
7626 	tmpString+="        <tr>\n          <!-- span -->\n";
7627 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7628 		if(htmlLevel>=2)
7629 			tmpString+="          <th class=\"xAxis\">";
7630 		else
7631 			tmpString+="          <th>";
7632 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7633 	}
7634 	tmpString+="        </tr>\n";
7635 	tmpString+="      </thead>\n";
7636 	/*workaround
7637 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7638 	*/
7639 	tmpString+="      <tbody>\n";
7640 	int currentCount=0;
7641 	for(int year=0; year<gt.rules.augmentedYearsList.size() && currentCount<maxYears; year++){
7642 		if(!excludedNames.contains(year)){
7643 			currentCount++;
7644 			excludedNames<<year;
7645 			tmpString+="        <tr>\n";
7646 			if(htmlLevel>=2)
7647 				tmpString+="          <th class=\"yAxis\">";
7648 			else
7649 				tmpString+="          <th>";
7650 			StudentsYear* sty=gt.rules.augmentedYearsList[year];
7651 			tmpString+=protect2(sty->name)+"</th>\n";
7652 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7653 				QList<int> allActivities;
7654 				allActivities.clear();
7655 				bool isNotAvailable=true;
7656 				for(int g=0; g<sty->groupsList.size(); g++){
7657 					StudentsGroup* stg=sty->groupsList[g];
7658 					for(int sg=0; sg<stg->subgroupsList.size(); sg++){
7659 						StudentsSubgroup* sts=stg->subgroupsList[sg];
7660 						int subgroup=sts->indexInInternalSubgroupsList;
7661 						if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour])))
7662 							allActivities<<students_timetable_weekly[subgroup][day][hour];
7663 						if(!subgroupNotAvailableDayHour[subgroup][day][hour])
7664 							isNotAvailable=false;
7665 					}
7666 				}
7667 				assert(!allActivities.isEmpty());
7668 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
7669 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
7670 					tmpString+=writeActivityStudents(htmlLevel, allActivities[0], day, hour, isNotAvailable, true, false, printActivityTags, gt.rules.augmentedYearsList.at(year)->name);
7671 				} else{
7672 					if(!detailed) tmpString+="          <td>"+protect2(STRING_SEVERAL_ACTIVITIES_IN_LESS_DETAILED_TABLES)+"</td>\n";
7673 					else{
7674 						tmpString+=writeActivitiesStudents(htmlLevel, allActivities, printActivityTags);
7675 					}
7676 				}
7677 			}
7678 			if(repeatNames){
7679 				if(htmlLevel>=2)
7680 					tmpString+="          <th class=\"yAxis\">";
7681 				else
7682 					tmpString+="          <th>";
7683 				tmpString+=protect2(sty->name)+"</th>\n";
7684 			}
7685 			tmpString+="        </tr>\n";
7686 		}
7687 	}
7688 	//workaround begin.
7689 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7690 	if(repeatNames){
7691 		tmpString+="<td></td>";
7692 	}
7693 	tmpString+="</tr>\n";
7694 	//workaround end.
7695 	tmpString+="      </tbody>\n";
7696 	tmpString+="    </table>\n\n";
7697 	return tmpString;
7698 }
7699 
7700 //by Volker Dirr
singleAllActivitiesTimetableDaysHorizontalHtml(int htmlLevel,const QString & saveTime,bool printActivityTags,bool repeatNames)7701 QString TimetableExport::singleAllActivitiesTimetableDaysHorizontalHtml(int htmlLevel, const QString& saveTime, bool printActivityTags, bool repeatNames){
7702 	QString tmpString;
7703 	tmpString+="    <table border=\"1\">\n";
7704 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7705 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+tr("All Activities")+"</th>";
7706 	if(repeatNames){
7707 		tmpString+="<td rowspan=\"2\"></td>";
7708 	}
7709 	tmpString+="</tr>\n";
7710 	tmpString+="        <tr>\n          <!-- span -->\n";
7711 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7712 		if(htmlLevel>=2)
7713 			tmpString+="          <th class=\"xAxis\">";
7714 		else
7715 			tmpString+="          <th>";
7716 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
7717 	}
7718 	tmpString+="        </tr>\n";
7719 	tmpString+="      </thead>\n";
7720 	/*workaround
7721 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7722 	*/
7723 	tmpString+="      <tbody>\n";
7724 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7725 		tmpString+="        <tr>\n";
7726 		if(htmlLevel>=2)
7727 			tmpString+="          <th class=\"yAxis\">";
7728 		else
7729 			tmpString+="          <th>";
7730 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7731 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7732 			if(activitiesAtTime[day][hour].isEmpty()){
7733 				if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
7734 					tmpString+=writeBreakSlot(htmlLevel, "");
7735 				} else {
7736 					tmpString+=writeEmpty(htmlLevel);
7737 				}
7738 			} else {
7739 				tmpString+=writeActivitiesStudents(htmlLevel, activitiesAtTime[day][hour], printActivityTags);
7740 			}
7741 		}
7742 		if(repeatNames){
7743 			if(htmlLevel>=2)
7744 				tmpString+="          <th class=\"yAxis\">";
7745 			else
7746 				tmpString+="          <th>";
7747 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7748 		}
7749 		tmpString+="        </tr>\n";
7750 	}
7751 	//workaround begin.
7752 	tmpString+="      <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7753 	if(repeatNames){
7754 		tmpString+="<td></td>";
7755 	}
7756 	tmpString+="</tr>\n";
7757 	//workaround end.
7758 	tmpString+="      </tbody>\n";
7759 	tmpString+="    </table>\n";
7760 	return tmpString;
7761 }
7762 
7763 //by Volker Dirr
singleAllActivitiesTimetableDaysVerticalHtml(int htmlLevel,const QString & saveTime,bool printActivityTags,bool repeatNames)7764 QString TimetableExport::singleAllActivitiesTimetableDaysVerticalHtml(int htmlLevel, const QString& saveTime, bool printActivityTags, bool repeatNames){
7765 	QString tmpString;
7766 	tmpString+="    <table border=\"1\">\n";
7767 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7768 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+tr("All Activities")+"</th>";
7769 	if(repeatNames){
7770 		tmpString+="<td rowspan=\"2\"></td>";
7771 	}
7772 	tmpString+="</tr>\n";
7773 	tmpString+="        <tr>\n          <!-- span -->\n";
7774 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7775 		if(htmlLevel>=2)
7776 			tmpString+="          <th class=\"xAxis\">";
7777 		else
7778 			tmpString+="          <th>";
7779 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7780 	}
7781 	tmpString+="        </tr>\n";
7782 	tmpString+="      </thead>\n";
7783 	/*workaround
7784 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7785 	*/
7786 	tmpString+="      <tbody>\n";
7787 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7788 		tmpString+="        <tr>\n";
7789 		if(htmlLevel>=2)
7790 			tmpString+="          <th class=\"yAxis\">";
7791 		else
7792 			tmpString+="          <th>";
7793 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
7794 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7795 			if(activitiesAtTime[day][hour].isEmpty()){
7796 				if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
7797 					tmpString+=writeBreakSlot(htmlLevel, "");
7798 				} else {
7799 					tmpString+=writeEmpty(htmlLevel);
7800 				}
7801 			} else {
7802 				tmpString+=writeActivitiesStudents(htmlLevel, activitiesAtTime[day][hour], printActivityTags);
7803 			}
7804 		}
7805 		if(repeatNames){
7806 			if(htmlLevel>=2)
7807 				tmpString+="          <th class=\"yAxis\">";
7808 			else
7809 				tmpString+="          <th>";
7810 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
7811 		}
7812 		tmpString+="        </tr>\n";
7813 	}
7814 	//workaround begin.
7815 	tmpString+="      <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7816 	if(repeatNames){
7817 		tmpString+="<td></td>";
7818 	}
7819 	tmpString+="</tr>\n";
7820 	//workaround end.
7821 	tmpString+="      </tbody>\n";
7822 	tmpString+="    </table>\n";
7823 	return tmpString;
7824 }
7825 
7826 
7827 //by Volker Dirr
singleAllActivitiesTimetableTimeVerticalHtml(int htmlLevel,const QString & saveTime,bool printActivityTags,bool repeatNames)7828 QString TimetableExport::singleAllActivitiesTimetableTimeVerticalHtml(int htmlLevel, const QString& saveTime, bool printActivityTags, bool repeatNames){
7829 QString tmpString;
7830 	tmpString+="    <table border=\"1\">\n";
7831 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7832 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
7833 	if(htmlLevel>=2)
7834 		tmpString+="          <th class=\"xAxis\">";
7835 	else
7836 		tmpString+="          <th>";
7837 	tmpString+=tr("All Activities");
7838 	tmpString+="</th>";
7839 	if(repeatNames){
7840 		tmpString+="<td colspan=\"2\"></td>";
7841 	}
7842 	tmpString+="</tr>\n      </thead>\n";
7843 	/*workaround
7844 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td>"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7845 	*/
7846 	tmpString+="      <tbody>\n";
7847 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7848 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7849 			tmpString+="        <tr>\n";
7850 			if(hour==0)
7851 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7852 			else tmpString+="          <!-- span -->\n";
7853 			if(htmlLevel>=2)
7854 				tmpString+="          <th class=\"yAxis\">";
7855 			else
7856 				tmpString+="          <th>";
7857 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7858 			if(activitiesAtTime[day][hour].isEmpty()){
7859 				if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
7860 					tmpString+=writeBreakSlot(htmlLevel, "");
7861 				} else {
7862 					tmpString+=writeEmpty(htmlLevel);
7863 				}
7864 			} else {
7865 				tmpString+=writeActivitiesStudents(htmlLevel, activitiesAtTime[day][hour], printActivityTags);
7866 			}
7867 			if(repeatNames){
7868 				if(htmlLevel>=2)
7869 					tmpString+="          <th class=\"yAxis\">";
7870 				else
7871 					tmpString+="          <th>";
7872 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7873 				if(hour==0)
7874 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7875 				else tmpString+="          <!-- span -->\n";
7876 			}
7877 			tmpString+="        </tr>\n";
7878 		}
7879 	}
7880 	//workaround begin.
7881 	tmpString+="      <tr class=\"foot\"><td colspan=\"2\"></td><td>"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7882 	if(repeatNames){
7883 		tmpString+="<td colspan=\"2\"></td>";
7884 	}
7885 	tmpString+="</tr>\n";
7886 	//workaround end.
7887 	tmpString+="      </tbody>\n";
7888 	tmpString+="    </table>\n";
7889 	return tmpString;
7890 }
7891 
7892 //by Volker Dirr
singleAllActivitiesTimetableTimeHorizontalHtml(int htmlLevel,const QString & saveTime,bool printActivityTags,bool repeatNames)7893 QString TimetableExport::singleAllActivitiesTimetableTimeHorizontalHtml(int htmlLevel, const QString& saveTime, bool printActivityTags, bool repeatNames){
7894 
7895 	QString tmpString;
7896 	tmpString+="    <table border=\"1\">\n";
7897 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7898 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
7899 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7900 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay) +"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
7901 	}
7902 	if(repeatNames){
7903 		tmpString+="<td></td>";
7904 	}
7905 	tmpString+="</tr>\n";
7906 	tmpString+="        <tr>\n          <!-- span -->\n";
7907 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
7908 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7909 			if(htmlLevel>=2)
7910 				tmpString+="          <th class=\"xAxis\">";
7911 			else
7912 				tmpString+="          <th>";
7913 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7914 		}
7915 	tmpString+="        </tr>\n";
7916 	tmpString+="      </thead>\n";
7917 	/*workaround
7918 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7919 	*/
7920 	tmpString+="      <tbody>\n";
7921 
7922 	tmpString+="        <tr>\n";
7923 	if(htmlLevel>=2)
7924 		tmpString+="          <th class=\"yAxis\">";
7925 	else
7926 		tmpString+="          <th>";
7927 	tmpString+=tr("All Activities")+"</th>\n";
7928 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
7929 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7930 			if(activitiesAtTime[day][hour].isEmpty()){
7931 				if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
7932 					tmpString+=writeBreakSlot(htmlLevel, "");
7933 				} else {
7934 					tmpString+=writeEmpty(htmlLevel);
7935 				}
7936 			} else {
7937 				tmpString+=writeActivitiesStudents(htmlLevel, activitiesAtTime[day][hour], printActivityTags);
7938 			}
7939 		}
7940 	}
7941 	if(repeatNames){
7942 		if(htmlLevel>=2)
7943 			tmpString+="          <th class=\"yAxis\">";
7944 		else
7945 			tmpString+="          <th>";
7946 		tmpString+=tr("All Activities")+"</th>\n";
7947 	}
7948 	tmpString+="        </tr>\n";
7949 	//workaround begin.
7950 	tmpString+="      <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
7951 	if(repeatNames){
7952 		tmpString+="<td></td>";
7953 	}
7954 	tmpString+="</tr>\n";
7955 	//workaround end.
7956 	tmpString+="      </tbody>\n";
7957 	tmpString+="    </table>\n";
7958 	return tmpString;
7959 }
7960 
7961 //by Volker Dirr
singleAllActivitiesTimetableTimeVerticalDailyHtml(int htmlLevel,int day,const QString & saveTime,bool printActivityTags,bool repeatNames)7962 QString TimetableExport::singleAllActivitiesTimetableTimeVerticalDailyHtml(int htmlLevel, int day, const QString& saveTime, bool printActivityTags, bool repeatNames){
7963 	assert(day>=0);
7964 	assert(day<gt.rules.nDaysPerWeek);
7965 	QString tmpString;
7966 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
7967 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
7968 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
7969 	if(htmlLevel>=2)
7970 		tmpString+="          <th class=\"xAxis\">";
7971 	else
7972 		tmpString+="          <th>";
7973 	tmpString+=tr("All Activities");
7974 	tmpString+="</th>";
7975 	if(repeatNames){
7976 		tmpString+="<td colspan=\"2\"></td>";
7977 	}
7978 	tmpString+="</tr>\n      </thead>\n";
7979 	/*workaround
7980 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td>"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
7981 	*/
7982 	tmpString+="      <tbody>\n";
7983 
7984 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
7985 		tmpString+="        <tr>\n";
7986 		if(hour==0)
7987 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
7988 		else tmpString+="          <!-- span -->\n";
7989 		if(htmlLevel>=2)
7990 			tmpString+="          <th class=\"yAxis\">";
7991 		else
7992 			tmpString+="          <th>";
7993 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
7994 		if(activitiesAtTime[day][hour].isEmpty()){
7995 			if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
7996 				tmpString+=writeBreakSlot(htmlLevel, "");
7997 			} else {
7998 				tmpString+=writeEmpty(htmlLevel);
7999 			}
8000 		} else {
8001 			tmpString+=writeActivitiesStudents(htmlLevel, activitiesAtTime[day][hour], printActivityTags);
8002 		}
8003 		if(repeatNames){
8004 			if(htmlLevel>=2)
8005 				tmpString+="          <th class=\"yAxis\">";
8006 			else
8007 				tmpString+="          <th>";
8008 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8009 			if(hour==0)
8010 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8011 			else tmpString+="          <!-- span -->\n";
8012 		}
8013 		tmpString+="        </tr>\n";
8014 	}
8015 	//workaround begin.
8016 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td>"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8017 	if(repeatNames){
8018 		tmpString+="<td colspan=\"2\"></td>";
8019 	}
8020 	tmpString+="</tr>\n";
8021 	//workaround end.
8022 	tmpString+="      </tbody>\n";
8023 	tmpString+="    </table>\n\n";
8024 	return tmpString;
8025 }
8026 
8027 //by Volker Dirr
singleAllActivitiesTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,const QString & saveTime,bool printActivityTags,bool repeatNames)8028 QString TimetableExport::singleAllActivitiesTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, const QString& saveTime, bool printActivityTags, bool repeatNames){
8029 	assert(day>=0);
8030 	assert(day<gt.rules.nDaysPerWeek);
8031 	QString tmpString;
8032 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
8033 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8034 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
8035 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
8036 	if(repeatNames){
8037 		tmpString+="<td rowspan=\"2\"></td>";
8038 	}
8039 	tmpString+="</tr>\n";
8040 	tmpString+="        <tr>\n          <!-- span -->\n";
8041 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8042 		if(htmlLevel>=2)
8043 			tmpString+="          <th class=\"xAxis\">";
8044 		else
8045 			tmpString+="          <th>";
8046 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8047 	}
8048 	tmpString+="        </tr>\n";
8049 	tmpString+="      </thead>\n";
8050 	/*workaround
8051 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8052 	*/
8053 	tmpString+="      <tbody>\n";
8054 
8055 	tmpString+="        <tr>\n";
8056 	if(htmlLevel>=2)
8057 		tmpString+="          <th class=\"yAxis\">";
8058 	else
8059 		tmpString+="          <th>";
8060 	tmpString+=tr("All Activities")+"</th>\n";
8061 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8062 		if(activitiesAtTime[day][hour].isEmpty()){
8063 			if(breakDayHour[day][hour] && PRINT_BREAK_TIME_SLOTS){
8064 				tmpString+=writeBreakSlot(htmlLevel, "");
8065 			} else {
8066 				tmpString+=writeEmpty(htmlLevel);
8067 			}
8068 		} else {
8069 			tmpString+=writeActivitiesStudents(htmlLevel, activitiesAtTime[day][hour], printActivityTags);
8070 		}
8071 	}
8072 	if(repeatNames){
8073 		if(htmlLevel>=2)
8074 			tmpString+="          <th class=\"yAxis\">";
8075 		else
8076 			tmpString+="          <th>";
8077 		tmpString+=tr("All Activities")+"</th>\n";
8078 	}
8079 	tmpString+="        </tr>\n";
8080 	//workaround begin.
8081 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8082 	if(repeatNames){
8083 		tmpString+="<td></td>";
8084 	}
8085 	tmpString+="</tr>\n";
8086 	//workaround end.
8087 	tmpString+="      </tbody>\n";
8088 	tmpString+="    </table>\n\n";
8089 	return tmpString;
8090 }
8091 
8092 //by Volker Dirr
singleTeachersTimetableDaysHorizontalHtml(int htmlLevel,int teacher,const QString & saveTime,bool printActivityTags,bool repeatNames)8093 QString TimetableExport::singleTeachersTimetableDaysHorizontalHtml(int htmlLevel, int teacher, const QString& saveTime, bool printActivityTags, bool repeatNames){
8094 	assert(teacher>=0);
8095 	assert(teacher<gt.rules.nInternalTeachers);
8096 	QString tmpString;
8097 	QString teacher_name = gt.rules.internalTeachersList[teacher]->name;
8098 	tmpString+="    <table id=\"table_"+hashTeacherIDsTimetable.value(teacher_name)+"\" border=\"1\"";
8099 	if(teacher%2==0)  tmpString+=" class=\"odd_table\"";
8100 	else tmpString+=" class=\"even_table\"";
8101 	tmpString+=">\n";
8102 
8103 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8104 
8105 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(teacher_name)+"</th>";
8106 	if(repeatNames){
8107 		tmpString+="<td rowspan=\"2\"></td>";
8108 	}
8109 	tmpString+="</tr>\n";
8110 	tmpString+="        <tr>\n          <!-- span -->\n";
8111 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8112 		if(htmlLevel>=2)
8113 			tmpString+="          <th class=\"xAxis\">";
8114 		else
8115 			tmpString+="          <th>";
8116 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
8117 	}
8118 	tmpString+="        </tr>\n";
8119 	tmpString+="      </thead>\n";
8120 	/*workaround
8121 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8122 	*/
8123 	tmpString+="      <tbody>\n";
8124 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8125 		tmpString+="        <tr>\n";
8126 		if(htmlLevel>=2)
8127 			tmpString+="          <th class=\"yAxis\">";
8128 		else
8129 			tmpString+="          <th>";
8130 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8131 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8132 			QList<int> allActivities;
8133 			allActivities.clear();
8134 			allActivities<<teachers_timetable_weekly[teacher][day][hour];
8135 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8136 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8137 				tmpString+=writeActivityTeacher(htmlLevel, teacher, day, hour, false, true, printActivityTags, teacher_name);
8138 			} else{
8139 				tmpString+=writeActivitiesTeachers(htmlLevel, allActivities, printActivityTags);
8140 			}
8141 		}
8142 		if(repeatNames){
8143 			if(htmlLevel>=2)
8144 				tmpString+="          <th class=\"yAxis\">";
8145 			else
8146 				tmpString+="          <th>";
8147 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8148 		}
8149 		tmpString+="        </tr>\n";
8150 	}
8151 	//workaround begin.
8152 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8153 	if(repeatNames){
8154 		tmpString+="<td></td>";
8155 	}
8156 	tmpString+="</tr>\n";
8157 	//workaround end.
8158 	tmpString+="      </tbody>\n";
8159 	tmpString+="    </table>\n\n";
8160 	return tmpString;
8161 }
8162 
8163 
8164 //by Volker Dirr
singleTeachersTimetableDaysVerticalHtml(int htmlLevel,int teacher,const QString & saveTime,bool printActivityTags,bool repeatNames)8165 QString TimetableExport::singleTeachersTimetableDaysVerticalHtml(int htmlLevel, int teacher, const QString& saveTime, bool printActivityTags, bool repeatNames){
8166 	assert(teacher>=0);
8167 	assert(teacher<gt.rules.nInternalTeachers);
8168 	QString tmpString;
8169 	QString teacher_name = gt.rules.internalTeachersList[teacher]->name;
8170 	tmpString+="    <table id=\"table_"+hashTeacherIDsTimetable.value(teacher_name)+"\" border=\"1\"";
8171 	if(teacher%2==0) tmpString+=" class=\"odd_table\"";
8172 	else tmpString+=" class=\"even_table\"";
8173 	tmpString+=">\n";
8174 
8175 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8176 
8177 	tmpString+="      <thead>\n";
8178 	tmpString+="        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(teacher_name)+"</th>";
8179 	if(repeatNames){
8180 		tmpString+="<td rowspan=\"2\"></td>";
8181 	}
8182 	tmpString+="</tr>\n";
8183 	tmpString+="        <tr>\n          <!-- span -->\n";
8184 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8185 		if(htmlLevel>=2)
8186 			tmpString+="          <th class=\"xAxis\">";
8187 		else
8188 			tmpString+="          <th>";
8189 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8190 	}
8191 	tmpString+="        </tr>\n";
8192 	tmpString+="      </thead>\n";
8193 	/*workaround
8194 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8195 	*/
8196 	tmpString+="      <tbody>\n";
8197 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8198 		tmpString+="        <tr>\n";
8199 		if(htmlLevel>=2)
8200 			tmpString+="          <th class=\"yAxis\">";
8201 		else
8202 			tmpString+="          <th>";
8203 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
8204 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8205 			QList<int> allActivities;
8206 			allActivities.clear();
8207 			allActivities<<teachers_timetable_weekly[teacher][day][hour];
8208 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8209 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8210 				tmpString+=writeActivityTeacher(htmlLevel, teacher, day, hour, true, false, printActivityTags, teacher_name);
8211 			} else{
8212 				tmpString+=writeActivitiesTeachers(htmlLevel, allActivities, printActivityTags);
8213 			}
8214 		}
8215 		if(repeatNames){
8216 			if(htmlLevel>=2)
8217 				tmpString+="          <th class=\"yAxis\">";
8218 			else
8219 				tmpString+="          <th>";
8220 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
8221 		}
8222 		tmpString+="        </tr>\n";
8223 	}
8224 	//workaround begin.
8225 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8226 	if(repeatNames){
8227 		tmpString+="<td></td>";
8228 	}
8229 	tmpString+="</tr>\n";
8230 	//workaround end.
8231 	tmpString+="      </tbody>\n";
8232 	tmpString+="    </table>\n\n";
8233 	return tmpString;
8234 }
8235 
8236 
8237 //by Volker Dirr
singleTeachersTimetableTimeVerticalHtml(int htmlLevel,int maxTeachers,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8238 QString TimetableExport::singleTeachersTimetableTimeVerticalHtml(int htmlLevel, int maxTeachers, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8239 QString tmpString;
8240 	tmpString+="    <table border=\"1\">\n";
8241 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8242 
8243 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
8244 	int currentCount=0;
8245 	for(int teacher=0; teacher<gt.rules.nInternalTeachers && currentCount<maxTeachers; teacher++){
8246 		if(!excludedNames.contains(teacher)){
8247 			currentCount++;
8248 			if(htmlLevel>=2)
8249 				tmpString+="          <th class=\"xAxis\">";
8250 			else
8251 				tmpString+="          <th>";
8252 			tmpString+=gt.rules.internalTeachersList[teacher]->name+"</th>";
8253 		}
8254 	}
8255 	if(repeatNames){
8256 		tmpString+="<td colspan=\"2\"></td>";
8257 	}
8258 	tmpString+="</tr>\n      </thead>\n";
8259 	/*workaround
8260 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8261 	*/
8262 	tmpString+="      <tbody>\n";
8263 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8264 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8265 			tmpString+="        <tr>\n";
8266 			if(hour==0)
8267 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8268 			else tmpString+="          <!-- span -->\n";
8269 			if(htmlLevel>=2)
8270 				tmpString+="          <th class=\"yAxis\">";
8271 			else
8272 				tmpString+="          <th>";
8273 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8274 			currentCount=0;
8275 			for(int teacher=0; teacher<gt.rules.nInternalTeachers && currentCount<maxTeachers; teacher++){
8276 				if(!excludedNames.contains(teacher)){
8277 					currentCount++;
8278 					if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
8279 						excludedNames<<teacher;
8280 					QList<int> allActivities;
8281 					allActivities.clear();
8282 					allActivities<<teachers_timetable_weekly[teacher][day][hour];
8283 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8284 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8285 						tmpString+=writeActivityTeacher(htmlLevel, teacher, day, hour, false, true, printActivityTags, gt.rules.internalTeachersList[teacher]->name);
8286 					} else {
8287 						tmpString+=writeActivitiesTeachers(htmlLevel, allActivities, printActivityTags);
8288 					}
8289 				}
8290 			}
8291 			if(repeatNames){
8292 				if(htmlLevel>=2)
8293 					tmpString+="          <th class=\"yAxis\">";
8294 				else
8295 					tmpString+="          <th>";
8296 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8297 				if(hour==0)
8298 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8299 				else tmpString+="          <!-- span -->\n";
8300 			}
8301 			tmpString+="        </tr>\n";
8302 		}
8303 	}
8304 	//workaround begin.
8305 	tmpString+="      <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8306 	if(repeatNames){
8307 		tmpString+="<td colspan=\"2\"></td>";
8308 	}
8309 	tmpString+="</tr>\n";
8310 	//workaround end.
8311 	tmpString+="      </tbody>\n    </table>\n";
8312 	return tmpString;
8313 }
8314 
8315 //by Volker Dirr
singleTeachersTimetableTimeHorizontalHtml(int htmlLevel,int maxTeachers,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8316 QString TimetableExport::singleTeachersTimetableTimeHorizontalHtml(int htmlLevel, int maxTeachers, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8317 	QString tmpString;
8318 	tmpString+="    <table border=\"1\">\n";
8319 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8320 
8321 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
8322 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
8323 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
8324 	if(repeatNames){
8325 		tmpString+="<td rowspan=\"2\"></td>";
8326 	}
8327 	tmpString+="</tr>\n";
8328 	tmpString+="        <tr>\n          <!-- span -->\n";
8329 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8330 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8331 			if(htmlLevel>=2)
8332 				tmpString+="          <th class=\"xAxis\">";
8333 			else
8334 				tmpString+="          <th>";
8335 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8336 		}
8337 	}
8338 	tmpString+="        </tr>\n";
8339 	tmpString+="      </thead>\n";
8340 	/*workaround
8341 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8342 	*/
8343 	tmpString+="      <tbody>\n";
8344 	int currentCount=0;
8345 	for(int teacher=0; teacher<gt.rules.nInternalTeachers && currentCount<maxTeachers; teacher++){
8346 		if(!excludedNames.contains(teacher)){
8347 			currentCount++;
8348 			excludedNames<<teacher;
8349 			tmpString+="        <tr>\n";
8350 			if(htmlLevel>=2)
8351 				tmpString+="          <th class=\"yAxis\">";
8352 			else
8353 				tmpString+="          <th>";
8354 			tmpString+=protect2(gt.rules.internalTeachersList[teacher]->name)+"</th>\n";
8355 			for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8356 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8357 					QList<int> allActivities;
8358 					allActivities.clear();
8359 					allActivities<<teachers_timetable_weekly[teacher][day][hour];
8360 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8361 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8362 						tmpString+=writeActivityTeacher(htmlLevel, teacher, day, hour, true, false, printActivityTags, gt.rules.internalTeachersList[teacher]->name);
8363 					} else {
8364 						tmpString+=writeActivitiesTeachers(htmlLevel, allActivities, printActivityTags);
8365 					}
8366 				}
8367 			}
8368 			if(repeatNames){
8369 				if(htmlLevel>=2)
8370 					tmpString+="          <th class=\"yAxis\">";
8371 				else
8372 					tmpString+="          <th>";
8373 				tmpString+=protect2(gt.rules.internalTeachersList[teacher]->name)+"</th>\n";
8374 			}
8375 			tmpString+="        </tr>\n";
8376 		}
8377 	}
8378 	//workaround begin.
8379 	tmpString+="      <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8380 	if(repeatNames){
8381 		tmpString+="<td></td>";
8382 	}
8383 	tmpString+="</tr>\n";
8384 	//workaround end.
8385 	tmpString+="      </tbody>\n    </table>\n";
8386 	return tmpString;
8387 }
8388 
8389 //by Volker Dirr
singleTeachersTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxTeachers,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8390 QString TimetableExport::singleTeachersTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxTeachers, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8391 	assert(day>=0);
8392 	assert(day<gt.rules.nDaysPerWeek);
8393 	QString tmpString;
8394 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
8395 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8396 
8397 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
8398 	int currentCount=0;
8399 	for(int teacher=0; teacher<gt.rules.nInternalTeachers && currentCount<maxTeachers; teacher++){
8400 		if(!excludedNames.contains(teacher)){
8401 			currentCount++;
8402 			if(htmlLevel>=2)
8403 				tmpString+="          <th class=\"xAxis\">";
8404 			else
8405 				tmpString+="          <th>";
8406 			tmpString+=gt.rules.internalTeachersList[teacher]->name+"</th>";
8407 		}
8408 	}
8409 	if(repeatNames){
8410 		tmpString+="<td colspan=\"2\"></td>";
8411 	}
8412 	tmpString+="</tr>\n      </thead>\n";
8413 	/*workaround
8414 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8415 	*/
8416 	tmpString+="      <tbody>\n";
8417 
8418 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8419 		tmpString+="        <tr>\n";
8420 		if(hour==0)
8421 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8422 		else tmpString+="          <!-- span -->\n";
8423 		if(htmlLevel>=2)
8424 			tmpString+="          <th class=\"yAxis\">";
8425 		else
8426 			tmpString+="          <th>";
8427 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8428 		currentCount=0;
8429 		for(int teacher=0; teacher<gt.rules.nInternalTeachers && currentCount<maxTeachers; teacher++){
8430 			if(!excludedNames.contains(teacher)){
8431 				currentCount++;
8432 				if(hour+1==gt.rules.nHoursPerDay)
8433 					excludedNames<<teacher;
8434 				QList<int> allActivities;
8435 				allActivities.clear();
8436 				allActivities<<teachers_timetable_weekly[teacher][day][hour];
8437 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8438 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8439 					tmpString+=writeActivityTeacher(htmlLevel, teacher, day, hour, false, true, printActivityTags, gt.rules.internalTeachersList[teacher]->name);
8440 				} else {
8441 					tmpString+=writeActivitiesTeachers(htmlLevel, allActivities, printActivityTags);
8442 				}
8443 			}
8444 		}
8445 		if(repeatNames){
8446 			if(htmlLevel>=2)
8447 				tmpString+="          <th class=\"yAxis\">";
8448 			else
8449 				tmpString+="          <th>";
8450 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8451 			if(hour==0)
8452 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8453 			else tmpString+="          <!-- span -->\n";
8454 		}
8455 		tmpString+="        </tr>\n";
8456 	}
8457 	//workaround begin.
8458 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8459 	if(repeatNames){
8460 		tmpString+="<td colspan=\"2\"></td>";
8461 	}
8462 	tmpString+="</tr>\n";
8463 	//workaround end.
8464 	tmpString+="      </tbody>\n";
8465 	tmpString+="    </table>\n\n";
8466 	return tmpString;
8467 }
8468 
8469 //by Volker Dirr
singleTeachersTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxTeachers,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8470 QString TimetableExport::singleTeachersTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxTeachers, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8471 	assert(day>=0);
8472 	assert(day<gt.rules.nDaysPerWeek);
8473 	QString tmpString;
8474 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
8475 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8476 
8477 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
8478 	tmpString+="<th colspan=\"" +QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
8479 	if(repeatNames){
8480 		tmpString+="<td rowspan=\"2\"></td>";
8481 	}
8482 	tmpString+="</tr>\n";
8483 	tmpString+="        <tr>\n          <!-- span -->\n";
8484 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8485 		if(htmlLevel>=2)
8486 			tmpString+="          <th class=\"xAxis\">";
8487 		else
8488 			tmpString+="          <th>";
8489 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8490 	}
8491 	tmpString+="        </tr>\n";
8492 	tmpString+="      </thead>\n";
8493 	/*workaround
8494 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8495 	*/
8496 	tmpString+="      <tbody>\n";
8497 	int currentCount=0;
8498 	for(int teacher=0; teacher<gt.rules.nInternalTeachers && currentCount<maxTeachers; teacher++){
8499 		if(!excludedNames.contains(teacher)){
8500 			currentCount++;
8501 			excludedNames<<teacher;
8502 			tmpString+="        <tr>\n";
8503 			if(htmlLevel>=2)
8504 				tmpString+="          <th class=\"yAxis\">";
8505 			else
8506 				tmpString+="          <th>";
8507 			tmpString+=protect2(gt.rules.internalTeachersList[teacher]->name)+"</th>\n";
8508 
8509 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8510 				QList<int> allActivities;
8511 				allActivities.clear();
8512 				allActivities<<teachers_timetable_weekly[teacher][day][hour];
8513 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8514 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8515 					tmpString+=writeActivityTeacher(htmlLevel, teacher, day, hour, true, false, printActivityTags, gt.rules.internalTeachersList[teacher]->name);
8516 				} else {
8517 					tmpString+=writeActivitiesTeachers(htmlLevel, allActivities, printActivityTags);
8518 				}
8519 			}
8520 			if(repeatNames){
8521 				if(htmlLevel>=2)
8522 					tmpString+="          <th class=\"yAxis\">";
8523 				else
8524 					tmpString+="          <th>";
8525 				tmpString+=protect2(gt.rules.internalTeachersList[teacher]->name)+"</th>\n";
8526 			}
8527 			tmpString+="        </tr>\n";
8528 		}
8529 	}
8530 	//workaround begin.
8531 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8532 	if(repeatNames){
8533 		tmpString+="<td></td>";
8534 	}
8535 	tmpString+="</tr>\n";
8536 	//workaround end.
8537 	tmpString+="      </tbody>\n";
8538 	tmpString+="    </table>\n\n";
8539 	return tmpString;
8540 }
8541 
8542 //by Volker Dirr
singleRoomsTimetableDaysHorizontalHtml(int htmlLevel,int room,const QString & saveTime,bool printActivityTags,bool repeatNames)8543 QString TimetableExport::singleRoomsTimetableDaysHorizontalHtml(int htmlLevel, int room, const QString& saveTime, bool printActivityTags, bool repeatNames){
8544 	assert(room>=0);
8545 	assert(room<gt.rules.nInternalRooms);
8546 	QString tmpString;
8547 	QString room_name = gt.rules.internalRoomsList[room]->name;
8548 	tmpString+="    <table id=\"table_"+hashRoomIDsTimetable.value(room_name)+"\" border=\"1\"";
8549 	if(room%2==0)  tmpString+=" class=\"odd_table\"";
8550 	else tmpString+=" class=\"even_table\"";
8551 	tmpString+=">\n";
8552 
8553 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8554 
8555 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(room_name)+"</th>";
8556 	if(repeatNames){
8557 		tmpString+="<td rowspan=\"2\"></td>";
8558 	}
8559 	tmpString+="</tr>\n";
8560 	tmpString+="        <tr>\n          <!-- span -->\n";
8561 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8562 		if(htmlLevel>=2)
8563 			tmpString+="          <th class=\"xAxis\">";
8564 		else
8565 			tmpString+="          <th>";
8566 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
8567 	}
8568 	tmpString+="        </tr>\n";
8569 	tmpString+="      </thead>\n";
8570 	/*workaround
8571 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8572 	*/
8573 	tmpString+="      <tbody>\n";
8574 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8575 		tmpString+="        <tr>\n";
8576 		if(htmlLevel>=2)
8577 			tmpString+="          <th class=\"yAxis\">";
8578 		else
8579 			tmpString+="          <th>";
8580 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8581 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8582 			QList<int> allActivities;
8583 			if(gt.rules.internalRoomsList[room]->isVirtual==false){
8584 				allActivities<<rooms_timetable_weekly[room][day][hour];
8585 			}
8586 			else{
8587 				allActivities<<virtual_rooms_timetable_weekly[room][day][hour];
8588 				if(allActivities.isEmpty())
8589 					allActivities<<UNALLOCATED_ACTIVITY;
8590 			}
8591 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8592 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8593 				tmpString+=writeActivityRoom(htmlLevel, room, day, hour, false, true, printActivityTags);
8594 			} else {
8595 				tmpString+=writeActivitiesRooms(htmlLevel, allActivities, printActivityTags);
8596 			}
8597 		}
8598 		if(repeatNames){
8599 			if(htmlLevel>=2)
8600 				tmpString+="          <th class=\"yAxis\">";
8601 			else
8602 				tmpString+="          <th>";
8603 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8604 		}
8605 		tmpString+="        </tr>\n";
8606 	}
8607 	//workaround begin.
8608 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8609 	if(repeatNames){
8610 		tmpString+="<td></td>";
8611 	}
8612 	tmpString+="</tr>\n";
8613 	//workaround end.
8614 	tmpString+="      </tbody>\n";
8615 	tmpString+="    </table>\n\n";
8616 	return tmpString;
8617 }
8618 
8619 //by Volker Dirr
singleRoomsTimetableDaysVerticalHtml(int htmlLevel,int room,const QString & saveTime,bool printActivityTags,bool repeatNames)8620 QString TimetableExport::singleRoomsTimetableDaysVerticalHtml(int htmlLevel, int room, const QString& saveTime, bool printActivityTags, bool repeatNames){
8621 	assert(room>=0);
8622 	assert(room<gt.rules.nInternalRooms);
8623 	QString tmpString;
8624 	QString room_name = gt.rules.internalRoomsList[room]->name;
8625 	tmpString+="    <table id=\"table_"+hashRoomIDsTimetable.value(room_name)+"\" border=\"1\"";
8626 	if(room%2==0)  tmpString+=" class=\"odd_table\"";
8627 	else tmpString+=" class=\"even_table\"";
8628 	tmpString+=">\n";
8629 
8630 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8631 
8632 	tmpString+="      <thead>\n";
8633 	tmpString+="        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(room_name)+"</th>";
8634 	if(repeatNames){
8635 		tmpString+="<td rowspan=\"2\"></td>";
8636 	}
8637 	tmpString+="</tr>\n";
8638 	tmpString+="        <tr>\n          <!-- span -->\n";
8639 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8640 		if(htmlLevel>=2)
8641 			tmpString+="          <th class=\"xAxis\">";
8642 			else
8643 				tmpString+="          <th>";
8644 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8645 	}
8646 	tmpString+="        </tr>\n";
8647 	tmpString+="      </thead>\n";
8648 	/*workaround
8649 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8650 	*/
8651 	tmpString+="      <tbody>\n";
8652 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8653 		tmpString+="        <tr>\n";
8654 		if(htmlLevel>=2)
8655 			tmpString+="          <th class=\"yAxis\">";
8656 		else
8657 			tmpString+="          <th>";
8658 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
8659 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8660 			QList<int> allActivities;
8661 
8662 			if(gt.rules.internalRoomsList[room]->isVirtual==false){
8663 				allActivities<<rooms_timetable_weekly[room][day][hour];
8664 			}
8665 			else{
8666 				allActivities<<virtual_rooms_timetable_weekly[room][day][hour];
8667 				if(allActivities.isEmpty())
8668 					allActivities<<UNALLOCATED_ACTIVITY;
8669 			}
8670 
8671 			bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8672 			if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8673 				tmpString+=writeActivityRoom(htmlLevel, room, day, hour, true, false, printActivityTags);
8674 			} else {
8675 				tmpString+=writeActivitiesRooms(htmlLevel, allActivities, printActivityTags);
8676 			}
8677 		}
8678 		if(repeatNames){
8679 			if(htmlLevel>=2)
8680 				tmpString+="          <th class=\"yAxis\">";
8681 			else
8682 				tmpString+="          <th>";
8683 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
8684 		}
8685 		tmpString+="        </tr>\n";
8686 	}
8687 	//workaround begin.
8688 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8689 	if(repeatNames){
8690 		tmpString+="<td></td>";
8691 	}
8692 	tmpString+="</tr>\n";
8693 	//workaround end.
8694 	tmpString+="      </tbody>\n";
8695 	tmpString+="    </table>\n\n";
8696 	return tmpString;
8697 }
8698 
8699 
8700 //by Volker Dirr
singleRoomsTimetableTimeVerticalHtml(int htmlLevel,int maxRooms,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8701 QString TimetableExport::singleRoomsTimetableTimeVerticalHtml(int htmlLevel, int maxRooms, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8702 	QString tmpString;
8703 	tmpString+="    <table border=\"1\">\n";
8704 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8705 
8706 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
8707 	int currentCount=0;
8708 	for(int room=0; room<gt.rules.nInternalRooms && currentCount<maxRooms; room++){
8709 		if(!excludedNames.contains(room)){
8710 			currentCount++;
8711 			if(htmlLevel>=2)
8712 				tmpString+="          <th class=\"xAxis\">";
8713 			else
8714 				tmpString+="          <th>";
8715 			tmpString+=gt.rules.internalRoomsList[room]->name+"</th>";
8716 		}
8717 	}
8718 	if(repeatNames){
8719 		tmpString+="<td colspan=\"2\"></td>";
8720 	}
8721 	tmpString+="</tr>\n      </thead>\n";
8722 	/*workaround
8723 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8724 	*/
8725 	tmpString+="      <tbody>\n";
8726 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8727 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8728 			tmpString+="        <tr>\n";
8729 			if(hour==0)
8730 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8731 			else tmpString+="          <!-- span -->\n";
8732 			if(htmlLevel>=2)
8733 				tmpString+="          <th class=\"yAxis\">";
8734 			else
8735 				tmpString+="          <th>";
8736 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8737 			currentCount=0;
8738 			for(int room=0; room<gt.rules.nInternalRooms && currentCount<maxRooms; room++){
8739 				if(!excludedNames.contains(room)){
8740 					currentCount++;
8741 					if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
8742 						excludedNames<<room;
8743 					QList<int> allActivities;
8744 
8745 					if(gt.rules.internalRoomsList[room]->isVirtual==false){
8746 						allActivities<<rooms_timetable_weekly[room][day][hour];
8747 					}
8748 					else{
8749 						allActivities<<virtual_rooms_timetable_weekly[room][day][hour];
8750 						if(allActivities.isEmpty())
8751 							allActivities<<UNALLOCATED_ACTIVITY;
8752 					}
8753 
8754 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8755 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8756 						tmpString+=writeActivityRoom(htmlLevel, room, day, hour, false, true, printActivityTags);
8757 					} else {
8758 						tmpString+=writeActivitiesRooms(htmlLevel, allActivities, printActivityTags);
8759 					}
8760 				}
8761 			}
8762 			if(repeatNames){
8763 				if(htmlLevel>=2)
8764 					tmpString+="          <th class=\"yAxis\">";
8765 				else
8766 					tmpString+="          <th>";
8767 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8768 				if(hour==0)
8769 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8770 				else tmpString+="          <!-- span -->\n";
8771 			}
8772 			tmpString+="        </tr>\n";
8773 		}
8774 	}
8775 	//workaround begin.
8776 	tmpString+="      <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8777 	if(repeatNames){
8778 		tmpString+="<td colspan=\"2\"></td>";
8779 	}
8780 	tmpString+="</tr>\n";
8781 	//workaround end.
8782 	tmpString+="      </tbody>\n    </table>\n";
8783 	return tmpString;
8784 }
8785 
8786 //by Volker Dirr
singleRoomsTimetableTimeHorizontalHtml(int htmlLevel,int maxRooms,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8787 QString TimetableExport::singleRoomsTimetableTimeHorizontalHtml(int htmlLevel, int maxRooms, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8788 	QString tmpString;
8789 	tmpString+="    <table border=\"1\">\n";
8790 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8791 
8792 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
8793 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
8794 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
8795 	if(repeatNames){
8796 		tmpString+="<td rowspan=\"2\"></td>";
8797 	}
8798 	tmpString+="</tr>\n";
8799 	tmpString+="        <tr>\n          <!-- span -->\n";
8800 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8801 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8802 			if(htmlLevel>=2)
8803 				tmpString+="          <th class=\"xAxis\">";
8804 			else
8805 				tmpString+="          <th>";
8806 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour]) + "</th>\n";
8807 		}
8808 	}
8809 	tmpString+="        </tr>\n";
8810 	tmpString+="      </thead>\n";
8811 	/*workaround
8812 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8813 	*/
8814 	tmpString+="      <tbody>\n";
8815 	int currentCount=0;
8816 	for(int room=0; room<gt.rules.nInternalRooms && currentCount<maxRooms; room++){
8817 		if(!excludedNames.contains(room)){
8818 			currentCount++;
8819 			excludedNames<<room;
8820 
8821 			tmpString+="        <tr>\n";
8822 			if(htmlLevel>=2)
8823 				tmpString+="          <th class=\"yAxis\">";
8824 			else
8825 				tmpString+="          <th>";
8826 			tmpString+=protect2(gt.rules.internalRoomsList[room]->name)+"</th>\n";
8827 			for(int day=0; day<gt.rules.nDaysPerWeek; day++){
8828 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8829 					QList<int> allActivities;
8830 
8831 					if(gt.rules.internalRoomsList[room]->isVirtual==false){
8832 						allActivities<<rooms_timetable_weekly[room][day][hour];
8833 					}
8834 					else{
8835 						allActivities<<virtual_rooms_timetable_weekly[room][day][hour];
8836 						if(allActivities.isEmpty())
8837 							allActivities<<UNALLOCATED_ACTIVITY;
8838 					}
8839 
8840 					bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8841 					if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8842 						tmpString+=writeActivityRoom(htmlLevel, room, day, hour, true, false, printActivityTags);
8843 					} else {
8844 						tmpString+=writeActivitiesRooms(htmlLevel, allActivities, printActivityTags);
8845 					}
8846 				}
8847 			}
8848 			if(repeatNames){
8849 				if(htmlLevel>=2)
8850 					tmpString+="          <th class=\"yAxis\">";
8851 				else
8852 					tmpString+="          <th>";
8853 				tmpString+=protect2(gt.rules.internalRoomsList[room]->name)+"</th>\n";
8854 			}
8855 			tmpString+="        </tr>\n";
8856 		}
8857 	}
8858 	//workaround begin.
8859 	tmpString+="      <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8860 	if(repeatNames){
8861 		tmpString+="<td></td>";
8862 	}
8863 	tmpString+="</tr>\n";
8864 	//workaround end.
8865 	tmpString+="      </tbody>\n    </table>\n";
8866 	return tmpString;
8867 }
8868 
8869 
8870 //by Volker Dirr
singleRoomsTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxRooms,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8871 QString TimetableExport::singleRoomsTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxRooms, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8872 	assert(day>=0);
8873 	assert(day<gt.rules.nDaysPerWeek);
8874 	QString tmpString;
8875 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
8876 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8877 
8878 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
8879 	int currentCount=0;
8880 	for(int room=0; room<gt.rules.nInternalRooms && currentCount<maxRooms; room++){
8881 		if(!excludedNames.contains(room)){
8882 			currentCount++;
8883 			if(htmlLevel>=2)
8884 				tmpString+="          <th class=\"xAxis\">";
8885 			else
8886 				tmpString+="          <th>";
8887 			tmpString+=gt.rules.internalRoomsList[room]->name+"</th>";
8888 		}
8889 	}
8890 	if(repeatNames){
8891 		tmpString+="<td colspan=\"2\"></td>";
8892 	}
8893 	tmpString+="</tr>\n      </thead>\n";
8894 	/*workaround
8895 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8896 	*/
8897 	tmpString+="      <tbody>\n";
8898 
8899 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8900 		tmpString+="        <tr>\n";
8901 		if(hour==0)
8902 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8903 		else tmpString+="          <!-- span -->\n";
8904 		if(htmlLevel>=2)
8905 			tmpString+="          <th class=\"yAxis\">";
8906 		else
8907 			tmpString+="          <th>";
8908 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8909 		currentCount=0;
8910 		for(int room=0; room<gt.rules.nInternalRooms && currentCount<maxRooms; room++){
8911 			if(!excludedNames.contains(room)){
8912 				currentCount++;
8913 				if(hour+1==gt.rules.nHoursPerDay)
8914 					excludedNames<<room;
8915 				QList<int> allActivities;
8916 
8917 				if(gt.rules.internalRoomsList[room]->isVirtual==false){
8918 					allActivities<<rooms_timetable_weekly[room][day][hour];
8919 				}
8920 				else{
8921 					allActivities<<virtual_rooms_timetable_weekly[room][day][hour];
8922 					if(allActivities.isEmpty())
8923 						allActivities<<UNALLOCATED_ACTIVITY;
8924 				}
8925 
8926 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
8927 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
8928 					tmpString+=writeActivityRoom(htmlLevel, room, day, hour, false, true, printActivityTags);
8929 				} else {
8930 					tmpString+=writeActivitiesRooms(htmlLevel, allActivities, printActivityTags);
8931 				}
8932 			}
8933 		}
8934 		if(repeatNames){
8935 			if(htmlLevel>=2)
8936 				tmpString+="          <th class=\"yAxis\">";
8937 			else
8938 				tmpString+="          <th>";
8939 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8940 
8941 			if(hour==0)
8942 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
8943 			else tmpString+="          <!-- span -->\n";
8944 		}
8945 		tmpString+="        </tr>\n";
8946 	}
8947 	//workaround begin.
8948 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
8949 	if(repeatNames){
8950 		tmpString+="<td colspan=\"2\"></td>";
8951 	}
8952 	tmpString+="</tr>\n";
8953 	//workaround end.
8954 	tmpString+="      </tbody>\n";
8955 	tmpString+="    </table>\n\n";
8956 	return tmpString;
8957 }
8958 
8959 //by Volker Dirr
singleRoomsTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxRooms,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)8960 QString TimetableExport::singleRoomsTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxRooms, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
8961 	assert(day>=0);
8962 	assert(day<gt.rules.nDaysPerWeek);
8963 	QString tmpString;
8964 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
8965 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
8966 
8967 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
8968 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
8969 	if(repeatNames){
8970 		tmpString+="<td rowspan=\"2\"></td>";
8971 	}
8972 	tmpString+="</tr>\n";
8973 	tmpString+="        <tr>\n          <!-- span -->\n";
8974 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8975 		if(htmlLevel>=2)
8976 			tmpString+="          <th class=\"xAxis\">";
8977 		else
8978 			tmpString+="          <th>";
8979 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
8980 	}
8981 	tmpString+="        </tr>\n";
8982 	tmpString+="      </thead>\n";
8983 	/*workaround
8984 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
8985 	*/
8986 	tmpString+="      <tbody>\n";
8987 	int currentCount=0;
8988 	for(int room=0; room<gt.rules.nInternalRooms && currentCount<maxRooms; room++){
8989 		if(!excludedNames.contains(room)){
8990 			currentCount++;
8991 			excludedNames<<room;
8992 			tmpString+="        <tr>\n";
8993 			if(htmlLevel>=2)
8994 				tmpString+="          <th class=\"yAxis\">";
8995 			else
8996 				tmpString+="          <th>";
8997 			tmpString+=protect2(gt.rules.internalRoomsList[room]->name)+"</th>\n";
8998 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
8999 				QList<int> allActivities;
9000 
9001 				if(gt.rules.internalRoomsList[room]->isVirtual==false){
9002 					allActivities<<rooms_timetable_weekly[room][day][hour];
9003 				}
9004 				else{
9005 					allActivities<<virtual_rooms_timetable_weekly[room][day][hour];
9006 					if(allActivities.isEmpty())
9007 						allActivities<<UNALLOCATED_ACTIVITY;
9008 				}
9009 
9010 				bool activitiesWithSameStartingtime=addActivitiesWithSameStartingTime(allActivities, hour);
9011 				if(allActivities.size()==1 && !activitiesWithSameStartingtime){  // because i am using colspan or rowspan!!!
9012 					tmpString+=writeActivityRoom(htmlLevel, room, day, hour, true, false, printActivityTags);
9013 				} else {
9014 					tmpString+=writeActivitiesRooms(htmlLevel, allActivities, printActivityTags);
9015 				}
9016 			}
9017 			if(repeatNames){
9018 				if(htmlLevel>=2)
9019 					tmpString+="          <th class=\"yAxis\">";
9020 				else
9021 					tmpString+="          <th>";
9022 				tmpString+=protect2(gt.rules.internalRoomsList[room]->name)+"</th>\n";
9023 			}
9024 			tmpString+="        </tr>\n";
9025 		}
9026 	}
9027 	//workaround begin.
9028 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9029 	if(repeatNames){
9030 		tmpString+="<td></td>";
9031 	}
9032 	tmpString+="</tr>\n";
9033 	//workaround end.
9034 	tmpString+="      </tbody>\n";
9035 	tmpString+="    </table>\n\n";
9036 	return tmpString;
9037 }
9038 
9039 //by Volker Dirr
singleSubjectsTimetableDaysHorizontalHtml(int htmlLevel,int subject,const QString & saveTime,bool printActivityTags,bool repeatNames)9040 QString TimetableExport::singleSubjectsTimetableDaysHorizontalHtml(int htmlLevel, int subject, const QString& saveTime, bool printActivityTags, bool repeatNames){
9041 	assert(subject>=0);
9042 	assert(subject<gt.rules.nInternalSubjects);
9043 	QString tmpString;
9044 	///////by Liviu Lalescu
9045 	activitiesForCurrentSubject.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9046 	for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9047 		for(int h=0; h<gt.rules.nHoursPerDay; h++)
9048 			activitiesForCurrentSubject[d][h].clear();
9049 	for(int ai : qAsConst(gt.rules.activitiesForSubjectList[subject]))
9050 		if(best_solution.times[ai]!=UNALLOCATED_TIME){
9051 			int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9052 			int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9053 			Activity* act=&gt.rules.internalActivitiesList[ai];
9054 			for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9055 				activitiesForCurrentSubject[d][h+dd].append(ai);
9056 		}
9057 	///////end Liviu Lalescu
9058 	tmpString+="    <table id=\"table_"+hashSubjectIDsTimetable.value(gt.rules.internalSubjectsList[subject]->name);
9059 	tmpString+="\" border=\"1\"";
9060 	if(subject%2==0)  tmpString+=" class=\"odd_table\"";
9061 	else tmpString+=" class=\"even_table\"";
9062 	tmpString+=">\n";
9063 
9064 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9065 
9066 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>";
9067 	if(repeatNames){
9068 		tmpString+="<td rowspan=\"2\"></td>";
9069 	}
9070 	tmpString+="</tr>\n";
9071 	tmpString+="        <tr>\n          <!-- span -->\n";
9072 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9073 		if(htmlLevel>=2)
9074 			tmpString+="          <th class=\"xAxis\">";
9075 		else
9076 			tmpString+="          <th>";
9077 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
9078 	}
9079 	tmpString+="        </tr>\n";
9080 	tmpString+="      </thead>\n";
9081 	/*workaround
9082 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9083 	*/
9084 	tmpString+="      <tbody>\n";
9085 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9086 		tmpString+="        <tr>\n";
9087 		if(htmlLevel>=2)
9088 			tmpString+="          <th class=\"yAxis\">";
9089 		else
9090 			tmpString+="          <th>";
9091 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9092 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9093 			QList<int> allActivities;
9094 
9095 			allActivities=activitiesForCurrentSubject[day][hour];
9096 
9097 			/*
9098 			allActivities.clear();
9099 			//Now get the activitiy ids. I don't run through the InternalActivitiesList, even that is faster. I run through subgroupsList, because by that the activites are sorted by that in the html-table.
9100 			for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
9101 				if(students_timetable_weekly[subgroup][day][hour]!=UNALLOCATED_ACTIVITY){
9102 					Activity* act=&gt.rules.internalActivitiesList[students_timetable_weekly[subgroup][day][hour]];
9103 					if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9104 						if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour]))){
9105 							allActivities+students_timetable_weekly[subgroup][day][hour];
9106 						}
9107 				}
9108 			}
9109 			//Now run through the teachers timetable, because activities without a students set are still missing.
9110 			for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
9111 				if(teachers_timetable_weekly[teacher][day][hour]!=UNALLOCATED_ACTIVITY){
9112 					Activity* act=&gt.rules.internalActivitiesList[teachers_timetable_weekly[teacher][day][hour]];
9113 					if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9114 						if(!(allActivities.contains(teachers_timetable_weekly[teacher][day][hour]))){
9115 							assert(act->studentsNames.isEmpty());
9116 							allActivities+teachers_timetable_weekly[teacher][day][hour];
9117 						}
9118 				}
9119 			}*/
9120 			addActivitiesWithSameStartingTime(allActivities, hour);
9121 			tmpString+=writeActivitiesSubjects(htmlLevel, allActivities, printActivityTags);
9122 		}
9123 		if(repeatNames){
9124 			if(htmlLevel>=2)
9125 				tmpString+="          <th class=\"yAxis\">";
9126 			else
9127 				tmpString+="          <th>";
9128 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9129 		}
9130 		tmpString+="        </tr>\n";
9131 	}
9132 	//workaround begin.
9133 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9134 	if(repeatNames){
9135 		tmpString+="<td></td>";
9136 	}
9137 	tmpString+="</tr>\n";
9138 	//workaround end.
9139 	tmpString+="      </tbody>\n";
9140 	tmpString+="    </table>\n\n";
9141 	return tmpString;
9142 }
9143 
9144 //by Volker Dirr
singleSubjectsTimetableDaysVerticalHtml(int htmlLevel,int subject,const QString & saveTime,bool printActivityTags,bool repeatNames)9145 QString TimetableExport::singleSubjectsTimetableDaysVerticalHtml(int htmlLevel, int subject, const QString& saveTime, bool printActivityTags, bool repeatNames){
9146 	assert(subject>=0);
9147 	assert(subject<gt.rules.nInternalSubjects);
9148 	QString tmpString;
9149 	///////by Liviu Lalescu
9150 	activitiesForCurrentSubject.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9151 	for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9152 		for(int h=0; h<gt.rules.nHoursPerDay; h++)
9153 			activitiesForCurrentSubject[d][h].clear();
9154 	for(int ai : qAsConst(gt.rules.activitiesForSubjectList[subject]))
9155 		if(best_solution.times[ai]!=UNALLOCATED_TIME){
9156 			int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9157 			int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9158 			Activity* act=&gt.rules.internalActivitiesList[ai];
9159 			for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9160 				activitiesForCurrentSubject[d][h+dd].append(ai);
9161 		}
9162 	///////end Liviu Lalescu
9163 	tmpString+="    <table id=\"table_"+hashSubjectIDsTimetable.value(gt.rules.internalSubjectsList[subject]->name);
9164 	tmpString+="\" border=\"1\"";
9165 	if(subject%2==0) tmpString+=" class=\"odd_table\"";
9166 	else tmpString+=" class=\"even_table\"";
9167 	tmpString+=">\n";
9168 
9169 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9170 
9171 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>";
9172 	if(repeatNames){
9173 		tmpString+="<td rowspan=\"2\"></td>";
9174 	}
9175 	tmpString+="</tr>\n";
9176 	tmpString+="        <tr>\n          <!-- span -->\n";
9177 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9178 		if(htmlLevel>=2)
9179 			tmpString+="          <th class=\"xAxis\">";
9180 		else
9181 			tmpString+="          <th>";
9182 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9183 	}
9184 	tmpString+="        </tr>\n";
9185 	tmpString+="      </thead>\n";
9186 	/*workaround
9187 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9188 	*/
9189 	tmpString+="      <tbody>\n";
9190 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9191 		tmpString+="        <tr>\n";
9192 		if(htmlLevel>=2)
9193 			tmpString+="          <th class=\"yAxis\">";
9194 		else
9195 			tmpString+="          <th>";
9196 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
9197 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9198 			QList<int> allActivities;
9199 
9200 			allActivities=activitiesForCurrentSubject[day][hour];
9201 
9202 			/*
9203 			allActivities.clear();
9204 			//Now get the activitiy ids. I don't run through the InternalActivitiesList, even that is faster. I run through subgroupsList, because by that the activites are sorted by that in the html-table.
9205 			for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
9206 				if(students_timetable_weekly[subgroup][day][hour]!=UNALLOCATED_ACTIVITY){
9207 					Activity* act=&gt.rules.internalActivitiesList[students_timetable_weekly[subgroup][day][hour]];
9208 					if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9209 						if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour]))){
9210 							allActivities+students_timetable_weekly[subgroup][day][hour];
9211 						}
9212 				}
9213 			}
9214 			//Now run through the teachers timetable, because activities without a students set are still missing.
9215 			for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
9216 				if(teachers_timetable_weekly[teacher][day][hour]!=UNALLOCATED_ACTIVITY){
9217 					Activity* act=&gt.rules.internalActivitiesList[teachers_timetable_weekly[teacher][day][hour]];
9218 					if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9219 						if(!(allActivities.contains(teachers_timetable_weekly[teacher][day][hour]))){
9220 							assert(act->studentsNames.isEmpty());
9221 							allActivities+teachers_timetable_weekly[teacher][day][hour];
9222 						}
9223 				}
9224 			}
9225 			*/
9226 			addActivitiesWithSameStartingTime(allActivities, hour);
9227 			tmpString+=writeActivitiesSubjects(htmlLevel, allActivities, printActivityTags);
9228 		}
9229 		if(repeatNames){
9230 			if(htmlLevel>=2)
9231 				tmpString+="          <th class=\"yAxis\">";
9232 			else
9233 				tmpString+="          <th>";
9234 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
9235 		}
9236 		tmpString+="        </tr>\n";
9237 	}
9238 	//workaround begin.
9239 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9240 	if(repeatNames){
9241 		tmpString+="<td></td>";
9242 	}
9243 	tmpString+="</tr>\n";
9244 	//workaround end.
9245 	tmpString+="      </tbody>\n";
9246 	tmpString+="    </table>\n\n";
9247 	return tmpString;
9248 }
9249 
9250 
9251 //by Volker Dirr
singleSubjectsTimetableTimeVerticalHtml(int htmlLevel,int maxSubjects,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)9252 QString TimetableExport::singleSubjectsTimetableTimeVerticalHtml(int htmlLevel, int maxSubjects, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
9253 	QString tmpString;
9254 	tmpString+="    <table id=\"table\" border=\"1\">\n";
9255 
9256 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9257 
9258 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
9259 	int currentCount=0;
9260 	for(int subject=0; subject<gt.rules.nInternalSubjects && currentCount<maxSubjects; subject++){
9261 		if(!excludedNames.contains(subject)){
9262 			currentCount++;
9263 			if(htmlLevel>=2)
9264 				tmpString+="          <th class=\"xAxis\">";
9265 			else
9266 				tmpString+="          <th>";
9267 			tmpString+=gt.rules.internalSubjectsList[subject]->name+"</th>";
9268 		}
9269 	}
9270 	if(repeatNames){
9271 		tmpString+="<td colspan=\"2\"></td>";
9272 	}
9273 	tmpString+="</tr>\n      </thead>\n";
9274 	/*workaround
9275 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9276 	*/
9277 	tmpString+="      <tbody>\n";
9278 
9279 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9280 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9281 			tmpString+="        <tr>\n";
9282 			if(hour==0)
9283 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
9284 			else tmpString+="          <!-- span -->\n";
9285 			if(htmlLevel>=2)
9286 				tmpString+="          <th class=\"yAxis\">";
9287 			else
9288 				tmpString+="          <th>";
9289 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9290 
9291 			currentCount=0;
9292 			for(int subject=0; subject<gt.rules.nInternalSubjects && currentCount<maxSubjects; subject++){
9293 				if(!excludedNames.contains(subject)){
9294 					currentCount++;
9295 					if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
9296 						excludedNames<<subject;
9297 					QList<int> allActivities;
9298 					allActivities.clear();
9299 
9300 					for(int ai : qAsConst(gt.rules.activitiesForSubjectList[subject]))
9301 						if(activitiesAtTime[day][hour].contains(ai)){
9302 							assert(!allActivities.contains(ai));
9303 							allActivities.append(ai);
9304 						}
9305 
9306 					/* //Now get the activities ids. I don't run through the InternalActivitiesList, even if that is faster. I run through subgroupsList, because by that the activites are sorted by that in the html-table.
9307 					for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
9308 						if(students_timetable_weekly[subgroup][day][hour]!=UNALLOCATED_ACTIVITY){
9309 							Activity* act=&gt.rules.internalActivitiesList[students_timetable_weekly[subgroup][day][hour]];
9310 							if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9311 								if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour]))){
9312 									allActivities+students_timetable_weekly[subgroup][day][hour];
9313 								}
9314 						}
9315 					}
9316 					//Now run through the teachers timetable, because activities without a students set are still missing.
9317 					for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
9318 						if(teachers_timetable_weekly[teacher][day][hour]!=UNALLOCATED_ACTIVITY){
9319 							Activity* act=&gt.rules.internalActivitiesList[teachers_timetable_weekly[teacher][day][hour]];
9320 							if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9321 								if(!(allActivities.contains(teachers_timetable_weekly[teacher][day][hour]))){
9322 									assert(act->studentsNames.isEmpty());
9323 									allActivities+teachers_timetable_weekly[teacher][day][hour];
9324 								}
9325 						}
9326 					}*/
9327 					addActivitiesWithSameStartingTime(allActivities, hour);
9328 					tmpString+=writeActivitiesSubjects(htmlLevel, allActivities, printActivityTags);
9329 				}
9330 			}
9331 			if(repeatNames){
9332 				if(htmlLevel>=2)
9333 					tmpString+="          <th class=\"yAxis\">";
9334 				else
9335 					tmpString+="          <th>";
9336 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9337 				if(hour==0)
9338 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
9339 				else tmpString+="          <!-- span -->\n";
9340 			}
9341 			tmpString+="        </tr>\n";
9342 		}
9343 	}
9344 	//workaround begin.
9345 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9346 	if(repeatNames){
9347 		tmpString+="<td colspan=\"2\"></td>";
9348 	}
9349 	tmpString+="</tr>\n";
9350 	//workaround end.
9351 	tmpString+="      </tbody>\n    </table>\n";
9352 	return tmpString;
9353 }
9354 
9355 //by Volker Dirr
singleSubjectsTimetableTimeHorizontalHtml(int htmlLevel,int maxSubjects,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)9356 QString TimetableExport::singleSubjectsTimetableTimeHorizontalHtml(int htmlLevel, int maxSubjects, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
9357 	QString tmpString;
9358 	tmpString+="    <table id=\"table\" border=\"1\">\n";
9359 
9360 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9361 
9362 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
9363 
9364 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
9365 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
9366 	if(repeatNames){
9367 		tmpString+="<td rowspan=\"2\"></td>";
9368 	}
9369 	tmpString+="</tr>\n";
9370 	tmpString+="        <tr>\n          <!-- span -->\n";
9371 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9372 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9373 			if(htmlLevel>=2)
9374 				tmpString+="          <th class=\"xAxis\">";
9375 			else
9376 				tmpString+="          <th>";
9377 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9378 		}
9379 	}
9380 	tmpString+="        </tr>\n";
9381 	tmpString+="      </thead>\n";
9382 	/*workaround
9383 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9384 	*/
9385 	tmpString+="      <tbody>\n";
9386 	int currentCount=0;
9387 	for(int subject=0; subject<gt.rules.nInternalSubjects && currentCount<maxSubjects; subject++){
9388 		if(!excludedNames.contains(subject)){
9389 			currentCount++;
9390 			excludedNames<<subject;
9391 			tmpString+="        <tr>\n";
9392 			if(htmlLevel>=2)
9393 				tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9394 			else
9395 				tmpString+="        <th>"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9396 
9397 			///////by Liviu Lalescu
9398 			activitiesForCurrentSubject.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9399 			for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9400 				for(int h=0; h<gt.rules.nHoursPerDay; h++)
9401 					activitiesForCurrentSubject[d][h].clear();
9402 			for(int ai : qAsConst(gt.rules.activitiesForSubjectList[subject]))
9403 				if(best_solution.times[ai]!=UNALLOCATED_TIME){
9404 					int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9405 					int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9406 					Activity* act=&gt.rules.internalActivitiesList[ai];
9407 					for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9408 						activitiesForCurrentSubject[d][h+dd].append(ai);
9409 				}
9410 			///////end Liviu Lalescu
9411 
9412 			for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9413 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9414 					QList<int> allActivities;
9415 
9416 					allActivities=activitiesForCurrentSubject[day][hour];
9417 
9418 					/*allActivities.clear();
9419 					//Now get the activitiy ids. I don't run through the InternalActivitiesList, even that is faster. I run through subgroupsList, because by that the activites are sorted by that in the html-table.
9420 					for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
9421 						if(students_timetable_weekly[subgroup][day][hour]!=UNALLOCATED_ACTIVITY){
9422 							Activity* act=&gt.rules.internalActivitiesList[students_timetable_weekly[subgroup][day][hour]];
9423 							if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9424 								if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour]))){
9425 									allActivities+students_timetable_weekly[subgroup][day][hour];
9426 								}
9427 						}
9428 					}
9429 					//Now run through the teachers timetable, because activities without a students set are still missing.
9430 					for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
9431 						if(teachers_timetable_weekly[teacher][day][hour]!=UNALLOCATED_ACTIVITY){
9432 							Activity* act=&gt.rules.internalActivitiesList[teachers_timetable_weekly[teacher][day][hour]];
9433 							if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9434 								if(!(allActivities.contains(teachers_timetable_weekly[teacher][day][hour]))){
9435 									assert(act->studentsNames.isEmpty());
9436 									allActivities+teachers_timetable_weekly[teacher][day][hour];
9437 								}
9438 						}
9439 					}*/
9440 					addActivitiesWithSameStartingTime(allActivities, hour);
9441 					tmpString+=writeActivitiesSubjects(htmlLevel, allActivities, printActivityTags);
9442 				}
9443 			}
9444 			if(repeatNames){
9445 				if(htmlLevel>=2)
9446 					tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9447 				else
9448 					tmpString+="        <th>"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9449 			}
9450 			tmpString+="        </tr>\n";
9451 		}
9452 	}
9453 	//workaround begin.
9454 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9455 	if(repeatNames){
9456 		tmpString+="<td></td>";
9457 	}
9458 	tmpString+="</tr>\n";
9459 	//workaround end.
9460 	tmpString+="      </tbody>\n    </table>\n";
9461 	return tmpString;
9462 }
9463 
9464 //by Volker Dirr
singleSubjectsTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxSubjects,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)9465 QString TimetableExport::singleSubjectsTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxSubjects, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
9466 	assert(day>=0);
9467 	assert(day<gt.rules.nDaysPerWeek);
9468 	QString tmpString;
9469 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
9470 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9471 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
9472 	int currentCount=0;
9473 	for(int subject=0; subject<gt.rules.nInternalSubjects && currentCount<maxSubjects; subject++){
9474 		if(!excludedNames.contains(subject)){
9475 			currentCount++;
9476 
9477 			if(htmlLevel>=2)
9478 				tmpString+="          <th class=\"xAxis\">";
9479 			else
9480 				tmpString+="          <th>";
9481 			tmpString+=gt.rules.internalSubjectsList[subject]->name+"</th>";
9482 		}
9483 	}
9484 	if(repeatNames){
9485 		tmpString+="<td colspan=\"2\"></td>";
9486 	}
9487 	tmpString+="</tr>\n      </thead>\n";
9488 	/*workaround
9489 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9490 	*/
9491 	tmpString+="      <tbody>\n";
9492 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9493 		tmpString+="        <tr>\n";
9494 		if(hour==0)
9495 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
9496 		else tmpString+="          <!-- span -->\n";
9497 		if(htmlLevel>=2)
9498 			tmpString+="          <th class=\"yAxis\">";
9499 		else
9500 			tmpString+="          <th>";
9501 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9502 
9503 		currentCount=0;
9504 		for(int subject=0; subject<gt.rules.nInternalSubjects && currentCount<maxSubjects; subject++){
9505 			if(!excludedNames.contains(subject)){
9506 				currentCount++;
9507 				if(hour+1==gt.rules.nHoursPerDay)
9508 					excludedNames<<subject;
9509 				QList<int> allActivities;
9510 				allActivities.clear();
9511 
9512 				for(int ai : qAsConst(gt.rules.activitiesForSubjectList[subject]))
9513 					if(activitiesAtTime[day][hour].contains(ai)){
9514 						assert(!allActivities.contains(ai));
9515 						allActivities.append(ai);
9516 					}
9517 
9518 				/*//Now get the activitiy ids. I don't run through the InternalActivitiesList, even that is faster. I run through subgroupsList, because by that the activites are sorted by that in the html-table.
9519 				for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
9520 					if(students_timetable_weekly[subgroup][day][hour]!=UNALLOCATED_ACTIVITY){
9521 						Activity* act=&gt.rules.internalActivitiesList[students_timetable_weekly[subgroup][day][hour]];
9522 						if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9523 							if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour]))){
9524 								allActivities+students_timetable_weekly[subgroup][day][hour];
9525 							}
9526 					}
9527 				}
9528 				//Now run through the teachers timetable, because activities without a students set are still missing.
9529 				for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
9530 					if(teachers_timetable_weekly[teacher][day][hour]!=UNALLOCATED_ACTIVITY){
9531 						Activity* act=&gt.rules.internalActivitiesList[teachers_timetable_weekly[teacher][day][hour]];
9532 						if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9533 							if(!(allActivities.contains(teachers_timetable_weekly[teacher][day][hour]))){
9534 								assert(act->studentsNames.isEmpty());
9535 								allActivities+teachers_timetable_weekly[teacher][day][hour];
9536 							}
9537 					}
9538 				}*/
9539 				addActivitiesWithSameStartingTime(allActivities, hour);
9540 				tmpString+=writeActivitiesSubjects(htmlLevel, allActivities, printActivityTags);
9541 			}
9542 		}
9543 		if(repeatNames){
9544 			if(htmlLevel>=2)
9545 				tmpString+="          <th class=\"yAxis\">";
9546 			else
9547 				tmpString+="          <th>";
9548 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9549 			if(hour==0)
9550 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
9551 			else tmpString+="          <!-- span -->\n";
9552 		}
9553 		tmpString+="        </tr>\n";
9554 	}
9555 	//workaround begin.
9556 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9557 	if(repeatNames){
9558 		tmpString+="<td colspan=\"2\"></td>";
9559 	}
9560 	tmpString+="</tr>\n";
9561 	//workaround end.
9562 	tmpString+="      </tbody>\n";
9563 	tmpString+="    </table>\n\n";
9564 	return tmpString;
9565 }
9566 
9567 //by Volker Dirr
singleSubjectsTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxSubjects,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)9568 QString TimetableExport::singleSubjectsTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxSubjects, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
9569 	assert(day>=0);
9570 	assert(day<gt.rules.nDaysPerWeek);
9571 	QString tmpString;
9572 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
9573 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9574 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
9575 
9576 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
9577 	if(repeatNames){
9578 		tmpString+="<td rowspan=\"2\"></td>";
9579 	}
9580 	tmpString+="</tr>\n";
9581 	tmpString+="        <tr>\n          <!-- span -->\n";
9582 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9583 		if(htmlLevel>=2)
9584 			tmpString+="          <th class=\"xAxis\">";
9585 		else
9586 			tmpString+="          <th>";
9587 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9588 	}
9589 	tmpString+="        </tr>\n";
9590 	tmpString+="      </thead>\n";
9591 	/*workaround
9592 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9593 	*/
9594 	tmpString+="      <tbody>\n";
9595 	int currentCount=0;
9596 	for(int subject=0; subject<gt.rules.nInternalSubjects && currentCount<maxSubjects; subject++){
9597 		if(!excludedNames.contains(subject)){
9598 			currentCount++;
9599 			excludedNames<<subject;
9600 			tmpString+="        <tr>\n";
9601 			if(htmlLevel>=2)
9602 				tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9603 			else
9604 				tmpString+="        <th>"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9605 
9606 			///////by Liviu Lalescu
9607 			activitiesForCurrentSubject.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9608 			for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9609 				for(int h=0; h<gt.rules.nHoursPerDay; h++)
9610 					activitiesForCurrentSubject[d][h].clear();
9611 			for(int ai : qAsConst(gt.rules.activitiesForSubjectList[subject]))
9612 				if(best_solution.times[ai]!=UNALLOCATED_TIME){
9613 					int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9614 					int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9615 					Activity* act=&gt.rules.internalActivitiesList[ai];
9616 					for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9617 						activitiesForCurrentSubject[d][h+dd].append(ai);
9618 				}
9619 			///////end Liviu Lalescu
9620 
9621 			for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9622 				QList<int> allActivities;
9623 
9624 				allActivities=activitiesForCurrentSubject[day][hour];
9625 
9626 				/*allActivities.clear();
9627 				//Now get the activitiy ids. I don't run through the InternalActivitiesList, even that is faster. I run through subgroupsList, because by that the activites are sorted by that in the html-table.
9628 				for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
9629 					if(students_timetable_weekly[subgroup][day][hour]!=UNALLOCATED_ACTIVITY){
9630 						Activity* act=&gt.rules.internalActivitiesList[students_timetable_weekly[subgroup][day][hour]];
9631 						if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9632 							if(!(allActivities.contains(students_timetable_weekly[subgroup][day][hour]))){
9633 								allActivities+students_timetable_weekly[subgroup][day][hour];
9634 							}
9635 					}
9636 				}
9637 				//Now run through the teachers timetable, because activities without a students set are still missing.
9638 				for(int teacher=0; teacher<gt.rules.nInternalTeachers; teacher++){
9639 					if(teachers_timetable_weekly[teacher][day][hour]!=UNALLOCATED_ACTIVITY){
9640 						Activity* act=&gt.rules.internalActivitiesList[teachers_timetable_weekly[teacher][day][hour]];
9641 						if(act->subjectName==gt.rules.internalSubjectsList[subject]->name)
9642 							if(!(allActivities.contains(teachers_timetable_weekly[teacher][day][hour]))){
9643 								assert(act->studentsNames.isEmpty());
9644 								allActivities+teachers_timetable_weekly[teacher][day][hour];
9645 							}
9646 					}
9647 				}*/
9648 				addActivitiesWithSameStartingTime(allActivities, hour);
9649 				tmpString+=writeActivitiesSubjects(htmlLevel, allActivities, printActivityTags);
9650 			}
9651 			if(repeatNames){
9652 				if(htmlLevel>=2)
9653 					tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9654 				else
9655 					tmpString+="        <th>"+protect2(gt.rules.internalSubjectsList[subject]->name)+"</th>\n";
9656 			}
9657 			tmpString+="        </tr>\n";
9658 		}
9659 	}
9660 	//workaround begin.
9661 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9662 	if(repeatNames){
9663 		tmpString+="<td></td>";
9664 	}
9665 	tmpString+="</tr>\n";
9666 	//workaround end.
9667 	tmpString+="      </tbody>\n";
9668 	tmpString+="    </table>\n\n";
9669 	return tmpString;
9670 }
9671 
9672 
9673 //by Volker Dirr
singleActivityTagsTimetableDaysHorizontalHtml(int htmlLevel,int activityTag,const QString & saveTime,bool printActivityTags,bool repeatNames)9674 QString TimetableExport::singleActivityTagsTimetableDaysHorizontalHtml(int htmlLevel, int activityTag, const QString& saveTime, bool printActivityTags, bool repeatNames){
9675 	assert(activityTag>=0);
9676 	assert(activityTag<gt.rules.nInternalActivityTags);
9677 	QString tmpString;
9678 	///////by Liviu Lalescu
9679 	activitiesForCurrentActivityTag.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9680 	for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9681 		for(int h=0; h<gt.rules.nHoursPerDay; h++)
9682 			activitiesForCurrentActivityTag[d][h].clear();
9683 	for(int ai : qAsConst(gt.rules.activitiesForActivityTagList[activityTag]))
9684 		if(best_solution.times[ai]!=UNALLOCATED_TIME){
9685 			int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9686 			int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9687 			Activity* act=&gt.rules.internalActivitiesList[ai];
9688 			for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9689 				activitiesForCurrentActivityTag[d][h+dd].append(ai);
9690 		}
9691 	///////end Liviu Lalescu
9692 	tmpString+="    <table id=\"table_"+hashActivityTagIDsTimetable.value(gt.rules.internalActivityTagsList[activityTag]->name);
9693 	tmpString+="\" border=\"1\"";
9694 	if(activityTag%2==0)  tmpString+=" class=\"odd_table\"";
9695 	else tmpString+=" class=\"even_table\"";
9696 	tmpString+=">\n";
9697 
9698 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9699 
9700 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>";
9701 	if(repeatNames){
9702 		tmpString+="<td rowspan=\"2\"></td>";
9703 	}
9704 	tmpString+="</tr>\n";
9705 	tmpString+="        <tr>\n          <!-- span -->\n";
9706 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9707 		if(htmlLevel>=2)
9708 			tmpString+="          <th class=\"xAxis\">";
9709 		else
9710 			tmpString+="          <th>";
9711 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
9712 	}
9713 	tmpString+="        </tr>\n";
9714 	tmpString+="      </thead>\n";
9715 	/*workaround
9716 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9717 	*/
9718 	tmpString+="      <tbody>\n";
9719 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9720 		tmpString+="        <tr>\n";
9721 		if(htmlLevel>=2)
9722 			tmpString+="          <th class=\"yAxis\">";
9723 		else
9724 			tmpString+="          <th>";
9725 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9726 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9727 			QList<int> allActivities;
9728 
9729 			allActivities=activitiesForCurrentActivityTag[day][hour];
9730 
9731 			addActivitiesWithSameStartingTime(allActivities, hour);
9732 			tmpString+=writeActivitiesActivityTags(htmlLevel, allActivities, printActivityTags);
9733 		}
9734 		if(repeatNames){
9735 			if(htmlLevel>=2)
9736 				tmpString+="          <th class=\"yAxis\">";
9737 			else
9738 				tmpString+="          <th>";
9739 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9740 		}
9741 		tmpString+="        </tr>\n";
9742 	}
9743 	//workaround begin.
9744 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9745 	if(repeatNames){
9746 		tmpString+="<td></td>";
9747 	}
9748 	tmpString+="</tr>\n";
9749 	//workaround end.
9750 	tmpString+="      </tbody>\n";
9751 	tmpString+="    </table>\n\n";
9752 	return tmpString;
9753 }
9754 
9755 //by Volker Dirr
singleActivityTagsTimetableDaysVerticalHtml(int htmlLevel,int activityTag,const QString & saveTime,bool printActivityTags,bool repeatNames)9756 QString TimetableExport::singleActivityTagsTimetableDaysVerticalHtml(int htmlLevel, int activityTag, const QString& saveTime, bool printActivityTags, bool repeatNames){
9757 	assert(activityTag>=0);
9758 	assert(activityTag<gt.rules.nInternalActivityTags);
9759 	QString tmpString;
9760 	///////by Liviu Lalescu
9761 	activitiesForCurrentActivityTag.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9762 	for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9763 		for(int h=0; h<gt.rules.nHoursPerDay; h++)
9764 			activitiesForCurrentActivityTag[d][h].clear();
9765 	for(int ai : qAsConst(gt.rules.activitiesForActivityTagList[activityTag]))
9766 		if(best_solution.times[ai]!=UNALLOCATED_TIME){
9767 			int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9768 			int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9769 			Activity* act=&gt.rules.internalActivitiesList[ai];
9770 			for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9771 				activitiesForCurrentActivityTag[d][h+dd].append(ai);
9772 		}
9773 	///////end Liviu Lalescu
9774 	tmpString+="    <table id=\"table_"+hashActivityTagIDsTimetable.value(gt.rules.internalActivityTagsList[activityTag]->name);
9775 	tmpString+="\" border=\"1\"";
9776 	if(activityTag%2==0) tmpString+=" class=\"odd_table\"";
9777 	else tmpString+=" class=\"even_table\"";
9778 	tmpString+=">\n";
9779 
9780 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9781 
9782 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>";
9783 	if(repeatNames){
9784 		tmpString+="<td rowspan=\"2\"></td>";
9785 	}
9786 	tmpString+="</tr>\n";
9787 	tmpString+="        <tr>\n          <!-- span -->\n";
9788 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9789 		if(htmlLevel>=2)
9790 			tmpString+="          <th class=\"xAxis\">";
9791 		else
9792 			tmpString+="          <th>";
9793 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9794 	}
9795 	tmpString+="        </tr>\n";
9796 	tmpString+="      </thead>\n";
9797 	/*workaround
9798 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9799 	*/
9800 	tmpString+="      <tbody>\n";
9801 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9802 		tmpString+="        <tr>\n";
9803 		if(htmlLevel>=2)
9804 			tmpString+="          <th class=\"yAxis\">";
9805 		else
9806 			tmpString+="          <th>";
9807 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
9808 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9809 			QList<int> allActivities;
9810 
9811 			allActivities=activitiesForCurrentActivityTag[day][hour];
9812 
9813 			addActivitiesWithSameStartingTime(allActivities, hour);
9814 			tmpString+=writeActivitiesActivityTags(htmlLevel, allActivities, printActivityTags);
9815 		}
9816 		if(repeatNames){
9817 			if(htmlLevel>=2)
9818 				tmpString+="          <th class=\"yAxis\">";
9819 			else
9820 				tmpString+="          <th>";
9821 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
9822 		}
9823 		tmpString+="        </tr>\n";
9824 	}
9825 	//workaround begin.
9826 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9827 	if(repeatNames){
9828 		tmpString+="<td></td>";
9829 	}
9830 	tmpString+="</tr>\n";
9831 	//workaround end.
9832 	tmpString+="      </tbody>\n";
9833 	tmpString+="    </table>\n\n";
9834 	return tmpString;
9835 }
9836 
9837 
9838 //by Volker Dirr
singleActivityTagsTimetableTimeVerticalHtml(int htmlLevel,int maxActivityTag,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)9839 QString TimetableExport::singleActivityTagsTimetableTimeVerticalHtml(int htmlLevel, int maxActivityTag, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
9840 	QString tmpString;
9841 	tmpString+="    <table id=\"table\" border=\"1\">\n";
9842 
9843 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9844 
9845 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
9846 	int currentCount=0;
9847 	for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags && currentCount<maxActivityTag; activityTag++){
9848 		if(gt.rules.internalActivityTagsList[activityTag]->printable){
9849 			if(!excludedNames.contains(activityTag)){
9850 				currentCount++;
9851 				if(htmlLevel>=2)
9852 					tmpString+="          <th class=\"xAxis\">";
9853 				else
9854 					tmpString+="          <th>";
9855 				tmpString+=gt.rules.internalActivityTagsList[activityTag]->name+"</th>";
9856 			}
9857 		}
9858 	}
9859 	if(repeatNames){
9860 		tmpString+="<td colspan=\"2\"></td>";
9861 	}
9862 	tmpString+="</tr>\n      </thead>\n";
9863 	/*workaround
9864 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9865 	*/
9866 	tmpString+="      <tbody>\n";
9867 
9868 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9869 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9870 			tmpString+="        <tr>\n";
9871 			if(hour==0)
9872 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
9873 			else tmpString+="          <!-- span -->\n";
9874 			if(htmlLevel>=2)
9875 				tmpString+="          <th class=\"yAxis\">";
9876 			else
9877 				tmpString+="          <th>";
9878 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9879 
9880 			currentCount=0;
9881 			for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags && currentCount<maxActivityTag; activityTag++){
9882 				if(gt.rules.internalActivityTagsList[activityTag]->printable){
9883 					if(!excludedNames.contains(activityTag)){
9884 						currentCount++;
9885 						if(day+1==gt.rules.nDaysPerWeek && hour+1==gt.rules.nHoursPerDay)
9886 							excludedNames<<activityTag;
9887 						QList<int> allActivities;
9888 						allActivities.clear();
9889 
9890 						for(int ai : qAsConst(gt.rules.activitiesForActivityTagList[activityTag]))
9891 							if(activitiesAtTime[day][hour].contains(ai)){
9892 								assert(!allActivities.contains(ai));
9893 								allActivities.append(ai);
9894 							}
9895 
9896 						addActivitiesWithSameStartingTime(allActivities, hour);
9897 						tmpString+=writeActivitiesActivityTags(htmlLevel, allActivities, printActivityTags);
9898 					}
9899 				}
9900 			}
9901 			if(repeatNames){
9902 				if(htmlLevel>=2)
9903 					tmpString+="          <th class=\"yAxis\">";
9904 				else
9905 					tmpString+="          <th>";
9906 				tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9907 				if(hour==0)
9908 					tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
9909 				else tmpString+="          <!-- span -->\n";
9910 			}
9911 			tmpString+="        </tr>\n";
9912 		}
9913 	}
9914 	//workaround begin.
9915 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
9916 	if(repeatNames){
9917 		tmpString+="<td colspan=\"2\"></td>";
9918 	}
9919 	tmpString+="</tr>\n";
9920 	//workaround end.
9921 	tmpString+="      </tbody>\n    </table>\n";
9922 	return tmpString;
9923 }
9924 
9925 //by Volker Dirr
singleActivityTagsTimetableTimeHorizontalHtml(int htmlLevel,int maxActivityTag,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)9926 QString TimetableExport::singleActivityTagsTimetableTimeHorizontalHtml(int htmlLevel, int maxActivityTag, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
9927 	QString tmpString;
9928 	tmpString+="    <table id=\"table\" border=\"1\">\n";
9929 
9930 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
9931 
9932 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
9933 
9934 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9935 		tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
9936 	}
9937 	if(repeatNames){
9938 		tmpString+="<td rowspan=\"2\"></td>";
9939 	}
9940 	tmpString+="</tr>\n";
9941 	tmpString+="        <tr>\n          <!-- span -->\n";
9942 	for(int day=0; day<gt.rules.nDaysPerWeek; day++)
9943 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9944 			if(htmlLevel>=2)
9945 				tmpString+="          <th class=\"xAxis\">";
9946 			else
9947 				tmpString+="          <th>";
9948 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
9949 		}
9950 	tmpString+="        </tr>\n";
9951 	tmpString+="      </thead>\n";
9952 	/*workaround
9953 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
9954 	*/
9955 	tmpString+="      <tbody>\n";
9956 	int currentCount=0;
9957 	for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags && currentCount<maxActivityTag; activityTag++){
9958 		if(gt.rules.internalActivityTagsList[activityTag]->printable){
9959 			if(!excludedNames.contains(activityTag)){
9960 				currentCount++;
9961 				excludedNames<<activityTag;
9962 				tmpString+="        <tr>\n";
9963 				if(htmlLevel>=2)
9964 					tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
9965 				else
9966 					tmpString+="        <th>"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
9967 
9968 				///////by Liviu Lalescu
9969 				activitiesForCurrentActivityTag.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
9970 				for(int d=0; d<gt.rules.nDaysPerWeek; d++)
9971 					for(int h=0; h<gt.rules.nHoursPerDay; h++)
9972 						activitiesForCurrentActivityTag[d][h].clear();
9973 				for(int ai : qAsConst(gt.rules.activitiesForActivityTagList[activityTag]))
9974 					if(best_solution.times[ai]!=UNALLOCATED_TIME){
9975 						int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
9976 						int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
9977 						Activity* act=&gt.rules.internalActivitiesList[ai];
9978 						for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
9979 							activitiesForCurrentActivityTag[d][h+dd].append(ai);
9980 					}
9981 				///////end Liviu Lalescu
9982 
9983 				for(int day=0; day<gt.rules.nDaysPerWeek; day++){
9984 					for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
9985 						QList<int> allActivities;
9986 
9987 						allActivities=activitiesForCurrentActivityTag[day][hour];
9988 
9989 						addActivitiesWithSameStartingTime(allActivities, hour);
9990 						tmpString+=writeActivitiesActivityTags(htmlLevel, allActivities, printActivityTags);
9991 					}
9992 				}
9993 				if(repeatNames){
9994 					if(htmlLevel>=2)
9995 						tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
9996 					else
9997 						tmpString+="        <th>"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
9998 				}
9999 				tmpString+="        </tr>\n";
10000 			}
10001 		}
10002 	}
10003 	//workaround begin.
10004 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10005 	if(repeatNames){
10006 		tmpString+="<td></td>";
10007 	}
10008 	tmpString+="</tr>\n";
10009 	//workaround end.
10010 	tmpString+="      </tbody>\n    </table>\n";
10011 	return tmpString;
10012 }
10013 
10014 //by Volker Dirr
singleActivityTagsTimetableTimeVerticalDailyHtml(int htmlLevel,int day,int maxActivityTag,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)10015 QString TimetableExport::singleActivityTagsTimetableTimeVerticalDailyHtml(int htmlLevel, int day, int maxActivityTag, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
10016 	assert(day>=0);
10017 	assert(day<gt.rules.nDaysPerWeek);
10018 	QString tmpString;
10019 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
10020 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10021 	tmpString+="      <thead>\n        <tr><td colspan=\"2\"></td>";
10022 	int currentCount=0;
10023 	for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags && currentCount<maxActivityTag; activityTag++){
10024 		if(gt.rules.internalActivityTagsList[activityTag]->printable){
10025 			if(!excludedNames.contains(activityTag)){
10026 				currentCount++;
10027 
10028 				if(htmlLevel>=2)
10029 					tmpString+="          <th class=\"xAxis\">";
10030 				else
10031 					tmpString+="          <th>";
10032 				tmpString+=gt.rules.internalActivityTagsList[activityTag]->name+"</th>";
10033 			}
10034 		}
10035 	}
10036 	if(repeatNames){
10037 		tmpString+="<td colspan=\"2\"></td>";
10038 	}
10039 	tmpString+="</tr>\n      </thead>\n";
10040 	/*workaround
10041 	tmpString+="      <tfoot><tr><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
10042 	*/
10043 	tmpString+="      <tbody>\n";
10044 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
10045 		tmpString+="        <tr>\n";
10046 		if(hour==0)
10047 			tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
10048 		else tmpString+="          <!-- span -->\n";
10049 		if(htmlLevel>=2)
10050 			tmpString+="          <th class=\"yAxis\">";
10051 		else
10052 			tmpString+="          <th>";
10053 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
10054 
10055 		currentCount=0;
10056 		for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags && currentCount<maxActivityTag; activityTag++){
10057 			if(gt.rules.internalActivityTagsList[activityTag]->printable){
10058 				if(!excludedNames.contains(activityTag)){
10059 					currentCount++;
10060 					if(hour+1==gt.rules.nHoursPerDay)
10061 						excludedNames<<activityTag;
10062 					QList<int> allActivities;
10063 					allActivities.clear();
10064 
10065 					for(int ai : qAsConst(gt.rules.activitiesForActivityTagList[activityTag]))
10066 						if(activitiesAtTime[day][hour].contains(ai)){
10067 							assert(!allActivities.contains(ai));
10068 							allActivities.append(ai);
10069 						}
10070 
10071 					addActivitiesWithSameStartingTime(allActivities, hour);
10072 					tmpString+=writeActivitiesActivityTags(htmlLevel, allActivities, printActivityTags);
10073 				}
10074 			}
10075 		}
10076 		if(repeatNames){
10077 			if(htmlLevel>=2)
10078 				tmpString+="          <th class=\"yAxis\">";
10079 			else
10080 				tmpString+="          <th>";
10081 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
10082 			if(hour==0)
10083 				tmpString+="        <th rowspan=\""+QString::number(gt.rules.nHoursPerDay)+ "\">"+protect2vert(gt.rules.daysOfTheWeek[day])+"</th>\n";
10084 			else tmpString+="          <!-- span -->\n";
10085 		}
10086 		tmpString+="        </tr>\n";
10087 	}
10088 	//workaround begin.
10089 	tmpString+="        <tr class=\"foot\"><td colspan=\"2\"></td><td colspan=\""+QString::number(currentCount)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10090 	if(repeatNames){
10091 		tmpString+="<td colspan=\"2\"></td>";
10092 	}
10093 	tmpString+="</tr>\n";
10094 	//workaround end.
10095 	tmpString+="      </tbody>\n";
10096 	tmpString+="    </table>\n\n";
10097 	return tmpString;
10098 }
10099 
10100 //by Volker Dirr
singleActivityTagsTimetableTimeHorizontalDailyHtml(int htmlLevel,int day,int maxActivityTag,QSet<int> & excludedNames,const QString & saveTime,bool printActivityTags,bool repeatNames)10101 QString TimetableExport::singleActivityTagsTimetableTimeHorizontalDailyHtml(int htmlLevel, int day, int maxActivityTag, QSet<int>& excludedNames, const QString& saveTime, bool printActivityTags, bool repeatNames){
10102 	assert(day>=0);
10103 	assert(day<gt.rules.nDaysPerWeek);
10104 	QString tmpString;
10105 	tmpString+="    <table id=\"table_"+hashDayIDsTimetable.value(gt.rules.daysOfTheWeek[day])+"\" border=\"1\">\n";
10106 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10107 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td>";
10108 
10109 	tmpString+="<th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+protect2(gt.rules.daysOfTheWeek[day])+"</th>";
10110 	if(repeatNames){
10111 		tmpString+="<td rowspan=\"2\"></td>";
10112 	}
10113 	tmpString+="</tr>\n";
10114 	tmpString+="        <tr>\n          <!-- span -->\n";
10115 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
10116 		if(htmlLevel>=2)
10117 			tmpString+="          <th class=\"xAxis\">";
10118 		else
10119 			tmpString+="          <th>";
10120 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
10121 	}
10122 	tmpString+="        </tr>\n";
10123 	tmpString+="      </thead>\n";
10124 	/*workaround
10125 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
10126 	*/
10127 	tmpString+="      <tbody>\n";
10128 	int currentCount=0;
10129 	for(int activityTag=0; activityTag<gt.rules.nInternalActivityTags && currentCount<maxActivityTag; activityTag++){
10130 		if(gt.rules.internalActivityTagsList[activityTag]->printable){
10131 			if(!excludedNames.contains(activityTag)){
10132 				currentCount++;
10133 				excludedNames<<activityTag;
10134 				tmpString+="        <tr>\n";
10135 				if(htmlLevel>=2)
10136 					tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
10137 				else
10138 					tmpString+="        <th>"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
10139 
10140 				///////by Liviu Lalescu
10141 				activitiesForCurrentActivityTag.resize(gt.rules.nDaysPerWeek, gt.rules.nHoursPerDay);
10142 				for(int d=0; d<gt.rules.nDaysPerWeek; d++)
10143 					for(int h=0; h<gt.rules.nHoursPerDay; h++)
10144 						activitiesForCurrentActivityTag[d][h].clear();
10145 				for(int ai : qAsConst(gt.rules.activitiesForActivityTagList[activityTag]))
10146 					if(best_solution.times[ai]!=UNALLOCATED_TIME){
10147 						int d=best_solution.times[ai]%gt.rules.nDaysPerWeek;
10148 						int h=best_solution.times[ai]/gt.rules.nDaysPerWeek;
10149 						Activity* act=&gt.rules.internalActivitiesList[ai];
10150 						for(int dd=0; dd < act->duration && h+dd < gt.rules.nHoursPerDay; dd++)
10151 							activitiesForCurrentActivityTag[d][h+dd].append(ai);
10152 					}
10153 				///////end Liviu Lalescu
10154 
10155 				for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
10156 					QList<int> allActivities;
10157 
10158 					allActivities=activitiesForCurrentActivityTag[day][hour];
10159 
10160 					addActivitiesWithSameStartingTime(allActivities, hour);
10161 					tmpString+=writeActivitiesActivityTags(htmlLevel, allActivities, printActivityTags);
10162 				}
10163 				if(repeatNames){
10164 					if(htmlLevel>=2)
10165 						tmpString+="        <th class=\"yAxis\">"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
10166 					else
10167 						tmpString+="        <th>"+protect2(gt.rules.internalActivityTagsList[activityTag]->name)+"</th>\n";
10168 				}
10169 				tmpString+="        </tr>\n";
10170 			}
10171 		}
10172 	}
10173 	//workaround begin.
10174 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10175 	if(repeatNames){
10176 		tmpString+="<td></td>";
10177 	}
10178 	tmpString+="</tr>\n";
10179 	//workaround end.
10180 	tmpString+="      </tbody>\n";
10181 	tmpString+="    </table>\n\n";
10182 	return tmpString;
10183 }
10184 
10185 //by Volker Dirr
singleTeachersFreePeriodsTimetableDaysHorizontalHtml(int htmlLevel,const QString & saveTime,bool detailed,bool repeatNames)10186 QString TimetableExport::singleTeachersFreePeriodsTimetableDaysHorizontalHtml(int htmlLevel, const QString& saveTime, bool detailed, bool repeatNames){
10187 	QString tmpString;
10188 	tmpString+="    <table id=\"table\" border=\"1\">\n";
10189 
10190 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10191 
10192 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Teachers' Free Periods")+"</th>";
10193 	if(repeatNames){
10194 		tmpString+="<td rowspan=\"2\"></td>";
10195 	}
10196 	tmpString+="</tr>\n";
10197 	tmpString+="        <tr>\n          <!-- span -->\n";
10198 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
10199 		if(htmlLevel>=2)
10200 			tmpString+="          <th class=\"xAxis\">";
10201 		else
10202 			tmpString+="          <th>";
10203 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
10204 	}
10205 	tmpString+="        </tr>\n";
10206 	tmpString+="      </thead>\n";
10207 	/*workaround
10208 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nDaysPerWeek+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
10209 	*/
10210 	tmpString+="      <tbody>\n";
10211 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
10212 		tmpString+="        <tr>\n";
10213 		if(htmlLevel>=2)
10214 			tmpString+="          <th class=\"yAxis\">";
10215 		else
10216 			tmpString+="          <th>";
10217 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
10218 		for(int day=0; day<gt.rules.nDaysPerWeek; day++){
10219 			bool empty_slot;
10220 			empty_slot=true;
10221 			for(int tfp=0; tfp<TEACHERS_FREE_PERIODS_N_CATEGORIES; tfp++){
10222 				if(teachers_free_periods_timetable_weekly[tfp][day][hour].size()>0){
10223 					empty_slot=false;
10224 				}
10225 				if(!detailed&&tfp>=TEACHER_MUST_COME_EARLIER) break;
10226 			}
10227 			if(!empty_slot) tmpString+="          <td>";
10228 			for(int tfp=0; tfp<TEACHERS_FREE_PERIODS_N_CATEGORIES; tfp++){
10229 				if(teachers_free_periods_timetable_weekly[tfp][day][hour].size()>0){
10230 					if(htmlLevel>=2)
10231 						tmpString+="<div class=\"DESCRIPTION\">";
10232 					switch(tfp){
10233 						case TEACHER_HAS_SINGLE_GAP		: tmpString+=TimetableExport::tr("Single gap"); break;
10234 						case TEACHER_HAS_BORDER_GAP		: tmpString+=TimetableExport::tr("Border gap"); break;
10235 						case TEACHER_HAS_BIG_GAP		: tmpString+=TimetableExport::tr("Big gap"); break;
10236 						case TEACHER_MUST_COME_EARLIER		: tmpString+=TimetableExport::tr("Must come earlier"); break;
10237 						case TEACHER_MUST_STAY_LONGER		: tmpString+=TimetableExport::tr("Must stay longer"); break;
10238 						case TEACHER_MUST_COME_MUCH_EARLIER	: tmpString+=TimetableExport::tr("Must come much earlier"); break;
10239 						case TEACHER_MUST_STAY_MUCH_LONGER	: tmpString+=TimetableExport::tr("Must stay much longer"); break;
10240 						case TEACHER_HAS_A_FREE_DAY		: tmpString+=TimetableExport::tr("Free day"); break;
10241 						case TEACHER_IS_NOT_AVAILABLE		: tmpString+=TimetableExport::tr("Not available", "It refers to a teacher"); break;
10242 						default: assert(0==1); break;
10243 					}
10244 					if(htmlLevel>=2)
10245 						tmpString+=":</div>";
10246 					else tmpString+=":<br />";
10247 					if(htmlLevel>=3)
10248 						switch(tfp){
10249 							case TEACHER_HAS_SINGLE_GAP		: tmpString+="<div class=\"TEACHER_HAS_SINGLE_GAP\">"; break;
10250 							case TEACHER_HAS_BORDER_GAP		: tmpString+="<div class=\"TEACHER_HAS_BORDER_GAP\">"; break;
10251 							case TEACHER_HAS_BIG_GAP		: tmpString+="<div class=\"TEACHER_HAS_BIG_GAP\">"; break;
10252 							case TEACHER_MUST_COME_EARLIER		: tmpString+="<div class=\"TEACHER_MUST_COME_EARLIER\">"; break;
10253 							case TEACHER_MUST_STAY_LONGER		: tmpString+="<div class=\"TEACHER_MUST_STAY_LONGER\">"; break;
10254 							case TEACHER_MUST_COME_MUCH_EARLIER	: tmpString+="<div class=\"TEACHER_MUST_COME_MUCH_EARLIER\">"; break;
10255 							case TEACHER_MUST_STAY_MUCH_LONGER	: tmpString+="<div class=\"TEACHER_MUST_STAY_MUCH_LONGER\">"; break;
10256 							case TEACHER_HAS_A_FREE_DAY		: tmpString+="<div class=\"TEACHER_HAS_A_FREE_DAY\">"; break;
10257 							case TEACHER_IS_NOT_AVAILABLE		: tmpString+="<div class=\"TEACHER_IS_NOT_AVAILABLE\">"; break;
10258 							default: assert(0==1); break;
10259 						}
10260 					for(int t=0; t<teachers_free_periods_timetable_weekly[tfp][day][hour].size(); t++){
10261 						QString teacher_name = gt.rules.internalTeachersList[teachers_free_periods_timetable_weekly[tfp][day][hour].at(t)]->name;
10262 							switch(htmlLevel){
10263 								case 4 : tmpString+="<span class=\"t_"+hashTeacherIDsTimetable.value(teacher_name)+"\">"+protect2(teacher_name)+"</span>"; break;
10264 								case 5 : ;
10265 								case 6 : tmpString+="<span class=\"t_"+hashTeacherIDsTimetable.value(teacher_name)+"\" onmouseover=\"highlight('t_"+hashTeacherIDsTimetable.value(teacher_name)+"')\">"+protect2(teacher_name)+"</span>"; break;
10266 								default: tmpString+=protect2(teacher_name); break;
10267 							}
10268 						tmpString+="<br />";
10269 					}
10270 					if(htmlLevel>=3)
10271 						tmpString+="</div>";
10272 				}
10273 				if(!detailed&&tfp>=TEACHER_MUST_COME_EARLIER) break;
10274 			}
10275 			if(!empty_slot){
10276 				tmpString+="</td>\n";
10277 			} else {
10278 				tmpString+=writeEmpty(htmlLevel);
10279 			}
10280 		}
10281 		if(repeatNames){
10282 			if(htmlLevel>=2)
10283 				tmpString+="          <th class=\"yAxis\">";
10284 			else
10285 				tmpString+="          <th>";
10286 			tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
10287 		}
10288 		tmpString+="        </tr>\n";
10289 	}
10290 	//workaround begin.
10291 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nDaysPerWeek)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10292 	if(repeatNames){
10293 		tmpString+="<td></td>";
10294 	}
10295 	tmpString+="</tr>\n";
10296 	//workaround end.
10297 	tmpString+="      </tbody>\n";
10298 	tmpString+="    </table>\n\n";
10299 	return tmpString;
10300 }
10301 
10302 //by Volker Dirr
singleTeachersFreePeriodsTimetableDaysVerticalHtml(int htmlLevel,const QString & saveTime,bool detailed,bool repeatNames)10303 QString TimetableExport::singleTeachersFreePeriodsTimetableDaysVerticalHtml(int htmlLevel, const QString& saveTime, bool detailed, bool repeatNames){
10304 	QString tmpString;
10305 
10306 	tmpString+="    <table id=\"table\" border=\"1\">\n";
10307 
10308 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10309 
10310 	tmpString+="      <thead>\n        <tr><td rowspan=\"2\"></td><th colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Teachers' Free Periods")+"</th>";
10311 	if(repeatNames){
10312 		tmpString+="<td rowspan=\"2\"></td>";
10313 	}
10314 	tmpString+="</tr>\n";
10315 
10316 	tmpString+="        <tr>\n          <!-- span -->\n";
10317 	for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
10318 		if(htmlLevel>=2)
10319 			tmpString+="          <th class=\"xAxis\">";
10320 		else
10321 			tmpString+="          <th>";
10322 		tmpString+=protect2(gt.rules.hoursOfTheDay[hour])+"</th>\n";
10323 	}
10324 	tmpString+="        </tr>\n";
10325 	tmpString+="      </thead>\n";
10326 	/*workaround
10327 	tmpString+="      <tfoot><tr><td></td><td colspan=\""+gt.rules.nHoursPerDay+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr></tfoot>\n";
10328 	*/
10329 	tmpString+="      <tbody>\n";
10330 	for(int day=0; day<gt.rules.nDaysPerWeek; day++){
10331 		tmpString+="        <tr>\n";
10332 		if(htmlLevel>=2)
10333 			tmpString+="          <th class=\"yAxis\">";
10334 		else
10335 			tmpString+="          <th>";
10336 		tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
10337 		for(int hour=0; hour<gt.rules.nHoursPerDay; hour++){
10338 			bool empty_slot;
10339 			empty_slot=true;
10340 			for(int tfp=0; tfp<TEACHERS_FREE_PERIODS_N_CATEGORIES; tfp++){
10341 				if(teachers_free_periods_timetable_weekly[tfp][day][hour].size()>0){
10342 					empty_slot=false;
10343 				}
10344 				if(!detailed&&tfp>=TEACHER_MUST_COME_EARLIER) break;
10345 			}
10346 			if(!empty_slot) tmpString+="          <td>";
10347 			for(int tfp=0; tfp<TEACHERS_FREE_PERIODS_N_CATEGORIES; tfp++){
10348 				if(teachers_free_periods_timetable_weekly[tfp][day][hour].size()>0){
10349 					if(htmlLevel>=2)
10350 						tmpString+="<div class=\"DESCRIPTION\">";
10351 					switch(tfp){
10352 						case TEACHER_HAS_SINGLE_GAP		: tmpString+=TimetableExport::tr("Single gap"); break;
10353 						case TEACHER_HAS_BORDER_GAP		: tmpString+=TimetableExport::tr("Border gap"); break;
10354 						case TEACHER_HAS_BIG_GAP		: tmpString+=TimetableExport::tr("Big gap"); break;
10355 						case TEACHER_MUST_COME_EARLIER		: tmpString+=TimetableExport::tr("Must come earlier"); break;
10356 						case TEACHER_MUST_STAY_LONGER		: tmpString+=TimetableExport::tr("Must stay longer"); break;
10357 						case TEACHER_MUST_COME_MUCH_EARLIER	: tmpString+=TimetableExport::tr("Must come much earlier"); break;
10358 						case TEACHER_MUST_STAY_MUCH_LONGER	: tmpString+=TimetableExport::tr("Must stay much longer"); break;
10359 						case TEACHER_HAS_A_FREE_DAY		: tmpString+=TimetableExport::tr("Free day"); break;
10360 						case TEACHER_IS_NOT_AVAILABLE		: tmpString+=TimetableExport::tr("Not available"); break;
10361 						default: assert(0==1); break;
10362 					}
10363 					if(htmlLevel>=2)
10364 						tmpString+=":</div>";
10365 					else tmpString+=":<br />";
10366 					if(htmlLevel>=3)
10367 						switch(tfp){
10368 							case TEACHER_HAS_SINGLE_GAP		: tmpString+="<div class=\"TEACHER_HAS_SINGLE_GAP\">"; break;
10369 							case TEACHER_HAS_BORDER_GAP		: tmpString+="<div class=\"TEACHER_HAS_BORDER_GAP\">"; break;
10370 							case TEACHER_HAS_BIG_GAP		: tmpString+="<div class=\"TEACHER_HAS_BIG_GAP\">"; break;
10371 							case TEACHER_MUST_COME_EARLIER		: tmpString+="<div class=\"TEACHER_MUST_COME_EARLIER\">"; break;
10372 							case TEACHER_MUST_STAY_LONGER		: tmpString+="<div class=\"TEACHER_MUST_STAY_LONGER\">"; break;
10373 							case TEACHER_MUST_COME_MUCH_EARLIER	: tmpString+="<div class=\"TEACHER_MUST_COME_MUCH_EARLIER\">"; break;
10374 							case TEACHER_MUST_STAY_MUCH_LONGER	: tmpString+="<div class=\"TEACHER_MUST_STAY_MUCH_LONGER\">"; break;
10375 							case TEACHER_HAS_A_FREE_DAY		: tmpString+="<div class=\"TEACHER_HAS_A_FREE_DAY\">"; break;
10376 							case TEACHER_IS_NOT_AVAILABLE		: tmpString+="<div class=\"TEACHER_IS_NOT_AVAILABLE\">"; break;
10377 							default: assert(0==1); break;
10378 						}
10379 					for(int t=0; t<teachers_free_periods_timetable_weekly[tfp][day][hour].size(); t++){
10380 						QString teacher_name = gt.rules.internalTeachersList[teachers_free_periods_timetable_weekly[tfp][day][hour].at(t)]->name;
10381 							switch(htmlLevel){
10382 								case 4 : tmpString+="<span class=\"t_"+hashTeacherIDsTimetable.value(teacher_name)+"\">"+protect2(teacher_name)+"</span>"; break;
10383 								case 5 : ;
10384 								case 6 : tmpString+="<span class=\"t_"+hashTeacherIDsTimetable.value(teacher_name)+"\" onmouseover=\"highlight('t_"+hashTeacherIDsTimetable.value(teacher_name)+"')\">"+protect2(teacher_name)+"</span>"; break;
10385 								default: tmpString+=protect2(teacher_name); break;
10386 							}
10387 						tmpString+="<br />";
10388 					}
10389 					if(htmlLevel>=3)
10390 						tmpString+="</div>";
10391 				}
10392 				if(!detailed&&tfp>=TEACHER_MUST_COME_EARLIER) break;
10393 			}
10394 			if(!empty_slot){
10395 				tmpString+="</td>\n";
10396 			} else
10397 				tmpString+=writeEmpty(htmlLevel);
10398 		}
10399 		if(repeatNames){
10400 			if(htmlLevel>=2)
10401 				tmpString+="          <th class=\"yAxis\">";
10402 			else
10403 				tmpString+="          <th>";
10404 			tmpString+=protect2(gt.rules.daysOfTheWeek[day])+"</th>\n";
10405 		}
10406 		tmpString+="        </tr>\n";
10407 	}
10408 	//workaround begin.
10409 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(gt.rules.nHoursPerDay)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10410 	if(repeatNames){
10411 		tmpString+="<td></td>";
10412 	}
10413 	tmpString+="</tr>\n";
10414 	//workaround end.
10415 	tmpString+="      </tbody>\n";
10416 	tmpString+="    </table>\n\n";
10417 	return tmpString;
10418 }
10419 
10420 //by Volker Dirr
singleTeachersStatisticsHtml(int htmlLevel,const QString & saveTime,bool detailed,bool repeatNames,bool printAll)10421 QString TimetableExport::singleTeachersStatisticsHtml(int htmlLevel, const QString& saveTime, bool detailed, bool repeatNames, bool printAll){
10422 	Q_UNUSED(htmlLevel);
10423 	QString tmpString;
10424 	if(!printAll){
10425 		tmpString+="    <p>\n";
10426 		tmpString+="      <strong>"+tr("This is a teaser only. Values are not correct!")+"</strong>\n";
10427 		tmpString+="    </p>\n";
10428 	}
10429 	tmpString+="    <p>\n";
10430 	tmpString+="      "+tr("This file doesn't list limits that are set by constraints. It contains statistics about the min and max values of the currently calculated solution.")+"\n";
10431 	tmpString+="    </p>\n";
10432 
10433 	QString teachersString="";
10434 	int freeDaysAllTeachers=0;
10435 	int minFreeDaysAllTeachers=gt.rules.nDaysPerWeek;
10436 	int maxFreeDaysAllTeachers=0;
10437 	int gapsAllTeachers=0;
10438 	int minGapsPerDayAllTeachers=gt.rules.nHoursPerDay;
10439 	int maxGapsPerDayAllTeachers=0;
10440 	int minGapsPerWeekAllTeachers=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10441 	int maxGapsPerWeekAllTeachers=0;
10442 	int minHoursPerDayAllTeachers=gt.rules.nHoursPerDay;
10443 	int maxHoursPerDayAllTeachers=0;
10444 	int minHoursPerWeekAllTeachers=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10445 	int maxHoursPerWeekAllTeachers=0;
10446 	int totalNumberOfHours=0;
10447 	for(int tch=0; tch<gt.rules.nInternalTeachers; tch++){
10448 		int freeDaysSingleTeacher=0;
10449 		int gapsSingleTeacher=0;
10450 		int minGapsPerDaySingleTeacher=gt.rules.nHoursPerDay;
10451 		int maxGapsPerDaySingleTeacher=0;
10452 		int minHoursPerDaySingleTeacher=gt.rules.nHoursPerDay;
10453 		int maxHoursPerDaySingleTeacher=0;
10454 		int hoursPerWeekSingleTeacher=0;
10455 		for(int d=0; d<gt.rules.nDaysPerWeek; d++){
10456 			int firstPeriod=-1;
10457 			int lastPeriod=-1;
10458 			int gapsPerDaySingleTeacher=0;
10459 			int hoursPerDaySingleTeacher=0;
10460 			for(int h=0; h<gt.rules.nHoursPerDay; h++){
10461 				if(teachers_timetable_weekly[tch][d][h]!=UNALLOCATED_ACTIVITY){
10462 					if(firstPeriod==-1)
10463 						firstPeriod=h;
10464 					lastPeriod=h;
10465 					hoursPerDaySingleTeacher++;
10466 				}
10467 			}
10468 			if(firstPeriod==-1){
10469 				freeDaysSingleTeacher++;
10470 			} else {
10471 				for(int h=firstPeriod; h<lastPeriod; h++){
10472 					if(teachers_timetable_weekly[tch][d][h]==UNALLOCATED_ACTIVITY && teacherNotAvailableDayHour[tch][d][h]==false && breakDayHour[d][h]==false){
10473 						gapsPerDaySingleTeacher++;
10474 					}
10475 				}
10476 			}
10477 			gapsSingleTeacher+=gapsPerDaySingleTeacher;
10478 			if(minGapsPerDaySingleTeacher>gapsPerDaySingleTeacher)
10479 				minGapsPerDaySingleTeacher=gapsPerDaySingleTeacher;
10480 			if(maxGapsPerDaySingleTeacher<gapsPerDaySingleTeacher)
10481 				maxGapsPerDaySingleTeacher=gapsPerDaySingleTeacher;
10482 			if(hoursPerDaySingleTeacher>0){
10483 				if(minHoursPerDaySingleTeacher>hoursPerDaySingleTeacher)
10484 					minHoursPerDaySingleTeacher=hoursPerDaySingleTeacher;
10485 				if(maxHoursPerDaySingleTeacher<hoursPerDaySingleTeacher)
10486 					maxHoursPerDaySingleTeacher=hoursPerDaySingleTeacher;
10487 			}
10488 			hoursPerWeekSingleTeacher+=hoursPerDaySingleTeacher;
10489 		}
10490 		totalNumberOfHours+=hoursPerWeekSingleTeacher;
10491 		if(minHoursPerWeekAllTeachers>hoursPerWeekSingleTeacher)
10492 			minHoursPerWeekAllTeachers=hoursPerWeekSingleTeacher;
10493 		if(maxHoursPerWeekAllTeachers<hoursPerWeekSingleTeacher)
10494 			maxHoursPerWeekAllTeachers=hoursPerWeekSingleTeacher;
10495 
10496 		if(minFreeDaysAllTeachers>freeDaysSingleTeacher)
10497 			minFreeDaysAllTeachers=freeDaysSingleTeacher;
10498 		if(maxFreeDaysAllTeachers<freeDaysSingleTeacher)
10499 			maxFreeDaysAllTeachers=freeDaysSingleTeacher;
10500 
10501 		if(minGapsPerDayAllTeachers>minGapsPerDaySingleTeacher)
10502 			minGapsPerDayAllTeachers=minGapsPerDaySingleTeacher;
10503 		if(maxGapsPerDayAllTeachers<maxGapsPerDaySingleTeacher)
10504 			maxGapsPerDayAllTeachers=maxGapsPerDaySingleTeacher;
10505 
10506 		if(minGapsPerWeekAllTeachers>gapsSingleTeacher)
10507 			minGapsPerWeekAllTeachers=gapsSingleTeacher;
10508 		if(maxGapsPerWeekAllTeachers<gapsSingleTeacher)
10509 			maxGapsPerWeekAllTeachers=gapsSingleTeacher;
10510 
10511 		if(minHoursPerDayAllTeachers>minHoursPerDaySingleTeacher)
10512 			minHoursPerDayAllTeachers=minHoursPerDaySingleTeacher;
10513 		if(maxHoursPerDayAllTeachers<maxHoursPerDaySingleTeacher)
10514 			maxHoursPerDayAllTeachers=maxHoursPerDaySingleTeacher;
10515 
10516 		gapsAllTeachers+=gapsSingleTeacher;
10517 		freeDaysAllTeachers+=freeDaysSingleTeacher;
10518 
10519 		if(detailed){
10520 			if(freeDaysSingleTeacher==gt.rules.nDaysPerWeek)
10521 				minHoursPerDaySingleTeacher=0;
10522 			teachersString+="      <tr><th>"+protect2(gt.rules.internalTeachersList[tch]->name)
10523 								+"</th><td>"+QString::number(hoursPerWeekSingleTeacher)
10524 								+"</td><td>"+QString::number(freeDaysSingleTeacher)
10525 								+"</td><td>"+QString::number(gapsSingleTeacher)
10526 								+"</td><td>"+QString::number(minGapsPerDaySingleTeacher)
10527 								+"</td><td>"+QString::number(maxGapsPerDaySingleTeacher)
10528 								+"</td><td>"+QString::number(minHoursPerDaySingleTeacher)
10529 								+"</td><td>"+QString::number(maxHoursPerDaySingleTeacher)
10530 								+"</td>";
10531 
10532 			if(repeatNames){
10533 				teachersString+="<th>"+protect2(gt.rules.internalTeachersList[tch]->name)+"</th>";
10534 			}
10535 			teachersString+="</tr>\n";
10536 		}
10537 		if(!printAll && tch>10){
10538 			break;
10539 		}
10540 	}
10541 
10542 	tmpString+="    <table border=\"1\">\n";
10543 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10544 	tmpString+="      <thead>\n";
10545 	tmpString+="        <tr><th>"+tr("All teachers")
10546 				+"</th><th>"+tr("Hours per week")
10547 				+"</th><th>"+tr("Free days")
10548 				+"</th><th>"+tr("Gaps")
10549 				+"</th><th>"+tr("Gaps per day")
10550 				+"</th><th>"+tr("Hours per day")
10551 				+"</th></tr>\n";
10552 	tmpString+="      </thead>\n";
10553 	tmpString+="      <tr><th>"+tr("Sum")+"</th>";
10554 	tmpString+="<td>"+QString::number(totalNumberOfHours)+"</td>";
10555 	tmpString+="<td>"+QString::number(freeDaysAllTeachers)+"</td>";
10556 	tmpString+="<td>"+QString::number(gapsAllTeachers)+"</td>";
10557 	tmpString+="<td>---</td>";
10558 	tmpString+="<td>---</td>";
10559 	tmpString+="</tr>\n";
10560 	tmpString+="      <tr><th>"+tr("Average")+"</th>";
10561 	tmpString+="<td>"+QString::number(double(totalNumberOfHours)/gt.rules.nInternalTeachers,'f',2)+"</td>";
10562 	tmpString+="<td>"+QString::number(double(freeDaysAllTeachers)/gt.rules.nInternalTeachers,'f',2)+"</td>";
10563 	tmpString+="<td>"+QString::number(double(gapsAllTeachers)/gt.rules.nInternalTeachers,'f',2)+"</td>";
10564 	tmpString+="<td>---</td>";
10565 	tmpString+="<td>---</td>";
10566 	tmpString+="</tr>\n";
10567 	tmpString+="      <tr><th>"+tr("Min")+"</th>";
10568 	tmpString+="<td>"+QString::number(minHoursPerWeekAllTeachers)+"</td>";
10569 	tmpString+="<td>"+QString::number(minFreeDaysAllTeachers)+"</td>";
10570 	tmpString+="<td>"+QString::number(minGapsPerWeekAllTeachers)+"</td>";
10571 	tmpString+="<td>"+QString::number(minGapsPerDayAllTeachers)+"</td>";
10572 	tmpString+="<td>"+QString::number(minHoursPerDayAllTeachers)+"</td>";
10573 	tmpString+="</tr>\n";
10574 	tmpString+="      <tr><th>"+tr("Max")+"</th>";
10575 	tmpString+="<td>"+QString::number(maxHoursPerWeekAllTeachers)+"</td>";
10576 	tmpString+="<td>"+QString::number(maxFreeDaysAllTeachers)+"</td>";
10577 	tmpString+="<td>"+QString::number(maxGapsPerWeekAllTeachers)+"</td>";
10578 	tmpString+="<td>"+QString::number(maxGapsPerDayAllTeachers)+"</td>";
10579 	tmpString+="<td>"+QString::number(maxHoursPerDayAllTeachers)+"</td>";
10580 	tmpString+="</tr>\n";
10581 	//workaround begin.
10582 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(5)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr>\n";
10583 	//workaround end.
10584 	tmpString+="    </table>\n";
10585 
10586 	if(detailed){
10587 		tmpString+="    <p class=\"back0\"><br /></p>\n\n";
10588 
10589 		tmpString+="    <table border=\"1\">\n";
10590 		tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10591 		tmpString+="      <thead>\n";
10592 		tmpString+="        <tr><th>"+tr("Teacher")
10593 			+"</th><th>"+tr("Hours per week")
10594 			+"</th><th>"+tr("Free days")
10595 			+"</th><th>"+tr("Total gaps")
10596 			+"</th><th>"+tr("Min gaps per day")
10597 			+"</th><th>"+tr("Max gaps per day")
10598 			+"</th><th>"+tr("Min hours per day")
10599 			+"</th><th>"+tr("Max hours per day")
10600 			+"</th>";
10601 		if(repeatNames){
10602 			tmpString+="<th>"+tr("Teacher")+"</th>";
10603 		}
10604 		tmpString+="</tr>\n";
10605 		tmpString+="      </thead>\n";
10606 		tmpString+=teachersString;
10607 		//workaround begin.
10608 		tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(7)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10609 		if(repeatNames){
10610 			tmpString+="<td></td>";
10611 		}
10612 		tmpString+="</tr>\n";
10613 		//workaround end.
10614 		tmpString+="    </table>\n";
10615 	}
10616 	return tmpString;
10617 }
10618 
10619 //by Volker Dirr
singleStudentsStatisticsHtml(int htmlLevel,const QString & saveTime,bool detailed,bool repeatNames,bool printAll)10620 QString TimetableExport::singleStudentsStatisticsHtml(int htmlLevel, const QString& saveTime, bool detailed, bool repeatNames, bool printAll){
10621 	Q_UNUSED(htmlLevel);
10622 	QString tmpString;
10623 	if(!printAll){
10624 		tmpString+="    <p>\n";
10625 		tmpString+="      <strong>"+tr("This is a teaser only. Values are not correct!")+"</strong>\n";
10626 		tmpString+="    </p>\n";
10627 	}
10628 	tmpString+="    <p>\n";
10629 	tmpString+="      "+tr("This file doesn't list limits that are set by constraints. It contains statistics about the min and max values of the currently calculated solution.")+"\n";
10630 	tmpString+="    </p>\n";
10631 
10632 	//subgroups statistics (start)
10633 	QString subgroupsString="";
10634 	int freeDaysAllSubgroups=0;
10635 	int minFreeDaysAllSubgroups=gt.rules.nDaysPerWeek;
10636 	int maxFreeDaysAllSubgroups=0;
10637 	int gapsAllSubgroups=0;
10638 	int minGapsPerDayAllSubgroups=gt.rules.nHoursPerDay;
10639 	int maxGapsPerDayAllSubgroups=0;
10640 	int minGapsPerWeekAllSubgroups=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10641 	int maxGapsPerWeekAllSubgroups=0;
10642 	int minHoursPerDayAllSubgroups=gt.rules.nHoursPerDay;
10643 	int maxHoursPerDayAllSubgroups=0;
10644 	int minHoursPerWeekAllSubgroups=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10645 	int maxHoursPerWeekAllSubgroups=0;
10646 	int totalNumberOfHours=0;
10647 	QList<int> freeDaysPerWeekSubgroupList;
10648 	QList<int> gapsPerWeekSubgroupList;
10649 	QList<int> minGapsPerDaySubgroupList;
10650 	QList<int> maxGapsPerDaySubgroupList;
10651 	QList<int> minHoursPerDaySubgroupList;
10652 	QList<int> maxHoursPerDaySubgroupList;
10653 	QList<int> hoursPerWeekSubgroupList;
10654 	for(int subgroup=0; subgroup<gt.rules.nInternalSubgroups; subgroup++){
10655 		int freeDaysSingleSubgroup=0;
10656 		int gapsSingleSubgroup=0;
10657 		int minGapsPerDaySingleSubgroup=gt.rules.nHoursPerDay;
10658 		int maxGapsPerDaySingleSubgroup=0;
10659 		int minHoursPerDaySingleSubgroup=gt.rules.nHoursPerDay;
10660 		int maxHoursPerDaySingleSubgroup=0;
10661 		int hoursPerWeekSingleSubgroup=0;
10662 		for(int d=0; d<gt.rules.nDaysPerWeek; d++){
10663 			int firstPeriod=-1;
10664 			int lastPeriod=-1;
10665 			int gapsPerDaySingleSubgroup=0;
10666 			int hoursPerDaySingleSubgroup=0;
10667 			for(int h=0; h<gt.rules.nHoursPerDay; h++){
10668 				if(students_timetable_weekly[subgroup][d][h]!=UNALLOCATED_ACTIVITY){
10669 					if(firstPeriod==-1)
10670 						firstPeriod=h;
10671 					lastPeriod=h;
10672 					hoursPerDaySingleSubgroup++;
10673 				}
10674 			}
10675 			if(firstPeriod==-1){
10676 				freeDaysSingleSubgroup++;
10677 			} else {
10678 				for(int h=firstPeriod; h<lastPeriod; h++){
10679 					if(students_timetable_weekly[subgroup][d][h]==UNALLOCATED_ACTIVITY && subgroupNotAvailableDayHour[subgroup][d][h]==false && breakDayHour[d][h]==false){
10680 						gapsPerDaySingleSubgroup++;
10681 					}
10682 				}
10683 			}
10684 			gapsSingleSubgroup+=gapsPerDaySingleSubgroup;
10685 			if(minGapsPerDaySingleSubgroup>gapsPerDaySingleSubgroup)
10686 				minGapsPerDaySingleSubgroup=gapsPerDaySingleSubgroup;
10687 			if(maxGapsPerDaySingleSubgroup<gapsPerDaySingleSubgroup)
10688 				maxGapsPerDaySingleSubgroup=gapsPerDaySingleSubgroup;
10689 			if(hoursPerDaySingleSubgroup>0){
10690 				if(minHoursPerDaySingleSubgroup>hoursPerDaySingleSubgroup)
10691 					minHoursPerDaySingleSubgroup=hoursPerDaySingleSubgroup;
10692 				if(maxHoursPerDaySingleSubgroup<hoursPerDaySingleSubgroup)
10693 					maxHoursPerDaySingleSubgroup=hoursPerDaySingleSubgroup;
10694 			}
10695 			hoursPerWeekSingleSubgroup+=hoursPerDaySingleSubgroup;
10696 		}
10697 		totalNumberOfHours+=hoursPerWeekSingleSubgroup;
10698 		if(minHoursPerWeekAllSubgroups>hoursPerWeekSingleSubgroup)
10699 			minHoursPerWeekAllSubgroups=hoursPerWeekSingleSubgroup;
10700 		if(maxHoursPerWeekAllSubgroups<hoursPerWeekSingleSubgroup)
10701 			maxHoursPerWeekAllSubgroups=hoursPerWeekSingleSubgroup;
10702 
10703 		if(minFreeDaysAllSubgroups>freeDaysSingleSubgroup)
10704 			minFreeDaysAllSubgroups=freeDaysSingleSubgroup;
10705 		if(maxFreeDaysAllSubgroups<freeDaysSingleSubgroup)
10706 			maxFreeDaysAllSubgroups=freeDaysSingleSubgroup;
10707 
10708 		if(minGapsPerDayAllSubgroups>minGapsPerDaySingleSubgroup)
10709 			minGapsPerDayAllSubgroups=minGapsPerDaySingleSubgroup;
10710 		if(maxGapsPerDayAllSubgroups<maxGapsPerDaySingleSubgroup)
10711 			maxGapsPerDayAllSubgroups=maxGapsPerDaySingleSubgroup;
10712 
10713 		if(minGapsPerWeekAllSubgroups>gapsSingleSubgroup)
10714 			minGapsPerWeekAllSubgroups=gapsSingleSubgroup;
10715 		if(maxGapsPerWeekAllSubgroups<gapsSingleSubgroup)
10716 			maxGapsPerWeekAllSubgroups=gapsSingleSubgroup;
10717 
10718 		if(minHoursPerDayAllSubgroups>minHoursPerDaySingleSubgroup)
10719 			minHoursPerDayAllSubgroups=minHoursPerDaySingleSubgroup;
10720 		if(maxHoursPerDayAllSubgroups<maxHoursPerDaySingleSubgroup)
10721 			maxHoursPerDayAllSubgroups=maxHoursPerDaySingleSubgroup;
10722 
10723 		gapsAllSubgroups+=gapsSingleSubgroup;
10724 		freeDaysAllSubgroups+=freeDaysSingleSubgroup;
10725 
10726 		if(freeDaysSingleSubgroup==gt.rules.nDaysPerWeek)
10727 			minHoursPerDaySingleSubgroup=0;
10728 		if(detailed){
10729 			subgroupsString+="      <tr><th>"+protect2(gt.rules.internalSubgroupsList[subgroup]->name)
10730 							+"</th><td>"+QString::number(hoursPerWeekSingleSubgroup)
10731 							+"</td><td>"+QString::number(freeDaysSingleSubgroup)
10732 							+"</td><td>"+QString::number(gapsSingleSubgroup)
10733 							+"</td><td>"+QString::number(minGapsPerDaySingleSubgroup)
10734 							+"</td><td>"+QString::number(maxGapsPerDaySingleSubgroup)
10735 							+"</td><td>"+QString::number(minHoursPerDaySingleSubgroup)
10736 							+"</td><td>"+QString::number(maxHoursPerDaySingleSubgroup)
10737 							+"</td>";
10738 			if(repeatNames){
10739 				subgroupsString+="<th>"+protect2(gt.rules.internalSubgroupsList[subgroup]->name)+"</th>";
10740 			}
10741 			subgroupsString+="</tr>\n";
10742 			hoursPerWeekSubgroupList<<hoursPerWeekSingleSubgroup;
10743 			freeDaysPerWeekSubgroupList<<freeDaysSingleSubgroup;
10744 			gapsPerWeekSubgroupList<<gapsSingleSubgroup;
10745 			minGapsPerDaySubgroupList<<minGapsPerDaySingleSubgroup;
10746 			maxGapsPerDaySubgroupList<<maxGapsPerDaySingleSubgroup;
10747 			minHoursPerDaySubgroupList<<minHoursPerDaySingleSubgroup;
10748 			maxHoursPerDaySubgroupList<<maxHoursPerDaySingleSubgroup;
10749 		}
10750 		if(!printAll && subgroup>10){
10751 			break;
10752 		}
10753 	}
10754 
10755 	tmpString+="    <table border=\"1\">\n";
10756 	tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10757 	tmpString+="      <thead>\n";
10758 	tmpString+="        <tr><th>"+tr("All students")
10759 				+"</th><th>"+tr("Hours per week")
10760 				+"</th><th>"+tr("Free days")
10761 				+"</th><th>"+tr("Gaps")
10762 				+"</th><th>"+tr("Gaps per day")
10763 				+"</th><th>"+tr("Hours per day")
10764 				+"</th></tr>\n";
10765 	tmpString+="      </thead>\n";
10766 	tmpString+="      <tr><th>"+tr("Sum")+"</th>";
10767 	tmpString+="<td>"+QString::number(totalNumberOfHours)+"</td>";
10768 	tmpString+="<td>"+QString::number(freeDaysAllSubgroups)+"</td>";
10769 	tmpString+="<td>"+QString::number(gapsAllSubgroups)+"</td>";
10770 	tmpString+="<td>---</td>";
10771 	tmpString+="<td>---</td>";
10772 	tmpString+="</tr>\n";
10773 	tmpString+="      <tr><th>"+tr("Average")+"</th>";
10774 	tmpString+="<td>"+QString::number(double(totalNumberOfHours)/gt.rules.nInternalSubgroups,'f',2)+"</td>";
10775 	tmpString+="<td>"+QString::number(double(freeDaysAllSubgroups)/gt.rules.nInternalSubgroups,'f',2)+"</td>";
10776 	tmpString+="<td>"+QString::number(double(gapsAllSubgroups)/gt.rules.nInternalSubgroups,'f',2)+"</td>";
10777 	tmpString+="<td>---</td>";
10778 	tmpString+="<td>---</td>";
10779 	tmpString+="</tr>\n";
10780 	tmpString+="      <tr><th>"+tr("Min")+"</th>";
10781 	tmpString+="<td>"+QString::number(minHoursPerWeekAllSubgroups)+"</td>";
10782 	tmpString+="<td>"+QString::number(minFreeDaysAllSubgroups)+"</td>";
10783 	tmpString+="<td>"+QString::number(minGapsPerWeekAllSubgroups)+"</td>";
10784 	tmpString+="<td>"+QString::number(minGapsPerDayAllSubgroups)+"</td>";
10785 	tmpString+="<td>"+QString::number(minHoursPerDayAllSubgroups)+"</td>";
10786 	tmpString+="</tr>\n";
10787 	tmpString+="      <tr><th>"+tr("Max")+"</th>";
10788 	tmpString+="<td>"+QString::number(maxHoursPerWeekAllSubgroups)+"</td>";
10789 	tmpString+="<td>"+QString::number(maxFreeDaysAllSubgroups)+"</td>";
10790 	tmpString+="<td>"+QString::number(maxGapsPerWeekAllSubgroups)+"</td>";
10791 	tmpString+="<td>"+QString::number(maxGapsPerDayAllSubgroups)+"</td>";
10792 	tmpString+="<td>"+QString::number(maxHoursPerDayAllSubgroups)+"</td>";
10793 	tmpString+="</tr>\n";
10794 	//workaround begin.
10795 	tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(5)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td></tr>\n";
10796 	//workaround end.
10797 	tmpString+="    </table>\n";
10798 
10799 	tmpString+="    <p class=\"back0\"><br /></p>\n\n";
10800 
10801 	//subgroups statistics (end)
10802 
10803 	if(detailed){
10804 		if(!printAll){
10805 			//similar to source in else part (start)
10806 			tmpString+="    <p></p>\n";
10807 			tmpString+="    <table border=\"1\">\n";
10808 			tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10809 			tmpString+="      <thead>\n";
10810 			tmpString+="      <tr><th>"+tr("Subgroup")
10811 					+"</th><th>"+tr("Hours per week")
10812 					+"</th><th>"+tr("Free days")
10813 					+"</th><th>"+tr("Total gaps")
10814 					+"</th><th>"+tr("Min gaps per day")
10815 					+"</th><th>"+tr("Max gaps per day")
10816 					+"</th><th>"+tr("Min hours per day")
10817 					+"</th><th>"+tr("Max hours per day")
10818 					+"</th>";
10819 			if(repeatNames){
10820 				tmpString+="<td>"+tr("Subgroup")+"</td>";
10821 			}
10822 			tmpString+="</tr>\n";
10823 			tmpString+="      </thead>\n";
10824 			tmpString+=subgroupsString;
10825 			//workaround begin.
10826 			tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(6)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10827 			if(repeatNames){
10828 				tmpString+="<td></td>";
10829 			}
10830 			tmpString+="</tr>\n";
10831 			//workaround end.
10832 			tmpString+="    </table>\n";
10833 			//similar to source in else part (end)
10834 		} else {
10835 			//groups and years statistics (start)
10836 			QString yearsString="    <table border=\"1\">\n";
10837 			yearsString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10838 			yearsString+="      <thead>\n";
10839 			yearsString+="      <tr><th>"+tr("Year")
10840 							+"</th><th>"+tr("Min hours per week")
10841 							+"</th><th>"+tr("Max hours per week")
10842 							+"</th><th>"+tr("Min free days")
10843 							+"</th><th>"+tr("Max free days")
10844 							+"</th><th>"+tr("Min hours per day")
10845 							+"</th><th>"+tr("Max hours per day")
10846 							+"</th><th>"+tr("Min gaps per week")
10847 							+"</th><th>"+tr("Max gaps per week")
10848 							+"</th><th>"+tr("Min gaps per day")
10849 							+"</th><th>"+tr("Max gaps per day")
10850 							+"</th>";
10851 			if(repeatNames){
10852 					yearsString+="<th>"+tr("Year")+"</th>";
10853 			}
10854 			yearsString+="</tr>\n";
10855 			yearsString+="      </thead>\n";
10856 			QString groupsString="    <table border=\"1\">\n";
10857 			groupsString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
10858 			groupsString+="      <thead>\n";
10859 			groupsString+="      <tr><th>"+tr("Group")
10860 							+"</th><th>"+tr("Min hours per week")
10861 							+"</th><th>"+tr("Max hours per week")
10862 							+"</th><th>"+tr("Min free days")
10863 							+"</th><th>"+tr("Max free days")
10864 							+"</th><th>"+tr("Min hours per day")
10865 							+"</th><th>"+tr("Max hours per day")
10866 							+"</th><th>"+tr("Min gaps per week")
10867 							+"</th><th>"+tr("Max gaps per week")
10868 							+"</th><th>"+tr("Min gaps per day")
10869 							+"</th><th>"+tr("Max gaps per day")
10870 							+"</th>";
10871 			if(repeatNames){
10872 				groupsString+="<th>"+tr("Group")+"</th>";
10873 			}
10874 			groupsString+="</tr>\n";
10875 			groupsString+="      </thead>\n";
10876 			for(int i=0; i<gt.rules.augmentedYearsList.size(); i++){
10877 				StudentsYear* sty=gt.rules.augmentedYearsList[i];
10878 				int minNumberOfHoursYear=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10879 				int maxNumberOfHoursYear=0;
10880 				int minFreeDaysPerWeekYear=gt.rules.nDaysPerWeek;
10881 				int maxFreeDaysPerWeekYear=0;
10882 				int minGapsPerDayYear=gt.rules.nHoursPerDay;
10883 				int maxGapsPerDayYear=0;
10884 				int minGapsPerWeekYear=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10885 				int maxGapsPerWeekYear=0;
10886 				int minHoursPerDayYear=gt.rules.nHoursPerDay;
10887 				int maxHoursPerDayYear=0;
10888 				for(int g=0; g<sty->groupsList.size(); g++){
10889 					StudentsGroup* stg=sty->groupsList[g];
10890 					int minNumberOfHoursGroup=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10891 					int maxNumberOfHoursGroup=0;
10892 					int minFreeDaysPerWeekGroup=gt.rules.nDaysPerWeek;
10893 					int maxFreeDaysPerWeekGroup=0;
10894 					int minGapsPerDayGroup=gt.rules.nHoursPerDay;
10895 					int maxGapsPerDayGroup=0;
10896 					int minGapsPerWeekGroup=gt.rules.nHoursPerDay*gt.rules.nDaysPerWeek;
10897 					int maxGapsPerWeekGroup=0;
10898 					int minHoursPerDayGroup=gt.rules.nHoursPerDay;
10899 					int maxHoursPerDayGroup=0;
10900 					for(int sg=0; sg<stg->subgroupsList.size(); sg++){
10901 						StudentsSubgroup* sts=stg->subgroupsList[sg];
10902 						int subgroup=sts->indexInInternalSubgroupsList;
10903 
10904 						if(minNumberOfHoursGroup>hoursPerWeekSubgroupList.at(subgroup))
10905 							minNumberOfHoursGroup=hoursPerWeekSubgroupList.at(subgroup);
10906 						if(maxNumberOfHoursGroup<hoursPerWeekSubgroupList.at(subgroup))
10907 							maxNumberOfHoursGroup=hoursPerWeekSubgroupList.at(subgroup);
10908 
10909 						if(minFreeDaysPerWeekGroup>freeDaysPerWeekSubgroupList.at(subgroup))
10910 							minFreeDaysPerWeekGroup=freeDaysPerWeekSubgroupList.at(subgroup);
10911 						if(maxFreeDaysPerWeekGroup<freeDaysPerWeekSubgroupList.at(subgroup))
10912 							maxFreeDaysPerWeekGroup=freeDaysPerWeekSubgroupList.at(subgroup);
10913 
10914 						if(minHoursPerDayGroup>minHoursPerDaySubgroupList.at(subgroup))
10915 							minHoursPerDayGroup=minHoursPerDaySubgroupList.at(subgroup);
10916 						if(maxHoursPerDayGroup<maxHoursPerDaySubgroupList.at(subgroup))
10917 							maxHoursPerDayGroup=maxHoursPerDaySubgroupList.at(subgroup);
10918 
10919 						if(minGapsPerWeekGroup>gapsPerWeekSubgroupList.at(subgroup))
10920 							minGapsPerWeekGroup=gapsPerWeekSubgroupList.at(subgroup);
10921 						if(maxGapsPerWeekGroup<gapsPerWeekSubgroupList.at(subgroup))
10922 							maxGapsPerWeekGroup=gapsPerWeekSubgroupList.at(subgroup);
10923 
10924 						if(minGapsPerDayGroup>minGapsPerDaySubgroupList.at(subgroup))
10925 							minGapsPerDayGroup=minGapsPerDaySubgroupList.at(subgroup);
10926 						if(maxGapsPerDayGroup<maxGapsPerDaySubgroupList.at(subgroup))
10927 							maxGapsPerDayGroup=maxGapsPerDaySubgroupList.at(subgroup);
10928 					}
10929 					//print groups
10930 					groupsString+="      <tr><th>"+protect2(stg->name)+"</th><td>"
10931 					+QString::number(minNumberOfHoursGroup)+"</td><td>"+QString::number(maxNumberOfHoursGroup)+"</td><td>"
10932 					+QString::number(minFreeDaysPerWeekGroup)+"</td><td>"+QString::number(maxFreeDaysPerWeekGroup)+"</td><td>"
10933 					+QString::number(minHoursPerDayGroup)+"</td><td>"+QString::number(maxHoursPerDayGroup)+"</td><td>"
10934 					+QString::number(minGapsPerWeekGroup)+"</td><td>"+QString::number(maxGapsPerWeekGroup)+"</td><td>"
10935 					+QString::number(minGapsPerDayGroup)+"</td><td>"+QString::number(maxGapsPerDayGroup)+"</td>";
10936 					if(repeatNames){
10937 						groupsString+="<th>"+protect2(stg->name)+"</th>";
10938 					}
10939 					groupsString+="</tr>\n";
10940 
10941 					//check years
10942 					if(minNumberOfHoursYear>minNumberOfHoursGroup)
10943 						minNumberOfHoursYear=minNumberOfHoursGroup;
10944 					if(maxNumberOfHoursYear<maxNumberOfHoursGroup)
10945 						maxNumberOfHoursYear=maxNumberOfHoursGroup;
10946 
10947 					if(minFreeDaysPerWeekYear>minFreeDaysPerWeekGroup)
10948 						minFreeDaysPerWeekYear=minFreeDaysPerWeekGroup;
10949 					if(maxFreeDaysPerWeekYear<maxFreeDaysPerWeekGroup)
10950 						maxFreeDaysPerWeekYear=maxFreeDaysPerWeekGroup;
10951 
10952 					if(minHoursPerDayYear>minHoursPerDayGroup)
10953 						minHoursPerDayYear=minHoursPerDayGroup;
10954 					if(maxHoursPerDayYear<maxHoursPerDayGroup)
10955 						maxHoursPerDayYear=maxHoursPerDayGroup;
10956 
10957 					if(minGapsPerWeekYear>minGapsPerWeekGroup)
10958 						minGapsPerWeekYear=minGapsPerWeekGroup;
10959 					if(maxGapsPerWeekYear<maxGapsPerWeekGroup)
10960 						maxGapsPerWeekYear=maxGapsPerWeekGroup;
10961 
10962 					if(minGapsPerDayYear>minGapsPerDayGroup)
10963 						minGapsPerDayYear=minGapsPerDayGroup;
10964 					if(maxGapsPerDayYear<maxGapsPerDayGroup)
10965 						maxGapsPerDayYear=maxGapsPerDayGroup;
10966 				}
10967 				//print years
10968 					yearsString+="      <tr><th>"+protect2(sty->name)+"</th><td>"
10969 					+QString::number(minNumberOfHoursYear)+"</td><td>"+QString::number(maxNumberOfHoursYear)+"</td><td>"
10970 					+QString::number(minFreeDaysPerWeekYear)+"</td><td>"+QString::number(maxFreeDaysPerWeekYear)+"</td><td>"
10971 					+QString::number(minHoursPerDayYear)+"</td><td>"+QString::number(maxHoursPerDayYear)+"</td><td>"
10972 					+QString::number(minGapsPerWeekYear)+"</td><td>"+QString::number(maxGapsPerWeekYear)+"</td><td>"
10973 					+QString::number(minGapsPerDayYear)+"</td><td>"+QString::number(maxGapsPerDayYear)+"</td>";
10974 					if(repeatNames){
10975 						yearsString+="<th>"+protect2(sty->name)+"</th>";
10976 					}
10977 					yearsString+="</tr>\n";
10978 			}
10979 			//workaround begin.
10980 			groupsString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(10)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10981 			if(repeatNames){
10982 				groupsString+="<td></td>";
10983 			}
10984 			groupsString+="</tr>\n";
10985 			//workaround end.
10986 			groupsString+="    </table>\n";
10987 			groupsString+="    <p class=\"back0\"><br /></p>\n\n";
10988 			//workaround begin.
10989 			yearsString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(10)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
10990 			if(repeatNames){
10991 				yearsString+="<td></td>";
10992 			}
10993 			yearsString+="</tr>\n";
10994 			//workaround end.
10995 			yearsString+="    </table>\n";
10996 			yearsString+="    <p class=\"back0\"><br /></p>\n\n";
10997 			tmpString+=yearsString;
10998 			tmpString+=groupsString;
10999 			//similar to source in if part (start)
11000 			tmpString+="    <p></p>\n";
11001 			tmpString+="    <table border=\"1\">\n";
11002 			tmpString+="      <caption>"+protect2(gt.rules.institutionName)+"</caption>\n";
11003 			tmpString+="      <thead>\n";
11004 			tmpString+="      <tr><th>"+tr("Subgroup")
11005 					+"</th><th>"+tr("Hours per week")
11006 					+"</th><th>"+tr("Free days")
11007 					+"</th><th>"+tr("Total gaps")
11008 					+"</th><th>"+tr("Min gaps per day")
11009 					+"</th><th>"+tr("Max gaps per day")
11010 					+"</th><th>"+tr("Min hours per day")
11011 					+"</th><th>"+tr("Max hours per day")
11012 					+"</th>";
11013 			if(repeatNames){
11014 				tmpString+="<td>"+tr("Subgroup")+"</td>";
11015 			}
11016 			tmpString+="</tr>\n";
11017 			tmpString+="      </thead>\n";
11018 			tmpString+=subgroupsString;
11019 			//workaround begin.
11020 			tmpString+="        <tr class=\"foot\"><td></td><td colspan=\""+QString::number(7)+"\">"+TimetableExport::tr("Timetable generated with FET %1 on %2", "%1 is FET version, %2 is the date and time of generation").arg(FET_VERSION).arg(saveTime)+"</td>";
11021 			if(repeatNames){
11022 				tmpString+="<td></td>";
11023 			}
11024 			tmpString+="</tr>\n";
11025 			//workaround end.
11026 			tmpString+="    </table>\n";
11027 			//similar to source in if part (end)
11028 		}
11029 	}
11030 	//groups and years statistics (end)
11031 	return tmpString;
11032 }
11033