Branch data Line data Source code
1 : : #include "TaskEditDialog.h"
2 : :
3 : : #include "ui/ThemeManager.h"
4 : :
5 : : #include <QCheckBox>
6 : : #include <QComboBox>
7 : : #include <QDateEdit>
8 : : #include <QDialogButtonBox>
9 : : #include <QFormLayout>
10 : : #include <QLabel>
11 : : #include <QLineEdit>
12 : : #include <QTextEdit>
13 : : #include "ui/MarkdownHighlighter.h"
14 : : #include <QPushButton>
15 : : #include <QUuid>
16 : : #include <QVBoxLayout>
17 : :
18 [ + - ]: 7 : TaskEditDialog::TaskEditDialog(QWidget *parent) : QDialog(parent) {
19 [ + - ]: 14 : setObjectName(QStringLiteral("taskEditDialog"));
20 [ + - ]: 7 : setAttribute(Qt::WA_StyledBackground, true);
21 [ + - ]: 7 : setupUi();
22 : 7 : }
23 : :
24 : : // T-76.B3: Runtime language switching
25 : 20 : void TaskEditDialog::changeEvent(QEvent *event) {
26 : 20 : QDialog::changeEvent(event);
27 [ - + ]: 20 : if (event->type() == QEvent::LanguageChange)
28 : 0 : retranslateUi();
29 : 20 : }
30 : :
31 : 0 : void TaskEditDialog::retranslateUi() {
32 [ # # # # : 0 : setWindowTitle(m_isNew ? tr("New task") : tr("Edit task"));
# # # # ]
33 [ # # # # : 0 : m_headerLabel->setText((m_isNew ? QStringLiteral("\u2705 ")
# # ]
34 [ # # # # : 0 : : QStringLiteral("\u270f\ufe0f "))
# # ]
35 [ # # # # ]: 0 : + windowTitle());
36 [ # # # # ]: 0 : m_titleLabel->setText(tr("Title:"));
37 [ # # # # ]: 0 : m_calendarLabel->setText(tr("Calendar:"));
38 [ # # # # ]: 0 : m_dueLabel->setText(tr("Due:"));
39 [ # # # # ]: 0 : m_priorityLabel->setText(tr("Priority:"));
40 [ # # # # ]: 0 : m_descriptionLabel->setText(tr("Description:"));
41 [ # # # # ]: 0 : m_titleEdit->setPlaceholderText(tr("Enter task..."));
42 [ # # # # ]: 0 : m_descEdit->setPlaceholderText(tr("Description (Markdown)..."));
43 [ # # # # ]: 0 : m_priorityCombo->setItemText(0, tr("Normal"));
44 [ # # # # ]: 0 : m_priorityCombo->setItemText(1, tr("High"));
45 [ # # # # ]: 0 : m_priorityCombo->setItemText(2, tr("Urgent"));
46 [ # # # # ]: 0 : m_cancelBtn->setText(tr("Cancel"));
47 [ # # # # ]: 0 : m_saveBtn->setText(tr("Save"));
48 : 0 : }
49 : :
50 : 7 : void TaskEditDialog::setupUi() {
51 [ + - ]: 7 : setMinimumWidth(400);
52 [ + - + - ]: 7 : setWindowTitle(tr("New task"));
53 : :
54 [ + - + - : 7 : auto *mainLayout = new QVBoxLayout(this);
- + - - ]
55 [ + - ]: 7 : mainLayout->setSpacing(12);
56 [ + - ]: 7 : mainLayout->setContentsMargins(16, 16, 16, 16);
57 : :
58 : : // Header
59 [ + - + - : 14 : m_headerLabel = new QLabel(QStringLiteral("\u2705 ") + windowTitle(), this);
+ - + - -
+ - - ]
60 [ + - ]: 7 : QFont hf = m_headerLabel->font();
61 [ + - ]: 7 : hf.setPointSize(14);
62 [ + - ]: 7 : hf.setBold(true);
63 [ + - ]: 7 : m_headerLabel->setFont(hf);
64 [ + - ]: 7 : mainLayout->addWidget(m_headerLabel);
65 : :
66 : : // Form
67 [ + - + - : 7 : auto *form = new QFormLayout();
- + - - ]
68 [ + - ]: 7 : form->setLabelAlignment(Qt::AlignRight);
69 [ + - ]: 7 : form->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
70 : :
71 : : // Title
72 [ + - + - : 7 : m_titleEdit = new QLineEdit(this);
- + - - ]
73 [ + - + - ]: 7 : m_titleEdit->setPlaceholderText(tr("Enter task..."));
74 [ + - + - : 7 : m_titleLabel = new QLabel(tr("Title:"), this);
+ - - + -
- ]
75 [ + - ]: 7 : form->addRow(m_titleLabel, m_titleEdit);
76 : :
77 : : // Calendar
78 [ + - + - : 7 : m_calendarCombo = new QComboBox(this);
- + - - ]
79 [ + - + - : 7 : m_calendarLabel = new QLabel(tr("Calendar:"), this);
+ - - + -
- ]
80 [ + - ]: 7 : form->addRow(m_calendarLabel, m_calendarCombo);
81 : :
82 : : // Due date (optional)
83 [ + - + - : 7 : auto *dueRow = new QHBoxLayout();
- + - - ]
84 [ + - + - : 7 : m_dueEnabled = new QCheckBox(this);
- + - - ]
85 [ + - + - : 7 : m_dueDate = new QDateEdit(QDate::currentDate().addDays(7), this);
+ - + - -
+ - - ]
86 [ + - ]: 7 : m_dueDate->setCalendarPopup(true);
87 [ + - ]: 14 : m_dueDate->setDisplayFormat(QStringLiteral("dd.MM.yyyy"));
88 [ + - ]: 7 : m_dueDate->setEnabled(false);
89 [ + - ]: 7 : dueRow->addWidget(m_dueEnabled);
90 [ + - ]: 7 : dueRow->addWidget(m_dueDate);
91 [ + - + - : 7 : m_dueLabel = new QLabel(tr("Due:"), this);
+ - - + -
- ]
92 [ + - ]: 7 : form->addRow(m_dueLabel, dueRow);
93 : 7 : connect(m_dueEnabled, &QCheckBox::toggled, m_dueDate,
94 [ + - ]: 7 : &QDateEdit::setEnabled);
95 : :
96 : : // Priority
97 [ + - + - : 7 : m_priorityCombo = new QComboBox(this);
- + - - ]
98 [ + - + - ]: 7 : m_priorityCombo->addItem(tr("Normal"), 0);
99 [ + - + - ]: 7 : m_priorityCombo->addItem(tr("High"), 5);
100 [ + - + - ]: 7 : m_priorityCombo->addItem(tr("Urgent"), 9);
101 [ + - ]: 14 : m_priorityCombo->addItem(QStringLiteral("★ Starred"), 1);
102 [ + - + - : 7 : m_priorityLabel = new QLabel(tr("Priority:"), this);
+ - - + -
- ]
103 [ + - ]: 7 : form->addRow(m_priorityLabel, m_priorityCombo);
104 : :
105 : : // Description
106 [ + - + - : 7 : m_descEdit = new QTextEdit(this);
- + - - ]
107 [ + - ]: 7 : m_descEdit->setAcceptRichText(false);
108 [ + - ]: 7 : m_descEdit->setMaximumHeight(160);
109 [ + - + - ]: 7 : m_descEdit->setPlaceholderText(tr("Description (Markdown)..."));
110 [ + - + - : 7 : new MarkdownHighlighter(m_descEdit->document());
+ - - + -
- ]
111 [ + - + - : 7 : m_descriptionLabel = new QLabel(tr("Description:"), this);
+ - - + -
- ]
112 [ + - ]: 7 : form->addRow(m_descriptionLabel, m_descEdit);
113 : :
114 [ + - ]: 7 : mainLayout->addLayout(form);
115 : :
116 : : // Buttons
117 [ + - + - : 7 : auto *btnBox = new QDialogButtonBox(this);
- + - - ]
118 [ + - + - ]: 7 : m_cancelBtn = btnBox->addButton(tr("Cancel"), QDialogButtonBox::RejectRole);
119 [ + - + - ]: 7 : m_saveBtn = btnBox->addButton(tr("Save"), QDialogButtonBox::AcceptRole);
120 [ + - ]: 14 : m_saveBtn->setObjectName(QStringLiteral("taskSaveButton"));
121 [ + - ]: 7 : m_saveBtn->setDefault(true);
122 [ + - ]: 7 : mainLayout->addWidget(btnBox);
123 : :
124 : : // Connections
125 [ + - ]: 7 : connect(btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
126 [ + - ]: 7 : connect(btnBox, &QDialogButtonBox::accepted, this, [this]() {
127 [ # # # # : 0 : if (m_titleEdit->text().trimmed().isEmpty()) {
# # ]
128 : 0 : m_titleEdit->setFocus();
129 [ # # ]: 0 : m_titleEdit->setStyleSheet(
130 : 0 : QStringLiteral("QLineEdit { border: 1px solid %1; }")
131 [ # # # # : 0 : .arg(ThemeManager::instance().color(
# # ]
132 : 0 : QStringLiteral("@danger"))));
133 : 0 : return;
134 : : }
135 : 0 : accept();
136 : : });
137 [ + - ]: 7 : connect(m_titleEdit, &QLineEdit::textChanged, this, [this]() {
138 [ + - ]: 3 : m_titleEdit->setStyleSheet(QString());
139 : 3 : });
140 : :
141 : : // Dialog styling lives in main.qss (67.B3); the save button picks up
142 : : // the QPushButton#taskSaveButton accent rule.
143 : 7 : }
144 : :
145 : 6 : void TaskEditDialog::setCalendars(const QList<CalendarInfo> &calendars) {
146 : 6 : m_calendars = calendars;
147 : 6 : m_calendarCombo->clear();
148 [ + + ]: 14 : for (int i = 0; i < calendars.size(); ++i) {
149 : 8 : const auto &cal = calendars.at(i);
150 [ - + ]: 8 : QString name = cal.displayName.isEmpty() ? cal.path : cal.displayName;
151 [ + - ]: 8 : m_calendarCombo->addItem(name, i);
152 : 8 : }
153 : 6 : }
154 : :
155 : 3 : void TaskEditDialog::setTask(const CalendarTask &task) {
156 : 3 : m_isNew = false;
157 : 3 : m_originalTask = task;
158 : : // Sprint 56: Update title for edit mode
159 [ + - + - ]: 3 : setWindowTitle(tr("Edit task"));
160 [ + - + - : 6 : m_headerLabel->setText(QStringLiteral("\u270f\ufe0f ") + windowTitle());
+ - ]
161 : :
162 : 3 : m_titleEdit->setText(task.summary);
163 : :
164 : : // Due
165 [ + + ]: 3 : if (task.due.isValid()) {
166 : 1 : m_dueEnabled->setChecked(true);
167 : 1 : m_dueDate->setDate(task.due.date());
168 : : }
169 : :
170 : : // Priority
171 [ + - ]: 6 : for (int i = 0; i < m_priorityCombo->count(); ++i) {
172 [ + - + - : 6 : if (m_priorityCombo->itemData(i).toInt() == task.priority) {
+ + ]
173 : 3 : m_priorityCombo->setCurrentIndex(i);
174 : 3 : break;
175 : : }
176 : : }
177 : :
178 : : // Calendar
179 [ + - ]: 4 : for (int i = 0; i < m_calendars.size(); ++i) {
180 : 4 : const auto &cal = m_calendars.at(i);
181 [ + + - + : 7 : if (cal.path == task.calendarPath &&
+ + ]
182 [ - - ]: 3 : (task.accountId.isEmpty() || cal.accountId == task.accountId)) {
183 : 3 : m_calendarCombo->setCurrentIndex(i);
184 : 3 : break;
185 : : }
186 : : }
187 : : // Sprint 56: Track calendar changes in edit mode
188 [ + - ]: 3 : connect(m_calendarCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
189 : 3 : this, [this]() { m_calendarChanged = true; });
190 : :
191 : 3 : m_descEdit->setPlainText(task.description);
192 : 3 : }
193 : :
194 : 2 : CalendarTask TaskEditDialog::result() const {
195 : 2 : CalendarTask t = m_originalTask;
196 [ + - + - ]: 2 : t.summary = m_titleEdit->text().trimmed();
197 [ + - ]: 2 : t.description = m_descEdit->toPlainText();
198 [ + - + - ]: 2 : t.priority = m_priorityCombo->currentData().toInt();
199 : :
200 [ + - + + ]: 2 : if (m_dueEnabled->isChecked()) {
201 [ + - + - : 1 : t.due = QDateTime(m_dueDate->date(), QTime(0, 0));
+ - ]
202 : : } else {
203 : 1 : t.due = QDateTime();
204 : : }
205 : :
206 [ + + ]: 2 : if (m_isNew) {
207 : 1 : t.status = QStringLiteral("NEEDS-ACTION");
208 [ + - + - ]: 1 : if (m_calendarCombo->currentIndex() >= 0) {
209 [ + - + - ]: 1 : const int calIndex = m_calendarCombo->currentData().toInt();
210 [ + - + - : 1 : if (calIndex >= 0 && calIndex < m_calendars.size()) {
+ - ]
211 : 1 : t.calendarPath = m_calendars.at(calIndex).path;
212 : 1 : t.accountId = m_calendars.at(calIndex).accountId;
213 : : }
214 : : }
215 [ + - ]: 1 : if (t.uid.isEmpty())
216 [ + - + - ]: 1 : t.uid = QUuid::createUuid().toString(QUuid::WithoutBraces);
217 [ + - ]: 1 : } else if (m_calendarChanged) {
218 : : // Sprint 56: Calendar move — update path
219 [ + - + - ]: 1 : const int calIndex = m_calendarCombo->currentData().toInt();
220 [ + - + - : 1 : if (calIndex >= 0 && calIndex < m_calendars.size()) {
+ - ]
221 : 1 : t.calendarPath = m_calendars.at(calIndex).path;
222 : 1 : t.accountId = m_calendars.at(calIndex).accountId;
223 : : }
224 : : }
225 : :
226 : 2 : return t;
227 : 0 : }
|