1 /*******************************************************************************************************
2 DkDialog.cpp
3 Created on: 21.04.2011
4
5 nomacs is a fast and small image viewer with the capability of synchronizing multiple instances
6
7 Copyright (C) 2011-2013 Markus Diem <markus@nomacs.org>
8 Copyright (C) 2011-2013 Stefan Fiel <stefan@nomacs.org>
9 Copyright (C) 2011-2013 Florian Kleber <florian@nomacs.org>
10
11 This file is part of nomacs.
12
13 nomacs is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 nomacs is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26 *******************************************************************************************************/
27
28 #include "DkDialog.h"
29
30 #include "DkImageStorage.h"
31 #include "DkSettings.h"
32 #include "DkBaseViewPort.h"
33 #include "DkTimer.h"
34 #include "DkWidgets.h"
35 #include "DkBasicWidgets.h"
36 #include "DkThumbs.h"
37 #include "DkUtils.h"
38 #include "DkActionManager.h"
39 #include "DkPluginManager.h"
40 #include "DkCentralWidget.h"
41 #include "DkViewPort.h"
42
43 #if defined(Q_OS_WIN) && !defined(SOCK_STREAM)
44 #include <winsock2.h> // needed since libraw 0.16
45 #endif
46
47 #pragma warning(push, 0) // no warnings from includes - begin
48 #include <QWidget>
49 #include <QLabel>
50 #include <QRadioButton>
51 #include <QSpinBox>
52 #include <QSlider>
53 #include <QColorDialog>
54 #include <QPushButton>
55 #include <QBoxLayout>
56 #include <QCheckBox>
57 #include <QFileInfo>
58 #include <QTableView>
59 #include <QCompleter>
60 #include <QMainWindow>
61 #include <QDialogButtonBox>
62 #include <QStandardItemModel>
63 #include <QItemEditorFactory>
64 #include <QHeaderView>
65 #include <QTreeView>
66 #include <QMimeData>
67 #include <QStringListModel>
68 #include <QListView>
69 #include <QDialogButtonBox>
70 #include <QListWidget>
71 #include <QProgressDialog>
72 #include <QTextEdit>
73 #include <QApplication>
74 #include <QInputDialog>
75 #include <QFileDialog>
76 #include <QPageSetupDialog>
77 #include <QPrintDialog>
78 #include <QToolBar>
79 #include <QFormLayout>
80 #include <QProgressBar>
81 #include <QFuture>
82 #include <QtConcurrentRun>
83 #include <QMouseEvent>
84 #include <QAction>
85 #include <QMessageBox>
86 #include <QToolButton>
87 #include <QComboBox>
88 #include <QTimer>
89 #include <qmath.h>
90 #include <QDesktopServices>
91 #include <QSplashScreen>
92 #include <QMenu>
93 #include <QKeySequenceEdit>
94 #include <QPrinterInfo>
95 #include <QDesktopWidget>
96 #include <QScreen>
97
98 // quazip
99 #ifdef WITH_QUAZIP
100 #ifdef WITH_QUAZIP1
101 #include <quazip/JlCompress.h>
102 #else
103 #include <quazip5/JlCompress.h>
104 #endif
105 #endif
106
107 #pragma warning(pop) // no warnings from includes - end
108
109 namespace nmc {
110
fileDialogOptions()111 QFileDialog::Options DkDialog::fileDialogOptions() {
112
113 QFileDialog::Options flags;
114
115 if (!DkSettingsManager::param().resources().nativeDialog)
116 flags = QFileDialog::DontUseNativeDialog;
117
118
119 return flags;
120 }
121
122 // DkSplashScreen --------------------------------------------------------------------
DkSplashScreen(QWidget *,Qt::WindowFlags flags)123 DkSplashScreen::DkSplashScreen(QWidget* /*parent*/, Qt::WindowFlags flags) : QDialog(0, flags) {
124
125 QPixmap img(":/nomacs/img/splash-screen.png");
126 setWindowFlags(Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
127 setMouseTracking(true);
128
129 #ifdef Q_OS_MAC
130 setObjectName("DkSplashScreenMac");
131 #else
132 setObjectName("DkSplashScreen");
133 setAttribute(Qt::WA_TranslucentBackground);
134 #endif
135
136 imgLabel = new QLabel(this, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
137 imgLabel->setObjectName("DkSplashInfoLabel");
138 imgLabel->setMouseTracking(true);
139 imgLabel->setScaledContents(true);
140 imgLabel->setPixmap(img);
141 imgLabel->setFixedSize(600, 474);
142 imgLabel->show();
143
144 setFixedSize(imgLabel->size());
145
146 exitButton = new QPushButton(DkImage::loadIcon(":/nomacs/img/close.svg"), "", this);
147 exitButton->setObjectName("cancelButtonSplash");
148 exitButton->setFlat(true);
149 exitButton->setToolTip(tr("Close (ESC)"));
150 exitButton->setShortcut(QKeySequence(Qt::Key_Escape));
151 exitButton->move(10, 435);
152 exitButton->hide();
153 connect(exitButton, SIGNAL(clicked()), this, SLOT(close())) ;
154
155 // set the text
156 text =
157 QString("Flo was here und wünscht<br>"
158 "Stefan fiel Spaß während<br>"
159 "Markus rockt... <br><br>"
160
161 "<a href=\"https://nomacs.org\">https://nomacs.org</a><br>"
162 "<a href=\"mailto:developers@nomacs.org\">developers@nomacs.org</a><br><br>"
163
164 "This program is licensed under GNU General Public License v3<br>"
165 "© Markus Diem, Stefan Fiel and Florian Kleber, 2011-2020<br><br>"
166
167 "Press [ESC] to exit");
168
169 textLabel = new QLabel(this, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
170 textLabel->setObjectName("DkSplashInfoLabel");
171 textLabel->setMouseTracking(true);
172 textLabel->setScaledContents(true);
173 textLabel->setTextFormat(Qt::RichText);
174 textLabel->setText(text);
175 textLabel->move(131, 280);
176 textLabel->setOpenExternalLinks(true);
177
178 //textLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
179
180 QLabel* versionLabel = new QLabel(this, Qt::FramelessWindowHint|Qt::WindowStaysOnTopHint);
181 versionLabel->setObjectName("DkSplashInfoLabel");
182 versionLabel->setTextFormat(Qt::RichText);
183
184 versionLabel->setText(versionText());
185 versionLabel->setAlignment(Qt::AlignRight);
186 versionLabel->move(360, 280);
187 versionLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
188
189 showTimer = new QTimer(this);
190 showTimer->setInterval(5000);
191 showTimer->setSingleShot(true);
192 connect(showTimer, SIGNAL(timeout()), exitButton, SLOT(hide()));
193
194 qDebug() << "splash screen created...";
195 }
196
mousePressEvent(QMouseEvent * event)197 void DkSplashScreen::mousePressEvent(QMouseEvent* event) {
198
199 setCursor(Qt::ClosedHandCursor);
200 mouseGrab = event->globalPos();
201 QDialog::mousePressEvent(event);
202 }
203
mouseMoveEvent(QMouseEvent * event)204 void DkSplashScreen::mouseMoveEvent(QMouseEvent *event) {
205
206 if (event->buttons() == Qt::LeftButton) {
207 move(pos()-(mouseGrab-event->globalPos()));
208 mouseGrab = event->globalPos();
209 qDebug() << "moving...";
210 }
211 else
212 setCursor(Qt::OpenHandCursor);
213 showClose();
214 QDialog::mouseMoveEvent(event);
215 }
216
mouseReleaseEvent(QMouseEvent * event)217 void DkSplashScreen::mouseReleaseEvent(QMouseEvent *event) {
218
219 setCursor(Qt::OpenHandCursor);
220 showClose();
221 QDialog::mouseReleaseEvent(event);
222 }
223
showClose()224 void DkSplashScreen::showClose() {
225
226 exitButton->show();
227 showTimer->start();
228 }
229
versionText() const230 QString DkSplashScreen::versionText() const {
231
232 QString vt;
233
234 // print out if the name is changed (e.g. READ build)
235 if (QApplication::applicationName() != "Image Lounge") {
236 vt += QApplication::applicationName() + "<br>";
237 }
238
239 // architecture
240 QString platform = "";
241 #ifdef _WIN64
242 platform = " [x64] ";
243 #elif defined _WIN32
244 platform = " [x86] ";
245 #endif
246
247 // version & build date
248 vt += QApplication::applicationVersion() + platform + "<br>";
249
250 // reproducable builds for linux (see #139)
251 #ifdef Q_OS_WIN
252 vt += QString(__DATE__) + "<br>";
253 #endif
254
255 // supplemental info
256 vt += "<p style=\"color: #666; font-size: 7pt; margin: 0; padding: 0;\">";
257
258 // OpenCV
259 #ifdef WITH_OPENCV
260 vt += "OpenCV " + QString(CV_VERSION) + "<br>";
261 #else
262 vt += "No CV support<br>";
263 #endif
264
265 // Qt
266 vt += "Qt " + QString(QT_VERSION_STR) + "<br>";
267
268 // LibRAW
269 #ifndef WITH_LIBRAW
270 vt += "No RAW support<br>";
271 #endif
272
273 // portable
274 vt += (DkSettingsManager::param().isPortable() ? tr("Portable") : "");
275 vt += "</p>";
276
277 return vt;
278 }
279
280 // file validator --------------------------------------------------------------------
DkFileValidator(const QString & lastFile,QObject * parent)281 DkFileValidator::DkFileValidator(const QString& lastFile, QObject * parent) : QValidator(parent) {
282
283 mLastFile = lastFile;
284 }
285
fixup(QString & input) const286 void DkFileValidator::fixup(QString& input) const {
287
288 if(!QFileInfo(input).exists())
289 input = mLastFile;
290 }
291
validate(QString & input,int &) const292 QValidator::State DkFileValidator::validate(QString& input, int&) const {
293
294 if (QFileInfo(input).exists())
295 return QValidator::Acceptable;
296 else
297 return QValidator::Intermediate;
298 }
299
300 // train dialog --------------------------------------------------------------------
DkTrainDialog(QWidget * parent,Qt::WindowFlags flags)301 DkTrainDialog::DkTrainDialog(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) {
302
303 setWindowTitle(tr("Add New Image Format"));
304 createLayout();
305 setFixedSize(340, 400); // due to the baseViewport we need fixed sized dialogs : (
306 setAcceptDrops(true);
307 }
308
createLayout()309 void DkTrainDialog::createLayout() {
310
311 // first row
312 QLabel* newImageLabel = new QLabel(tr("Load New Image Format"), this);
313 mPathEdit = new QLineEdit(this);
314 mPathEdit->setValidator(&mFileValidator);
315 mPathEdit->setObjectName("DkWarningEdit");
316 connect(mPathEdit, SIGNAL(textChanged(const QString&)), this, SLOT(textChanged(const QString&)));
317 connect(mPathEdit, SIGNAL(editingFinished()), this, SLOT(loadFile()));
318
319 QPushButton* openButton = new QPushButton(tr("&Browse"), this);
320 connect(openButton, SIGNAL(pressed()), this, SLOT(openFile()));
321
322 mFeedbackLabel = new QLabel("", this);
323 mFeedbackLabel->setObjectName("DkDecentInfo");
324
325 // shows the image if it could be loaded
326 mViewport = new DkBaseViewPort(this);
327 mViewport->setForceFastRendering(true);
328 mViewport->setPanControl(QPointF(0.0f, 0.0f));
329
330 // mButtons
331 mButtons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
332 mButtons->button(QDialogButtonBox::Ok)->setText(tr("&Add"));
333 mButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
334 mButtons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
335 connect(mButtons, SIGNAL(accepted()), this, SLOT(accept()));
336 connect(mButtons, SIGNAL(rejected()), this, SLOT(reject()));
337
338 QWidget* trainWidget = new QWidget(this);
339 QGridLayout* gdLayout = new QGridLayout(trainWidget);
340 gdLayout->addWidget(newImageLabel, 0, 0);
341 gdLayout->addWidget(mPathEdit, 1, 0);
342 gdLayout->addWidget(openButton, 1, 1);
343 gdLayout->addWidget(mFeedbackLabel, 2, 0, 1, 2);
344 gdLayout->addWidget(mViewport, 3, 0, 1, 2);
345
346 QVBoxLayout* layout = new QVBoxLayout(this);
347 layout->addWidget(trainWidget);
348 layout->addWidget(mButtons);
349 }
350
textChanged(const QString & text)351 void DkTrainDialog::textChanged(const QString& text) {
352
353 if (QFileInfo(text).exists())
354 mPathEdit->setProperty("warning", false);
355 else
356 mPathEdit->setProperty("warning", false);
357
358 mPathEdit->style()->unpolish(mPathEdit);
359 mPathEdit->style()->polish(mPathEdit);
360 mPathEdit->update();
361 }
362
openFile()363 void DkTrainDialog::openFile() {
364
365 // load system default open dialog
366 QString filePath = QFileDialog::getOpenFileName(
367 this,
368 tr("Open Image"),
369 mFile,
370 tr("All Files (*.*)"),
371 nullptr,
372 DkDialog::fileDialogOptions()
373 );
374
375 if (QFileInfo(filePath).exists()) {
376 mPathEdit->setText(filePath);
377 loadFile(filePath);
378 }
379 }
380
userFeedback(const QString & msg,bool error)381 void DkTrainDialog::userFeedback(const QString& msg, bool error) {
382
383 if (!error)
384 mFeedbackLabel->setProperty("warning", false);
385 else
386 mFeedbackLabel->setProperty("warning", true);
387
388 mFeedbackLabel->setText(msg);
389 mFeedbackLabel->style()->unpolish(mFeedbackLabel);
390 mFeedbackLabel->style()->polish(mFeedbackLabel);
391 mFeedbackLabel->update();
392 }
393
loadFile(const QString & filePath)394 void DkTrainDialog::loadFile(const QString& filePath) {
395
396 QString lFilePath = filePath;
397
398 if (filePath.isEmpty() && !mPathEdit->text().isEmpty())
399 lFilePath = mPathEdit->text();
400 else if (filePath.isEmpty())
401 return;
402
403 QFileInfo fileInfo(lFilePath);
404 if (!fileInfo.exists() || mAcceptedFile == lFilePath)
405 return; // error message?!
406
407 // update validator
408 mFileValidator.setLastFile(lFilePath);
409
410 DkBasicLoader basicLoader;
411 basicLoader.setTraining(true);
412
413 // TODO: archives cannot be trained currently
414 bool imgLoaded = basicLoader.loadGeneral(lFilePath, true);
415
416 if (!imgLoaded) {
417 mViewport->setImage(QImage()); // remove the image
418 mAcceptedFile = "";
419 userFeedback(tr("Sorry, currently we don't support: *.%1 files").arg(fileInfo.suffix()), true);
420 return;
421 }
422
423 if (DkSettingsManager::param().app().fileFilters.join(" ").contains(fileInfo.suffix(), Qt::CaseInsensitive)) {
424 userFeedback(tr("*.%1 is already supported.").arg(fileInfo.suffix()), false);
425 imgLoaded = false;
426 }
427 else
428 userFeedback(tr("*.%1 is supported.").arg(fileInfo.suffix()), false);
429
430 mViewport->setImage(basicLoader.image());
431 mAcceptedFile = lFilePath;
432
433 // try loading the file
434 // if loaded !
435 mButtons->button(QDialogButtonBox::Ok)->setEnabled(imgLoaded);
436 }
437
accept()438 void DkTrainDialog::accept() {
439
440 QFileInfo acceptedFileInfo(mAcceptedFile);
441
442 // add the extension to user filters
443 if (!DkSettingsManager::param().app().fileFilters.join(" ").contains(acceptedFileInfo.suffix(), Qt::CaseInsensitive)) {
444
445 QString name = QInputDialog::getText(this, "Format Name", tr("Please name the new format:"), QLineEdit::Normal, "Your File Format");
446 QString tag = name + " (*." + acceptedFileInfo.suffix() + ")";
447
448 // load user filters
449 DefaultSettings settings;
450 QStringList userFilters = settings.value("ResourceSettings/userFilters", QStringList()).toStringList();
451 userFilters.append(tag);
452 settings.setValue("ResourceSettings/userFilters", userFilters);
453 DkSettingsManager::param().app().openFilters.append(tag);
454 DkSettingsManager::param().app().fileFilters.append("*." + acceptedFileInfo.suffix());
455 DkSettingsManager::param().app().browseFilters += acceptedFileInfo.suffix();
456 }
457
458 QDialog::accept();
459 }
460
dropEvent(QDropEvent * event)461 void DkTrainDialog::dropEvent(QDropEvent *event) {
462
463 if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
464 QUrl url = event->mimeData()->urls().at(0);
465 qDebug() << "dropping: " << url;
466 url = url.toLocalFile();
467
468 mPathEdit->setText(url.toString());
469 loadFile();
470 }
471 }
472
dragEnterEvent(QDragEnterEvent * event)473 void DkTrainDialog::dragEnterEvent(QDragEnterEvent *event) {
474
475 if (event->mimeData()->hasUrls()) {
476 QUrl url = event->mimeData()->urls().at(0);
477 url = url.toLocalFile();
478 QFileInfo file = QFileInfo(url.toString());
479
480 if (file.exists())
481 event->acceptProposedAction();
482 }
483 }
484
485 // DkAppManagerDialog --------------------------------------------------------------------
DkAppManagerDialog(DkAppManager * manager,QWidget * parent,Qt::WindowFlags flags)486 DkAppManagerDialog::DkAppManagerDialog(DkAppManager* manager /* = 0 */, QWidget* parent /* = 0 */, Qt::WindowFlags flags /* = 0 */) : QDialog(parent, flags) {
487
488 this->manager = manager;
489 setWindowTitle(tr("Manage Applications"));
490 createLayout();
491 }
492
createLayout()493 void DkAppManagerDialog::createLayout() {
494
495 QVector<QAction* > appActions = manager->getActions();
496
497 model = new QStandardItemModel(this);
498 for (int rIdx = 0; rIdx < appActions.size(); rIdx++)
499 model->appendRow(getItems(appActions.at(rIdx)));
500
501 appTableView = new QTableView(this);
502 appTableView->setModel(model);
503 appTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
504 appTableView->verticalHeader()->hide();
505 appTableView->horizontalHeader()->hide();
506 //appTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
507 appTableView->setShowGrid(false);
508 appTableView->resizeColumnsToContents();
509 appTableView->resizeRowsToContents();
510 appTableView->setWordWrap(false);
511
512 QPushButton* runButton = new QPushButton(tr("&Run"), this);
513 runButton->setObjectName("runButton");
514
515 QPushButton* addButton = new QPushButton(tr("&Add"), this);
516 addButton->setObjectName("addButton");
517
518 QPushButton* deleteButton = new QPushButton(tr("&Delete"), this);
519 deleteButton->setObjectName("deleteButton");
520 deleteButton->setShortcut(QKeySequence::Delete);
521
522 // mButtons
523 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
524 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
525 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
526 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
527 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
528 buttons->addButton(runButton, QDialogButtonBox::ActionRole);
529 buttons->addButton(addButton, QDialogButtonBox::ActionRole);
530 buttons->addButton(deleteButton, QDialogButtonBox::ActionRole);
531
532 QVBoxLayout* layout = new QVBoxLayout(this);
533 layout->addWidget(appTableView);
534 layout->addWidget(buttons);
535 QMetaObject::connectSlotsByName(this);
536 }
537
getItems(QAction * action)538 QList<QStandardItem* > DkAppManagerDialog::getItems(QAction* action) {
539
540 QList<QStandardItem* > items;
541 QStandardItem* item = new QStandardItem(action->icon(), action->text().remove("&"));
542 //item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
543 items.append(item);
544 item = new QStandardItem(action->toolTip());
545 item->setFlags(Qt::ItemIsSelectable);
546 items.append(item);
547
548 return items;
549 }
550
on_addButton_clicked()551 void DkAppManagerDialog::on_addButton_clicked() {
552
553 // load system default open dialog
554 QString appFilter;
555 QString defaultPath;
556 #ifdef Q_OS_WIN
557 appFilter += tr("Executable Files (*.exe);;");
558 defaultPath = getenv("PROGRAMFILES");
559 #elif QT_VERSION < 0x050000
560 defaultPath = QDesktopServices::storageLocation(QDesktopServices::ApplicationsLocation); // retrieves startmenu on windows?!
561 #else
562 defaultPath = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation); // still retrieves startmenu on windows
563 #endif
564
565 QString filePath = QFileDialog::getOpenFileName(
566 this,
567 tr("Open Application"),
568 defaultPath,
569 appFilter,
570 nullptr,
571 DkDialog::fileDialogOptions()
572 );
573
574 if (filePath.isEmpty())
575 return;
576
577 QAction* newApp = manager->createAction(filePath);
578
579 if (newApp)
580 model->appendRow(getItems(newApp));
581
582 }
583
on_deleteButton_clicked()584 void DkAppManagerDialog::on_deleteButton_clicked() {
585
586 QModelIndexList selRows = appTableView->selectionModel()->selectedRows();
587
588 while (!selRows.isEmpty()) {
589 model->removeRows(selRows.last().row(), 1);
590 selRows.removeLast();
591 }
592 }
593
on_runButton_clicked()594 void DkAppManagerDialog::on_runButton_clicked() {
595
596 accept();
597
598 QItemSelectionModel* sel = appTableView->selectionModel();
599
600 if (!sel->hasSelection() && !manager->getActions().isEmpty())
601 emit openWithSignal(manager->getActions().first());
602
603 else if (!manager->getActions().isEmpty()) {
604
605 QModelIndexList rows = sel->selectedRows();
606
607 for (int idx = 0; idx < rows.size(); idx++) {
608 emit openWithSignal(manager->getActions().at(rows.at(idx).row()));
609 }
610 }
611
612 }
613
accept()614 void DkAppManagerDialog::accept() {
615
616 QVector<QAction* > apps;
617
618 for (int idx = 0; idx < model->rowCount(); idx++) {
619
620 QString filePath = model->item(idx, 1)->text();
621 QString name = model->item(idx, 0)->text();
622 QAction* action = manager->findAction(filePath);
623
624 if (!action)
625 action = manager->createAction(filePath);
626 // obviously I cannot create this action - should we tell user?
627 if (!action)
628 continue;
629
630 if (name != action->text().remove("&"))
631 action->setText(name);
632
633 qDebug() << "pushing back: " << action->text();
634
635 apps.append(action);
636 }
637
638 manager->setActions(apps);
639
640 QDialog::accept();
641 }
642
643 // DkSearchDialaog --------------------------------------------------------------------
DkSearchDialog(QWidget * parent,Qt::WindowFlags flags)644 DkSearchDialog::DkSearchDialog(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) {
645
646 init();
647 }
648
init()649 void DkSearchDialog::init() {
650
651 setObjectName("DkSearchDialog");
652 setWindowTitle(tr("Find & Filter"));
653
654 mEndMessage = tr("Load All");
655
656 QVBoxLayout* layout = new QVBoxLayout(this);
657
658 QCompleter* history = new QCompleter(DkSettingsManager::param().global().searchHistory, this);
659 history->setCompletionMode(QCompleter::InlineCompletion);
660 mSearchBar = new QLineEdit();
661 mSearchBar->setObjectName("searchBar");
662 mSearchBar->setToolTip(tr("Type search words or a regular expression"));
663 mSearchBar->setCompleter(history);
664
665 mStringModel = new QStringListModel(this);
666
667 mResultListView = new QListView(this);
668 mResultListView->setObjectName("resultListView");
669 mResultListView->setModel(mStringModel);
670 mResultListView->setEditTriggers(QAbstractItemView::NoEditTriggers);
671 mResultListView->setSelectionMode(QAbstractItemView::SingleSelection);
672
673 mFilterButton = new QPushButton(tr("&Filter"), this);
674 mFilterButton->setObjectName("filterButton");
675
676 mButtons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
677 mButtons->button(QDialogButtonBox::Ok)->setDefault(true);
678 mButtons->button(QDialogButtonBox::Ok)->setText(tr("F&ind"));
679 mButtons->addButton(mFilterButton, QDialogButtonBox::ActionRole);
680
681 connect(mButtons, SIGNAL(accepted()), this, SLOT(accept()));
682 connect(mButtons, SIGNAL(rejected()), this, SLOT(reject()));
683
684 layout->addWidget(mSearchBar);
685 layout->addWidget(mResultListView);
686 layout->addWidget(mButtons);
687
688 mSearchBar->setFocus(Qt::MouseFocusReason);
689
690 QMetaObject::connectSlotsByName(this);
691 }
692
setFiles(const QStringList & fileList)693 void DkSearchDialog::setFiles(const QStringList& fileList) {
694 mFileList = fileList;
695 mResultList = fileList;
696 mStringModel->setStringList(makeViewable(fileList));
697 }
698
setPath(const QString & dirPath)699 void DkSearchDialog::setPath(const QString& dirPath) {
700 mPath = dirPath;
701 }
702
filterPressed() const703 bool DkSearchDialog::filterPressed() const {
704 return mIsFilterPressed;
705 }
706
on_searchBar_textChanged(const QString & text)707 void DkSearchDialog::on_searchBar_textChanged(const QString& text) {
708
709 DkTimer dt;
710
711 if (text == mCurrentSearch)
712 return;
713
714 mResultList = DkUtils::filterStringList(text, mFileList);
715 qDebug() << "searching [" << text << "] - converted to individual keywords [" << text.split(" ") << "] takes: " << dt;
716 mCurrentSearch = text;
717
718 if (mResultList.empty()) {
719 QStringList answerList;
720 answerList.append(tr("No Matching Items"));
721 mStringModel->setStringList(answerList);
722
723 mResultListView->setProperty("empty", true);
724
725 mFilterButton->setEnabled(false);
726 mButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
727 }
728 else {
729 mFilterButton->setEnabled(true);
730 mButtons->button(QDialogButtonBox::Ok)->setEnabled(true);
731 mStringModel->setStringList(makeViewable(mResultList));
732 mResultListView->selectionModel()->setCurrentIndex(mStringModel->index(0, 0), QItemSelectionModel::SelectCurrent);
733 mResultListView->setProperty("empty", false);
734 }
735
736 mResultListView->style()->unpolish(mResultListView);
737 mResultListView->style()->polish(mResultListView);
738 mResultListView->update();
739
740 qDebug() << "searching takes (total): " << dt;
741 }
742
on_resultListView_doubleClicked(const QModelIndex & modelIndex)743 void DkSearchDialog::on_resultListView_doubleClicked(const QModelIndex& modelIndex) {
744
745 if (modelIndex.data().toString() == mEndMessage) {
746 mStringModel->setStringList(makeViewable(mResultList, true));
747 return;
748 }
749
750 emit loadFileSignal(QFileInfo(mPath, modelIndex.data().toString()).absoluteFilePath());
751 close();
752 }
753
on_resultListView_clicked(const QModelIndex & modelIndex)754 void DkSearchDialog::on_resultListView_clicked(const QModelIndex& modelIndex) {
755
756 if (modelIndex.data().toString() == mEndMessage)
757 mStringModel->setStringList(makeViewable(mResultList, true));
758 }
759
accept()760 void DkSearchDialog::accept() {
761
762 if (mResultListView->selectionModel()->currentIndex().data().toString() == mEndMessage) {
763 mStringModel->setStringList(makeViewable(mResultList, true));
764 return;
765 }
766
767 updateHistory();
768
769 // ok load the selected file
770 QString fileName = mResultListView->selectionModel()->currentIndex().data().toString();
771 qDebug() << "opening filename: " << fileName;
772
773 if (!fileName.isEmpty())
774 emit loadFileSignal(QFileInfo(mPath, fileName).absoluteFilePath());
775
776 QDialog::accept();
777 }
778
on_filterButton_pressed()779 void DkSearchDialog::on_filterButton_pressed() {
780
781 filterSignal(mCurrentSearch);
782 mIsFilterPressed = true;
783 DkSearchDialog::accept();
784 done(filter_button);
785 }
786
setDefaultButton(int defaultButton)787 void DkSearchDialog::setDefaultButton(int defaultButton /* = find_button */) {
788
789 if (defaultButton == find_button) {
790 mButtons->button(QDialogButtonBox::Ok)->setAutoDefault(true);
791 mButtons->button(QDialogButtonBox::Cancel)->setAutoDefault(false);
792 mFilterButton->setAutoDefault(false);
793 }
794 else if (defaultButton == filter_button) {
795 mButtons->button(QDialogButtonBox::Ok)->setAutoDefault(false);
796 mButtons->button(QDialogButtonBox::Cancel)->setAutoDefault(false);
797 mFilterButton->setAutoDefault(true);
798 }
799 }
800
updateHistory()801 void DkSearchDialog::updateHistory() {
802
803 DkSettingsManager::param().global().searchHistory.append(mCurrentSearch);
804
805 // keep the history small
806 if (DkSettingsManager::param().global().searchHistory.size() > 50)
807 DkSettingsManager::param().global().searchHistory.pop_front();
808
809 //QCompleter* history = new QCompleter(DkSettingsManager::param().global().searchHistory);
810 //searchBar->setCompleter(history);
811 }
812
makeViewable(const QStringList & resultList,bool forceAll)813 QStringList DkSearchDialog::makeViewable(const QStringList& resultList, bool forceAll) {
814
815 QStringList answerList;
816
817 // if size > 1000 it gets slow -> cut at 1000 and make an entry for 'expand'
818 if (!forceAll && resultList.size() > 1000) {
819
820 for (int idx = 0; idx < 1000; idx++)
821 answerList.append(resultList[idx]);
822 answerList.append(mEndMessage);
823
824 mAllDisplayed = false;
825 }
826 else {
827 mAllDisplayed = true;
828 answerList = resultList;
829 }
830
831 return answerList;
832 }
833
834 // DkResizeDialog --------------------------------------------------------------------
DkResizeDialog(QWidget * parent,Qt::WindowFlags flags)835 DkResizeDialog::DkResizeDialog(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) {
836
837 init();
838 resize(DkUtils::getInitialDialogSize());
839 }
840
accept()841 void DkResizeDialog::accept() {
842 saveSettings();
843
844 QDialog::accept();
845 }
846
saveSettings()847 void DkResizeDialog::saveSettings() {
848
849 DefaultSettings settings;
850 settings.beginGroup(objectName());
851
852 settings.setValue("ResampleMethod", mResampleBox->currentIndex());
853 settings.setValue("Resample", mResampleCheck->isChecked());
854 settings.setValue("CorrectGamma", mGammaCorrection->isChecked());
855
856 if (mSizeBox->currentIndex() == size_percent) {
857 settings.setValue("Width", mWPixelSpin->value());
858 settings.setValue("Height", mHPixelSpin->value());
859 }
860 else {
861 settings.setValue("Width", 0);
862 settings.setValue("Height", 0);
863 }
864 settings.endGroup();
865 }
866
867
loadSettings()868 void DkResizeDialog::loadSettings() {
869
870 qDebug() << "loading new settings...";
871
872 DefaultSettings settings;
873 settings.beginGroup(objectName());
874
875 mResampleBox->setCurrentIndex(settings.value("ResampleMethod", ipl_cubic).toInt());
876 mResampleCheck->setChecked(settings.value("Resample", true).toBool());
877 mGammaCorrection->setChecked(settings.value("CorrectGamma", true).toBool());
878
879 if (settings.value("Width", 0).toDouble() != 0) {
880
881 double w = settings.value("Width", 0).toDouble();
882 double h = settings.value("Height", 0).toDouble();
883
884 qDebug() << "width loaded from settings: " << w;
885
886 if (w != h) {
887 mLockButton->setChecked(false);
888 mLockButtonDim->setChecked(false);
889 }
890 mSizeBox->setCurrentIndex(size_percent);
891
892 mWPixelSpin->setValue(w);
893 mHPixelSpin->setValue(h);
894
895 updateWidth();
896 updateHeight();
897 }
898 settings.endGroup();
899
900 }
901
init()902 void DkResizeDialog::init() {
903
904 setObjectName("DkResizeDialog");
905
906 mUnitFactor.resize(unit_end);
907 mUnitFactor.insert(unit_cm, 1.0f);
908 mUnitFactor.insert(unit_mm, 10.0f);
909 mUnitFactor.insert(unit_inch, 1.0f/2.54f);
910
911 mResFactor.resize(res_end);
912 mResFactor.insert(res_ppi, 2.54f);
913 mResFactor.insert(res_ppc, 1.0f);
914
915 setWindowTitle(tr("Resize Image"));
916 //setFixedSize(600, 512);
917 createLayout();
918 initBoxes();
919
920 mWPixelSpin->setFocus(Qt::ActiveWindowFocusReason);
921
922 QMetaObject::connectSlotsByName(this);
923 }
924
createLayout()925 void DkResizeDialog::createLayout() {
926
927 // preview
928 int minPx = 1;
929 int maxPx = 100000;
930 double minWidth = 1;
931 double maxWidth = 500000;
932 int decimals = 2;
933
934 QLabel* origLabelText = new QLabel(tr("Original"));
935 origLabelText->setAlignment(Qt::AlignHCenter);
936 QLabel* newLabel = new QLabel(tr("New"));
937 newLabel->setAlignment(Qt::AlignHCenter);
938
939 // shows the original image
940 mOrigView = new DkBaseViewPort(this);
941 mOrigView->setForceFastRendering(true);
942 mOrigView->setPanControl(QPointF(0.0f, 0.0f));
943 connect(mOrigView, SIGNAL(imageUpdated()), this, SLOT(drawPreview()));
944
945 //// maybe we should report this:
946 //// if a stylesheet (with border) is set, the var
947 //// cornerPaintingRect in QAbstractScrollArea (which we don't even need : )
948 //// is invalid which blocks re-paints unless the widget gets a focus...
949 //origView->setStyleSheet("QViewPort{border: 1px solid #888;}");
950
951 // shows the preview
952 mPreviewLabel = new QLabel(this);
953 mPreviewLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
954 mPreviewLabel->setMinimumHeight(100);
955
956 // all text dialogs...
957 QDoubleValidator* doubleValidator = new QDoubleValidator(1, 1000000, 2, 0);
958 doubleValidator->setRange(0, 100, 2);
959
960 QWidget* resizeBoxes = new QWidget(this);
961 resizeBoxes->setGeometry(QRect(QPoint(40, 300), QSize(400, 170)));
962
963 QGridLayout* gridLayout = new QGridLayout(resizeBoxes);
964
965 QLabel* wPixelLabel = new QLabel(tr("Width: "), this);
966 wPixelLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
967 mWPixelSpin = new DkSelectAllDoubleSpinBox(this);
968 mWPixelSpin->setObjectName("wPixelSpin");
969 mWPixelSpin->setRange(minPx, maxPx);
970 mWPixelSpin->setDecimals(0);
971
972 mLockButton = new DkButton(DkImage::loadIcon(":/nomacs/img/lock.svg"), DkImage::loadIcon(":/nomacs/img/lock-unlocked.svg"), "lock", this);
973 mLockButton->setObjectName("lockButton");
974 mLockButton->setCheckable(true);
975 mLockButton->setChecked(true);
976
977 QLabel* hPixelLabel = new QLabel(tr("Height: "), this);
978 hPixelLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
979 mHPixelSpin = new DkSelectAllDoubleSpinBox(this);
980 mHPixelSpin->setObjectName("hPixelSpin");
981 mHPixelSpin->setRange(minPx, maxPx);
982 mHPixelSpin->setDecimals(0);
983
984 mSizeBox = new QComboBox(this);
985 QStringList sizeList;
986 sizeList.insert(size_pixel, "pixel");
987 sizeList.insert(size_percent, "%");
988 mSizeBox->addItems(sizeList);
989 mSizeBox->setObjectName("sizeBox");
990
991 // first row
992 int rIdx = 0;
993 gridLayout->addWidget(wPixelLabel, 0, rIdx);
994 gridLayout->addWidget(mWPixelSpin, 0, ++rIdx);
995 gridLayout->addWidget(mLockButton, 0, ++rIdx);
996 gridLayout->addWidget(hPixelLabel, 0, ++rIdx);
997 gridLayout->addWidget(mHPixelSpin, 0, ++rIdx);
998 gridLayout->addWidget(mSizeBox, 0, ++rIdx);
999
1000 // Document dimensions
1001 QLabel* widthLabel = new QLabel(tr("Width: "));
1002 widthLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1003 mWidthSpin = new DkSelectAllDoubleSpinBox();
1004 mWidthSpin->setObjectName("widthSpin");
1005 mWidthSpin->setRange(minWidth, maxWidth);
1006 mWidthSpin->setDecimals(decimals);
1007
1008
1009 mLockButtonDim = new DkButton(DkImage::loadIcon(":/nomacs/img/lock.svg"), DkImage::loadIcon(":/nomacs/img/lock-unlocked.svg"), "lock");
1010 //mLockButtonDim->setFixedSize(QSize(16,16));
1011 mLockButtonDim->setObjectName("lockButtonDim");
1012 mLockButtonDim->setCheckable(true);
1013 mLockButtonDim->setChecked(true);
1014
1015 QLabel* heightLabel = new QLabel(tr("Height: "));
1016 heightLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1017 mHeightSpin = new DkSelectAllDoubleSpinBox();
1018 mHeightSpin->setObjectName("heightSpin");
1019 mHeightSpin->setRange(minWidth, maxWidth);
1020 mHeightSpin->setDecimals(decimals);
1021
1022 mUnitBox = new QComboBox();
1023 QStringList unitList;
1024 unitList.insert(unit_cm, "cm");
1025 unitList.insert(unit_mm, "mm");
1026 unitList.insert(unit_inch, "inch");
1027 //unitList.insert(unit_percent, "%");
1028 mUnitBox->addItems(unitList);
1029 mUnitBox->setObjectName("unitBox");
1030
1031 // second row
1032 rIdx = 0;
1033 gridLayout->addWidget(widthLabel, 1, rIdx);
1034 gridLayout->addWidget(mWidthSpin, 1, ++rIdx);
1035 gridLayout->addWidget(mLockButtonDim, 1, ++rIdx);
1036 gridLayout->addWidget(heightLabel, 1, ++rIdx);
1037 gridLayout->addWidget(mHeightSpin, 1, ++rIdx);
1038 gridLayout->addWidget(mUnitBox, 1, ++rIdx);
1039
1040 // resolution
1041 QLabel* resolutionLabel = new QLabel(tr("Resolution: "));
1042 resolutionLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1043 mResolutionSpin = new DkSelectAllDoubleSpinBox();
1044 mResolutionSpin->setObjectName("resolutionSpin");
1045 mResolutionSpin->setRange(minWidth, maxWidth);
1046 mResolutionSpin->setDecimals(decimals);
1047
1048 mResUnitBox = new QComboBox();
1049 QStringList resUnitList;
1050 resUnitList.insert(res_ppi, tr("pixel/inch"));
1051 resUnitList.insert(res_ppc, tr("pixel/cm"));
1052 mResUnitBox->addItems(resUnitList);
1053 mResUnitBox->setObjectName("resUnitBox");
1054
1055 // third row
1056 rIdx = 0;
1057 gridLayout->addWidget(resolutionLabel, 2, rIdx);
1058 gridLayout->addWidget(mResolutionSpin, 2, ++rIdx);
1059 gridLayout->addWidget(mResUnitBox, 2, ++rIdx, 1, 2);
1060
1061 // resample
1062 mResampleCheck = new QCheckBox(tr("Resample Image:"));
1063 mResampleCheck->setChecked(true);
1064 mResampleCheck->setObjectName("resampleCheck");
1065
1066 // TODO: disable items if no opencv is available
1067 mResampleBox = new QComboBox();
1068 QStringList resampleList;
1069 resampleList.insert(ipl_nearest, tr("Nearest Neighbor"));
1070 resampleList.insert(ipl_area, tr("Area (best for downscaling)"));
1071 resampleList.insert(ipl_linear, tr("Linear"));
1072 resampleList.insert(ipl_cubic, tr("Bicubic (4x4 pixel interpolation)"));
1073 resampleList.insert(ipl_lanczos, tr("Lanczos (8x8 pixel interpolation)"));
1074 mResampleBox->addItems(resampleList);
1075 mResampleBox->setObjectName("resampleBox");
1076 mResampleBox->setCurrentIndex(ipl_cubic);
1077
1078 // last two rows
1079 gridLayout->addWidget(mResampleCheck, 3, 1, 1, 3);
1080 gridLayout->addWidget(mResampleBox, 4, 1, 1, 3);
1081
1082 mGammaCorrection = new QCheckBox(tr("Gamma Correction"));
1083 mGammaCorrection->setObjectName("gammaCorrection");
1084 mGammaCorrection->setChecked(false); // default: false since gamma might destroy soft gradients
1085
1086 gridLayout->addWidget(mGammaCorrection, 5, 1, 1, 3);
1087
1088 // add stretch
1089 gridLayout->setColumnStretch(6, 1);
1090
1091 // mButtons
1092 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
1093 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
1094 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
1095 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
1096 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
1097
1098 QGridLayout* layout = new QGridLayout(this);
1099 layout->setColumnStretch(0,1);
1100 layout->setColumnStretch(1,1);
1101
1102 layout->addWidget(origLabelText, 0, 0);
1103 layout->addWidget(newLabel, 0, 1);
1104 layout->addWidget(mOrigView, 1, 0);
1105 layout->addWidget(mPreviewLabel, 1, 1);
1106 layout->addWidget(resizeBoxes, 2, 0, 1, 2, Qt::AlignLeft);
1107 //layout->addStretch();
1108 layout->addWidget(buttons, 3, 0, 1, 2, Qt::AlignRight);
1109
1110 adjustSize();
1111 resize(700, 500);
1112
1113 //show();
1114 }
1115
initBoxes(bool updateSettings)1116 void DkResizeDialog::initBoxes(bool updateSettings) {
1117
1118 if (mImg.isNull())
1119 return;
1120
1121 if (mSizeBox->currentIndex() == size_pixel) {
1122 mWPixelSpin->setValue(mImg.width());
1123 mHPixelSpin->setValue(mImg.height());
1124 }
1125 else {
1126 mWPixelSpin->setValue(100);
1127 mHPixelSpin->setValue(100);
1128 }
1129
1130 float units = mResFactor.at(mResUnitBox->currentIndex()) * mUnitFactor.at(mUnitBox->currentIndex());
1131 float width = (float)mImg.width()/mExifDpi * units;
1132 mWidthSpin->setValue(width);
1133
1134 float height = (float)mImg.height()/mExifDpi * units;
1135 mHeightSpin->setValue(height);
1136
1137 if (updateSettings)
1138 loadSettings();
1139 }
1140
setImage(const QImage & img)1141 void DkResizeDialog::setImage(const QImage& img) {
1142
1143 mImg = img;
1144 initBoxes(true);
1145 updateSnippets();
1146 drawPreview();
1147 mWPixelSpin->selectAll();
1148 }
1149
getResizedImage()1150 QImage DkResizeDialog::getResizedImage() {
1151
1152 return resizeImg(mImg, false);
1153 }
1154
setExifDpi(float exifDpi)1155 void DkResizeDialog::setExifDpi(float exifDpi) {
1156
1157 mExifDpi = exifDpi;
1158 mResolutionSpin->blockSignals(true);
1159 mResolutionSpin->setValue(exifDpi);
1160 mResolutionSpin->blockSignals(false);
1161 }
1162
getExifDpi()1163 float DkResizeDialog::getExifDpi() {
1164 return mExifDpi;
1165 }
1166
resample()1167 bool DkResizeDialog::resample() {
1168 return mResampleCheck->isChecked();
1169 }
1170
updateWidth()1171 void DkResizeDialog::updateWidth() {
1172
1173 float pWidth = (float)mWPixelSpin->value();
1174
1175 if (mSizeBox->currentIndex() == size_percent)
1176 pWidth = (float)qRound(pWidth/100 * mImg.width());
1177
1178 float units = mResFactor.at(mResUnitBox->currentIndex()) * mUnitFactor.at(mUnitBox->currentIndex());
1179 float width = pWidth/mExifDpi * units;
1180 mWidthSpin->setValue(width);
1181 }
1182
updateHeight()1183 void DkResizeDialog::updateHeight() {
1184
1185 float pHeight = (float)mHPixelSpin->value();
1186
1187 if (mSizeBox->currentIndex() == size_percent)
1188 pHeight = (float)qRound(pHeight/100 * mImg.height());
1189
1190 float units = mResFactor.at(mResUnitBox->currentIndex()) * mUnitFactor.at(mUnitBox->currentIndex());
1191 float height = pHeight/mExifDpi * units;
1192 mHeightSpin->setValue(height);
1193 }
1194
updateResolution()1195 void DkResizeDialog::updateResolution() {
1196
1197 qDebug() << "updating resolution...";
1198 float wPixel = (float)mWPixelSpin->value();
1199 float width = (float)mWidthSpin->value();
1200
1201 float units = mResFactor.at(mResUnitBox->currentIndex()) * mUnitFactor.at(mUnitBox->currentIndex());
1202 float resolution = wPixel/width * units;
1203 mResolutionSpin->setValue(resolution);
1204 }
1205
updatePixelHeight()1206 void DkResizeDialog::updatePixelHeight() {
1207
1208 float height = (float)mHeightSpin->value();
1209
1210 // *1000/10 is for more beautiful values
1211 float units = mResFactor.at(mResUnitBox->currentIndex()) * mUnitFactor.at(mUnitBox->currentIndex());
1212 float pixelHeight = (mSizeBox->currentIndex() != size_percent) ? qRound(height*mExifDpi / units) : qRound(1000.0f*height*mExifDpi / (units * mImg.height()))/10.0f;
1213
1214 mHPixelSpin->setValue(pixelHeight);
1215 }
1216
updatePixelWidth()1217 void DkResizeDialog::updatePixelWidth() {
1218
1219 float width = (float)mWidthSpin->value();
1220
1221 float units = mResFactor.at(mResUnitBox->currentIndex()) * mUnitFactor.at(mUnitBox->currentIndex());
1222 float pixelWidth = (mSizeBox->currentIndex() != size_percent) ? qRound(width*mExifDpi / units) : qRound(1000.0f*width*mExifDpi / (units * mImg.width()))/10.0f;
1223 mWPixelSpin->setValue(pixelWidth);
1224 }
1225
on_lockButtonDim_clicked()1226 void DkResizeDialog::on_lockButtonDim_clicked() {
1227
1228 mLockButton->setChecked(mLockButtonDim->isChecked());
1229 if (!mLockButtonDim->isChecked())
1230 return;
1231
1232 initBoxes();
1233 drawPreview();
1234 }
1235
on_lockButton_clicked()1236 void DkResizeDialog::on_lockButton_clicked() {
1237
1238 mLockButtonDim->setChecked(mLockButton->isChecked());
1239
1240 if (!mLockButton->isChecked())
1241 return;
1242
1243 initBoxes();
1244 drawPreview();
1245 }
1246
on_wPixelSpin_valueChanged(double val)1247 void DkResizeDialog::on_wPixelSpin_valueChanged(double val) {
1248
1249 if (!mWPixelSpin->hasFocus())
1250 return;
1251
1252 updateWidth();
1253
1254 if (!mLockButton->isChecked()) {
1255 drawPreview();
1256 return;
1257 }
1258
1259 int newHeight = (mSizeBox->currentIndex() != size_percent) ? qRound((float)val/(float)mImg.width() * mImg.height()) : qRound(val);
1260 mHPixelSpin->setValue(newHeight);
1261 updateHeight();
1262 drawPreview();
1263 }
1264
on_hPixelSpin_valueChanged(double val)1265 void DkResizeDialog::on_hPixelSpin_valueChanged(double val) {
1266
1267 if(!mHPixelSpin->hasFocus())
1268 return;
1269
1270 updateHeight();
1271
1272 if (!mLockButton->isChecked()) {
1273 drawPreview();
1274 return;
1275 }
1276
1277 int newWidth = (mSizeBox->currentIndex() != size_percent) ? qRound(val/(float)mImg.height() * (float)mImg.width()) : qRound(val);
1278 mWPixelSpin->setValue(newWidth);
1279 updateWidth();
1280 drawPreview();
1281 }
1282
on_widthSpin_valueChanged(double val)1283 void DkResizeDialog::on_widthSpin_valueChanged(double val) {
1284
1285 if (!mWidthSpin->hasFocus())
1286 return;
1287
1288 if (mResampleCheck->isChecked())
1289 updatePixelWidth();
1290
1291 if (!mLockButton->isChecked()) {
1292 drawPreview();
1293 return;
1294 }
1295
1296 mHeightSpin->setValue(val/(float)mImg.width() * (float)mImg.height());
1297
1298 if (mResampleCheck->isChecked())
1299 updatePixelHeight();
1300
1301 if (!mResampleCheck->isChecked())
1302 updateResolution();
1303
1304 drawPreview();
1305 }
1306
on_heightSpin_valueChanged(double val)1307 void DkResizeDialog::on_heightSpin_valueChanged(double val) {
1308
1309 if (!mHeightSpin->hasFocus())
1310 return;
1311
1312 if (mResampleCheck->isChecked())
1313 updatePixelHeight();
1314
1315 if (!mLockButton->isChecked()) {
1316 drawPreview();
1317 return;
1318 }
1319
1320 mWidthSpin->setValue(val/(float)mImg.height() * (float)mImg.width());
1321
1322 if (mResampleCheck->isChecked())
1323 updatePixelWidth();
1324
1325 if (!mResampleCheck->isChecked())
1326 updateResolution();
1327 drawPreview();
1328 }
1329
on_resolutionSpin_valueChanged(double val)1330 void DkResizeDialog::on_resolutionSpin_valueChanged(double val) {
1331
1332 mExifDpi = (float)val;
1333
1334 if (!mResolutionSpin->hasFocus())
1335 return;
1336
1337 updatePixelWidth();
1338 updatePixelHeight();
1339
1340 if (mResampleCheck->isChecked()) {
1341 drawPreview();
1342 return;
1343 }
1344
1345 initBoxes();
1346 }
1347
1348
on_unitBox_currentIndexChanged(int)1349 void DkResizeDialog::on_unitBox_currentIndexChanged(int) {
1350
1351 updateHeight();
1352 updateWidth();
1353 //initBoxes();
1354 }
1355
on_sizeBox_currentIndexChanged(int idx)1356 void DkResizeDialog::on_sizeBox_currentIndexChanged(int idx) {
1357
1358 if (idx == size_pixel) {
1359 mWPixelSpin->setDecimals(0);
1360 mHPixelSpin->setDecimals(0);
1361 }
1362 else {
1363 mWPixelSpin->setDecimals(2);
1364 mHPixelSpin->setDecimals(2);
1365 }
1366
1367 updatePixelHeight();
1368 updatePixelWidth();
1369 }
1370
on_resUnitBox_currentIndexChanged(int)1371 void DkResizeDialog::on_resUnitBox_currentIndexChanged(int) {
1372
1373 updateResolution();
1374 //initBoxes();
1375 }
1376
on_resampleCheck_clicked()1377 void DkResizeDialog::on_resampleCheck_clicked() {
1378
1379 mResampleBox->setEnabled(mResampleCheck->isChecked());
1380 mWPixelSpin->setEnabled(mResampleCheck->isChecked());
1381 mHPixelSpin->setEnabled(mResampleCheck->isChecked());
1382
1383 if (!mResampleCheck->isChecked()) {
1384 mLockButton->setChecked(true);
1385 mLockButtonDim->setChecked(true);
1386 initBoxes();
1387 }
1388 else
1389 drawPreview();
1390 }
1391
on_gammaCorrection_clicked()1392 void DkResizeDialog::on_gammaCorrection_clicked() {
1393
1394 drawPreview(); // diem: just update
1395 }
1396
on_resampleBox_currentIndexChanged(int)1397 void DkResizeDialog::on_resampleBox_currentIndexChanged(int) {
1398 drawPreview();
1399 }
1400
updateSnippets()1401 void DkResizeDialog::updateSnippets() {
1402
1403 if (mImg.isNull() /*|| !isVisible()*/)
1404 return;
1405
1406 mOrigView->setImage(mImg);
1407 mOrigView->fullView();
1408 mOrigView->zoomConstraints(mOrigView->get100Factor());
1409 }
1410
drawPreview()1411 void DkResizeDialog::drawPreview() {
1412
1413 if (mImg.isNull() || !isVisible())
1414 return;
1415
1416 QImage newImg = mOrigView->getCurrentImageRegion();
1417
1418 // TODO: thread here!
1419 QImage img = resizeImg(newImg);
1420
1421 // TODO: is there a better way of mapping the pixels? (ipl here introduces artifacts that are not in the final image)
1422 img = img.scaled(mPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation);
1423 mPreviewLabel->setPixmap(QPixmap::fromImage(img));
1424 }
1425
resizeImg(QImage img,bool silent)1426 QImage DkResizeDialog::resizeImg(QImage img, bool silent) {
1427
1428 if (img.isNull())
1429 return img;
1430
1431 QSize newSize;
1432
1433 if (mSizeBox->currentIndex() == size_percent)
1434 newSize = QSize(qRound(mWPixelSpin->value()/100.0f * mImg.width()), qRound(mHPixelSpin->value()/100.0f * mImg.height()));
1435 else
1436 newSize = QSize(qRound(mWPixelSpin->value()), qRound(mHPixelSpin->value()));
1437
1438 QSize imgSize = mImg.size();
1439
1440 // nothing to do
1441 if (mImg.size() == newSize)
1442 return img;
1443
1444 if (mImg.size() != img.size()) {
1445 // compute relative size
1446 float relWidth = (float)newSize.width()/(float)imgSize.width();
1447 float relHeight = (float)newSize.height()/(float)imgSize.height();
1448
1449 newSize = QSize(qRound(img.width()*relWidth), qRound(img.height()*relHeight));
1450 }
1451
1452 if (newSize.width() < mWPixelSpin->minimum() || newSize.width() > mWPixelSpin->maximum() ||
1453 newSize.height() < mHPixelSpin->minimum() || newSize.height() > mHPixelSpin->maximum()) {
1454
1455 if (!silent) {
1456 QMessageBox errorDialog(this);
1457 errorDialog.setIcon(QMessageBox::Critical);
1458 errorDialog.setText(tr("Sorry, but the image size %1 x %2 is illegal.").arg(newSize.width()).arg(newSize.height()));
1459 errorDialog.show();
1460 errorDialog.exec();
1461 }
1462 }
1463
1464 QImage rImg = DkImage::resizeImage(img, newSize, 1.0f, mResampleBox->currentIndex(), mGammaCorrection->isChecked());
1465
1466 if (rImg.isNull() && !silent) {
1467 qDebug() << "image size: " << newSize;
1468 QMessageBox errorDialog(this);
1469 errorDialog.setIcon(QMessageBox::Critical);
1470 errorDialog.setText(tr("Sorry, the image is too large: %1").arg(DkImage::getBufferSize(newSize, 32)));
1471 errorDialog.show();
1472 errorDialog.exec();
1473 }
1474
1475 return rImg;
1476 }
1477
setVisible(bool visible)1478 void DkResizeDialog::setVisible(bool visible) {
1479
1480 QDialog::setVisible(visible);
1481
1482 updateSnippets();
1483 drawPreview();
1484
1485 }
1486
resizeEvent(QResizeEvent * re)1487 void DkResizeDialog::resizeEvent(QResizeEvent* re) {
1488
1489 drawPreview();
1490 QDialog::resizeEvent(re);
1491 }
1492
1493 // DkShortcutDelegate --------------------------------------------------------------------
DkShortcutDelegate(QObject * parent)1494 DkShortcutDelegate::DkShortcutDelegate(QObject* parent) : QItemDelegate(parent) {
1495 mItem = 0;
1496 mClearPm = DkImage::loadIcon(":/nomacs/img/close.svg");
1497 }
1498
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const1499 QWidget* DkShortcutDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const {
1500
1501 QWidget* scW = QItemDelegate::createEditor(parent, option, index);
1502
1503 if (!scW)
1504 return scW;
1505
1506 #if QT_VERSION < 0x050000
1507 connect(scW, SIGNAL(textChanged(const QString&)), this, SLOT(textChanged(const QString&)));
1508 connect(scW, SIGNAL(editingFinished()), this, SLOT(textChanged()));
1509 #else
1510 connect(scW, SIGNAL(keySequenceChanged(const QKeySequence&)), this, SLOT(keySequenceChanged(const QKeySequence&)));
1511 #endif
1512
1513 return scW;
1514 }
1515
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const1516 QSize DkShortcutDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
1517
1518 QSize s = QItemDelegate::sizeHint(option, index);
1519
1520 if (index.column() == 1)
1521 s.setWidth(s.width() + s.height()); // make room for our x
1522
1523 return s;
1524 }
1525
setEditorData(QWidget * editor,const QModelIndex & index) const1526 void DkShortcutDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const {
1527
1528 const_cast<DkShortcutDelegate*>(this)->mItem = index.internalPointer();
1529 emit clearDuplicateSignal();
1530
1531 QItemDelegate::setEditorData(editor, index);
1532 }
1533
editorEvent(QEvent * event,QAbstractItemModel * model,const QStyleOptionViewItem & option,const QModelIndex & index)1534 bool DkShortcutDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) {
1535
1536 // did the user click the x?
1537 if (event->type() == QEvent::MouseButtonRelease) {
1538
1539 QMouseEvent * e = (QMouseEvent *)event;
1540 int clickX = e->x();
1541 int clickY = e->y();
1542
1543 QRect r = option.rect;
1544 int x = r.left() + r.width() - r.height();
1545
1546 if (clickX > x && clickX < x + r.height())
1547 if (clickY > r.top() && clickY < r.top() + r.height()) {
1548 model->setData(index, QKeySequence());
1549 }
1550 }
1551
1552 mItem = index.internalPointer();
1553
1554 return QItemDelegate::editorEvent(event, model, option, index);
1555 }
1556
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const1557 void DkShortcutDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
1558
1559 // calling before means, that our x is always in front
1560 QItemDelegate::paint(painter, option, index);
1561
1562 TreeItem* ti = static_cast<TreeItem*>(index.internalPointer());
1563
1564 if (index.column() == 1 && ti && !ti->data(1).toString().isEmpty()) {
1565
1566 QRect r = option.rect;//getting the rect of the cell
1567 int s = r.height();
1568 QRect pmr(r.right()-s, r.top(), s, s);
1569
1570 painter->drawPixmap(pmr, mClearPm);
1571 }
1572 }
1573
1574 #if QT_VERSION < 0x050000
textChanged(const QString & text)1575 void DkShortcutDelegate::textChanged(const QString& text) {
1576 emit checkDuplicateSignal(text, mItem);
1577 }
1578
keySequenceChanged(const QKeySequence &)1579 void DkShortcutDelegate::keySequenceChanged(const QKeySequence&) {}
1580 #else
1581
textChanged(const QString &)1582 void DkShortcutDelegate::textChanged(const QString&) {} // dummy since the moccer is to dumb to get #if defs
1583
keySequenceChanged(const QKeySequence & keySequence)1584 void DkShortcutDelegate::keySequenceChanged(const QKeySequence& keySequence) {
1585 emit checkDuplicateSignal(keySequence, mItem);
1586 }
1587 #endif
1588
1589 // fun fact: there are ~10^4500 (binary) images of size 128x128
1590 // increase counter if you think this is fascinating: 1
1591
1592 // DkShortcutsModel --------------------------------------------------------------------
DkShortcutsModel(QObject * parent)1593 DkShortcutsModel::DkShortcutsModel(QObject* parent) : QAbstractItemModel(parent) {
1594
1595 // create root
1596 QVector<QVariant> rootData;
1597 rootData << tr("Name") << tr("Shortcut");
1598
1599 mRootItem = new TreeItem(rootData);
1600
1601 }
1602
~DkShortcutsModel()1603 DkShortcutsModel::~DkShortcutsModel() {
1604 delete mRootItem;
1605 }
1606
index(int row,int column,const QModelIndex & parent) const1607 QModelIndex DkShortcutsModel::index(int row, int column, const QModelIndex &parent) const {
1608
1609 if (!hasIndex(row, column, parent))
1610 return QModelIndex();
1611
1612 TreeItem *parentItem;
1613
1614 if (!parent.isValid())
1615 parentItem = mRootItem;
1616 else
1617 parentItem = static_cast<TreeItem*>(parent.internalPointer());
1618
1619 TreeItem *childItem = parentItem->child(row);
1620
1621 if (childItem)
1622 return createIndex(row, column, childItem);
1623 else
1624 return QModelIndex();
1625 }
1626
parent(const QModelIndex & index) const1627 QModelIndex DkShortcutsModel::parent(const QModelIndex &index) const {
1628
1629 if (!index.isValid())
1630 return QModelIndex();
1631
1632 TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
1633 TreeItem *parentItem = childItem->parent();
1634
1635 if (parentItem == mRootItem)
1636 return QModelIndex();
1637
1638 return createIndex(parentItem->row(), 0, parentItem);
1639 }
1640
rowCount(const QModelIndex & parent) const1641 int DkShortcutsModel::rowCount(const QModelIndex& parent) const {
1642
1643 TreeItem *parentItem;
1644 if (parent.column() > 0)
1645 return 0;
1646
1647 if (!parent.isValid())
1648 parentItem = mRootItem;
1649 else
1650 parentItem = static_cast<TreeItem*>(parent.internalPointer());
1651
1652 return parentItem->childCount();
1653 }
1654
columnCount(const QModelIndex & parent) const1655 int DkShortcutsModel::columnCount(const QModelIndex& parent) const {
1656
1657 if (parent.isValid())
1658 return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
1659 else
1660 return mRootItem->columnCount();
1661 }
1662
data(const QModelIndex & index,int role) const1663 QVariant DkShortcutsModel::data(const QModelIndex& index, int role) const {
1664
1665 if (!index.isValid()) {
1666 qDebug() << "invalid row: " << index.row();
1667 return QVariant();
1668 }
1669
1670 if (role == Qt::DisplayRole || role == Qt::EditRole) {
1671
1672 TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
1673 return item->data(index.column());
1674 }
1675
1676 return QVariant();
1677 }
1678
headerData(int section,Qt::Orientation orientation,int role) const1679 QVariant DkShortcutsModel::headerData(int section, Qt::Orientation orientation, int role) const {
1680
1681 if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
1682 return QVariant();
1683
1684 return mRootItem->data(section);
1685 }
1686
setData(const QModelIndex & index,const QVariant & value,int role)1687 bool DkShortcutsModel::setData(const QModelIndex& index, const QVariant& value, int role) {
1688
1689 if (!index.isValid() || role != Qt::EditRole)
1690 return false;
1691
1692 if (index.column() == 1) {
1693
1694 QKeySequence ks = value.value<QKeySequence>();
1695 TreeItem* duplicate = mRootItem->find(ks, index.column());
1696 if (duplicate) duplicate->setData(QKeySequence(), index.column());
1697 //if (!duplicate) qDebug() << ks << " no duplicate found...";
1698
1699 TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
1700 item->setData(ks, index.column());
1701 }
1702 else {
1703 TreeItem* item = static_cast<TreeItem*>(index.internalPointer());
1704 item->setData(value, index.column());
1705 }
1706
1707 emit dataChanged(index, index);
1708 return true;
1709 }
1710
flags(const QModelIndex & index) const1711 Qt::ItemFlags DkShortcutsModel::flags(const QModelIndex& index) const {
1712
1713 if (!index.isValid())
1714 return Qt::ItemIsEditable;
1715
1716 Qt::ItemFlags flags;
1717
1718 if (index.column() == 0)
1719 flags = QAbstractItemModel::flags(index);
1720 if (index.column() == 1)
1721 flags = QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
1722
1723 return flags;
1724 }
1725
addDataActions(QVector<QAction * > actions,const QString & name)1726 void DkShortcutsModel::addDataActions(QVector<QAction*> actions, const QString& name) {
1727
1728 // create root
1729 QVector<QVariant> menuData;
1730 menuData << name;
1731
1732 TreeItem* menuItem = new TreeItem(menuData, mRootItem);
1733
1734 for (int idx = 0; idx < actions.size(); idx++) {
1735
1736 // skip NULL actions - this should never happen!
1737 if (actions[idx]->text().isNull()) {
1738 qDebug() << "NULL Action detected when creating shortcuts...";
1739 continue;
1740 }
1741
1742 QString text = actions[idx]->text().remove("&");
1743
1744 QVector<QVariant> actionData;
1745 actionData << text << actions[idx]->shortcut();
1746
1747 TreeItem* dataItem = new TreeItem(actionData, menuItem);
1748 menuItem->appendChild(dataItem);
1749 }
1750
1751 mRootItem->appendChild(menuItem);
1752 mActions.append(actions);
1753 }
1754
checkDuplicate(const QString & text,void * item)1755 void DkShortcutsModel::checkDuplicate(const QString& text, void* item) {
1756
1757 if (text.isEmpty()) {
1758 emit duplicateSignal("");
1759 return;
1760 }
1761
1762 QKeySequence ks(text);
1763 checkDuplicate(ks, item);
1764 }
1765
checkDuplicate(const QKeySequence & ks,void * item)1766 void DkShortcutsModel::checkDuplicate(const QKeySequence& ks, void* item) {
1767
1768 if (ks.isEmpty()) {
1769 emit duplicateSignal("");
1770 return;
1771 }
1772
1773 TreeItem* duplicate = mRootItem->find(ks, 1);
1774
1775 if (duplicate == item)
1776 return;
1777
1778 if (duplicate && duplicate->parent()) {
1779 emit duplicateSignal(tr("%1 already used by %2 > %3\nPress ESC to undo changes")
1780 .arg(duplicate->data(1).toString())
1781 .arg(duplicate->parent()->data(0).toString())
1782 .arg(duplicate->data(0).toString()));
1783 }
1784 else if (duplicate) {
1785 emit duplicateSignal(tr("%1 already used by %2\nPress ESC to undo changes")
1786 .arg(duplicate->data(1).toString())
1787 .arg(duplicate->data(0).toString()));
1788 }
1789 else
1790 emit duplicateSignal("");
1791 }
1792
clearDuplicateInfo() const1793 void DkShortcutsModel::clearDuplicateInfo() const {
1794
1795 qDebug() << "duplicates cleared...";
1796 emit duplicateSignal("");
1797 }
1798
resetActions()1799 void DkShortcutsModel::resetActions() {
1800
1801 DefaultSettings settings;
1802 settings.beginGroup("CustomShortcuts");
1803
1804 for (int pIdx = 0; pIdx < mActions.size(); pIdx++) {
1805
1806 QVector<QAction*> cActions = mActions.at(pIdx);
1807
1808 for (int idx = 0; idx < cActions.size(); idx++) {
1809 QString val = settings.value(cActions[idx]->text(), "no-shortcut").toString();
1810
1811 if (val != "no-shortcut") {
1812 cActions[idx]->setShortcut(QKeySequence());
1813 }
1814 }
1815 }
1816
1817 settings.endGroup();
1818 }
1819
saveActions() const1820 void DkShortcutsModel::saveActions() const {
1821
1822 if (!mRootItem)
1823 return;
1824
1825 DefaultSettings settings;
1826 settings.beginGroup("CustomShortcuts");
1827
1828 // loop all menu entries
1829 for (int idx = 0; idx < mRootItem->childCount(); idx++) {
1830
1831 TreeItem* cMenu = mRootItem->child(idx);
1832 QVector<QAction*> cActions = mActions.at(idx);
1833
1834 // loop all action entries
1835 for (int mIdx = 0; mIdx < cMenu->childCount(); mIdx++) {
1836
1837 TreeItem* cItem = cMenu->child(mIdx);
1838 QKeySequence ks = cItem->data(1).value<QKeySequence>();
1839
1840 if (cActions.at(mIdx)->shortcut() != ks) {
1841
1842 if (cActions.at(mIdx)->text().isEmpty()) {
1843 qDebug() << "empty action detected! shortcut is: " << ks;
1844 continue;
1845 }
1846
1847 QString aT = cActions.at(mIdx)->text().remove("&");
1848
1849 cActions.at(mIdx)->setShortcut(ks); // assign new shortcut
1850 settings.setValue(aT, ks.toString()); // note this works as long as you don't change the language!
1851 }
1852 }
1853 }
1854 settings.endGroup();
1855
1856 }
1857
1858 // DkShortcutsDialog --------------------------------------------------------------------
DkShortcutsDialog(QWidget * parent,Qt::WindowFlags flags)1859 DkShortcutsDialog::DkShortcutsDialog(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) {
1860
1861 createLayout();
1862 }
1863
createLayout()1864 void DkShortcutsDialog::createLayout() {
1865
1866 setWindowTitle(tr("Keyboard Shortcuts"));
1867
1868 QVBoxLayout* layout = new QVBoxLayout(this);
1869
1870 // register our special shortcut editor
1871 QItemEditorFactory *factory = new QItemEditorFactory;
1872
1873 #if QT_VERSION < 0x050000
1874 QItemEditorCreatorBase *shortcutListCreator =
1875 new QStandardItemEditorCreator<DkShortcutEditor>();
1876 #else
1877 QItemEditorCreatorBase *shortcutListCreator =
1878 new QStandardItemEditorCreator<QKeySequenceEdit>();
1879 #endif
1880
1881 factory->registerEditor(QVariant::KeySequence, shortcutListCreator);
1882
1883 QItemEditorFactory::setDefaultFactory(factory);
1884
1885 // create our beautiful shortcut view
1886 mModel = new DkShortcutsModel(this);
1887
1888 DkShortcutDelegate* scDelegate = new DkShortcutDelegate(this);
1889
1890 QTreeView* treeView = new QTreeView(this);
1891 treeView->setModel(mModel);
1892 treeView->setItemDelegate(scDelegate);
1893 treeView->setAlternatingRowColors(true);
1894 treeView->setIndentation(8);
1895 treeView->header()->resizeSection(0, 200);
1896
1897 mNotificationLabel = new QLabel(this);
1898 mNotificationLabel->setObjectName("DkDecentInfo");
1899 mNotificationLabel->setProperty("warning", true);
1900 //notificationLabel->setTextFormat(Qt::)
1901
1902 mDefaultButton = new QPushButton(tr("Set to &Default"), this);
1903 mDefaultButton->setToolTip(tr("Removes All Custom Shortcuts"));
1904 connect(mDefaultButton, SIGNAL(clicked()), this, SLOT(defaultButtonClicked()));
1905 connect(mModel, SIGNAL(duplicateSignal(const QString&)), mNotificationLabel, SLOT(setText(const QString&)));
1906
1907 #if QT_VERSION < 0x050000
1908 connect(scDelegate, SIGNAL(checkDuplicateSignal(const QString&, void*)), model, SLOT(checkDuplicate(const QString&, void*)));
1909 #else
1910 connect(scDelegate, SIGNAL(checkDuplicateSignal(const QKeySequence&, void*)), mModel, SLOT(checkDuplicate(const QKeySequence&, void*)));
1911 connect(scDelegate, SIGNAL(clearDuplicateSignal()), mModel, SLOT(clearDuplicateInfo()));
1912 #endif
1913
1914 // mButtons
1915 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
1916 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
1917 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
1918 buttons->addButton(mDefaultButton, QDialogButtonBox::ActionRole);
1919
1920 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
1921 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
1922
1923 layout->addWidget(treeView);
1924 layout->addWidget(mNotificationLabel);
1925 //layout->addSpacing()
1926 layout->addWidget(buttons);
1927 resize(420, 500);
1928 }
1929
addActions(const QVector<QAction * > & actions,const QString & name)1930 void DkShortcutsDialog::addActions(const QVector<QAction*>& actions, const QString& name) {
1931
1932 QString cleanName = name;
1933 mModel->addDataActions(actions, cleanName.remove("&"));
1934
1935 }
1936
contextMenu(const QPoint &)1937 void DkShortcutsDialog::contextMenu(const QPoint&) {
1938
1939 }
1940
defaultButtonClicked()1941 void DkShortcutsDialog::defaultButtonClicked() {
1942
1943 if (mModel) mModel->resetActions();
1944
1945 DefaultSettings settings;
1946 settings.remove("CustomShortcuts");
1947
1948 QDialog::reject();
1949 }
1950
accept()1951 void DkShortcutsDialog::accept() {
1952
1953 // assign shortcuts & save them if they are changed
1954 if (mModel) mModel->saveActions();
1955
1956 QDialog::accept();
1957 }
1958
1959 // DkTextDialog --------------------------------------------------------------------
DkTextDialog(QWidget * parent,Qt::WindowFlags flags)1960 DkTextDialog::DkTextDialog(QWidget* parent /* = 0 */, Qt::WindowFlags flags /* = 0 */) : QDialog(parent, flags) {
1961
1962 setWindowTitle(tr("Text Editor"));
1963 createLayout();
1964 }
1965
createLayout()1966 void DkTextDialog::createLayout() {
1967
1968 textEdit = new QTextEdit(this);
1969
1970 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal);
1971 buttons->button(QDialogButtonBox::Ok)->setDefault(true); // ok is auto-default
1972 buttons->button(QDialogButtonBox::Ok)->setText(tr("&Save"));
1973 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Close"));
1974
1975 connect(buttons, SIGNAL(accepted()), this, SLOT(save()));
1976 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
1977
1978 // dialog layout
1979 QVBoxLayout* layout = new QVBoxLayout(this);
1980 layout->addWidget(textEdit);
1981 layout->addWidget(buttons);
1982 }
1983
setText(const QStringList & text)1984 void DkTextDialog::setText(const QStringList& text) {
1985 textEdit->setText(text.join("\n"));
1986 }
1987
save()1988 void DkTextDialog::save() {
1989
1990 QStringList folders = DkSettingsManager::param().global().recentFolders;
1991 QString savePath = QDir::rootPath();
1992
1993 if (folders.size() > 0)
1994 savePath = folders.first();
1995
1996 QStringList extList;
1997 extList << tr("Text File (*.txt)") << tr("All Files (*.*)");
1998 QString saveFilters(extList.join(";;"));
1999
2000 QString fileName = QFileDialog::getSaveFileName(
2001 this,
2002 tr("Save Text File"),
2003 savePath,
2004 saveFilters,
2005 nullptr,
2006 DkDialog::fileDialogOptions()
2007 );
2008
2009 if (fileName.isEmpty())
2010 return;
2011
2012 QFile file(fileName);
2013
2014 if (file.open(QIODevice::WriteOnly)) {
2015 QTextStream stream(&file);
2016 stream << textEdit->toPlainText();
2017 }
2018 else {
2019 QMessageBox::critical(this, tr("Error"), tr("Could not save: %1\n%2").arg(fileName).arg(file.errorString()));
2020 return;
2021 }
2022
2023 file.close();
2024 accept();
2025 }
2026
2027 // DkUpdateDialog --------------------------------------------------------------------
DkUpdateDialog(QWidget * parent,Qt::WindowFlags flags)2028 DkUpdateDialog::DkUpdateDialog(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) {
2029 init();
2030 }
2031
init()2032 void DkUpdateDialog::init() {
2033 createLayout();
2034
2035 connect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
2036 connect(okButton, SIGNAL(clicked()), this, SLOT(okButtonClicked()));
2037 }
2038
createLayout()2039 void DkUpdateDialog::createLayout() {
2040 setFixedWidth(300);
2041 setFixedHeight(150);
2042 setWindowTitle(tr("nomacs updater"));
2043
2044 QGridLayout* gridlayout = new QGridLayout;
2045 upperLabel = new QLabel;
2046 upperLabel->setOpenExternalLinks(true);
2047
2048 QWidget* lowerWidget = new QWidget;
2049 QHBoxLayout* hbox = new QHBoxLayout;
2050 okButton = new QPushButton(tr("Install Now"));
2051 cancelButton = new QPushButton(tr("Cancel"));
2052 hbox->addStretch();
2053 hbox->addWidget(okButton);
2054 hbox->addWidget(cancelButton);
2055 lowerWidget->setLayout(hbox);
2056
2057 gridlayout->addWidget(upperLabel, 0, 0);
2058 gridlayout->addWidget(lowerWidget, 1, 0);
2059
2060 setLayout(gridlayout);
2061
2062 }
2063
okButtonClicked()2064 void DkUpdateDialog::okButtonClicked() {
2065 emit startUpdate();
2066 close();
2067 }
2068
2069 // DkPrintPreviewDialog --------------------------------------------------------------------
DkPrintPreviewDialog(QWidget * parent,Qt::WindowFlags flags)2070 DkPrintPreviewDialog::DkPrintPreviewDialog(QWidget* parent, Qt::WindowFlags flags)
2071 : QDialog(parent, flags) {
2072
2073 setWindowTitle(tr("Print Preview"));
2074 init();
2075 }
2076
setImage(const QImage & img)2077 void DkPrintPreviewDialog::setImage(const QImage& img) {
2078
2079 mPreview->setImage(img);
2080
2081 if (!img.isNull() && img.width() > img.height())
2082 mPreview->setLandscapeOrientation();
2083 else
2084 mPreview->setPortraitOrientation();
2085 }
2086
addImage(const QImage & img)2087 void DkPrintPreviewDialog::addImage(const QImage& img) {
2088
2089 mPreview->addImage(img);
2090 }
2091
init()2092 void DkPrintPreviewDialog::init() {
2093
2094 if (!mPrinter) {
2095 #ifdef Q_OS_WIN
2096 if(QPrinterInfo::defaultPrinter().isNull())
2097 mPrinter = new QPrinter(); // new QPrinter(QPrinter::HighResolution);
2098 else
2099 mPrinter = new QPrinter(QPrinterInfo::defaultPrinter());
2100
2101 #else
2102 mPrinter = new QPrinter();
2103 #endif
2104 }
2105
2106 mPreview = new DkPrintPreviewWidget(mPrinter, this);
2107
2108 createIcons();
2109 createLayout();
2110
2111 setMinimumHeight(600);
2112 setMinimumWidth(800);
2113
2114 connect(mPreview, SIGNAL(dpiChanged(int)), mDpiBox, SLOT(setValue(int)));
2115 }
2116
createIcons()2117 void DkPrintPreviewDialog::createIcons() {
2118
2119 mIcons.resize(print_end);
2120
2121 mIcons[print_fit_width] = DkImage::loadIcon(":/nomacs/img/fit-width.svg");
2122 mIcons[print_fit_page] = DkImage::loadIcon(":/nomacs/img/zoom-reset.svg");
2123 mIcons[print_zoom_in] = DkImage::loadIcon(":/nomacs/img/zoom-in.svg");
2124 mIcons[print_zoom_out] = DkImage::loadIcon(":/nomacs/img/zoom-out.svg");
2125 mIcons[print_reset_dpi] = DkImage::loadIcon(":/nomacs/img/zoom-100.svg");
2126 mIcons[print_landscape] = DkImage::loadIcon(":/nomacs/img/landscape.svg");
2127 mIcons[print_portrait] = DkImage::loadIcon(":/nomacs/img/portrait.svg");
2128 mIcons[print_setup] = DkImage::loadIcon(":/nomacs/img/print-setup.svg");
2129 mIcons[print_printer] = DkImage::loadIcon(":/nomacs/img/print.svg");
2130 }
2131
createLayout()2132 void DkPrintPreviewDialog::createLayout() {
2133
2134 QAction* fitWidth = new QAction(mIcons[print_fit_width], tr("Fit Width"), this);
2135 QAction* fitPage = new QAction(mIcons[print_fit_page], tr("Fit Page"), this);
2136
2137 QAction* zoomIn = new QAction(mIcons[print_zoom_in], tr("Zoom in"), this);
2138 zoomIn->setShortcut(Qt::Key_Plus);
2139
2140 QAction* zoomOut = new QAction(mIcons[print_zoom_out], tr("Zoom out"), this);
2141 zoomOut->setShortcut(Qt::Key_Minus);
2142
2143 QString zoomTip = tr("keep ALT key pressed to zoom with the mouse wheel");
2144 zoomIn->setToolTip(zoomTip);
2145 zoomOut->setToolTip(zoomTip);
2146
2147 mDpiBox = new QSpinBox(this);
2148 mDpiBox->setSuffix(" dpi");
2149 mDpiBox->setMinimum(10);
2150 mDpiBox->setMaximum(9999);
2151 mDpiBox->setSingleStep(100);
2152
2153 // Portrait/Landscape
2154 QAction* prt = new QAction(mIcons[print_portrait], tr("Portrait"), this);
2155 prt->setObjectName("portrait");
2156
2157 QAction* lsc = new QAction(mIcons[print_landscape], tr("Landscape"), this);
2158 lsc->setObjectName("landscape");
2159
2160 // Print
2161 QAction* pageSetup = new QAction(mIcons[print_setup], tr("Page setup"), this);
2162 QAction* printAction = new QAction(mIcons[print_printer], tr("Print"), this);
2163
2164 // create toolbar
2165 QToolBar *toolbar = new QToolBar(tr("Print Preview"), this);
2166
2167 toolbar->addAction(fitWidth);
2168 toolbar->addAction(fitPage);
2169
2170 toolbar->addAction(zoomIn);
2171 toolbar->addAction(zoomOut);
2172
2173 toolbar->addWidget(mDpiBox);
2174
2175 toolbar->addAction(prt);
2176 toolbar->addAction(lsc);
2177
2178 toolbar->addSeparator();
2179 toolbar->addAction(pageSetup);
2180 toolbar->addAction(printAction);
2181
2182 toolbar->setIconSize(QSize(DkSettingsManager::param().effectiveIconSize(this), DkSettingsManager::param().effectiveIconSize(this)));
2183
2184 // Cannot use the actions' triggered signal here, since it doesn't autorepeat
2185 QToolButton *zoomInButton = static_cast<QToolButton *>(toolbar->widgetForAction(zoomIn));
2186 zoomInButton->setAutoRepeat(true);
2187 zoomInButton->setAutoRepeatInterval(200);
2188 zoomInButton->setAutoRepeatDelay(200);
2189
2190 QToolButton *zoomOutButton = static_cast<QToolButton *>(toolbar->widgetForAction(zoomOut));
2191 zoomOutButton->setAutoRepeat(true);
2192 zoomOutButton->setAutoRepeatInterval(200);
2193 zoomOutButton->setAutoRepeatDelay(200);
2194
2195 connect(mDpiBox, SIGNAL(valueChanged(int)), mPreview, SLOT(changeDpi(int)));
2196 connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomIn()));
2197 connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOut()));
2198
2199 connect(lsc, SIGNAL(triggered()), mPreview, SLOT(setLandscapeOrientation()));
2200 connect(prt, SIGNAL(triggered()), mPreview, SLOT(setPortraitOrientation()));
2201 connect(fitWidth, SIGNAL(triggered()), this, SLOT(previewFitWidth()));
2202 connect(fitPage, SIGNAL(triggered()), this, SLOT(previewFitPage()));
2203
2204 connect(printAction, SIGNAL(triggered(bool)), this, SLOT(print()));
2205 connect(pageSetup, SIGNAL(triggered(bool)), this, SLOT(pageSetup()));
2206
2207
2208 QMainWindow* mw = new QMainWindow();
2209 mw->addToolBar(toolbar);
2210 mw->setCentralWidget(mPreview);
2211
2212 QHBoxLayout* layout = new QHBoxLayout(this);
2213 layout->setContentsMargins(0, 0, 0, 0);
2214 layout->addWidget(mw);
2215
2216 setLayout(layout);
2217 }
2218
zoomIn()2219 void DkPrintPreviewDialog::zoomIn() {
2220 mPreview->zoomIn();
2221 }
2222
zoomOut()2223 void DkPrintPreviewDialog::zoomOut() {
2224 mPreview->zoomOut();
2225 }
2226
zoom(int scale)2227 void DkPrintPreviewDialog::zoom(int scale) {
2228 mPreview->setZoomFactor(scale/100.0);
2229 }
2230
previewFitWidth()2231 void DkPrintPreviewDialog::previewFitWidth() {
2232
2233 mPreview->fitToWidth();
2234 }
2235
previewFitPage()2236 void DkPrintPreviewDialog::previewFitPage() {
2237
2238 mPreview->fitInView();
2239 }
2240
updateDpiFactor(qreal dpi)2241 void DkPrintPreviewDialog::updateDpiFactor(qreal dpi) {
2242
2243 mDpiBox->setValue(qRound(dpi));
2244 }
2245
pageSetup()2246 void DkPrintPreviewDialog::pageSetup() {
2247
2248 QPageSetupDialog pageSetup(mPrinter, this);
2249
2250 if (pageSetup.exec() == QDialog::Accepted) {
2251
2252 // update possible orientation changes
2253 if (mPreview->orientation() == QPrinter::Portrait) {
2254 mPreview->setPortraitOrientation();
2255 }
2256 else {
2257 mPreview->setLandscapeOrientation();
2258 }
2259 }
2260 }
2261
print()2262 void DkPrintPreviewDialog::print() {
2263
2264 QRect pr = mPrinter->pageRect();
2265
2266 QPrintDialog* pDialog = new QPrintDialog(mPrinter, this);
2267
2268 if (pDialog->exec() == QDialog::Accepted) {
2269
2270 // if the page rect is changed - we have to newly fit the images...
2271 if (pr != mPrinter->pageRect())
2272 mPreview->fitImages();
2273
2274 mPreview->paintForPrinting();
2275 close();
2276 }
2277 }
2278
2279 // DkPrintPreviewWidget --------------------------------------------------------------------
DkPrintPreviewWidget(QPrinter * printer,QWidget * parent,Qt::WindowFlags flags)2280 DkPrintPreviewWidget::DkPrintPreviewWidget(QPrinter* printer, QWidget* parent, Qt::WindowFlags flags) : QPrintPreviewWidget(printer, parent, flags) {
2281
2282 mPrinter = printer;
2283 connect(this, SIGNAL(paintRequested(QPrinter*)), this, SLOT(paintPreview(QPrinter*)));
2284 }
2285
paintEvent(QPaintEvent * event)2286 void DkPrintPreviewWidget::paintEvent(QPaintEvent * event) {
2287
2288 // TODO: can we get a better anti-aliasing here?
2289 QPrintPreviewWidget::paintEvent(event);
2290 }
2291
setImage(const QImage & img)2292 void DkPrintPreviewWidget::setImage(const QImage & img) {
2293
2294 mPrintImages.clear();
2295 addImage(img);
2296 }
2297
addImage(const QImage & img)2298 void DkPrintPreviewWidget::addImage(const QImage & img) {
2299
2300 if (!mPrinter) {
2301 qWarning() << "cannot add images to preview if the printer is empty";
2302 return;
2303 }
2304
2305 QSharedPointer<DkPrintImage> pi(new DkPrintImage(img, mPrinter));
2306
2307 // for now - think of adding multiple images here
2308 mPrintImages << pi;
2309 fitImages();
2310 }
2311
fitImages()2312 void DkPrintPreviewWidget::fitImages() {
2313
2314 double dpi = 0;
2315
2316 for (auto pi : mPrintImages) {
2317 pi->fit();
2318 dpi = pi->dpi();
2319 }
2320
2321 updatePreview();
2322 emit dpiChanged(qRound(dpi));
2323 }
2324
wheelEvent(QWheelEvent * event)2325 void DkPrintPreviewWidget::wheelEvent(QWheelEvent *event) {
2326
2327 if (event->modifiers() != Qt::AltModifier) {
2328 QPrintPreviewWidget::wheelEvent(event);
2329 return;
2330 }
2331
2332 qreal delta = event->delta();
2333 if (DkSettingsManager::param().display().invertZoom)
2334 delta *= -1;
2335 if (event->delta() > 0)
2336 zoomIn();
2337 else
2338 zoomOut();
2339 emit zoomChanged();
2340
2341 QPrintPreviewWidget::wheelEvent(event);
2342 }
2343
centerImage()2344 void DkPrintPreviewWidget::centerImage() {
2345
2346 for (auto pi : mPrintImages)
2347 pi->center();
2348
2349 updatePreview();
2350 }
2351
setLandscapeOrientation()2352 void DkPrintPreviewWidget::setLandscapeOrientation() {
2353
2354 QPrintPreviewWidget::setLandscapeOrientation();
2355
2356 fitImages();
2357 fitInView();
2358 }
2359
setPortraitOrientation()2360 void DkPrintPreviewWidget::setPortraitOrientation() {
2361
2362 QPrintPreviewWidget::setPortraitOrientation();
2363
2364 fitImages();
2365 fitInView();
2366 }
2367
changeDpi(int value)2368 void DkPrintPreviewWidget::changeDpi(int value) {
2369
2370 double inchW = mPrinter->pageRect(QPrinter::Inch).width();
2371 int pxW = mPrinter->pageRect().width();
2372 double sf = ((double)pxW / inchW) / value;
2373
2374 for (auto pi : mPrintImages)
2375 pi->scale(sf);
2376
2377 updatePreview();
2378 }
2379
2380
paintPreview(QPrinter * printer)2381 void DkPrintPreviewWidget::paintPreview(QPrinter* printer) {
2382
2383 QPainter painter(printer);
2384
2385 for (auto pi : mPrintImages) {
2386 pi->draw(painter);
2387
2388 if (pi != mPrintImages.last())
2389 printer->newPage();
2390 }
2391
2392 }
2393
paintForPrinting()2394 void DkPrintPreviewWidget::paintForPrinting() {
2395
2396 int to = mPrinter->toPage() ? mPrinter->toPage() : mPrintImages.size();
2397
2398 QPainter painter(mPrinter);
2399
2400 for (int idx = mPrinter->fromPage(); idx <= to && idx < mPrintImages.size(); idx++) {
2401
2402 mPrintImages[idx]->draw(painter, true);
2403
2404 if (idx+1 < to)
2405 mPrinter->newPage();
2406
2407 }
2408 }
2409
2410
2411 // DkOpacityDialog --------------------------------------------------------------------
DkOpacityDialog(QWidget * parent,Qt::WindowFlags f)2412 DkOpacityDialog::DkOpacityDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) {
2413
2414 createLayout();
2415 }
2416
createLayout()2417 void DkOpacityDialog::createLayout() {
2418
2419 QVBoxLayout* layout = new QVBoxLayout(this);
2420
2421 slider = new DkSlider(tr("Window Opacity"), this);
2422 slider->setMinimum(5);
2423
2424 // mButtons
2425 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
2426 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
2427 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
2428 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
2429 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
2430
2431 layout->addWidget(slider);
2432 layout->addWidget(buttons);
2433 }
2434
value() const2435 int DkOpacityDialog::value() const {
2436 return slider->value();
2437 }
2438
2439 // DkExportTiffDialog --------------------------------------------------------------------
DkExportTiffDialog(QWidget * parent,Qt::WindowFlags f)2440 DkExportTiffDialog::DkExportTiffDialog(QWidget* parent /* = 0 */, Qt::WindowFlags f /* = 0 */) : QDialog(parent, f) {
2441
2442 setWindowTitle(tr("Export Multi-Page TIFF"));
2443 createLayout();
2444 setAcceptDrops(true);
2445
2446 connect(this, SIGNAL(updateImage(const QImage&)), mViewport, SLOT(setImage(const QImage&)));
2447 connect(&mWatcher, SIGNAL(finished()), this, SLOT(processingFinished()));
2448 connect(this, SIGNAL(infoMessage(const QString&)), mMsgLabel, SLOT(setText(const QString&)));
2449 connect(this, SIGNAL(updateProgress(int)), mProgress, SLOT(setValue(int)));
2450 QMetaObject::connectSlotsByName(this);
2451 }
2452
dropEvent(QDropEvent * event)2453 void DkExportTiffDialog::dropEvent(QDropEvent *event) {
2454
2455 if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
2456 QUrl url = event->mimeData()->urls().at(0);
2457 url = url.toLocalFile();
2458
2459 setFile(url.toString());
2460 }
2461 }
2462
dragEnterEvent(QDragEnterEvent * event)2463 void DkExportTiffDialog::dragEnterEvent(QDragEnterEvent *event) {
2464
2465 if (event->mimeData()->hasUrls()) {
2466 QUrl url = event->mimeData()->urls().at(0);
2467 url = url.toLocalFile();
2468 QFileInfo file = QFileInfo(url.toString());
2469
2470 if (file.exists() && file.suffix().indexOf(QRegExp("tif"), Qt::CaseInsensitive) != -1)
2471 event->acceptProposedAction();
2472 }
2473
2474 }
2475
2476
createLayout()2477 void DkExportTiffDialog::createLayout() {
2478
2479 // progress bar
2480 mProgress = new QProgressBar(this);
2481 mProgress->hide();
2482
2483 mMsgLabel = new QLabel(this);
2484 mMsgLabel->setObjectName("DkWarningInfo");
2485 mMsgLabel->hide();
2486
2487 // open handles
2488 QLabel* openLabel = new QLabel(tr("Multi-Page TIFF:"), this);
2489 openLabel->setAlignment(Qt::AlignRight);
2490
2491 QPushButton* openButton = new QPushButton(tr("&Browse"), this);
2492 openButton->setObjectName("openButton");
2493
2494 mTiffLabel = new QLabel(tr("No Multi-Page TIFF loaded"), this);
2495
2496 // save handles
2497 QLabel* saveLabel = new QLabel(tr("Save Folder:"), this);
2498 saveLabel->setAlignment(Qt::AlignRight);
2499
2500 QPushButton* saveButton = new QPushButton(tr("&Browse"), this);
2501 saveButton->setObjectName("saveButton");
2502
2503 mFolderLabel = new QLabel(tr("Specify a Save Folder"), this);
2504
2505 // file name handles
2506 QLabel* fileLabel = new QLabel(tr("Filename:"), this);
2507 fileLabel->setAlignment(Qt::AlignRight);
2508
2509 mFileEdit = new QLineEdit("tiff_page", this);
2510 mFileEdit->setObjectName("fileEdit");
2511
2512 mSuffixBox = new QComboBox(this);
2513 mSuffixBox->addItems(DkSettingsManager::param().app().saveFilters);
2514 mSuffixBox->setCurrentIndex(DkSettingsManager::param().app().saveFilters.indexOf(QRegExp(".*tif.*")));
2515
2516 // export handles
2517 QLabel* exportLabel = new QLabel(tr("Export Pages"));
2518 exportLabel->setAlignment(Qt::AlignRight);
2519
2520 mFromPage = new QSpinBox(0);
2521
2522 mToPage = new QSpinBox(0);
2523
2524 mOverwrite = new QCheckBox(tr("Overwrite"));
2525
2526 mControlWidget = new QWidget(this);
2527 QGridLayout* controlLayout = new QGridLayout(mControlWidget);
2528 controlLayout->addWidget(openLabel, 0, 0);
2529 controlLayout->addWidget(openButton, 0, 1, 1, 2);
2530 controlLayout->addWidget(mTiffLabel, 0, 3, 1, 2);
2531 //controlLayout->setColumnStretch(3, 1);
2532
2533 controlLayout->addWidget(saveLabel, 1, 0);
2534 controlLayout->addWidget(saveButton, 1, 1, 1, 2);
2535 controlLayout->addWidget(mFolderLabel, 1, 3, 1, 2);
2536 //controlLayout->setColumnStretch(3, 1);
2537
2538 controlLayout->addWidget(fileLabel, 2, 0);
2539 controlLayout->addWidget(mFileEdit, 2, 1, 1, 2);
2540 controlLayout->addWidget(mSuffixBox, 2, 3, 1, 2);
2541 //controlLayout->setColumnStretch(3, 1);
2542
2543 controlLayout->addWidget(exportLabel, 3, 0);
2544 controlLayout->addWidget(mFromPage, 3, 1);
2545 controlLayout->addWidget(mToPage, 3, 2);
2546 controlLayout->addWidget(mOverwrite, 3, 3);
2547 controlLayout->setColumnStretch(5, 1);
2548
2549 // shows the image if it could be loaded
2550 mViewport = new DkBaseViewPort(this);
2551 mViewport->setForceFastRendering(true);
2552 mViewport->setPanControl(QPointF(0.0f, 0.0f));
2553
2554 // Buttons
2555 mButtons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
2556 mButtons->button(QDialogButtonBox::Ok)->setText(tr("&Export"));
2557 mButtons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
2558 connect(mButtons, SIGNAL(accepted()), this, SLOT(accept()));
2559 connect(mButtons, SIGNAL(rejected()), this, SLOT(reject()));
2560
2561 QVBoxLayout* layout = new QVBoxLayout(this);
2562 layout->addWidget(mViewport);
2563 layout->addWidget(mProgress);
2564 layout->addWidget(mMsgLabel);
2565 layout->addWidget(mControlWidget);
2566 layout->addWidget(mButtons);
2567
2568 enableTIFFSave(false);
2569 }
2570
on_openButton_pressed()2571 void DkExportTiffDialog::on_openButton_pressed() {
2572
2573 // load system default open dialog
2574 QString fileName = QFileDialog::getOpenFileName(
2575 this,
2576 tr("Open TIFF"),
2577 mFilePath,
2578 DkSettingsManager::param().app().saveFilters.filter(QRegExp(".*tif.*")).join(";;"),
2579 nullptr,
2580 DkDialog::fileDialogOptions()
2581 );
2582
2583 setFile(fileName);
2584 }
2585
on_saveButton_pressed()2586 void DkExportTiffDialog::on_saveButton_pressed() {
2587 qDebug() << "save triggered...";
2588
2589 // load system default open dialog
2590 QString dirName = QFileDialog::getExistingDirectory(
2591 this,
2592 tr("Open an Image Directory"),
2593 mSaveDirPath,
2594 QFileDialog::ShowDirsOnly | DkDialog::fileDialogOptions()
2595 );
2596
2597 if (QDir(dirName).exists()) {
2598 mSaveDirPath = dirName;
2599 mFolderLabel->setText(mSaveDirPath);
2600 }
2601 }
2602
on_fileEdit_textChanged(const QString & filename)2603 void DkExportTiffDialog::on_fileEdit_textChanged(const QString& filename) {
2604
2605 qDebug() << "new file name: " << filename;
2606 }
2607
reject()2608 void DkExportTiffDialog::reject() {
2609
2610 // not sure if this is a nice way to do: but we change cancel behavior while processing
2611 if (mProcessing)
2612 mProcessing = false;
2613 else
2614 QDialog::reject();
2615
2616 }
2617
accept()2618 void DkExportTiffDialog::accept() {
2619
2620 mProgress->setMinimum(mFromPage->value()-1);
2621 mProgress->setMaximum(mToPage->value());
2622 mProgress->setValue(mProgress->minimum());
2623 mProgress->show();
2624 mMsgLabel->show();
2625
2626 enableAll(false);
2627
2628 QString suffix = mSuffixBox->currentText();
2629
2630 for (int idx = 0; idx < DkSettingsManager::param().app().fileFilters.size(); idx++) {
2631 if (suffix.contains("(" + DkSettingsManager::param().app().fileFilters.at(idx))) {
2632 suffix = DkSettingsManager::param().app().fileFilters.at(idx);
2633 suffix.replace("*","");
2634 break;
2635 }
2636 }
2637
2638 QFileInfo sFile(mSaveDirPath, mFileEdit->text() + "-" + suffix);
2639
2640 emit infoMessage("");
2641
2642 QFuture<int> future = QtConcurrent::run(this,
2643 &nmc::DkExportTiffDialog::exportImages,
2644 sFile.absoluteFilePath(),
2645 mFromPage->value(),
2646 mToPage->value(),
2647 mOverwrite->isChecked());
2648 mWatcher.setFuture(future);
2649 }
2650
processingFinished()2651 void DkExportTiffDialog::processingFinished() {
2652
2653 enableAll(true);
2654 mProgress->hide();
2655 mMsgLabel->hide();
2656
2657 if (mWatcher.future() == QDialog::Accepted)
2658 QDialog::accept();
2659 }
2660
exportImages(const QString & saveFilePath,int from,int to,bool overwrite)2661 int DkExportTiffDialog::exportImages(const QString& saveFilePath, int from, int to, bool overwrite) {
2662
2663 mProcessing = true;
2664
2665 QFileInfo saveInfo(saveFilePath);
2666
2667 // Do your job
2668 for (int idx = from; idx <= to; idx++) {
2669
2670 QFileInfo cInfo(saveInfo.absolutePath(), saveInfo.baseName() + QString::number(idx) + "." + saveInfo.suffix());
2671 qDebug() << "trying to save: " << cInfo.absoluteFilePath();
2672
2673 emit updateProgress(idx-1);
2674
2675 // user wants to overwrite files
2676 if (cInfo.exists() && overwrite) {
2677 QFile f(cInfo.absoluteFilePath());
2678 f.remove();
2679 }
2680 else if (cInfo.exists()) {
2681 emit infoMessage(tr("%1 exists, skipping...").arg(cInfo.fileName()));
2682 continue;
2683 }
2684
2685 if (!mLoader.loadPageAt(idx)) { // load next
2686 emit infoMessage(tr("Sorry, I could not load page: %1").arg(idx));
2687 continue;
2688 }
2689
2690 QString lSaveFilePath = mLoader.save(cInfo.absoluteFilePath(), mLoader.image(), 90); //TODO: ask user for compression?
2691 QFileInfo lSaveInfo = QFileInfo(lSaveFilePath);
2692
2693 if (!lSaveInfo.exists() || !lSaveInfo.isFile())
2694 emit infoMessage(tr("Sorry, I could not save: %1").arg(cInfo.fileName()));
2695
2696 emit updateImage(mLoader.image());
2697 emit updateProgress(idx);
2698
2699 // user canceled?
2700 if (!mProcessing)
2701 return QDialog::Rejected;
2702 }
2703
2704 mProcessing = false;
2705
2706 return QDialog::Accepted;
2707 }
2708
setFile(const QString & filePath)2709 void DkExportTiffDialog::setFile(const QString& filePath) {
2710
2711 if (!QFileInfo(filePath).exists())
2712 return;
2713
2714 QFileInfo fInfo(filePath);
2715 mFilePath = filePath;
2716 mSaveDirPath = fInfo.absolutePath();
2717 mFolderLabel->setText(mSaveDirPath);
2718 mTiffLabel->setText(filePath);
2719 mFileEdit->setText(fInfo.baseName());
2720
2721 mLoader.loadGeneral(filePath, true);
2722 mViewport->setImage(mLoader.image());
2723
2724 enableTIFFSave(mLoader.getNumPages() > 1);
2725
2726 mFromPage->setRange(1, mLoader.getNumPages());
2727 mToPage->setRange(1, mLoader.getNumPages());
2728
2729 mFromPage->setValue(1);
2730 mToPage->setValue(mLoader.getNumPages());
2731 }
2732
enableAll(bool enable)2733 void DkExportTiffDialog::enableAll(bool enable) {
2734
2735 enableTIFFSave(enable);
2736 mControlWidget->setEnabled(enable);
2737 }
2738
enableTIFFSave(bool enable)2739 void DkExportTiffDialog::enableTIFFSave(bool enable) {
2740
2741 mFileEdit->setEnabled(enable);
2742 mSuffixBox->setEnabled(enable);
2743 mFromPage->setEnabled(enable);
2744 mToPage->setEnabled(enable);
2745 mButtons->button(QDialogButtonBox::Ok)->setEnabled(enable);
2746 }
2747
2748 #ifdef WITH_OPENCV
2749
2750 // DkMosaicDialog --------------------------------------------------------------------
DkMosaicDialog(QWidget * parent,Qt::WindowFlags f)2751 DkMosaicDialog::DkMosaicDialog(QWidget* parent /* = 0 */, Qt::WindowFlags f /* = 0 */) : QDialog(parent, f) {
2752
2753 mProcessing = false;
2754 mPostProcessing = false;
2755 mUpdatePostProcessing = false;
2756
2757 setWindowTitle(tr("Create Mosaic Image"));
2758 createLayout();
2759 setAcceptDrops(true);
2760
2761 connect(this, SIGNAL(updateImage(const QImage&)), mPreview, SLOT(setImage(const QImage&)));
2762 connect(&mMosaicWatcher, SIGNAL(finished()), this, SLOT(mosaicFinished()));
2763 connect(&mPostProcessWatcher, SIGNAL(finished()), this, SLOT(postProcessFinished()));
2764 connect(&mPostProcessWatcher, SIGNAL(canceled()), this, SLOT(postProcessFinished()));
2765 connect(this, SIGNAL(infoMessage(const QString&)), mMsgLabel, SLOT(setText(const QString&)));
2766 connect(this, SIGNAL(updateProgress(int)), mProgress, SLOT(setValue(int)));
2767 QMetaObject::connectSlotsByName(this);
2768 }
2769
dropEvent(QDropEvent * event)2770 void DkMosaicDialog::dropEvent(QDropEvent *event) {
2771
2772 if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
2773 QUrl url = event->mimeData()->urls().at(0);
2774 url = url.toLocalFile();
2775
2776 setFile(url.toString());
2777 }
2778 }
2779
dragEnterEvent(QDragEnterEvent * event)2780 void DkMosaicDialog::dragEnterEvent(QDragEnterEvent *event) {
2781
2782 if (event->mimeData()->hasUrls()) {
2783 QUrl url = event->mimeData()->urls().at(0);
2784 url = url.toLocalFile();
2785 QFileInfo file = QFileInfo(url.toString());
2786
2787 if (file.exists() && DkUtils::isValid(file))
2788 event->acceptProposedAction();
2789 }
2790 }
2791
createLayout()2792 void DkMosaicDialog::createLayout() {
2793
2794 // progress bar
2795 mProgress = new QProgressBar(this);
2796 mProgress->hide();
2797
2798 mMsgLabel = new QLabel(this);
2799 mMsgLabel->setObjectName("DkWarningInfo");
2800 mMsgLabel->hide();
2801
2802 // post processing sliders
2803 mDarkenSlider = new QSlider(Qt::Horizontal, this);
2804 mDarkenSlider->setObjectName("darkenSlider");
2805 mDarkenSlider->setValue(40);
2806 //darkenSlider->hide();
2807
2808 mLightenSlider = new QSlider(Qt::Horizontal, this);
2809 mLightenSlider->setObjectName("lightenSlider");
2810 mLightenSlider->setValue(40);
2811 //lightenSlider->hide();
2812
2813 mSaturationSlider = new QSlider(Qt::Horizontal, this);
2814 mSaturationSlider->setObjectName("saturationSlider");
2815 mSaturationSlider->setValue(60);
2816 //saturationSlider->hide();
2817
2818 mSliderWidget = new QWidget(this);
2819 QGridLayout* sliderLayout = new QGridLayout(mSliderWidget);
2820 sliderLayout->addWidget(new QLabel(tr("Darken")), 0, 0);
2821 sliderLayout->addWidget(new QLabel(tr("Lighten")), 0, 1);
2822 sliderLayout->addWidget(new QLabel(tr("Saturation")), 0, 2);
2823
2824 sliderLayout->addWidget(mDarkenSlider, 1, 0);
2825 sliderLayout->addWidget(mLightenSlider, 1, 1);
2826 sliderLayout->addWidget(mSaturationSlider, 1, 2);
2827 mSliderWidget->hide();
2828
2829 // open handles
2830 QLabel* openLabel = new QLabel(tr("Mosaic Image:"), this);
2831 openLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2832
2833 QPushButton* openButton = new QPushButton(tr("&Browse"), this);
2834 openButton->setObjectName("openButton");
2835 openButton->setToolTip(tr("Choose which image to mosaic."));
2836
2837 mFileLabel = new QLabel(tr("No Image loaded"), this);
2838
2839 // save handles
2840 QLabel* saveLabel = new QLabel(tr("Mosaic Elements Folder:"), this);
2841 saveLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2842
2843 QPushButton* dbButton = new QPushButton(tr("&Browse"), this);
2844 dbButton->setObjectName("dbButton");
2845 dbButton->setToolTip(tr("Specify the root folder of images used for mosaic elements."));
2846
2847 mFolderLabel = new QLabel(tr("Specify an Image Database"), this);
2848
2849 // resolution handles
2850 QLabel* sizeLabel = new QLabel(tr("Resolution:"));
2851 sizeLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2852 mNewWidthBox = new QSpinBox();
2853 mNewWidthBox->setObjectName("newWidthBox");
2854 mNewWidthBox->setToolTip(tr("Pixel Width"));
2855 mNewWidthBox->setMinimum(100);
2856 mNewWidthBox->setMaximum(30000);
2857 mNewHeightBox = new QSpinBox();
2858 mNewHeightBox->setObjectName("newHeightBox");
2859 mNewHeightBox->setToolTip(tr("Pixel Height"));
2860 mNewHeightBox->setMinimum(100);
2861 mNewHeightBox->setMaximum(30000);
2862 mRealResLabel = new QLabel("");
2863 //realResLabel->setToolTip(tr("."));
2864
2865 // num patch handles
2866 QLabel* patchLabel = new QLabel(tr("Patches:"));
2867 patchLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2868 mNumPatchesH = new QSpinBox(this);
2869 mNumPatchesH->setObjectName("numPatchesH");
2870 mNumPatchesH->setToolTip(tr("Number of Horizontal Patches"));
2871 mNumPatchesH->setMinimum(1);
2872 mNumPatchesH->setMaximum(1000);
2873 mNumPatchesV = new QSpinBox(this);
2874 mNumPatchesV->setObjectName("numPatchesV");
2875 mNumPatchesV->setToolTip(tr("Number of Vertical Patches"));
2876 mNumPatchesV->setMinimum(1);
2877 mNumPatchesV->setMaximum(1000);
2878 mPatchResLabel = new QLabel("", this);
2879 mPatchResLabel->setObjectName("DkDecentInfo");
2880 mPatchResLabel->setToolTip(tr("If this label turns red, the computation might be slower."));
2881
2882 // file filters
2883 QLabel* filterLabel = new QLabel(tr("Filters:"), this);
2884 filterLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
2885
2886 mFilterEdit = new QLineEdit("", this);
2887 mFilterEdit->setObjectName("fileEdit");
2888 mFilterEdit->setToolTip(tr("You can split multiple ignore words with ;"));
2889
2890 QStringList filters = DkSettingsManager::param().app().openFilters;
2891 filters.pop_front(); // replace for better readability
2892 filters.push_front(tr("All Images"));
2893 mSuffixBox = new QComboBox(this);
2894 mSuffixBox->addItems(filters);
2895 //suffixBox->setCurrentIndex(DkImageLoader::saveFilters.indexOf(QRegExp(".*tif.*")));
2896
2897 mControlWidget = new QWidget(this);
2898 QGridLayout* controlLayout = new QGridLayout(mControlWidget);
2899 controlLayout->addWidget(openLabel, 0, 0);
2900 controlLayout->addWidget(openButton, 0, 1, 1, 2);
2901 controlLayout->addWidget(mFileLabel, 0, 3, 1, 2);
2902 //controlLayout->setColumnStretch(3, 1);
2903
2904 controlLayout->addWidget(saveLabel, 1, 0);
2905 controlLayout->addWidget(dbButton, 1, 1, 1, 2);
2906 controlLayout->addWidget(mFolderLabel, 1, 3, 1, 2);
2907 //controlLayout->setColumnStretch(3, 1);
2908
2909 controlLayout->addWidget(sizeLabel, 2, 0);
2910 controlLayout->addWidget(mNewWidthBox, 2, 1);
2911 controlLayout->addWidget(mNewHeightBox, 2, 2);
2912 controlLayout->addWidget(mRealResLabel, 2, 3);
2913
2914 controlLayout->addWidget(patchLabel, 4, 0);
2915 controlLayout->addWidget(mNumPatchesH, 4, 1);
2916 controlLayout->addWidget(mNumPatchesV, 4, 2);
2917 controlLayout->addWidget(mPatchResLabel, 4, 3);
2918
2919 controlLayout->addWidget(filterLabel, 5, 0);
2920 controlLayout->addWidget(mFilterEdit, 5, 1, 1, 2);
2921 controlLayout->addWidget(mSuffixBox, 5, 3, 1, 2);
2922 controlLayout->setColumnStretch(5, 1);
2923
2924 // shows the image if it could be loaded
2925 mViewport = new DkBaseViewPort(this);
2926 mViewport->setForceFastRendering(true);
2927 mViewport->setPanControl(QPointF(0.0f, 0.0f));
2928
2929 mPreview = new DkBaseViewPort(this);
2930 mPreview->setForceFastRendering(true);
2931 mPreview->setPanControl(QPointF(0.0f, 0.0f));
2932 mPreview->hide();
2933
2934 QWidget* viewports = new QWidget(this);
2935 QHBoxLayout* viewLayout = new QHBoxLayout(viewports);
2936 viewLayout->addWidget(mViewport);
2937 viewLayout->addWidget(mPreview);
2938
2939 // mButtons
2940 mButtons = new QDialogButtonBox(QDialogButtonBox::Apply | QDialogButtonBox::Save | QDialogButtonBox::Cancel, Qt::Horizontal, this);
2941 mButtons->button(QDialogButtonBox::Save)->setText(tr("&Save"));
2942 mButtons->button(QDialogButtonBox::Apply)->setText(tr("&Generate"));
2943 mButtons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
2944 //connect(mButtons, SIGNAL(accepted()), this, SLOT(accept()));
2945 connect(mButtons, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
2946 connect(mButtons, SIGNAL(rejected()), this, SLOT(reject()));
2947 mButtons->button(QDialogButtonBox::Save)->setEnabled(false);
2948
2949 QVBoxLayout* layout = new QVBoxLayout(this);
2950 layout->addWidget(viewports);
2951 layout->addWidget(mProgress);
2952 layout->addWidget(mSliderWidget);
2953 layout->addWidget(mMsgLabel);
2954 layout->addWidget(mControlWidget);
2955 layout->addWidget(mButtons);
2956
2957 enableMosaicSave(false);
2958 }
2959
on_openButton_pressed()2960 void DkMosaicDialog::on_openButton_pressed() {
2961
2962 // load system default open dialog
2963 QString fileName = QFileDialog::getOpenFileName(
2964 this,
2965 tr("Open TIFF"),
2966 mFilePath,
2967 DkSettingsManager::param().app().openFilters.join(";;"),
2968 nullptr,
2969 DkDialog::fileDialogOptions()
2970 );
2971
2972 setFile(fileName);
2973 }
2974
on_dbButton_pressed()2975 void DkMosaicDialog::on_dbButton_pressed() {
2976 qDebug() << "save triggered...";
2977
2978 // load system default open dialog
2979 QString dirName = QFileDialog::getExistingDirectory(
2980 this,
2981 tr("Open an Image Directory"),
2982 mSavePath,
2983 QFileDialog::ShowDirsOnly | DkDialog::fileDialogOptions()
2984 );
2985
2986 if (QFileInfo(dirName).exists()) {
2987 mSavePath = dirName;
2988 mFolderLabel->setText(mSavePath);
2989 }
2990 }
2991
on_fileEdit_textChanged(const QString & filename)2992 void DkMosaicDialog::on_fileEdit_textChanged(const QString& filename) {
2993
2994 qDebug() << "new file name: " << filename;
2995 }
2996
on_newWidthBox_valueChanged(int)2997 void DkMosaicDialog::on_newWidthBox_valueChanged(int) {
2998
2999 if (!mLoader.hasImage())
3000 return;
3001
3002 mNewHeightBox->blockSignals(true);
3003 mNewHeightBox->setValue(qRound((float)mNewWidthBox->value()/mLoader.image().width()*mLoader.image().height()));
3004 mNewHeightBox->blockSignals(false);
3005 mRealResLabel->setText(tr("%1 x %2 cm @150 dpi").arg(mNewWidthBox->value()/150.0*2.54, 0, 'f', 1).arg(mNewHeightBox->value()/150.0*2.54, 0, 'f', 1));
3006 updatePatchRes();
3007 }
3008
on_newHeightBox_valueChanged(int)3009 void DkMosaicDialog::on_newHeightBox_valueChanged(int) {
3010
3011 if (!mLoader.hasImage())
3012 return;
3013
3014 mNewWidthBox->blockSignals(true);
3015 mNewWidthBox->setValue(qRound((float)mNewHeightBox->value()/mLoader.image().height()*mLoader.image().width()));
3016 mNewWidthBox->blockSignals(false);
3017 mRealResLabel->setText(tr("%1 x %2 cm @150 dpi").arg(mNewWidthBox->value()/150.0*2.54, 0, 'f', 1).arg(mNewHeightBox->value()/150.0*2.54, 0, 'f', 1));
3018 updatePatchRes();
3019 }
3020
on_numPatchesH_valueChanged(int)3021 void DkMosaicDialog::on_numPatchesH_valueChanged(int) {
3022
3023 if (!mLoader.hasImage())
3024 return;
3025
3026 mNumPatchesV->blockSignals(true);
3027 mNumPatchesV->setValue(qFloor((float)mLoader.image().height()/((float)mLoader.image().width()/mNumPatchesH->value())));
3028 mNumPatchesV->blockSignals(false);
3029 updatePatchRes();
3030 }
3031
on_numPatchesV_valueChanged(int)3032 void DkMosaicDialog::on_numPatchesV_valueChanged(int) {
3033
3034 if (!mLoader.hasImage())
3035 return;
3036
3037 mNumPatchesH->blockSignals(true);
3038 mNumPatchesH->setValue(qFloor((float)mLoader.image().width()/((float)mLoader.image().height()/mNumPatchesV->value())));
3039 mNumPatchesH->blockSignals(false);
3040 updatePatchRes();
3041 }
3042
on_darkenSlider_valueChanged(int)3043 void DkMosaicDialog::on_darkenSlider_valueChanged(int) {
3044
3045 updatePostProcess();
3046 }
3047
on_lightenSlider_valueChanged(int)3048 void DkMosaicDialog::on_lightenSlider_valueChanged(int) {
3049
3050 updatePostProcess();
3051 }
3052
on_saturationSlider_valueChanged(int)3053 void DkMosaicDialog::on_saturationSlider_valueChanged(int) {
3054
3055 updatePostProcess();
3056 }
3057
updatePatchRes()3058 void DkMosaicDialog::updatePatchRes() {
3059
3060 int patchResD = qFloor((float)mNewWidthBox->value()/mNumPatchesH->value());
3061
3062 mPatchResLabel->setText(tr("Patch Resolution: %1 px").arg(patchResD));
3063 mPatchResLabel->show();
3064
3065 // show the user if we can work with the thumbnails or not
3066 if (patchResD > 97)
3067 mPatchResLabel->setProperty("warning", true);
3068 else
3069 mPatchResLabel->setProperty("warning", false);
3070
3071 mPatchResLabel->style()->unpolish(mPatchResLabel);
3072 mPatchResLabel->style()->polish(mPatchResLabel);
3073 mPatchResLabel->update();
3074 }
3075
getImage()3076 QImage DkMosaicDialog::getImage() {
3077
3078 if (mMosaic.isNull() && !mMosaicMat.empty())
3079 return DkImage::mat2QImage(mMosaicMat);
3080
3081 return mMosaic;
3082 }
3083
reject()3084 void DkMosaicDialog::reject() {
3085
3086 // not sure if this is a nice way to do: but we change cancel behavior while processing
3087 if (mProcessing)
3088 mProcessing = false;
3089 else if (!mMosaic.isNull() && !mButtons->button(QDialogButtonBox::Apply)->isEnabled()) {
3090 mButtons->button(QDialogButtonBox::Apply)->setEnabled(true);
3091 enableAll(true);
3092 mViewport->show();
3093 mSliderWidget->hide();
3094 }
3095 else
3096 QDialog::reject();
3097
3098 }
3099
buttonClicked(QAbstractButton * button)3100 void DkMosaicDialog::buttonClicked(QAbstractButton* button) {
3101
3102 if (button == mButtons->button(QDialogButtonBox::Save)) {
3103
3104 // render the full image
3105 if (!mMosaic.isNull()) {
3106 mSliderWidget->hide();
3107 mProgress->setValue(mProgress->minimum());
3108 mProgress->show();
3109 enableAll(false);
3110 button->setEnabled(false);
3111
3112 QFuture<bool> future = QtConcurrent::run(this,
3113 &nmc::DkMosaicDialog::postProcessMosaic,
3114 mDarkenSlider->value()/100.0f,
3115 mLightenSlider->value()/100.0f,
3116 mSaturationSlider->value()/100.0f,
3117 false);
3118 mPostProcessWatcher.setFuture(future);
3119 }
3120 }
3121 else if (button == mButtons->button(QDialogButtonBox::Apply))
3122 compute();
3123 }
3124
compute()3125 void DkMosaicDialog::compute() {
3126
3127 if (mPostProcessing)
3128 return;
3129
3130 mProgress->setValue(mProgress->minimum());
3131 mProgress->show();
3132 mMsgLabel->setText("");
3133 mMsgLabel->show();
3134 mMosaicMatSmall.release();
3135 mMosaicMat.release();
3136 mOrigImg.release();
3137 mMosaic = QImage();
3138 mSliderWidget->hide();
3139 mViewport->show();
3140 mPreview->setForceFastRendering(true);
3141 mPreview->show();
3142
3143 enableAll(false);
3144
3145 QString suffixTmp = mSuffixBox->currentText();
3146 QString suffix;
3147
3148 for (int idx = 0; idx < DkSettingsManager::param().app().fileFilters.size(); idx++) {
3149 if (suffixTmp.contains("(" + DkSettingsManager::param().app().fileFilters.at(idx))) {
3150 suffix = DkSettingsManager::param().app().fileFilters.at(idx);
3151 break;
3152 }
3153 }
3154
3155 QString filter = mFilterEdit->text();
3156 mFilesUsed.clear();
3157
3158 mProcessing = true;
3159 QFuture<int> future = QtConcurrent::run(this,
3160 &nmc::DkMosaicDialog::computeMosaic,
3161 filter,
3162 suffix,
3163 mNewWidthBox->value(),
3164 mNumPatchesH->value());
3165 mMosaicWatcher.setFuture(future);
3166
3167 //// debug
3168 //computeMosaic(
3169 // cFile,
3170 // filter,
3171 // suffix,
3172 // fromPage->value(),
3173 // toPage->value(),
3174 // overwrite->isChecked());
3175
3176 }
3177
mosaicFinished()3178 void DkMosaicDialog::mosaicFinished() {
3179
3180 mProgress->hide();
3181 //msgLabel->hide();
3182
3183 if (!mMosaicMat.empty()) {
3184 mSliderWidget->show();
3185 mMsgLabel->hide();
3186 mViewport->hide();
3187 mPreview->setForceFastRendering(false);
3188
3189 updatePostProcess(); // add values
3190 mButtons->button(QDialogButtonBox::Save)->setEnabled(true);
3191 }
3192 else
3193 enableAll(true);
3194 }
3195
computeMosaic(const QString & filter,const QString & suffix,int newWidth,int numPatchesH)3196 int DkMosaicDialog::computeMosaic(const QString& filter, const QString& suffix, int newWidth, int numPatchesH) {
3197
3198 DkTimer dt;
3199
3200 // compute new image size
3201 cv::Mat mImg = DkImage::qImage2Mat(mLoader.image());
3202
3203 QSize numPatches = QSize(numPatchesH, 0);
3204
3205 // compute new image size
3206 //float aratio = (float)mImg.rows/mImg.cols;
3207 int patchResO = qFloor((float)mImg.cols/numPatches.width());
3208 numPatches.setHeight(qFloor((float)mImg.rows/patchResO));
3209
3210 int patchResD = qFloor(newWidth/numPatches.width());
3211
3212 float shR = (mImg.rows-patchResO*numPatches.height())/2.0f;
3213 float shC = (mImg.cols-patchResO*numPatches.width())/2.0f;
3214
3215 mImg = mImg.rowRange(qFloor(shR), mImg.rows-qCeil(shR)).colRange(qFloor(shC), mImg.cols-qCeil(shC));
3216 cv::Mat mImgLab;
3217 cv::cvtColor(mImg, mImgLab, CV_RGB2Lab);
3218 std::vector<cv::Mat> channels;
3219 cv::split(mImgLab, channels);
3220 cv::Mat imgL = channels[0];
3221
3222 // keeps track of the weights
3223 cv::Mat cc(numPatches.height(), numPatches.width(), CV_32FC1);
3224 cc.setTo(0);
3225 cv::Mat ccD(numPatches.height(), numPatches.width(), CV_8UC1); // tells us if we have already computed the real patch
3226
3227 mFilesUsed.resize(numPatches.height()*numPatches.width());
3228
3229 // destination image
3230 cv::Mat dImg(patchResD*numPatches.height(), patchResD*numPatches.width(), CV_8UC1);
3231 dImg = 255;
3232
3233 // patch image (preview)
3234 cv::Mat pImg(patchResO*numPatches.height(), patchResO*numPatches.width(), CV_8UC1);
3235 pImg = 255;
3236
3237 qDebug() << "mosaic data --------------------------------";
3238 qDebug() << "patchRes: " << patchResD;
3239 qDebug() << "new resolution: " << dImg.cols << " x " << dImg.rows;
3240 qDebug() << "num patches: " << numPatches.width() << " x " << numPatches.height();
3241 qDebug() << "mosaic data --------------------------------";
3242
3243 // progress bar
3244 int pIdx = 0;
3245 int maxP = numPatches.width()*numPatches.height();
3246
3247 int iDidNothing = 0;
3248 bool force = false;
3249 bool useTwice = false;
3250
3251 while (iDidNothing < 10000) {
3252
3253 if (!mProcessing)
3254 return QDialog::Rejected;
3255
3256 if (iDidNothing > 100) {
3257 force = true;
3258
3259 if (!useTwice)
3260 emit infoMessage(tr("Filling empty areas..."));
3261 }
3262
3263 if (iDidNothing > 400 && !useTwice) {
3264 emit infoMessage(tr("I need to use some images twice - maybe the database is too small?"));
3265 iDidNothing = 0;
3266 useTwice = true;
3267 }
3268 else if (iDidNothing > 400) {
3269 emit infoMessage(tr("Sorry, it seems that i cannot create your mosaic with this database."));
3270 return QDialog::Rejected;
3271 }
3272
3273 QString imgPath = getRandomImagePath(mSavePath, filter, suffix);
3274
3275 if (!useTwice && mFilesUsed.contains(QFileInfo(imgPath))) {
3276 iDidNothing++;
3277 continue;
3278 }
3279
3280 try {
3281
3282 DkThumbNail thumb = DkThumbNail(imgPath);
3283 thumb.compute();
3284
3285 cv::Mat ccTmp(cc.size(), cc.depth());
3286
3287 if (!force)
3288 ccTmp.setTo(0);
3289 else
3290 ccTmp = cc.clone();
3291
3292 cv::Mat thumbPatch = createPatch(thumb, patchResO);
3293
3294 if (thumbPatch.rows != patchResO || thumbPatch.cols != patchResO) {
3295 iDidNothing++;
3296 continue;
3297 }
3298
3299 matchPatch(imgL, thumbPatch, patchResO, ccTmp);
3300
3301 if (force) {
3302 cv::Mat mask = (cc == 0);
3303 mask.convertTo(mask, CV_32FC1, 1.0f/255.0f);
3304 ccTmp = ccTmp.mul(mask);
3305 }
3306
3307 double maxVal = 0;
3308 cv::Point maxIdx;
3309 cv::minMaxLoc(ccTmp, 0, &maxVal, 0, &maxIdx);
3310 float* ccPtr = cc.ptr<float>(maxIdx.y);
3311
3312 if (maxVal > ccPtr[maxIdx.x]) {
3313
3314 cv::Mat pPatch = pImg.rowRange(maxIdx.y*patchResO, maxIdx.y*patchResO+patchResO)
3315 .colRange(maxIdx.x*patchResO, maxIdx.x*patchResO+patchResO);
3316 thumbPatch.copyTo(pPatch);
3317
3318 // visualize
3319 if (pIdx % 10 == 0) {
3320
3321 channels[0] = pImg;
3322
3323 //debug
3324 cv::Mat imgT3;
3325 cv::merge(channels, imgT3);
3326 cv::cvtColor(imgT3, imgT3, CV_Lab2BGR);
3327 emit updateImage(DkImage::mat2QImage(imgT3));
3328 }
3329
3330 if (ccPtr[maxIdx.x] == 0) {
3331 pIdx++;
3332 emit updateProgress(qRound((float)pIdx/maxP*100));
3333 }
3334
3335 // compute it now if we already have the full image loaded
3336 if (thumb.getImage().width() > patchResD && thumb.getImage().height() > patchResD) {
3337 thumbPatch = createPatch(thumb, patchResD);
3338
3339 cv::Mat dPatch = dImg.rowRange(maxIdx.y*patchResD, maxIdx.y*patchResD+patchResD)
3340 .colRange(maxIdx.x*patchResD, maxIdx.x*patchResD+patchResD);
3341 thumbPatch.copyTo(dPatch);
3342 ccD.ptr<unsigned char>(maxIdx.y)[maxIdx.x] = 1;
3343 }
3344 else
3345 ccD.ptr<unsigned char>(maxIdx.y)[maxIdx.x] = 0;
3346
3347 // update cc
3348 ccPtr[maxIdx.x] = (float)maxVal;
3349
3350 mFilesUsed[maxIdx.y*numPatchesH+maxIdx.x] = thumb.getFilePath(); // replaces additionally the old file
3351 iDidNothing = 0;
3352 }
3353 else
3354 iDidNothing++;
3355 }
3356 // catch cv exceptions e.g. out of memory
3357 catch(...) {
3358 iDidNothing++;
3359 }
3360
3361 // are we done yet?
3362 double minVal = 0;
3363 cv::minMaxLoc(cc, &minVal);
3364
3365 if (minVal > 0)
3366 break;
3367
3368 }
3369
3370 pIdx = 0;
3371
3372 // compute real resolution
3373 for (int rIdx = 0; rIdx < ccD.rows; rIdx++) {
3374
3375 const unsigned char* ccDPtr = ccD.ptr<unsigned char>(rIdx);
3376
3377 for (int cIdx = 0; cIdx < ccD.cols; cIdx++) {
3378
3379 // is the patch already computed?
3380 if (ccDPtr[cIdx])
3381 continue;
3382
3383 QFileInfo cFile = mFilesUsed.at(rIdx*ccD.cols+cIdx);
3384
3385 if (!cFile.exists()) {
3386 emit infoMessage(tr("Something is seriously wrong, I could not load: %1").arg(cFile.absoluteFilePath()));
3387 continue;
3388 }
3389
3390 cv::Mat thumbPatch = createPatch(DkThumbNail(cFile.absoluteFilePath()), patchResD);
3391
3392 cv::Mat dPatch = dImg.rowRange(rIdx*patchResD, rIdx*patchResD+patchResD)
3393 .colRange(cIdx*patchResD, cIdx*patchResD+patchResD);
3394 thumbPatch.copyTo(dPatch);
3395 emit updateProgress(qRound((float)pIdx/maxP*100));
3396 pIdx++;
3397 }
3398 }
3399
3400 qDebug() << "I fully rendered: " << ccD.rows*ccD.cols-cv::sum(ccD)[0] << " images";
3401
3402 // create final images
3403 mOrigImg = mImgLab;
3404 mMosaicMat = dImg;
3405 mMosaicMatSmall = pImg;
3406
3407 mProcessing = false;
3408
3409 qDebug() << "mosaic computed in: " << dt;
3410
3411 return QDialog::Accepted;
3412 }
3413
matchPatch(const cv::Mat & img,const cv::Mat & thumb,int patchRes,cv::Mat & cc)3414 void DkMosaicDialog::matchPatch(const cv::Mat& img, const cv::Mat& thumb, int patchRes, cv::Mat& cc) {
3415
3416 for (int rIdx = 0; rIdx < cc.rows; rIdx++) {
3417
3418 float* ccPtr = cc.ptr<float>(rIdx);
3419 const cv::Mat imgStrip = img.rowRange(rIdx*patchRes, rIdx*patchRes+patchRes);
3420
3421 for (int cIdx = 0; cIdx < cc.cols; cIdx++) {
3422
3423 // already computed?
3424 if (ccPtr[cIdx] != 0)
3425 continue;
3426
3427 const cv::Mat cPatch = imgStrip.colRange(cIdx*patchRes, cIdx*patchRes+patchRes);
3428
3429 cv::Mat absDiff;
3430 cv::absdiff(cPatch, thumb, absDiff);
3431 ccPtr[cIdx] = 1.0f-((float)cv::sum(absDiff)[0]/(patchRes*patchRes*255));
3432 }
3433 }
3434 }
3435
createPatch(const DkThumbNail & thumb,int patchRes)3436 cv::Mat DkMosaicDialog::createPatch(const DkThumbNail& thumb, int patchRes) {
3437
3438 QImage img;
3439
3440 // load full image if we have not enough resolution
3441 if (thumb.getImage().isNull() ||
3442 qMin(thumb.getImage().width(), thumb.getImage().height()) < patchRes) {
3443 DkBasicLoader loader;
3444 loader.loadGeneral(thumb.getFilePath(), true, true);
3445 img = loader.image();
3446 }
3447 else
3448 img = thumb.getImage();
3449
3450 cv::Mat cvThumb = DkImage::qImage2Mat(img);
3451 cv::cvtColor(cvThumb, cvThumb, CV_RGB2Lab);
3452 std::vector<cv::Mat> channels;
3453 cv::split(cvThumb, channels);
3454 cvThumb = channels[0];
3455 channels.clear();
3456
3457 // make square
3458 if (cvThumb.rows != cvThumb.cols) {
3459
3460 if (cvThumb.rows > cvThumb.cols) {
3461 float sh = (cvThumb.rows - cvThumb.cols)/2.0f;
3462 cvThumb = cvThumb.rowRange(qFloor(sh), cvThumb.rows-qCeil(sh));
3463 }
3464 else {
3465 float sh = (cvThumb.cols - cvThumb.rows)/2.0f;
3466 cvThumb = cvThumb.colRange(qFloor(sh), cvThumb.cols-qCeil(sh));
3467 }
3468 }
3469
3470 if (cvThumb.rows < patchRes || cvThumb.cols < patchRes)
3471 qDebug() << "enlarging thumbs!!";
3472
3473 cv::resize(cvThumb, cvThumb, cv::Size(patchRes, patchRes), 0.0, 0.0, CV_INTER_AREA);
3474
3475 return cvThumb;
3476 }
3477
getRandomImagePath(const QString & cPath,const QString & ignore,const QString & suffix)3478 QString DkMosaicDialog::getRandomImagePath(const QString& cPath, const QString& ignore, const QString& suffix) {
3479
3480 // TODO: remove hierarchy
3481 QStringList fileFilters = (suffix.isEmpty()) ? DkSettingsManager::param().app().fileFilters : QStringList(suffix);
3482
3483 // get all dirs
3484 QFileInfoList entries = QDir(cPath).entryInfoList(QStringList(), QDir::AllDirs | QDir::NoDotAndDotDot);
3485 //qDebug() << entries;
3486 // get all files
3487 entries += QDir(cPath).entryInfoList(fileFilters);
3488 //qDebug() << "current entries: " << e;
3489
3490 if (!ignore.isEmpty()) {
3491
3492 QStringList ignoreList = ignore.split(";");
3493 QFileInfoList entriesTmp = entries;
3494 entries.clear();
3495
3496 for (int idx = 0; idx < entriesTmp.size(); idx++) {
3497
3498 bool lIgnore = false;
3499 QString p = entriesTmp.at(idx).absoluteFilePath();
3500
3501 for (int iIdx = 0; iIdx < ignoreList.size(); iIdx++) {
3502 if (p.contains(ignoreList.at(iIdx))) {
3503 lIgnore = true;
3504 break;
3505 }
3506 }
3507
3508 if (!lIgnore)
3509 entries.append(entriesTmp.at(idx));
3510 }
3511 }
3512
3513
3514 if (entries.isEmpty())
3515 return QString();
3516
3517 int rIdx = qRound((float)qrand()/RAND_MAX*(entries.size()-1));
3518
3519 //qDebug() << "rand index: " << rIdx;
3520
3521 QFileInfo rPath = entries.at(rIdx);
3522 //qDebug() << rPath.absoluteFilePath();
3523
3524 if (rPath.isDir())
3525 return getRandomImagePath(rPath.absoluteFilePath(), ignore, suffix);
3526 else
3527 return rPath.absoluteFilePath();
3528 }
3529
updatePostProcess()3530 void DkMosaicDialog::updatePostProcess() {
3531
3532 if (mMosaicMat.empty() || mProcessing)
3533 return;
3534
3535 if (mPostProcessing) {
3536 mUpdatePostProcessing = true;
3537 return;
3538 }
3539
3540 mButtons->button(QDialogButtonBox::Apply)->setEnabled(false);
3541 mButtons->button(QDialogButtonBox::Save)->setEnabled(false);
3542
3543 QFuture<bool> future = QtConcurrent::run(this,
3544 &nmc::DkMosaicDialog::postProcessMosaic,
3545 mDarkenSlider->value()/100.0f,
3546 mLightenSlider->value()/100.0f,
3547 mSaturationSlider->value()/100.0f,
3548 true);
3549 mPostProcessWatcher.setFuture(future);
3550
3551 mUpdatePostProcessing = false;
3552 //postProcessMosaic(darkenSlider->value()/100.0f, lightenSlider->value()/100.0f, saturationSlider->value()/100.0f);
3553 }
3554
postProcessFinished()3555 void DkMosaicDialog::postProcessFinished() {
3556
3557 if (mPostProcessWatcher.result()) {
3558 QDialog::accept();
3559 }
3560 else if (mUpdatePostProcessing)
3561 updatePostProcess();
3562 else {
3563 mButtons->button(QDialogButtonBox::Save)->setEnabled(true);
3564 }
3565 }
3566
postProcessMosaic(float multiply,float screen,float saturation,bool computePreview)3567 bool DkMosaicDialog::postProcessMosaic(float multiply /* = 0.3 */, float screen /* = 0.5 */, float saturation, bool computePreview) {
3568
3569 mPostProcessing = true;
3570
3571 qDebug() << "darken: " << multiply << " lighten: " << screen;
3572
3573 cv::Mat origR;
3574 cv::Mat mosaicR;
3575
3576 try {
3577 if (computePreview) {
3578 origR = mOrigImg.clone();
3579 mosaicR = mMosaicMatSmall.clone();
3580 }
3581 else {
3582 cv::resize(mOrigImg, origR, mMosaicMat.size(), 0, 0, CV_INTER_LANCZOS4);
3583 mosaicR = mMosaicMat;
3584 mOrigImg.release();
3585 }
3586
3587 // multiply the two images
3588 for (int rIdx = 0; rIdx < origR.rows; rIdx++) {
3589
3590 const unsigned char* mosaicPtr = mosaicR.ptr<unsigned char>(rIdx);
3591 unsigned char* origPtr = origR.ptr<unsigned char>(rIdx);
3592
3593 if (!computePreview)
3594 emit updateProgress(qRound((float)rIdx/origR.rows*100));
3595
3596 for (int cIdx = 0; cIdx < origR.cols; cIdx++) {
3597
3598 // mix the luminance channel
3599 float mosaic = mosaicPtr[cIdx]/255.0f;
3600 float luminance = (*origPtr)/255.0f;
3601
3602
3603 float lighten = (1.0f-luminance)*screen + (1.0f-screen);
3604 lighten *= 1.0f-mosaic; // multiply inverse
3605 lighten = 1.0f-lighten;
3606
3607 float darken = luminance*multiply + (1.0f-multiply);
3608 darken *= lighten; // mix with the mosaic pixel
3609
3610 // now stretch to the dynamic range and save it
3611 *origPtr = (unsigned char)qRound(darken*255.0f);
3612
3613 // now adopt the saturation
3614 origPtr++;
3615 *origPtr = (unsigned char)qRound((*origPtr-128) * saturation)+128;
3616 origPtr++;
3617 *origPtr = (unsigned char)qRound((*origPtr-128) * saturation)+128;
3618 origPtr++;
3619 }
3620 }
3621
3622 //if (!computePreview)
3623 // mosaicMat.release();
3624 cv::cvtColor(origR, origR, CV_Lab2BGR);
3625 qDebug() << "color converted";
3626
3627 mMosaic = DkImage::mat2QImage(origR);
3628 qDebug() << "mosaicing computed...";
3629
3630 }
3631 catch(...) {
3632 origR.release();
3633
3634 QMessageBox::critical(DkUtils::getMainWindow(), tr("Error"), tr("Sorry, I could not mix the image..."));
3635 qDebug() << "exception caught...";
3636 mMosaic = DkImage::mat2QImage(mMosaicMat);
3637 }
3638
3639 if (computePreview)
3640 mPreview->setImage(mMosaic);
3641
3642 mPostProcessing = false;
3643
3644 return !computePreview;
3645
3646 }
3647
setFile(const QString & filePath)3648 void DkMosaicDialog::setFile(const QString& filePath) {
3649
3650 QFileInfo fInfo(filePath);
3651 if (!fInfo.exists())
3652 return;
3653
3654 mFilePath = filePath;
3655 mSavePath = fInfo.absolutePath();
3656 mFolderLabel->setText(mSavePath);
3657 mFileLabel->setText(filePath);
3658
3659 mLoader.loadGeneral(filePath, true);
3660 mViewport->setImage(mLoader.image());
3661
3662 enableMosaicSave(mLoader.hasImage());
3663
3664 //newWidthBox->blockSignals(true);
3665 //newHeightBox->blockSignals(true);
3666 mNewWidthBox->setValue(mLoader.image().width());
3667 mNumPatchesH->setValue(qFloor((float)mLoader.image().width()/90)); // 130 is a pretty good patch resolution
3668 mNumPatchesH->setMaximum(qMin(1000, qFloor(mLoader.image().width()*0.5f)));
3669 mNumPatchesV->setMaximum(qMin(1000, qFloor(mLoader.image().height()*0.5f)));
3670 //newHeightBox->setValue(loader.image().height());
3671 //newWidthBox->blockSignals(false);
3672 //newHeightBox->blockSignals(false);
3673
3674 //fromPage->setRange(1, loader.getNumPages());
3675 //toPage->setRange(1, loader.getNumPages());
3676
3677 //fromPage->setValue(1);
3678 //toPage->setValue(loader.getNumPages());
3679 }
3680
enableAll(bool enable)3681 void DkMosaicDialog::enableAll(bool enable) {
3682
3683 enableMosaicSave(enable);
3684 mControlWidget->setEnabled(enable);
3685 }
3686
enableMosaicSave(bool enable)3687 void DkMosaicDialog::enableMosaicSave(bool enable) {
3688
3689 mFilterEdit->setEnabled(enable);
3690 mSuffixBox->setEnabled(enable);
3691 mNewWidthBox->setEnabled(enable);
3692 mNewHeightBox->setEnabled(enable);
3693 mNumPatchesH->setEnabled(enable);
3694 mNumPatchesV->setEnabled(enable);
3695 mButtons->button(QDialogButtonBox::Apply)->setEnabled(enable);
3696
3697 if (!enable)
3698 mButtons->button(QDialogButtonBox::Save)->setEnabled(enable);
3699 }
3700 #endif
3701 // DkForceThumbDialog --------------------------------------------------------------------
DkForceThumbDialog(QWidget * parent,Qt::WindowFlags f)3702 DkForceThumbDialog::DkForceThumbDialog(QWidget* parent /* = 0 */, Qt::WindowFlags f /* = 0 */) : QDialog(parent, f) {
3703
3704 createLayout();
3705 }
3706
createLayout()3707 void DkForceThumbDialog::createLayout() {
3708
3709 QVBoxLayout* layout = new QVBoxLayout(this);
3710
3711 infoLabel = new QLabel();
3712 infoLabel->setAlignment(Qt::AlignHCenter);
3713
3714 cbForceSave = new QCheckBox(tr("Overwrite Existing Thumbnails"));
3715 cbForceSave->setToolTip("If checked, existing thumbnails will be replaced");
3716
3717 // mButtons
3718 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
3719 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
3720 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
3721 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
3722 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
3723
3724 layout->addWidget(infoLabel);
3725 layout->addWidget(cbForceSave);
3726 layout->addWidget(buttons);
3727
3728 }
3729
forceSave() const3730 bool DkForceThumbDialog::forceSave() const {
3731
3732 return cbForceSave->isChecked();
3733 }
3734
setDir(const QDir & fileInfo)3735 void DkForceThumbDialog::setDir(const QDir& fileInfo) {
3736
3737 infoLabel->setText(tr("Compute thumbnails for all images in:\n %1\n").arg(fileInfo.absolutePath()));
3738 }
3739
3740 // Welcome dialog --------------------------------------------------------------------
DkWelcomeDialog(QWidget * parent,Qt::WindowFlags f)3741 DkWelcomeDialog::DkWelcomeDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) {
3742
3743 setWindowTitle(tr("Welcome"));
3744 createLayout();
3745 mLanguageChanged = false;
3746 }
3747
createLayout()3748 void DkWelcomeDialog::createLayout() {
3749
3750 QGridLayout* layout = new QGridLayout(this);
3751
3752 QLabel* welcomeLabel = new QLabel(tr("Welcome to nomacs, please choose your preferred language below."), this);
3753
3754 mLanguageCombo = new QComboBox(this);
3755 DkUtils::addLanguages(mLanguageCombo, mLanguages);
3756
3757 mRegisterFilesCheckBox = new QCheckBox(tr("&Register File Associations"), this);
3758 mRegisterFilesCheckBox->setChecked(!DkSettingsManager::param().isPortable());
3759
3760 mSetAsDefaultCheckBox = new QCheckBox(tr("Set As &Default Viewer"), this);
3761 mSetAsDefaultCheckBox->setChecked(!DkSettingsManager::param().isPortable());
3762
3763 // mButtons
3764 QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
3765 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
3766 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
3767 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
3768 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
3769
3770 layout->addItem(new QSpacerItem(10, 10), 0, 0, -1, -1);
3771 layout->addWidget(welcomeLabel, 1, 0, 1, 3);
3772 layout->addItem(new QSpacerItem(10, 10), 2, 0, -1, -1);
3773 layout->addWidget(mLanguageCombo, 3, 1);
3774
3775 #ifdef Q_OS_WIN
3776 layout->addWidget(mRegisterFilesCheckBox, 4, 1);
3777 layout->addWidget(mSetAsDefaultCheckBox, 5, 1);
3778 #else
3779 mRegisterFilesCheckBox->setChecked(false);
3780 mRegisterFilesCheckBox->hide();
3781 mSetAsDefaultCheckBox->setChecked(false);
3782 mSetAsDefaultCheckBox->hide();
3783 #endif
3784
3785 layout->addWidget(buttons, 6, 0, 1, 3);
3786 }
3787
accept()3788 void DkWelcomeDialog::accept() {
3789
3790 DkFileFilterHandling fh;
3791
3792 if (mRegisterFilesCheckBox->isChecked())
3793 DkFileFilterHandling::registerFileAssociations();
3794 fh.registerNomacs(mSetAsDefaultCheckBox->isChecked()); // register nomacs again - to be save
3795
3796 // change language
3797 if (mLanguageCombo->currentIndex() != mLanguages.indexOf(DkSettingsManager::param().global().language) && mLanguageCombo->currentIndex() >= 0) {
3798 DkSettingsManager::param().global().language = mLanguages.at(mLanguageCombo->currentIndex());
3799 mLanguageChanged = true;
3800 }
3801
3802 QDialog::accept();
3803 }
3804
isLanguageChanged()3805 bool DkWelcomeDialog::isLanguageChanged() {
3806
3807 return mLanguageChanged;
3808 }
3809
3810
3811 // archive extraction dialog --------------------------------------------------------------------
3812 #ifdef WITH_QUAZIP
DkArchiveExtractionDialog(QWidget * parent,Qt::WindowFlags flags)3813 DkArchiveExtractionDialog::DkArchiveExtractionDialog(QWidget* parent, Qt::WindowFlags flags) : QDialog(parent, flags) {
3814
3815 mFileList = QStringList();
3816 setWindowTitle(tr("Extract images from an archive"));
3817 createLayout();
3818 setMinimumSize(340, 400);
3819 setAcceptDrops(true);
3820 }
3821
createLayout()3822 void DkArchiveExtractionDialog::createLayout() {
3823
3824 // archive file path
3825 QLabel* archiveLabel = new QLabel(tr("Archive (%1)").arg(DkSettingsManager::param().app().containerRawFilters.replace(" *", ", *")), this);
3826 mArchivePathEdit = new QLineEdit(this);
3827 mArchivePathEdit->setObjectName("DkWarningEdit");
3828 mArchivePathEdit->setValidator(&mFileValidator);
3829 connect(mArchivePathEdit, SIGNAL(textChanged(const QString&)), this, SLOT(textChanged(const QString&)));
3830 connect(mArchivePathEdit, SIGNAL(editingFinished()), this, SLOT(loadArchive()));
3831
3832 QPushButton* openArchiveButton = new QPushButton(tr("&Browse"));
3833 connect(openArchiveButton, SIGNAL(pressed()), this, SLOT(openArchive()));
3834
3835 // dir file path
3836 QLabel* dirLabel = new QLabel(tr("Extract to"));
3837 mDirPathEdit = new QLineEdit();
3838 mDirPathEdit->setValidator(&mFileValidator);
3839 connect(mDirPathEdit, SIGNAL(textChanged(const QString&)), this, SLOT(dirTextChanged(const QString&)));
3840
3841 QPushButton* openDirButton = new QPushButton(tr("&Browse"));
3842 connect(openDirButton, SIGNAL(pressed()), this, SLOT(openDir()));
3843
3844 mFeedbackLabel = new QLabel("", this);
3845 mFeedbackLabel->setObjectName("DkDecentInfo");
3846
3847 mFileListDisplay = new QListWidget(this);
3848
3849 mRemoveSubfolders = new QCheckBox(tr("Remove Subfolders"), this);
3850 mRemoveSubfolders->setChecked(false);
3851 connect(mRemoveSubfolders, SIGNAL(stateChanged(int)), this, SLOT(checkbocChecked(int)));
3852
3853 // mButtons
3854 mButtons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
3855 mButtons->button(QDialogButtonBox::Ok)->setText(tr("&Extract"));
3856 mButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
3857 mButtons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
3858 connect(mButtons, SIGNAL(accepted()), this, SLOT(accept()));
3859 connect(mButtons, SIGNAL(rejected()), this, SLOT(reject()));
3860
3861 QWidget* extractWidget = new QWidget(this);
3862 QGridLayout* gdLayout = new QGridLayout(extractWidget);
3863 gdLayout->addWidget(archiveLabel, 0, 0);
3864 gdLayout->addWidget(mArchivePathEdit, 1, 0);
3865 gdLayout->addWidget(openArchiveButton, 1, 1);
3866 gdLayout->addWidget(dirLabel, 2, 0);
3867 gdLayout->addWidget(mDirPathEdit, 3, 0);
3868 gdLayout->addWidget(openDirButton, 3, 1);
3869 gdLayout->addWidget(mFeedbackLabel, 4, 0, 1, 2);
3870 gdLayout->addWidget(mFileListDisplay, 5, 0, 1, 2);
3871 gdLayout->addWidget(mRemoveSubfolders, 6, 0, 1, 2);
3872
3873 QVBoxLayout* layout = new QVBoxLayout(this);
3874 layout->addWidget(extractWidget);
3875 layout->addWidget(mButtons);
3876 }
3877
setCurrentFile(const QString & filePath,bool isZip)3878 void DkArchiveExtractionDialog::setCurrentFile(const QString& filePath, bool isZip) {
3879
3880 userFeedback("", false);
3881 mArchivePathEdit->setText("");
3882 mDirPathEdit->setText("");
3883 mFileListDisplay->clear();
3884 mRemoveSubfolders->setChecked(false);
3885
3886 mFilePath = filePath;
3887 if (isZip) {
3888 mArchivePathEdit->setText(mFilePath);
3889 loadArchive();
3890 }
3891 }
3892
textChanged(const QString & text)3893 void DkArchiveExtractionDialog::textChanged(const QString& text) {
3894
3895 bool oldStyle = mArchivePathEdit->property("error").toBool();
3896 bool newStyle = false;
3897
3898 if (QFileInfo(text).exists() && DkBasicLoader::isContainer(text)) {
3899 newStyle = false;
3900 mArchivePathEdit->setProperty("error", newStyle);
3901 loadArchive(text);
3902 }
3903 else {
3904 newStyle = true;
3905 mArchivePathEdit->setProperty("error", newStyle);
3906 userFeedback("", false);
3907 mFileListDisplay->clear();
3908 mButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
3909 }
3910
3911 if (oldStyle != newStyle) {
3912 mArchivePathEdit->style()->unpolish(mArchivePathEdit);
3913 mArchivePathEdit->style()->polish(mArchivePathEdit);
3914 mArchivePathEdit->update();
3915 }
3916
3917 }
3918
dirTextChanged(const QString & text)3919 void DkArchiveExtractionDialog::dirTextChanged(const QString& text) {
3920
3921 if (text.isEmpty()) {
3922 userFeedback("", false);
3923 mButtons->button(QDialogButtonBox::Ok)->setEnabled(false);
3924 }
3925 }
3926
checkbocChecked(int)3927 void DkArchiveExtractionDialog::checkbocChecked(int) {
3928
3929 loadArchive();
3930 }
3931
openArchive()3932 void DkArchiveExtractionDialog::openArchive() {
3933
3934 // load system default open dialog
3935 QString filePath = QFileDialog::getOpenFileName(
3936 this,
3937 tr("Open Archive"),
3938 (mArchivePathEdit->text().isEmpty()) ? QFileInfo(mFilePath).absolutePath() : mArchivePathEdit->text(),
3939 tr("Archives (%1)").arg(DkSettingsManager::param().app().containerRawFilters.remove(",")),
3940 nullptr,
3941 DkDialog::fileDialogOptions()
3942 );
3943
3944 if (QFileInfo(filePath).exists()) {
3945 mArchivePathEdit->setText(filePath);
3946 loadArchive(filePath);
3947 }
3948 }
3949
openDir()3950 void DkArchiveExtractionDialog::openDir() {
3951
3952 // load system default open dialog
3953 QString filePath = QFileDialog::getExistingDirectory(
3954 this,
3955 tr("Open Directory"),
3956 (mDirPathEdit->text().isEmpty()) ? QFileInfo(mFilePath).absolutePath() : mDirPathEdit->text(),
3957 QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks | DkDialog::fileDialogOptions()
3958 );
3959
3960 if (QFileInfo(filePath).exists())
3961 mDirPathEdit->setText(filePath);
3962 }
3963
userFeedback(const QString & msg,bool error)3964 void DkArchiveExtractionDialog::userFeedback(const QString& msg, bool error) {
3965
3966 if (!error)
3967 mFeedbackLabel->setProperty("warning", false);
3968 else
3969 mFeedbackLabel->setProperty("warning", true);
3970
3971 mFeedbackLabel->setText(msg);
3972 mFeedbackLabel->style()->unpolish(mFeedbackLabel);
3973 mFeedbackLabel->style()->polish(mFeedbackLabel);
3974 mFeedbackLabel->update();
3975 }
3976
loadArchive(const QString & filePath)3977 void DkArchiveExtractionDialog::loadArchive(const QString& filePath) {
3978
3979 mFileList = QStringList();
3980 mFileListDisplay->clear();
3981
3982 QString lFilePath = filePath;
3983 if (lFilePath.isEmpty())
3984 lFilePath = mArchivePathEdit->text();
3985
3986 QFileInfo fileInfo(lFilePath);
3987 if (!fileInfo.exists())
3988 return;
3989
3990 if (!DkBasicLoader::isContainer(lFilePath)) {
3991 userFeedback(tr("Not a valid archive."), true);
3992 return;
3993 }
3994
3995 if (mDirPathEdit->text().isEmpty()) {
3996
3997 mDirPathEdit->setText(lFilePath.remove("." + fileInfo.suffix()));
3998 mDirPathEdit->setFocus();
3999 }
4000
4001 QStringList fileNameList = JlCompress::getFileList(lFilePath);
4002
4003 // remove the * in fileFilters
4004 QStringList fileFiltersClean = DkSettingsManager::param().app().browseFilters;
4005 for (int idx = 0; idx < fileFiltersClean.size(); idx++)
4006 fileFiltersClean[idx].replace("*", "");
4007
4008 for (int idx = 0; idx < fileNameList.size(); idx++) {
4009
4010 for (int idxFilter = 0; idxFilter < fileFiltersClean.size(); idxFilter++) {
4011
4012 if (fileNameList.at(idx).contains(fileFiltersClean[idxFilter], Qt::CaseInsensitive)) {
4013 mFileList.append(fileNameList.at(idx));
4014 break;
4015 }
4016 }
4017 }
4018
4019 if (mFileList.size() > 0)
4020 userFeedback(tr("Number of images: ") + QString::number(mFileList.size()), false);
4021 else {
4022 userFeedback(tr("The archive does not contain any images."), false);
4023 return;
4024 }
4025
4026 mFileListDisplay->addItems(mFileList);
4027
4028 if (mRemoveSubfolders->checkState() == Qt::Checked) {
4029 for (int i = 0; i < mFileListDisplay->count(); i++) {
4030
4031 QFileInfo fi(mFileListDisplay->item(i)->text());
4032 mFileListDisplay->item(i)->setText(fi.fileName());
4033 }
4034 }
4035 mFileListDisplay->update();
4036
4037 mButtons->button(QDialogButtonBox::Ok)->setEnabled(true);
4038 }
4039
accept()4040 void DkArchiveExtractionDialog::accept() {
4041
4042 QStringList extractedFiles = extractFilesWithProgress(mArchivePathEdit->text(), mFileList, mDirPathEdit->text(), mRemoveSubfolders->isChecked());
4043
4044 if ((extractedFiles.isEmpty() || extractedFiles.size() != mFileList.size()) && !extractedFiles.contains("userCanceled")) {
4045
4046 QMessageBox msgBox(this);
4047 msgBox.setText(tr("The images could not be extracted!"));
4048 msgBox.setIcon(QMessageBox::Critical);
4049 msgBox.exec();
4050 }
4051
4052 QDialog::accept();
4053 }
4054
dropEvent(QDropEvent * event)4055 void DkArchiveExtractionDialog::dropEvent(QDropEvent *event) {
4056
4057 if (event->mimeData()->hasUrls() && event->mimeData()->urls().size() > 0) {
4058 QUrl url = event->mimeData()->urls().at(0);
4059 qDebug() << "dropping: " << url;
4060 url = url.toLocalFile();
4061
4062 if (QFileInfo(url.toString()).isFile()) {
4063 mArchivePathEdit->setText(url.toString());
4064 loadArchive(url.toString());
4065 }
4066 else
4067 mDirPathEdit->setText(url.toString());
4068 }
4069 }
4070
dragEnterEvent(QDragEnterEvent * event)4071 void DkArchiveExtractionDialog::dragEnterEvent(QDragEnterEvent *event) {
4072
4073 if (event->mimeData()->hasUrls()) {
4074 QUrl url = event->mimeData()->urls().at(0);
4075 url = url.toLocalFile();
4076 QFileInfo file = QFileInfo(url.toString());
4077
4078 if (file.exists())
4079 event->acceptProposedAction();
4080 }
4081
4082 }
4083
extractFilesWithProgress(const QString & fileCompressed,const QStringList & files,const QString & dir,bool removeSubfolders)4084 QStringList DkArchiveExtractionDialog::extractFilesWithProgress(const QString& fileCompressed, const QStringList& files, const QString& dir, bool removeSubfolders) {
4085
4086 QProgressDialog progressDialog(this);
4087 progressDialog.setCancelButtonText(tr("&Cancel"));
4088 progressDialog.setRange(0, files.size() - 1);
4089 progressDialog.setWindowTitle(tr("Extracting files..."));
4090 progressDialog.setWindowModality(Qt::WindowModal);
4091 progressDialog.setModal(true);
4092 progressDialog.hide();
4093 progressDialog.show();
4094
4095 QStringList extracted;
4096 for (int i=0; i<files.count(); i++) {
4097 progressDialog.setValue(i);
4098 progressDialog.setLabelText(tr("Extracting file %1 of %2").arg(i + 1).arg(files.size()));
4099
4100 QString absPath;
4101 if(removeSubfolders)
4102 absPath = QDir(dir).absoluteFilePath(QFileInfo(files.at(i)).fileName());
4103 else
4104 absPath = QDir(dir).absoluteFilePath(files.at(i));
4105
4106 if (JlCompress::extractFile(fileCompressed, files.at(i), absPath).isEmpty()) {
4107 qDebug() << "unable to extract:" << files.at(i);
4108 //return QStringList();
4109 }
4110 extracted.append(absPath);
4111 if(progressDialog.wasCanceled()) {
4112 return QStringList("userCanceled");
4113 }
4114 }
4115
4116 progressDialog.close();
4117
4118 return extracted;
4119 }
4120
4121 #endif
4122
4123
4124 // DkDialogManager --------------------------------------------------------------------
DkDialogManager(QObject * parent)4125 DkDialogManager::DkDialogManager(QObject* parent) : QObject(parent) {
4126
4127 DkActionManager& am = DkActionManager::instance();
4128
4129 connect(am.action(DkActionManager::menu_edit_shortcuts), SIGNAL(triggered()), this, SLOT(openShortcutsDialog()));
4130 connect(am.action(DkActionManager::menu_file_app_manager), SIGNAL(triggered()), this, SLOT(openAppManager()));
4131 connect(am.action(DkActionManager::menu_file_print), SIGNAL(triggered()), this, SLOT(openPrintDialog()));
4132 connect(am.action(DkActionManager::menu_tools_mosaic), SIGNAL(triggered()), this, SLOT(openMosaicDialog()));
4133 }
4134
openShortcutsDialog() const4135 void DkDialogManager::openShortcutsDialog() const {
4136
4137 DkActionManager& am = DkActionManager::instance();
4138
4139 DkShortcutsDialog* shortcutsDialog = new DkShortcutsDialog(DkUtils::getMainWindow());
4140 shortcutsDialog->addActions(am.fileActions(), am.fileMenu()->title());
4141 shortcutsDialog->addActions(am.openWithActions(), am.openWithMenu()->title());
4142 shortcutsDialog->addActions(am.sortActions(), am.sortMenu()->title());
4143 shortcutsDialog->addActions(am.editActions(), am.editMenu()->title());
4144 shortcutsDialog->addActions(am.manipulatorActions(), am.manipulatorMenu()->title());
4145 shortcutsDialog->addActions(am.viewActions(), am.viewMenu()->title());
4146 shortcutsDialog->addActions(am.panelActions(), am.panelMenu()->title());
4147 shortcutsDialog->addActions(am.toolsActions(), am.toolsMenu()->title());
4148 shortcutsDialog->addActions(am.syncActions(), am.syncMenu()->title());
4149 shortcutsDialog->addActions(am.previewActions(), tr("Preview"));
4150 #ifdef WITH_PLUGINS // TODO
4151
4152 DkPluginActionManager* pm = am.pluginActionManager();
4153 pm->updateMenu();
4154
4155 QVector<QAction*> allPluginActions = pm->pluginActions();
4156
4157 for (const QMenu* m : pm->pluginSubMenus()) {
4158 allPluginActions << m->actions().toVector();
4159 }
4160
4161 shortcutsDialog->addActions(allPluginActions, pm->menu()->title());
4162 #endif // WITH_PLUGINS
4163 shortcutsDialog->addActions(am.helpActions(), am.helpMenu()->title());
4164 shortcutsDialog->addActions(am.hiddenActions(), tr("Shortcuts"));
4165
4166 shortcutsDialog->exec();
4167 shortcutsDialog->deleteLater();
4168 }
4169
setCentralWidget(DkCentralWidget * cw)4170 void DkDialogManager::setCentralWidget(DkCentralWidget * cw) {
4171 mCentralWidget = cw;
4172 }
4173
openAppManager() const4174 void DkDialogManager::openAppManager() const {
4175
4176 DkActionManager& am = DkActionManager::instance();
4177
4178 DkAppManagerDialog* appManagerDialog = new DkAppManagerDialog(am.appManager(), DkUtils::getMainWindow());
4179 connect(appManagerDialog, SIGNAL(openWithSignal(QAction*)), am.appManager(), SIGNAL(openFileSignal(QAction*))); // forward
4180 appManagerDialog->exec();
4181
4182 appManagerDialog->deleteLater();
4183
4184 DkActionManager::instance().updateOpenWithMenu();
4185 }
4186
openMosaicDialog() const4187 void DkDialogManager::openMosaicDialog() const {
4188
4189 if (!mCentralWidget) {
4190 qWarning() << "cannot compute mosaic if there is no central widget...";
4191 return;
4192 }
4193
4194 #ifdef WITH_OPENCV
4195 DkMosaicDialog* mosaicDialog = new DkMosaicDialog(DkUtils::getMainWindow(), Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint);
4196 mosaicDialog->setFile(mCentralWidget->getCurrentFilePath());
4197
4198 int response = mosaicDialog->exec();
4199
4200 if (response == QDialog::Accepted && !mosaicDialog->getImage().isNull()) {
4201 QImage editedImage = mosaicDialog->getImage();
4202
4203 QSharedPointer<DkImageContainerT> imgC(new DkImageContainerT(""));
4204 imgC->setImage(mosaicDialog->getImage(), tr("Mosaic"));
4205
4206 mCentralWidget->addTab(imgC);
4207 DkActionManager::instance().action(DkActionManager::menu_file_save_as)->trigger();
4208 }
4209
4210 mosaicDialog->deleteLater();
4211 #endif
4212 }
4213
openPrintDialog() const4214 void DkDialogManager::openPrintDialog() const {
4215
4216 if (!mCentralWidget) {
4217 qWarning() << "cannot open print dialog if there is no central widget...";
4218 return;
4219 }
4220
4221 QSharedPointer<DkImageContainerT> imgC = mCentralWidget->getCurrentImage();
4222
4223 DkPrintPreviewDialog* previewDialog = new DkPrintPreviewDialog(DkUtils::getMainWindow());
4224 previewDialog->setImage(imgC->image());
4225
4226 // load all pages of tiffs
4227 if (imgC->getLoader()->getNumPages() > 1) {
4228
4229 auto l = imgC->getLoader();
4230
4231 for (int idx = 1; idx < l->getNumPages(); idx++) {
4232 l->loadPageAt(idx+1);
4233 previewDialog->addImage(l->image());
4234 }
4235 }
4236
4237 previewDialog->exec();
4238 previewDialog->deleteLater();
4239 }
4240
4241 // -------------------------------------------------------------------- DkPrintImage
DkPrintImage(const QImage & img,QPrinter * printer)4242 DkPrintImage::DkPrintImage(const QImage & img, QPrinter * printer) {
4243 mImg = img;
4244 mPrinter = printer;
4245 }
4246
image() const4247 QImage DkPrintImage::image() const {
4248 return mImg;
4249 }
4250
draw(QPainter & p,bool highQuality)4251 void DkPrintImage::draw(QPainter & p, bool highQuality) {
4252
4253 QRect r = mImg.rect();
4254 r = mTransform.mapRect(r);
4255
4256 QImage img = mImg;
4257
4258 if (highQuality)
4259 img = DkImage::resizeImage(mImg, QSize(), mTransform.m11(), DkImage::ipl_area, false);
4260 else
4261 p.setRenderHints(QPainter::SmoothPixmapTransform);
4262
4263 p.drawImage(r, img, img.rect());
4264 }
4265
fit()4266 void DkPrintImage::fit() {
4267
4268 if (!mPrinter) {
4269 qWarning() << "cannot fit image if the printer is NULL";
4270 return;
4271 }
4272
4273 double sf = 0;
4274 QRectF pr = mPrinter->pageRect();
4275
4276 // scale image to fit on paper
4277 if (pr.width() / mImg.width() < pr.height() / mImg.height()) {
4278 sf = pr.width() / (mImg.width() + DBL_EPSILON);
4279 }
4280 else {
4281 sf = pr.height() / (mImg.height() + DBL_EPSILON);
4282 }
4283
4284 double inchW = mPrinter->pageRect(QPrinter::Inch).width();
4285 double pxW = mPrinter->pageRect().width();
4286 double cDpi = dpi();
4287
4288 // use at least 150 dpi
4289 if (cDpi < 150 && sf > 1) {
4290 cDpi = 150;
4291 sf = (pxW / inchW) / cDpi;
4292 qDebug() << "new scale Factor:" << sf;
4293 }
4294
4295 mTransform.reset();
4296 mTransform.scale(sf, sf);
4297
4298 // TODO: print
4299 //updateDpiFactor(mDpi);
4300 center();
4301 }
4302
dpi()4303 double DkPrintImage::dpi() {
4304
4305 double iW = mPrinter->pageRect(QPrinter::Inch).width();
4306 double pxW = mPrinter->pageRect().width();
4307
4308 return (pxW / iW) / mTransform.m11();
4309 }
4310
center()4311 void DkPrintImage::center() {
4312 center(mTransform);
4313 }
4314
scale(double sf)4315 void DkPrintImage::scale(double sf) {
4316
4317 mTransform.reset();
4318 mTransform.scale(sf, sf);
4319
4320 center();
4321 }
4322
center(QTransform & t) const4323 void DkPrintImage::center(QTransform & t) const {
4324
4325 QRectF transRect = t.mapRect(mImg.rect());
4326 qreal xtrans = 0, ytrans = 0;
4327 xtrans = ((mPrinter->pageRect().width() - transRect.width()) / 2);
4328 ytrans = (mPrinter->pageRect().height() - transRect.height()) / 2;
4329
4330 t.translate(-t.dx() / (t.m11() + DBL_EPSILON), -t.dy() / (t.m22() + DBL_EPSILON)); // reset old transformation
4331 t.translate(xtrans / (t.m11() + DBL_EPSILON), ytrans / (t.m22() + DBL_EPSILON));
4332 }
4333
4334 // -------------------------------------------------------------------- DkSvgSizeDialog
DkSvgSizeDialog(const QSize & size,QWidget * parent)4335 DkSvgSizeDialog::DkSvgSizeDialog(const QSize& size, QWidget* parent) : QDialog(parent) {
4336
4337 mSize = size;
4338 mARatio = (double)size.width() / size.height();
4339 setWindowTitle("Resize SVG");
4340 createLayout();
4341
4342 QMetaObject::connectSlotsByName(this);
4343 }
4344
createLayout()4345 void DkSvgSizeDialog::createLayout() {
4346
4347
4348 QLabel* wl = new QLabel(tr("width:"), this);
4349
4350 mSizeBox.resize(b_end);
4351
4352 mSizeBox[b_width] = new QSpinBox(this);
4353 mSizeBox[b_width]->setObjectName("width");
4354
4355 QLabel* hl = new QLabel(tr("height:"), this);
4356
4357 mSizeBox[b_height] = new QSpinBox(this);
4358 mSizeBox[b_height]->setObjectName("height");
4359
4360 for (auto s : mSizeBox) {
4361 s->setMinimum(1);
4362 s->setMaximum(50000);
4363 s->setSuffix(" px");
4364 }
4365
4366 mSizeBox[b_width]->setValue(mSize.width());
4367 mSizeBox[b_height]->setValue(mSize.height());
4368
4369 // buttons
4370 auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
4371 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
4372 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
4373 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
4374 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
4375
4376 QGridLayout* layout = new QGridLayout(this);
4377 layout->addWidget(wl, 1, 1);
4378 layout->addWidget(mSizeBox[b_width], 1, 2);
4379 layout->addWidget(hl, 1, 3);
4380 layout->addWidget(mSizeBox[b_height], 1, 4);
4381 layout->setColumnStretch(0, 1);
4382 layout->setColumnStretch(5, 1);
4383 layout->setRowStretch(0, 1);
4384 layout->setRowStretch(2, 1);
4385 layout->addWidget(buttons, 3, 1, 1, 6, Qt::AlignBottom);
4386
4387 }
4388
on_width_valueChanged(int val)4389 void DkSvgSizeDialog::on_width_valueChanged(int val) {
4390
4391 mSize.setWidth(val);
4392 mSize.setHeight(qRound(val/mARatio));
4393
4394 mSizeBox[b_height]->blockSignals(true);
4395 mSizeBox[b_height]->setValue(mSize.height());
4396 mSizeBox[b_height]->blockSignals(false);
4397 }
4398
on_height_valueChanged(int val)4399 void DkSvgSizeDialog::on_height_valueChanged(int val) {
4400
4401 mSize.setWidth(qRound(val * mARatio));
4402 mSize.setHeight(val);
4403
4404 mSizeBox[b_width]->blockSignals(true);
4405 mSizeBox[b_width]->setValue(mSize.width());
4406 mSizeBox[b_width]->blockSignals(false);
4407 }
4408
size() const4409 QSize DkSvgSizeDialog::size() const {
4410 return mSize;
4411 }
4412
4413 // -------------------------------------------------------------------- DkChooseMonitorDialog
DkChooseMonitorDialog(QWidget * parent)4414 DkChooseMonitorDialog::DkChooseMonitorDialog(QWidget* parent) : QDialog(parent) {
4415
4416 mScreens = screens();
4417 createLayout();
4418 loadSettings();
4419 resize(300, 150);
4420 }
4421
createLayout()4422 void DkChooseMonitorDialog::createLayout() {
4423
4424 mDisplayWidget = new DkDisplayWidget(this);
4425 mDisplayWidget->show();
4426
4427 mCbRemember = new QCheckBox(tr("Remember Monitor Settings"), this);
4428
4429 // buttons
4430 auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
4431 buttons->button(QDialogButtonBox::Ok)->setText(tr("&OK"));
4432 buttons->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel"));
4433 connect(buttons, SIGNAL(accepted()), this, SLOT(accept()));
4434 connect(buttons, SIGNAL(rejected()), this, SLOT(reject()));
4435
4436 QGridLayout* layout = new QGridLayout(this);
4437 layout->setRowStretch(0, 1);
4438 layout->addWidget(mDisplayWidget, 1, 1);
4439 layout->addWidget(mCbRemember, 2, 1);
4440 layout->addWidget(buttons, 3, 1);
4441 layout->setRowStretch(4, 1);
4442 }
4443
loadSettings()4444 void DkChooseMonitorDialog::loadSettings() {
4445
4446 DefaultSettings settings;
4447 settings.beginGroup("MonitorSetup");
4448
4449 int mIdx = settings.value("monitorIndex", 0).toInt();
4450 mCbRemember->setChecked(!settings.value("showDialog", true).toBool());
4451
4452 settings.endGroup();
4453
4454 if (mIdx >= 0 && mIdx < mDisplayWidget->count())
4455 mDisplayWidget->setCurrentIndex(mIdx);
4456 else
4457 mCbRemember->setChecked(false); // fall-back if the count is illegal
4458 }
4459
saveSettings() const4460 void DkChooseMonitorDialog::saveSettings() const {
4461
4462 DefaultSettings settings;
4463
4464 settings.beginGroup("MonitorSetup");
4465 settings.setValue("monitorIndex", mDisplayWidget->currentIndex());
4466 settings.setValue("showDialog", !mCbRemember->isChecked());
4467 settings.endGroup();
4468 }
4469
exec()4470 int DkChooseMonitorDialog::exec() {
4471
4472 int answer = QDialog::exec();
4473
4474 if (answer == QDialog::Accepted)
4475 saveSettings();
4476
4477 return answer;
4478 }
4479
screens() const4480 QList<QScreen*> DkChooseMonitorDialog::screens() const {
4481
4482 return QGuiApplication::screens();
4483 }
4484
screenRect() const4485 QRect DkChooseMonitorDialog::screenRect() const {
4486
4487 return mDisplayWidget->screenRect();
4488 }
4489
showDialog() const4490 bool DkChooseMonitorDialog::showDialog() const {
4491 return !mCbRemember->isChecked();
4492 }
4493
4494 } // close namespace
4495
4496