/[qspeakers]/branches/qtcharts/mainwindow.cpp
ViewVC logotype

Contents of /branches/qtcharts/mainwindow.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 299 - (show annotations)
Wed Nov 25 20:05:12 2020 UTC (6 months, 2 weeks ago) by ben
File size: 54510 byte(s)
Display SPL value
1 #include <iostream>
2 #include <QtCore>
3 #include <QDebug>
4 #include <QMessageBox>
5 #include <QPrintDialog>
6 #include <QPrinter>
7 #include <QPrinterInfo>
8 #include <QPainter>
9 #include <QUndoStack>
10 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
11 #include <QStandardPaths>
12 #endif
13 #include <QDesktopServices>
14
15 #include "mainwindow.h"
16 #include "ui_mainwindow.h"
17
18 #include "speaker.h"
19 #include "speakerdb.h"
20 #include "importexport.h"
21 #include "plot.h"
22 #include "searchdialog.h"
23 #include "listdialog.h"
24 #include "system.h"
25 #include "optimizer.h"
26 #include "undocommands.h"
27
28 MainWindow::MainWindow(QWidget *parent) :
29 QMainWindow(parent),
30 ui(new Ui::MainWindow),
31 projectSaved(false),
32 isModifying(false),
33 spkDialog(nullptr),
34 fileDialog(nullptr),
35 searchDialog(nullptr),
36 listDialog(nullptr),
37 bandpassDialog(nullptr),
38 currentSpeakerNumber(0),
39 currentTabIndex(-1),
40 sealedPlot(nullptr),
41 portedPlot(nullptr),
42 bandpassPlot(nullptr),
43 notInDbSpeaker(nullptr),
44 commandStack(new QUndoStack(this))
45 {
46 ui->setupUi(this);
47
48 /* insert QChartView plotters in ui */
49
50 QHBoxLayout *ly1 = new QHBoxLayout(ui->sealedVolumePlotWidget);
51 sealedPlot = new Plot(tr("Sealed volume frequency response"), ui->sealedVolumePlotWidget);
52 sealedPlot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
53 ly1->insertWidget(0, sealedPlot);
54
55 QHBoxLayout *ly2 = new QHBoxLayout(ui->portedPlotWidget);
56 portedPlot = new Plot(tr("Ported volume frequency response"), ui->portedPlotWidget);
57 portedPlot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
58 ly2->insertWidget(0, portedPlot);
59
60 QHBoxLayout *ly3 = new QHBoxLayout(ui->bandpassPlotWidget);
61 bandpassPlot = new Plot(tr("Bandpass volumes frequency response"), ui->bandpassPlotWidget);
62 bandpassPlot->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored);
63 ly3->insertWidget(0, bandpassPlot);
64
65 /* test db */
66 if (!SpeakerDb::exists() || (SpeakerDb::lastModified() <= SpeakerDb::pkgInstalled())) {
67 QFile pkg_db(SpeakerDb::pkgPath());
68 if (SpeakerDb::merge(pkg_db)) qDebug() << "merged db";
69 }
70
71 /* fill speaker combos */
72 ui->vendorComboBox->addItems(SpeakerDb::getVendors());
73 ui->vendorComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents);
74 ui->modelComboBox->addItems(SpeakerDb::getModelsByVendor(ui->vendorComboBox->currentText()));
75 ui->modelComboBox->setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy::AdjustToContents);
76
77 /* undo history is empty */
78 this->ui->actionUndo->setEnabled(false);
79 this->ui->actionRedo->setEnabled(false);
80
81 /* restore last saved project... */
82 ImportExport::restoreProject(currentSpeaker, currentSealedBox, currentPortedBox, currentBandPassBox, &currentSpeakerNumber, &currentTabIndex);
83 projectSaved = true;
84
85 /* on first run, no speaker. reset to the speaker of the combos if needed */
86 if (!currentSpeaker.isValid()) {
87 currentSpeaker = SpeakerDb::getByVendorAndModel(ui->vendorComboBox->currentText(), ui->modelComboBox->currentText());
88 }
89
90 /* display sibling number */
91 ui->numberSpinBox->setValue(currentSpeakerNumber);
92
93 /* select restored tab */
94 ui->tabWidget->setCurrentIndex(currentTabIndex);
95
96 /* tab index change is not connected yet, do menu modifications manually */
97 if (currentTabIndex != 1) {
98 /* index != 1 needs to deactivate the ported alignments menu */
99 setActivateActions(ui->menuPorted_alignments->actions(), false);
100 }
101 if (currentTabIndex != 2) {
102 /* index != 2 needs to deactivate the bandpass alignment menu */
103 ui->actionBandpass_alignment->setEnabled(false);
104 }
105
106 syncUiFromCurrentSpeaker(currentSpeaker);
107 syncUiFromCurrentSealedBox(currentSealedBox);
108 syncUiFromCurrentPortedBox(currentPortedBox);
109 syncUiFromCurrentBandPassBox(currentBandPassBox);
110
111 /*
112 * connections
113 */
114
115 /* menu actions */
116 linkMenus();
117
118 /* tabs actions */
119 linkTabs();
120
121 /* non-graphic */
122 linkInternals();
123 }
124
125 MainWindow::~MainWindow()
126 {
127 delete ui;
128 }
129
130 void MainWindow::onProjectSave()
131 {
132 projectSaved = true;
133
134 ImportExport::saveProject(currentSpeaker, currentSealedBox, currentPortedBox, currentBandPassBox, currentSpeakerNumber, currentTabIndex);
135 }
136
137 void MainWindow::onProjectExport()
138 {
139 QString home = MainWindow::getHome();
140 QString fileName = QFileDialog::getSaveFileName(this, tr("Export File"), home + tr("/untitled.qsp"), tr("QSpeakers project (*.qsp)"));
141
142 QFile file(fileName);
143 ImportExport::exportProject(file, currentSpeaker, currentSealedBox, currentPortedBox, currentBandPassBox, currentSpeakerNumber, currentTabIndex);
144 }
145
146 void MainWindow::onProjectImport()
147 {
148 if (!projectSaved) {
149 QMessageBox::StandardButton pressed =
150 QMessageBox::question(this, tr("Project not saved"),
151 tr("The current project has not been saved. Continue anyway?"),
152 QMessageBox::Yes|QMessageBox::No);
153 if (pressed == QMessageBox::No)
154 return;
155 }
156
157 QString home = MainWindow::getHome();
158 QString fileName = QFileDialog::getOpenFileName(this, tr("Import File"), home, tr("QSpeakers project (*.qsp)"));
159
160 /* user cancelled */
161 if (fileName.isEmpty())
162 return;
163
164 /* clear undo history */
165 this->commandStack->clear();
166
167 unlinkInternals();
168 unlinkTabs();
169
170 QFile file(fileName);
171 ImportExport::importProject(currentSpeaker, currentSealedBox, currentPortedBox, currentBandPassBox, &currentSpeakerNumber, &currentTabIndex, file);
172 ImportExport::setSavePath(fileName);
173 setWindowFilePath(ImportExport::getSavePath());
174
175 ui->tabWidget->setCurrentIndex(currentTabIndex);
176
177 syncUiFromCurrentSpeaker(currentSpeaker);
178 syncUiFromCurrentSealedBox(currentSealedBox);
179 syncUiFromCurrentPortedBox(currentPortedBox);
180 syncUiFromCurrentBandPassBox(currentBandPassBox);
181
182 linkInternals();
183 linkTabs();
184
185 if (notInDbSpeaker)
186 onSpeakerModify();
187 }
188
189 void MainWindow::onProjectQuit()
190 {
191 if (projectSaved) {
192 this->close();
193 return;
194 }
195
196 if (!currentSpeaker.isValid()) {
197 this->close();
198 return;
199 }
200
201 QMessageBox::StandardButton pressed =
202 QMessageBox::question(this, tr("Project not saved"),
203 tr("The current project has not been saved. Exit anyway?"),
204 QMessageBox::Yes|QMessageBox::No);
205 if (pressed == QMessageBox::Yes)
206 this->close();
207 }
208
209 void MainWindow::syncUiFromCurrentSpeaker(const Speaker& spk)
210 {
211 if (!spk.isValid())
212 return;
213
214 /* look if vendor is in the db */
215 QList<Speaker> speakers = SpeakerDb::getByVendor(spk.getVendor());
216
217 unlinkTabs();
218 if (speakers.count() == 0) {
219 /* if this is an unknown vendor, insert in combo */
220 ui->vendorComboBox->insertItem(0, spk.getVendor());
221 ui->vendorComboBox->setCurrentIndex(0);
222 ui->modelComboBox->clear();
223 /* this means also a new model */
224 ui->modelComboBox->insertItem(0, spk.getModel());
225 ui->modelComboBox->setCurrentIndex(0);
226 notInDbSpeaker = &spk;
227 } else {
228 /* select correct vendor */
229 ui->vendorComboBox->setCurrentIndex(ui->vendorComboBox->findText(spk.getVendor()));
230 /* fill speakers of this vendor */
231 ui->modelComboBox->clear();
232 ui->modelComboBox->addItems(SpeakerDb::getModelsByVendor(spk.getVendor()));
233
234 int i = 0;
235 for (i = 0; i < speakers.count(); i++) {
236 Speaker speaker = speakers[i];
237 if (speaker.getModel() == spk.getModel()) {
238 if (speaker != spk) {
239 /* speaker conflict, renaming */
240 currentSpeaker.setModel(speaker.getModel() + "*");
241 ui->modelComboBox->addItem(spk.getModel());
242 ui->modelComboBox->setCurrentIndex(ui->modelComboBox->findText(spk.getModel()));
243 notInDbSpeaker = &spk;
244 } else {
245 /* okay, select correct combo */
246 ui->modelComboBox->setCurrentIndex(ui->modelComboBox->findText(spk.getModel()));
247 notInDbSpeaker = nullptr;
248 }
249
250 break;
251 }
252 }
253
254 if (i == speakers.count()) {
255 /* vendor was ok but model was not in DB */
256 ui->modelComboBox->addItem(spk.getModel());
257 ui->modelComboBox->setCurrentIndex(ui->modelComboBox->findText(spk.getModel()));
258 notInDbSpeaker = &spk;
259 }
260 }
261
262 ui->numberSpinBox->setValue(currentSpeakerNumber);
263
264 /* display speaker's values */
265 ui->splValueLabel->setNum(currentSpeaker.getSpl());
266 ui->fsValueLabel->setNum(currentSpeaker.getFs());
267 ui->qtsValueLabel->setNum(currentSpeaker.getQts());
268 ui->vasValueLabel->setNum(currentSpeaker.getVas());
269 ui->diaValueLabel->setNum(currentSpeaker.getDia());
270 ui->zValueLabel->setNum(currentSpeaker.getZ());
271 ui->vcValueLabel->setNum(currentSpeaker.getVc());
272
273 linkTabs();
274 }
275
276 void MainWindow::syncUiFromCurrentSealedBox(const SealedBox& box)
277 {
278 unlinkTabs();
279 ui->sealedVolumeDoubleSpinBox->setValue(box.getVolume());
280
281 System s(currentSpeaker, &box, currentSpeakerNumber);
282 sealedPlot->clear();
283 for (double f = sealedPlot->getXmin(); f < portedPlot->getXmax(); f *= pow(10, 1.0/100.0)) {
284 double db = s.response(f);
285 sealedPlot->appendPointF(QPointF(f, db));
286 }
287 sealedPlot->draw3dbVLine();
288 linkTabs();
289 }
290
291 void MainWindow::syncUiFromCurrentPortedBox(const PortedBox& box)
292 {
293 unlinkTabs();
294 ui->portedVolumeDoubleSpinBox->setValue(box.getBoxVolume());
295 ui->portedResonancedoubleSpinBox->setValue(box.getResFreq());
296 ui->portedPortsNumberSpinBox->setValue(box.getPortNum());
297 ui->portedPortDiameterDoubleSpinBox->setValue(box.getPortDiam());
298 ui->portedPortSlotWidthButton->setChecked(box.getSlotPortActivated());
299 ui->portedPortSlotWidthButton->clicked(box.getSlotPortActivated());
300 ui->portedPortSlotWidthDoubleSpinBox->setValue(box.getSlotWidth());
301 ui->portedPortSlotHeightLineEdit->setText(QString::number(box.getSlotHeight(), 'f', 2));
302 ui->portedPortLengthLineEdit->setText(QString::number(box.getPortLen(), 'f', 2));
303
304 System s(currentSpeaker, &box, currentSpeakerNumber);
305 portedPlot->clear();
306 for (double f = portedPlot->getXmin(); f < portedPlot->getXmax(); f *= pow(10, 1.0/100.0)) {
307 double db = s.response(f);
308 portedPlot->appendPointF(QPointF(f, db));
309 }
310 portedPlot->draw3dbVLine();
311 linkTabs();
312 }
313
314 void MainWindow::syncUiFromCurrentBandPassBox(const BandPassBox& box)
315 {
316 unlinkTabs();
317 ui->bandPassSealedVolumeDoubleSpinBox->setValue(box.getSealedBoxVolume());
318 ui->bandPassPortedVolumeDoubleSpinBox->setValue(box.getPortedBoxVolume());
319 ui->bandPassPortedResonanceDoubleSpinBox->setValue(box.getPortedBoxResFreq());
320 ui->bandPassPortsNumberSpinBox->setValue(box.getPortedBoxPortNum());
321 ui->bandPassPortDiameterDoubleSpinBox->setValue(box.getPortedBoxPortDiam());
322 ui->bandPassPortLengthLineEdit->setText(QString::number(box.getPortedBoxPortLen(), 'f', 2));
323
324 System s(currentSpeaker, &box, currentSpeakerNumber);
325 bandpassPlot->clear();
326 for (double f = bandpassPlot->getXmin(); f < bandpassPlot->getXmax(); f *= pow(10, 1.0/100.0)) {
327 double db = s.response(f);
328 bandpassPlot->appendPointF(QPointF(f, db));
329 }
330 bandpassPlot->draw3dbVLine();
331 linkTabs();
332 }
333
334 void MainWindow::setCurrentSpeaker(const Speaker &spk)
335 {
336 if ((currentSpeaker.getVendor() == spk.getVendor()) &&
337 (currentSpeaker.getModel() == spk.getModel()))
338 return;
339
340 currentSpeaker = spk;
341 emit currentSpeakerChanged(spk);
342 }
343
344 bool MainWindow::print(QPrinter *printer)
345 {
346 QPainter painter;
347 if (!painter.begin(printer))
348 return false;
349
350 QRect page = printer->pageRect();
351
352 qreal step = page.height() / 4.0;
353
354 if (ui->tabWidget->currentWidget() == ui->sealedTab) {
355 System s(currentSpeaker, &currentSealedBox, currentSpeakerNumber);
356 s.render(&painter, QRectF(page.left(), page.top(), page.width(), step));
357 //sealedPlot->setUseOpenGL(false);
358 sealedPlot->render(&painter, QRectF(page.left(), page.top() + step, page.width(), page.height() - step));
359 //sealedPlot->setUseOpenGL(true);
360 } else if (ui->tabWidget->currentWidget() == ui->portedTab) {
361 System s(currentSpeaker, &currentPortedBox, currentSpeakerNumber);
362 s.render(&painter, QRectF(page.left(), page.top(), page.width(), step));
363 //portedPlot->setUseOpenGL(false);
364 portedPlot->render(&painter, QRectF(page.left(), page.top() + step, page.width(), page.height() - step));
365 //portedPlot->setUseOpenGL(true);
366 } else {
367 System s(currentSpeaker, &currentBandPassBox, currentSpeakerNumber);
368 s.render(&painter, QRectF(page.left(), page.top(), page.width(), step));
369 //bandpassPlot->setUseOpenGL(false);
370 bandpassPlot->render(&painter, QRectF(page.left(), page.top() + step, page.width(), page.height() - step));
371 //bandpassPlot->setUseOpenGL(true);
372 }
373
374 painter.end();
375 return true;
376 }
377
378 QString MainWindow::getHome()
379 {
380 QString home;
381 #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
382 home = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
383 #else
384 home = QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
385 #endif
386 return home;
387 }
388
389 void MainWindow::onBandpassAlignment()
390 {
391 bandpassDialog = new BandpassDialog(this);
392 connect(bandpassDialog, SIGNAL(optimizeRequested(double, double)), this, SLOT(onBandpassOptimizeRequested(double, double)));
393 connect(bandpassDialog, SIGNAL(optimizeCancelled()), this, SLOT(onBandpassOptimizeCancelled()));
394 bandpassDialog->show();
395 }
396
397 void MainWindow::onBandpassOptimizeRequested(double s, double pa)
398 {
399 if (ui->tabWidget->currentWidget() == ui->bandpassTab) {
400 Optimizer optimizer(currentSpeaker, &currentBandPassBox, currentSpeakerNumber, this);
401 optimizer.bandpassAlignS_Pa(s, pa);
402 emit currentBandPassBoxChanged(currentBandPassBox);
403 }
404
405 disconnect(bandpassDialog, SIGNAL(optimizeRequested(double,double)), this, SLOT(onBandpassOptimizeRequested(double,double)));
406 disconnect(bandpassDialog, SIGNAL(optimizeCancelled()), this, SLOT(onBandpassOptimizeCancelled()));
407 bandpassDialog->close();
408 bandpassDialog->deleteLater();
409 bandpassDialog = nullptr;
410 }
411
412 void MainWindow::onBandpassOptimizeCancelled()
413 {
414 disconnect(bandpassDialog, SIGNAL(optimizeRequested(double,double)), this, SLOT(onBandpassOptimizeRequested(double,double)));
415 disconnect(bandpassDialog, SIGNAL(optimizeCancelled()), this, SLOT(onBandpassOptimizeCancelled()));
416 bandpassDialog->close();
417 bandpassDialog->deleteLater();
418 bandpassDialog = nullptr;
419 }
420
421 void MainWindow::onCurvePlot()
422 {
423 QString home = getHome();
424 QString box = ui->tabWidget->currentWidget()->objectName().replace("Tab", "Box");
425 QString f = QString("QSpeakers %1 %2").arg(currentSpeaker.getModel()).arg(box);
426 f.replace(' ', '_');
427 QString fileName = QFileDialog::getSaveFileName(this, tr("Export for Gnuplot"), home + QDir::separator() + f + ".dat", tr("Gnuplot data (*.dat)"));
428 exportPlot(fileName, currentTabIndex);
429 }
430
431 void MainWindow::on3DScadExport()
432 {
433 QString home = getHome();
434 QString box = ui->tabWidget->currentWidget()->objectName().replace("Tab", "Box");
435 QString f = QString("QSpeakers %1 %2 3D").arg(currentSpeaker.getModel()).arg(box);
436 f.replace(' ', '_');
437 QString fileName = QFileDialog::getSaveFileName(this, tr("Export for 3D OpenSCAD"), home + QDir::separator() + f + ".scad", tr("OpenSCAD script (*.scad)"));
438 exportScad3D(fileName, currentTabIndex);
439 }
440
441 void MainWindow::on2DScadExport()
442 {
443 QString home = getHome();
444 QString box = ui->tabWidget->currentWidget()->objectName().replace("Tab", "Box");
445 QString f = QString("QSpeakers %1 %2 2D").arg(currentSpeaker.getModel()).arg(box);
446 f.replace(' ', '_');
447 QString fileName = QFileDialog::getSaveFileName(this, tr("Export for 2D OpenSCAD"), home + QDir::separator() + f + ".scad", tr("OpenSCAD script (*.scad)"));
448 exportScad2D(fileName, currentTabIndex);
449 }
450
451 void MainWindow::exportPlot(const QString& outfileName, int tabindex)
452 {
453 QFile file(outfileName);
454 QString f("# QSpeakers plot: ");
455 f += currentSpeaker.getModel() + "\n";
456 f += "# freq., dB.\n";
457
458 QLineSeries* series;
459 switch (tabindex) {
460 case 0: /* closed box */
461 series = sealedPlot->series();
462 break;
463 case 1: /* vented box */
464 series = portedPlot->series();
465 break;
466 case 2: /* bandpass box */
467 series = bandpassPlot->series();
468 break;
469 default:
470 return;
471 }
472
473 QVector<QPointF> points = series->pointsVector();
474 int len = points.length();
475
476 QString line;
477 for (int i = 0; i < len; i++) {
478 line.clear();
479 QPointF p = points.at(i);
480 line += QString("%1 %2 ").arg(p.x()).arg(p.y());
481 line.chop(1);
482 line.append('\n');
483 f.append(line);
484 }
485
486 file.open(QIODevice::WriteOnly);
487 file.write(f.toUtf8());
488 file.close();
489 }
490
491 /* FIXME: should be taken from a popup before to export */
492 #define MARGIN 5 /* cm */
493 #define WOODTHICK 2 /* cm */
494 #define SAWTHICK 0.1 /* cm */
495
496 void MainWindow::exportScad(const QString& scad, const QString &outfileName, int tabindex)
497 {
498 QFile templ(scad);
499 templ.open(QIODevice::ReadOnly);
500 QString s = templ.readAll();
501
502 s.replace("__MODEL__", currentSpeaker.getModel());
503 s.replace("__NUMBER__", QString::number(currentSpeakerNumber));
504 s.replace("__MARGIN__", QString::number(MARGIN));
505 s.replace("__DIAMETER__", QString::number(currentSpeaker.getDia() * 100));
506 s.replace("__WOODTHICK__", QString::number(WOODTHICK));
507 s.replace("__SAWTHICK__", QString::number(SAWTHICK));
508 if (tabindex == 0) {
509 s.replace("__SEALEDBOXVOLUME__", QString::number(currentSealedBox.getVolume()));
510 } else if (tabindex == 1) {
511 s.replace("__PORTEDBOXVOLUME__", QString::number(currentPortedBox.getBoxVolume()));
512 s.replace("__PORTEDBOXPORTNUMBER__", QString::number(currentPortedBox.getPortNum()));
513 } else if (tabindex == 2) {
514 s.replace("__SEALEDBOXVOLUME__", QString::number(currentBandPassBox.getSealedBoxVolume()));
515 s.replace("__PORTEDBOXVOLUME__", QString::number(currentBandPassBox.getPortedBoxVolume()));
516 s.replace("__PORTEDBOXPORTNUMBER__", QString::number(currentBandPassBox.getPortedBoxPortNum()));
517 s.replace("__PORTEDBOXPORTSLOTWIDTH__", "0"); /* not supported yet */
518 s.replace("__PORTEDBOXPORTSLOTHEIGHT__", "0"); /* not supported yet */
519 }
520
521 if (tabindex == 1) {
522 if (currentPortedBox.getSlotPortActivated()) {
523 s.replace("__PORTEDBOXPORTSLOTACTIVATED__", "true");
524 s.replace("__PORTEDBOXPORTSLOTWIDTH__", QString::number(currentPortedBox.getSlotWidth()));
525 s.replace("__PORTEDBOXPORTSLOTHEIGHT__", QString::number(currentPortedBox.getSlotHeight()));
526 } else {
527 s.replace("__PORTEDBOXPORTSLOTACTIVATED__", "false");
528 s.replace("__PORTEDBOXPORTSLOTWIDTH__", "0");
529 s.replace("__PORTEDBOXPORTSLOTHEIGHT__", "0");
530 }
531 s.replace("__PORTEDBOXPORTDIAMETER__", QString::number(currentPortedBox.getPortDiam()));
532 s.replace("__PORTEDBOXPORTLENGTH__", QString::number(currentPortedBox.getPortLen()));
533 } else if (tabindex == 2) {
534 s.replace("__PORTEDBOXPORTSLOTACTIVATED__", "false");
535 s.replace("__PORTEDBOXPORTDIAMETER__", QString::number(currentBandPassBox.getPortedBoxPortDiam()));
536 s.replace("__PORTEDBOXPORTLENGTH__", QString::number(currentBandPassBox.getPortedBoxPortLen()));
537 }
538
539 QFile f(outfileName);
540 f.open(QIODevice::WriteOnly);
541 f.write(s.toUtf8());
542 f.close();
543 }
544
545 void MainWindow::exportScad3D(const QString &outfileName, int tabindex)
546 {
547 QFile file(outfileName);
548 QString scad;
549
550 switch (tabindex) {
551 case 0: /* closed box */
552 /* non-prod version: */
553 scad = "../qtcharts/sealedbox_template.scad";
554 if (!QFileInfo::exists(scad))
555 #ifdef __mswin
556 scad = (QCoreApplication::applicationDirPath() + QDir::separator() + "sealedbox_template.scad");
557 #else
558 scad = (DATADIR "/" TARGET "/sealedbox_template.scad");
559 #endif
560 break;
561 case 1: /* vented box */
562 /* non-prod version: */
563 scad = "../qtcharts/portedbox_template.scad";
564 if (!QFileInfo::exists(scad))
565 #ifdef __mswin
566 scad = (QCoreApplication::applicationDirPath() + QDir::separator() + "sealedbox_template.scad");
567 #else
568 scad = (DATADIR "/" TARGET "/portedbox_template.scad");
569 #endif
570 break;
571 case 2: /* bandpass box */
572 /* non-prod version: */
573 scad = "../qtcharts/bandpassbox_template.scad";
574 if (!QFileInfo::exists(scad))
575 #ifdef __mswin
576 scad = (QCoreApplication::applicationDirPath() + QDir::separator() + "sealedbox_template.scad");
577 #else
578 scad = (DATADIR "/" TARGET "/bandpassbox_template.scad");
579 #endif
580 break;
581 default:
582 return;
583 }
584
585 exportScad(scad, outfileName, tabindex);
586 }
587
588
589 void MainWindow::exportScad2D(const QString &outfileName, int tabindex)
590 {
591 QFile file(outfileName);
592 QString scad;
593
594 switch (tabindex) {
595 case 0: /* closed box */
596 /* non-prod version: */
597 scad = "../qtcharts/sealedbox_cutting_template.scad";
598 if (!QFileInfo::exists(scad))
599 #ifdef __mswin
600 scad = (QCoreApplication::applicationDirPath() + QDir::separator() + "sealedbox_cutting_template.scad");
601 #else
602 scad = (DATADIR "/" TARGET "/sealedbox_cutting_template.scad");
603 #endif
604 break;
605 case 1: /* vented box */
606 /* non-prod version: */
607 scad = "../qtcharts/portedbox_cutting_template.scad";
608 if (!QFileInfo::exists(scad))
609 #ifdef __mswin
610 scad = (QCoreApplication::applicationDirPath() + QDir::separator() + "sealedbox_cutting_template.scad");
611 #else
612 scad = (DATADIR "/" TARGET "/portedbox_cutting_template.scad");
613 #endif
614 break;
615 case 2: /* bandpass box */
616 /* non-prod version: */
617 scad = "../qtcharts/bandpassbox_cutting_template.scad";
618 if (!QFileInfo::exists(scad))
619 #ifdef __mswin
620 scad = (QCoreApplication::applicationDirPath() + QDir::separator() + "sealedbox_cutting_template.scad");
621 #else
622 scad = (DATADIR "/" TARGET "/bandpassbox_cutting_template.scad");
623 #endif
624 break;
625 default:
626 return;
627 }
628
629 exportScad(scad, outfileName, tabindex);
630 }
631
632 void MainWindow::linkMenus()
633 {
634 connect(ui->actionProjectSave, SIGNAL(triggered()), this, SLOT(onProjectSave()));
635 connect(ui->actionProjectQuit, SIGNAL(triggered()), this, SLOT(onProjectQuit()));
636 connect(ui->actionSpeakerNew, SIGNAL(triggered()), this, SLOT(onSpeakerNew()));
637 connect(ui->actionSpeakerModify, SIGNAL(triggered()), this, SLOT(onSpeakerModify()));
638 connect(ui->actionSpeakerRemove, SIGNAL(triggered()), this, SLOT(onSpeakerRemove()));
639 connect(ui->actionProjectExport, SIGNAL(triggered()), this, SLOT(onProjectExport()));
640 connect(ui->actionProjectImport, SIGNAL(triggered()), this, SLOT(onProjectImport()));
641 connect(ui->actionEditOptimize, SIGNAL(triggered()), this, SLOT(onEditOptimize()));
642 connect(ui->actionSpeakerSearch, SIGNAL(triggered()), this, SLOT(onSpeakerSearch()));
643 connect(ui->actionProjectPrint, SIGNAL(triggered()), this, SLOT(onProjectPrint()));
644 connect(ui->actionUndo, SIGNAL(triggered()), this, SLOT(onUndo()));
645 connect(ui->actionRedo, SIGNAL(triggered()), this, SLOT(onRedo()));
646 connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(onAboutAbout()));
647
648 /* ported alignments sub-menu */
649 connect(ui->actionModerate_Inf, SIGNAL(triggered()), this, SLOT(onAlignModerate_Inf()));
650 connect(ui->actionLegendre, SIGNAL(triggered()), this, SLOT(onAlignLegendre()));
651 connect(ui->actionBessel, SIGNAL(triggered()), this, SLOT(onAlignBessel()));
652 connect(ui->actionBullock, SIGNAL(triggered()), this, SLOT(onAlignBullock()));
653 connect(ui->actionKeele_Hoge, SIGNAL(triggered()), this, SLOT(onAlignKeele_Hoge()));
654
655 /* bandpass alignment */
656 connect(ui->actionBandpass_alignment, SIGNAL(triggered()), this, SLOT(onBandpassAlignment()));
657
658 /* exports menu */
659 connect(ui->actionCurve_Plot, SIGNAL(triggered()), this, SLOT(onCurvePlot()));
660 connect(ui->action3D_OpenScad, SIGNAL(triggered()), this, SLOT(on3DScadExport()));
661 connect(ui->action2D_OpenScad, SIGNAL(triggered()), this, SLOT(on2DScadExport()));
662 }
663
664 void MainWindow::linkTabs()
665 {
666 connect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(onCurrentTabChanged(int)));
667
668 /* spk combos action */
669 connect(ui->vendorComboBox, SIGNAL(activated(QString)), this, SLOT(onVendorChanged(QString)));
670 connect(ui->modelComboBox, SIGNAL(activated(QString)), this, SLOT(onModelChanged(QString)));
671
672 /* drivers number spin action */
673 connect(ui->numberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onNumberSpinChanged(int)));
674
675 connect(ui->sealedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onSealedVolumeDoubleSpinChanged(double)));
676
677 connect(ui->portedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedVolumeDoubleSpinChanged(double)));
678 connect(ui->portedResonancedoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedResonanceDoubleSpinChanged(double)));
679 connect(ui->portedPortsNumberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onPortedPortsNumberSpinChanged(int)));
680 connect(ui->portedPortDiameterDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedPortDiameterDoubleSpinChanged(double)));
681 connect(ui->portedPortSlotWidthButton, SIGNAL(clicked(bool)), this, SLOT(onPortedSlotPortActivated(bool)));
682 connect(ui->portedPortSlotWidthDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedSlotWidthDoubleSpinChanged(double)));
683
684 connect(ui->bandPassSealedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassSealedVolumeDoubleSpinChanged(double)));
685 connect(ui->bandPassPortedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassPortedVolumeDoubleSpinChanged(double)));
686 connect(ui->bandPassPortedResonanceDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassPortedResonanceDoubleSpinChanged(double)));
687 connect(ui->bandPassPortsNumberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onBandPassPortNumSpinChanged(int)));
688 connect(ui->bandPassPortDiameterDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassPortDiameterDoubleSpinChanged(double)));
689 }
690
691 void MainWindow::linkInternals()
692 {
693 connect(this, SIGNAL(currentSpeakerChanged(Speaker)), this, SLOT(onCurrentSpeakerChanged(const Speaker&)));
694 connect(this, SIGNAL(currentSealedBoxChanged(SealedBox)), this, SLOT(onCurrentSealedBoxChanged(const SealedBox&)));
695 connect(this, SIGNAL(currentPortedBoxChanged(PortedBox)), this, SLOT(onCurrentPortedBoxChanged(const PortedBox&)));
696 connect(this, SIGNAL(currentBandPassBoxChanged(BandPassBox)), this, SLOT(onCurrentBandPassBoxChanged(const BandPassBox&)));
697
698 connect(this->commandStack, SIGNAL(canUndoChanged(bool)), ui->actionUndo, SLOT(setEnabled(bool)));
699 connect(this->commandStack, SIGNAL(canRedoChanged(bool)), ui->actionRedo, SLOT(setEnabled(bool)));
700 }
701
702 void MainWindow::unlinkMenus()
703 {
704 disconnect(ui->actionProjectSave, SIGNAL(triggered()), this, SLOT(onProjectSave()));
705 disconnect(ui->actionProjectQuit, SIGNAL(triggered()), this, SLOT(onProjectQuit()));
706 disconnect(ui->actionSpeakerNew, SIGNAL(triggered()), this, SLOT(onSpeakerNew()));
707 disconnect(ui->actionSpeakerModify, SIGNAL(triggered()), this, SLOT(onSpeakerModify()));
708 disconnect(ui->actionSpeakerRemove, SIGNAL(triggered()), this, SLOT(onSpeakerRemove()));
709 disconnect(ui->actionProjectExport, SIGNAL(triggered()), this, SLOT(onProjectExport()));
710 disconnect(ui->actionProjectImport, SIGNAL(triggered()), this, SLOT(onProjectImport()));
711 disconnect(ui->actionEditOptimize, SIGNAL(triggered()), this, SLOT(onEditOptimize()));
712 disconnect(ui->actionSpeakerSearch, SIGNAL(triggered()), this, SLOT(onSpeakerSearch()));
713 disconnect(ui->actionProjectPrint, SIGNAL(triggered()), this, SLOT(onProjectPrint()));
714 disconnect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(onAboutAbout()));
715
716 /* ported alignments sub-menu */
717 disconnect(ui->actionModerate_Inf, SIGNAL(triggered()), this, SLOT(onAlignModerate_Inf()));
718 disconnect(ui->actionLegendre, SIGNAL(triggered()), this, SLOT(onAlignLegendre()));
719 disconnect(ui->actionBessel, SIGNAL(triggered()), this, SLOT(onAlignBessel()));
720 disconnect(ui->actionBullock, SIGNAL(triggered()), this, SLOT(onAlignBullock()));
721 disconnect(ui->actionKeele_Hoge, SIGNAL(triggered()), this, SLOT(onAlignKeele_Hoge()));
722
723 /* bandpass alignment */
724 disconnect(ui->actionBandpass_alignment, SIGNAL(triggered(bool)), this, SLOT(onBandpassAlignment()));
725 }
726
727 void MainWindow::unlinkTabs()
728 {
729 disconnect(ui->tabWidget, SIGNAL(currentChanged(int)), this, SLOT(onCurrentTabChanged(int)));
730
731 /* spk combos action */
732 disconnect(ui->vendorComboBox, SIGNAL(activated(QString)), this, SLOT(onVendorChanged(QString)));
733 disconnect(ui->modelComboBox, SIGNAL(activated(QString)), this, SLOT(onModelChanged(QString)));
734
735 disconnect(ui->numberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onNumberSpinChanged(int)));
736
737 disconnect(ui->sealedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onSealedVolumeDoubleSpinChanged(double)));
738
739 disconnect(ui->portedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedVolumeDoubleSpinChanged(double)));
740 disconnect(ui->portedResonancedoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedResonanceDoubleSpinChanged(double)));
741 disconnect(ui->portedPortsNumberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onPortedPortsNumberSpinChanged(int)));
742 disconnect(ui->portedPortDiameterDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedPortDiameterDoubleSpinChanged(double)));
743 disconnect(ui->portedPortSlotWidthButton, SIGNAL(clicked(bool)), this, SLOT(onPortedSlotPortActivated(bool)));
744 disconnect(ui->portedPortSlotWidthDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onPortedSlotWidthDoubleSpinChanged(double)));
745
746 disconnect(ui->bandPassSealedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassSealedVolumeDoubleSpinChanged(double)));
747 disconnect(ui->bandPassPortedVolumeDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassPortedVolumeDoubleSpinChanged(double)));
748 disconnect(ui->bandPassPortedResonanceDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassPortedResonanceDoubleSpinChanged(double)));
749 disconnect(ui->bandPassPortsNumberSpinBox, SIGNAL(valueChanged(int)), this, SLOT(onBandPassPortNumSpinChanged(int)));
750 disconnect(ui->bandPassPortDiameterDoubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBandPassPortDiameterDoubleSpinChanged(double)));
751 }
752
753 void MainWindow::unlinkInternals()
754 {
755 disconnect(this, SIGNAL(currentSpeakerChanged(Speaker)), this, SLOT(onCurrentSpeakerChanged(const Speaker&)));
756 disconnect(this, SIGNAL(currentSealedBoxChanged(SealedBox)), this, SLOT(onCurrentSealedBoxChanged(const SealedBox&)));
757 disconnect(this, SIGNAL(currentPortedBoxChanged(PortedBox)), this, SLOT(onCurrentPortedBoxChanged(const PortedBox&)));
758 disconnect(this, SIGNAL(currentBandPassBoxChanged(BandPassBox)), this, SLOT(onCurrentBandPassBoxChanged(const BandPassBox&)));
759 }
760
761 void MainWindow::onSpeakerNew()
762 {
763 isModifying = false;
764 spkDialog = new SpeakerDialog(this);
765 connect(spkDialog, SIGNAL(speakerInserted(Speaker)), this, SLOT(onSpeakerInserted(Speaker)));
766 connect(spkDialog, SIGNAL(speakerCancelled()), this, SLOT(onSpeakerCancelled()));
767 spkDialog->show();
768 }
769
770 void MainWindow::onSpeakerRemove()
771 {
772
773 QMessageBox::StandardButton pressed =
774 QMessageBox::question(this,
775 tr("Removal confirmation"),
776 tr("Do you really want to remove this loudspeaker from the database?"),
777 QMessageBox::Yes|QMessageBox::No);
778
779 if (pressed == QMessageBox::No)
780 return;
781
782 QString vendor = currentSpeaker.getVendor();
783 QString model = currentSpeaker.getModel();
784
785 int num = SpeakerDb::getModelsByVendor(currentSpeaker.getVendor()).size();
786 ui->modelComboBox->removeItem(ui->modelComboBox->currentIndex());
787 if (num == 1) {
788 ui->vendorComboBox->removeItem(ui->vendorComboBox->currentIndex());
789 }
790
791 SpeakerDb::removeByVendorAndModel(vendor, model);
792
793 /* fill speakers of automatically reselected vendor */
794 QString v = ui->vendorComboBox->currentText();
795 ui->modelComboBox->clear();
796 ui->modelComboBox->addItems(SpeakerDb::getModelsByVendor(v));
797 /* insert a fake speaker to let the choice to the user */
798 ui->modelComboBox->insertItem(0, "?");
799 ui->modelComboBox->setCurrentIndex(0);
800 setCurrentSpeaker(Speaker());
801 }
802
803 void MainWindow::onSpeakerModify()
804 {
805 isModifying = true;
806 spkDialog = new SpeakerDialog(currentSpeaker, this);
807 connect(spkDialog, SIGNAL(speakerInserted(Speaker)), this, SLOT(onSpeakerInserted(Speaker)));
808 connect(spkDialog, SIGNAL(speakerCancelled()), this, SLOT(onSpeakerCancelled()));
809 spkDialog->show();
810 }
811
812 void MainWindow::onSpeakerInserted(Speaker spk)
813 {
814 if (isModifying) {
815 int idx;
816 idx = ui->vendorComboBox->currentIndex();
817 ui->vendorComboBox->setItemText(idx, spk.getVendor());
818 idx = ui->modelComboBox->currentIndex();
819 ui->modelComboBox->setItemText(idx, spk.getModel());
820
821 currentSpeaker = spk;
822 emit currentSpeakerChanged(currentSpeaker);
823 } else {
824 int idx = ui->vendorComboBox->findText(spk.getVendor());
825 if (idx == -1)
826 ui->vendorComboBox->addItem(spk.getVendor());
827 else if (idx == ui->vendorComboBox->currentIndex())
828 ui->modelComboBox->addItem(spk.getModel());
829 }
830
831 /* spk has been inserted or modified: remove anyway notInDbSpeaker */
832 notInDbSpeaker = nullptr;
833
834 disconnect(spkDialog, SIGNAL(speakerInserted(Speaker)), this, SLOT(onSpeakerInserted(Speaker)));
835 disconnect(spkDialog, SIGNAL(speakerCancelled()), this, SLOT(onSpeakerCancelled()));
836 spkDialog->close();
837 spkDialog->deleteLater();
838 spkDialog = nullptr;
839 }
840
841 void MainWindow::onSpeakerCancelled()
842 {
843 disconnect(spkDialog, SIGNAL(speakerInserted(Speaker)), this, SLOT(onSpeakerInserted(Speaker)));
844 disconnect(spkDialog, SIGNAL(speakerCancelled()), this, SLOT(onSpeakerCancelled()));
845 spkDialog->close();
846 spkDialog->deleteLater();
847 spkDialog = nullptr;
848 }
849
850 void MainWindow::onEditOptimize()
851 {
852 if (ui->tabWidget->currentWidget() == ui->sealedTab) {
853 Optimizer optimizer(currentSpeaker, &currentSealedBox, currentSpeakerNumber, this);
854 optimizer.genericOptimizeBox();
855 emit currentSealedBoxChanged(currentSealedBox);
856 } else if (ui->tabWidget->currentWidget() == ui->portedTab) {
857 Optimizer optimizer(currentSpeaker, &currentPortedBox, currentSpeakerNumber, this);
858 optimizer.genericOptimizeBox();
859 emit currentPortedBoxChanged(currentPortedBox);
860 } else {
861 Optimizer optimizer(currentSpeaker, &currentBandPassBox, currentSpeakerNumber, this);
862 optimizer.genericOptimizeBox();
863 emit currentBandPassBoxChanged(currentBandPassBox);
864 }
865 }
866
867 void MainWindow::onAlignModerate_Inf()
868 {
869 if (ui->tabWidget->currentWidget() == ui->portedTab) {
870 Optimizer optimzer(currentSpeaker, &currentPortedBox, currentSpeakerNumber, this);
871 optimzer.portedAlignModerate_Inf();
872 emit currentPortedBoxChanged(currentPortedBox);
873 }
874 }
875
876 void MainWindow::onAlignLegendre()
877 {
878 if (ui->tabWidget->currentWidget() == ui->portedTab) {
879 Optimizer optimizer(currentSpeaker, &currentPortedBox, currentSpeakerNumber, this);
880 optimizer.portedAlignLegendre();
881 emit currentPortedBoxChanged(currentPortedBox);
882 }
883 }
884
885 void MainWindow::onAlignBessel()
886 {
887 if (ui->tabWidget->currentWidget() == ui->portedTab) {
888 Optimizer optimzer(currentSpeaker, &currentPortedBox, currentSpeakerNumber, this);
889 optimzer.portedAlignBessel();
890 emit currentPortedBoxChanged(currentPortedBox);
891 }
892 }
893
894 void MainWindow::onAlignKeele_Hoge()
895 {
896 /* BB4 */
897 if (ui->tabWidget->currentWidget() == ui->portedTab) {
898 Optimizer optimizer(currentSpeaker, &currentPortedBox, currentSpeakerNumber, this);
899 optimizer.portedAlignKeele_Hoge();
900 emit currentPortedBoxChanged(currentPortedBox);
901 }
902 }
903
904 void MainWindow::onAlignBullock()
905 {
906 /* SBB4 */
907 if (ui->tabWidget->currentWidget() == ui->portedTab) {
908 Optimizer optimizer(currentSpeaker, &currentPortedBox, currentSpeakerNumber, this);
909 optimizer.portedAlignBullock();
910 emit currentPortedBoxChanged(currentPortedBox);
911 }
912 }
913
914 void MainWindow::onSpeakerSearch()
915 {
916 searchDialog = new SearchDialog(this);
917 connect(searchDialog, SIGNAL(searchRequested(const QString&, double, double)), this, SLOT(onSearchRequested(const QString&, double, double)));
918 connect(searchDialog, SIGNAL(searchCancelled()), this, SLOT(onSearchCancelled()));
919 searchDialog->show();
920 }
921
922 void MainWindow::onProjectPrint()
923 {
924 QPrinter printer;
925 printer.setCreator("QSpeakers");
926 printer.setDocName("qspeakers_project");
927 printer.setOrientation(QPrinter::Landscape);
928 QPrintDialog dialog(&printer, this);
929 if (dialog.exec() == QDialog::Accepted) {
930 if (printer.isValid()) {
931 if (!print(&printer)) {
932 QPrinterInfo info(printer);
933 qWarning() << "printerinfo definition null:" << info.isNull();
934 qWarning() << "printerinfo state error:" << (info.state() == QPrinter::Error);
935 }
936 } else {
937 qWarning() << "invalid printer object";
938 }
939 }
940 }
941
942 void MainWindow::onUndo()
943 {
944 if (this->commandStack->canUndo())
945 this->commandStack->undo();
946 }
947
948 void MainWindow::onRedo()
949 {
950 if (this->commandStack->canRedo())
951 this->commandStack->redo();
952 }
953
954 void MainWindow::onAboutAbout()
955 {
956 QMessageBox::about(this, tr("About QSpeakers"),
957 tr("QSpeakers version " VERSION "\n\n"
958 "This program simulates common acoustical enclosures behaviour to help designing loudspeaker systems.\n\n"
959 "This program is free software, copyright (C) 2014 Benoit Rouits <brouits@free.fr> and released under the GNU General Public Lisence "
960 "version 3. It is delivered as is in the hope it can be useful, but with no warranty of correctness."));
961 }
962
963 void MainWindow::onSearchRequested(const QString& param, double min, double max)
964 {
965 disconnect(searchDialog, SIGNAL(searchRequested(const QString&, double, double)), this, SLOT(onSearchRequested(const QString&, double, double)));
966 disconnect(searchDialog, SIGNAL(searchCancelled()), this, SLOT(onSearchCancelled()));
967 searchDialog->close();
968 searchDialog->deleteLater();
969 searchDialog = nullptr;
970
971 QList<Speaker> speakers = SpeakerDb::getByValue(param, min, max);
972 listDialog = new ListDialog(speakers, this);
973 connect(listDialog, SIGNAL(speakerItemSelected(QString, const Speaker&)), this, SLOT(onSpeakerItemSelected(QString, const Speaker&)));
974 connect(listDialog, SIGNAL(speakerItemCancelled()), this, SLOT(onSpeakerItemCancelled()));
975 listDialog->show();
976 }
977
978 void MainWindow::onSearchCancelled()
979 {
980 disconnect(searchDialog, SIGNAL(searchRequested(const QString&, double, double)), this, SLOT(onSearchRequested(const QString&, double, double)));
981 disconnect(searchDialog, SIGNAL(searchCancelled()), this, SLOT(onSearchCancelled()));
982 searchDialog->close();
983 searchDialog->deleteLater();
984 searchDialog = nullptr;
985 }
986
987 void MainWindow::onSpeakerItemSelected(QString title, const Speaker& speaker)
988 {
989 qDebug() << title;
990 if (speaker.isValid()) {
991 setCurrentSpeaker(speaker);
992 syncUiFromCurrentSpeaker(speaker);
993 }
994
995 disconnect(listDialog, SIGNAL(speakerItemSelected(QString, const Speaker&)), this, SLOT(onSpeakerItemSelected(QString, const Speaker&)));
996 disconnect(listDialog, SIGNAL(speakerItemCancelled()), this, SLOT(onSpeakerItemCancelled()));
997 listDialog->close();
998 listDialog->deleteLater();
999 listDialog = nullptr;
1000 }
1001
1002 void MainWindow::onSpeakerItemCancelled()
1003 {
1004 disconnect(listDialog, SIGNAL(speakerItemSelected(QString,const Speaker&)), this, SLOT(onSpeakerItemSelected(QString,const Speaker&)));
1005 disconnect(listDialog, SIGNAL(speakerItemCancelled()), this, SLOT(onSpeakerItemCancelled()));
1006 listDialog->close();
1007 listDialog->deleteLater();
1008 listDialog = nullptr;
1009 }
1010
1011 void MainWindow::setActivateActions(QList<QAction*> actions, bool enable)
1012 {
1013 for (int i = 0; i < actions.count(); i++) {
1014 actions[i]->setEnabled(enable);
1015 }
1016 }
1017
1018 void MainWindow::onCurrentTabChanged(int tab)
1019 {
1020 if (tab == currentTabIndex)
1021 return;
1022
1023 this->commandStack->clear();
1024
1025 QList<QAction*> alignments = ui->menuPorted_alignments->actions();
1026 currentTabIndex = tab;
1027
1028 switch (tab) {
1029 case 0:
1030 {
1031 setActivateActions(alignments, false);
1032 ui->actionBandpass_alignment->setEnabled(false);
1033
1034 syncUiFromCurrentSealedBox(currentSealedBox);
1035 break;
1036 }
1037 case 1:
1038 {
1039 setActivateActions(alignments, true);
1040 ui->actionBandpass_alignment->setEnabled(false);
1041
1042 syncUiFromCurrentPortedBox(currentPortedBox);
1043 break;
1044 }
1045 case 2:
1046 {
1047 setActivateActions(alignments, false);
1048 ui->actionBandpass_alignment->setEnabled(true);
1049
1050 syncUiFromCurrentBandPassBox(currentBandPassBox);
1051 break;
1052 }
1053 default:
1054 break;
1055 }
1056 }
1057
1058 void MainWindow::onNumberSpinChanged(int number) {
1059 if (currentSpeakerNumber == number)
1060 return;
1061
1062 NumberCommand* com = new NumberCommand (currentSpeakerNumber, number, this);
1063 this->commandStack->push(com);
1064 }
1065
1066 void MainWindow::changeSpeakerNumber(int number) {
1067 /* order is very important to avoid crash loop */
1068 currentSpeakerNumber = number;
1069 ui->numberSpinBox->setValue(number);
1070
1071 /* the user may have used the "?" fake speaker */
1072 if (!currentSpeaker.isValid()) {
1073 qDebug() << "invalid speaker, not computing.";
1074 return;
1075 }
1076
1077 projectSaved = false;
1078 syncUiFromCurrentSealedBox(currentSealedBox);
1079 syncUiFromCurrentPortedBox(currentPortedBox);
1080 syncUiFromCurrentBandPassBox(currentBandPassBox);
1081 }
1082
1083 void MainWindow::onVendorChanged(QString vendor)
1084 {
1085 if (currentSpeaker.getVendor() == vendor)
1086 return;
1087
1088 if (notInDbSpeaker) {
1089 if (vendor == notInDbSpeaker->getVendor())
1090 return;
1091
1092 QMessageBox::StandardButton pressed =
1093 QMessageBox::question(this, tr("Unsaved speaker"),
1094 tr("This speaker is not present in the database. Would you like to insert it before to continue?"),
1095 QMessageBox::Yes|QMessageBox::No);
1096 if (pressed == QMessageBox::Yes) {
1097 SpeakerDb::insertOrReplace(notInDbSpeaker->getVendor(), notInDbSpeaker->getModel(), *notInDbSpeaker);
1098 notInDbSpeaker = nullptr;
1099 } else /* no insert wanted */ {
1100 if (0 == SpeakerDb::getByVendor(notInDbSpeaker->getVendor()).count()) {
1101 /* forget entries */
1102 ui->vendorComboBox->removeItem(ui->vendorComboBox->findText(notInDbSpeaker->getVendor()));
1103 ui->modelComboBox->removeItem(ui->modelComboBox->findText(notInDbSpeaker->getModel()));
1104 } else {
1105 /* forget model */
1106 ui->modelComboBox->removeItem(ui->modelComboBox->findText(notInDbSpeaker->getModel()));
1107 }
1108 notInDbSpeaker = nullptr;
1109 }
1110 }
1111
1112 this->commandStack->beginMacro("changing loudspeaker");
1113 VendorCommand* com = new VendorCommand (currentSpeaker.getVendor(), vendor, currentSpeaker, this);
1114 this->commandStack->push(com);
1115 }
1116
1117 void MainWindow::changeVendor(const QString& vendor, const Speaker& oldspeaker)
1118 {
1119 ui->vendorComboBox->setCurrentIndex(ui->vendorComboBox->findText(vendor));
1120
1121 /* fill speakers of this vendor */
1122 ui->modelComboBox->clear();
1123 ui->modelComboBox->addItems(SpeakerDb::getModelsByVendor(vendor));
1124
1125 /* if we come back to a previous vendor, set last vendor's selected speaker */
1126 if (oldspeaker.getVendor() == vendor) {
1127 ui->modelComboBox->setCurrentIndex(ui->modelComboBox->findText(oldspeaker.getModel()));
1128 setCurrentSpeaker(SpeakerDb::getByVendorAndModel(vendor, oldspeaker.getModel()));
1129 } else {
1130 /* insert a fake speaker to let the choice to the user */
1131 ui->modelComboBox->insertItem(0, "?");
1132 ui->modelComboBox->setCurrentIndex(0);
1133 setCurrentSpeaker(Speaker());
1134 }
1135 }
1136
1137 void MainWindow::onModelChanged(QString model)
1138 {
1139 if (model.isNull() || model.isEmpty())
1140 return;
1141
1142 if (currentSpeaker.getModel() == model)
1143 return;
1144
1145 if (notInDbSpeaker) {
1146 if (model == notInDbSpeaker->getModel())
1147 return;
1148
1149 QMessageBox::StandardButton pressed =
1150 QMessageBox::question(this, tr("Unsaved speaker"),
1151 tr("This speaker is not present in the database. Would you like to insert it before to continue?"),
1152 QMessageBox::Yes|QMessageBox::No);
1153 if (pressed == QMessageBox::Yes) {
1154 SpeakerDb::insertOrReplace(notInDbSpeaker->getVendor(), notInDbSpeaker->getModel(), *notInDbSpeaker);
1155 notInDbSpeaker = nullptr;
1156 } else /* no insert wanted */ {
1157 if (0 == SpeakerDb::getByVendor(notInDbSpeaker->getVendor()).count()) {
1158 /* forget entries */
1159 ui->vendorComboBox->removeItem(ui->vendorComboBox->findText(notInDbSpeaker->getVendor()));
1160 ui->modelComboBox->removeItem(ui->modelComboBox->findText(notInDbSpeaker->getModel()));
1161 } else {
1162 /* forget model */
1163 ui->modelComboBox->removeItem(ui->modelComboBox->findText(notInDbSpeaker->getModel()));
1164 }
1165 notInDbSpeaker = nullptr;
1166 }
1167 }
1168
1169 oldSpeaker = currentSpeaker;
1170
1171 ModelCommand* com = new ModelCommand (currentSpeaker.getModel(), model, this);
1172 this->commandStack->push(com);
1173
1174 if (oldSpeaker.getVendor() != currentSpeaker.getVendor()) {
1175 oldSpeaker = currentSpeaker;
1176 this->commandStack->endMacro();
1177 }
1178 }
1179
1180 void MainWindow::changeModel(const QString& model)
1181 {
1182 ui->modelComboBox->setCurrentIndex(ui->modelComboBox->findText(model));
1183
1184 /* change model */
1185 if ((ui->modelComboBox->findText("?") == 0) && (ui->modelComboBox->currentIndex() != 0)) {
1186 /* user choose a speaker; safely remove fake speaker */
1187 ui->modelComboBox->removeItem(0);
1188 }
1189
1190 QString vendor = ui->vendorComboBox->currentText();
1191 setCurrentSpeaker(SpeakerDb::getByVendorAndModel(vendor, model));
1192 }
1193
1194 void MainWindow::onCurrentSpeakerChanged(const Speaker &spk)
1195 {
1196 ui->splValueLabel->setNum(spk.getSpl());
1197 ui->fsValueLabel->setNum(spk.getFs());
1198 ui->qtsValueLabel->setNum(spk.getQts());
1199 ui->vasValueLabel->setNum(spk.getVas());
1200 ui->diaValueLabel->setNum(spk.getDia());
1201 ui->zValueLabel->setNum(spk.getZ());
1202 ui->vcValueLabel->setNum(spk.getVc());
1203
1204 /* the user may have activated the "?" fake speaker */
1205 if (!spk.isValid()) {
1206 qDebug() << "invalid speaker, not computing.";
1207 return;
1208 }
1209
1210 projectSaved = false;
1211 syncUiFromCurrentSealedBox(currentSealedBox);
1212 syncUiFromCurrentPortedBox(currentPortedBox);
1213 syncUiFromCurrentBandPassBox(currentBandPassBox);
1214 }
1215
1216 void MainWindow::onSealedVolumeDoubleSpinChanged(double val)
1217 {
1218 SealedVolumeCommand* com = new SealedVolumeCommand (currentSealedBox.getVolume(), val, this);
1219 this->commandStack->push(com);
1220 }
1221
1222 void MainWindow::changeSealedVolume(double val)
1223 {
1224 currentSealedBox.setVolume(val);
1225 syncUiFromCurrentSealedBox(currentSealedBox);
1226 }
1227
1228 void MainWindow::onCurrentSealedBoxChanged(const SealedBox &box)
1229 {
1230 if (!currentSpeaker.isValid())
1231 return;
1232
1233 projectSaved = false;
1234 syncUiFromCurrentSealedBox(box);
1235 }
1236
1237 void MainWindow::onPortedVolumeDoubleSpinChanged(double val)
1238 {
1239 PortedVolumeCommand* com = new PortedVolumeCommand (currentPortedBox.getBoxVolume(), val, this);
1240 this->commandStack->push(com);
1241 }
1242
1243 void MainWindow::changePortedVolume(double val)
1244 {
1245 currentPortedBox.setBoxVolume(val);
1246 currentPortedBox.updatePorts(currentSpeaker.getSd() * currentSpeakerNumber, currentSpeaker.getXmax());
1247 syncUiFromCurrentPortedBox(currentPortedBox);
1248 }
1249
1250 void MainWindow::onPortedResonanceDoubleSpinChanged(double val)
1251 {
1252 PortedResFreqCommand* com = new PortedResFreqCommand (currentPortedBox.getResFreq(), val, this);
1253 this->commandStack->push(com);
1254 }
1255
1256 void MainWindow::changePortedResFreq(double val)
1257 {
1258 currentPortedBox.setResFreq(val);
1259 currentPortedBox.updatePorts(currentSpeaker.getSd() * currentSpeakerNumber, currentSpeaker.getXmax());
1260 syncUiFromCurrentPortedBox(currentPortedBox);
1261 }
1262
1263 void MainWindow::onPortedPortsNumberSpinChanged(int val)
1264 {
1265 PortedPortNumberCommand* com = new PortedPortNumberCommand (currentPortedBox.getPortNum(), val, this);
1266 this->commandStack->push(com);
1267 }
1268
1269 void MainWindow::changePortedPortNumber(unsigned int val)
1270 {
1271 currentPortedBox.setPortNum(val);
1272 currentPortedBox.updatePorts(currentSpeaker.getSd()* currentSpeakerNumber, currentSpeaker.getXmax());
1273 syncUiFromCurrentPortedBox(currentPortedBox);
1274 }
1275
1276 void MainWindow::onPortedPortDiameterDoubleSpinChanged(double val)
1277 {
1278 /* allow manual decrease/increase of port diameter */
1279 PortedPortDiamCommand* com = new PortedPortDiamCommand (currentPortedBox.getPortDiam(), val, this);
1280 this->commandStack->push(com);
1281 }
1282
1283 void MainWindow::changePortedPortDiam(double val)
1284 {
1285 currentPortedBox.setPortDiam(val);
1286 currentPortedBox.updateSlots();
1287 currentPortedBox.updatePortsLength();
1288 syncUiFromCurrentPortedBox(currentPortedBox);
1289 }
1290
1291 void MainWindow::onPortedSlotPortActivated(bool checked)
1292 {
1293 PortedSlotPortCommand* com = new PortedSlotPortCommand (this->currentPortedBox.getSlotPortActivated(), checked, this);
1294 this->commandStack->push(com);
1295 }
1296
1297 void MainWindow::changePortedSlotPortActivation(bool checked)
1298 {
1299 currentPortedBox.setSlotPortActivated(checked);
1300
1301 ui->portedPortSlotWidthDoubleSpinBox->setEnabled(checked);
1302 ui->portedPortSlotHeightLineEdit->setEnabled(checked);
1303
1304 syncUiFromCurrentPortedBox(currentPortedBox);
1305 }
1306
1307 void MainWindow::onPortedSlotWidthDoubleSpinChanged(double val)
1308 {
1309 PortedSlotWidthCommand* com = new PortedSlotWidthCommand (currentPortedBox.getSlotWidth(), val, this);
1310 this->commandStack->push(com);
1311 }
1312
1313 void MainWindow::changePortedSlotWidth(double val)
1314 {
1315 currentPortedBox.setSlotWidth(val);
1316 syncUiFromCurrentPortedBox(currentPortedBox);
1317 }
1318
1319 void MainWindow::onCurrentPortedBoxChanged(const PortedBox &box)
1320 {
1321 if (!currentSpeaker.isValid())
1322 return;
1323
1324 projectSaved = false;
1325 syncUiFromCurrentPortedBox(box);
1326 }
1327
1328 void MainWindow::onBandPassSealedVolumeDoubleSpinChanged(double val)
1329 {
1330 BPSealedVolumeCommand* com = new BPSealedVolumeCommand (currentBandPassBox.getSealedBoxVolume(), val, this);
1331 this->commandStack->push(com);
1332 }
1333
1334 void MainWindow::changeBPSealedVolume(double val)
1335 {
1336 currentBandPassBox.setSealedBoxVolume(val);
1337 syncUiFromCurrentBandPassBox(currentBandPassBox);
1338 }
1339
1340 void MainWindow::onBandPassPortedVolumeDoubleSpinChanged(double val)
1341 {
1342 BPPortedVolumeCommand* com = new BPPortedVolumeCommand (currentBandPassBox.getPortedBoxVolume(), val, this);
1343 this->commandStack->push(com);
1344 }
1345
1346 void MainWindow::changeBPPortedVolume(double val)
1347 {
1348 currentBandPassBox.setPortedBoxVolume(val);
1349 currentBandPassBox.updatePortedBoxPorts(currentSpeaker.getSd() * currentSpeakerNumber, currentSpeaker.getXmax());
1350 syncUiFromCurrentBandPassBox(currentBandPassBox);
1351 }
1352
1353 void MainWindow::onBandPassPortedResonanceDoubleSpinChanged(double val)
1354 {
1355 BPPortedResFreqCommand* com = new BPPortedResFreqCommand (currentBandPassBox.getPortedBoxResFreq(), val, this);
1356 this->commandStack->push(com);
1357 }
1358
1359 void MainWindow::changeBPPortedResFreq(double val)
1360 {
1361 currentBandPassBox.setPortedBoxResFreq(val);
1362 currentBandPassBox.updatePortedBoxPorts(currentSpeaker.getSd() * currentSpeakerNumber, currentSpeaker.getXmax());
1363 syncUiFromCurrentBandPassBox(currentBandPassBox);
1364 }
1365
1366 void MainWindow::onBandPassPortNumSpinChanged(int val)
1367 {
1368 BPPortedPortNumberCommand* com = new BPPortedPortNumberCommand (currentBandPassBox.getPortedBoxPortNum(), val, this);
1369 this->commandStack->push(com);
1370 }
1371
1372 void MainWindow::changeBPPortedPortNumber(double val)
1373 {
1374 currentBandPassBox.setPortedBoxPortNum(val);
1375 currentBandPassBox.updatePortedBoxPorts(currentSpeaker.getSd() * currentSpeakerNumber, currentSpeaker.getXmax());
1376 syncUiFromCurrentBandPassBox(currentBandPassBox);
1377 }
1378
1379 void MainWindow::onBandPassPortDiameterDoubleSpinChanged(double val)
1380 {
1381 /* allow manual decrease/increase of port diameter */
1382 BPPortedPortDiamCommand* com = new BPPortedPortDiamCommand (currentBandPassBox.getPortedBoxPortDiam(), val, this);
1383 this->commandStack->push(com);
1384 }
1385
1386 void MainWindow::changeBPPortedPortDiam(double val)
1387 {
1388 currentBandPassBox.setPortedBoxPortDiam(val);
1389 currentBandPassBox.updatePortedBoxPortsLength();
1390 syncUiFromCurrentBandPassBox(currentBandPassBox);
1391 }
1392
1393 void MainWindow::onCurrentBandPassBoxChanged(const BandPassBox &box)
1394 {
1395 if (!currentSpeaker.isValid())
1396 return;
1397
1398 projectSaved = false;
1399 syncUiFromCurrentBandPassBox(box);
1400 }

  ViewVC Help
Powered by ViewVC 1.1.26