1 //
2 //
3 // Description: This file is part of FET
4 //
5 //
6 // Author: Liviu Lalescu <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) 2003 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 "fet.h"
20 #include "roomsform.h"
21 #include "addroomform.h"
22 #include "modifyroomform.h"
23 
24 #include "roommakeeditvirtualform.h"
25 
26 #include "longtextmessagebox.h"
27 
28 #include <QMessageBox>
29 
30 #include <QListWidget>
31 #include <QScrollBar>
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 
RoomsForm(QWidget * parent)46 RoomsForm::RoomsForm(QWidget* parent): QDialog(parent)
47 {
48 	setupUi(this);
49 
50 	currentRoomTextEdit->setReadOnly(true);
51 
52 	modifyRoomPushButton->setDefault(true);
53 
54 	roomsListWidget->setSelectionMode(QAbstractItemView::SingleSelection);
55 
56 	connect(addRoomPushButton, SIGNAL(clicked()), this, SLOT(addRoom()));
57 	connect(removeRoomPushButton, SIGNAL(clicked()), this, SLOT(removeRoom()));
58 	connect(roomsListWidget, SIGNAL(currentRowChanged(int)), this, SLOT(roomChanged(int)));
59 	connect(closePushButton, SIGNAL(clicked()), this, SLOT(close()));
60 	connect(modifyRoomPushButton, SIGNAL(clicked()), this, SLOT(modifyRoom()));
61 
62 	connect(moveRoomUpPushButton, SIGNAL(clicked()), this, SLOT(moveRoomUp()));
63 	connect(moveRoomDownPushButton, SIGNAL(clicked()), this, SLOT(moveRoomDown()));
64 
65 	connect(sortRoomsPushButton, SIGNAL(clicked()), this, SLOT(sortRooms()));
66 	connect(roomsListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(modifyRoom()));
67 
68 	connect(commentsPushButton, SIGNAL(clicked()), this, SLOT(comments()));
69 
70 	connect(makeRealPushButton, SIGNAL(clicked()), this, SLOT(makeReal()));
71 	connect(makeEditVirtualPushButton, SIGNAL(clicked()), this, SLOT(makeEditVirtual()));
72 	connect(helpPushButton, SIGNAL(clicked()), this, SLOT(help()));
73 
74 	centerWidgetOnScreen(this);
75 	restoreFETDialogGeometry(this);
76 	//restore splitter state
77 	QSettings settings(COMPANY, PROGRAM);
78 	if(settings.contains(this->metaObject()->className()+QString("/splitter-state")))
79 		splitter->restoreState(settings.value(this->metaObject()->className()+QString("/splitter-state")).toByteArray());
80 
81 	this->filterChanged();
82 }
83 
~RoomsForm()84 RoomsForm::~RoomsForm()
85 {
86 	saveFETDialogGeometry(this);
87 	//save splitter state
88 	QSettings settings(COMPANY, PROGRAM);
89 	settings.setValue(this->metaObject()->className()+QString("/splitter-state"), splitter->saveState());
90 }
91 
filterOk(Room * rm)92 bool RoomsForm::filterOk(Room* rm)
93 {
94 	Q_UNUSED(rm);
95 
96 	bool ok=true;
97 	return ok;
98 }
99 
filterChanged()100 void RoomsForm::filterChanged()
101 {
102 	QString s;
103 	roomsListWidget->clear();
104 	visibleRoomsList.clear();
105 	for(int i=0; i<gt.rules.roomsList.size(); i++){
106 		Room* rm=gt.rules.roomsList[i];
107 		if(this->filterOk(rm)){
108 			//s=rm->getDescription();
109 			//roomsListWidget->addItem(s);
110 			if(rm->isVirtual==false)
111 				roomsListWidget->addItem(rm->name);
112 			else
113 				roomsListWidget->addItem(tr("V: %1", "V means virtual room, %1 is the name of the room").arg(rm->name));
114 			visibleRoomsList.append(rm);
115 		}
116 	}
117 
118 	if(roomsListWidget->count()>0)
119 		roomsListWidget->setCurrentRow(0);
120 	else
121 		roomChanged(-1);
122 }
123 
addRoom()124 void RoomsForm::addRoom()
125 {
126 	AddRoomForm addRoomForm(this);
127 	setParentAndOtherThings(&addRoomForm, this);
128 	addRoomForm.exec();
129 
130 	filterChanged();
131 
132 	roomsListWidget->setCurrentRow(roomsListWidget->count()-1);
133 }
134 
removeRoom()135 void RoomsForm::removeRoom()
136 {
137 	int ind=roomsListWidget->currentRow();
138 	if(ind<0){
139 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected room"));
140 		return;
141 	}
142 
143 	Room* rm=visibleRoomsList.at(ind);
144 	assert(rm!=nullptr);
145 
146 	if(QMessageBox::warning( this, tr("FET"),
147 		tr("Are you sure you want to delete this room and all related constraints?"),
148 		QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No)
149 		return;
150 
151 	int nv=0;
152 	int nvtotal=0;
153 	if(rm->isVirtual==false){
154 		for(Room* rr : qAsConst(gt.rules.roomsList)){
155 			bool met=false;
156 			if(rr->isVirtual==true){
157 				assert(rr->name!=rm->name);
158 				for(const QStringList& tl : qAsConst(rr->realRoomsSetsList)){
159 					if(tl.contains(rm->name)){
160 						nvtotal++;
161 						met=true;
162 					}
163 				}
164 			}
165 			if(met)
166 				nv++;
167 		}
168 	}
169 	if(nv>0 || nvtotal>0){
170 		assert(nv>0 && nvtotal>0);
171 
172 		if(QMessageBox::warning( this, tr("FET"),
173 			tr("This is a real room. If you remove it, it will affect %1 virtual rooms."
174 			" The real room is met %2 times overall in the lists of sets of the virtual rooms."
175 			" Do you really want to remove it?").arg(nv).arg(nvtotal),
176 			QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No)
177 			return;
178 	}
179 
180 	bool tmp=gt.rules.removeRoom(rm->name);
181 	assert(tmp);
182 
183 	visibleRoomsList.removeAt(ind);
184 	roomsListWidget->setCurrentRow(-1);
185 	QListWidgetItem* item=roomsListWidget->takeItem(ind);
186 	delete item;
187 
188 	if(ind>=roomsListWidget->count())
189 		ind=roomsListWidget->count()-1;
190 	if(ind>=0)
191 		roomsListWidget->setCurrentRow(ind);
192 	else
193 		currentRoomTextEdit->setPlainText(QString(""));
194 }
195 
roomChanged(int index)196 void RoomsForm::roomChanged(int index)
197 {
198 	if(index<0){
199 		currentRoomTextEdit->setPlainText("");
200 		return;
201 	}
202 
203 	QString s;
204 	Room* room=visibleRoomsList.at(index);
205 
206 	assert(room!=nullptr);
207 	s=room->getDetailedDescriptionWithConstraints(gt.rules);
208 	currentRoomTextEdit->setPlainText(s);
209 }
210 
moveRoomUp()211 void RoomsForm::moveRoomUp()
212 {
213 	if(roomsListWidget->count()<=1)
214 		return;
215 	int i=roomsListWidget->currentRow();
216 	if(i<0 || i>=roomsListWidget->count())
217 		return;
218 	if(i==0)
219 		return;
220 
221 	QString s1=roomsListWidget->item(i)->text();
222 	QString s2=roomsListWidget->item(i-1)->text();
223 
224 	Room* rm1=gt.rules.roomsList.at(i);
225 	Room* rm2=gt.rules.roomsList.at(i-1);
226 
227 	gt.rules.internalStructureComputed=false;
228 	setRulesModifiedAndOtherThings(&gt.rules);
229 
230 	teachers_schedule_ready=false;
231 	students_schedule_ready=false;
232 	rooms_schedule_ready=false;
233 
234 	roomsListWidget->item(i)->setText(s2);
235 	roomsListWidget->item(i-1)->setText(s1);
236 
237 	gt.rules.roomsList[i]=rm2;
238 	gt.rules.roomsList[i-1]=rm1;
239 
240 	//Begin bug fix on 2017-08-29
241 	Room* vr1=visibleRoomsList[i];
242 	Room* vr2=visibleRoomsList[i-1];
243 	visibleRoomsList[i]=vr2;
244 	visibleRoomsList[i-1]=vr1;
245 	//End bug fix
246 
247 	roomsListWidget->setCurrentRow(i-1);
248 	roomChanged(i-1);
249 }
250 
moveRoomDown()251 void RoomsForm::moveRoomDown()
252 {
253 	if(roomsListWidget->count()<=1)
254 		return;
255 	int i=roomsListWidget->currentRow();
256 	if(i<0 || i>=roomsListWidget->count())
257 		return;
258 	if(i==roomsListWidget->count()-1)
259 		return;
260 
261 	QString s1=roomsListWidget->item(i)->text();
262 	QString s2=roomsListWidget->item(i+1)->text();
263 
264 	Room* rm1=gt.rules.roomsList.at(i);
265 	Room* rm2=gt.rules.roomsList.at(i+1);
266 
267 	gt.rules.internalStructureComputed=false;
268 	setRulesModifiedAndOtherThings(&gt.rules);
269 
270 	teachers_schedule_ready=false;
271 	students_schedule_ready=false;
272 	rooms_schedule_ready=false;
273 
274 	roomsListWidget->item(i)->setText(s2);
275 	roomsListWidget->item(i+1)->setText(s1);
276 
277 	gt.rules.roomsList[i]=rm2;
278 	gt.rules.roomsList[i+1]=rm1;
279 
280 	//Begin bug fix on 2017-08-29
281 	Room* vr1=visibleRoomsList[i];
282 	Room* vr2=visibleRoomsList[i+1];
283 	visibleRoomsList[i]=vr2;
284 	visibleRoomsList[i+1]=vr1;
285 	//End bug fix
286 
287 	roomsListWidget->setCurrentRow(i+1);
288 	roomChanged(i+1);
289 }
290 
sortRooms()291 void RoomsForm::sortRooms()
292 {
293 	gt.rules.sortRoomsAlphabetically();
294 
295 	filterChanged();
296 }
297 
modifyRoom()298 void RoomsForm::modifyRoom()
299 {
300 	int valv=roomsListWidget->verticalScrollBar()->value();
301 	int valh=roomsListWidget->horizontalScrollBar()->value();
302 
303 	int ci=roomsListWidget->currentRow();
304 	if(ci<0){
305 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected room"));
306 		return;
307 	}
308 
309 	Room* rm=visibleRoomsList.at(ci);
310 	ModifyRoomForm form(this, rm->name, rm->building, rm->capacity);
311 	setParentAndOtherThings(&form, this);
312 	form.exec();
313 
314 	filterChanged();
315 
316 	roomsListWidget->verticalScrollBar()->setValue(valv);
317 	roomsListWidget->horizontalScrollBar()->setValue(valh);
318 
319 	if(ci>=roomsListWidget->count())
320 		ci=roomsListWidget->count()-1;
321 
322 	if(ci>=0)
323 		roomsListWidget->setCurrentRow(ci);
324 }
325 
comments()326 void RoomsForm::comments()
327 {
328 	int ind=roomsListWidget->currentRow();
329 	if(ind<0){
330 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected room"));
331 		return;
332 	}
333 
334 	Room* rm=gt.rules.roomsList[ind];
335 	assert(rm!=nullptr);
336 
337 	QDialog getCommentsDialog(this);
338 
339 	getCommentsDialog.setWindowTitle(tr("Room comments"));
340 
341 	QPushButton* okPB=new QPushButton(tr("OK"));
342 	okPB->setDefault(true);
343 	QPushButton* cancelPB=new QPushButton(tr("Cancel"));
344 
345 	connect(okPB, SIGNAL(clicked()), &getCommentsDialog, SLOT(accept()));
346 	connect(cancelPB, SIGNAL(clicked()), &getCommentsDialog, SLOT(reject()));
347 
348 	QHBoxLayout* hl=new QHBoxLayout();
349 	hl->addStretch();
350 	hl->addWidget(okPB);
351 	hl->addWidget(cancelPB);
352 
353 	QVBoxLayout* vl=new QVBoxLayout();
354 
355 	QPlainTextEdit* commentsPT=new QPlainTextEdit();
356 	commentsPT->setPlainText(rm->comments);
357 	commentsPT->selectAll();
358 	commentsPT->setFocus();
359 
360 	vl->addWidget(commentsPT);
361 	vl->addLayout(hl);
362 
363 	getCommentsDialog.setLayout(vl);
364 
365 	const QString settingsName=QString("RoomCommentsDialog");
366 
367 	getCommentsDialog.resize(500, 320);
368 	centerWidgetOnScreen(&getCommentsDialog);
369 	restoreFETDialogGeometry(&getCommentsDialog, settingsName);
370 
371 	int t=getCommentsDialog.exec();
372 	saveFETDialogGeometry(&getCommentsDialog, settingsName);
373 
374 	if(t==QDialog::Accepted){
375 		rm->comments=commentsPT->toPlainText();
376 
377 		gt.rules.internalStructureComputed=false;
378 		setRulesModifiedAndOtherThings(&gt.rules);
379 
380 		roomChanged(ind);
381 	}
382 }
383 
help()384 void RoomsForm::help()
385 {
386 	QString s;
387 
388 	s=tr("Virtual rooms were suggested by the user %1 on the FET forum (you can follow the discussion on the internet page %2)."
389 	 " They can be useful in two situations (but you might think of more uses):")
390 	 .arg("math").arg("https://lalescu.ro/liviu/fet/forum/index.php?topic=4249.0");
391 	s+="\n\n";
392 	s+=tr("1) If you want an activity to take place in more real rooms.");
393 	s+="\n\n";
394 	s+=tr("2) If you want an activity to take place, say, either in a large real room or in three smaller real rooms.");
395 	s+="\n\n";
396 	s+=tr("More details about defining and using virtual rooms are shown in the dialog of making/editing virtual rooms, if you click the Help button there.");
397 
398 	LongTextMessageBox::largeInformation(this, tr("FET Help"), s);
399 }
400 
makeReal()401 void RoomsForm::makeReal()
402 {
403 	int valv=roomsListWidget->verticalScrollBar()->value();
404 	int valh=roomsListWidget->horizontalScrollBar()->value();
405 
406 	int ci=roomsListWidget->currentRow();
407 	if(ci<0){
408 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected room"));
409 		return;
410 	}
411 
412 	Room* rm=visibleRoomsList.at(ci);
413 
414 	if(rm->isVirtual==false){
415 		QMessageBox::information(this, tr("FET information"), tr("The selected room is already real."));
416 		return;
417 	}
418 
419 	for(SpaceConstraint* ctr : qAsConst(gt.rules.spaceConstraintsList))
420 		if(ctr->type==CONSTRAINT_ACTIVITY_PREFERRED_ROOM){
421 			ConstraintActivityPreferredRoom* c=(ConstraintActivityPreferredRoom*)ctr;
422 
423 			if(c->roomName==rm->name && c->preferredRealRoomsNames.count()>0){
424 				QMessageBox::information(this, tr("FET information"), tr("This virtual room is used in at least a constraint of type"
425 				 " activity preferred room, having specified a nonempty list of preferred real rooms. It cannot thus be made real"
426 				 " directly. Firstly you need to edit/remove that/those constraints (converting this virtual room to a real one will"
427 				 " be possible if you clear the preferred real rooms list for each such constraint)."));
428 				return;
429 			}
430 		}
431 
432 	if(QMessageBox::warning( this, tr("FET confirmation"),
433 		tr("Are you sure you want to make this room real? This will erase the list of sets of real rooms for this virtual room."),
434 		QMessageBox::Yes | QMessageBox::No ) == QMessageBox::No)
435 		return;
436 
437 	rm->isVirtual=false;
438 	rm->realRoomsSetsList.clear();
439 
440 	////////
441 	gt.rules.internalStructureComputed=false;
442 	setRulesModifiedAndOtherThings(&gt.rules);
443 
444 	teachers_schedule_ready=false;
445 	students_schedule_ready=false;
446 	rooms_schedule_ready=false;
447 	////////
448 
449 	filterChanged();
450 
451 	roomsListWidget->verticalScrollBar()->setValue(valv);
452 	roomsListWidget->horizontalScrollBar()->setValue(valh);
453 
454 	if(ci>=roomsListWidget->count())
455 		ci=roomsListWidget->count()-1;
456 
457 	if(ci>=0)
458 		roomsListWidget->setCurrentRow(ci);
459 }
460 
makeEditVirtual()461 void RoomsForm::makeEditVirtual()
462 {
463 	int valv=roomsListWidget->verticalScrollBar()->value();
464 	int valh=roomsListWidget->horizontalScrollBar()->value();
465 
466 	int ci=roomsListWidget->currentRow();
467 	if(ci<0){
468 		QMessageBox::information(this, tr("FET information"), tr("Invalid selected room"));
469 		return;
470 	}
471 
472 	Room* rm=visibleRoomsList.at(ci);
473 
474 	QStringList vlcr; //virtual rooms containing rm
475 	if(rm->isVirtual==false)
476 		for(Room* rm2 : qAsConst(gt.rules.roomsList))
477 			if(rm2!=rm)
478 				if(rm2->isVirtual==true)
479 					for(const QStringList& tl : qAsConst(rm2->realRoomsSetsList))
480 						if(tl.contains(rm->name)){
481 							vlcr.append(rm2->name);
482 							break;
483 						}
484 
485 	if(!vlcr.isEmpty()){
486 		QMessageBox::information(this, tr("FET information"), tr("This real room is contained in %1 other virtual rooms, so it cannot be made virtual"
487 		 " (because a virtual room can only contain real rooms).").arg(vlcr.count()));
488 		return;
489 	}
490 
491 	if(rm->isVirtual==false)
492 		for(SpaceConstraint* ctr : qAsConst(gt.rules.spaceConstraintsList))
493 			if(ctr->type==CONSTRAINT_ACTIVITY_PREFERRED_ROOM){
494 				ConstraintActivityPreferredRoom* c=(ConstraintActivityPreferredRoom*)ctr;
495 
496 				if(c->preferredRealRoomsNames.contains(rm->name)){
497 					QMessageBox::information(this, tr("FET information"), tr("This real room is used in at least a constraint of type"
498 					 " activity preferred room, in the list of preferred real rooms. It cannot thus be made virtual"
499 					 " directly. Firstly you need to edit/remove that/those constraints."));
500 					return;
501 				}
502 			}
503 
504 	RoomMakeEditVirtualForm form(this, rm);
505 	setParentAndOtherThings(&form, this);
506 	form.exec();
507 
508 	filterChanged();
509 
510 	roomsListWidget->verticalScrollBar()->setValue(valv);
511 	roomsListWidget->horizontalScrollBar()->setValue(valh);
512 
513 	if(ci>=roomsListWidget->count())
514 		ci=roomsListWidget->count()-1;
515 
516 	if(ci>=0)
517 		roomsListWidget->setCurrentRow(ci);
518 }
519