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