1 /***************************************************************************
2                           subactivitiesform.cpp  -  description
3                              -------------------
4     begin                : 2009
5     copyright            : (C) 2009 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 "timetable_defs.h"
19 #include "fet.h"
20 #include "timetable.h"
21 
22 #include "studentsset.h"
23 
24 #include "subactivitiesform.h"
25 #include "modifysubactivityform.h"
26 
27 #include <QString>
28 #include <QMessageBox>
29 
30 #include <QListWidget>
31 #include <QScrollBar>
32 
33 #include <QAbstractItemView>
34 
35 #include "longtextmessagebox.h"
36 
37 #include <QBrush>
38 #include <QPalette>
39 
40 #include <QSplitter>
41 #include <QSettings>
42 #include <QObject>
43 #include <QMetaObject>
44 
45 extern const QString COMPANY;
46 extern const QString PROGRAM;
47 
SubactivitiesForm(QWidget * parent,const QString & teacherName,const QString & studentsSetName,const QString & subjectName,const QString & activityTagName)48 SubactivitiesForm::SubactivitiesForm(QWidget* parent, const QString& teacherName, const QString& studentsSetName, const QString& subjectName, const QString& activityTagName): QDialog(parent)
49 {
50 	setupUi(this);
51 
52 	subactivityTextEdit->setReadOnly(true);
53 
54 	modifySubactivityPushButton->setDefault(true);
55 
56 	subactivitiesListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
57 
58 	centerWidgetOnScreen(this);
59 	restoreFETDialogGeometry(this);
60 	//restore splitter state
61 	QSettings settings(COMPANY, PROGRAM);
62 	if(settings.contains(this->metaObject()->className()+QString("/splitter-state")))
63 		splitter->restoreState(settings.value(this->metaObject()->className()+QString("/splitter-state")).toByteArray());
64 	showRelatedCheckBox->setChecked(settings.value(this->metaObject()->className()+QString("/show-related-check-box-state"), "false").toBool());
65 
66 	connect(subactivitiesListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(subactivityChanged()));
67 	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
68 
69 	connect(modifySubactivityPushButton, SIGNAL(clicked()), this, SLOT(modifySubactivity()));
70 	connect(subactivitiesListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(modifySubactivity()));
71 	connect(showRelatedCheckBox, SIGNAL(toggled(bool)), this, SLOT(studentsFilterChanged()));
72 	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
73 	connect(commentsPushButton, SIGNAL(clicked()), this, SLOT(subactivityComments()));
74 
75 	QSize tmp1=teachersComboBox->minimumSizeHint();
76 	Q_UNUSED(tmp1);
77 	QSize tmp2=studentsComboBox->minimumSizeHint();
78 	Q_UNUSED(tmp2);
79 	QSize tmp3=subjectsComboBox->minimumSizeHint();
80 	Q_UNUSED(tmp3);
81 	QSize tmp4=activityTagsComboBox->minimumSizeHint();
82 	Q_UNUSED(tmp4);
83 
84 	teachersComboBox->addItem("");
85 	int cit=0;
86 	for(int i=0; i<gt.rules.teachersList.size(); i++){
87 		Teacher* tch=gt.rules.teachersList[i];
88 		teachersComboBox->addItem(tch->name);
89 		if(tch->name==teacherName)
90 			cit=i+1;
91 	}
92 	teachersComboBox->setCurrentIndex(cit);
93 
94 	subjectsComboBox->addItem("");
95 	int cisu=0;
96 	for(int i=0; i<gt.rules.subjectsList.size(); i++){
97 		Subject* sb=gt.rules.subjectsList[i];
98 		subjectsComboBox->addItem(sb->name);
99 		if(sb->name==subjectName)
100 			cisu=i+1;
101 	}
102 	subjectsComboBox->setCurrentIndex(cisu);
103 
104 	activityTagsComboBox->addItem("");
105 	int ciat=0;
106 	for(int i=0; i<gt.rules.activityTagsList.size(); i++){
107 		ActivityTag* st=gt.rules.activityTagsList[i];
108 		activityTagsComboBox->addItem(st->name);
109 		if(st->name==activityTagName)
110 			ciat=i+1;
111 	}
112 	activityTagsComboBox->setCurrentIndex(ciat);
113 
114 	int cist=populateStudentsComboBox(studentsComboBox, studentsSetName, true);
115 	if(studentsSetName==""){
116 		assert(cist==0);
117 		studentsComboBox->setCurrentIndex(0);
118 
119 		showedStudents.clear();
120 		showedStudents.insert("");
121 
122 		this->filterChanged();
123 	}
124 	else{
125 		assert(cist!=0);
126 
127 		if(cist==-1){
128 			studentsComboBox->setCurrentIndex(0);
129 
130 			showWarningForInvisibleSubgroupActivity(parent, studentsSetName);
131 
132 			showedStudents.clear();
133 			showedStudents.insert("");
134 
135 			this->filterChanged();
136 		}
137 		else{
138 			studentsComboBox->setCurrentIndex(cist);
139 
140 			this->studentsFilterChanged();
141 		}
142 	}
143 
144 	/*studentsComboBox->setCurrentIndex(cist);
145 
146 	if(studentsSetName!=""){
147 		if(cist==0){
148 			showWarningForInvisibleSubgroupActivity(parent, studentsSetName);
149 
150 			showedStudents.clear();
151 			showedStudents.insert("");
152 
153 			this->filterChanged();
154 		}
155 		else{
156 			this->studentsFilterChanged();
157 		}
158 	}
159 	else{
160 		showedStudents.clear();
161 		showedStudents.insert("");
162 
163 		this->filterChanged();
164 	}*/
165 
166 	connect(teachersComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
167 	connect(studentsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(studentsFilterChanged()));
168 	connect(subjectsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
169 	connect(activityTagsComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(filterChanged()));
170 }
171 
~SubactivitiesForm()172 SubactivitiesForm::~SubactivitiesForm()
173 {
174 	saveFETDialogGeometry(this);
175 	//save splitter state
176 	QSettings settings(COMPANY, PROGRAM);
177 	settings.setValue(this->metaObject()->className()+QString("/splitter-state"), splitter->saveState());
178 
179 	settings.setValue(this->metaObject()->className()+QString("/show-related-check-box-state"), showRelatedCheckBox->isChecked());
180 }
181 
filterOk(Activity * act)182 bool SubactivitiesForm::filterOk(Activity* act)
183 {
184 	QString tn=teachersComboBox->currentText();
185 	QString stn=studentsComboBox->currentText();
186 	QString sbn=subjectsComboBox->currentText();
187 	QString atn=activityTagsComboBox->currentText();
188 	int ok=true;
189 
190 	//teacher
191 	if(tn!=""){
192 		bool ok2=false;
193 		for(QStringList::const_iterator it=act->teachersNames.constBegin(); it!=act->teachersNames.constEnd(); it++)
194 			if(*it == tn){
195 				ok2=true;
196 				break;
197 			}
198 		if(!ok2)
199 			ok=false;
200 	}
201 
202 	//subject
203 	if(sbn!="" && sbn!=act->subjectName)
204 		ok=false;
205 
206 	//activity tag
207 	if(atn!="" && !act->activityTagsNames.contains(atn))
208 		ok=false;
209 
210 	//students
211 	if(stn!=""){
212 		bool ok2=false;
213 		for(QStringList::const_iterator it=act->studentsNames.constBegin(); it!=act->studentsNames.constEnd(); it++)
214 			if(showedStudents.contains(*it)){
215 				//if(*it == stn){
216 				ok2=true;
217 				break;
218 			}
219 		if(!ok2)
220 			ok=false;
221 	}
222 	else{
223 		assert(showedStudents.count()==1);
224 		assert(showedStudents.contains(""));
225 	}
226 
227 	return ok;
228 }
229 
studentsFilterChanged()230 void SubactivitiesForm::studentsFilterChanged()
231 {
232 	bool showRelated=showRelatedCheckBox->isChecked();
233 
234 	showedStudents.clear();
235 
236 	if(!showRelated){
237 		showedStudents.insert(studentsComboBox->currentText());
238 	}
239 	else{
240 		if(studentsComboBox->currentText()=="")
241 			showedStudents.insert("");
242 		else{
243 			//down
244 			StudentsSet* studentsSet=gt.rules.searchStudentsSet(studentsComboBox->currentText());
245 			assert(studentsSet!=nullptr);
246 			if(studentsSet->type==STUDENTS_YEAR){
247 				StudentsYear* year=(StudentsYear*)studentsSet;
248 				showedStudents.insert(year->name);
249 				for(StudentsGroup* group : qAsConst(year->groupsList)){
250 					showedStudents.insert(group->name);
251 					for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList))
252 						showedStudents.insert(subgroup->name);
253 				}
254 			}
255 			else if(studentsSet->type==STUDENTS_GROUP){
256 				StudentsGroup* group=(StudentsGroup*)studentsSet;
257 				showedStudents.insert(group->name);
258 				for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList))
259 					showedStudents.insert(subgroup->name);
260 			}
261 			else if(studentsSet->type==STUDENTS_SUBGROUP){
262 				StudentsSubgroup* subgroup=(StudentsSubgroup*)studentsSet;
263 				showedStudents.insert(subgroup->name);
264 			}
265 			else
266 				assert(0);
267 
268 			//up
269 			QString crt=studentsComboBox->currentText();
270 			for(StudentsYear* year : qAsConst(gt.rules.yearsList)){
271 				for(StudentsGroup* group : qAsConst(year->groupsList)){
272 					if(group->name==crt){
273 						showedStudents.insert(year->name);
274 					}
275 					for(StudentsSubgroup* subgroup : qAsConst(group->subgroupsList)){
276 						if(subgroup->name==crt){
277 							showedStudents.insert(year->name);
278 							showedStudents.insert(group->name);
279 						}
280 					}
281 				}
282 			}
283 		}
284 	}
285 
286 	filterChanged();
287 }
288 
filterChanged()289 void SubactivitiesForm::filterChanged()
290 {
291 	int nacts=0, nsubacts=0, nh=0;
292 	int ninact=0, ninacth=0;
293 
294 	QString s;
295 	subactivitiesListWidget->clear();
296 	visibleSubactivitiesList.clear();
297 
298 	int k=0;
299 	for(int i=0; i<gt.rules.activitiesList.size(); i++){
300 		Activity* act=gt.rules.activitiesList[i];
301 		if(this->filterOk(act)){
302 			s=act->getDescription(gt.rules);
303 			visibleSubactivitiesList.append(act);
304 			subactivitiesListWidget->addItem(s);
305 			k++;
306 
307 			if(USE_GUI_COLORS && !act->active)
308 				subactivitiesListWidget->item(k-1)->setBackground(subactivitiesListWidget->palette().alternateBase());
309 
310 			if(act->id==act->activityGroupId || act->activityGroupId==0)
311 				nacts++;
312 			nsubacts++;
313 
314 			nh+=act->duration;
315 
316 			if(!act->active){
317 				ninact++;
318 				ninacth+=act->duration;
319 			}
320 		}
321 	}
322 
323 	assert(nsubacts-ninact>=0);
324 	assert(nh-ninacth>=0);
325 	activeTextLabel->setText(tr("No: %1 / %2", "No means number, %1 is the number of active subactivities, %2 is the total number of subactivities."
326 		"Please leave space between fields, so that they are better visible").arg(nsubacts-ninact).arg(nsubacts));
327 	totalTextLabel->setText(tr("Dur: %1 / %2", "Dur means duration, %1 is the duration of active subactivities, %2 is the total duration of subactivities."
328 		"Please leave space between fields, so that they are better visible").arg(nh-ninacth).arg(nh));
329 
330 	if(subactivitiesListWidget->count()>0)
331 		subactivitiesListWidget->setCurrentRow(0);
332 	else
333 		subactivityTextEdit->setPlainText(QString(""));
334 }
335 
modifySubactivity()336 void SubactivitiesForm::modifySubactivity()
337 {
338 	int ind=subactivitiesListWidget->currentRow();
339 	if(ind<0){
340 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected subactivity"));
341 		return;
342 	}
343 
344 	assert(ind<visibleSubactivitiesList.count());
345 
346 	int valv=subactivitiesListWidget->verticalScrollBar()->value();
347 	int valh=subactivitiesListWidget->horizontalScrollBar()->value();
348 
349 	Activity* act=visibleSubactivitiesList[ind];
350 	assert(act!=nullptr);
351 
352 	ModifySubactivityForm modifySubactivityForm(this, act->id, act->activityGroupId);
353 	int t;
354 	setParentAndOtherThings(&modifySubactivityForm, this);
355 	t=modifySubactivityForm.exec();
356 
357 	if(t==QDialog::Accepted){
358 		//cout<<"Acc"<<endl;
359 		filterChanged();
360 
361 		subactivitiesListWidget->verticalScrollBar()->setValue(valv);
362 		subactivitiesListWidget->horizontalScrollBar()->setValue(valh);
363 
364 		if(ind>=subactivitiesListWidget->count())
365 			ind=subactivitiesListWidget->count()-1;
366 		if(ind>=0)
367 			subactivitiesListWidget->setCurrentRow(ind);
368 	}
369 	else{
370 		//cout<<"Rej"<<endl;
371 		assert(t==QDialog::Rejected);
372 	}
373 }
374 
subactivityChanged()375 void SubactivitiesForm::subactivityChanged()
376 {
377 	int index=subactivitiesListWidget->currentRow();
378 
379 	if(index<0){
380 		subactivityTextEdit->setPlainText(QString(""));
381 		return;
382 	}
383 	if(index>=visibleSubactivitiesList.count()){
384 		subactivityTextEdit->setPlainText(QString("Invalid subactivity"));
385 		return;
386 	}
387 
388 	QString s;
389 	Activity* act=visibleSubactivitiesList[index];
390 
391 	assert(act!=nullptr);
392 	s=act->getDetailedDescriptionWithConstraints(gt.rules);
393 	subactivityTextEdit->setPlainText(s);
394 }
395 
help()396 void SubactivitiesForm::help()
397 {
398 	QString s;
399 
400 	s+=tr("Useful instructions/tips:");
401 	s+="\n\n";
402 
403 	s+=tr("Above the (sub)activities list, we have 2 labels, containing 4 numbers. The first label contains text: No: a / b. The first number a is the"
404 		" number of active (sub)activities (we number each individual subactivity as 1), while the second number b is the number of total (sub)activities."
405 		" The second label contains text: Dur: c / d. The third number c is the duration of active (sub)activities, in periods"
406 		" (or FET hours), while the fourth number d is the duration of total (sub)activities, in periods (or FET hours)."
407 		" So, No means number and Dur means duration.");
408 	s+="\n\n";
409 	s+=tr("Example: No: 100 / 102, Dur: 114 / 117. They represent: 100 - the number of active (sub)activities,"
410 		" then 102 - the number of total (sub)activities,"
411 		" 114 - the duration of active activities (in periods or FET hours) and 117 - the duration of total activities"
412 		" (in periods or FET hours). In this example we have 2 inactive activities with their combined duration being 3 periods.");
413 
414 	s+="\n\n";
415 	s+=tr("Explanation of the short description of an activity: first comes the id."
416 		" If the activity is inactive, an X follows. Then the duration. Then, if the activity is split, a slash and the total duration."
417 		" Then teachers, subject, activity tag (if it is not void) and students. Then the number of students (if specified).");
418 	s+="\n\n";
419 	s+=tr("The activities which are inactive:");
420 	s+="\n";
421 	s+=" -";
422 	s+=tr("have an X mark after the id.");
423 	s+="\n";
424 /*	s+=" -";
425 	s+=tr("are shown with lowercase letters.");
426 	s+="\n";*/
427 	s+=" -";
428 	s+=tr("if you use colors in interface (see Settings/Interface menu), they will appear with different background color.");
429 	s+="\n\n";
430 	s+=tr("To modify a subactivity, you can also double click it.");
431 	s+="\n\n";
432 	s+=tr("Show related: if you select this, there will be listed subactivities for groups and subgroups contained also in the current set (if the current set"
433 		" is a year or a group) and also higher ranked year or group (if the current set is a group or a subgroup).");
434 
435 	LongTextMessageBox::largeInformation(this, tr("FET Help"), s);
436 }
437 
subactivityComments()438 void SubactivitiesForm::subactivityComments()
439 {
440 	int ind=subactivitiesListWidget->currentRow();
441 	if(ind<0){
442 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected subactivity"));
443 		return;
444 	}
445 
446 	assert(ind<visibleSubactivitiesList.count());
447 
448 	Activity* act=visibleSubactivitiesList[ind];
449 	assert(act!=nullptr);
450 
451 	QDialog getCommentsDialog(this);
452 
453 	getCommentsDialog.setWindowTitle(tr("Subactivity comments"));
454 
455 	QPushButton* okPB=new QPushButton(tr("OK"));
456 	okPB->setDefault(true);
457 	QPushButton* cancelPB=new QPushButton(tr("Cancel"));
458 
459 	connect(okPB, SIGNAL(clicked()), &getCommentsDialog, SLOT(accept()));
460 	connect(cancelPB, SIGNAL(clicked()), &getCommentsDialog, SLOT(reject()));
461 
462 	QHBoxLayout* hl=new QHBoxLayout();
463 	hl->addStretch();
464 	hl->addWidget(okPB);
465 	hl->addWidget(cancelPB);
466 
467 	QVBoxLayout* vl=new QVBoxLayout();
468 
469 	QPlainTextEdit* commentsPT=new QPlainTextEdit();
470 	commentsPT->setPlainText(act->comments);
471 	commentsPT->selectAll();
472 	commentsPT->setFocus();
473 
474 	vl->addWidget(commentsPT);
475 	vl->addLayout(hl);
476 
477 	getCommentsDialog.setLayout(vl);
478 
479 	const QString settingsName=QString("SubactivityCommentsDialog");
480 
481 	getCommentsDialog.resize(500, 320);
482 	centerWidgetOnScreen(&getCommentsDialog);
483 	restoreFETDialogGeometry(&getCommentsDialog, settingsName);
484 
485 	int t=getCommentsDialog.exec();
486 	saveFETDialogGeometry(&getCommentsDialog, settingsName);
487 
488 	if(t==QDialog::Accepted){
489 		act->comments=commentsPT->toPlainText();
490 
491 		gt.rules.internalStructureComputed=false;
492 		setRulesModifiedAndOtherThings(&gt.rules);
493 
494 		subactivitiesListWidget->currentItem()->setText(act->getDescription(gt.rules));
495 		subactivityChanged();
496 	}
497 }
498