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(>.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(>.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(>.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(>.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