1 /***************************************************************************
2                           timetableviewroomstimehorizontalform.cpp  -  description
3                              -------------------
4     begin                : 2017
5     copyright            : (C) 2017 by Lalescu Liviu
6     email                : Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software: you can redistribute it and/or modify  *
12  *   it under the terms of the GNU Affero General Public License as        *
13  *   published by the Free Software Foundation, either version 3 of the    *
14  *   License, or (at your option) any later version.                       *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <Qt>
19 
20 #include <QtGlobal>
21 
22 #include "longtextmessagebox.h"
23 
24 #include "fetmainform.h"
25 #include "timetableviewroomstimehorizontalform.h"
26 #include "timetable_defs.h"
27 #include "timetable.h"
28 #include "solution.h"
29 
30 #include "fet.h"
31 
32 #include "matrix.h"
33 
34 #include "lockunlock.h"
35 
36 #include <QMessageBox>
37 
38 #include <QTableWidget>
39 #include <QTableWidgetItem>
40 #include <QHeaderView>
41 
42 #include <QAbstractItemView>
43 
44 #include <QListWidget>
45 
46 #include <QList>
47 
48 #include <QCoreApplication>
49 #include <QApplication>
50 
51 #include <QString>
52 #include <QStringList>
53 
54 #include <QSplitter>
55 #include <QSettings>
56 #include <QObject>
57 #include <QMetaObject>
58 
59 //begin by Marco Vassura
60 #include <QBrush>
61 #include <QColor>
62 //end by Marco Vassura
63 
64 extern const QString COMPANY;
65 extern const QString PROGRAM;
66 
67 extern bool students_schedule_ready;
68 extern bool teachers_schedule_ready;
69 extern bool rooms_schedule_ready;
70 
71 extern Solution best_solution;
72 
73 extern bool simulation_running;
74 extern bool simulation_running_multi;
75 
76 extern Matrix2D<double> notAllowedRoomTimePercentages;
77 extern Matrix2D<bool> breakDayHour;
78 
79 extern QSet<int> idsOfLockedTime;		//care about locked activities in view forms
80 extern QSet<int> idsOfLockedSpace;		//care about locked activities in view forms
81 extern QSet<int> idsOfPermanentlyLockedTime;	//care about locked activities in view forms
82 extern QSet<int> idsOfPermanentlyLockedSpace;	//care about locked activities in view forms
83 
84 extern CommunicationSpinBox communicationSpinBox;	//small hint to sync the forms
85 
86 extern const int MINIMUM_WIDTH_SPIN_BOX_VALUE;
87 extern const int MINIMUM_HEIGHT_SPIN_BOX_VALUE;
88 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const89 void TimetableViewRoomsTimeHorizontalDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
90 {
91 	QStyledItemDelegate::paint(painter, option, index);
92 
93 	//int day=index.column()/gt.rules.nHoursPerDay;
94 	//int hour=index.column()%gt.rules.nHoursPerDay;
95 	int hour=index.column()%nColumns;
96 
97 	/*if(day>=0 && day<gt.rules.nDaysPerWeek-1 && hour==gt.rules.nHoursPerDay-1){
98 		QPen pen(painter->pen());
99 		pen.setWidth(2);
100 		painter->setPen(pen);
101 		painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
102 	}*/
103 
104 	/*assert(table!=nullptr);
105 	QBrush bg(table->item(index.row(), index.column())->background());
106 	QPen pen(painter->pen());
107 
108 	double brightness = bg.color().redF()*0.299 + bg.color().greenF()*0.587 + bg.color().blueF()*0.114;
109 	if (brightness<0.5)
110 		pen.setColor(Qt::white);
111 	else
112 		pen.setColor(Qt::black);
113 
114 	painter->setPen(pen);*/
115 
116 	if(hour==0)
117 		painter->drawLine(option.rect.topLeft(), option.rect.bottomLeft());
118 	if(hour==nColumns-1)
119 		painter->drawLine(option.rect.topRight(), option.rect.bottomRight());
120 
121 	if(index.row()==0)
122 		painter->drawLine(option.rect.topLeft(), option.rect.topRight());
123 	if(index.row()==nRows-1)
124 		painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
125 }
126 
127 
TimetableViewRoomsTimeHorizontalForm(QWidget * parent)128 TimetableViewRoomsTimeHorizontalForm::TimetableViewRoomsTimeHorizontalForm(QWidget* parent): QDialog(parent)
129 {
130 	setupUi(this);
131 
132 	closePushButton->setDefault(true);
133 
134 	detailsTextEdit->setReadOnly(true);
135 
136 	//columnResizeModeInitialized=false;
137 
138 	verticalSplitter->setStretchFactor(0, 20);
139 	verticalSplitter->setStretchFactor(1, 1);
140 	horizontalSplitter->setStretchFactor(0, 5);
141 	horizontalSplitter->setStretchFactor(1, 1);
142 
143 	roomsTimetableTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
144 
145 	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
146 	connect(roomsTimetableTable, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)));
147 	connect(lockTimePushButton, SIGNAL(clicked()), this, SLOT(lockTime()));
148 	connect(lockSpacePushButton, SIGNAL(clicked()), this, SLOT(lockSpace()));
149 	connect(lockTimeSpacePushButton, SIGNAL(clicked()), this, SLOT(lockTimeSpace()));
150 
151 	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
152 
153 	lockRadioButton->setChecked(true);
154 	unlockRadioButton->setChecked(false);
155 	toggleRadioButton->setChecked(false);
156 
157 	centerWidgetOnScreen(this);
158 	restoreFETDialogGeometry(this);
159 
160 	//restore vertical splitter state
161 	QSettings settings(COMPANY, PROGRAM);
162 	if(settings.contains(this->metaObject()->className()+QString("/vertical-splitter-state")))
163 		verticalSplitter->restoreState(settings.value(this->metaObject()->className()+QString("/vertical-splitter-state")).toByteArray());
164 
165 	//restore horizontal splitter state
166 	//QSettings settings(COMPANY, PROGRAM);
167 	if(settings.contains(this->metaObject()->className()+QString("/horizontal-splitter-state")))
168 		horizontalSplitter->restoreState(settings.value(this->metaObject()->className()+QString("/horizontal-splitter-state")).toByteArray());
169 
170 	if(settings.contains(this->metaObject()->className()+QString("/lock-radio-button")))
171 		lockRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/lock-radio-button")).toBool());
172 	if(settings.contains(this->metaObject()->className()+QString("/unlock-radio-button")))
173 		unlockRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/unlock-radio-button")).toBool());
174 	if(settings.contains(this->metaObject()->className()+QString("/toggle-radio-button")))
175 		toggleRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/toggle-radio-button")).toBool());
176 
177 ///////////just for testing
178 	QSet<int> backupLockedTime;
179 	QSet<int> backupPermanentlyLockedTime;
180 	QSet<int> backupLockedSpace;
181 	QSet<int> backupPermanentlyLockedSpace;
182 
183 	backupLockedTime=idsOfLockedTime;
184 	backupPermanentlyLockedTime=idsOfPermanentlyLockedTime;
185 	backupLockedSpace=idsOfLockedSpace;
186 	backupPermanentlyLockedSpace=idsOfPermanentlyLockedSpace;
187 
188 	//added by Volker Dirr
189 	//these 2 lines are not really needed - just to be safer
190 	LockUnlock::computeLockedUnlockedActivitiesTimeSpace();
191 
192 	assert(backupLockedTime==idsOfLockedTime);
193 	assert(backupPermanentlyLockedTime==idsOfPermanentlyLockedTime);
194 	assert(backupLockedSpace==idsOfLockedSpace);
195 	assert(backupPermanentlyLockedSpace==idsOfPermanentlyLockedSpace);
196 ///////////
197 
198 	if(gt.rules.nInternalRooms!=gt.rules.roomsList.count()){
199 		assert(0); //should be taken care of by Rules - rooms_schedule_ready is false in the Rules if adding or removing rooms.
200 
201 		initialRecommendedHeight=10;
202 
203 		oldItemDelegate=roomsTimetableTable->itemDelegate();
204 		newItemDelegate=new TimetableViewRoomsTimeHorizontalDelegate(nullptr, 1, 1);
205 		roomsTimetableTable->setItemDelegate(newItemDelegate);
206 
207 		QMessageBox::warning(this, tr("FET warning"), tr("Cannot display the timetable, because you added or removed some rooms. Please regenerate the timetable and then view it"));
208 		return;
209 	}
210 
211 	//Commented on 2018-07-20
212 	//LockUnlock::increaseCommunicationSpinBox();
213 
214 	roomsTimetableTable->setRowCount(gt.rules.nInternalRooms);
215 	roomsTimetableTable->setColumnCount(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
216 
217 	oldItemDelegate=roomsTimetableTable->itemDelegate();
218 	newItemDelegate=new TimetableViewRoomsTimeHorizontalDelegate(nullptr, roomsTimetableTable->rowCount(), gt.rules.nHoursPerDay);
219 	roomsTimetableTable->setItemDelegate(newItemDelegate);
220 
221 	bool min2letters=false;
222 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
223 		if(gt.rules.daysOfTheWeek[d].size()>gt.rules.nHoursPerDay){
224 			min2letters=true;
225 			break;
226 		}
227 	}
228 	//QMessageBox::information(this, "", QString::number(min2letters));
229 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
230 		QString dayName=gt.rules.daysOfTheWeek[d];
231 		int t=dayName.size();
232 		int q=t/gt.rules.nHoursPerDay;
233 		int r=t%gt.rules.nHoursPerDay;
234 		QStringList list;
235 
236 		if(q==0)
237 			q=1;
238 
239 		for(int i=0; i<gt.rules.nHoursPerDay; i++){
240 			if(!min2letters){
241 				list.append(dayName.left(1));
242 				dayName.remove(0, 1);
243 			}
244 			else if(i<r || q<=1){
245 				assert(q>=1);
246 				list.append(dayName.left(q+1));
247 				dayName.remove(0, q+1);
248 			}
249 			else{
250 				list.append(dayName.left(q));
251 				dayName.remove(0, q);
252 			}
253 		}
254 
255 		for(int h=0; h<gt.rules.nHoursPerDay; h++){
256 			QTableWidgetItem* item=new QTableWidgetItem(list.at(h)+"\n"+gt.rules.hoursOfTheDay[h]);
257 			item->setToolTip(gt.rules.daysOfTheWeek[d]+"\n"+gt.rules.hoursOfTheDay[h]);
258 			roomsTimetableTable->setHorizontalHeaderItem(d*gt.rules.nHoursPerDay+h, item);
259 		}
260 	}
261 	for(int t=0; t<gt.rules.nInternalRooms; t++){
262 		QTableWidgetItem* item=new QTableWidgetItem(gt.rules.internalRoomsList[t]->name);
263 		item->setToolTip(gt.rules.internalRoomsList[t]->name);
264 		roomsTimetableTable->setVerticalHeaderItem(t, item);
265 	}
266 
267 	for(int t=0; t<gt.rules.nInternalRooms; t++){
268 		for(int d=0; d<gt.rules.nDaysPerWeek; d++){
269 			for(int h=0; h<gt.rules.nHoursPerDay; h++){
270 				QTableWidgetItem* item= new QTableWidgetItem();
271 				item->setTextAlignment(Qt::AlignCenter);
272 				item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
273 
274 				roomsTimetableTable->setItem(t, d*gt.rules.nHoursPerDay+h, item);
275 			}
276 		}
277 	}
278 
279 	//resize columns
280 	//if(!columnResizeModeInitialized){
281 //	teachersTimetableTable->horizontalHeader()->setMinimumSectionSize(teachersTimetableTable->horizontalHeader()->defaultSectionSize());
282 	//	columnResizeModeInitialized=true;
283 
284 	initialRecommendedHeight=roomsTimetableTable->verticalHeader()->sectionSizeHint(0);
285 
286 	int h;
287 	int w;
288 
289 	if(settings.contains(this->metaObject()->className()+QString("/vertical-header-size"))){
290 		h=settings.value(this->metaObject()->className()+QString("/vertical-header-size")).toInt();
291 		if(h==0)
292 			h=MINIMUM_HEIGHT_SPIN_BOX_VALUE;
293 	}
294 	else{
295 		h=MINIMUM_HEIGHT_SPIN_BOX_VALUE;
296 	}
297 //	if(h==0)
298 //		h=initialRecommendedHeight;
299 
300 	if(settings.contains(this->metaObject()->className()+QString("/horizontal-header-size"))){
301 		w=settings.value(this->metaObject()->className()+QString("/horizontal-header-size")).toInt();
302 		if(w==0)
303 			w=MINIMUM_WIDTH_SPIN_BOX_VALUE;
304 	}
305 	else{
306 		w=MINIMUM_WIDTH_SPIN_BOX_VALUE;
307 	}
308 //	if(w==0)
309 //		w=2*initialRecommendedHeight;
310 
311 	widthSpinBox->setSuffix(QString(" ")+tr("px", "Abbreviation for pixels"));
312 	widthSpinBox->setMinimum(MINIMUM_WIDTH_SPIN_BOX_VALUE);
313 #if QT_VERSION >= QT_VERSION_CHECK(5,2,0)
314 	widthSpinBox->setMaximum(roomsTimetableTable->verticalHeader()->maximumSectionSize());
315 #else
316 	widthSpinBox->setMaximum(maxScreenWidth(this));
317 #endif
318 	widthSpinBox->setValue(w);
319 	widthSpinBox->setSpecialValueText(tr("Automatic"));
320 
321 	heightSpinBox->setSuffix(QString(" ")+tr("px", "Abbreviation for pixels"));
322 	heightSpinBox->setMinimum(MINIMUM_HEIGHT_SPIN_BOX_VALUE);
323 #if QT_VERSION >= QT_VERSION_CHECK(5,2,0)
324 	heightSpinBox->setMaximum(roomsTimetableTable->verticalHeader()->maximumSectionSize());
325 #else
326 	heightSpinBox->setMaximum(maxScreenWidth(this));
327 #endif
328 	heightSpinBox->setValue(h);
329 	heightSpinBox->setSpecialValueText(tr("Automatic"));
330 
331 	widthSpinBoxValueChanged();
332 	heightSpinBoxValueChanged();
333 
334 	connect(widthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(widthSpinBoxValueChanged()));
335 	connect(heightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(heightSpinBoxValueChanged()));
336 
337 //	teachersTimetableTable->verticalHeader()->setDefaultSectionSize(h);
338 //	teachersTimetableTable->horizontalHeader()->setDefaultSectionSize(w);
339 
340 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
341 	roomsTimetableTable->verticalHeader()->setSectionResizeMode(QHeaderView::Interactive);
342 	roomsTimetableTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
343 #else
344 	roomsTimetableTable->verticalHeader()->setResizeMode(QHeaderView::Interactive);
345 	roomsTimetableTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
346 #endif
347 	//}
348 	////////////////
349 
350 	teachersCheckBox->setChecked(true);
351 	subjectsCheckBox->setChecked(true);
352 	studentsCheckBox->setChecked(true);
353 
354 	if(settings.contains(this->metaObject()->className()+QString("/teachers-check-box-state"))){
355 		bool state=settings.value(this->metaObject()->className()+QString("/teachers-check-box-state")).toBool();
356 		teachersCheckBox->setChecked(state);
357 	}
358 	if(settings.contains(this->metaObject()->className()+QString("/subjects-check-box-state"))){
359 		bool state=settings.value(this->metaObject()->className()+QString("/subjects-check-box-state")).toBool();
360 		subjectsCheckBox->setChecked(state);
361 	}
362 	if(settings.contains(this->metaObject()->className()+QString("/students-check-box-state"))){
363 		bool state=settings.value(this->metaObject()->className()+QString("/students-check-box-state")).toBool();
364 		studentsCheckBox->setChecked(state);
365 	}
366 
367 	connect(teachersCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateRoomsTimetableTable()));
368 	connect(subjectsCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateRoomsTimetableTable()));
369 	connect(studentsCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateRoomsTimetableTable()));
370 
371 	//added by Volker Dirr
372 	connect(&communicationSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateRoomsTimetableTable()));
373 
374 	updateRoomsTimetableTable();
375 }
376 
newTimetableGenerated()377 void TimetableViewRoomsTimeHorizontalForm::newTimetableGenerated()
378 {
379 /*	setupUi(this);
380 
381 	closePushButton->setDefault(true);
382 
383 	detailsTextEdit->setReadOnly(true);
384 
385 	//columnResizeModeInitialized=false;
386 
387 	verticalSplitter->setStretchFactor(0, 20);
388 	verticalSplitter->setStretchFactor(1, 1);
389 	horizontalSplitter->setStretchFactor(0, 5);
390 	horizontalSplitter->setStretchFactor(1, 1);
391 
392 	roomsTimetableTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
393 
394 	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
395 	connect(roomsTimetableTable, SIGNAL(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)), this, SLOT(currentItemChanged(QTableWidgetItem*, QTableWidgetItem*)));
396 	connect(lockTimePushButton, SIGNAL(clicked()), this, SLOT(lockTime()));
397 	connect(lockSpacePushButton, SIGNAL(clicked()), this, SLOT(lockSpace()));
398 	connect(lockTimeSpacePushButton, SIGNAL(clicked()), this, SLOT(lockTimeSpace()));
399 
400 	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
401 
402 	lockRadioButton->setChecked(true);
403 	unlockRadioButton->setChecked(false);
404 	toggleRadioButton->setChecked(false);
405 
406 	centerWidgetOnScreen(this);
407 	restoreFETDialogGeometry(this);
408 
409 	//restore vertical splitter state
410 	QSettings settings(COMPANY, PROGRAM);
411 	if(settings.contains(this->metaObject()->className()+QString("/vertical-splitter-state")))
412 		verticalSplitter->restoreState(settings.value(this->metaObject()->className()+QString("/vertical-splitter-state")).toByteArray());
413 
414 	//restore horizontal splitter state
415 	//QSettings settings(COMPANY, PROGRAM);
416 	if(settings.contains(this->metaObject()->className()+QString("/horizontal-splitter-state")))
417 		horizontalSplitter->restoreState(settings.value(this->metaObject()->className()+QString("/horizontal-splitter-state")).toByteArray());
418 
419 	if(settings.contains(this->metaObject()->className()+QString("/lock-radio-button")))
420 		lockRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/lock-radio-button")).toBool());
421 	if(settings.contains(this->metaObject()->className()+QString("/unlock-radio-button")))
422 		unlockRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/unlock-radio-button")).toBool());
423 	if(settings.contains(this->metaObject()->className()+QString("/toggle-radio-button")))
424 		toggleRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/toggle-radio-button")).toBool());
425 */
426 
427 ///////////just for testing
428 	QSet<int> backupLockedTime;
429 	QSet<int> backupPermanentlyLockedTime;
430 	QSet<int> backupLockedSpace;
431 	QSet<int> backupPermanentlyLockedSpace;
432 
433 	backupLockedTime=idsOfLockedTime;
434 	backupPermanentlyLockedTime=idsOfPermanentlyLockedTime;
435 	backupLockedSpace=idsOfLockedSpace;
436 	backupPermanentlyLockedSpace=idsOfPermanentlyLockedSpace;
437 
438 	//added by Volker Dirr
439 	//these 2 lines are not really needed - just to be safer
440 	LockUnlock::computeLockedUnlockedActivitiesTimeSpace();
441 
442 	assert(backupLockedTime==idsOfLockedTime);
443 	assert(backupPermanentlyLockedTime==idsOfPermanentlyLockedTime);
444 	assert(backupLockedSpace==idsOfLockedSpace);
445 	assert(backupPermanentlyLockedSpace==idsOfPermanentlyLockedSpace);
446 ///////////
447 
448 	if(gt.rules.nInternalRooms!=gt.rules.roomsList.count()){
449 		assert(0); //should be taken care of by Rules - rooms_schedule_ready is false in the Rules if adding or removing rooms.
450 	}
451 
452 	//DON'T UNCOMMENT THIS CODE -> LEADS TO CRASH IF THERE ARE MORE VIEWS OPENED.
453 	//LockUnlock::increaseCommunicationSpinBox();
454 
455 	roomsTimetableTable->clear();
456 	roomsTimetableTable->setRowCount(gt.rules.nInternalRooms);
457 	roomsTimetableTable->setColumnCount(gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay);
458 
459 	newItemDelegate->nRows=roomsTimetableTable->rowCount();
460 	newItemDelegate->nColumns=gt.rules.nHoursPerDay;
461 	/*roomsTimetableTable->setItemDelegate(oldItemDelegate);
462 	delete newItemDelegate;
463 	//oldItemDelegate=roomsTimetableTable->itemDelegate();
464 	newItemDelegate=new TimetableViewRoomsTimeHorizontalDelegate(nullptr, roomsTimetableTable->rowCount(), gt.rules.nHoursPerDay);
465 	roomsTimetableTable->setItemDelegate(newItemDelegate);*/
466 
467 	bool min2letters=false;
468 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
469 		if(gt.rules.daysOfTheWeek[d].size()>gt.rules.nHoursPerDay){
470 			min2letters=true;
471 			break;
472 		}
473 	}
474 	//QMessageBox::information(this, "", QString::number(min2letters));
475 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
476 		QString dayName=gt.rules.daysOfTheWeek[d];
477 		int t=dayName.size();
478 		int q=t/gt.rules.nHoursPerDay;
479 		int r=t%gt.rules.nHoursPerDay;
480 		QStringList list;
481 
482 		if(q==0)
483 			q=1;
484 
485 		for(int i=0; i<gt.rules.nHoursPerDay; i++){
486 			if(!min2letters){
487 				list.append(dayName.left(1));
488 				dayName.remove(0, 1);
489 			}
490 			else if(i<r || q<=1){
491 				assert(q>=1);
492 				list.append(dayName.left(q+1));
493 				dayName.remove(0, q+1);
494 			}
495 			else{
496 				list.append(dayName.left(q));
497 				dayName.remove(0, q);
498 			}
499 		}
500 
501 		for(int h=0; h<gt.rules.nHoursPerDay; h++){
502 			QTableWidgetItem* item=new QTableWidgetItem(list.at(h)+"\n"+gt.rules.hoursOfTheDay[h]);
503 			item->setToolTip(gt.rules.daysOfTheWeek[d]+"\n"+gt.rules.hoursOfTheDay[h]);
504 			roomsTimetableTable->setHorizontalHeaderItem(d*gt.rules.nHoursPerDay+h, item);
505 		}
506 	}
507 	for(int t=0; t<gt.rules.nInternalRooms; t++){
508 		QTableWidgetItem* item=new QTableWidgetItem(gt.rules.internalRoomsList[t]->name);
509 		item->setToolTip(gt.rules.internalRoomsList[t]->name);
510 		roomsTimetableTable->setVerticalHeaderItem(t, item);
511 	}
512 
513 	for(int t=0; t<gt.rules.nInternalRooms; t++){
514 		for(int d=0; d<gt.rules.nDaysPerWeek; d++){
515 			for(int h=0; h<gt.rules.nHoursPerDay; h++){
516 				QTableWidgetItem* item= new QTableWidgetItem();
517 				item->setTextAlignment(Qt::AlignCenter);
518 				item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
519 
520 				roomsTimetableTable->setItem(t, d*gt.rules.nHoursPerDay+h, item);
521 			}
522 		}
523 	}
524 
525 	//resize columns
526 	//if(!columnResizeModeInitialized){
527 //	teachersTimetableTable->horizontalHeader()->setMinimumSectionSize(teachersTimetableTable->horizontalHeader()->defaultSectionSize());
528 	//	columnResizeModeInitialized=true;
529 
530 	/*initialRecommendedHeight=roomsTimetableTable->verticalHeader()->sectionSizeHint(0);
531 
532 	int h;
533 	int w;
534 
535 	if(settings.contains(this->metaObject()->className()+QString("/vertical-header-size"))){
536 		h=settings.value(this->metaObject()->className()+QString("/vertical-header-size")).toInt();
537 		if(h==0)
538 			h=MINIMUM_HEIGHT_SPIN_BOX_VALUE;
539 	}
540 	else{
541 		h=MINIMUM_HEIGHT_SPIN_BOX_VALUE;
542 	}
543 //	if(h==0)
544 //		h=initialRecommendedHeight;
545 
546 	if(settings.contains(this->metaObject()->className()+QString("/horizontal-header-size"))){
547 		w=settings.value(this->metaObject()->className()+QString("/horizontal-header-size")).toInt();
548 		if(w==0)
549 			w=MINIMUM_WIDTH_SPIN_BOX_VALUE;
550 	}
551 	else{
552 		w=MINIMUM_WIDTH_SPIN_BOX_VALUE;
553 	}
554 //	if(w==0)
555 //		w=2*initialRecommendedHeight;
556 
557 	widthSpinBox->setSuffix(QString(" ")+tr("px", "Abbreviation for pixels"));
558 	widthSpinBox->setMinimum(MINIMUM_WIDTH_SPIN_BOX_VALUE);
559 #if QT_VERSION >= QT_VERSION_CHECK(5,2,0)
560 	widthSpinBox->setMaximum(roomsTimetableTable->verticalHeader()->maximumSectionSize());
561 #else
562 	widthSpinBox->setMaximum(maxScreenWidth(this));
563 #endif
564 	widthSpinBox->setValue(w);
565 	widthSpinBox->setSpecialValueText(tr("Automatic"));
566 
567 	heightSpinBox->setSuffix(QString(" ")+tr("px", "Abbreviation for pixels"));
568 	heightSpinBox->setMinimum(MINIMUM_HEIGHT_SPIN_BOX_VALUE);
569 #if QT_VERSION >= QT_VERSION_CHECK(5,2,0)
570 	heightSpinBox->setMaximum(roomsTimetableTable->verticalHeader()->maximumSectionSize());
571 #else
572 	heightSpinBox->setMaximum(maxScreenWidth(this));
573 #endif
574 	heightSpinBox->setValue(h);
575 	heightSpinBox->setSpecialValueText(tr("Automatic"));
576 
577 	widthSpinBoxValueChanged();
578 	heightSpinBoxValueChanged();
579 
580 	connect(widthSpinBox, SIGNAL(valueChanged(int)), this, SLOT(widthSpinBoxValueChanged()));
581 	connect(heightSpinBox, SIGNAL(valueChanged(int)), this, SLOT(heightSpinBoxValueChanged()));
582 
583 //	teachersTimetableTable->verticalHeader()->setDefaultSectionSize(h);
584 //	teachersTimetableTable->horizontalHeader()->setDefaultSectionSize(w);
585 
586 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
587 	roomsTimetableTable->verticalHeader()->setSectionResizeMode(QHeaderView::Interactive);
588 	roomsTimetableTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
589 #else
590 	roomsTimetableTable->verticalHeader()->setResizeMode(QHeaderView::Interactive);
591 	roomsTimetableTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
592 #endif
593 	//}
594 	////////////////
595 
596 	teachersCheckBox->setChecked(true);
597 	subjectsCheckBox->setChecked(true);
598 	studentsCheckBox->setChecked(true);
599 
600 	if(settings.contains(this->metaObject()->className()+QString("/teachers-check-box-state"))){
601 		bool state=settings.value(this->metaObject()->className()+QString("/teachers-check-box-state")).toBool();
602 		teachersCheckBox->setChecked(state);
603 	}
604 	if(settings.contains(this->metaObject()->className()+QString("/subjects-check-box-state"))){
605 		bool state=settings.value(this->metaObject()->className()+QString("/subjects-check-box-state")).toBool();
606 		subjectsCheckBox->setChecked(state);
607 	}
608 	if(settings.contains(this->metaObject()->className()+QString("/students-check-box-state"))){
609 		bool state=settings.value(this->metaObject()->className()+QString("/students-check-box-state")).toBool();
610 		studentsCheckBox->setChecked(state);
611 	}
612 
613 	connect(teachersCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateRoomsTimetableTable()));
614 	connect(subjectsCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateRoomsTimetableTable()));
615 	connect(studentsCheckBox, SIGNAL(toggled(bool)), this, SLOT(updateRoomsTimetableTable()));*/
616 
617 	//added by Volker Dirr
618 	//connect(&communicationSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateRoomsTimetableTable()));
619 
620 	updateRoomsTimetableTable();
621 }
622 
~TimetableViewRoomsTimeHorizontalForm()623 TimetableViewRoomsTimeHorizontalForm::~TimetableViewRoomsTimeHorizontalForm()
624 {
625 	saveFETDialogGeometry(this);
626 
627 	//save vertical splitter state
628 	QSettings settings(COMPANY, PROGRAM);
629 	settings.setValue(this->metaObject()->className()+QString("/vertical-splitter-state"), verticalSplitter->saveState());
630 
631 	//save horizontal splitter state
632 	//QSettings settings(COMPANY, PROGRAM);
633 	settings.setValue(this->metaObject()->className()+QString("/horizontal-splitter-state"), horizontalSplitter->saveState());
634 
635 	settings.setValue(this->metaObject()->className()+QString("/lock-radio-button"), lockRadioButton->isChecked());
636 	settings.setValue(this->metaObject()->className()+QString("/unlock-radio-button"), unlockRadioButton->isChecked());
637 	settings.setValue(this->metaObject()->className()+QString("/toggle-radio-button"), toggleRadioButton->isChecked());
638 
639 	if(heightSpinBox->value()<=MINIMUM_HEIGHT_SPIN_BOX_VALUE)
640 		settings.setValue(this->metaObject()->className()+QString("/vertical-header-size"), 0);
641 	else
642 		settings.setValue(this->metaObject()->className()+QString("/vertical-header-size"), heightSpinBox->value());
643 
644 	if(widthSpinBox->value()<=MINIMUM_WIDTH_SPIN_BOX_VALUE)
645 		settings.setValue(this->metaObject()->className()+QString("/horizontal-header-size"), 0);
646 	else
647 		settings.setValue(this->metaObject()->className()+QString("/horizontal-header-size"), widthSpinBox->value());
648 
649 	settings.setValue(this->metaObject()->className()+QString("/teachers-check-box-state"), teachersCheckBox->isChecked());
650 	settings.setValue(this->metaObject()->className()+QString("/subjects-check-box-state"), subjectsCheckBox->isChecked());
651 	settings.setValue(this->metaObject()->className()+QString("/students-check-box-state"), studentsCheckBox->isChecked());
652 
653 	roomsTimetableTable->setItemDelegate(oldItemDelegate);
654 	delete newItemDelegate;
655 }
656 
resizeRowsAfterShow()657 void TimetableViewRoomsTimeHorizontalForm::resizeRowsAfterShow()
658 {
659 //	roomsTimetableTable->resizeRowsToContents();
660 }
661 
updateRoomsTimetableTable()662 void TimetableViewRoomsTimeHorizontalForm::updateRoomsTimetableTable(){
663 	if(!(students_schedule_ready && teachers_schedule_ready)){
664 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable "
665 		"or close the timetable view rooms dialog"));
666 		return;
667 	}
668 	assert(students_schedule_ready && teachers_schedule_ready);
669 
670 	if(!rooms_schedule_ready){
671 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable "
672 		"or close the timetable view rooms dialog"));
673 		return;
674 	}
675 	assert(rooms_schedule_ready);
676 
677 	if(gt.rules.nInternalRooms!=gt.rules.roomsList.count()){
678 		QMessageBox::warning(this, tr("FET warning"), tr("Cannot display the timetable, because you added or removed some rooms. Please regenerate the timetable and then view it"));
679 		return;
680 	}
681 
682 	assert(gt.rules.initialized);
683 
684 	for(int t=0; t<gt.rules.nInternalRooms; t++){
685 		assert(t<roomsTimetableTable->rowCount());
686 		for(int d=0; d<gt.rules.nDaysPerWeek; d++){
687 			for(int h=0; h<gt.rules.nHoursPerDay; h++){
688 				assert(d*gt.rules.nHoursPerDay+h<roomsTimetableTable->columnCount());
689 
690 				//begin by Marco Vassura
691 				// add colors (start)
692 				//if(USE_GUI_COLORS) {
693 				roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setBackground(roomsTimetableTable->palette().color(QPalette::Base));
694 				roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setForeground(roomsTimetableTable->palette().color(QPalette::Text));
695 				//}
696 				// add colors (end)
697 				//end by Marco Vassura
698 
699 				QString s = "";
700 				QString shortString="";
701 				int ai=rooms_timetable_weekly[t][d][h]; //activity index
702 				//Activity* act=gt.rules.activitiesList.at(ai);
703 				if(ai!=UNALLOCATED_ACTIVITY){
704 					Activity* act=&gt.rules.internalActivitiesList[ai];
705 					assert(act!=nullptr);
706 
707 					if(TIMETABLE_HTML_PRINT_ACTIVITY_TAGS){
708 						QString ats=act->activityTagsNames.join(", ");
709 						s += act->subjectName+" "+ats;
710 					}
711 					else{
712 						s += act->subjectName;
713 					}
714 
715 					//teachers
716 					if(act->teachersNames.count()>0){
717 						//s+=" ";
718 						s+="\n";
719 						s+=act->teachersNames.join(", ");
720 
721 						if(teachersCheckBox->isChecked()){
722 							shortString+=act->teachersNames.join(", ");
723 						}
724 					}
725 
726 					//students
727 					if(act->studentsNames.count()>0){
728 						//s+=" ";
729 						s+="\n";
730 						s+=act->studentsNames.join(", ");
731 
732 						if(studentsCheckBox->isChecked()){
733 							if(!shortString.isEmpty()){
734 								shortString+=" ";
735 								//shortString+="\n";
736 							}
737 							shortString+=act->studentsNames.join(", ");
738 						}
739 					}
740 
741 					if(subjectsCheckBox->isChecked()){
742 						if(!shortString.isEmpty()){
743 							shortString+=" ";
744 							//shortString+="\n";
745 						}
746 						shortString+=act->subjectName;
747 					}
748 
749 					assert(best_solution.rooms[ai]!=UNALLOCATED_SPACE && best_solution.rooms[ai]!=UNSPECIFIED_ROOM);
750 					/*int r=best_solution.rooms[ai];
751 					if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
752 						//s+=" ";
753 						//s+=tr("R:%1", "Room").arg(gt.rules.internalRoomsList[r]->name);
754 						s+=" ";
755 						s+="\n";
756 						s+=gt.rules.internalRoomsList[r]->name;
757 					}*/
758 
759 					//added by Volker Dirr (start)
760 					QString descr="";
761 					QString tt="";
762 					if(idsOfPermanentlyLockedTime.contains(act->id)){
763 						descr+=QCoreApplication::translate("TimetableViewForm", "PLT", "Abbreviation for permanently locked time. There are 4 strings: permanently locked time, permanently locked space, "
764 							"locked time, locked space. Make sure their abbreviations contain different letters and are visually different, so user can easily differentiate between them."
765 							" These abbreviations may appear also in other places, please use the same abbreviations.");
766 						tt=", ";
767 					}
768 					else if(idsOfLockedTime.contains(act->id)){
769 						descr+=QCoreApplication::translate("TimetableViewForm", "LT", "Abbreviation for locked time. There are 4 strings: permanently locked time, permanently locked space, "
770 						"locked time, locked space. Make sure their abbreviations contain different letters and are visually different, so user can easily differentiate between them."
771 							" These abbreviations may appear also in other places, please use the same abbreviations.");
772 						tt=", ";
773 					}
774 					if(idsOfPermanentlyLockedSpace.contains(act->id)){
775 						descr+=tt+QCoreApplication::translate("TimetableViewForm", "PLS", "Abbreviation for permanently locked space. There are 4 strings: permanently locked time, permanently locked space, "
776 							"locked time, locked space. Make sure their abbreviations contain different letters and are visually different, so user can easily differentiate between them."
777 							" These abbreviations may appear also in other places, please use the same abbreviations.");
778 					}
779 					else if(idsOfLockedSpace.contains(act->id)){
780 						descr+=tt+QCoreApplication::translate("TimetableViewForm", "LS", "Abbreviation for locked space. There are 4 strings: permanently locked time, permanently locked space, "
781 							"locked time, locked space. Make sure their abbreviations contain different letters and are visually different, so user can easily differentiate between them."
782 							" These abbreviations may appear also in other places, please use the same abbreviations.");
783 					}
784 					if(descr!=""){
785 						descr.prepend("\n(");
786 						//descr.prepend(" ");
787 						descr.append(")");
788 					}
789 
790 					if(idsOfPermanentlyLockedTime.contains(act->id) || idsOfLockedTime.contains(act->id)){
791 						QFont font(roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->font());
792 						font.setBold(true);
793 						roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setFont(font);
794 					}
795 					else{
796 						QFont font(roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->font());
797 						font.setBold(false);
798 						roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setFont(font);
799 					}
800 
801 					if(idsOfPermanentlyLockedSpace.contains(act->id) || idsOfLockedSpace.contains(act->id)){
802 						QFont font(roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->font());
803 						font.setItalic(true);
804 						roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setFont(font);
805 					}
806 					else{
807 						QFont font(roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->font());
808 						font.setItalic(false);
809 						roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setFont(font);
810 					}
811 
812 					s+=descr;
813 					//added by Volker Dirr (end)
814 
815 					//begin by Marco Vassura
816 					// add colors (start)
817 					if(USE_GUI_COLORS /*&& act->studentsNames.count()>0*/){
818 						QBrush bg(stringToColor(act->subjectName+" "+act->studentsNames.join(", ")));
819 						roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setBackground(bg);
820 						double brightness = bg.color().redF()*0.299 + bg.color().greenF()*0.587 + bg.color().blueF()*0.114;
821 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
822 						if (brightness<0.5)
823 							roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setForeground(QBrush(QColorConstants::White));
824 						else
825 							roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setForeground(QBrush(QColorConstants::Black));
826 #else
827 						if (brightness<0.5)
828 							roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setForeground(QBrush(Qt::white));
829 						else
830 							roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setForeground(QBrush(Qt::black));
831 #endif
832 					}
833 					// add colors (end)
834 					//end by Marco Vassura
835 					roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setText(shortString);
836 				}
837 				else{
838 					if(notAllowedRoomTimePercentages[t][d+h*gt.rules.nDaysPerWeek]>=0 && PRINT_NOT_AVAILABLE_TIME_SLOTS)
839 						s+="-x-";
840 					else if(breakDayHour[d][h] && PRINT_BREAK_TIME_SLOTS)
841 						s+="-X-";
842 					roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setText(s);
843 				}
844 				roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->setToolTip(s);
845 			}
846 		}
847 	}
848 	//	for(int i=0; i<gt.rules.nHoursPerDay; i++) //added in version 3_9_16, on 16 Oct. 2004
849 	//		teachersTimetableTable->adjustRow(i);
850 
851 //	teachersTimetableTable->resizeRowsToContents();
852 
853 	detailActivity(roomsTimetableTable->currentItem());
854 }
855 
856 /*void TimetableViewTeachersTimeHorizontalForm::resizeEvent(QResizeEvent* event)
857 {
858 	QDialog::resizeEvent(event);
859 
860 //	teachersTimetableTable->resizeRowsToContents();
861 }*/
862 
863 //begin by Marco Vassura
864 //slightly modified by Liviu Lalescu on 2021-03-01
stringToColor(const QString & s)865 QColor TimetableViewRoomsTimeHorizontalForm::stringToColor(const QString& s)
866 {
867 	// CRC-24 based on RFC 2440 Section 6.1
868 	unsigned long int crc = 0xB704CEUL;
869 	QByteArray ba=s.toUtf8();
870 	for(char c : qAsConst(ba)){
871 		unsigned char uc=(unsigned char)(c);
872 		crc ^= (uc & 0xFF) << 16;
873 		for (int i = 0; i < 8; i++) {
874 			crc <<= 1;
875 			if (crc & 0x1000000UL)
876 				crc ^= 0x1864CFBUL;
877 		}
878 	}
879 	return QColor::fromRgb(int((crc>>16) & 0xFF), int((crc>>8) & 0xFF), int(crc & 0xFF));
880 }
881 //end by Marco Vassura
882 
currentItemChanged(QTableWidgetItem * current,QTableWidgetItem * previous)883 void TimetableViewRoomsTimeHorizontalForm::currentItemChanged(QTableWidgetItem* current, QTableWidgetItem* previous)
884 {
885 	Q_UNUSED(previous);
886 
887 	detailActivity(current);
888 }
889 
detailActivity(QTableWidgetItem * item)890 void TimetableViewRoomsTimeHorizontalForm::detailActivity(QTableWidgetItem* item){
891 	if(item==nullptr){
892 		detailsTextEdit->setPlainText(QString(""));
893 		return;
894 	}
895 
896 	if(!(students_schedule_ready && teachers_schedule_ready)){
897 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable"));
898 		return;
899 	}
900 	assert(students_schedule_ready && teachers_schedule_ready);
901 
902 	if(!rooms_schedule_ready){
903 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable"));
904 		return;
905 	}
906 	assert(rooms_schedule_ready);
907 
908 	if(item->row()>=gt.rules.nInternalRooms || item->column()>=gt.rules.nDaysPerWeek*gt.rules.nHoursPerDay){
909 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable "
910 		"or close the timetable view rooms dialog"));
911 		return;
912 	}
913 
914 	if(gt.rules.nInternalRooms!=gt.rules.roomsList.count()){
915 		QMessageBox::warning(this, tr("FET warning"), tr("Cannot display the timetable, because you added or removed some rooms. Please regenerate the timetable and then view it"));
916 		return;
917 	}
918 
919 	assert(item->row()>=0);
920 	assert(item->column()>=0);
921 
922 	int t=item->row();
923 
924 	if(gt.rules.nInternalTeachers!=gt.rules.teachersList.count()){
925 		QMessageBox::warning(this, tr("FET warning"), tr("Cannot display the timetable, because you added or removed some teachers. Please regenerate the timetable and then view it"));
926 		return;
927 	}
928 
929 	int d=item->column()/gt.rules.nHoursPerDay;
930 	int h=item->column()%gt.rules.nHoursPerDay;
931 
932 	int roomIndex=gt.rules.searchRoom(gt.rules.internalRoomsList[t]->name);
933 	if(roomIndex<0){
934 		QMessageBox::warning(this, tr("FET warning"), tr("The room is invalid - please close this dialog and open a new view rooms timetable"));
935 		return;
936 	}
937 	else{
938 		QString s = "";
939 		if(d>=0 && d<gt.rules.nDaysPerWeek && h>=0 && h<gt.rules.nHoursPerDay){
940 			int ai=rooms_timetable_weekly[t][d][h]; //activity index
941 			//Activity* act=gt.rules.activitiesList.at(ai);
942 			if(ai!=UNALLOCATED_ACTIVITY){
943 				Activity* act=&gt.rules.internalActivitiesList[ai];
944 				assert(act!=nullptr);
945 				//s += act->getDetailedDescriptionWithConstraints(gt.rules);
946 				s += act->getDetailedDescription(gt.rules);
947 
948 				int r=best_solution.rooms[ai];
949 				if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
950 					s+="\n";
951 					s+=tr("Room: %1").arg(gt.rules.internalRoomsList[r]->name);
952 
953 					if(gt.rules.internalRoomsList[r]->isVirtual==true){
954 						QStringList tsl;
955 						for(int i : qAsConst(best_solution.realRoomsList[ai]))
956 							tsl.append(gt.rules.internalRoomsList[i]->name);
957 						s+=QString(" (")+tsl.join(", ")+QString(")");
958 					}
959 
960 					if(gt.rules.internalRoomsList[r]->building!=""){
961 						s+="\n";
962 						s+=tr("Building=%1").arg(gt.rules.internalRoomsList[r]->building);
963 					}
964 					s+="\n";
965 					s+=tr("Capacity=%1").arg(gt.rules.internalRoomsList[r]->capacity);
966 				}
967 
968 				//int r=rooms_timetable_weekly[teacher][k][j];
969 				/*int r=best_solution.rooms[ai];
970 				if(r!=UNALLOCATED_SPACE && r!=UNSPECIFIED_ROOM){
971 					s+="\n";
972 					s+=tr("Room: %1").arg(gt.rules.internalRoomsList[r]->name);
973 				}*/
974 				//added by Volker Dirr (start)
975 				QString descr="";
976 				QString tt="";
977 				if(idsOfPermanentlyLockedTime.contains(act->id)){
978 					descr+=QCoreApplication::translate("TimetableViewForm", "permanently locked time", "refers to activity");
979 					tt=", ";
980 				}
981 				else if(idsOfLockedTime.contains(act->id)){
982 					descr+=QCoreApplication::translate("TimetableViewForm", "locked time", "refers to activity");
983 					tt=", ";
984 				}
985 				if(idsOfPermanentlyLockedSpace.contains(act->id)){
986 					descr+=tt+QCoreApplication::translate("TimetableViewForm", "permanently locked space", "refers to activity");
987 				}
988 				else if(idsOfLockedSpace.contains(act->id)){
989 					descr+=tt+QCoreApplication::translate("TimetableViewForm", "locked space", "refers to activity");
990 				}
991 				if(descr!=""){
992 					descr.prepend("\n(");
993 					descr.append(")");
994 				}
995 				s+=descr;
996 				//added by Volker Dirr (end)
997 			}
998 			else{
999 				if(notAllowedRoomTimePercentages[roomIndex][d+h*gt.rules.nDaysPerWeek]>=0){
1000 					s+=tr("Room is not available with weight %1%").arg(CustomFETString::number(notAllowedRoomTimePercentages[roomIndex][d+h*gt.rules.nDaysPerWeek]));
1001 					s+="\n";
1002 				}
1003 				if(breakDayHour[d][h]){
1004 					s+=tr("Break with weight 100% in this slot");
1005 					s+="\n";
1006 				}
1007 			}
1008 		}
1009 		detailsTextEdit->setPlainText(s);
1010 	}
1011 }
1012 
lockTime()1013 void TimetableViewRoomsTimeHorizontalForm::lockTime()
1014 {
1015 	this->lock(true, false);
1016 }
1017 
lockSpace()1018 void TimetableViewRoomsTimeHorizontalForm::lockSpace()
1019 {
1020 	this->lock(false, true);
1021 }
1022 
lockTimeSpace()1023 void TimetableViewRoomsTimeHorizontalForm::lockTimeSpace()
1024 {
1025 	this->lock(true, true);
1026 }
1027 
lock(bool lockTime,bool lockSpace)1028 void TimetableViewRoomsTimeHorizontalForm::lock(bool lockTime, bool lockSpace)
1029 {
1030 	if(simulation_running || simulation_running_multi){
1031 		QMessageBox::information(this, tr("FET information"),
1032 			tr("Allocation in course.\nPlease stop simulation before this."));
1033 		return;
1034 	}
1035 
1036 	if(!(students_schedule_ready && teachers_schedule_ready)){
1037 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable"));
1038 		return;
1039 	}
1040 	assert(students_schedule_ready && teachers_schedule_ready);
1041 
1042 	if(!rooms_schedule_ready){
1043 		QMessageBox::warning(this, tr("FET warning"), tr("Timetable not available in view rooms timetable dialog - please generate a new timetable"));
1044 		return;
1045 	}
1046 	assert(rooms_schedule_ready);
1047 
1048 	if(gt.rules.nInternalRooms!=gt.rules.roomsList.count()){
1049 		QMessageBox::warning(this, tr("FET warning"), tr("Cannot display the timetable, because you added or removed some rooms. Please regenerate the timetable and then view it"));
1050 		return;
1051 	}
1052 
1053 	Solution* tc=&best_solution;
1054 
1055 	bool report=false; //the messages are annoying
1056 
1057 	int addedT=0, unlockedT=0;
1058 	int addedS=0, unlockedS=0;
1059 
1060 	/*QSet<int> dummyActivitiesColumn; //Dummy activities (no teacher) column to be considered, because the whole column is selected.
1061 	for(int d=0; d<gt.rules.nDaysPerWeek; d++){
1062 		for(int h=0; h<gt.rules.nHoursPerDay; h++){
1063 			assert(d*gt.rules.nHoursPerDay+h < teachersTimetableTable->columnCount());
1064 			bool wholeColumn=true;
1065 			for(int t=0; t<gt.rules.nInternalTeachers; t++){
1066 				assert(t<teachersTimetableTable->rowCount());
1067 
1068 				if(!teachersTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->isSelected()){
1069 					wholeColumn=false;
1070 					break;
1071 				}
1072 			}
1073 			if(wholeColumn)
1074 				dummyActivitiesColumn.insert(d+h*gt.rules.nDaysPerWeek);
1075 		}
1076 	}
1077 
1078 	QSet<int> dummyActivities;
1079 	for(int ai=0; ai<gt.rules.nInternalActivities; ai++){
1080 		if(gt.rules.internalActivitiesList[ai].iTeachersList.count()==0){
1081 			if(tc->times[ai]!=UNALLOCATED_TIME){
1082 				int da=tc->times[ai]%gt.rules.nDaysPerWeek;
1083 				int ha=tc->times[ai]/gt.rules.nDaysPerWeek;
1084 				for(int ha2=ha; ha2<ha+gt.rules.internalActivitiesList[ai].duration; ha2++)
1085 					if(dummyActivitiesColumn.contains(da+ha2*gt.rules.nDaysPerWeek))
1086 						if(!dummyActivities.contains(ai))
1087 							dummyActivities.insert(ai);
1088 			}
1089 		}
1090 	}*/
1091 
1092 	QSet<int> realActivities;
1093 	for(int t=0; t<gt.rules.nInternalRooms; t++){
1094 		assert(t<roomsTimetableTable->rowCount());
1095 		for(int d=0; d<gt.rules.nDaysPerWeek; d++){
1096 			for(int h=0; h<gt.rules.nHoursPerDay; h++){
1097 				assert(d*gt.rules.nHoursPerDay+h<roomsTimetableTable->columnCount());
1098 				if(roomsTimetableTable->item(t, d*gt.rules.nHoursPerDay+h)->isSelected()){
1099 					int ai=rooms_timetable_weekly[t][d][h];
1100 					if(ai!=UNALLOCATED_ACTIVITY)
1101 						if(!realActivities.contains(ai))
1102 							realActivities.insert(ai);
1103 				}
1104 			}
1105 		}
1106 	}
1107 
1108 	for(int ai=0; ai<gt.rules.nInternalActivities; ai++){
1109 		//assert( ! (realActivities.contains(ai) && dummyActivities.contains(ai)) );
1110 		if(realActivities.contains(ai) /*|| dummyActivities.contains(ai)*/){
1111 			assert(tc->times[ai]!=UNALLOCATED_TIME);
1112 			int day=tc->times[ai]%gt.rules.nDaysPerWeek;
1113 			int hour=tc->times[ai]/gt.rules.nDaysPerWeek;
1114 
1115 			Activity* act=&gt.rules.internalActivitiesList[ai];
1116 
1117 			if(lockTime){
1118 				QString s;
1119 
1120 				QList<TimeConstraint*> tmptc;
1121 				int count=0;
1122 
1123 				for(ConstraintActivityPreferredStartingTime* c : gt.rules.apstHash.value(act->id, QSet<ConstraintActivityPreferredStartingTime*>())){
1124 					assert(c->activityId==act->id);
1125 					if(c->activityId==act->id && c->weightPercentage==100.0 && c->active && c->day>=0 && c->hour>=0){
1126 						count++;
1127 						if(c->permanentlyLocked){
1128 							if(idsOfLockedTime.contains(c->activityId) || !idsOfPermanentlyLockedTime.contains(c->activityId)){
1129 								QMessageBox::warning(this, tr("FET warning"), tr("Small problem detected")
1130 									+"\n\n"+tr("A possible problem might be that you have 2 or more constraints of type activity preferred starting time with weight 100% related to activity id %1, please leave only one of them").arg(act->id)
1131 									+"\n\n"+tr("A possible problem might be synchronization - so maybe try to close the timetable view dialog and open it again")
1132 									+"\n\n"+tr("Please report possible bug")
1133 									);
1134 							}
1135 							else{
1136 								if(unlockRadioButton->isChecked() || toggleRadioButton->isChecked()){
1137 									s+=tr("Constraint %1 will not be removed, because it is permanently locked. If you want to unlock it you must go to the constraints menu.").arg("\n"+c->getDetailedDescription(gt.rules)+"\n");
1138 								}
1139 							}
1140 						}
1141 						else{
1142 							if(!idsOfLockedTime.contains(c->activityId) || idsOfPermanentlyLockedTime.contains(c->activityId)){
1143 								QMessageBox::warning(this, tr("FET warning"), tr("Small problem detected")
1144 									+"\n\n"+tr("A possible problem might be that you have 2 or more constraints of type activity preferred starting time with weight 100% related to activity id %1, please leave only one of them").arg(act->id)
1145 									+"\n\n"+tr("A possible problem might be synchronization - so maybe try to close the timetable view dialog and open it again")
1146 									+"\n\n"+tr("Please report possible bug")
1147 									);
1148 							}
1149 							else{
1150 								if(unlockRadioButton->isChecked() || toggleRadioButton->isChecked()){
1151 									tmptc.append((TimeConstraint*)c);
1152 								}
1153 							}
1154 						}
1155 					}
1156 				}
1157 				if(count==0 && (lockRadioButton->isChecked() || toggleRadioButton->isChecked())){
1158 					ConstraintActivityPreferredStartingTime* ctr=new ConstraintActivityPreferredStartingTime(100.0, act->id, day, hour, false);
1159 					bool t=gt.rules.addTimeConstraint(ctr);
1160 					QString s;
1161 					if(t){
1162 						addedT++;
1163 						idsOfLockedTime.insert(act->id);
1164 						s+=tr("Added the following constraint:")+"\n"+ctr->getDetailedDescription(gt.rules);
1165 					}
1166 					else{
1167 						delete ctr;
1168 
1169 						QMessageBox::warning(this, tr("FET warning"), tr("You may have a problem, because FET expected to add 1 constraint, but this is not possible. "
1170 						 "Please report possible bug"));
1171 					}
1172 				}
1173 				else if(count>=1 && (unlockRadioButton->isChecked() || toggleRadioButton->isChecked())){
1174 					if(count>=2)
1175 						QMessageBox::warning(this, tr("FET warning"), tr("You may have a problem, because FET expected to delete 1 constraint, but will delete %1 constraints").arg(tmptc.size()));
1176 
1177 					for(TimeConstraint* deltc : qAsConst(tmptc)){
1178 						s+=tr("The following constraint will be deleted:")+"\n"+deltc->getDetailedDescription(gt.rules)+"\n";
1179 						gt.rules.removeTimeConstraint(deltc);
1180 						idsOfLockedTime.remove(act->id);
1181 						unlockedT++;
1182 						//delete deltc; - done by rules.removeTimeConstraint(...)
1183 					}
1184 				}
1185 				tmptc.clear();
1186 
1187 				if(report){
1188 					/*int k;
1189 					k=QMessageBox::information(this, tr("FET information"), s,
1190 						tr("Skip information"), tr("See next"), QString(), 1, 0 );
1191 
1192 					if(k==0)
1193 						report=false;*/
1194 					QMessageBox::StandardButton k;
1195 					k=QMessageBox::information(this, tr("FET information"), s, QMessageBox::YesToAll | QMessageBox::Ok);
1196 
1197 					if(k==QMessageBox::YesToAll)
1198 						report=false;
1199 				}
1200 			}
1201 
1202 			int ri=tc->rooms[ai];
1203 			if(ri!=UNALLOCATED_SPACE && ri!=UNSPECIFIED_ROOM && lockSpace){
1204 				QString s;
1205 
1206 				QList<SpaceConstraint*> tmpsc;
1207 				int count=0;
1208 
1209 				for(ConstraintActivityPreferredRoom* c : gt.rules.aprHash.value(act->id, QSet<ConstraintActivityPreferredRoom*>())){
1210 					assert(c->activityId==act->id);
1211 
1212 					if(c->activityId==act->id && c->weightPercentage==100.0 && c->active
1213 					 && (gt.rules.internalRoomsList[ri]->isVirtual==false
1214 					 || (gt.rules.internalRoomsList[ri]->isVirtual==true && !c->preferredRealRoomsNames.isEmpty()))){
1215 						count++;
1216 						if(c->permanentlyLocked){
1217 							if(idsOfLockedSpace.contains(c->activityId) || !idsOfPermanentlyLockedSpace.contains(c->activityId)){
1218 								QMessageBox::warning(this, tr("FET warning"), tr("Small problem detected")
1219 									+"\n\n"+tr("A possible problem might be that you have 2 or more constraints of type activity preferred room with weight 100% related to activity id %1, please leave only one of them").arg(act->id)
1220 									+"\n\n"+tr("A possible problem might be synchronization - so maybe try to close the timetable view dialog and open it again")
1221 									+"\n\n"+tr("Please report possible bug")
1222 									);
1223 							}
1224 							else{
1225 								if(unlockRadioButton->isChecked() || toggleRadioButton->isChecked()){
1226 									s+=tr("Constraint %1 will not be removed, because it is permanently locked. If you want to unlock it you must go to the constraints menu.").arg("\n"+c->getDetailedDescription(gt.rules)+"\n");
1227 								}
1228 							}
1229 						}
1230 						else{
1231 							if(!idsOfLockedSpace.contains(c->activityId) || idsOfPermanentlyLockedSpace.contains(c->activityId)){
1232 								QMessageBox::warning(this, tr("FET warning"), tr("Small problem detected")
1233 									+"\n\n"+tr("A possible problem might be that you have 2 or more constraints of type activity preferred room with weight 100% related to activity id %1, please leave only one of them").arg(act->id)
1234 									+"\n\n"+tr("A possible problem might be synchronization - so maybe try to close the timetable view dialog and open it again")
1235 									+"\n\n"+tr("Please report possible bug")
1236 									);
1237 							}
1238 							else{
1239 								if(unlockRadioButton->isChecked() || toggleRadioButton->isChecked()){
1240 									tmpsc.append((SpaceConstraint*)c);
1241 								}
1242 							}
1243 						}
1244 					}
1245 				}
1246 				if(count==0 && (lockRadioButton->isChecked() || toggleRadioButton->isChecked())){
1247 					QStringList tl;
1248 					if(gt.rules.internalRoomsList[ri]->isVirtual==false)
1249 						assert(tc->realRoomsList[ai].isEmpty());
1250 					else
1251 						for(int rr : qAsConst(tc->realRoomsList[ai]))
1252 							tl.append(gt.rules.internalRoomsList[rr]->name);
1253 
1254 					ConstraintActivityPreferredRoom* ctr=new ConstraintActivityPreferredRoom(100, act->id, (gt.rules.internalRoomsList[ri])->name, tl, false);
1255 					bool t=gt.rules.addSpaceConstraint(ctr);
1256 
1257 					QString s;
1258 
1259 					if(t){
1260 						addedS++;
1261 						idsOfLockedSpace.insert(act->id);
1262 						s+=tr("Added the following constraint:")+"\n"+ctr->getDetailedDescription(gt.rules);
1263 					}
1264 					else{
1265 						delete ctr;
1266 
1267 						QMessageBox::warning(this, tr("FET warning"), tr("You may have a problem, because FET expected to add 1 constraint, but this is not possible. "
1268 						 "Please report possible bug"));
1269 					}
1270 				}
1271 				else if(count>=1 && (unlockRadioButton->isChecked() || toggleRadioButton->isChecked())){
1272 					if(count>=2)
1273 						QMessageBox::warning(this, tr("FET warning"), tr("You may have a problem, because FET expected to delete 1 constraint, but will delete %1 constraints").arg(tmpsc.size()));
1274 
1275 					for(SpaceConstraint* delsc : qAsConst(tmpsc)){
1276 						s+=tr("The following constraint will be deleted:")+"\n"+delsc->getDetailedDescription(gt.rules)+"\n";
1277 						gt.rules.removeSpaceConstraint(delsc);
1278 						idsOfLockedSpace.remove(act->id);
1279 						unlockedS++;
1280 						//delete delsc; done by rules.removeSpaceConstraint(...)
1281 					}
1282 				}
1283 				tmpsc.clear();
1284 
1285 				if(report){
1286 					/*int k;
1287 					k=QMessageBox::information(this, tr("FET information"), s,
1288 						tr("Skip information"), tr("See next"), QString(), 1, 0 );
1289 
1290 					if(k==0)
1291 						report=false;*/
1292 					QMessageBox::StandardButton k;
1293 					k=QMessageBox::information(this, tr("FET information"), s, QMessageBox::YesToAll | QMessageBox::Ok);
1294 
1295 					if(k==QMessageBox::YesToAll)
1296 						report=false;
1297 				}
1298 			}
1299 		}
1300 	}
1301 
1302 	QStringList added;
1303 	QStringList removed;
1304 	if(FET_LANGUAGE=="en_US"){
1305 		if(addedT>0){
1306 			if(addedT==1)
1307 				added << QString("Added 1 locking time constraint.");
1308 			else
1309 				added << QString("Added %1 locking time constraints.").arg(addedT);
1310 		}
1311 		if(addedS>0){
1312 			if(addedS==1)
1313 				added << QString("Added 1 locking space constraint.");
1314 			else
1315 				added << QString("Added %1 locking space constraints.").arg(addedS);
1316 		}
1317 		if(unlockedT>0){
1318 			if(unlockedT==1)
1319 				removed << QString("Removed 1 locking time constraint.");
1320 			else
1321 				removed << QString("Removed %1 locking time constraints.").arg(unlockedT);
1322 		}
1323 		if(unlockedS>0){
1324 			if(unlockedS==1)
1325 				removed << QString("Removed 1 locking space constraint.");
1326 			else
1327 				removed << QString("Removed %1 locking space constraints.").arg(unlockedS);
1328 		}
1329 	}
1330 	else{
1331 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
1332 		if(addedT>0){
1333 			added << QCoreApplication::translate("TimetableViewForm", "Added %n locking time constraint(s).",
1334 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1335 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1336 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1337 			 "(open these files with Qt Linguist and see the translation of this field).",
1338 			 addedT);
1339 		}
1340 		if(addedS>0){
1341 			added << QCoreApplication::translate("TimetableViewForm", "Added %n locking space constraint(s).",
1342 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1343 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1344 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1345 			 "(open these files with Qt Linguist and see the translation of this field).",
1346 			 addedS);
1347 		}
1348 		if(unlockedT>0){
1349 			removed << QCoreApplication::translate("TimetableViewForm", "Removed %n locking time constraint(s).",
1350 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1351 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1352 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1353 			 "(open these files with Qt Linguist and see the translation of this field).",
1354 			 unlockedT);
1355 		}
1356 		if(unlockedS>0){
1357 			removed << QCoreApplication::translate("TimetableViewForm", "Removed %n locking space constraint(s).",
1358 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1359 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1360 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1361 			 "(open these files with Qt Linguist and see the translation of this field).",
1362 			 unlockedS);
1363 		}
1364 #else
1365 		if(addedT>0){
1366 			added << QCoreApplication::translate("TimetableViewForm", "Added %n locking time constraint(s).",
1367 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1368 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1369 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1370 			 "(open these files with Qt Linguist and see the translation of this field).", QCoreApplication::UnicodeUTF8,
1371 			 addedT);
1372 		}
1373 		if(addedS>0){
1374 			added << QCoreApplication::translate("TimetableViewForm", "Added %n locking space constraint(s).",
1375 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1376 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1377 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1378 			 "(open these files with Qt Linguist and see the translation of this field).", QCoreApplication::UnicodeUTF8,
1379 			 addedS);
1380 		}
1381 		if(unlockedT>0){
1382 			removed << QCoreApplication::translate("TimetableViewForm", "Removed %n locking time constraint(s).",
1383 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1384 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1385 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1386 			 "(open these files with Qt Linguist and see the translation of this field).", QCoreApplication::UnicodeUTF8,
1387 			 unlockedT);
1388 		}
1389 		if(unlockedS>0){
1390 			removed << QCoreApplication::translate("TimetableViewForm", "Removed %n locking space constraint(s).",
1391 			 "See http://doc.qt.io/qt-5/i18n-plural-rules.html for advice on how to correctly translate this field."
1392 			 "Also, see http://doc.qt.io/qt-5/i18n-source-translation.html, section 'Handling Plurals'."
1393 			 "You have two examples on how to translate this field in fet_en_GB.ts and in fet_ro.ts"
1394 			 "(open these files with Qt Linguist and see the translation of this field).", QCoreApplication::UnicodeUTF8,
1395 			 unlockedS);
1396 		}
1397 #endif
1398 	}
1399 	QString ad=added.join("\n");
1400 	QString re=removed.join("\n");
1401 	QStringList all;
1402 	if(!ad.isEmpty())
1403 		all<<ad;
1404 	if(!re.isEmpty())
1405 		all<<re;
1406 	QString s=all.join("\n\n");
1407 	if(s.isEmpty())
1408 		s=QCoreApplication::translate("TimetableViewForm", "No locking constraints added or removed.");
1409 	QMessageBox::information(this, tr("FET information"), s);
1410 
1411 ////////// just for testing
1412 	QSet<int> backupLockedTime;
1413 	QSet<int> backupPermanentlyLockedTime;
1414 	QSet<int> backupLockedSpace;
1415 	QSet<int> backupPermanentlyLockedSpace;
1416 
1417 	backupLockedTime=idsOfLockedTime;
1418 	backupPermanentlyLockedTime=idsOfPermanentlyLockedTime;
1419 	backupLockedSpace=idsOfLockedSpace;
1420 	backupPermanentlyLockedSpace=idsOfPermanentlyLockedSpace;
1421 
1422 	LockUnlock::computeLockedUnlockedActivitiesTimeSpace(); //not needed, just for testing
1423 
1424 	assert(backupLockedTime==idsOfLockedTime);
1425 	assert(backupPermanentlyLockedTime==idsOfPermanentlyLockedTime);
1426 	assert(backupLockedSpace==idsOfLockedSpace);
1427 	assert(backupPermanentlyLockedSpace==idsOfPermanentlyLockedSpace);
1428 ///////////
1429 
1430 	LockUnlock::increaseCommunicationSpinBox();
1431 }
1432 
widthSpinBoxValueChanged()1433 void TimetableViewRoomsTimeHorizontalForm::widthSpinBoxValueChanged()
1434 {
1435 	if(widthSpinBox->value()==MINIMUM_WIDTH_SPIN_BOX_VALUE)
1436 		roomsTimetableTable->horizontalHeader()->setDefaultSectionSize(2*initialRecommendedHeight);
1437 	else
1438 		roomsTimetableTable->horizontalHeader()->setDefaultSectionSize(widthSpinBox->value());
1439 }
1440 
heightSpinBoxValueChanged()1441 void TimetableViewRoomsTimeHorizontalForm::heightSpinBoxValueChanged()
1442 {
1443 	if(heightSpinBox->value()==MINIMUM_HEIGHT_SPIN_BOX_VALUE)
1444 		roomsTimetableTable->verticalHeader()->setDefaultSectionSize(initialRecommendedHeight);
1445 	else
1446 		roomsTimetableTable->verticalHeader()->setDefaultSectionSize(heightSpinBox->value());
1447 }
1448 
help()1449 void TimetableViewRoomsTimeHorizontalForm::help()
1450 {
1451 	QString s="";
1452 	s+=QCoreApplication::translate("TimetableViewForm", "Lock/unlock: you can select one or more activities in the table and toggle lock/unlock in time, space or both.");
1453 	s+=" ";
1454 	s+=QCoreApplication::translate("TimetableViewForm", "There will be added or removed locking constraints for the selected activities (they can be unlocked only if they are not permanently locked).");
1455 	s+="\n\n";
1456 	s+=QCoreApplication::translate("TimetableViewForm", "Locking time constraints are constraints of type activity preferred starting time. Locking space constraints are constraints of type"
1457 		" activity preferred room. You can see these constraints in the corresponding constraints dialogs. New locking constraints are added at the end of the list of constraints.");
1458 	s+="\n\n";
1459 	s+=QCoreApplication::translate("TimetableViewForm", "If a cell is (permanently) locked in time or space, it contains abbreviations to show that: PLT (permanently locked time), LT (locked time), "
1460 		"PLS (permanently locked space) or LS (locked space).", "Translate the abbreviations also. Make sure the abbreviations in your language are different between themselves "
1461 		"and the user can differentiate easily between them. These abbreviations may appear also in other places, please use the same abbreviations.");
1462 
1463 	s+="\n\n";
1464 	s+=tr("If a whole column (day+hour) is selected, the activities with no room from that column will NOT be locked/unlocked.");
1465 
1466 	s+="\n\n";
1467 	s+=tr("A bold font cell means that the activity is locked in time, either permanently or not.");
1468 	s+=" ";
1469 	s+=tr("An italic font cell means that the activity is locked in space, either permanently or not.");
1470 
1471 	s+="\n\n";
1472 	s+=QCoreApplication::translate("TimetableViewForm", "Note: In this dialog, the virtual rooms' timetable will be empty.");
1473 
1474 	LongTextMessageBox::largeInformation(this, tr("FET help"), s);
1475 }
1476