1 /*
2   TaskEditor.cpp
3 
4   This file is part of Charm, a task-based time tracking application.
5 
6   Copyright (C) 2008-2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
7 
8   Author: Mirko Boehm <mirko.boehm@kdab.com>
9   Author: Frank Osterfeld <frank.osterfeld@kdab.com>
10 
11   This program is free software; you can redistribute it and/or modify
12   it under the terms of the GNU General Public License as published by
13   the Free Software Foundation, either version 2 of the License, or
14   (at your option) any later version.
15 
16   This program is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20 
21   You should have received a copy of the GNU General Public License
22   along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24 
25 #include "TaskEditor.h"
26 #include "SelectTaskDialog.h"
27 #include "ViewHelpers.h"
28 
29 #include "Core/CharmConstants.h"
30 #include "Core/CharmDataModel.h"
31 #include "Core/TaskTreeItem.h"
32 
33 #include <QCalendarWidget>
34 #include <QCheckBox>
35 #include <QPushButton>
36 #include <QMessageBox>
37 
38 #include "ui_TaskEditor.h"
39 
TaskEditor(QWidget * parent)40 TaskEditor::TaskEditor(QWidget *parent)
41     : QDialog(parent)
42     , m_ui(new Ui::TaskEditor)
43 {
44     m_ui->setupUi(this);
45     m_ui->dateEditFrom->calendarWidget()->setFirstDayOfWeek(Qt::Monday);
46     m_ui->dateEditFrom->calendarWidget()->setVerticalHeaderFormat(QCalendarWidget::ISOWeekNumbers);
47     m_ui->dateEditTo->calendarWidget()->setFirstDayOfWeek(Qt::Monday);
48     m_ui->dateEditTo->calendarWidget()->setVerticalHeaderFormat(QCalendarWidget::ISOWeekNumbers);
49     connect(m_ui->pushButtonParent, &QPushButton::clicked, this, &TaskEditor::slotSelectParent);
50     connect(m_ui->dateEditFrom, &QDateEdit::dateChanged, this, &TaskEditor::slotDateChanged);
51     connect(m_ui->dateEditTo, &QDateEdit::dateChanged, this, &TaskEditor::slotDateChanged);
52     connect(m_ui->checkBoxFrom, &QCheckBox::clicked, this, &TaskEditor::slotCheckBoxChecked);
53     connect(m_ui->checkBoxUntil, &QCheckBox::clicked, this, &TaskEditor::slotCheckBoxChecked);
54 }
55 
~TaskEditor()56 TaskEditor::~TaskEditor()
57 {
58 }
59 
setTask(const Task & task)60 void TaskEditor::setTask(const Task &task)
61 {
62     Q_ASSERT(m_ui);
63     m_task = task;
64     const TaskTreeItem &taskTreeItem
65         = MODEL.charmDataModel()->taskTreeItem(task.id());
66     m_ui->labelTaskName->setText(MODEL.charmDataModel()->fullTaskName(taskTreeItem.task()));
67     m_ui->lineEditName->setText(task.name());
68     m_ui->checkBoxTrackable->setChecked(task.trackable());
69     if (task.parent() != 0) {
70         const TaskTreeItem &parentItem
71             = MODEL.charmDataModel()->taskTreeItem(task.parent());
72         const QString name = parentItem.task().name();
73         m_ui->pushButtonParent->setText(name);
74     } else {
75         m_ui->pushButtonParent->setText(tr("Choose Parent Task"));
76     }
77     if (task.parent() == 0) {
78         m_ui->checkBoxTopLevel->setChecked(true);
79     } else {
80         m_ui->checkBoxTopLevel->setChecked(false);
81     }
82     QDate start = task.validFrom().date();
83     if (start.isValid()) {
84         m_ui->dateEditFrom->setDate(start);
85         m_ui->checkBoxFrom->setChecked(false);
86     } else {
87         m_ui->checkBoxFrom->setChecked(true);
88         m_ui->dateEditFrom->setDate(QDate::currentDate());
89     }
90     QDate end = task.validUntil().date();
91     if (end.isValid()) {
92         m_ui->dateEditTo->setDate(end);
93         m_ui->checkBoxUntil->setChecked(false);
94     } else {
95         m_ui->checkBoxUntil->setChecked(true);
96         m_ui->dateEditTo->setDate(QDate::currentDate());
97     }
98     m_ui->lineEditComment->setText(task.comment());
99     checkInvariants();
100 }
101 
getTask() const102 Task TaskEditor::getTask() const
103 {
104     Task newTask = m_task;
105     newTask.setName(m_ui->lineEditName->text());
106     newTask.setTrackable(m_ui->checkBoxTrackable->isChecked());
107     if (m_ui->checkBoxTopLevel->isChecked())
108         newTask.setParent(0);
109     if (m_ui->checkBoxFrom->isChecked()) {
110         newTask.setValidFrom(QDateTime());
111     } else {
112         newTask.setValidFrom(m_ui->dateEditFrom->dateTime());
113     }
114     if (m_ui->checkBoxUntil->isChecked()) {
115         newTask.setValidUntil(QDateTime());
116     } else {
117         newTask.setValidUntil(m_ui->dateEditTo->dateTime());
118     }
119     newTask.setComment(m_ui->lineEditComment->text());
120     return newTask;
121 }
122 
parentTreeIsTree(TaskId newParent,TaskId task)123 bool parentTreeIsTree(TaskId newParent, TaskId task)
124 {
125     QList<TaskId> parents;
126     TaskId parent = newParent;
127     while (parent != 0) {
128         parents << parent;
129         const TaskTreeItem &parentItem = MODEL.charmDataModel()->taskTreeItem(parent);
130         parent = parentItem.task().parent();
131     }
132     return !parents.contains(task);
133 }
134 
slotSelectParent()135 void TaskEditor::slotSelectParent()
136 {
137     SelectTaskDialog dialog(this);
138     dialog.setNonTrackableSelectable();
139     Q_FOREVER {
140         if (dialog.exec()) {
141             TaskId newParentId = dialog.selectedTask();
142             const TaskTreeItem &parentItem
143                 = MODEL.charmDataModel()->taskTreeItem(newParentId);
144             QString name = m_task.name();
145             QString parent = parentItem.task().name();
146             if (parentTreeIsTree(newParentId, m_task.id())) {
147                 m_task.setParent(dialog.selectedTask());
148                 if (newParentId != 0) {
149                     m_ui->pushButtonParent->setText(parent);
150                 } else {
151                     m_ui->pushButtonParent->setText(tr("Choose Parent Task"));
152                 }
153 
154                 break;
155             }
156             QMessageBox::information(this, tr("Please choose another task"),
157                                      tr(
158                                          "The task \"%1\" cannot be selected as the parent task for \"%2\","
159                                          " because they are the same, or \"%3\" is a direct or indirect subtask of \"%4\".")
160                                      .arg(parent, name, parent, name));
161         } else {
162             break;
163         }
164     }
165 }
166 
slotDateChanged(const QDate &)167 void TaskEditor::slotDateChanged(const QDate &)
168 {
169     checkInvariants();
170 }
171 
slotCheckBoxChecked(bool)172 void TaskEditor::slotCheckBoxChecked(bool)
173 {
174     checkInvariants();
175 }
176 
checkInvariants()177 void TaskEditor::checkInvariants()
178 {
179     bool acceptable;
180     // the start and end dates are acceptable if
181     // * both start and end date are deactivated,
182     // * if only one date is set, or
183     // * if none are set, and the start date is before the end date
184     if (m_ui->checkBoxFrom->isChecked() && m_ui->checkBoxUntil->isChecked()) {
185         acceptable = true;
186     } else if (m_ui->checkBoxFrom->isChecked() || m_ui->checkBoxUntil->isChecked()) {
187         acceptable = true;
188     } else if (m_ui->dateEditFrom->dateTime() < m_ui->dateEditTo->dateTime()) {
189         acceptable = true;
190     } else {
191         acceptable = false;
192     }
193 
194     m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(acceptable);
195 }
196