1 /* This file is part of the KDE project
2 Copyright (C) 2017 Dag Andersen <danders@get2net.dk>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18 */
19
20 // clazy:excludeall=qstring-arg
21 #include "TasksGeneralPanel.h"
22 #include "kpttaskdialog.h"
23 #include "kpttask.h"
24 #include "kptcommand.h"
25 #include "kptduration.h"
26 #include "kptdurationspinbox.h"
27 #include "kptcalendar.h"
28 #include "kptdatetime.h"
29 #include "kptproject.h"
30
31 #include <KLocalizedString>
32
33 #ifdef PLAN_KDEPIMLIBS_FOUND
34 #include <akonadi/contact/emailaddressselectiondialog.h>
35 #include <akonadi/contact/emailaddressselectionwidget.h>
36 #include <akonadi/contact/emailaddressselection.h>
37 #endif
38
39 #include <QDateTime>
40
41 #include <kptdebug.h>
42
43 namespace KPlato
44 {
45
TasksGeneralPanel(Project & project,QList<Task * > & tasks,QWidget * p,const char * n)46 TasksGeneralPanel::TasksGeneralPanel(Project &project, QList<Task*> &tasks, QWidget *p, const char *n)
47 : TasksGeneralPanelImpl(p, n),
48 m_tasks(tasks),
49 m_project(project)
50 {
51 Q_ASSERT(!tasks.isEmpty());
52
53 useTime = true;
54 setStartValues(tasks.at(0));
55
56 // if (task.isBaselined(BASELINESCHEDULE)) {
57 // schedulingGroup->setEnabled(false);
58 // }
59
60 }
61
setStartValues(Task * task)62 void TasksGeneralPanel::setStartValues(Task *task) {
63 m_estimate = m_duration = task->estimate()->expectedValue();
64 leaderfield->setText(task->leader());
65
66 int cal = 0;
67 m_calendars.clear();
68 calendarCombo->addItem(i18n("None"));
69 m_calendars.insert(0, 0);
70 QList<Calendar*> list = m_project.allCalendars();
71 int i=1;
72 foreach (Calendar *c, list) {
73 calendarCombo->insertItem(i, c->name());
74 m_calendars.insert(i, c);
75 if (c == task->estimate()->calendar()) {
76 cal = i;
77 }
78 ++i;
79 }
80 calendarCombo->setCurrentIndex(cal);
81
82 estimate->setMinimumUnit((Duration::Unit)(m_project.config().minimumDurationUnit()));
83 estimate->setMaximumUnit((Duration::Unit)(m_project.config().maximumDurationUnit()));
84 estimate->setUnit(task->estimate()->unit());
85 setEstimateType(task->estimate()->type());
86 if (task->estimate()->type() == Estimate::Type_Effort && task->estimate()->expectedEstimate() == 0.0) {
87 setEstimateType(2 /*Milestone*/);
88 }
89 setSchedulingType(task->constraint());
90 if (task->constraintStartTime().isValid()) {
91 setStartDateTime(task->constraintStartTime());
92 } else {
93 QDate date = QDate::currentDate();
94 setStartDateTime(QDateTime(date, QTime(), Qt::LocalTime));
95 }
96 if (task->constraintEndTime().isValid()) {
97 setEndDateTime(task->constraintEndTime());
98 } else {
99 setEndDateTime(QDateTime(startDate().addDays(1), QTime(), Qt::LocalTime));
100 }
101 //debugPlan<<"Estimate:"<<task->estimate()->expected().toString();
102 setEstimate(task->estimate()->expectedEstimate());
103 setOptimistic(task->estimate()->optimisticRatio());
104 setPessimistic(task->estimate()->pessimisticRatio());
105 setRisktype(task->estimate()->risktype());
106
107 namefield->setFocus();
108 }
109
buildCommand()110 MacroCommand *TasksGeneralPanel::buildCommand() {
111 MacroCommand *cmd = new MacroCommand(kundo2_i18n("Modify Tasks"));
112 bool modified = false;
113
114 for (Task *t : m_tasks) {
115 if (!leaderfield->isHidden() && t->leader() != leaderfield->text()) {
116 cmd->addCommand(new NodeModifyLeaderCmd(*t, leaderfield->text()));
117 modified = true;
118 }
119 Node::ConstraintType c = (Node::ConstraintType)schedulingType();
120 if (c != t->constraint()) {
121 cmd->addCommand(new NodeModifyConstraintCmd(*t, c));
122 modified = true;
123 }
124 if (startDateTime() != t->constraintStartTime() &&
125 (c == Node::FixedInterval || c == Node::StartNotEarlier || c == Node::MustStartOn)) {
126 cmd->addCommand(new NodeModifyConstraintStartTimeCmd(*t, startDateTime()));
127 modified = true;
128 }
129 if (endDateTime() != t->constraintEndTime() &&
130 (c == Node::FinishNotLater || c == Node::FixedInterval || c == Node::MustFinishOn)) {
131 cmd->addCommand(new NodeModifyConstraintEndTimeCmd(*t, endDateTime()));
132 modified = true;
133 }
134 int et = estimationType();
135 if (et == 2 /*Milestome*/) {
136 et = 0; /*Effort*/
137 }
138 if (et != t->estimate()->type()) {
139 cmd->addCommand(new ModifyEstimateTypeCmd(*t, t->estimate()->type(), et));
140 modified = true;
141 }
142 bool unitchanged = estimate->unit() != t->estimate()->unit();
143 if (unitchanged) {
144 cmd->addCommand(new ModifyEstimateUnitCmd(*t, t->estimate()->unit(), estimate->unit()));
145 modified = true;
146 }
147 bool expchanged = estimationValue() != t->estimate()->expectedEstimate();
148 if (expchanged) {
149 cmd->addCommand(new ModifyEstimateCmd(*t, t->estimate()->expectedEstimate(), estimationValue()));
150 modified = true;
151 }
152 int x = optimistic();
153 if (x != t->estimate()->optimisticRatio() || expchanged || unitchanged) {
154 cmd->addCommand(new EstimateModifyOptimisticRatioCmd(*t, t->estimate()->optimisticRatio(), x));
155 modified = true;
156 }
157 x = pessimistic();
158 if (x != t->estimate()->pessimisticRatio() || expchanged || unitchanged) {
159 cmd->addCommand(new EstimateModifyPessimisticRatioCmd(*t, t->estimate()->pessimisticRatio(), x));
160 modified = true;
161 }
162 if (t->estimate()->risktype() != risktype()) {
163 cmd->addCommand(new EstimateModifyRiskCmd(*t, t->estimate()->risktype(), risktype()));
164 modified = true;
165 }
166 if (t->estimate()->calendar() != calendar()) {
167 cmd->addCommand(new ModifyEstimateCalendarCmd(*t, t->estimate()->calendar(), calendar()));
168 modified = true;
169 }
170 }
171 if (!modified) {
172 delete cmd;
173 return 0;
174 }
175 return cmd;
176 }
177
ok()178 bool TasksGeneralPanel::ok() {
179 return true;
180 }
181
estimationTypeChanged(int type)182 void TasksGeneralPanel::estimationTypeChanged(int type) {
183 if (type == 0 /*Effort*/) {
184 estimate->setEnabled(true);
185 calendarCombo->setEnabled(false);
186 } else if (type == 1 /*Duration*/) {
187 calendarCombo->setEnabled(false);
188 if (schedulingType() == 6) { /*Fixed interval*/
189 estimate->setEnabled(false);
190 } else {
191 estimate->setEnabled(true);
192 calendarCombo->setEnabled(true);
193 }
194 } else if (type == 2 /* Milestone */) {
195 estimate->setValue(0);
196 estimate->setEnabled(false);
197 calendarCombo->setEnabled(false);
198 }
199 TasksGeneralPanelImpl::estimationTypeChanged(type);
200 }
201
scheduleTypeChanged(int value)202 void TasksGeneralPanel::scheduleTypeChanged(int value)
203 {
204 if (value == 6 /*Fixed interval*/) {
205 if (estimateType->currentIndex() == 1/*duration*/){
206 // setEstimateScales(24);
207 estimate->setEnabled(false);
208 //TODO setEstimate(DateTime(endDateTime(), KDateTime::UTC) - DateTime(startDateTime(), KDateTime::UTC));
209 }
210 } else {
211 estimate->setEnabled(true);
212 }
213 TasksGeneralPanelImpl::scheduleTypeChanged(value);
214 }
215
216 //-----------------------------
TasksGeneralPanelImpl(QWidget * p,const char * n)217 TasksGeneralPanelImpl::TasksGeneralPanelImpl(QWidget *p, const char *n)
218 : QWidget(p) {
219
220 setObjectName(n);
221 setupUi(this);
222
223 #ifndef PLAN_KDEPIMLIBS_FOUND
224 chooseLeader->hide();
225 #endif
226
227 // FIXME
228 // [Bug 311940] New: Plan crashes when typing a text in the filter textbox before the textbook is fully loaded when selecting a contact from the addressbook
229 chooseLeader->hide();
230
231 connect(namefield, SIGNAL(textChanged(QString)), SLOT(checkAllFieldsFilled()));
232 connect(leaderfield, SIGNAL(textChanged(QString)), SLOT(checkAllFieldsFilled()));
233 connect(chooseLeader, SIGNAL(clicked()), SLOT(changeLeader()));
234 connect(estimateType, SIGNAL(activated(int)), SLOT(estimationTypeChanged(int)));
235 connect(scheduleType, SIGNAL(activated(int)), SLOT(scheduleTypeChanged(int)));
236 connect(scheduleStartDate, SIGNAL(dateChanged(QDate)), SLOT(startDateChanged()));
237 connect(scheduleStartTime, SIGNAL(timeChanged(QTime)), SLOT(startTimeChanged(QTime)));
238 connect(scheduleEndDate, SIGNAL(dateChanged(QDate)), SLOT(endDateChanged()));
239 connect(scheduleEndTime, SIGNAL(timeChanged(QTime)), SLOT(endTimeChanged(QTime)));
240 connect(estimate, SIGNAL(valueChanged(double)), SLOT(checkAllFieldsFilled()));
241 connect(optimisticValue, SIGNAL(valueChanged(int)), SLOT(checkAllFieldsFilled()));
242 connect(pessimisticValue, SIGNAL(valueChanged(int)), SLOT(checkAllFieldsFilled()));
243 connect(risk, SIGNAL(activated(int)), SLOT(checkAllFieldsFilled()));
244 connect(calendarCombo, SIGNAL(activated(int)), SLOT(calendarChanged(int)));
245
246 }
247
setSchedulingType(int type)248 void TasksGeneralPanelImpl::setSchedulingType(int type)
249 {
250 enableDateTime(type);
251 scheduleType->setCurrentIndex(type);
252 emit schedulingTypeChanged(type);
253 }
254
schedulingType() const255 int TasksGeneralPanelImpl::schedulingType() const
256 {
257 return scheduleType->currentIndex();
258 }
259
changeLeader()260 void TasksGeneralPanelImpl::changeLeader()
261 {
262 #ifdef PLAN_KDEPIMLIBS_FOUND
263 QPointer<Akonadi::EmailAddressSelectionDialog> dlg = new Akonadi::EmailAddressSelectionDialog(this);
264 if (dlg->exec() && dlg) {
265 QStringList names;
266 const Akonadi::EmailAddressSelection::List selections = dlg->selectedAddresses();
267 foreach (const Akonadi::EmailAddressSelection &selection, selections) {
268 QString s = selection.name();
269 if (! selection.email().isEmpty()) {
270 if (! selection.name().isEmpty()) {
271 s += " <";
272 }
273 s += selection.email();
274 if (! selection.name().isEmpty()) {
275 s += '>';
276 }
277 if (! s.isEmpty()) {
278 names << s;
279 }
280 }
281 }
282 if (! names.isEmpty()) {
283 leaderfield->setText(names.join(", "));
284 }
285 }
286 #endif
287 }
288
setEstimationType(int type)289 void TasksGeneralPanelImpl::setEstimationType(int type)
290 {
291 estimateType->setCurrentIndex(type);
292 }
293
estimationType() const294 int TasksGeneralPanelImpl::estimationType() const
295 {
296 return estimateType->currentIndex();
297 }
298
setOptimistic(int value)299 void TasksGeneralPanelImpl::setOptimistic(int value)
300 {
301 optimisticValue->setValue(value);
302 }
303
setPessimistic(int value)304 void TasksGeneralPanelImpl::setPessimistic(int value)
305 {
306 pessimisticValue->setValue(value);
307 }
308
optimistic() const309 int TasksGeneralPanelImpl::optimistic() const
310 {
311 return optimisticValue->value();
312 }
313
pessimistic()314 int TasksGeneralPanelImpl::pessimistic()
315 {
316 return pessimisticValue->value();
317 }
318
enableDateTime(int scheduleType)319 void TasksGeneralPanelImpl::enableDateTime(int scheduleType)
320 {
321 scheduleStartTime->setEnabled(false);
322 scheduleEndTime->setEnabled(false);
323 scheduleStartDate->setEnabled(false);
324 scheduleEndDate->setEnabled(false);
325 switch (scheduleType)
326 {
327 case 0: //ASAP
328 case 1: //ALAP
329 break;
330 case 2: //Must start on
331 case 4: // Start not earlier
332 if (useTime) {
333 scheduleStartTime->setEnabled(true);
334 scheduleEndTime->setEnabled(false);
335 }
336 scheduleStartDate->setEnabled(true);
337 scheduleEndDate->setEnabled(false);
338 break;
339 case 3: //Must finish on
340 case 5: // Finish not later
341 if (useTime) {
342 scheduleStartTime->setEnabled(false);
343 scheduleEndTime->setEnabled(true);
344 }
345 scheduleStartDate->setEnabled(false);
346 scheduleEndDate->setEnabled(true);
347 break;
348 case 6: //Fixed interval
349 if (useTime) {
350 scheduleStartTime->setEnabled(true);
351 scheduleEndTime->setEnabled(true);
352 }
353 scheduleStartDate->setEnabled(true);
354 scheduleEndDate->setEnabled(true);
355 break;
356 default:
357 break;
358 }
359 }
360
361
estimationTypeChanged(int)362 void TasksGeneralPanelImpl::estimationTypeChanged(int /*type*/)
363 {
364 checkAllFieldsFilled();
365 }
366
calendarChanged(int)367 void TasksGeneralPanelImpl::calendarChanged(int /*index*/)
368 {
369 checkAllFieldsFilled();
370 }
371
setEstimate(double duration)372 void TasksGeneralPanelImpl::setEstimate(double duration)
373 {
374 estimate->setValue(duration);
375 }
376
377
setEstimateType(int type)378 void TasksGeneralPanelImpl::setEstimateType(int type)
379 {
380 estimateType->setCurrentIndex(type);
381 estimationTypeChanged(type);
382 }
383
384
checkAllFieldsFilled()385 void TasksGeneralPanelImpl::checkAllFieldsFilled()
386 {
387 emit changed();
388 emit obligatedFieldsFilled(true); // do not block save even if name is not filled
389 }
390
391
estimationValue()392 double TasksGeneralPanelImpl::estimationValue()
393 {
394 return estimate->value();
395 }
396
startDateChanged()397 void TasksGeneralPanelImpl::startDateChanged()
398 {
399 if (!scheduleStartDate->isEnabled()) {
400 return;
401 }
402 QDate date = startDate();
403 if (startDateTime() > endDateTime())
404 {
405 scheduleEndTime->blockSignals(true);
406 scheduleEndDate->blockSignals(true);
407 setEndDate(date);
408 setEndTime(startTime());
409 scheduleEndTime->blockSignals(false);
410 scheduleEndDate->blockSignals(false);
411 }
412 if (scheduleType->currentIndex() == 6 /*FixedInterval*/)
413 {
414 estimationTypeChanged(estimateType->currentIndex());
415 }
416 checkAllFieldsFilled();
417 }
418
startTimeChanged(const QTime & time)419 void TasksGeneralPanelImpl::startTimeChanged(const QTime &time)
420 {
421 if (!scheduleStartTime->isEnabled()) {
422 return;
423 }
424 if (startDateTime() > endDateTime())
425 {
426 scheduleEndTime->blockSignals(true);
427 setEndTime(time);
428 scheduleEndTime->blockSignals(false);
429 }
430 if (scheduleType->currentIndex() == 6 /*FixedInterval*/)
431 {
432 estimationTypeChanged(estimateType->currentIndex());
433 }
434 checkAllFieldsFilled();
435 }
436
437
endDateChanged()438 void TasksGeneralPanelImpl::endDateChanged()
439 {
440 if (!scheduleEndDate->isEnabled()) {
441 return;
442 }
443 QDate date = endDate();
444 if (endDateTime() < startDateTime())
445 {
446 scheduleStartTime->blockSignals(true);
447 scheduleStartDate->blockSignals(true);
448 setStartDate(date);
449 setStartTime(endTime());
450 scheduleStartTime->blockSignals(false);
451 scheduleStartDate->blockSignals(false);
452 }
453
454 if (scheduleType->currentIndex() == 6 /*FixedInterval*/)
455 {
456 estimationTypeChanged(estimateType->currentIndex());
457 }
458 checkAllFieldsFilled();
459 }
460
endTimeChanged(const QTime & time)461 void TasksGeneralPanelImpl::endTimeChanged(const QTime &time)
462 {
463 if (!scheduleEndTime->isEnabled()) {
464 return;
465 }
466 if (endDateTime() < startDateTime())
467 {
468 scheduleStartTime->blockSignals(true);
469 setStartTime(time);
470 scheduleStartTime->blockSignals(false);
471 }
472
473 if (scheduleType->currentIndex() == 6 /*FixedInterval*/)
474 {
475 estimationTypeChanged(estimateType->currentIndex());
476 }
477 checkAllFieldsFilled();
478 }
479
scheduleTypeChanged(int value)480 void TasksGeneralPanelImpl::scheduleTypeChanged(int value)
481 {
482 estimationTypeChanged(estimateType->currentIndex());
483 enableDateTime(value);
484 checkAllFieldsFilled();
485 }
486
487
startDateTime()488 QDateTime TasksGeneralPanelImpl::startDateTime()
489 {
490 return QDateTime(startDate(), startTime(), Qt::LocalTime);
491 }
492
493
endDateTime()494 QDateTime TasksGeneralPanelImpl::endDateTime()
495 {
496 return QDateTime(endDate(), endTime(), Qt::LocalTime);
497 }
498
setStartTime(const QTime & time)499 void TasksGeneralPanelImpl::setStartTime(const QTime &time)
500 {
501 scheduleStartTime->setTime(QTime(time.hour(), time.minute(), 0));
502 }
503
setEndTime(const QTime & time)504 void TasksGeneralPanelImpl::setEndTime(const QTime &time)
505 {
506 scheduleEndTime->setTime(QTime(time.hour(), time.minute(), 0));
507 }
508
startTime() const509 QTime TasksGeneralPanelImpl::startTime() const
510 {
511 QTime t = scheduleStartTime->time();
512 t.setHMS(t.hour(), t.minute(), 0);
513 return t;
514 }
515
endTime()516 QTime TasksGeneralPanelImpl::endTime()
517 {
518 QTime t = scheduleEndTime->time();
519 t.setHMS(t.hour(), t.minute(), 0);
520 return t;
521 }
522
startDate()523 QDate TasksGeneralPanelImpl::startDate()
524 {
525 return scheduleStartDate->date();
526 }
527
528
endDate()529 QDate TasksGeneralPanelImpl::endDate()
530 {
531 return scheduleEndDate->date();
532 }
533
setStartDateTime(const QDateTime & dt)534 void TasksGeneralPanelImpl::setStartDateTime(const QDateTime &dt)
535 {
536 setStartDate(dt.date());
537 setStartTime(dt.time());
538 }
539
540
setEndDateTime(const QDateTime & dt)541 void TasksGeneralPanelImpl::setEndDateTime(const QDateTime &dt)
542 {
543 setEndDate(dt.date());
544 setEndTime(dt.time());
545 }
546
setStartDate(const QDate & date)547 void TasksGeneralPanelImpl::setStartDate(const QDate &date)
548 {
549 scheduleStartDate->setDate(date);
550 }
551
552
setEndDate(const QDate & date)553 void TasksGeneralPanelImpl::setEndDate(const QDate &date)
554 {
555 scheduleEndDate->setDate(date);
556 }
557
setRisktype(int r)558 void TasksGeneralPanelImpl::setRisktype(int r)
559 {
560 risk->setCurrentIndex(r);
561 }
562
risktype() const563 int TasksGeneralPanelImpl::risktype() const
564 {
565 return risk->currentIndex();
566 }
567
calendar() const568 Calendar *TasksGeneralPanelImpl::calendar() const
569 {
570 return m_calendars.value(calendarCombo->currentIndex());
571 }
572
573 } //KPlato namespace
574