1 //
2 //
3 // Description: This file is part of FET
4 //
5 //
6 // Author: Lalescu Liviu <Please see https://lalescu.ro/liviu/ for details about contacting Liviu Lalescu (in particular, you can find here the e-mail address)>
7 // Copyright (C) 2005 Liviu Lalescu <https://lalescu.ro/liviu/>
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 "timetable.h"
20 #include "fet.h"
21 #include "activitytagsform.h"
22 #include "studentsset.h"
23 #include "teacher.h"
24 #include "subject.h"
25 #include "activitytag.h"
26 
27 #include <QInputDialog>
28 
29 #include <QMessageBox>
30 
31 #include <QListWidget>
32 #include <QAbstractItemView>
33 
34 #include <QSplitter>
35 #include <QSettings>
36 #include <QObject>
37 #include <QMetaObject>
38 
39 extern const QString COMPANY;
40 extern const QString PROGRAM;
41 
42 extern bool students_schedule_ready;
43 extern bool rooms_schedule_ready;
44 extern bool teachers_schedule_ready;
45 
ActivityTagsForm(QWidget * parent)46 ActivityTagsForm::ActivityTagsForm(QWidget* parent): QDialog(parent)
47 {
48 	setupUi(this);
49 
50 	currentActivityTagTextEdit->setReadOnly(true);
51 
52 	renameActivityTagPushButton->setDefault(true);
53 
54 	activityTagsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
55 
56 	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
57 	connect(addActivityTagPushButton, SIGNAL(clicked()), this, SLOT(addActivityTag()));
58 	connect(removeActivityTagPushButton, SIGNAL(clicked()), this, SLOT(removeActivityTag()));
59 	connect(renameActivityTagPushButton, SIGNAL(clicked()), this, SLOT(renameActivityTag()));
60 
61 	connect(moveActivityTagUpPushButton, SIGNAL(clicked()), this, SLOT(moveActivityTagUp()));
62 	connect(moveActivityTagDownPushButton, SIGNAL(clicked()), this, SLOT(moveActivityTagDown()));
63 
64 	connect(sortActivityTagsPushButton, SIGNAL(clicked()), this, SLOT(sortActivityTags()));
65 	connect(activityTagsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(activityTagChanged(int)));
66 	connect(activateActivityTagPushButton, SIGNAL(clicked()), this, SLOT(activateActivityTag()));
67 	connect(deactivateActivityTagPushButton, SIGNAL(clicked()), this, SLOT(deactivateActivityTag()));
68 	connect(activityTagsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(renameActivityTag()));
69 	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
70 
71 	connect(printablePushButton, SIGNAL(clicked()), this, SLOT(printableActivityTag()));
72 	connect(notPrintablePushButton, SIGNAL(clicked()), this, SLOT(notPrintableActivityTag()));
73 
74 	connect(commentsPushButton, SIGNAL(clicked()), this, SLOT(comments()));
75 
76 	centerWidgetOnScreen(this);
77 	restoreFETDialogGeometry(this);
78 	//restore splitter state
79 	QSettings settings(COMPANY, PROGRAM);
80 	if(settings.contains(this->metaObject()->className()+QString("/splitter-state")))
81 		splitter->restoreState(settings.value(this->metaObject()->className()+QString("/splitter-state")).toByteArray());
82 
83 	activityTagsListWidget->clear();
84 	for(int i=0; i<gt.rules.activityTagsList.size(); i++){
85 		ActivityTag* at=gt.rules.activityTagsList[i];
86 		activityTagsListWidget->addItem(at->name);
87 	}
88 
89 	if(activityTagsListWidget->count()>0)
90 		activityTagsListWidget->setCurrentRow(0);
91 }
92 
93 
~ActivityTagsForm()94 ActivityTagsForm::~ActivityTagsForm()
95 {
96 	saveFETDialogGeometry(this);
97 	//save splitter state
98 	QSettings settings(COMPANY, PROGRAM);
99 	settings.setValue(this->metaObject()->className()+QString("/splitter-state"), splitter->saveState());
100 }
101 
addActivityTag()102 void ActivityTagsForm::addActivityTag()
103 {
104 	bool ok = false;
105 	ActivityTag* at=new ActivityTag();
106 	at->name = QInputDialog::getText( this, tr("Add activity tag"), tr("Please enter activity tag's name") ,
107 	 QLineEdit::Normal, QString(), &ok );
108 
109 	if ( ok && !((at->name).isEmpty()) ){
110 		// user entered something and pressed OK
111 		if(!gt.rules.addActivityTag(at)){
112 			QMessageBox::information( this, tr("Activity tag insertion dialog"),
113 				tr("Could not insert item. Must be a duplicate"));
114 			delete at;
115 		}
116 		else{
117 			activityTagsListWidget->addItem(at->name);
118 			activityTagsListWidget->setCurrentRow(activityTagsListWidget->count()-1);
119 		}
120 	}
121 	else{
122 		if(ok){ //the user entered nothing
123 			QMessageBox::information(this, tr("FET information"), tr("Incorrect name"));
124 		}
125 		delete at;// user entered nothing or pressed Cancel
126 	}
127 }
128 
removeActivityTag()129 void ActivityTagsForm::removeActivityTag()
130 {
131 	int i=activityTagsListWidget->currentRow();
132 	if(activityTagsListWidget->currentRow()<0){
133 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
134 		return;
135 	}
136 
137 	QString text=activityTagsListWidget->currentItem()->text();
138 	int activity_tag_ID=gt.rules.searchActivityTag(text);
139 	if(activity_tag_ID<0){
140 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
141 		return;
142 	}
143 
144 	/*if(QMessageBox::warning( this, tr("FET"),
145 		tr("Are you sure you want to delete this activity tag?"),
146 		tr("Yes"), tr("No"), 0, 0, 1 ) == 1)
147 		return;*/
148 	if(QMessageBox::warning( this, tr("FET"),
149 		tr("Are you sure you want to delete this activity tag?"),
150 		QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
151 		return;
152 
153 	int tmp=gt.rules.removeActivityTag(text);
154 	if(tmp){
155 		activityTagsListWidget->setCurrentRow(-1);
156 		QListWidgetItem* item;
157 		item=activityTagsListWidget->takeItem(i);
158 		delete item;
159 
160 		if(i>=activityTagsListWidget->count())
161 			i=activityTagsListWidget->count()-1;
162 		if(i>=0)
163 			activityTagsListWidget->setCurrentRow(i);
164 		else
165 			currentActivityTagTextEdit->setPlainText(QString(""));
166 	}
167 }
168 
renameActivityTag()169 void ActivityTagsForm::renameActivityTag()
170 {
171 	int i=activityTagsListWidget->currentRow();
172 	if(activityTagsListWidget->currentRow()<0){
173 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
174 		return;
175 	}
176 
177 	QString initialActivityTagName=activityTagsListWidget->currentItem()->text();
178 
179 	int activity_tag_ID=gt.rules.searchActivityTag(initialActivityTagName);
180 	if(activity_tag_ID<0){
181 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
182 		return;
183 	}
184 
185 	bool ok = false;
186 	QString finalActivityTagName;
187 	finalActivityTagName = QInputDialog::getText( this, tr("Rename activity tag"), tr("Please enter new activity tag's name") ,
188 	 QLineEdit::Normal, initialActivityTagName, &ok );
189 
190 	if ( ok && !(finalActivityTagName.isEmpty()) ){
191 		// user entered something and pressed OK
192 		if(gt.rules.searchActivityTag(finalActivityTagName)>=0){
193 			QMessageBox::information( this, tr("Activity tag insertion dialog"),
194 				tr("Could not modify item. New name must be a duplicate"));
195 		}
196 		else{
197 			gt.rules.modifyActivityTag(initialActivityTagName, finalActivityTagName);
198 			activityTagsListWidget->item(i)->setText(finalActivityTagName);
199 			activityTagChanged(activityTagsListWidget->currentRow());
200 		}
201 	}
202 }
203 
moveActivityTagUp()204 void ActivityTagsForm::moveActivityTagUp()
205 {
206 	if(activityTagsListWidget->count()<=1)
207 		return;
208 	int i=activityTagsListWidget->currentRow();
209 	if(i<0 || i>=activityTagsListWidget->count())
210 		return;
211 	if(i==0)
212 		return;
213 
214 	QString s1=activityTagsListWidget->item(i)->text();
215 	QString s2=activityTagsListWidget->item(i-1)->text();
216 
217 	ActivityTag* at1=gt.rules.activityTagsList.at(i);
218 	ActivityTag* at2=gt.rules.activityTagsList.at(i-1);
219 
220 	gt.rules.internalStructureComputed=false;
221 	setRulesModifiedAndOtherThings(&gt.rules);
222 
223 	teachers_schedule_ready=false;
224 	students_schedule_ready=false;
225 	rooms_schedule_ready=false;
226 
227 	activityTagsListWidget->item(i)->setText(s2);
228 	activityTagsListWidget->item(i-1)->setText(s1);
229 
230 	gt.rules.activityTagsList[i]=at2;
231 	gt.rules.activityTagsList[i-1]=at1;
232 
233 	activityTagsListWidget->setCurrentRow(i-1);
234 	activityTagChanged(i-1);
235 }
236 
moveActivityTagDown()237 void ActivityTagsForm::moveActivityTagDown()
238 {
239 	if(activityTagsListWidget->count()<=1)
240 		return;
241 	int i=activityTagsListWidget->currentRow();
242 	if(i<0 || i>=activityTagsListWidget->count())
243 		return;
244 	if(i==activityTagsListWidget->count()-1)
245 		return;
246 
247 	QString s1=activityTagsListWidget->item(i)->text();
248 	QString s2=activityTagsListWidget->item(i+1)->text();
249 
250 	ActivityTag* at1=gt.rules.activityTagsList.at(i);
251 	ActivityTag* at2=gt.rules.activityTagsList.at(i+1);
252 
253 	gt.rules.internalStructureComputed=false;
254 	setRulesModifiedAndOtherThings(&gt.rules);
255 
256 	teachers_schedule_ready=false;
257 	students_schedule_ready=false;
258 	rooms_schedule_ready=false;
259 
260 	activityTagsListWidget->item(i)->setText(s2);
261 	activityTagsListWidget->item(i+1)->setText(s1);
262 
263 	gt.rules.activityTagsList[i]=at2;
264 	gt.rules.activityTagsList[i+1]=at1;
265 
266 	activityTagsListWidget->setCurrentRow(i+1);
267 	activityTagChanged(i+1);
268 }
269 
sortActivityTags()270 void ActivityTagsForm::sortActivityTags()
271 {
272 	gt.rules.sortActivityTagsAlphabetically();
273 
274 	activityTagsListWidget->clear();
275 	for(int i=0; i<gt.rules.activityTagsList.size(); i++){
276 		ActivityTag* at=gt.rules.activityTagsList[i];
277 		activityTagsListWidget->addItem(at->name);
278 	}
279 
280 	if(activityTagsListWidget->count()>0)
281 		activityTagsListWidget->setCurrentRow(0);
282 }
283 
activityTagChanged(int index)284 void ActivityTagsForm::activityTagChanged(int index)
285 {
286 	if(index<0){
287 		currentActivityTagTextEdit->setPlainText(QString(""));
288 		return;
289 	}
290 
291 	ActivityTag* st=gt.rules.activityTagsList.at(index);
292 	assert(st);
293 	QString s=st->getDetailedDescriptionWithConstraints(gt.rules);
294 	currentActivityTagTextEdit->setPlainText(s);
295 }
296 
activateActivityTag()297 void ActivityTagsForm::activateActivityTag()
298 {
299 	if(activityTagsListWidget->currentRow()<0){
300 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
301 		return;
302 	}
303 
304 	QString text=activityTagsListWidget->currentItem()->text();
305 	int count=gt.rules.activateActivityTag(text);
306 	QMessageBox::information(this, tr("FET information"), tr("Activated a number of %1 activities").arg(count));
307 }
308 
deactivateActivityTag()309 void ActivityTagsForm::deactivateActivityTag()
310 {
311 	if(activityTagsListWidget->currentRow()<0){
312 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
313 		return;
314 	}
315 
316 	QString text=activityTagsListWidget->currentItem()->text();
317 	int count=gt.rules.deactivateActivityTag(text);
318 	QMessageBox::information(this, tr("FET information"), tr("De-activated a number of %1 activities").arg(count));
319 }
320 
printableActivityTag()321 void ActivityTagsForm::printableActivityTag()
322 {
323 	if(activityTagsListWidget->currentRow()<0){
324 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
325 		return;
326 	}
327 
328 	QString text=activityTagsListWidget->currentItem()->text();
329 
330 	gt.rules.makeActivityTagPrintable(text);
331 
332 	activityTagChanged(activityTagsListWidget->currentRow());
333 }
334 
notPrintableActivityTag()335 void ActivityTagsForm::notPrintableActivityTag()
336 {
337 	if(activityTagsListWidget->currentRow()<0){
338 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
339 		return;
340 	}
341 
342 	QString text=activityTagsListWidget->currentItem()->text();
343 
344 	gt.rules.makeActivityTagNotPrintable(text);
345 
346 	activityTagChanged(activityTagsListWidget->currentRow());
347 }
348 
help()349 void ActivityTagsForm::help()
350 {
351 	QMessageBox::information(this, tr("FET help on activity tags"),
352 	 tr("Activity tag is a field which can be used or not, depending on your wish (optional field)."
353 	 " It is designed to help you with some constraints. Each activity has a list of activity tags"
354 	 " (which may be empty)."));
355 }
356 
comments()357 void ActivityTagsForm::comments()
358 {
359 	int ind=activityTagsListWidget->currentRow();
360 	if(ind<0){
361 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected activity tag"));
362 		return;
363 	}
364 
365 	ActivityTag* at=gt.rules.activityTagsList[ind];
366 	assert(at!=nullptr);
367 
368 	QDialog getCommentsDialog(this);
369 
370 	getCommentsDialog.setWindowTitle(tr("Activity tag comments"));
371 
372 	QPushButton* okPB=new QPushButton(tr("OK"));
373 	okPB->setDefault(true);
374 	QPushButton* cancelPB=new QPushButton(tr("Cancel"));
375 
376 	connect(okPB, SIGNAL(clicked()), &getCommentsDialog, SLOT(accept()));
377 	connect(cancelPB, SIGNAL(clicked()), &getCommentsDialog, SLOT(reject()));
378 
379 	QHBoxLayout* hl=new QHBoxLayout();
380 	hl->addStretch();
381 	hl->addWidget(okPB);
382 	hl->addWidget(cancelPB);
383 
384 	QVBoxLayout* vl=new QVBoxLayout();
385 
386 	QPlainTextEdit* commentsPT=new QPlainTextEdit();
387 	commentsPT->setPlainText(at->comments);
388 	commentsPT->selectAll();
389 	commentsPT->setFocus();
390 
391 	vl->addWidget(commentsPT);
392 	vl->addLayout(hl);
393 
394 	getCommentsDialog.setLayout(vl);
395 
396 	const QString settingsName=QString("ActivityTagCommentsDialog");
397 
398 	getCommentsDialog.resize(500, 320);
399 	centerWidgetOnScreen(&getCommentsDialog);
400 	restoreFETDialogGeometry(&getCommentsDialog, settingsName);
401 
402 	int t=getCommentsDialog.exec();
403 	saveFETDialogGeometry(&getCommentsDialog, settingsName);
404 
405 	if(t==QDialog::Accepted){
406 		at->comments=commentsPT->toPlainText();
407 
408 		gt.rules.internalStructureComputed=false;
409 		setRulesModifiedAndOtherThings(&gt.rules);
410 
411 		activityTagChanged(ind);
412 	}
413 }
414