|
1 /** |
|
2 * EditProfileFerment.cpp is part of bmsapp. |
|
3 * |
|
4 * bmsapp is free software: you can redistribute it and/or modify |
|
5 * it under the terms of the GNU General Public License as published by |
|
6 * the Free Software Foundation, either version 3 of the License, or |
|
7 * (at your option) any later version. |
|
8 * |
|
9 * bmsapp 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 |
|
12 * GNU General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU General Public License |
|
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 */ |
|
17 #include "EditProfileFerment.h" |
|
18 #include "../ui/ui_EditProfileFerment.h" |
|
19 #include "bmsapp.h" |
|
20 |
|
21 |
|
22 EditProfileFerment::EditProfileFerment(int id, QWidget *parent) : QDialog(parent), ui(new Ui::EditProfileFerment) |
|
23 { |
|
24 QSqlQuery query; |
|
25 |
|
26 qDebug() << "EditProfileFerment record:" << id; |
|
27 ui->setupUi(this); |
|
28 this->recno = id; |
|
29 |
|
30 ui->sensorEdit->addItem(tr("Beer")); |
|
31 ui->sensorEdit->addItem(tr("Fridge")); |
|
32 |
|
33 if (id >= 0) { |
|
34 query.prepare("SELECT * FROM profile_fermentation WHERE record = :recno"); |
|
35 query.bindValue(":recno", id); |
|
36 query.exec(); |
|
37 query.next(); |
|
38 |
|
39 ui->nameEdit->setText(query.value(2).toString()); |
|
40 ui->temploEdit->setValue(query.value(3).toDouble()); |
|
41 ui->temphiEdit->setValue(query.value(4).toDouble()); |
|
42 ui->sensorEdit->setCurrentIndex(query.value(5).toInt()); |
|
43 |
|
44 QJsonParseError parseError; |
|
45 const auto& json = query.value(8).toString(); |
|
46 |
|
47 if (!json.trimmed().isEmpty()) { |
|
48 const auto& formattedJson = QString("%1").arg(json); |
|
49 this->steps = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); |
|
50 |
|
51 if (parseError.error != QJsonParseError::NoError) |
|
52 qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; |
|
53 } |
|
54 |
|
55 } else { |
|
56 /* Set some defaults */ |
|
57 const auto& formattedJson = QString("[]"); |
|
58 this->steps = QJsonDocument::fromJson(formattedJson.toUtf8()); |
|
59 } |
|
60 |
|
61 connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditProfileFerment::is_changed); |
|
62 connect(ui->temploEdit, &QDoubleSpinBox::textChanged, this, &EditProfileFerment::templo_changed); |
|
63 connect(ui->temphiEdit, &QDoubleSpinBox::textChanged, this, &EditProfileFerment::temphi_changed); |
|
64 connect(ui->sensorEdit, &QComboBox::currentTextChanged, this, &EditProfileFerment::is_changed); |
|
65 connect(ui->stepsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int))); |
|
66 |
|
67 ui->saveButton->setEnabled(false); |
|
68 ui->deleteButton->setEnabled((id >= 0) ? true:false); |
|
69 |
|
70 emit refreshTable(); |
|
71 |
|
72 WindowTitle(); |
|
73 } |
|
74 |
|
75 |
|
76 void EditProfileFerment::refreshTable() |
|
77 { |
|
78 QString w; |
|
79 QWidget* pWidget; |
|
80 QHBoxLayout* pLayout; |
|
81 double d; |
|
82 |
|
83 // qDebug() << "refreshTable" << this->steps << this->steps.isArray() << this->steps.array().size() ; |
|
84 /* |
|
85 * During filling the table turn off the cellChanged signal because every cell that is filled |
|
86 * triggers the cellChanged signal. The QTableWidget has no better signal to use. |
|
87 */ |
|
88 this->ignoreChanges = true; |
|
89 |
|
90 const QStringList labels({tr("Step name"), tr("Start °C"), tr("End °C"), tr("Sensor"), tr("Ramp time"), tr("Rest time"), tr("Button")}); |
|
91 ui->stepsTable->setColumnCount(7); |
|
92 ui->stepsTable->setColumnWidth(0, 248); /* Step name */ |
|
93 ui->stepsTable->setColumnWidth(1, 75); /* Start temp */ |
|
94 ui->stepsTable->setColumnWidth(2, 75); /* End temp */ |
|
95 ui->stepsTable->setColumnWidth(3, 120); /* Sensor */ |
|
96 ui->stepsTable->setColumnWidth(4, 85); /* Ramp time */ |
|
97 ui->stepsTable->setColumnWidth(5, 85); /* Rest time */ |
|
98 ui->stepsTable->setColumnWidth(6, 80); /* Button */ |
|
99 ui->stepsTable->setHorizontalHeaderLabels(labels); |
|
100 ui->stepsTable->verticalHeader()->hide(); |
|
101 ui->stepsTable->setRowCount(this->steps.array().size()); |
|
102 |
|
103 totalsteps = 0; |
|
104 duration = 0; |
|
105 |
|
106 if (this->steps.isArray()) { |
|
107 totalsteps = this->steps.array().size(); |
|
108 for (int i = 0; i < this->steps.array().size(); i++) { |
|
109 QJsonObject obj = this->steps.array().at(i).toObject(); |
|
110 |
|
111 ui->stepsTable->setItem(i, 0, new QTableWidgetItem(obj["name"].toString())); |
|
112 |
|
113 /* Numbers can be double quoted or not, the old application could do this wrong. */ |
|
114 if (obj["target_lo"].isString()) |
|
115 d = QString(obj["target_lo"].toString()).toDouble(); |
|
116 else |
|
117 d = obj["target_lo"].toDouble(); |
|
118 w = QString("%1").arg(d, 2, 'f', 1, '0'); |
|
119 QTableWidgetItem *item = new QTableWidgetItem(w); |
|
120 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); |
|
121 ui->stepsTable->setItem(i, 1, item); |
|
122 |
|
123 if (obj["target_hi"].isString()) |
|
124 d = QString(obj["target_hi"].toString()).toDouble(); |
|
125 else |
|
126 d = obj["target_hi"].toDouble(); |
|
127 w = QString("%1").arg(d, 2, 'f', 1, '0'); |
|
128 item = new QTableWidgetItem(w); |
|
129 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); |
|
130 ui->stepsTable->setItem(i, 2, item); |
|
131 |
|
132 /* Adding fridgemode 0, 1 as combobox. */ |
|
133 QComboBox* myComboBox = new QComboBox(); |
|
134 myComboBox->addItem(tr("Beer")); |
|
135 myComboBox->addItem(tr("Fridge")); |
|
136 ui->stepsTable->setCellWidget(i, 3, myComboBox); |
|
137 |
|
138 if (obj["fridgemode"].isString()) { |
|
139 d = QString(obj["fridgemode"].toString()).toDouble(); |
|
140 } else { |
|
141 d = obj["fridgemode"].toDouble(); |
|
142 } |
|
143 |
|
144 myComboBox->setCurrentIndex((int)d); |
|
145 connect<void(QComboBox::*)(int)>(myComboBox, &QComboBox::currentIndexChanged, this, &EditProfileFerment::combo_Changed); |
|
146 |
|
147 if (obj["steptime"].isString()) |
|
148 d = QString(obj["steptime"].toString()).toDouble(); |
|
149 else |
|
150 d = obj["steptime"].toDouble(); |
|
151 w = QString("%1").arg(d, 1, 'f', 0, '0'); |
|
152 item = new QTableWidgetItem(w); |
|
153 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); |
|
154 ui->stepsTable->setItem(i, 4, item); |
|
155 duration += (int)d; |
|
156 |
|
157 if (obj["resttime"].isString()) |
|
158 d = QString(obj["resttime"].toString()).toDouble(); |
|
159 else |
|
160 d = obj["resttime"].toDouble(); |
|
161 w = QString("%1").arg(d, 1, 'f', 0, '0'); |
|
162 item = new QTableWidgetItem(w); |
|
163 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); |
|
164 ui->stepsTable->setItem(i, 5, item); |
|
165 duration += (int)d; |
|
166 |
|
167 /* Add the Delete row button */ |
|
168 pWidget = new QWidget(); |
|
169 QPushButton* btn_edit = new QPushButton(); |
|
170 btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */ |
|
171 btn_edit->setText(tr("Delete")); |
|
172 connect(btn_edit, SIGNAL(clicked()), this, SLOT(on_deleteRow_clicked())); |
|
173 pLayout = new QHBoxLayout(pWidget); |
|
174 pLayout->addWidget(btn_edit); |
|
175 pLayout->setContentsMargins(5, 0, 5, 0); |
|
176 pWidget->setLayout(pLayout); |
|
177 ui->stepsTable->setCellWidget(i, 6, pWidget); |
|
178 } |
|
179 } |
|
180 |
|
181 /* Show the calculated total fermentation time. */ |
|
182 ui->totalEdit->setText(Utils::hours_to_string(duration)); |
|
183 this->ignoreChanges = false; |
|
184 } |
|
185 |
|
186 |
|
187 EditProfileFerment::~EditProfileFerment() |
|
188 { |
|
189 qDebug() << "EditProfileFerment done"; |
|
190 delete ui; |
|
191 emit entry_changed(); |
|
192 } |
|
193 |
|
194 |
|
195 /* |
|
196 * Window header, mark any change with '**' |
|
197 */ |
|
198 void EditProfileFerment::WindowTitle() |
|
199 { |
|
200 QString txt; |
|
201 |
|
202 if (this->recno < 0) { |
|
203 txt = QString(tr("BMSapp - Add new fermentation profile")); |
|
204 } else { |
|
205 txt = QString(tr("BMSapp - Edit fermentation profile %1").arg(this->recno)); |
|
206 } |
|
207 |
|
208 if (this->textIsChanged) { |
|
209 txt.append((QString(" **"))); |
|
210 } |
|
211 setWindowTitle(txt); |
|
212 } |
|
213 |
|
214 |
|
215 void EditProfileFerment::on_saveButton_clicked() |
|
216 { |
|
217 QSqlQuery query; |
|
218 |
|
219 /* If there are errors in the form, show a message and do "return;" */ |
|
220 if (ui->nameEdit->text().length() < 2) { |
|
221 QMessageBox::warning(this, tr("Edit Ferment"), tr("Name empty or too short.")); |
|
222 return; |
|
223 } |
|
224 |
|
225 if (this->textIsChanged) { |
|
226 if (this->recno == -1) { |
|
227 query.prepare("INSERT INTO profile_fermentation SET name=:name, inittemp_lo=:templo, " |
|
228 "inittemp_hi=:temphi, fridgemode=:fridgemode, totalsteps=:totalsteps, duration=:duration, " |
|
229 "steps=:steps, uuid = :uuid"); |
|
230 } else { |
|
231 query.prepare("UPDATE profile_fermentation SET name=:name, inittemp_lo=:templo, " |
|
232 "inittemp_hi=:temphi, fridgemode=:fridgemode, totalsteps=:totalsteps, duration=:duration, " |
|
233 "steps=:steps WHERE record = :recno"); |
|
234 } |
|
235 query.bindValue(":name", ui->nameEdit->text()); |
|
236 query.bindValue(":templo", QString("%1").arg(ui->temploEdit->value(), 2, 'f', 1, '0')); |
|
237 query.bindValue(":temphi", QString("%1").arg(ui->temphiEdit->value(), 2, 'f', 1, '0')); |
|
238 query.bindValue(":fridgemode", ui->sensorEdit->currentIndex()); |
|
239 query.bindValue(":totalsteps", QString("%1").arg(totalsteps)); |
|
240 query.bindValue(":duration", QString("%1").arg(duration)); |
|
241 query.bindValue(":steps", this->steps.toJson(QJsonDocument::Compact)); |
|
242 if (this->recno == -1) { |
|
243 query.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36)); |
|
244 } else { |
|
245 query.bindValue(":recno", this->recno); |
|
246 } |
|
247 query.exec(); |
|
248 if (query.lastError().isValid()) { |
|
249 qDebug() << "EditProfileFerment" << query.lastError(); |
|
250 QMessageBox::warning(this, tr("Database error"), |
|
251 tr("MySQL error: %1\n%2\n%3") |
|
252 .arg(query.lastError().nativeErrorCode()) |
|
253 .arg(query.lastError().driverText()) |
|
254 .arg(query.lastError().databaseText())); |
|
255 } else { |
|
256 qDebug() << "EditProfileFerment Saved"; |
|
257 } |
|
258 } |
|
259 |
|
260 ui->saveButton->setEnabled(false); |
|
261 this->textIsChanged = false; |
|
262 WindowTitle(); |
|
263 } |
|
264 |
|
265 |
|
266 void EditProfileFerment::on_deleteButton_clicked() |
|
267 { |
|
268 QSqlQuery query; |
|
269 |
|
270 query.prepare("DELETE FROM profile_fermentation WHERE record = :recno"); |
|
271 query.bindValue(":recno", this->recno); |
|
272 query.exec(); |
|
273 if (query.lastError().isValid()) { |
|
274 qDebug() << "EditProfileFerment" << query.lastError(); |
|
275 QMessageBox::warning(this, tr("Database error"), |
|
276 tr("MySQL error: %1\n%2\n%3") |
|
277 .arg(query.lastError().nativeErrorCode()) |
|
278 .arg(query.lastError().driverText()) |
|
279 .arg(query.lastError().databaseText())); |
|
280 } else { |
|
281 qDebug() << "EditProfileFerment Deleted" << this->recno; |
|
282 } |
|
283 |
|
284 this->close(); |
|
285 this->setResult(1); |
|
286 } |
|
287 |
|
288 |
|
289 void EditProfileFerment::is_changed() |
|
290 { |
|
291 ui->saveButton->setEnabled(true); |
|
292 ui->deleteButton->setEnabled((this->recno >= 0) ? true:false); |
|
293 this->textIsChanged = true; |
|
294 WindowTitle(); |
|
295 } |
|
296 |
|
297 |
|
298 /* |
|
299 * Rebuild the json string from the table contents. |
|
300 */ |
|
301 void EditProfileFerment::make_Json() |
|
302 { |
|
303 QTableWidgetItem *item; |
|
304 QJsonArray array; |
|
305 |
|
306 for (int i = 0; i < ui->stepsTable->rowCount(); i++) { |
|
307 |
|
308 QJsonObject obj; |
|
309 item = ui->stepsTable->item(i, 0); |
|
310 obj.insert("name", item->text()); |
|
311 item = ui->stepsTable->item(i, 1); |
|
312 obj.insert("target_lo", item->text().toDouble()); |
|
313 item = ui->stepsTable->item(i, 2); |
|
314 obj.insert("target_hi", item->text().toDouble()); |
|
315 QWidget *widget = ui->stepsTable->cellWidget(i, 3); |
|
316 obj.insert("fridgemode", static_cast<QComboBox*>(widget)->currentIndex() ); |
|
317 item = ui->stepsTable->item(i, 4); |
|
318 obj.insert("steptime", item->text().toInt()); |
|
319 item = ui->stepsTable->item(i, 5); |
|
320 obj.insert("resttime", item->text().toInt()); |
|
321 // qDebug() << "make_Json" << i << obj; |
|
322 array.append(obj); /* Append this object */ |
|
323 } |
|
324 |
|
325 // qDebug() << array; |
|
326 /* Copy to the global array and refresh */ |
|
327 this->steps.setArray(array); |
|
328 is_changed(); |
|
329 emit refreshTable(); |
|
330 } |
|
331 |
|
332 |
|
333 void EditProfileFerment::cell_Changed(int nRow, int nCol) |
|
334 { |
|
335 QString w; |
|
336 |
|
337 if (this->ignoreChanges) |
|
338 return; |
|
339 |
|
340 qDebug() << "Cell at row " + QString::number(nRow) + " column " + QString::number(nCol) + " was changed."; |
|
341 |
|
342 /* |
|
343 * Check the temperature ranges. Make sure that the high temperature is at least |
|
344 * 0.1 degree higher then the low temperature. |
|
345 */ |
|
346 if (nCol == 1) { // Low temperature was changed |
|
347 if (ui->stepsTable->item(nRow, 1)->text().toDouble() > (ui->stepsTable->item(nRow, 2)->text().toDouble() - 0.1)) { |
|
348 w = QString("%1").arg(ui->stepsTable->item(nRow, 1)->text().toDouble() + 0.1, 2, 'f', 1, '0'); |
|
349 ui->stepsTable->setItem(nRow, 2, new QTableWidgetItem(w)); |
|
350 } |
|
351 } else if (nCol == 2) { // High temperature was changed |
|
352 if (ui->stepsTable->item(nRow, 2)->text().toDouble() < (ui->stepsTable->item(nRow, 1)->text().toDouble() + 0.1)) { |
|
353 w = QString("%1").arg(ui->stepsTable->item(nRow, 2)->text().toDouble() - 0.1, 2, 'f', 1, '0'); |
|
354 ui->stepsTable->setItem(nRow, 1, new QTableWidgetItem(w)); |
|
355 } |
|
356 } |
|
357 make_Json(); |
|
358 } |
|
359 |
|
360 |
|
361 void EditProfileFerment::combo_Changed() |
|
362 { |
|
363 make_Json(); |
|
364 } |
|
365 |
|
366 |
|
367 void EditProfileFerment::templo_changed() |
|
368 { |
|
369 if (ui->temploEdit->value() > (ui->temphiEdit->value() - 0.1)) |
|
370 ui->temphiEdit->setValue(ui->temploEdit->value() + 0.1); |
|
371 is_changed(); |
|
372 } |
|
373 |
|
374 |
|
375 void EditProfileFerment::temphi_changed() |
|
376 { |
|
377 if (ui->temphiEdit->value() < (ui->temploEdit->value() + 0.1)) |
|
378 ui->temploEdit->setValue(ui->temphiEdit->value() - 0.1); |
|
379 is_changed(); |
|
380 } |
|
381 |
|
382 |
|
383 /* |
|
384 * Add new row and initialize all fields. |
|
385 */ |
|
386 void EditProfileFerment::on_addButton_clicked() |
|
387 { |
|
388 QWidget* pWidget; |
|
389 QHBoxLayout* pLayout; |
|
390 |
|
391 qDebug() << "Add row" << totalsteps; |
|
392 this->ignoreChanges = true; |
|
393 ui->stepsTable->insertRow(totalsteps); |
|
394 ui->stepsTable->setItem(totalsteps, 0, new QTableWidgetItem(QString("new row %1").arg(totalsteps))); |
|
395 ui->stepsTable->setItem(totalsteps, 1, new QTableWidgetItem(QString("19.8"))); |
|
396 ui->stepsTable->setItem(totalsteps, 2, new QTableWidgetItem(QString("20.0"))); |
|
397 QComboBox* myComboBox = new QComboBox(); |
|
398 myComboBox->addItem(tr("Beer")); |
|
399 myComboBox->addItem(tr("Fridge")); |
|
400 myComboBox->setCurrentIndex(0); |
|
401 ui->stepsTable->setCellWidget(totalsteps, 3, myComboBox); |
|
402 ui->stepsTable->setItem(totalsteps, 4, new QTableWidgetItem(QString("0"))); |
|
403 ui->stepsTable->setItem(totalsteps, 5, new QTableWidgetItem(QString("0"))); |
|
404 |
|
405 pWidget = new QWidget(); |
|
406 QPushButton* btn_edit = new QPushButton(); |
|
407 btn_edit->setObjectName(QString("%1").arg(totalsteps)); /* Send row with the button */ |
|
408 btn_edit->setText(tr("Delete")); |
|
409 connect(btn_edit, SIGNAL(clicked()), this, SLOT(on_deleteRow_clicked())); |
|
410 pLayout = new QHBoxLayout(pWidget); |
|
411 pLayout->addWidget(btn_edit); |
|
412 pLayout->setContentsMargins(5, 0, 5, 0); |
|
413 pWidget->setLayout(pLayout); |
|
414 ui->stepsTable->setCellWidget(totalsteps, 6, pWidget); |
|
415 |
|
416 this->ignoreChanges = false; |
|
417 make_Json(); |
|
418 } |
|
419 |
|
420 |
|
421 void EditProfileFerment::on_deleteRow_clicked() |
|
422 { |
|
423 QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); |
|
424 int row = pb->objectName().toInt(); |
|
425 qDebug() << "Delete row" << row; |
|
426 ui->stepsTable->removeRow(row); |
|
427 make_Json(); |
|
428 } |
|
429 |
|
430 |
|
431 void EditProfileFerment::on_quitButton_clicked() |
|
432 { |
|
433 if (this->textIsChanged) { |
|
434 int rc = QMessageBox::warning(this, tr("Fermentation changed"), tr("This fermentation profile has been modified. Save changes?"), |
|
435 QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); |
|
436 switch (rc) { |
|
437 case QMessageBox::Save: |
|
438 on_saveButton_clicked(); |
|
439 break; /* Saved and then Quit */ |
|
440 case QMessageBox::Discard: |
|
441 break; /* Quit without Save */ |
|
442 case QMessageBox::Cancel: |
|
443 return; /* Return to the editor page */ |
|
444 } |
|
445 } |
|
446 |
|
447 this->close(); |
|
448 this->setResult(1); |
|
449 } |