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(&gt.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