1 /***************************************************************************
2 modifyconstraintactivitiespreferredtimeslotsform.cpp - description
3 -------------------
4 begin : 15 May 2004
5 copyright : (C) 2004 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 <QMessageBox>
21
22 #include "modifyconstraintactivitiespreferredtimeslotsform.h"
23 #include "timeconstraint.h"
24
25 #include <QHeaderView>
26 #include <QTableWidget>
27 #include <QTableWidgetItem>
28
29 #include <QBrush>
30 #include <QColor>
31
32 #define YES (QString(" "))
33 #define NO (QString("X"))
34
ModifyConstraintActivitiesPreferredTimeSlotsForm(QWidget * parent,ConstraintActivitiesPreferredTimeSlots * ctr)35 ModifyConstraintActivitiesPreferredTimeSlotsForm::ModifyConstraintActivitiesPreferredTimeSlotsForm(QWidget* parent, ConstraintActivitiesPreferredTimeSlots* ctr): QDialog(parent)
36 {
37 setupUi(this);
38
39 int duration=ctr->duration;
40 durationCheckBox->setChecked(duration>=1);
41 durationSpinBox->setEnabled(duration>=1);
42 durationSpinBox->setMinimum(1);
43 durationSpinBox->setMaximum(gt.rules.nHoursPerDay);
44 if(duration>=1)
45 durationSpinBox->setValue(duration);
46 else
47 durationSpinBox->setValue(1);
48
49 okPushButton->setDefault(true);
50
51 connect(preferredTimesTable, SIGNAL(itemClicked(QTableWidgetItem*)), this, SLOT(itemClicked(QTableWidgetItem*)));
52 connect(cancelPushButton, SIGNAL(clicked()), this, SLOT(cancel()));
53 connect(okPushButton, SIGNAL(clicked()), this, SLOT(ok()));
54 connect(setAllAllowedPushButton, SIGNAL(clicked()), this, SLOT(setAllSlotsAllowed()));
55 connect(setAllNotAllowedPushButton, SIGNAL(clicked()), this, SLOT(setAllSlotsNotAllowed()));
56
57 centerWidgetOnScreen(this);
58 restoreFETDialogGeometry(this);
59
60 QSize tmp1=teachersComboBox->minimumSizeHint();
61 Q_UNUSED(tmp1);
62 QSize tmp2=studentsComboBox->minimumSizeHint();
63 Q_UNUSED(tmp2);
64 QSize tmp3=subjectsComboBox->minimumSizeHint();
65 Q_UNUSED(tmp3);
66 QSize tmp4=activityTagsComboBox->minimumSizeHint();
67 Q_UNUSED(tmp4);
68
69 this->_ctr=ctr;
70
71 updateTeachersComboBox();
72 updateStudentsComboBox(parent);
73 updateSubjectsComboBox();
74 updateActivityTagsComboBox();
75
76 preferredTimesTable->setRowCount(gt.rules.nHoursPerDay);
77 preferredTimesTable->setColumnCount(gt.rules.nDaysPerWeek);
78
79 for(int j=0; j<gt.rules.nDaysPerWeek; j++){
80 QTableWidgetItem* item= new QTableWidgetItem(gt.rules.daysOfTheWeek[j]);
81 preferredTimesTable->setHorizontalHeaderItem(j, item);
82 }
83 for(int i=0; i<gt.rules.nHoursPerDay; i++){
84 QTableWidgetItem* item=new QTableWidgetItem(gt.rules.hoursOfTheDay[i]);
85 preferredTimesTable->setVerticalHeaderItem(i, item);
86 }
87
88 Matrix2D<bool> currentMatrix;
89 currentMatrix.resize(gt.rules.nHoursPerDay, gt.rules.nDaysPerWeek);
90
91 for(int i=0; i<gt.rules.nHoursPerDay; i++)
92 for(int j=0; j<gt.rules.nDaysPerWeek; j++)
93 currentMatrix[i][j]=false;
94 for(int k=0; k<ctr->p_nPreferredTimeSlots_L; k++){
95 if(ctr->p_hours_L[k]==-1 || ctr->p_days_L[k]==-1)
96 assert(0);
97 int i=ctr->p_hours_L[k];
98 int j=ctr->p_days_L[k];
99 if(i>=0 && i<gt.rules.nHoursPerDay && j>=0 && j<gt.rules.nDaysPerWeek)
100 currentMatrix[i][j]=true;
101 }
102
103 for(int i=0; i<gt.rules.nHoursPerDay; i++)
104 for(int j=0; j<gt.rules.nDaysPerWeek; j++){
105 QTableWidgetItem* item= new QTableWidgetItem();
106 item->setTextAlignment(Qt::AlignCenter);
107 item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled);
108 if(SHOW_TOOLTIPS_FOR_CONSTRAINTS_WITH_TABLES)
109 item->setToolTip(gt.rules.daysOfTheWeek[j]+QString("\n")+gt.rules.hoursOfTheDay[i]);
110 preferredTimesTable->setItem(i, j, item);
111
112 if(!currentMatrix[i][j])
113 item->setText(NO);
114 else
115 item->setText(YES);
116
117 colorItem(item);
118 }
119
120 preferredTimesTable->resizeRowsToContents();
121
122 weightLineEdit->setText(CustomFETString::number(ctr->weightPercentage));
123
124 connect(preferredTimesTable->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(horizontalHeaderClicked(int)));
125 connect(preferredTimesTable->verticalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(verticalHeaderClicked(int)));
126
127 preferredTimesTable->setSelectionMode(QAbstractItemView::NoSelection);
128
129 setStretchAvailabilityTableNicely(preferredTimesTable);
130 }
131
~ModifyConstraintActivitiesPreferredTimeSlotsForm()132 ModifyConstraintActivitiesPreferredTimeSlotsForm::~ModifyConstraintActivitiesPreferredTimeSlotsForm()
133 {
134 saveFETDialogGeometry(this);
135 }
136
colorItem(QTableWidgetItem * item)137 void ModifyConstraintActivitiesPreferredTimeSlotsForm::colorItem(QTableWidgetItem* item)
138 {
139 if(USE_GUI_COLORS){
140 #if QT_VERSION >= QT_VERSION_CHECK(5,14,0)
141 if(item->text()==YES)
142 item->setBackground(QBrush(QColorConstants::DarkGreen));
143 else
144 item->setBackground(QBrush(QColorConstants::DarkRed));
145 item->setForeground(QBrush(QColorConstants::LightGray));
146 #else
147 if(item->text()==YES)
148 item->setBackground(QBrush(Qt::darkGreen));
149 else
150 item->setBackground(QBrush(Qt::darkRed));
151 item->setForeground(QBrush(Qt::lightGray));
152 #endif
153 }
154 }
155
horizontalHeaderClicked(int col)156 void ModifyConstraintActivitiesPreferredTimeSlotsForm::horizontalHeaderClicked(int col)
157 {
158 if(col>=0 && col<gt.rules.nDaysPerWeek){
159 QString s=preferredTimesTable->item(0, col)->text();
160 if(s==YES)
161 s=NO;
162 else{
163 assert(s==NO);
164 s=YES;
165 }
166
167 for(int row=0; row<gt.rules.nHoursPerDay; row++){
168 preferredTimesTable->item(row, col)->setText(s);
169 colorItem(preferredTimesTable->item(row,col));
170 }
171 }
172 }
173
verticalHeaderClicked(int row)174 void ModifyConstraintActivitiesPreferredTimeSlotsForm::verticalHeaderClicked(int row)
175 {
176 if(row>=0 && row<gt.rules.nHoursPerDay){
177 QString s=preferredTimesTable->item(row, 0)->text();
178 if(s==YES)
179 s=NO;
180 else{
181 assert(s==NO);
182 s=YES;
183 }
184
185 for(int col=0; col<gt.rules.nDaysPerWeek; col++){
186 preferredTimesTable->item(row, col)->setText(s);
187 colorItem(preferredTimesTable->item(row,col));
188 }
189 }
190 }
191
setAllSlotsAllowed()192 void ModifyConstraintActivitiesPreferredTimeSlotsForm::setAllSlotsAllowed()
193 {
194 for(int i=0; i<gt.rules.nHoursPerDay; i++)
195 for(int j=0; j<gt.rules.nDaysPerWeek; j++){
196 preferredTimesTable->item(i, j)->setText(YES);
197 colorItem(preferredTimesTable->item(i,j));
198 }
199 }
200
setAllSlotsNotAllowed()201 void ModifyConstraintActivitiesPreferredTimeSlotsForm::setAllSlotsNotAllowed()
202 {
203 for(int i=0; i<gt.rules.nHoursPerDay; i++)
204 for(int j=0; j<gt.rules.nDaysPerWeek; j++){
205 preferredTimesTable->item(i, j)->setText(NO);
206 colorItem(preferredTimesTable->item(i,j));
207 }
208 }
209
itemClicked(QTableWidgetItem * item)210 void ModifyConstraintActivitiesPreferredTimeSlotsForm::itemClicked(QTableWidgetItem* item)
211 {
212 QString s=item->text();
213 if(s==YES)
214 s=NO;
215 else{
216 assert(s==NO);
217 s=YES;
218 }
219 item->setText(s);
220 colorItem(item);
221 }
222
updateTeachersComboBox()223 void ModifyConstraintActivitiesPreferredTimeSlotsForm::updateTeachersComboBox(){
224 int i=0, j=-1;
225 teachersComboBox->clear();
226 teachersComboBox->addItem("");
227 if(this->_ctr->p_teacherName=="")
228 j=i;
229 i++;
230 for(int k=0; k<gt.rules.teachersList.size(); k++){
231 Teacher* t=gt.rules.teachersList[k];
232 teachersComboBox->addItem(t->name);
233 if(t->name==this->_ctr->p_teacherName)
234 j=i;
235 i++;
236 }
237 assert(j>=0);
238 teachersComboBox->setCurrentIndex(j);
239 }
240
updateStudentsComboBox(QWidget * parent)241 void ModifyConstraintActivitiesPreferredTimeSlotsForm::updateStudentsComboBox(QWidget* parent){
242 int j=populateStudentsComboBox(studentsComboBox, this->_ctr->p_studentsName, true);
243 if(j<0)
244 showWarningForInvisibleSubgroupConstraint(parent, this->_ctr->p_studentsName);
245 else
246 assert(j>=0);
247 studentsComboBox->setCurrentIndex(j);
248 }
249
updateSubjectsComboBox()250 void ModifyConstraintActivitiesPreferredTimeSlotsForm::updateSubjectsComboBox(){
251 int i=0, j=-1;
252 subjectsComboBox->clear();
253 subjectsComboBox->addItem("");
254 if(this->_ctr->p_subjectName=="")
255 j=i;
256 i++;
257 for(int k=0; k<gt.rules.subjectsList.size(); k++){
258 Subject* s=gt.rules.subjectsList[k];
259 subjectsComboBox->addItem(s->name);
260 if(s->name==this->_ctr->p_subjectName)
261 j=i;
262 i++;
263 }
264 assert(j>=0);
265 subjectsComboBox->setCurrentIndex(j);
266 }
267
updateActivityTagsComboBox()268 void ModifyConstraintActivitiesPreferredTimeSlotsForm::updateActivityTagsComboBox(){
269 int i=0, j=-1;
270 activityTagsComboBox->clear();
271 activityTagsComboBox->addItem("");
272 if(this->_ctr->p_activityTagName=="")
273 j=i;
274 i++;
275 for(int k=0; k<gt.rules.activityTagsList.size(); k++){
276 ActivityTag* s=gt.rules.activityTagsList[k];
277 activityTagsComboBox->addItem(s->name);
278 if(s->name==this->_ctr->p_activityTagName)
279 j=i;
280 i++;
281 }
282 assert(j>=0);
283 activityTagsComboBox->setCurrentIndex(j);
284 }
285
ok()286 void ModifyConstraintActivitiesPreferredTimeSlotsForm::ok()
287 {
288 int duration=-1;
289 if(durationCheckBox->isChecked()){
290 assert(durationSpinBox->isEnabled());
291 duration=durationSpinBox->value();
292 }
293
294 if(studentsComboBox->currentIndex()<0){
295 showWarningCannotModifyConstraintInvisibleSubgroupConstraint(this, this->_ctr->p_studentsName);
296 return;
297 }
298
299 double weight;
300 QString tmp=weightLineEdit->text();
301 weight_sscanf(tmp, "%lf", &weight);
302 if(weight<0.0 || weight>100.0){
303 QMessageBox::warning(this, tr("FET information"),
304 tr("Invalid weight (percentage)"));
305 return;
306 }
307
308 QString teacher=teachersComboBox->currentText();
309 if(teacher!="")
310 assert(gt.rules.searchTeacher(teacher)>=0);
311
312 QString students=studentsComboBox->currentText();
313 if(students!="")
314 assert(gt.rules.searchStudentsSet(students)!=nullptr);
315
316 QString subject=subjectsComboBox->currentText();
317 if(subject!="")
318 assert(gt.rules.searchSubject(subject)>=0);
319
320 QString activityTag=activityTagsComboBox->currentText();
321 if(activityTag!="")
322 assert(gt.rules.searchActivityTag(activityTag)>=0);
323
324 if(duration==-1 && teacher=="" && students=="" && subject=="" && activityTag==""){
325 int t=QMessageBox::question(this, tr("FET question"),
326 tr("You specified all the activities. This might be a small problem: if you specify"
327 " a not allowed slot between two allowed slots, this not allowed slot will"
328 " be counted as a gap in the teachers' and students' timetable.\n\n"
329 " The best practice would be to use constraint break times.\n\n"
330 " If you need weight under 100%, then you can use this constraint, but be careful"
331 " not to obtain an impossible timetable (if your teachers/students are constrained on gaps"
332 " or early gaps and if you leave a not allowed slot between 2 allowed slots or"
333 " a not allowed slot early in the day and more allowed slots after it,"
334 " this possible gap might be counted in teachers' and students' timetable)")
335 +"\n\n"+tr("Do you want to add current constraint?"),
336 QMessageBox::Yes, QMessageBox::Cancel);
337
338 if(t==QMessageBox::Cancel)
339 return;
340 }
341
342 if(duration==-1 && teacher!="" && students=="" && subject=="" && activityTag==""){
343 int t=QMessageBox::question(this, tr("FET question"),
344 tr("You specified only the teacher. This might be a small problem: if you specify"
345 " a not allowed slot between two allowed slots, this not allowed slot will"
346 " be counted as a gap in the teacher's timetable.\n\n"
347 " The best practice would be to use constraint teacher not available times.\n\n"
348 " If you need weight under 100%, then you can use this constraint, but be careful"
349 " not to obtain an impossible timetable (if your teacher is constrained on gaps"
350 " and if you leave a not allowed slot between 2 allowed slots, this possible"
351 " gap might be counted in teacher's timetable)")
352 +"\n\n"+tr("Do you want to add current constraint?"),
353 QMessageBox::Yes, QMessageBox::Cancel);
354
355 if(t==QMessageBox::Cancel)
356 return;
357 }
358 if(duration==-1 && teacher=="" && students!="" && subject=="" && activityTag==""){
359 int t=QMessageBox::question(this, tr("FET question"),
360 tr("You specified only the students set. This might be a small problem: if you specify"
361 " a not allowed slot between two allowed slots (or a not allowed slot before allowed slots),"
362 " this not allowed slot will"
363 " be counted as a gap (or early gap) in the students' timetable.\n\n"
364 " The best practice would be to use constraint students set not available times.\n\n"
365 " If you need weight under 100%, then you can use this constraint, but be careful"
366 " not to obtain an impossible timetable (if your students set is constrained on gaps or early gaps"
367 " and if you leave a not allowed slot between 2 allowed slots (or a not allowed slot before allowed slots), this possible"
368 " gap might be counted in students' timetable)")
369 +"\n\n"+tr("Do you want to add current constraint?"),
370 QMessageBox::Yes, QMessageBox::Cancel);
371
372 if(t==QMessageBox::Cancel)
373 return;
374 }
375
376 QList<int> days_L;
377 QList<int> hours_L;
378 int n=0;
379 for(int j=0; j<gt.rules.nDaysPerWeek; j++)
380 for(int i=0; i<gt.rules.nHoursPerDay; i++)
381 if(preferredTimesTable->item(i, j)->text()==YES){
382 days_L.append(j);
383 hours_L.append(i);
384 n++;
385 }
386
387 if(n<=0){
388 int t=QMessageBox::question(this, tr("FET question"),
389 tr("Warning: 0 slots selected. Are you sure?"),
390 QMessageBox::Yes, QMessageBox::Cancel);
391
392 if(t==QMessageBox::Cancel)
393 return;
394 }
395
396 this->_ctr->weightPercentage=weight;
397 this->_ctr->p_teacherName=teacher;
398 this->_ctr->p_studentsName=students;
399 this->_ctr->p_subjectName=subject;
400 this->_ctr->p_activityTagName=activityTag;
401 this->_ctr->p_nPreferredTimeSlots_L=n;
402 this->_ctr->p_days_L=days_L;
403 this->_ctr->p_hours_L=hours_L;
404
405 this->_ctr->duration=duration;
406
407 gt.rules.internalStructureComputed=false;
408 setRulesModifiedAndOtherThings(>.rules);
409
410 this->close();
411 }
412
cancel()413 void ModifyConstraintActivitiesPreferredTimeSlotsForm::cancel()
414 {
415 this->close();
416 }
417
on_durationCheckBox_toggled()418 void ModifyConstraintActivitiesPreferredTimeSlotsForm::on_durationCheckBox_toggled()
419 {
420 durationSpinBox->setEnabled(durationCheckBox->isChecked());
421 }
422
423 #undef YES
424 #undef NO
425