1 /***************************************************************************
2                           modifyconstraintmindaysbetweenactivitiesform.cpp  -  description
3                              -------------------
4     begin                : Feb 11, 2005
5     copyright            : (C) 2005 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 <QMessageBox>
19 
20 #include "modifyconstraintmindaysbetweenactivitiesform.h"
21 #include "spaceconstraint.h"
22 
23 #include <QList>
24 
25 #include <QListWidget>
26 #include <QAbstractItemView>
27 #include <QScrollBar>
28 
ModifyConstraintMinDaysBetweenActivitiesForm(QWidget * parent,ConstraintMinDaysBetweenActivities * ctr)29 ModifyConstraintMinDaysBetweenActivitiesForm::ModifyConstraintMinDaysBetweenActivitiesForm(QWidget* parent, ConstraintMinDaysBetweenActivities* ctr): QDialog(parent)
30 {
31 	setupUi(this);
32 
33 	okPushButton->setDefault(true);
34 
35 	allActivitiesListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
36 	selectedActivitiesListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
37 
38 	connect(cancelPushButton, SIGNAL(clicked()), this, SLOT(cancel()));
39 	connect(okPushButton, SIGNAL(clicked()), this, SLOT(ok()));
40 	connect(allActivitiesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(addActivity()));
41 	connect(addAllActivitiesPushButton, SIGNAL(clicked()), this, SLOT(addAllActivities()));
42 	connect(selectedActivitiesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(removeActivity()));
43 
44 	connect(clearPushButton, SIGNAL(clicked()), this, SLOT(clear()));
45 
46 	centerWidgetOnScreen(this);
47 	restoreFETDialogGeometry(this);
48 
49 	QSize tmp1=teachersComboBox->minimumSizeHint();
50 	Q_UNUSED(tmp1);
51 	QSize tmp2=studentsComboBox->minimumSizeHint();
52 	Q_UNUSED(tmp2);
53 	QSize tmp3=subjectsComboBox->minimumSizeHint();
54 	Q_UNUSED(tmp3);
55 	QSize tmp4=activityTagsComboBox->minimumSizeHint();
56 	Q_UNUSED(tmp4);
57 
58 	this->_ctr=ctr;
59 
60 	selectedActivitiesList.clear();
61 	selectedActivitiesListWidget->clear();
62 	for(int i=0; i<ctr->n_activities; i++){
63 		int actId=ctr->activitiesId[i];
64 		this->selectedActivitiesList.append(actId);
65 		Activity *act=gt.rules.activitiesPointerHash.value(actId, nullptr);
66 		assert(act!=nullptr);
67 		this->selectedActivitiesListWidget->addItem(act->getDescription(gt.rules));
68 	}
69 
70 	if(gt.rules.mode!=MORNINGS_AFTERNOONS){
71 		minDaysSpinBox->setMinimum(1);
72 		minDaysSpinBox->setMaximum(gt.rules.nDaysPerWeek-1);
73 		minDaysSpinBox->setValue(ctr->minDays);
74 	}
75 	else{
76 		minDaysSpinBox->setMinimum(1);
77 		minDaysSpinBox->setMaximum(gt.rules.nDaysPerWeek/2-1);
78 		minDaysSpinBox->setValue(ctr->minDays);
79 	}
80 
81 	consecutiveIfSameDayCheckBox->setChecked(ctr->consecutiveIfSameDay);
82 	weightLineEdit->setText(CustomFETString::number(ctr->weightPercentage));
83 
84 	////////////////
85 	teachersComboBox->addItem("");
86 	for(int i=0; i<gt.rules.teachersList.size(); i++){
87 		Teacher* tch=gt.rules.teachersList[i];
88 		teachersComboBox->addItem(tch->name);
89 	}
90 	teachersComboBox->setCurrentIndex(0);
91 
92 	subjectsComboBox->addItem("");
93 	for(int i=0; i<gt.rules.subjectsList.size(); i++){
94 		Subject* sb=gt.rules.subjectsList[i];
95 		subjectsComboBox->addItem(sb->name);
96 	}
97 	subjectsComboBox->setCurrentIndex(0);
98 
99 	activityTagsComboBox->addItem("");
100 	for(int i=0; i<gt.rules.activityTagsList.size(); i++){
101 		ActivityTag* st=gt.rules.activityTagsList[i];
102 		activityTagsComboBox->addItem(st->name);
103 	}
104 	activityTagsComboBox->setCurrentIndex(0);
105 
106 	populateStudentsComboBox(studentsComboBox, QString(""), true);
107 	studentsComboBox->setCurrentIndex(0);
108 
109 	filterChanged();
110 
111 	connect(teachersComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
112 	connect(studentsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
113 	connect(subjectsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
114 	connect(activityTagsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
115 }
116 
~ModifyConstraintMinDaysBetweenActivitiesForm()117 ModifyConstraintMinDaysBetweenActivitiesForm::~ModifyConstraintMinDaysBetweenActivitiesForm()
118 {
119 	saveFETDialogGeometry(this);
120 }
121 
filterChanged()122 void ModifyConstraintMinDaysBetweenActivitiesForm::filterChanged()
123 {
124 	allActivitiesListWidget->clear();
125 	this->activitiesList.clear();
126 
127 	for(int i=0; i<gt.rules.activitiesList.size(); i++){
128 		Activity* ac=gt.rules.activitiesList[i];
129 		if(filterOk(ac)){
130 			allActivitiesListWidget->addItem(ac->getDescription(gt.rules));
131 			this->activitiesList.append(ac->id);
132 		}
133 	}
134 
135 	int q=allActivitiesListWidget->verticalScrollBar()->minimum();
136 	allActivitiesListWidget->verticalScrollBar()->setValue(q);
137 }
138 
filterOk(Activity * act)139 bool ModifyConstraintMinDaysBetweenActivitiesForm::filterOk(Activity* act)
140 {
141 	QString tn=teachersComboBox->currentText();
142 	QString stn=studentsComboBox->currentText();
143 	QString sbn=subjectsComboBox->currentText();
144 	QString atn=activityTagsComboBox->currentText();
145 	int ok=true;
146 
147 	//teacher
148 	if(tn!=""){
149 		bool ok2=false;
150 		for(QStringList::const_iterator it=act->teachersNames.constBegin(); it!=act->teachersNames.constEnd(); it++)
151 			if(*it == tn){
152 				ok2=true;
153 				break;
154 			}
155 		if(!ok2)
156 			ok=false;
157 	}
158 
159 	//subject
160 	if(sbn!="" && sbn!=act->subjectName)
161 		ok=false;
162 
163 	//activity tag
164 	if(atn!="" && !act->activityTagsNames.contains(atn))
165 		ok=false;
166 
167 	//students
168 	if(stn!=""){
169 		bool ok2=false;
170 		for(QStringList::const_iterator it=act->studentsNames.constBegin(); it!=act->studentsNames.constEnd(); it++)
171 			if(*it == stn){
172 				ok2=true;
173 				break;
174 			}
175 		if(!ok2)
176 			ok=false;
177 	}
178 
179 	return ok;
180 }
181 
ok()182 void ModifyConstraintMinDaysBetweenActivitiesForm::ok()
183 {
184 	double weight;
185 	QString tmp=weightLineEdit->text();
186 	weight_sscanf(tmp, "%lf", &weight);
187 	if(weight<0.0 || weight>100.0){
188 		QMessageBox::warning(this, tr("FET information"),
189 			tr("Invalid weight (percentage)"));
190 		return;
191 	}
192 
193 	if(minDaysSpinBox->value()<=0){
194 		QMessageBox::warning(this, tr("FET information"),
195 			tr("Invalid number of min days between activities - it must be greater than 0."));
196 		return;
197 	}
198 
199 	if(this->selectedActivitiesList.size()==0){
200 		QMessageBox::warning(this, tr("FET information"),
201 			tr("Empty list of selected activities"));
202 		return;
203 	}
204 	if(this->selectedActivitiesList.size()==1){
205 		QMessageBox::warning(this, tr("FET information"),
206 			tr("Only one selected activity"));
207 		return;
208 	}
209 
210 #if 0
211 	if(0 && this->selectedActivitiesList.size()>gt.rules.nDaysPerWeek){
212 		QString s=tr("You want to add a constraint min days between activities for more activities than the number of days per week."
213 		  " This is a very bad practice from the way the algorithm of generation works (it slows down the generation and makes it harder to find a solution).")+
214 		 "\n\n"+
215 		 tr("The best way to add the activities would be:")+
216 		 "\n\n"+
217 
218 		 tr("1. If you add 'force consecutive if same day', then couple extra activities in pairs to obtain a number of activities equal to the number of days per week"
219 		  ". Example: 7 activities with duration 1 in a 5 days week, then transform into 5 activities with durations: 2,2,1,1,1 and add a single container activity with these 5 components"
220 		  " (possibly raising the weight of added constraint min days between activities up to 100%)")+
221 
222 		  "\n\n"+
223 
224 		 tr("2. If you don't add 'force consecutive if same day', then add a larger activity split into a number of"
225 		  " activities equal with the number of days per week and the remaining components into other larger split activity."
226 		  " For example, suppose you need to add 7 activities with duration 1 in a 5 days week. Add 2 larger container activities,"
227 		  " first one split into 5 activities with duration 1 and second one split into 2 activities with duration 1"
228 		  " (possibly raising the weight of added constraints min days between activities for each of the 2 containers up to 100%)")+
229 
230 	  	 "\n\n"+
231 		 tr("Do you want to add current constraint as it is now (not recommended) or cancel and edit as instructed?");
232 
233 		int t=QMessageBox::warning(this, tr("FET warning"),	s,
234 		 QMessageBox::Yes, QMessageBox::Cancel);
235 		if(t==QMessageBox::Cancel)
236 			return;
237 	}
238 #endif
239 
240 	if(1){
241 		ConstraintMinDaysBetweenActivities adc;
242 
243 		int i;
244 		QList<int>::const_iterator it;
245 		adc.activitiesId.clear();
246 		for(i=0, it=this->selectedActivitiesList.constBegin(); it!=this->selectedActivitiesList.constEnd(); it++, i++){
247 			adc.activitiesId.append(*it);
248 		}
249 		adc.n_activities=i;
250 
251 		adc.weightPercentage=weight;
252 		adc.consecutiveIfSameDay=consecutiveIfSameDayCheckBox->isChecked();
253 		adc.minDays=minDaysSpinBox->value();
254 
255 		bool duplicate=false;
256 
257 		for(TimeConstraint* tc : qAsConst(gt.rules.timeConstraintsList))
258 			if(tc!=this->_ctr && tc->type==CONSTRAINT_MIN_DAYS_BETWEEN_ACTIVITIES)
259 				if( ( *((ConstraintMinDaysBetweenActivities*)tc) ) == adc){
260 					duplicate=true;
261 					break;
262 				}
263 
264 		if(duplicate){
265 			QMessageBox::warning(this, tr("FET information"), tr("Cannot proceed, current constraint is equal to another one (it is duplicated)"));
266 			return;
267 		}
268 	}
269 
270 	if(_ctr->activitiesId!=selectedActivitiesList){
271 		for(int oldId : qAsConst(_ctr->activitiesId)){
272 			QSet<ConstraintMinDaysBetweenActivities*> cs=gt.rules.mdbaHash.value(oldId, QSet<ConstraintMinDaysBetweenActivities*>());
273 			assert(cs.contains(_ctr));
274 			cs.remove(_ctr);
275 			gt.rules.mdbaHash.insert(oldId, cs);
276 		}
277 
278 		for(int newId : qAsConst(selectedActivitiesList)){
279 			QSet<ConstraintMinDaysBetweenActivities*> cs=gt.rules.mdbaHash.value(newId, QSet<ConstraintMinDaysBetweenActivities*>());
280 			assert(!cs.contains(_ctr));
281 			cs.insert(_ctr);
282 			gt.rules.mdbaHash.insert(newId, cs);
283 		}
284 
285 		_ctr->activitiesId=selectedActivitiesList;
286 		_ctr->n_activities=_ctr->activitiesId.count();
287 	}
288 
289 	/*int i;
290 	QList<int>::iterator it;
291 	this->_ctr->activitiesId.clear();
292 	for(i=0, it=this->selectedActivitiesList.begin(); it!=this->selectedActivitiesList.end(); it++, i++){
293 		this->_ctr->activitiesId.append(*it);
294 	}
295 	this->_ctr->n_activities=i;*/
296 
297 	this->_ctr->weightPercentage=weight;
298 	this->_ctr->consecutiveIfSameDay=consecutiveIfSameDayCheckBox->isChecked();
299 	this->_ctr->minDays=minDaysSpinBox->value();
300 
301 	gt.rules.internalStructureComputed=false;
302 	setRulesModifiedAndOtherThings(&gt.rules);
303 
304 	this->close();
305 }
306 
cancel()307 void ModifyConstraintMinDaysBetweenActivitiesForm::cancel()
308 {
309 	this->close();
310 }
311 
addActivity()312 void ModifyConstraintMinDaysBetweenActivitiesForm::addActivity()
313 {
314 	if(allActivitiesListWidget->currentRow()<0)
315 		return;
316 	int tmp=allActivitiesListWidget->currentRow();
317 	int _id=this->activitiesList.at(tmp);
318 
319 	QString actName=allActivitiesListWidget->currentItem()->text();
320 	assert(actName!="");
321 
322 	//duplicate?
323 	if(this->selectedActivitiesList.contains(_id))
324 		return;
325 
326 	selectedActivitiesListWidget->addItem(actName);
327 	selectedActivitiesListWidget->setCurrentRow(selectedActivitiesListWidget->count()-1);
328 
329 	this->selectedActivitiesList.append(_id);
330 }
331 
addAllActivities()332 void ModifyConstraintMinDaysBetweenActivitiesForm::addAllActivities()
333 {
334 	for(int tmp=0; tmp<allActivitiesListWidget->count(); tmp++){
335 		int _id=this->activitiesList.at(tmp);
336 
337 		QString actName=allActivitiesListWidget->item(tmp)->text();
338 		assert(actName!="");
339 
340 		if(this->selectedActivitiesList.contains(_id))
341 			continue;
342 
343 		selectedActivitiesListWidget->addItem(actName);
344 		this->selectedActivitiesList.append(_id);
345 	}
346 
347 	selectedActivitiesListWidget->setCurrentRow(selectedActivitiesListWidget->count()-1);
348 }
349 
removeActivity()350 void ModifyConstraintMinDaysBetweenActivitiesForm::removeActivity()
351 {
352 	if(selectedActivitiesListWidget->currentRow()<0 || selectedActivitiesListWidget->count()<=0)
353 		return;
354 	int tmp=selectedActivitiesListWidget->currentRow();
355 
356 	this->selectedActivitiesList.removeAt(tmp);
357 
358 	selectedActivitiesListWidget->setCurrentRow(-1);
359 	QListWidgetItem* item=selectedActivitiesListWidget->takeItem(tmp);
360 	delete item;
361 	if(tmp<selectedActivitiesListWidget->count())
362 		selectedActivitiesListWidget->setCurrentRow(tmp);
363 	else
364 		selectedActivitiesListWidget->setCurrentRow(selectedActivitiesListWidget->count()-1);
365 }
366 
clear()367 void ModifyConstraintMinDaysBetweenActivitiesForm::clear()
368 {
369 	selectedActivitiesListWidget->clear();
370 	selectedActivitiesList.clear();
371 }
372