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=>.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=>.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=>.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