1 /***************************************************************************
2 addactivityform.cpp - description
3 -------------------
4 begin : Wed Apr 23 2003
5 copyright : (C) 2003 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 <QtGlobal>
19
20 #include "longtextmessagebox.h"
21
22 #include "addactivityform.h"
23 #include "teacher.h"
24 #include "subject.h"
25 #include "studentsset.h"
26
27 #include "activityplanningform.h"
28
29 #include <QMessageBox>
30
31 #include <QDialog>
32
33 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
34 #include <QtWidgets>
35 #else
36 #include <QtGui>
37 #endif
38
39 #include <QList>
40
41 #include <QListWidget>
42 #include <QAbstractItemView>
43 #include <QModelIndex>
44 #include <QScrollBar>
45
46 #include <QSettings>
47 #include <QObject>
48 #include <QMetaObject>
49
50 extern const QString COMPANY;
51 extern const QString PROGRAM;
52
dur(int i)53 QSpinBox* AddActivityForm::dur(int i)
54 {
55 assert(i>=0 && i<durList.count());
56 assert(i<MAX_SPLIT_OF_AN_ACTIVITY);
57 return durList.at(i);
58 }
59
activ(int i)60 QCheckBox* AddActivityForm::activ(int i)
61 {
62 assert(i>=0 && i<activList.count());
63 assert(i<MAX_SPLIT_OF_AN_ACTIVITY);
64 return activList.at(i);
65 }
66
AddActivityForm(QWidget * parent,const QString & teacherName,const QString & studentsSetName,const QString & subjectName,const QString & activityTagName)67 AddActivityForm::AddActivityForm(QWidget* parent, const QString& teacherName, const QString& studentsSetName, const QString& subjectName, const QString& activityTagName): QDialog(parent)
68 {
69 setupUi(this);
70
71 studentsSeparatelyCheckBox->setChecked(false);
72
73 for(Teacher* tch : qAsConst(gt.rules.teachersList))
74 teacherNamesSet.insert(tch->name);
75 for(Subject* sbj : qAsConst(gt.rules.subjectsList))
76 subjectNamesSet.insert(sbj->name);
77 for(ActivityTag* at : qAsConst(gt.rules.activityTagsList))
78 activityTagNamesSet.insert(at->name);
79
80 allTeachersListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
81 selectedTeachersListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
82 allStudentsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
83 selectedStudentsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
84 allActivityTagsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
85 selectedActivityTagsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
86
87 splitSpinBox->setMinimum(1);
88 splitSpinBox->setMaximum(MAX_SPLIT_OF_AN_ACTIVITY);
89 splitSpinBox->setValue(1);
90
91 durList.clear();
92 activList.clear();
93
94 populateSubactivitiesTabWidget(splitSpinBox->value());
95
96 QSettings settings(COMPANY, PROGRAM);
97
98 subgroupsCheckBox->setChecked(settings.value(this->metaObject()->className()+QString("/show-subgroups-check-box-state"), "false").toBool());
99 groupsCheckBox->setChecked(settings.value(this->metaObject()->className()+QString("/show-groups-check-box-state"), "true").toBool());
100 yearsCheckBox->setChecked(settings.value(this->metaObject()->className()+QString("/show-years-check-box-state"), "true").toBool());
101
102 allTeachersRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/all-teachers-radio-button-state"), "true").toBool());
103 qualifiedTeachersRadioButton->setChecked(settings.value(this->metaObject()->className()+QString("/qualified-teachers-radio-button-state"), "false").toBool());
104
105 connect(subgroupsCheckBox, SIGNAL(toggled(bool)), this, SLOT(showSubgroupsChanged()));
106 connect(groupsCheckBox, SIGNAL(toggled(bool)), this, SLOT(showGroupsChanged()));
107 connect(yearsCheckBox, SIGNAL(toggled(bool)), this, SLOT(showYearsChanged()));
108
109 connect(splitSpinBox, SIGNAL(valueChanged(int)), this, SLOT(splitChanged()));
110
111 connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
112 connect(addActivityPushButton, SIGNAL(clicked()), this, SLOT(addActivity()));
113 connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
114
115 connect(allTeachersListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(addTeacher()));
116 connect(selectedTeachersListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(removeTeacher()));
117 connect(allStudentsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(addStudents()));
118 connect(selectedStudentsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(removeStudents()));
119 connect(allActivityTagsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(addActivityTag()));
120 connect(selectedActivityTagsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(removeActivityTag()));
121
122 connect(clearActivityTagPushButton, SIGNAL(clicked()), this, SLOT(clearActivityTags()));
123 connect(clearStudentsPushButton, SIGNAL(clicked()), this, SLOT(clearStudents()));
124 connect(clearTeacherPushButton, SIGNAL(clicked()), this, SLOT(clearTeachers()));
125
126 connect(minDayDistanceSpinBox, SIGNAL(valueChanged(int)), this, SLOT(minDaysChanged()));
127
128 centerWidgetOnScreen(this);
129 restoreFETDialogGeometry(this);
130
131 QSize tmp3=subjectsComboBox->minimumSizeHint();
132 Q_UNUSED(tmp3);
133
134 selectedStudentsListWidget->clear();
135 updateStudentsListWidget();
136
137 updateSubjectsComboBox();
138 if(subjectName!=""){
139 int pos=-1;
140 for(int i=0; i<subjectsComboBox->count(); i++){
141 if(subjectsComboBox->itemText(i)==subjectName){
142 pos=i;
143 break;
144 }
145 }
146 assert(pos>=0);
147 subjectsComboBox->setCurrentIndex(pos);
148 }
149 else{
150 //begin trick to pass a Qt 4.6.0 bug: the first entry is not highlighted with mouse until you move to second entry and then back up
151 //also, this trick makes the combo box behave nicer under Windows: the first subject is shown with an ugly edge if not using this trick.
152 if(subjectsComboBox->view()){
153 subjectsComboBox->view()->setCurrentIndex(QModelIndex());
154 }
155 //end trick
156 subjectsComboBox->setCurrentIndex(-1);
157 }
158
159 updateActivityTagsListWidget();
160
161 //after updateSubjectsComboBox
162 connect(subjectsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateAllTeachersListWidget()));
163 connect(allTeachersRadioButton, SIGNAL(toggled(bool)), this, SLOT(allTeachersRadioButtonToggled(bool)));
164 connect(qualifiedTeachersRadioButton, SIGNAL(toggled(bool)), this, SLOT(qualifiedTeachersRadioButtonToggled(bool)));
165 updateAllTeachersListWidget();
166 selectedTeachersListWidget->clear();
167
168 if(gt.rules.mode!=MORNINGS_AFTERNOONS)
169 minDayDistanceSpinBox->setMaximum(gt.rules.nDaysPerWeek-1);
170 else
171 minDayDistanceSpinBox->setMaximum(gt.rules.nDaysPerWeek/2-1);
172 minDayDistanceSpinBox->setMinimum(0);
173 minDayDistanceSpinBox->setValue(1);
174
175 int nSplit=splitSpinBox->value();
176
177 minDayDistanceTextLabel->setEnabled(nSplit>=2);
178 minDayDistanceSpinBox->setEnabled(nSplit>=2);
179 percentageTextLabel->setEnabled(nSplit>=2 && minDayDistanceSpinBox->value()>0);
180 percentageLineEdit->setEnabled(nSplit>=2 && minDayDistanceSpinBox->value()>0);
181 forceConsecutiveCheckBox->setEnabled(nSplit>=2 && minDayDistanceSpinBox->value()>0);
182
183 nStudentsSpinBox->setMinimum(-1);
184 nStudentsSpinBox->setMaximum(MAX_ROOM_CAPACITY);
185 nStudentsSpinBox->setValue(-1);
186
187 addActivityPushButton->setDefault(true);
188 addActivityPushButton->setFocus();
189
190 if(teacherName!="")
191 selectedTeachersListWidget->addItem(teacherName);
192 if(studentsSetName!="")
193 selectedStudentsListWidget->addItem(studentsSetName);
194 if(activityTagName!="")
195 selectedActivityTagsListWidget->addItem(activityTagName);
196 }
197
~AddActivityForm()198 AddActivityForm::~AddActivityForm()
199 {
200 saveFETDialogGeometry(this);
201
202 QSettings settings(COMPANY, PROGRAM);
203
204 settings.setValue(this->metaObject()->className()+QString("/show-subgroups-check-box-state"), subgroupsCheckBox->isChecked());
205 settings.setValue(this->metaObject()->className()+QString("/show-groups-check-box-state"), groupsCheckBox->isChecked());
206 settings.setValue(this->metaObject()->className()+QString("/show-years-check-box-state"), yearsCheckBox->isChecked());
207
208 settings.setValue(this->metaObject()->className()+QString("/qualified-teachers-radio-button-state"), qualifiedTeachersRadioButton->isChecked());
209 settings.setValue(this->metaObject()->className()+QString("/all-teachers-radio-button-state"), allTeachersRadioButton->isChecked());
210 }
211
populateSubactivitiesTabWidget(int n)212 void AddActivityForm::populateSubactivitiesTabWidget(int n)
213 {
214 int oldIndex=subactivitiesTabWidget->currentIndex();
215
216 int oldN=subactivitiesTabWidget->count();
217 if(oldN>n){
218 for(int i=oldN-1; i>=n; i--){
219 QWidget* wd=subactivitiesTabWidget->widget(i);
220 subactivitiesTabWidget->removeTab(i);
221 assert(durList.count()>0);
222 durList.removeLast();
223 assert(activList.count()>0);
224 activList.removeLast();
225 delete wd;
226 }
227
228 if(oldIndex<subactivitiesTabWidget->count())
229 subactivitiesTabWidget->setCurrentIndex(oldIndex);
230 else
231 subactivitiesTabWidget->setCurrentIndex(subactivitiesTabWidget->count()-1);
232 }
233 else if(oldN<n){
234 for(int i=oldN; i<n; i++){
235 QWidget* wd=new QWidget(subactivitiesTabWidget);
236
237 QCheckBox* cb=new QCheckBox(tr("Active"), wd);
238 cb->setChecked(true);
239 QSpinBox* sb=new QSpinBox(wd);
240 sb->setMinimum(1);
241 sb->setMaximum(MAX_HOURS_PER_DAY);
242 sb->setValue(1);
243 QLabel* ld=new QLabel(tr("Duration"), wd);
244
245 QHBoxLayout* hld=new QHBoxLayout();
246 hld->addWidget(ld);
247 hld->addWidget(sb);
248
249 QHBoxLayout* hla=new QHBoxLayout();
250 hla->addStretch();
251 hla->addWidget(cb);
252
253 QVBoxLayout* vl=new QVBoxLayout(wd);
254 vl->addLayout(hld);
255 vl->addLayout(hla);
256
257 subactivitiesTabWidget->addTab(wd, QString::number(i+1));
258
259 durList.append(sb);
260 activList.append(cb);
261 }
262
263 subactivitiesTabWidget->setCurrentIndex(subactivitiesTabWidget->count()-1);
264 }
265 }
266
allTeachersRadioButtonToggled(bool toggled)267 void AddActivityForm::allTeachersRadioButtonToggled(bool toggled)
268 {
269 if(toggled)
270 updateAllTeachersListWidget();
271 }
272
qualifiedTeachersRadioButtonToggled(bool toggled)273 void AddActivityForm::qualifiedTeachersRadioButtonToggled(bool toggled)
274 {
275 if(toggled)
276 updateAllTeachersListWidget();
277 }
278
updateAllTeachersListWidget()279 void AddActivityForm::updateAllTeachersListWidget()
280 {
281 allTeachersListWidget->clear();
282
283 for(int i=0; i<gt.rules.teachersList.size(); i++){
284 Teacher* tch=gt.rules.teachersList[i];
285 if(allTeachersRadioButton->isChecked() || subjectsComboBox->currentIndex()==-1){
286 allTeachersListWidget->addItem(tch->name);
287 }
288 else{
289 assert(qualifiedTeachersRadioButton->isChecked());
290 assert(subjectsComboBox->currentText()!="");
291 assert(subjectNamesSet.contains(subjectsComboBox->currentText()));
292 if(tch->qualifiedSubjectsHash.contains(subjectsComboBox->currentText())){
293 allTeachersListWidget->addItem(tch->name);
294 }
295 }
296 }
297 }
298
addTeacher()299 void AddActivityForm::addTeacher()
300 {
301 if(allTeachersListWidget->currentRow()<0 || allTeachersListWidget->currentRow()>=allTeachersListWidget->count())
302 return;
303
304 for(int i=0; i<selectedTeachersListWidget->count(); i++)
305 if(selectedTeachersListWidget->item(i)->text()==allTeachersListWidget->currentItem()->text())
306 return;
307
308 selectedTeachersListWidget->addItem(allTeachersListWidget->currentItem()->text());
309 selectedTeachersListWidget->setCurrentRow(selectedTeachersListWidget->count()-1);
310 }
311
removeTeacher()312 void AddActivityForm::removeTeacher()
313 {
314 if(selectedTeachersListWidget->count()<=0 || selectedTeachersListWidget->currentRow()<0 ||
315 selectedTeachersListWidget->currentRow()>=selectedTeachersListWidget->count())
316 return;
317
318 int i=selectedTeachersListWidget->currentRow();
319 selectedTeachersListWidget->setCurrentRow(-1);
320 QListWidgetItem* item=selectedTeachersListWidget->takeItem(i);
321 delete item;
322 if(i<selectedTeachersListWidget->count())
323 selectedTeachersListWidget->setCurrentRow(i);
324 else
325 selectedTeachersListWidget->setCurrentRow(selectedTeachersListWidget->count()-1);
326 }
327
addStudents()328 void AddActivityForm::addStudents()
329 {
330 if(allStudentsListWidget->currentRow()<0 || allStudentsListWidget->currentRow()>=allStudentsListWidget->count())
331 return;
332
333 assert(canonicalStudentsSetsNames.count()==allStudentsListWidget->count());
334 QString sn=canonicalStudentsSetsNames.at(allStudentsListWidget->currentRow());
335
336 for(int i=0; i<selectedStudentsListWidget->count(); i++)
337 if(selectedStudentsListWidget->item(i)->text()==sn)
338 return;
339
340 selectedStudentsListWidget->addItem(sn);
341 selectedStudentsListWidget->setCurrentRow(selectedStudentsListWidget->count()-1);
342 }
343
removeStudents()344 void AddActivityForm::removeStudents()
345 {
346 if(selectedStudentsListWidget->count()<=0 || selectedStudentsListWidget->currentRow()<0 ||
347 selectedStudentsListWidget->currentRow()>=selectedStudentsListWidget->count())
348 return;
349
350 int i=selectedStudentsListWidget->currentRow();
351 selectedStudentsListWidget->setCurrentRow(-1);
352 QListWidgetItem* item=selectedStudentsListWidget->takeItem(i);
353 delete item;
354 if(i<selectedStudentsListWidget->count())
355 selectedStudentsListWidget->setCurrentRow(i);
356 else
357 selectedStudentsListWidget->setCurrentRow(selectedStudentsListWidget->count()-1);
358 }
359
addActivityTag()360 void AddActivityForm::addActivityTag()
361 {
362 if(allActivityTagsListWidget->currentRow()<0 || allActivityTagsListWidget->currentRow()>=allActivityTagsListWidget->count())
363 return;
364
365 for(int i=0; i<selectedActivityTagsListWidget->count(); i++)
366 if(selectedActivityTagsListWidget->item(i)->text()==allActivityTagsListWidget->currentItem()->text())
367 return;
368
369 selectedActivityTagsListWidget->addItem(allActivityTagsListWidget->currentItem()->text());
370 selectedActivityTagsListWidget->setCurrentRow(selectedActivityTagsListWidget->count()-1);
371 }
372
removeActivityTag()373 void AddActivityForm::removeActivityTag()
374 {
375 if(selectedActivityTagsListWidget->count()<=0 || selectedActivityTagsListWidget->currentRow()<0 ||
376 selectedActivityTagsListWidget->currentRow()>=selectedActivityTagsListWidget->count())
377 return;
378
379 int i=selectedActivityTagsListWidget->currentRow();
380 selectedActivityTagsListWidget->setCurrentRow(-1);
381 QListWidgetItem* item=selectedActivityTagsListWidget->takeItem(i);
382 delete item;
383 if(i<selectedActivityTagsListWidget->count())
384 selectedActivityTagsListWidget->setCurrentRow(i);
385 else
386 selectedActivityTagsListWidget->setCurrentRow(selectedActivityTagsListWidget->count()-1);
387 }
388
updateSubjectsComboBox()389 void AddActivityForm::updateSubjectsComboBox()
390 {
391 subjectsComboBox->clear();
392 for(int i=0; i<gt.rules.subjectsList.size(); i++){
393 Subject* sbj=gt.rules.subjectsList[i];
394 subjectsComboBox->addItem(sbj->name);
395 }
396 }
397
updateActivityTagsListWidget()398 void AddActivityForm::updateActivityTagsListWidget()
399 {
400 allActivityTagsListWidget->clear();
401 for(int i=0; i<gt.rules.activityTagsList.size(); i++){
402 ActivityTag* at=gt.rules.activityTagsList[i];
403 allActivityTagsListWidget->addItem(at->name);
404 }
405
406 selectedActivityTagsListWidget->clear();
407 }
408
showYearsChanged()409 void AddActivityForm::showYearsChanged()
410 {
411 updateStudentsListWidget();
412 }
413
showGroupsChanged()414 void AddActivityForm::showGroupsChanged()
415 {
416 updateStudentsListWidget();
417 }
418
showSubgroupsChanged()419 void AddActivityForm::showSubgroupsChanged()
420 {
421 updateStudentsListWidget();
422 }
423
updateStudentsListWidget()424 void AddActivityForm::updateStudentsListWidget()
425 {
426 const int INDENT=2;
427
428 bool showYears=yearsCheckBox->isChecked();
429 bool showGroups=groupsCheckBox->isChecked();
430 bool showSubgroups=subgroupsCheckBox->isChecked();
431
432 allStudentsListWidget->clear();
433 canonicalStudentsSetsNames.clear();
434 for(int i=0; i<gt.rules.yearsList.size(); i++){
435 StudentsYear* sty=gt.rules.yearsList[i];
436 if(showYears){
437 allStudentsListWidget->addItem(sty->name);
438 canonicalStudentsSetsNames.append(sty->name);
439 }
440 for(int j=0; j<sty->groupsList.size(); j++){
441 StudentsGroup* stg=sty->groupsList[j];
442 if(showGroups){
443 QString begin=QString("");
444 QString end=QString("");
445 begin=QString(INDENT, ' ');
446 allStudentsListWidget->addItem(begin+stg->name+end);
447 canonicalStudentsSetsNames.append(stg->name);
448 }
449 if(showSubgroups) for(int k=0; k<stg->subgroupsList.size(); k++){
450 StudentsSubgroup* sts=stg->subgroupsList[k];
451
452 QString begin=QString("");
453 QString end=QString("");
454 begin=QString(2*INDENT, ' ');
455 allStudentsListWidget->addItem(begin+sts->name+end);
456 canonicalStudentsSetsNames.append(sts->name);
457 }
458 }
459 }
460
461 int q=allStudentsListWidget->verticalScrollBar()->minimum();
462 allStudentsListWidget->verticalScrollBar()->setValue(q);
463 }
464
splitChanged()465 void AddActivityForm::splitChanged()
466 {
467 int nSplit=splitSpinBox->value();
468
469 minDayDistanceTextLabel->setEnabled(nSplit>=2);
470 minDayDistanceSpinBox->setEnabled(nSplit>=2);
471 percentageTextLabel->setEnabled(nSplit>=2 && minDayDistanceSpinBox->value()>0);
472 percentageLineEdit->setEnabled(nSplit>=2 && minDayDistanceSpinBox->value()>0);
473 forceConsecutiveCheckBox->setEnabled(nSplit>=2 && minDayDistanceSpinBox->value()>0);
474
475 populateSubactivitiesTabWidget(nSplit);
476 }
477
SecondMinDaysDialog(QWidget * p,int minD,double w)478 SecondMinDaysDialog::SecondMinDaysDialog(QWidget* p, int minD, double w) :QDialog(p)
479 {
480 weight=-1;
481
482 QString l=tr
483 ("You selected min days between activities %1 (above 1) and weight %2 (under 100.0). "
484 "Would you like to add also a second constraint to ensure that almost certainly the "
485 "distance between activities is at least %3 (%1-1) days? If yes, please select weight (recommended "
486 "95.0%-100.0%) and click Yes. If no, please click No (only one constraint will be added)").arg(CustomFETString::number(minD)).arg(w).arg(minD-1);
487 l+="\n\n";
488 l+=tr("(Yes means to add an additional constraint min %1 days between activities, weight 0.0%-100.0%. "
489 "If you say Yes, you will have 2 constraints min days added for current activities. "
490 "Adding the second constraint might lead to impossible timetables if the condition is "
491 "too tight, but you can remove the second constraint at any time).").arg(minD-1);
492 l+="\n\n";
493 l+=tr("Note: 95% is usually enough for min days constraints referring to same activities. "
494 "The weights are cumulated if referring to the same activities. If you have 2 constraints with say 95%"
495 " (say min n days and min n-1 days), "
496 "the min n days constraint is skipped with probability 5%, then min n-1 days constraint is skipped with "
497 "probability 0.25%=5%*5%, so you'll get in 99.75% cases the min n-1 days constraint respected.");
498 l+="\n\n";
499 l+=tr("Recommended answer is Yes, 95% (or higher).");
500
501 setWindowTitle(tr("Add a second constraint or not?"));
502
503 QVBoxLayout* vl=new QVBoxLayout(this);
504
505 QPlainTextEdit* la=new QPlainTextEdit();
506 la->setPlainText(l);
507 la->setReadOnly(true);
508
509 vl->addWidget(la);
510
511 QPushButton* yes=new QPushButton(tr("Yes"));
512 yes->setDefault(true);
513
514 QPushButton* no=new QPushButton(tr("No"));
515
516 QLabel* percLabel=new QLabel(this);
517 percLabel->setText("Percentage");
518 percText=new QLineEdit(this);
519 percText->setText("95.0");
520
521 //QHBoxLayout* hl2=new QHBoxLayout(vl);
522 QHBoxLayout* hl2=new QHBoxLayout();
523 vl->addLayout(hl2);
524
525 //////
526 QLabel* minDaysLabel=new QLabel(this);
527 minDaysLabel->setText("Min days");
528 QSpinBox* minDaysSpinBox=new QSpinBox(this);
529 minDaysSpinBox->setMinimum(minD-1);
530 minDaysSpinBox->setMaximum(minD-1);
531 minDaysSpinBox->setValue(minD-1);
532 minDaysSpinBox->setEnabled(false);
533 //////
534
535 //////
536 hl2->addStretch(1);
537 hl2->addWidget(minDaysLabel);
538 hl2->addWidget(minDaysSpinBox);
539 //////
540
541 hl2->addStretch(1);
542 hl2->addWidget(percLabel);
543 hl2->addWidget(percText);
544
545 //QHBoxLayout* hl=new QHBoxLayout(vl);
546 QHBoxLayout* hl=new QHBoxLayout();
547 vl->addLayout(hl);
548
549 hl->addStretch(1);
550 hl->addWidget(yes);
551 hl->addWidget(no);
552
553 connect(yes, SIGNAL(clicked()), this, SLOT(yesPressed()));
554 connect(no, SIGNAL(clicked()), this, SLOT(reject()));
555
556 int ww=this->sizeHint().width();
557 if(ww>1000)
558 ww=1000;
559 if(ww<590)
560 ww=590;
561
562 int hh=this->sizeHint().height();
563 if(hh>650)
564 hh=650;
565 if(hh<380)
566 hh=380;
567
568 this->resize(ww, hh);
569 centerWidgetOnScreen(this);
570 restoreFETDialogGeometry(this);
571 }
572
~SecondMinDaysDialog()573 SecondMinDaysDialog::~SecondMinDaysDialog()
574 {
575 saveFETDialogGeometry(this);
576 }
577
yesPressed()578 void SecondMinDaysDialog::yesPressed()
579 {
580 double wt;
581 QString tmp=percText->text();
582 weight_sscanf(tmp, "%lf", &wt);
583 if(wt<0.0 || wt>100.0){
584 QMessageBox::warning(this, tr("FET information"),
585 tr("Invalid weight (percentage) - must be >=0 and <=100.0"));
586 return;
587 }
588 weight=wt;
589 accept();
590 }
591
addActivity()592 void AddActivityForm::addActivity()
593 {
594 if(studentsSeparatelyCheckBox->isChecked()){
595 addMultipleActivities();
596 return;
597 }
598
599 double weight;
600 QString tmp=percentageLineEdit->text();
601 weight_sscanf(tmp, "%lf", &weight);
602 if(percentageLineEdit->isEnabled() && (weight<0.0 || weight>100.0)){
603 QMessageBox::warning(this, tr("FET information"),
604 tr("Invalid weight (percentage) for added constraint min days between activities"));
605 return;
606 }
607
608 //subject
609 QString subject_name=subjectsComboBox->currentText();
610 bool found=subjectNamesSet.contains(subject_name);
611 /*int subject_index=gt.rules.searchSubject(subject_name);
612 if(subject_index<0){*/
613 if(!found){
614 QMessageBox::warning(this, tr("FET warning"),
615 tr("Invalid subject"));
616 return;
617 }
618
619 QStringList activity_tags_names;
620 for(int i=0; i<selectedActivityTagsListWidget->count(); i++){
621 //assert(gt.rules.searchActivityTag(selectedActivityTagsListWidget->item(i)->text())>=0);
622 assert(activityTagNamesSet.contains(selectedActivityTagsListWidget->item(i)->text()));
623 activity_tags_names.append(selectedActivityTagsListWidget->item(i)->text());
624 }
625
626 //teachers
627 QStringList teachers_names;
628 if(selectedTeachersListWidget->count()<=0){
629 int t=QMessageBox::question(this, tr("FET question"),
630 tr("Do you really want to add an activity without teacher(s)?"),
631 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
632
633 if(t==QMessageBox::No)
634 return;
635 }
636 else{
637 for(int i=0; i<selectedTeachersListWidget->count(); i++){
638 //assert(gt.rules.searchTeacher(selectedTeachersListWidget->item(i)->text())>=0);
639 assert(teacherNamesSet.contains(selectedTeachersListWidget->item(i)->text()));
640 teachers_names.append(selectedTeachersListWidget->item(i)->text());
641 }
642 }
643
644 //students
645 int numberOfStudents=0;
646 QStringList students_names;
647 if(selectedStudentsListWidget->count()<=0){
648 int t=QMessageBox::question(this, tr("FET question"),
649 tr("Do you really want to add an activity without student set(s)?"),
650 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
651
652 if(t==QMessageBox::No)
653 return;
654
655 //Crash bug fixed when adding an activity without students but with a specified number of students >= 1
656 //Reported by Zsolt Udvari
657 //Reported and fixed on 2017-07-25
658 if(nStudentsSpinBox->value()>=0)
659 numberOfStudents=nStudentsSpinBox->value();
660 }
661 else{
662 for(int i=0; i<selectedStudentsListWidget->count(); i++){
663 //assert(gt.rules.searchStudentsSet(selectedStudentsListWidget->item(i)->text())!=nullptr);
664 /*assert(numberOfStudentsHash.contains(selectedStudentsListWidget->item(i)->text()));
665 numberOfStudents+=numberOfStudentsHash.value(selectedStudentsListWidget->item(i)->text());*/
666 assert(gt.rules.permanentStudentsHash.contains(selectedStudentsListWidget->item(i)->text()));
667 numberOfStudents+=gt.rules.permanentStudentsHash.value(selectedStudentsListWidget->item(i)->text())->numberOfStudents;
668 students_names.append(selectedStudentsListWidget->item(i)->text());
669 }
670
671 if(nStudentsSpinBox->value()>=0)
672 numberOfStudents=nStudentsSpinBox->value();
673 }
674
675 if(splitSpinBox->value()==1){ //indivisible activity
676 int duration=dur(0)->value();
677 if(duration<0){
678 QMessageBox::warning(this, tr("FET information"),
679 tr("Invalid duration"));
680 return;
681 }
682
683 bool active=false;
684 if(activ(0)->isChecked())
685 active=true;
686
687 int activityid=0; //We set the id of this newly added activity = (the largest existing id + 1)
688 for(int i=0; i<gt.rules.activitiesList.size(); i++){
689 Activity* act=gt.rules.activitiesList[i];
690 if(act->id > activityid)
691 activityid = act->id;
692 }
693 activityid++;
694 Activity a(gt.rules, activityid, 0, teachers_names, subject_name, activity_tags_names, students_names,
695 duration, duration, /*parity,*/ active, (nStudentsSpinBox->value()==-1), nStudentsSpinBox->value(), numberOfStudents);
696
697 bool already_existing=false;
698 for(int i=0; i<gt.rules.activitiesList.size(); i++){
699 Activity* act=gt.rules.activitiesList[i];
700 if((*act)==a)
701 already_existing=true;
702 }
703
704 if(already_existing){
705 /*int t=QMessageBox::question(this, tr("FET question"),
706 tr("A similar activity already exists. Do you want to insert current activity?"),
707 tr("Yes"),tr("No"));
708 assert(t==0 || t==1 || t==-1);
709 if(t==1) //no pressed
710 return;
711 if(t==-1) //Esc pressed
712 return;*/
713 QMessageBox::StandardButton t=QMessageBox::question(this, tr("FET question"),
714 tr("A similar activity already exists. Do you want to insert current activity?"),
715 QMessageBox::Yes | QMessageBox::No);
716 if(t==QMessageBox::No)
717 return;
718 }
719
720 bool tmp=gt.rules.addSimpleActivityFast(this, activityid, 0, teachers_names, subject_name, activity_tags_names,
721 students_names, duration, duration, active,
722 (nStudentsSpinBox->value()==-1), nStudentsSpinBox->value(), numberOfStudents);
723 if(tmp)
724 QMessageBox::information(this, tr("FET information"), tr("Activity added"));
725 else
726 QMessageBox::critical(this, tr("FET information"), tr("Activity NOT added - please report error"));
727 }
728 else{ //split activity
729 if(gt.rules.mode!=MORNINGS_AFTERNOONS){
730 if(minDayDistanceSpinBox->value()>0 && splitSpinBox->value()>gt.rules.nDaysPerWeek){
731 int t=LongTextMessageBox::largeConfirmation(this, tr("FET confirmation"),
732 tr("Possible incorrect setting. Are you sure you want to add current activity? See details below:")+"\n\n"+
733 tr("You want to add a container activity split into more than the number of days per week and also add a constraint min days between activities."
734 " 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).")+
735 "\n\n"+
736 tr("The best way to add the activities would be:")+
737 "\n\n"+
738 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"
739 ". 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"
740 " (possibly raising the weight of added constraint min days between activities up to 100%)")+
741 "\n\n"+
742 tr("2. If you don't add 'force consecutive if same day', then add a larger activity split into a number of"
743 " activities equal with the number of days per week and the remaining components into other larger split activity."
744 " For example, suppose you need to add 7 activities with duration 1 in a 5 days week. Add 2 larger container activities,"
745 " first one split into 5 activities with duration 1 and second one split into 2 activities with duration 1"
746 " (possibly raising the weight of added constraints min days between activities for each of the 2 containers up to 100%)")+
747 "\n\n"+
748 tr("Do you want to add current activities as they are now (not recommended) or cancel and edit them as instructed?")
749 ,
750 tr("Yes"), tr("No"), QString(), 0, 1);
751
752 if(t==1)
753 return;
754 }
755 }
756 else{
757 if(minDayDistanceSpinBox->value()>0 && splitSpinBox->value()>gt.rules.nDaysPerWeek/2){
758 int t=LongTextMessageBox::largeConfirmation(this, tr("FET confirmation"),
759 tr("Possible incorrect setting. Are you sure you want to add current activity? See details below:")+"\n\n"+
760 tr("You want to add a container activity split into more than the number of real days per week and also add a constraint min days between activities."
761 " 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).")+
762 "\n\n"+
763 tr("The best way to add the activities would be:")+
764 "\n\n"+
765 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 real days per week"
766 ". Example: 7 activities with duration 1 in a 5 real days week, then transform into 5 activities with durations: 2,2,1,1,1 and add a single container activity with these 5 components"
767 " (possibly raising the weight of added constraint min days between activities up to 100%)")+
768 "\n\n"+
769 tr("2. If you don't add 'force consecutive if same day', then add a larger activity split into a number of"
770 " activities equal with the number of real days per week and the remaining components into other larger split activity."
771 " For example, suppose you need to add 7 activities with duration 1 in a 5 real days week. Add 2 larger container activities,"
772 " first one split into 5 activities with duration 1 and second one split into 2 activities with duration 1"
773 " (possibly raising the weight of added constraints min days between activities for each of the 2 containers up to 100%)")+
774 "\n\n"+
775 tr("Do you want to add current activities as they are now (not recommended) or cancel and edit them as instructed?")
776 ,
777 tr("Yes"), tr("No"), QString(), 0, 1);
778
779 if(t==1)
780 return;
781 }
782 }
783
784 int totalduration;
785 QList<int> durations;
786 QList<bool> active;
787 int nsplit=splitSpinBox->value();
788
789 totalduration=0;
790 for(int i=0; i<nsplit; i++){
791 durations.append(dur(i)->value());
792 active.append(false);
793 if(activ(i)->isChecked())
794 active[i]=true;
795
796 totalduration+=durations[i];
797 }
798
799 //the group id of this split activity and the id of the first partial activity
800 //it is the maximum already existing id + 1
801 int firstactivityid=0;
802 for(int i=0; i<gt.rules.activitiesList.size(); i++){
803 Activity* act=gt.rules.activitiesList[i];
804 if(act->id > firstactivityid)
805 firstactivityid = act->id;
806 }
807 firstactivityid++;
808
809 int minD=minDayDistanceSpinBox->value();
810 bool tmp=gt.rules.addSplitActivityFast(this, firstactivityid, firstactivityid,
811 teachers_names, subject_name, activity_tags_names, students_names,
812 nsplit, totalduration, durations,
813 active, minD, weight, forceConsecutiveCheckBox->isChecked(),
814 (nStudentsSpinBox->value()==-1), nStudentsSpinBox->value(), numberOfStudents);
815 if(tmp){
816 if(minD>1 && weight<100.0){
817 SecondMinDaysDialog second(this, minD, weight);
818 setParentAndOtherThings(&second, this);
819 int code=second.exec();
820
821 if(code==QDialog::Accepted){
822 assert(second.weight>=0 && second.weight<=100.0);
823 QList<int> acts;
824 for(int i=0; i<nsplit; i++){
825 acts.append(firstactivityid+i);
826 }
827 TimeConstraint* c=new ConstraintMinDaysBetweenActivities(second.weight, forceConsecutiveCheckBox->isChecked(), nsplit, acts, minD-1);
828 bool tmp=gt.rules.addTimeConstraint(c);
829 assert(tmp);
830 }
831 }
832
833 QMessageBox::information(this, tr("FET information"), tr("Split activity added."
834 " Please note that FET currently cannot check for duplicates when adding split activities"
835 ". It is advisable to check the statistics after adding all the activities"));
836 }
837 else
838 QMessageBox::critical(this, tr("FET information"), tr("Split activity NOT added - error???"));
839 }
840
841 PlanningChanged::increasePlanningCommunicationSpinBox();
842 }
843
addMultipleActivities()844 void AddActivityForm::addMultipleActivities()
845 {
846 assert(studentsSeparatelyCheckBox->isChecked());
847
848 double weight;
849 QString tmp=percentageLineEdit->text();
850 weight_sscanf(tmp, "%lf", &weight);
851 if(percentageLineEdit->isEnabled() && (weight<0.0 || weight>100.0)){
852 QMessageBox::warning(this, tr("FET information"),
853 tr("Invalid weight (percentage) for added constraint min days between activities"));
854 return;
855 }
856
857 //subject
858 QString subject_name=subjectsComboBox->currentText();
859 bool found=subjectNamesSet.contains(subject_name);
860 /*int subject_index=gt.rules.searchSubject(subject_name);
861 if(subject_index<0){*/
862 if(!found){
863 QMessageBox::warning(this, tr("FET warning"),
864 tr("Invalid subject"));
865 return;
866 }
867
868 QStringList activity_tags_names;
869 for(int i=0; i<selectedActivityTagsListWidget->count(); i++){
870 //assert(gt.rules.searchActivityTag(selectedActivityTagsListWidget->item(i)->text())>=0);
871 assert(activityTagNamesSet.contains(selectedActivityTagsListWidget->item(i)->text()));
872 activity_tags_names.append(selectedActivityTagsListWidget->item(i)->text());
873 }
874
875 //teachers
876 QStringList teachers_names;
877 if(selectedTeachersListWidget->count()<=0){
878 int t=QMessageBox::question(this, tr("FET question"),
879 tr("Do you really want to add an activity without teacher(s)?"),
880 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
881
882 if(t==QMessageBox::No)
883 return;
884 }
885 else{
886 for(int i=0; i<selectedTeachersListWidget->count(); i++){
887 //assert(gt.rules.searchTeacher(selectedTeachersListWidget->item(i)->text())>=0);
888 assert(teacherNamesSet.contains(selectedTeachersListWidget->item(i)->text()));
889 teachers_names.append(selectedTeachersListWidget->item(i)->text());
890 }
891 }
892
893 //students
894 //int numberOfStudents=0;
895 QStringList students_names;
896 if(selectedStudentsListWidget->count()<=1){
897 QMessageBox::warning(this, tr("FET question"),
898 tr("If you intend to add multiple activities for students, please select at least 2 students sets."));
899
900 return;
901 }
902 else{
903 for(int i=0; i<selectedStudentsListWidget->count(); i++){
904 assert(gt.rules.permanentStudentsHash.contains(selectedStudentsListWidget->item(i)->text()));
905 students_names.append(selectedStudentsListWidget->item(i)->text());
906 }
907 }
908
909 if(splitSpinBox->value()==1){ //indivisible activity
910 int duration=dur(0)->value();
911 if(duration<0){
912 QMessageBox::warning(this, tr("FET information"),
913 tr("Invalid duration"));
914 return;
915 }
916
917 bool active=false;
918 if(activ(0)->isChecked())
919 active=true;
920
921 int activityid=0; //We set the id of this newly added activity = (the largest existing id + 1)
922 for(int i=0; i<gt.rules.activitiesList.size(); i++){
923 Activity* act=gt.rules.activitiesList[i];
924 if(act->id > activityid)
925 activityid = act->id;
926 }
927
928 int cnt_act=0;
929 activityid++;
930
931 for(int st=0; st<students_names.count(); st++){
932 int n1=nStudentsSpinBox->value();
933 int n2;
934 if(n1==-1)
935 n2=gt.rules.permanentStudentsHash.value(students_names.at(st))->numberOfStudents;
936 else
937 n2=nStudentsSpinBox->value();
938
939 Activity a(gt.rules, activityid, 0, teachers_names, subject_name, activity_tags_names, QStringList(students_names.at(st)),
940 duration, duration, /*parity,*/ active, (nStudentsSpinBox->value()==-1), n1, n2);
941
942 bool already_existing=false;
943 for(int i=0; i<gt.rules.activitiesList.size(); i++){
944 Activity* act=gt.rules.activitiesList[i];
945 if((*act)==a)
946 already_existing=true;
947 }
948
949 if(already_existing){
950 /*int t=QMessageBox::question(this, tr("FET question"),
951 tr("A similar activity for students set %1 already exists. Do you want to insert current activity?").arg(students_names.at(st)),
952 tr("Yes"),tr("No"));
953 assert(t==0 || t==1 ||t==-1);
954 if(t==1){ //no pressed
955 continue;
956 }
957 if(t==-1){ //Esc pressed
958 continue;
959 }*/
960
961 QMessageBox::StandardButton t=QMessageBox::question(this, tr("FET question"),
962 tr("A similar activity for students set %1 already exists. Do you want to insert current activity?").arg(students_names.at(st)),
963 QMessageBox::Yes | QMessageBox::No);
964 if(t==QMessageBox::No)
965 return;
966 }
967
968 bool tmp=gt.rules.addSimpleActivityFast(this, activityid, 0, teachers_names, subject_name, activity_tags_names,
969 QStringList(students_names.at(st)), duration, duration, active,
970 (nStudentsSpinBox->value()==-1), n1, n2);
971 if(tmp){
972 cnt_act++;
973 activityid++;
974 }
975 }
976 QMessageBox::information(this, tr("FET information"), tr("%1 activities added").arg(cnt_act));
977 }
978 else{ //split activity
979 if(gt.rules.mode!=MORNINGS_AFTERNOONS){
980 if(minDayDistanceSpinBox->value()>0 && splitSpinBox->value()>gt.rules.nDaysPerWeek){
981 int t=LongTextMessageBox::largeConfirmation(this, tr("FET confirmation"),
982 tr("Possible incorrect setting. Are you sure you want to add current activity? See details below:")+"\n\n"+
983 tr("You want to add a container activity split into more than the number of days per week and also add a constraint min days between activities."
984 " 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).")+
985 "\n\n"+
986 tr("The best way to add the activities would be:")+
987 "\n\n"+
988 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"
989 ". 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"
990 " (possibly raising the weight of added constraint min days between activities up to 100%)")+
991 "\n\n"+
992 tr("2. If you don't add 'force consecutive if same day', then add a larger activity split into a number of"
993 " activities equal with the number of days per week and the remaining components into other larger split activity."
994 " For example, suppose you need to add 7 activities with duration 1 in a 5 days week. Add 2 larger container activities,"
995 " first one split into 5 activities with duration 1 and second one split into 2 activities with duration 1"
996 " (possibly raising the weight of added constraints min days between activities for each of the 2 containers up to 100%)")+
997 "\n\n"+
998 tr("Do you want to add current activities as they are now (not recommended) or cancel and edit them as instructed?")
999 ,
1000 tr("Yes"), tr("No"), QString(), 0, 1);
1001
1002 if(t==1)
1003 return;
1004 }
1005 }
1006 else{
1007 if(minDayDistanceSpinBox->value()>0 && splitSpinBox->value()>gt.rules.nDaysPerWeek/2){
1008 int t=LongTextMessageBox::largeConfirmation(this, tr("FET confirmation"),
1009 tr("Possible incorrect setting. Are you sure you want to add current activity? See details below:")+"\n\n"+
1010 tr("You want to add a container activity split into more than the number of real days per week and also add a constraint min days between activities."
1011 " 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).")+
1012 "\n\n"+
1013 tr("The best way to add the activities would be:")+
1014 "\n\n"+
1015 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"
1016 ". Example: 7 activities with duration 1 in a 5 real days week, then transform into 5 activities with durations: 2,2,1,1,1 and add a single container activity with these 5 components"
1017 " (possibly raising the weight of added constraint min days between activities up to 100%)")+
1018 "\n\n"+
1019 tr("2. If you don't add 'force consecutive if same day', then add a larger activity split into a number of"
1020 " activities equal with the number of days per week and the remaining components into other larger split activity."
1021 " For example, suppose you need to add 7 activities with duration 1 in a 5 real days week. Add 2 larger container activities,"
1022 " first one split into 5 activities with duration 1 and second one split into 2 activities with duration 1"
1023 " (possibly raising the weight of added constraints min days between activities for each of the 2 containers up to 100%)")+
1024 "\n\n"+
1025 tr("Do you want to add current activities as they are now (not recommended) or cancel and edit them as instructed?")
1026 ,
1027 tr("Yes"), tr("No"), QString(), 0, 1);
1028
1029 if(t==1)
1030 return;
1031 }
1032 }
1033
1034 int totalduration;
1035 QList<int> durations;
1036 QList<bool> active;
1037 int nsplit=splitSpinBox->value();
1038
1039 totalduration=0;
1040 for(int i=0; i<nsplit; i++){
1041 durations.append(dur(i)->value());
1042 active.append(false);
1043 if(activ(i)->isChecked())
1044 active[i]=true;
1045
1046 totalduration+=durations[i];
1047 }
1048
1049 //the group id of this split activity and the id of the first partial activity
1050 //it is the maximum already existing id + 1
1051 int firstactivityid=0;
1052 for(int i=0; i<gt.rules.activitiesList.size(); i++){
1053 Activity* act=gt.rules.activitiesList[i];
1054 if(act->id > firstactivityid)
1055 firstactivityid = act->id;
1056 }
1057 firstactivityid++;
1058
1059 int cnt_act=0;
1060
1061 bool begin=true;
1062 bool addSecondConstraint=false;
1063 double secondWeight=-1.0;
1064
1065 for(int st=0; st<students_names.count(); st++){
1066 int n1=nStudentsSpinBox->value();
1067 int n2;
1068 if(n1==-1)
1069 n2=gt.rules.permanentStudentsHash.value(students_names.at(st))->numberOfStudents;
1070 else
1071 n2=nStudentsSpinBox->value();
1072
1073 int minD=minDayDistanceSpinBox->value();
1074 bool tmp=gt.rules.addSplitActivityFast(this, firstactivityid, firstactivityid,
1075 teachers_names, subject_name, activity_tags_names, QStringList(students_names.at(st)),
1076 nsplit, totalduration, durations,
1077 active, minD, weight, forceConsecutiveCheckBox->isChecked(),
1078 (nStudentsSpinBox->value()==-1), n1, n2);
1079 if(tmp){
1080 firstactivityid+=nsplit;
1081 cnt_act++;
1082 }
1083
1084 if(tmp && begin){
1085 begin=false;
1086 if(minD>1 && weight<100.0){
1087 SecondMinDaysDialog second(this, minD, weight);
1088 setParentAndOtherThings(&second, this);
1089 int code=second.exec();
1090
1091 if(code==QDialog::Accepted){
1092 addSecondConstraint=true;
1093 assert(second.weight>=0 && second.weight<=100.0);
1094 secondWeight=second.weight;
1095 QList<int> acts;
1096 for(int i=0; i<nsplit; i++){
1097 acts.append(firstactivityid+i-nsplit);
1098 }
1099 TimeConstraint* c=new ConstraintMinDaysBetweenActivities(secondWeight, forceConsecutiveCheckBox->isChecked(), nsplit, acts, minD-1);
1100 bool tmp=gt.rules.addTimeConstraint(c);
1101 assert(tmp);
1102 }
1103 else
1104 addSecondConstraint=false;
1105 }
1106 }
1107 else if(tmp){
1108 if(addSecondConstraint){
1109 assert(secondWeight>=0 && secondWeight<=100.0);
1110 QList<int> acts;
1111 for(int i=0; i<nsplit; i++){
1112 acts.append(firstactivityid+i-nsplit);
1113 }
1114 TimeConstraint* c=new ConstraintMinDaysBetweenActivities(secondWeight, forceConsecutiveCheckBox->isChecked(), nsplit, acts, minD-1);
1115 bool tmp=gt.rules.addTimeConstraint(c);
1116 assert(tmp);
1117 }
1118 }
1119 }
1120 QMessageBox::information(this, tr("FET information"), tr("%1 split activities added").arg(cnt_act));
1121 }
1122
1123 PlanningChanged::increasePlanningCommunicationSpinBox();
1124 }
1125
clearTeachers()1126 void AddActivityForm::clearTeachers()
1127 {
1128 selectedTeachersListWidget->clear();
1129 }
1130
clearStudents()1131 void AddActivityForm::clearStudents()
1132 {
1133 selectedStudentsListWidget->clear();
1134 }
1135
clearActivityTags()1136 void AddActivityForm::clearActivityTags()
1137 {
1138 selectedActivityTagsListWidget->clear();
1139 }
1140
help()1141 void AddActivityForm::help()
1142 {
1143 QString s;
1144
1145 s+=tr("Abbreviations in this dialog:");
1146 s+="\n\n";
1147 s+=tr("'Students' (the text near the spin box), means 'Number of students (-1 for automatic)'");
1148 s+="\n";
1149 s+=tr("'Split' means 'Split into ... activities per week'");
1150 s+="\n";
1151 s+=tr("'Min days' means 'The minimum required distance in days between each pair of activities'");
1152 s+="\n";
1153 s+=tr("'Weight %' means 'Percentage of added constraint (min days between activities constraint). Recommended: 95.0%-100.0%'");
1154 s+="\n";
1155 s+=tr("'Consecutive' means 'If activities on same day, force consecutive?'");
1156 s+="\n";
1157 s+=tr("The 'Duration' spin box and the 'Active' check box refer to each component of current activity, you can change "
1158 "them for each component, separately, by selecting the corresponding tab in the tab widget.");
1159 s+="\n";
1160 s+=tr("'Qualified' means that only the teachers who are qualified to teach the selected subject will be shown in the 'Teachers' list.",
1161 "Qualified refers to teachers");
1162 s+="\n";
1163 s+=tr("'Separately' means that multiple activities will be added, one for each selected students set, separately.");
1164 s+="\n\n";
1165
1166 s+=tr("A first notice: "
1167 "If you use a 5 days week: "
1168 "when adding an activity split into only 2 components "
1169 "per week, the best practice is to add min days between activities to be 2. "
1170 "If you split an activity into 3 components per week - please read FAQ question Q1-5-September-2008");
1171 s+="\n\n";
1172
1173 s+=tr("You can select a teacher from all the teachers with the mouse or with the keyboard tab/up/down, then "
1174 "double click it to add it to the selected teachers for current activity. "
1175 "You can then choose to remove a teacher from the selected teachers. You can highlight it "
1176 "with the mouse or with the keyboard, then double click it to remove this teacher from the selected teachers.");
1177
1178 s+="\n\n";
1179
1180 s+=tr("The same procedure (double click) applies to students sets and activity tags.");
1181
1182 s+="\n\n";
1183
1184 s+=tr("You can check/uncheck show years, show groups or show subgroups.");
1185 s+="\n\n";
1186
1187 s+=tr("If you split a larger activity into more activities per week, you have a multitude of choices:\n"
1188 "You can choose the minimum distance in days between each pair of activities."
1189 " Please note that a minimum distance of 1 means that the activities must not be in the same day, "
1190 "a minimum distance of 2 means that the activities must be separated by one day (distance from Monday"
1191 " to Wednesday for instance is 2 days), etc.");
1192
1193 s+="\n\n";
1194
1195 s+=tr("If you have for instance an activity with 2 lessons per week and you want to spread them to at "
1196 "least 2 days distance, you can add a constraint min days with min days = 2 and weight 95% "
1197 "(or higher). If you want also to ensure that activities will "
1198 "be separated by at least one day, you can use this feature: "
1199 "add a constraint min days with minimum days 2 and weight 95% or lower, and after that you'll get "
1200 "the possibility to add another constraint with min 1 days and weight 95% or higher. "
1201 "It works if you first select in the dialog the min days >= 2 and click Add activities. Or you can add manually the constraints "
1202 "(difficult this way). "
1203 "Important: it is best practice to consider both constraints to have 95% weight. The combination assures that "
1204 "the resultant is 99.75% weight");
1205
1206 s+="\n\n";
1207
1208 s+=tr("Please note that the min days distance is a time constraint and you can only see/modify it in the "
1209 "time constraints dialogs, not in the modify activity dialog. Additionally, you can see the constraints "
1210 "for each activity in the details text box of each activity");
1211
1212 s+="\n\n";
1213
1214 s+=tr("If you choose a value greater or equal with 1 for min days, a time constraint min days between activities will be added automatically "
1215 "(you can see this constraint in the time constraints list or you can see this constraint in the "
1216 "detailed description of the activity). You can select a weight percentage for this constraint. "
1217 "If you select 100%, the constraint must be respected all the time. If you select 95%, there is a small chance "
1218 "that the timetable will not respect this constraint. Recommended values are 95.0%-100.0% (maybe you could try "
1219 "with 95%, then 99.75%, or even 100.0%, but the generation time might be larger). Generally, 99.75% might be a good value. "
1220 "Note: if you put a value less than 100% and the constraint is too tough, FET is able to find that this constraint "
1221 "is impossible and will break it. 99.75% might be better than 95% but possibly slower. The percentage is subjective "
1222 "(if you put 95% you may get 6 soft conflicts and if you put 99.75% you may get 3 soft conflicts). "
1223 "Starting with FET-5.3.6, it is possible to change this value for all constraints in one click, in constraint min days"
1224 " between activities dialog.");
1225
1226 s+="\n\n";
1227
1228 s+=tr("There is another option, if the activities are in the same day, force consecutive activities. You can select "
1229 "this option for instance if you have 5 lessons of math in 5 days, and there is no timetable which respects "
1230 "fully the days separation. Then, you can set the weight percent of the min days constraint to 95% and "
1231 "add consecutive if same day. You will have as results say 3 lessons with duration 1 and a 2 hours lesson in another day. "
1232 "Please be careful: if the activities are on the same day, even if the constraint has 0% weight, then the activities are forced to be "
1233 "consecutive.");
1234
1235 s+="\n\n";
1236
1237 s+=tr("Current algorithm cannot schedule 3 activities in the same day if consecutive is checked, so "
1238 "you will get no solution in such extreme cases (for instance, if you have 3 lessons and a teacher which works only 1 day per week, "
1239 "and select 'force consecutive if same day', you will get an imposssible timetable. But these are extremely unlikely cases).");
1240
1241 s+="\n\n";
1242
1243 s+=tr("Note: You cannot add 'consecutive if same day' with min days=0. If you want this, you have to add "
1244 "min days at least 1 (and any weight percentage).");
1245
1246 s+="\n\n";
1247
1248 s+=tr("Starting with version 5.0.0, it is possible to add activities with no students or no teachers");
1249
1250 s+="\n\n";
1251
1252 s+=tr("If you select a number of min days above 1 (say this number is n), you will get the possibility "
1253 "to add a second constraint min days between activities, with min days = n-1 and a percentage of your choice. Just click "
1254 "Add activities");
1255
1256 //show the message in a dialog
1257 QDialog dialog(this);
1258
1259 dialog.setWindowTitle(tr("FET - help on adding activity(ies)"));
1260
1261 QVBoxLayout* vl=new QVBoxLayout(&dialog);
1262 QPlainTextEdit* te=new QPlainTextEdit();
1263 te->setPlainText(s);
1264 te->setReadOnly(true);
1265 QPushButton* pb=new QPushButton(tr("OK"));
1266
1267 QHBoxLayout* hl=new QHBoxLayout(0);
1268 hl->addStretch(1);
1269 hl->addWidget(pb);
1270
1271 vl->addWidget(te);
1272 vl->addLayout(hl);
1273 connect(pb, SIGNAL(clicked()), &dialog, SLOT(close()));
1274
1275 dialog.resize(700,500);
1276 centerWidgetOnScreen(&dialog);
1277
1278 setParentAndOtherThings(&dialog, this);
1279 dialog.exec();
1280 }
1281
minDaysChanged()1282 void AddActivityForm::minDaysChanged()
1283 {
1284 percentageTextLabel->setEnabled(splitSpinBox->value()>=2 && minDayDistanceSpinBox->value()>0);
1285 percentageLineEdit->setEnabled(splitSpinBox->value()>=2 && minDayDistanceSpinBox->value()>0);
1286 forceConsecutiveCheckBox->setEnabled(splitSpinBox->value()>=2 && minDayDistanceSpinBox->value()>0);
1287 }
1288