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&uuml;nscht<br>"
158 		"Stefan fiel Spa&szlig; w&auml;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 		"&#169; 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