1 /*******************************************************************************************************
2  DkNoMacs.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 "DkNoMacs.h"
29 
30 // my stuff
31 #include "DkNetwork.h"
32 #include "DkViewPort.h"
33 #include "DkWidgets.h"
34 #include "DkDialog.h"
35 #include "DkSettings.h"
36 #include "DkMenu.h"
37 #include "DkMessageBox.h"
38 #include "DkMetaDataWidgets.h"
39 #include "DkManipulatorWidgets.h"
40 #include "DkThumbsWidgets.h"
41 #include "DkBatch.h"
42 #include "DkCentralWidget.h"
43 #include "DkMetaData.h"
44 #include "DkImageContainer.h"
45 #include "DkQuickAccess.h"
46 #include "DkUtils.h"
47 #include "DkControlWidget.h"
48 #include "DkImageLoader.h"
49 #include "DkTimer.h"
50 #include "DkActionManager.h"
51 #include "DkStatusBar.h"
52 #include "DkDockWidgets.h"
53 #include "DkLogWidget.h"
54 #include "DkUpdater.h"
55 #include "DkToolbars.h"
56 
57 #ifdef  WITH_PLUGINS
58 #include "DkPluginInterface.h"
59 #include "DkPluginManager.h"
60 #endif //  WITH_PLUGINS
61 
62 #pragma warning(push, 0)	// no warnings from includes - begin
63 #include <QBoxLayout>
64 #include <QResizeEvent>
65 #include <QAction>
66 #include <QFileDialog>
67 #include <QMessageBox>
68 #include <QStatusBar>
69 //#include <QPanGesture>
70 #include <QSplashScreen>
71 #include <QErrorMessage>
72 #include <QDesktopServices>
73 #include <QClipboard>
74 #include <QEvent>
75 #include <QSettings>
76 #include <QFileInfo>
77 #include <QTimer>
78 #include <QProcess>
79 #include <QStringBuilder>
80 #include <QDesktopWidget>
81 #include <QProgressDialog>
82 #include <QDrag>
83 #include <QVector2D>
84 #include <qmath.h>
85 #include <QMimeData>
86 #include <QNetworkProxyFactory>
87 #include <QInputDialog>
88 #include <QApplication>
89 #include <QShortcut>
90 #pragma warning(pop)		// no warnings from includes - end
91 
92 #if defined(Q_OS_WIN) && !defined(SOCK_STREAM)
93 #include <winsock2.h>	// needed since libraw 0.16
94 #endif
95 
96 #ifdef Q_OS_WIN
97 #include <QWinTaskbarButton>
98 #endif
99 
100 #include <assert.h>
101 
102 #include <iostream>
103 
104 namespace nmc {
105 
DkNomacsOSXEventFilter(QObject * parent)106 DkNomacsOSXEventFilter::DkNomacsOSXEventFilter(QObject *parent) : QObject(parent) {
107 }
108 
109 /*! Handle QFileOpenEvent for mac here */
eventFilter(QObject * obj,QEvent * event)110 bool DkNomacsOSXEventFilter::eventFilter(QObject *obj, QEvent *event) {
111 	if (event->type() == QEvent::FileOpen) {
112 		emit loadFile(static_cast<QFileOpenEvent*>(event)->file());
113 		return true;
114 	}
115 	return QObject::eventFilter(obj, event);
116 }
117 
DkNoMacs(QWidget * parent,Qt::WindowFlags flags)118 DkNoMacs::DkNoMacs(QWidget *parent, Qt::WindowFlags flags)
119 	: QMainWindow(parent, flags) {
120 
121 	QMainWindow::setWindowTitle("nomacs | Image Lounge");
122 	setObjectName("DkNoMacs");
123 
124 	mMenu = new DkMenuBar(this, -1);
125 
126 	DkActionManager& am = DkActionManager::instance();
127 	am.createActions(this);
128 	am.createMenus(mMenu);
129 	am.enableImageActions(false);
130 
131 	mSaveSettings = true;
132 
133 	mOpenDialog = 0;
134 	mSaveDialog = 0;
135 	mThumbSaver = 0;
136 	mOpacityDialog = 0;
137 	mUpdater = 0;
138 	mTranslationUpdater = 0;
139 	mExportTiffDialog = 0;
140 	mUpdateDialog = 0;
141 	mProgressDialog = 0;
142 	mProgressDialogTranslations = 0;
143 	mForceDialog = 0;
144 	mTrainDialog = 0;
145 	mExplorer = 0;
146 	mMetaDataDock = 0;
147 	mPrintPreviewDialog = 0;
148 	mThumbsDock = 0;
149 	mQuickAccess = 0;
150 #ifdef WITH_QUAZIP
151 	mArchiveExtractionDialog = 0;
152 #endif
153 
154 	mOldGeometry = geometry();
155 	mOverlaid = false;
156 
157 	resize(850, 504);
158 	setMinimumSize(20, 20);
159 
160 	//// fun fact
161 	//double an = pow(3987, 12);
162 	//double bn = pow(4365, 12);
163 
164 	//qDebug() << "3987 ^ 12 + 4365 ^ 12 = " << pow(an + bn, 1/12.0) << "^ 12";
165 	//qDebug() << "Sorry Fermat, but the Simpsons are right.";
166 }
167 
~DkNoMacs()168 DkNoMacs::~DkNoMacs() {
169 }
170 
init()171 void DkNoMacs::init() {
172 
173 // assign icon -> in windows the 32px version
174 	QString iconPath = ":/nomacs/img/nomacs.svg";
175 	loadStyleSheet();
176 
177 	QIcon nmcIcon = QIcon(iconPath);
178 	setObjectName("DkNoMacs");
179 
180 	if (!nmcIcon.isNull())
181 		setWindowIcon(nmcIcon);
182 
183 	// shortcuts and actions
184 	createActions();
185 	createMenu();
186 	createContextMenu();
187 	createStatusBar();
188 
189 	//// TODO - just for android register me as a gesture recognizer
190 	//grabGesture(Qt::PanGesture);
191 	//grabGesture(Qt::PinchGesture);
192 	//grabGesture(Qt::SwipeGesture);
193 
194 	// load the window at the same position as last time
195 	readSettings();
196 	installEventFilter(this);
197 
198 	if (DkSettingsManager::param().app().appMode != DkSettings::mode_frameless) {
199 		DkToolBarManager::inst().showDefaultToolBar(DkSettingsManager::param().app().showToolBar);
200 		showMenuBar(DkSettingsManager::param().app().showMenuBar);
201 		DkStatusBarManager::instance().show(DkSettingsManager::param().app().showStatusBar);
202 	}
203 
204 	// connections to the image loader
205 	connect(getTabWidget(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), this, SLOT(setWindowTitle(QSharedPointer<DkImageContainerT>)));
206 
207 	DkActionManager::instance().enableMovieActions(false);
208 
209 // clean up nomacs
210 #ifdef Q_OS_WIN
211 	if (!nmc::DkSettingsManager::param().global().setupPath.isEmpty() && QApplication::applicationVersion() == nmc::DkSettingsManager::param().global().setupVersion) {
212 
213 		// ask for exists - otherwise we always try to delete it if the user deleted it
214 		if (!QFileInfo(nmc::DkSettingsManager::param().global().setupPath).exists() || QFile::remove(nmc::DkSettingsManager::param().global().setupPath)) {
215 			nmc::DkSettingsManager::param().global().setupPath = "";
216 			nmc::DkSettingsManager::param().global().setupVersion = "";
217 			nmc::DkSettingsManager::param().save();
218 		}
219 	}
220 #endif // Q_WS_WIN
221 }
222 
createStatusBar()223 void DkNoMacs::createStatusBar() {
224 
225 	setStatusBar(DkStatusBarManager::instance().statusbar());
226 }
227 
loadStyleSheet()228 void DkNoMacs::loadStyleSheet() {
229 
230 	DkThemeManager tm;
231 	tm.applyTheme();
232 }
233 
createMenu()234 void DkNoMacs::createMenu() {
235 
236 	setMenuBar(mMenu);
237 	DkActionManager& am = DkActionManager::instance();
238 	mMenu->addMenu(am.fileMenu());
239 	mMenu->addMenu(am.editMenu());
240 	mMenu->addMenu(am.manipulatorMenu());
241 	mMenu->addMenu(am.viewMenu());
242 	mMenu->addMenu(am.panelMenu());
243 	mMenu->addMenu(am.toolsMenu());
244 
245 	// no sync menu in frameless view
246 	if (DkSettingsManager::param().app().appMode != DkSettings::mode_frameless)
247 		mMenu->addMenu(am.syncMenu());
248 
249 #ifdef WITH_PLUGINS
250 	// plugin menu
251 	mPluginsMenu = mMenu->addMenu(tr("Pl&ugins"));
252 	am.pluginActionManager()->setMenu(mPluginsMenu);
253 #endif // WITH_PLUGINS
254 
255 	mMenu->addMenu(am.helpMenu());
256 }
257 
createContextMenu()258 void DkNoMacs::createContextMenu() {
259 }
260 
createActions()261 void DkNoMacs::createActions() {
262 
263 	DkActionManager& am = DkActionManager::instance();
264 
265 	connect(am.action(DkActionManager::menu_file_open), SIGNAL(triggered()), this, SLOT(openFile()));
266 	connect(am.action(DkActionManager::menu_file_open_dir), SIGNAL(triggered()), this, SLOT(openDir()));
267 	connect(am.action(DkActionManager::menu_file_quick_launch), SIGNAL(triggered()), this, SLOT(openQuickLaunch()));
268 	connect(am.action(DkActionManager::menu_file_open_list), SIGNAL(triggered()), this, SLOT(openFileList()));
269 	connect(am.action(DkActionManager::menu_file_save_list), SIGNAL(triggered()), this, SLOT(saveFileList()));
270 	connect(am.action(DkActionManager::menu_file_rename), SIGNAL(triggered()), this, SLOT(renameFile()));
271 	connect(am.action(DkActionManager::menu_file_goto), SIGNAL(triggered()), this, SLOT(goTo()));
272 	connect(am.action(DkActionManager::menu_file_show_recent), SIGNAL(triggered(bool)), centralWidget(), SLOT(showRecentFiles(bool)));
273 	connect(am.action(DkActionManager::menu_file_new_instance), SIGNAL(triggered()), this, SLOT(newInstance()));
274 	connect(am.action(DkActionManager::menu_file_private_instance), SIGNAL(triggered()), this, SLOT(newInstance()));
275 	connect(am.action(DkActionManager::menu_file_find), SIGNAL(triggered()), this, SLOT(find()));
276 	connect(am.action(DkActionManager::menu_file_recursive), SIGNAL(triggered(bool)), this, SLOT(setRecursiveScan(bool)));
277 	connect(am.action(DkActionManager::menu_file_exit), SIGNAL(triggered()), this, SLOT(close()));
278 
279 	connect(am.action(DkActionManager::menu_sort_filename), SIGNAL(triggered(bool)), this, SLOT(changeSorting(bool)));
280 	connect(am.action(DkActionManager::menu_sort_date_created), SIGNAL(triggered(bool)), this, SLOT(changeSorting(bool)));
281 	connect(am.action(DkActionManager::menu_sort_date_modified), SIGNAL(triggered(bool)), this, SLOT(changeSorting(bool)));
282 	connect(am.action(DkActionManager::menu_sort_random), SIGNAL(triggered(bool)), this, SLOT(changeSorting(bool)));
283 	connect(am.action(DkActionManager::menu_sort_ascending), SIGNAL(triggered(bool)), this, SLOT(changeSorting(bool)));
284 	connect(am.action(DkActionManager::menu_sort_descending), SIGNAL(triggered(bool)), this, SLOT(changeSorting(bool)));
285 
286 	connect(am.action(DkActionManager::menu_panel_menu), SIGNAL(toggled(bool)), this, SLOT(showMenuBar(bool)));
287 	connect(am.action(DkActionManager::menu_panel_explorer), SIGNAL(toggled(bool)), this, SLOT(showExplorer(bool)));
288 	connect(am.action(DkActionManager::menu_panel_metadata_dock), SIGNAL(toggled(bool)), this, SLOT(showMetaDataDock(bool)));
289 	connect(am.action(DkActionManager::menu_edit_image), SIGNAL(toggled(bool)), this, SLOT(showEditDock(bool)));
290 	connect(am.action(DkActionManager::menu_panel_history), SIGNAL(toggled(bool)), this, SLOT(showHistoryDock(bool)));
291 	connect(am.action(DkActionManager::menu_panel_log), SIGNAL(toggled(bool)), this, SLOT(showLogDock(bool)));
292 	connect(am.action(DkActionManager::menu_panel_preview), SIGNAL(toggled(bool)), this, SLOT(showThumbsDock(bool)));
293 	connect(am.action(DkActionManager::menu_panel_toggle), SIGNAL(toggled(bool)), this, SLOT(toggleDocks(bool)));
294 
295 	connect(am.action(DkActionManager::menu_view_fit_frame), SIGNAL(triggered()), this, SLOT(fitFrame()));
296 	connect(am.action(DkActionManager::menu_view_fullscreen), SIGNAL(triggered()), this, SLOT(toggleFullScreen()));
297 	connect(am.action(DkActionManager::menu_view_frameless), SIGNAL(toggled(bool)), this, SLOT(restartFrameless(bool)));
298 	connect(am.action(DkActionManager::menu_panel_transfertoolbar), SIGNAL(toggled(bool)), this, SLOT(restartWithPseudoColor(bool)));
299 	connect(am.action(DkActionManager::menu_view_opacity_change), SIGNAL(triggered()), this, SLOT(showOpacityDialog()));
300 	connect(am.action(DkActionManager::menu_view_opacity_up), SIGNAL(triggered()), this, SLOT(opacityUp()));
301 	connect(am.action(DkActionManager::menu_view_opacity_down), SIGNAL(triggered()), this, SLOT(opacityDown()));
302 	connect(am.action(DkActionManager::menu_view_opacity_an), SIGNAL(triggered()), this, SLOT(animateChangeOpacity()));
303 	connect(am.action(DkActionManager::menu_view_lock_window), SIGNAL(triggered(bool)), this, SLOT(lockWindow(bool)));
304 
305 	connect(am.action(DkActionManager::menu_tools_thumbs), SIGNAL(triggered()), this, SLOT(computeThumbsBatch()));
306 	connect(am.action(DkActionManager::menu_tools_filter), SIGNAL(triggered(bool)), this, SLOT(find(bool)));
307 	connect(am.action(DkActionManager::menu_tools_export_tiff), SIGNAL(triggered()), this, SLOT(exportTiff()));
308 	connect(am.action(DkActionManager::menu_tools_extract_archive), SIGNAL(triggered()), this, SLOT(extractImagesFromArchive()));
309 	connect(am.action(DkActionManager::menu_tools_train_format), SIGNAL(triggered()), this, SLOT(trainFormat()));
310 
311 	connect(am.action(DkActionManager::sc_test_rec), SIGNAL(triggered()), this, SLOT(loadRecursion()));
312 	connect(am.action(DkActionManager::sc_test_pong), SIGNAL(triggered()), this, SLOT(startPong()));
313 
314 	connect(am.action(DkActionManager::menu_plugin_manager), SIGNAL(triggered()), this, SLOT(openPluginManager()));
315 
316 	// help menu
317 	connect(am.action(DkActionManager::menu_help_about), SIGNAL(triggered()), this, SLOT(aboutDialog()));
318 	connect(am.action(DkActionManager::menu_help_documentation), SIGNAL(triggered()), this, SLOT(openDocumentation()));
319 	connect(am.action(DkActionManager::menu_help_bug), SIGNAL(triggered()), this, SLOT(bugReport()));
320 	connect(am.action(DkActionManager::menu_help_update), SIGNAL(triggered()), this, SLOT(checkForUpdate()));
321 	connect(am.action(DkActionManager::menu_help_update_translation), SIGNAL(triggered()), this, SLOT(updateTranslations()));
322 
323 	connect(am.appManager(), SIGNAL(openFileSignal(QAction*)), this, SLOT(openFileWith(QAction*)));
324 }
325 
clearFileHistory()326 void DkNoMacs::clearFileHistory() {
327 	DkSettingsManager::param().global().recentFiles.clear();
328 }
329 
clearFolderHistory()330 void DkNoMacs::clearFolderHistory() {
331 	DkSettingsManager::param().global().recentFolders.clear();
332 }
333 
getTabWidget() const334 DkCentralWidget* DkNoMacs::getTabWidget() const {
335 
336 	DkCentralWidget* cw = dynamic_cast<DkCentralWidget*>(centralWidget());
337 	return cw;
338 }
339 
340 // Qt how-to
closeEvent(QCloseEvent * event)341 void DkNoMacs::closeEvent(QCloseEvent *event) {
342 
343 	DkCentralWidget* cw = static_cast<DkCentralWidget*>(centralWidget());
344 
345 	if (cw && cw->getTabs().size() > 1) {
346 
347 		DkMessageBox* msg = new DkMessageBox(QMessageBox::Question, tr("Quit nomacs"),
348 			tr("Do you want nomacs to save your tabs?"),
349 			(QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel), this);
350 		msg->setButtonText(QMessageBox::Yes, tr("&Save and Quit"));
351 		msg->setButtonText(QMessageBox::No, tr("&Quit"));
352 		msg->setObjectName("saveTabsDialog");
353 
354 		int answer = msg->exec();
355 
356 		if (answer == QMessageBox::Cancel || answer == QMessageBox::NoButton) {	// User canceled - do not close
357 			event->ignore();
358 			return;
359 		}
360 
361 		cw->saveSettings(answer == QMessageBox::Yes);
362 	}
363 	else
364 		cw->saveSettings(false);
365 
366 	if (!getTabWidget()->requestClose()) {
367 		// do not close if the user hit cancel in the save changes dialog
368 		event->ignore();
369 		return;
370 	}
371 
372 	emit closeSignal();
373 	qDebug() << "saving window settings...";
374 	setVisible(false);
375 	//showNormal();
376 
377 	if (mSaveSettings) {
378 		DefaultSettings settings;
379 		settings.setValue("geometryNomacs", geometry());
380 		settings.setValue("geometry", saveGeometry());
381 		settings.setValue("windowState", saveState());
382 
383 		if (mExplorer)
384 			settings.setValue(mExplorer->objectName(), QMainWindow::dockWidgetArea(mExplorer));
385 		if (mMetaDataDock)
386 			settings.setValue(mMetaDataDock->objectName(), QMainWindow::dockWidgetArea(mMetaDataDock));
387 		if (mEditDock)
388 			settings.setValue(mEditDock->objectName(), QMainWindow::dockWidgetArea(mEditDock));
389 		if (mThumbsDock)
390 			settings.setValue(mThumbsDock->objectName(), QMainWindow::dockWidgetArea(mThumbsDock));
391 
392 		nmc::DkSettingsManager::param().save();
393 	}
394 
395 	QMainWindow::closeEvent(event);
396 }
397 
resizeEvent(QResizeEvent * event)398 void DkNoMacs::resizeEvent(QResizeEvent *event) {
399 
400 	QMainWindow::resizeEvent(event);
401 
402 	if (!mOverlaid)
403 		mOldGeometry = geometry();
404 	else if (windowOpacity() < 1.0f) {
405 		animateChangeOpacity();
406 		mOverlaid = false;
407 	}
408 
409 }
410 
moveEvent(QMoveEvent * event)411 void DkNoMacs::moveEvent(QMoveEvent *event) {
412 
413 	QMainWindow::moveEvent(event);
414 
415 	if (!mOverlaid)
416 		mOldGeometry = geometry();
417 	else if (windowOpacity() < 1.0f) {
418 		animateChangeOpacity();
419 		mOverlaid = false;
420 	}
421 }
422 
mouseDoubleClickEvent(QMouseEvent * event)423 void DkNoMacs::mouseDoubleClickEvent(QMouseEvent* event) {
424 
425 	if (event->button() != Qt::LeftButton || (getTabWidget() && !getTabWidget()->getCurrentImage()))
426 		return;
427 
428 	if (isFullScreen())
429 		exitFullScreen();
430 	else if (DkSettingsManager::instance().param().global().doubleClickForFullscreen)
431 		enterFullScreen();
432 
433 	//QMainWindow::mouseDoubleClickEvent(event);
434 }
435 
436 
mousePressEvent(QMouseEvent * event)437 void DkNoMacs::mousePressEvent(QMouseEvent* event) {
438 
439 	mMousePos = event->pos();
440 
441 	QMainWindow::mousePressEvent(event);
442 }
443 
mouseReleaseEvent(QMouseEvent * event)444 void DkNoMacs::mouseReleaseEvent(QMouseEvent *event) {
445 
446 	QMainWindow::mouseReleaseEvent(event);
447 }
448 
contextMenuEvent(QContextMenuEvent * event)449 void DkNoMacs::contextMenuEvent(QContextMenuEvent *event) {
450 
451 	QMainWindow::contextMenuEvent(event);
452 
453 	if (!event->isAccepted())
454 		DkActionManager::instance().contextMenu()->exec(event->globalPos());
455 }
456 
mouseMoveEvent(QMouseEvent * event)457 void DkNoMacs::mouseMoveEvent(QMouseEvent *event) {
458 
459 	QMainWindow::mouseMoveEvent(event);
460 }
461 
462 //bool DkNoMacs::gestureEvent(QGestureEvent *event) {
463 //
464 //	DkViewPort* vp = viewport();
465 //
466 //	if (QGesture *swipe = event->gesture(Qt::SwipeGesture)) {
467 //		QSwipeGesture* swipeG = static_cast<QSwipeGesture *>(swipe);
468 //
469 //		qDebug() << "swipe detected\n";
470 //		if (vp) {
471 //
472 //			if (swipeG->horizontalDirection() == QSwipeGesture::Left)
473 //				vp->loadNextFileFast();
474 //			else if (swipeG->horizontalDirection() == QSwipeGesture::Right)
475 //				vp->loadPrevFileFast();
476 //
477 //			// TODO: recognize some other gestures please
478 //		}
479 //
480 //	}
481 //	else if (QGesture *pan = event->gesture(Qt::PanGesture)) {
482 //
483 //		QPanGesture* panG = static_cast<QPanGesture *>(pan);
484 //
485 //		qDebug() << "you're speedy: " << panG->acceleration();
486 //
487 //		QPointF delta = panG->delta();
488 //
489 //		if (panG->acceleration() > 10 && delta.x() && fabs(delta.y()/delta.x()) < 0.2) {
490 //
491 //			if (delta.x() < 0)
492 //				vp->loadNextFileFast();
493 //			else
494 //				vp->loadPrevFileFast();
495 //		}
496 //
497 //		if (vp)
498 //			vp->moveView(panG->delta());
499 //	}
500 //	else if (QGesture *pinch = event->gesture(Qt::PinchGesture)) {
501 //
502 //		QPinchGesture* pinchG = static_cast<QPinchGesture *>(pinch);
503 //
504 //		//if (pinchG->changeFlags() == QPinchGesture::ChangeFlag.ScaleFactorChanged) {
505 //		qDebug() << "scale Factor: " << pinchG->scaleFactor();
506 //		if (pinchG->scaleFactor() != 0 && vp) {
507 //			vp->zoom((float)pinchG->scaleFactor());
508 //		}
509 //		else if (pinchG->rotationAngle() != 0 && vp) {
510 //
511 //			float angle = (float)pinchG->rotationAngle();
512 //			qDebug() << "angle: " << angle;
513 //			//vp->rotate(angle);
514 //		}
515 //	}
516 //
517 //	qDebug() << "gesture event (NoMacs)";
518 //
519 //	//	pinchTriggered(static_cast<QPinchGesture *>(pinch));
520 //	return true;
521 //}
522 
readSettings()523 void DkNoMacs::readSettings() {
524 
525 	DefaultSettings settings;
526 
527 #ifdef Q_WS_WIN
528 	// fixes #392 - starting maximized on 2nd screen - tested on win8 only
529 	QRect r = settings.value("geometryNomacs", QRect()).toRect();
530 
531 	if (r.width() && r.height())	// do not set the geometry if nomacs is loaded the first time
532 		setGeometry(r);
533 #endif
534 
535 	restoreGeometry(settings.value("geometry").toByteArray());
536 	restoreState(settings.value("windowState").toByteArray());
537 
538 	// restore state makes the toolbar visible - so hide it again...
539 	if (DkSettingsManager::param().app().appMode == DkSettings::mode_frameless) {
540 		DkToolBarManager::inst().showDefaultToolBar(false);
541 		DkStatusBarManager::instance().show(false);
542 	}
543 }
544 
toggleFullScreen()545 void DkNoMacs::toggleFullScreen() {
546 
547 	if (isFullScreen())
548 		exitFullScreen();
549 	else
550 		enterFullScreen();
551 }
552 
enterFullScreen()553 void DkNoMacs::enterFullScreen() {
554 
555 	DkSettingsManager::param().app().currentAppMode += qFloor(DkSettings::mode_end*0.5f);
556 	if (DkSettingsManager::param().app().currentAppMode < 0) {
557 		qDebug() << "illegal state: " << DkSettingsManager::param().app().currentAppMode;
558 		DkSettingsManager::param().app().currentAppMode = DkSettings::mode_default;
559 	}
560 
561 	menuBar()->hide();
562 	DkToolBarManager::inst().show(false);
563 	DkStatusBarManager::instance().statusbar()->hide();
564 	getTabWidget()->showTabs(false);
565 
566 	restoreDocks();
567 
568 	// here is an issue with windows that I can't quite fix:
569 	// if we send nomacs to fullscreen from an attached window (i.e. split window)
570 	setWindowState(windowState() ^ Qt::WindowFullScreen);
571 
572 	if (getTabWidget()->getViewPort())
573 		getTabWidget()->getViewPort()->setFullScreen(true);
574 
575 }
576 
exitFullScreen()577 void DkNoMacs::exitFullScreen() {
578 
579 	if (isFullScreen()) {
580 		DkSettingsManager::param().app().currentAppMode -= qFloor(DkSettings::mode_end*0.5f);
581 		if (DkSettingsManager::param().app().currentAppMode < 0) {
582 			qDebug() << "illegal state: " << DkSettingsManager::param().app().currentAppMode;
583 			DkSettingsManager::param().app().currentAppMode = DkSettings::mode_default;
584 		}
585 
586 		if (DkSettingsManager::param().app().showMenuBar) mMenu->show();
587 		if (DkSettingsManager::param().app().showStatusBar) DkStatusBarManager::instance().statusbar()->show();
588 
589 		DkToolBarManager::inst().restore();
590 		restoreDocks();
591 		setWindowState(windowState() ^ Qt::WindowFullScreen);
592 
593 
594 		if (getTabWidget())
595 			getTabWidget()->showTabs(true);
596 
597 		update();	// if no resize is triggered, the viewport won't change its color
598 	}
599 
600 	if (getTabWidget()->getViewPort())
601 		getTabWidget()->getViewPort()->setFullScreen(false);
602 }
603 
toggleDocks(bool hide)604 void DkNoMacs::toggleDocks(bool hide) {
605 
606 	if (hide) {
607 		showExplorer(false, false);
608 		showMetaDataDock(false, false);
609 		showEditDock(false, false);
610 		showHistoryDock(false, false);
611 		showLogDock(false, false);
612 		DkToolBarManager::inst().show(false, false);
613 		DkStatusBarManager::instance().show(false, false);
614 	}
615 	else {
616 		restoreDocks();
617 		DkToolBarManager::inst().restore();
618 		DkStatusBarManager::instance().show(DkSettingsManager::param().app().showStatusBar, false);
619 	}
620 }
621 
restoreDocks()622 void DkNoMacs::restoreDocks() {
623 
624 	showExplorer(DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showExplorer), false);
625 	showMetaDataDock(DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showMetaDataDock), false);
626 	showEditDock(DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showEditDock), false);
627 	showHistoryDock(DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showHistoryDock), false);
628 	showLogDock(DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showLogDock), false);
629 }
630 
restartFrameless(bool)631 void DkNoMacs::restartFrameless(bool) {
632 
633 	QString exe = QApplication::applicationFilePath();
634 	QStringList args;
635 
636 	if (objectName() != "DkNoMacsFrameless")
637 		args << "-m" << "frameless";
638 	else
639 		args << "-m" << "default";
640 
641 	if (getTabWidget()->getCurrentImage())
642 		args.append(getTabWidget()->getCurrentImage()->filePath());
643 
644 	nmc::DkSettingsManager::param().save();
645 
646 	bool started = mProcess.startDetached(exe, args);
647 
648 	// close me if the new instance started
649 	if (started)
650 		close();
651 
652 	qDebug() << "frameless arguments: " << args;
653 }
654 
showRecentFilesOnStartUp()655 void DkNoMacs::showRecentFilesOnStartUp() {
656 
657 	QTimer::singleShot(100, getTabWidget(), SLOT(showRecentFiles()));
658 }
659 
startPong() const660 void DkNoMacs::startPong() const {
661 
662 	QString exe = QApplication::applicationFilePath();
663 	QStringList args;
664 
665 	args.append("--pong");
666 
667 	bool started = mProcess.startDetached(exe, args);
668 	qDebug() << "pong started: " << started;
669 }
670 
fitFrame()671 void DkNoMacs::fitFrame() {
672 
673 	if (!getTabWidget()->getViewPort())
674 		return;
675 
676 	auto vp = getTabWidget()->getViewPort();
677 
678 	QRectF viewRect = vp->getImageViewRect();
679 	QRectF vpRect = vp->geometry();
680 	QRectF nmRect = frameGeometry();
681 	QSize frDiff = frameGeometry().size()-geometry().size();
682 
683 	// compute new size
684 	QPointF c = nmRect.center();
685 	nmRect.setSize(nmRect.size() + viewRect.size() - vpRect.size());
686 	nmRect.moveCenter(c);
687 
688 	// still fits on screen?
689 	QDesktopWidget* dw = QApplication::desktop();
690 	QRect screenRect = dw->availableGeometry(this);
691 	QRect newGeometry = screenRect.intersected(nmRect.toRect());
692 
693 	// correct frame
694 	newGeometry.setSize(newGeometry.size()-frDiff);
695 	newGeometry.moveTopLeft(newGeometry.topLeft() - frameGeometry().topLeft()+geometry().topLeft());
696 
697 	setGeometry(newGeometry);
698 
699 	// reset viewport if we did not clip -> compensates round-off errors
700 	if (screenRect.contains(nmRect.toRect()))
701 		vp->resetView();
702 
703 }
704 
setRecursiveScan(bool recursive)705 void DkNoMacs::setRecursiveScan(bool recursive) {
706 
707 	DkSettingsManager::param().global().scanSubFolders = recursive;
708 
709 	QSharedPointer<DkImageLoader> loader = getTabWidget()->getCurrentImageLoader();
710 
711 	if (!loader)
712 		return;
713 
714 	if (recursive)
715 		getTabWidget()->setInfo(tr("Recursive Folder Scan is Now Enabled"));
716 	else
717 		getTabWidget()->setInfo(tr("Recursive Folder Scan is Now Disabled"));
718 
719 	loader->updateSubFolders(loader->getDirPath());
720 }
721 
showOpacityDialog()722 void DkNoMacs::showOpacityDialog() {
723 
724 	if (!mOpacityDialog) {
725 		mOpacityDialog = new DkOpacityDialog(this);
726 		mOpacityDialog->setWindowTitle(tr("Change Opacity"));
727 	}
728 
729 	if (mOpacityDialog->exec())
730 		setWindowOpacity(mOpacityDialog->value()/100.0f);
731 }
732 
opacityDown()733 void DkNoMacs::opacityDown() {
734 
735 	changeOpacity(-0.3f);
736 }
737 
opacityUp()738 void DkNoMacs::opacityUp() {
739 
740 	changeOpacity(0.3f);
741 }
742 
changeOpacity(float change)743 void DkNoMacs::changeOpacity(float change) {
744 
745 	float newO = (float)windowOpacity() + change;
746 	if (newO > 1) newO = 1.0f;
747 	if (newO < 0.1) newO = 0.1f;
748 	setWindowOpacity(newO);
749 }
750 
animateOpacityDown()751 void DkNoMacs::animateOpacityDown() {
752 
753 	float newO = (float)windowOpacity() - 0.03f;
754 
755 	if (newO < 0.3f) {
756 		setWindowOpacity(0.3f);
757 		return;
758 	}
759 
760 	setWindowOpacity(newO);
761 	QTimer::singleShot(20, this, SLOT(animateOpacityDown()));
762 }
763 
animateOpacityUp()764 void DkNoMacs::animateOpacityUp() {
765 
766 	float newO = (float)windowOpacity() + 0.03f;
767 
768 	if (newO > 1.0f) {
769 		setWindowOpacity(1.0f);
770 		return;
771 	}
772 
773 	setWindowOpacity(newO);
774 	QTimer::singleShot(20, this, SLOT(animateOpacityUp()));
775 }
776 
777 // >DIR: diem - why can't we put it in mViewport?
animateChangeOpacity()778 void DkNoMacs::animateChangeOpacity() {
779 
780 	float newO = (float)windowOpacity();
781 
782 	if (newO >= 1.0f)
783 		animateOpacityDown();
784 	else
785 		animateOpacityUp();
786 }
787 
lockWindow(bool lock)788 void DkNoMacs::lockWindow(bool lock) {
789 
790 
791 #ifdef Q_OS_WIN
792 
793 	qDebug() << "locking: " << lock;
794 
795 	if (lock) {
796 		//setAttribute(Qt::WA_TransparentForMouseEvents);
797 		HWND hwnd = (HWND) winId(); // get handle of the widget
798 		LONG styles = GetWindowLong(hwnd, GWL_EXSTYLE);
799 		SetWindowLong(hwnd, GWL_EXSTYLE, styles | WS_EX_TRANSPARENT);
800 		SetWindowPos((HWND)this->winId(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
801 
802 		auto keyStr = DkActionManager::instance().action(DkActionManager::menu_view_lock_window)->shortcut().toString();
803 		getTabWidget()->setInfo(tr("Window Locked\nTo unlock: gain focus (ALT+Tab),\nthen press %1")
804 			.arg(keyStr));
805 	}
806 	else if (lock && windowOpacity() == 1.0f) {
807 		getTabWidget()->setInfo(tr("You should first reduce opacity\n before working through the window."));
808 		DkActionManager::instance().action(DkActionManager::menu_view_lock_window)->setChecked(false);
809 	}
810 	else {
811 		qDebug() << "deactivating...";
812 		HWND hwnd = (HWND) winId(); // get handle of the widget
813 		LONG styles = GetWindowLong(hwnd, GWL_EXSTYLE);
814 		SetWindowLong(hwnd, GWL_EXSTYLE, styles & ~WS_EX_TRANSPARENT);
815 
816 		SetWindowPos((HWND)this->winId(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
817 	}
818 #else
819 	// TODO: find corresponding command for linux etc
820 
821 	//setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
822 	//show();
823 #endif
824 }
825 
newClientConnected(bool connected)826 void DkNoMacs::newClientConnected(bool connected) {
827 
828 	mOverlaid = false;
829 	// add methods if clients are connected
830 
831 	DkActionManager& am = DkActionManager::instance();
832 	am.action(DkActionManager::menu_sync_view)->setEnabled(connected);
833 	am.action(DkActionManager::menu_sync_pos)->setEnabled(connected);
834 	am.action(DkActionManager::menu_sync_arrange)->setEnabled(connected);
835 
836 }
837 
tcpSetWindowRect(QRect newRect,bool opacity,bool overlaid)838 void DkNoMacs::tcpSetWindowRect(QRect newRect, bool opacity, bool overlaid) {
839 
840 	this->mOverlaid = overlaid;
841 
842 	// we are currently overlaid...
843 	if (!overlaid) {
844 
845 		setGeometry(mOldGeometry);
846 		if (opacity)
847 			animateOpacityUp();
848 		mOldGeometry = geometry();
849 	}
850 	else {
851 
852 #ifdef Q_OS_WIN
853 		showMinimized();
854 		setWindowState(Qt::WindowActive);
855 #else
856 		Qt::WindowFlags flags = windowFlags();
857 		setWindowFlags(Qt::WindowStaysOnTopHint);	// we need this to 'generally' (for all OSs) bring the window to front
858 		setWindowFlags(flags);	// reset flags
859 		showNormal();
860 #endif
861 
862 		mOldGeometry = geometry();
863 
864 		this->move(newRect.topLeft());
865 		this->resize(newRect.size() - (frameGeometry().size() - geometry().size()));
866 
867 		//setGeometry(newRect);
868 		if (opacity)
869 			animateOpacityDown();
870 
871 		//this->setActiveWindow();
872 	}
873 }
874 
tcpSendWindowRect()875 void DkNoMacs::tcpSendWindowRect() {
876 
877 	mOverlaid = !mOverlaid;
878 
879 	qDebug() << "overlaying";
880 	// change my geometry
881 	tcpSetWindowRect(this->frameGeometry(), !mOverlaid, mOverlaid);
882 
883 
884 	auto cm = DkSyncManager::inst().client();
885 	if (cm)
886 		cm->sendPosition(frameGeometry(), mOverlaid);
887 }
888 
tcpSendArrange()889 void DkNoMacs::tcpSendArrange() {
890 
891 	mOverlaid = !mOverlaid;
892 	emit sendArrangeSignal(mOverlaid);
893 }
894 
showExplorer(bool show,bool saveSettings)895 void DkNoMacs::showExplorer(bool show, bool saveSettings) {
896 
897 	if (!show && !mExplorer)
898 		return;
899 
900 	if (!mExplorer) {
901 
902 		// get last location
903 		mExplorer = new DkBrowseExplorer(tr("File Explorer"));
904 		mExplorer->registerAction(DkActionManager::instance().action(DkActionManager::menu_panel_explorer));
905 		mExplorer->setDisplaySettings(&DkSettingsManager::param().app().showExplorer);
906 		addDockWidget(mExplorer->getDockLocationSettings(Qt::LeftDockWidgetArea), mExplorer);
907 
908 		connect(mExplorer, SIGNAL(openFile(const QString&)), getTabWidget(), SLOT(loadFile(const QString&)));
909 		connect(mExplorer, SIGNAL(openDir(const QString&)), getTabWidget(), SLOT(loadDirToTab(const QString&)));
910 		connect(getTabWidget(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mExplorer, SLOT(setCurrentImage(QSharedPointer<DkImageContainerT>)));
911 	}
912 
913 	mExplorer->setVisible(show, saveSettings);
914 
915 	if (getTabWidget()->getCurrentImage() && QFileInfo(getTabWidget()->getCurrentFilePath()).exists()) {
916 		mExplorer->setCurrentPath(getTabWidget()->getCurrentFilePath());
917 	}
918 	else {
919 		QStringList folders = DkSettingsManager::param().global().recentFiles;
920 
921 		if (folders.size() > 0)
922 			mExplorer->setCurrentPath(folders[0]);
923 	}
924 }
925 
showMetaDataDock(bool show,bool saveSettings)926 void DkNoMacs::showMetaDataDock(bool show, bool saveSettings) {
927 
928 	if (!show && !mMetaDataDock)
929 		return;
930 
931 	if (!mMetaDataDock) {
932 
933 		mMetaDataDock = new DkMetaDataDock(tr("Meta Data Info"), this);
934 		mMetaDataDock->registerAction(DkActionManager::instance().action(DkActionManager::menu_panel_metadata_dock));
935 		mMetaDataDock->setDisplaySettings(&DkSettingsManager::param().app().showMetaDataDock);
936 		addDockWidget(mMetaDataDock->getDockLocationSettings(Qt::RightDockWidgetArea), mMetaDataDock);
937 
938 		connect(getTabWidget(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mMetaDataDock, SLOT(setImage(QSharedPointer<DkImageContainerT>)));
939 	}
940 
941 	mMetaDataDock->setVisible(show, saveSettings);
942 
943 	if (getTabWidget()->getCurrentImage())
944 		mMetaDataDock->setImage(getTabWidget()->getCurrentImage());
945 }
946 
showEditDock(bool show,bool saveSettings)947 void DkNoMacs::showEditDock(bool show, bool saveSettings) {
948 
949 	if (!show && !mEditDock)
950 		return;
951 
952 	if (!mEditDock) {
953 
954 		mEditDock = new DkEditDock(tr("Edit Image"), this);
955 		mEditDock->registerAction(DkActionManager::instance().action(DkActionManager::menu_edit_image));
956 		mEditDock->setDisplaySettings(&DkSettingsManager::param().app().showEditDock);
957 		addDockWidget(mEditDock->getDockLocationSettings(Qt::RightDockWidgetArea), mEditDock);
958 
959 		connect(getTabWidget(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mEditDock, SLOT(setImage(QSharedPointer<DkImageContainerT>)));
960 	}
961 
962 	mEditDock->setVisible(show, saveSettings);
963 
964 	if (getTabWidget()->getCurrentImage())
965 		mEditDock->setImage(getTabWidget()->getCurrentImage());
966 }
967 
showHistoryDock(bool show,bool saveSettings)968 void DkNoMacs::showHistoryDock(bool show, bool saveSettings) {
969 
970 	if (!show && !mHistoryDock)
971 		return;
972 
973 	if (!mHistoryDock) {
974 
975 		mHistoryDock = new DkHistoryDock(tr("History"), this);
976 		mHistoryDock->registerAction(DkActionManager::instance().action(DkActionManager::menu_panel_history));
977 		mHistoryDock->setDisplaySettings(&DkSettingsManager::param().app().showHistoryDock);
978 		addDockWidget(mHistoryDock->getDockLocationSettings(Qt::RightDockWidgetArea), mHistoryDock);
979 
980 		connect(getTabWidget(), SIGNAL(imageUpdatedSignal(QSharedPointer<DkImageContainerT>)), mHistoryDock, SLOT(updateImage(QSharedPointer<DkImageContainerT>)));
981 	}
982 
983 	mHistoryDock->setVisible(show, saveSettings);
984 
985 	if (show && getTabWidget()->getCurrentImage())
986 		mHistoryDock->updateImage(getTabWidget()->getCurrentImage());
987 }
988 
showLogDock(bool show,bool saveSettings)989 void DkNoMacs::showLogDock(bool show, bool saveSettings) {
990 
991 	if (!show && !mLogDock)
992 		return;
993 
994 	if (!mLogDock) {
995 
996 		// get last location
997 		mLogDock = new DkLogDock(tr("Console"), this);
998 		mLogDock->registerAction(DkActionManager::instance().action(DkActionManager::menu_panel_log));
999 		mLogDock->setDisplaySettings(&DkSettingsManager::param().app().showLogDock);
1000 		addDockWidget(mLogDock->getDockLocationSettings(Qt::LeftDockWidgetArea), mLogDock);
1001 	}
1002 
1003 	mLogDock->setVisible(show, saveSettings);
1004 	qInfoClean() << QStringLiteral("Say \"Hi\" to ") << QApplication::applicationName() << " " << QApplication::applicationVersion();
1005 
1006 }
1007 
showThumbsDock(bool show)1008 void DkNoMacs::showThumbsDock(bool show) {
1009 
1010 	if (!show && !mThumbsDock)
1011 		return;
1012 
1013 	// nothing todo here
1014 	if (mThumbsDock && mThumbsDock->isVisible() && show)
1015 		return;
1016 
1017 	if (!getTabWidget()->getViewPort())
1018 		return;
1019 
1020 	auto vp = getTabWidget()->getViewPort();
1021 	int winPos = vp->getController()->getFilePreview()->getWindowPosition();
1022 
1023 	if (winPos != DkFilePreview::cm_pos_dock_hor && winPos != DkFilePreview::cm_pos_dock_ver) {
1024 		if (mThumbsDock) {
1025 
1026 			//DkSettingsManager::param().display().thumbDockSize = qMin(thumbsDock->width(), thumbsDock->height());
1027 			DefaultSettings settings;
1028 			settings.setValue("thumbsDockLocation", QMainWindow::dockWidgetArea(mThumbsDock));
1029 
1030 			mThumbsDock->hide();
1031 			mThumbsDock->setWidget(0);
1032 			mThumbsDock->deleteLater();
1033 			mThumbsDock = 0;
1034 		}
1035 		return;
1036 	}
1037 
1038 	if (!mThumbsDock) {
1039 		mThumbsDock = new DkDockWidget(tr("Thumbnails"), this);
1040 		mThumbsDock->registerAction(DkActionManager::instance().action(DkActionManager::menu_panel_preview));
1041 		mThumbsDock->setDisplaySettings(&DkSettingsManager::param().app().showFilePreview);
1042 		mThumbsDock->setWidget(vp->getController()->getFilePreview());
1043 		addDockWidget(mThumbsDock->getDockLocationSettings(Qt::TopDockWidgetArea), mThumbsDock);
1044 		thumbsDockAreaChanged();
1045 
1046 		QLabel* thumbsTitle = new QLabel(mThumbsDock);
1047 		thumbsTitle->setObjectName("thumbsTitle");
1048 		thumbsTitle->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
1049 		thumbsTitle->setPixmap(QPixmap(":/nomacs/img/widget-separator.png").scaled(QSize(16, 4)));
1050 		thumbsTitle->setFixedHeight(16);
1051 		mThumbsDock->setTitleBarWidget(thumbsTitle);
1052 
1053 		connect(mThumbsDock, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(thumbsDockAreaChanged()));
1054 	}
1055 
1056 	if (show != mThumbsDock->isVisible())
1057 		mThumbsDock->setVisible(show);
1058 }
1059 
thumbsDockAreaChanged()1060 void DkNoMacs::thumbsDockAreaChanged() {
1061 
1062 	Qt::DockWidgetArea area = dockWidgetArea(mThumbsDock);
1063 
1064 	int thumbsOrientation = DkFilePreview::cm_pos_dock_hor;
1065 
1066 	if (area == Qt::LeftDockWidgetArea || area == Qt::RightDockWidgetArea)
1067 		thumbsOrientation = DkFilePreview::cm_pos_dock_ver;
1068 
1069 	if (getTabWidget()->getViewPort())
1070 		getTabWidget()->getViewPort()->getController()->getFilePreview()->setWindowPosition(thumbsOrientation);
1071 
1072 }
1073 
openDir()1074 void DkNoMacs::openDir() {
1075 
1076 	// load system default open dialog
1077 	QString dirName = QFileDialog::getExistingDirectory(
1078 		this,
1079 		tr("Open an Image Directory"),
1080 		getTabWidget()->getCurrentDir(),
1081 		QFileDialog::ShowDirsOnly | DkDialog::fileDialogOptions()
1082 	);
1083 
1084 	if (dirName.isEmpty())
1085 		return;
1086 
1087 	getTabWidget()->loadDirToTab(dirName);
1088 }
1089 
openFile()1090 void DkNoMacs::openFile() {
1091 
1092 	QStringList openFilters = DkSettingsManager::param().app().openFilters;
1093 	openFilters.pop_front();
1094 	openFilters.prepend(tr("All Files (*.*)"));
1095 
1096 	// load system default open dialog
1097 	QStringList filePaths = QFileDialog::getOpenFileNames(
1098 		this,
1099 		tr("Open Image"),
1100 		getTabWidget()->getCurrentDir(),
1101 		openFilters.join(";;"),
1102 		nullptr,
1103 		DkDialog::fileDialogOptions()
1104 	);
1105 
1106 	if (filePaths.isEmpty())
1107 		return;
1108 
1109 	int count = getTabWidget()->getTabs().count(); // Save current count of tabs for setting tab position later
1110 	if (getTabWidget()->getTabs().at(0)->getMode() == DkTabInfo::tab_empty)
1111 		count = 0;
1112 
1113 	QSet<QString> duplicates;
1114 	for (const QString& fp : filePaths) {
1115 		bool dup = false;
1116 
1117 		if (DkSettingsManager::param().global().checkOpenDuplicates) { // Should we check for duplicates?
1118 			for (auto tab : getTabWidget()->getTabs()) {
1119 				if (tab->getFilePath().compare(fp) == 0) {
1120 					duplicates.insert(tab->getFilePath());
1121 					dup = true;
1122 					break;
1123 				}
1124 			}
1125 		}
1126 
1127 		if (!dup) {
1128 
1129 			// > 1: only open in tab if more than one file is opened
1130 			bool newTab = filePaths.size() > 1 | getTabWidget()->getTabs().size() > 1;
1131 			getTabWidget()->loadFile(fp, newTab);
1132 		}
1133 	}
1134 	if (duplicates.count() > 0) { // Show message if at least one duplicate was found
1135 		QString duptext = tr("The following duplicates were not opened:");
1136 		for (auto dup : duplicates) {
1137 			duptext.append("\n" + dup);
1138 		}
1139 		getTabWidget()->getViewPort()->getController()->setInfo(duptext);
1140 	}
1141 
1142 	if (filePaths.count() > duplicates.count()) // Only set the active tab if there is actually something added
1143 		getTabWidget()->setActiveTab(count); // Set first file opened to be the active tab
1144 }
1145 
openFileList()1146 void DkNoMacs::openFileList() {
1147 
1148 	QStringList openFilters;
1149 	openFilters.append(tr("Text file (*.txt)"));
1150 	openFilters.append(tr("All files (*.*)"));
1151 
1152 	// load system default open dialog
1153 	QString fileName = QFileDialog::getOpenFileName(
1154 		this,
1155 		tr("Open Image"),
1156 		getTabWidget()->getCurrentDir(),
1157 		openFilters.join(";;"),
1158 		nullptr,
1159 		DkDialog::fileDialogOptions()
1160 	);
1161 
1162 	if (fileName.isEmpty())
1163 		return;
1164 
1165 	int count = getTabWidget()->getTabs().count();
1166 	if (getTabWidget()->getTabs().at(0)->getMode() == DkTabInfo::tab_empty)
1167 		count = 0;
1168 
1169 	QFile file(fileName);
1170 	if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
1171 		return;
1172 
1173 	while (!file.atEnd()) {
1174 		QString line = file.readLine().simplified();
1175 		if (QFileInfo::exists(line)) {
1176 			getTabWidget()->loadFile(line, true);
1177 		}
1178 	}
1179 
1180 	getTabWidget()->setActiveTab(count);
1181 }
1182 
saveFileList()1183 void DkNoMacs::saveFileList() {
1184 
1185 	QStringList saveFilters;
1186 	saveFilters.append(tr("Text file (*.txt)"));
1187 	saveFilters.append(tr("All files (*.*)"));
1188 
1189 	QString fileName = QFileDialog::getSaveFileName(
1190 		this,
1191 		tr("Save Tab List"),
1192 		getTabWidget()->getCurrentDir(),
1193 		saveFilters.join(";;"),
1194 		nullptr,
1195 		DkDialog::fileDialogOptions()
1196 	);
1197 
1198 	if (fileName.isEmpty())
1199 		return;
1200 
1201 	QFile file(fileName);
1202 	if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate))
1203 		return;
1204 
1205 	for (auto tab : getTabWidget()->getTabs()) {
1206 		file.write(tab->getFilePath().toUtf8()+"\n");
1207 	}
1208 
1209 	file.close();
1210 }
1211 
openQuickLaunch()1212 void DkNoMacs::openQuickLaunch() {
1213 
1214 	DkMainToolBar* tb = DkToolBarManager::inst().defaultToolBar();
1215 
1216 	// create new model
1217 	if (!mQuickAccess) {
1218 		mQuickAccess = new DkQuickAccess(this);
1219 
1220 		// add all actions
1221 		mQuickAccess->addActions(DkActionManager::instance().allActions());
1222 
1223 		connect(mQuickAccess, SIGNAL(loadFileSignal(const QString&)), getTabWidget(), SLOT(loadFile(const QString&)));
1224 	}
1225 
1226 	if (tb)
1227 		connect(tb->getQuickAccess(), SIGNAL(executeSignal(const QString&)), mQuickAccess, SLOT(execute(const QString&)), Qt::UniqueConnection);
1228 
1229 	mQuickAccess->addDirs(DkSettingsManager::param().global().recentFolders);
1230 	mQuickAccess->addFiles(DkSettingsManager::param().global().recentFiles);
1231 
1232 	if (tb && tb->isVisible())
1233 		tb->setQuickAccessModel(mQuickAccess->getModel());
1234 	else {
1235 
1236 		if (!mQuickAccessEdit) {
1237 			mQuickAccessEdit = new DkQuickAccessEdit(this);
1238 			connect(mQuickAccessEdit, SIGNAL(executeSignal(const QString&)), mQuickAccess, SLOT(execute(const QString&)));
1239 		}
1240 
1241 		int right = getTabWidget()->geometry().right();
1242 		mQuickAccessEdit->setFixedWidth(qRound(width()/3.0f));
1243 		mQuickAccessEdit->move(QPoint(right-mQuickAccessEdit->width()-10, qRound(height()*0.25)));
1244 		mQuickAccessEdit->setModel(mQuickAccess->getModel());
1245 		mQuickAccessEdit->show();
1246 	}
1247 }
1248 
loadFile(const QString & filePath)1249 void DkNoMacs::loadFile(const QString& filePath) {
1250 
1251 	if (!getTabWidget())
1252 		return;
1253 
1254 	if (QFileInfo(filePath).isDir())
1255 		getTabWidget()->loadDirToTab(filePath);
1256 	else
1257 		getTabWidget()->loadFile(filePath, false);
1258 
1259 }
1260 
1261 // TODO: move this
renameFile()1262 void DkNoMacs::renameFile() {
1263 
1264 	// TODO:ref move!
1265 	QString filePath = getTabWidget()->getCurrentFilePath();
1266 	QFileInfo file(filePath);
1267 
1268 	if (!file.absoluteDir().exists()) {
1269 		getTabWidget()->setInfo(tr("Sorry, the directory: %1 does not exist\n").arg(file.absolutePath()));
1270 		return;
1271 	}
1272 	if (file.exists() && !file.isWritable()) {
1273 		getTabWidget()->setInfo(tr("Sorry, I can't write to the fileInfo: %1").arg(file.fileName()));
1274 		return;
1275 	}
1276 
1277 	QString fileName = file.fileName();
1278 	int dotIdx = fileName.lastIndexOf(".");
1279 	QString baseName = dotIdx != -1 ? fileName.left(dotIdx) : fileName;
1280 
1281 	bool ok = false;
1282 	QString newFileName = QInputDialog::getText(this, baseName, tr("Rename:"), QLineEdit::Normal, baseName, &ok);
1283 
1284 	if (ok && !newFileName.isEmpty() && newFileName != baseName) {
1285 
1286 		if (!file.suffix().isEmpty())
1287 			newFileName.append("." + file.suffix());
1288 
1289 		qDebug() << "renaming: " << file.fileName() << " -> " << newFileName;
1290 		QFileInfo renamedFile = QFileInfo(file.absoluteDir(), newFileName);
1291 
1292 		// overwrite file?
1293 		// the second comparison is important for windows (case insensitive filenames)
1294 		if (renamedFile.exists() && renamedFile.absoluteFilePath().compare(file.absoluteFilePath(), Qt::CaseInsensitive) != 0) {
1295 
1296 			QMessageBox infoDialog(this);
1297 			infoDialog.setWindowTitle(tr("Question"));
1298 			infoDialog.setText(tr("The fileInfo: %1  already exists.\n Do you want to replace it?").arg(newFileName));
1299 			infoDialog.setIcon(QMessageBox::Question);
1300 			infoDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
1301 			infoDialog.setDefaultButton(QMessageBox::No);
1302 			infoDialog.show();
1303 			int choice = infoDialog.exec();
1304 
1305 			if (choice == QMessageBox::Yes) {
1306 
1307 				QFile oldFile(renamedFile.absoluteFilePath());
1308 				bool removed = oldFile.remove();
1309 
1310 				// tell user that deleting went wrong, and stop the renaming
1311 				if (!removed) {
1312 					getTabWidget()->setInfo(tr("Sorry, I can't delete: %1").arg(file.fileName()));
1313 					return;
1314 				}
1315 			}
1316 			else
1317 				return;		// cancel renaming
1318 		}
1319 
1320 		if (getTabWidget()->getViewPort())
1321 			getTabWidget()->getViewPort()->unloadImage();
1322 
1323 		QFile newFile(file.absoluteFilePath());
1324 		bool renamed = newFile.rename(renamedFile.absoluteFilePath());
1325 
1326 		// tell user that deleting went wrong, and stop the renaming
1327 		if (!renamed)
1328 			getTabWidget()->setInfo(tr("Sorry, I can't rename: %1").arg(file.fileName()));
1329 		else if (DkSettingsManager::param().resources().loadSavedImage == DkSettings::ls_load)
1330 			getTabWidget()->loadFile(renamedFile.absoluteFilePath());
1331 		else if (getTabWidget()->getViewPort())
1332 			getTabWidget()->getViewPort()->loadNextFileFast();
1333 
1334 	}
1335 }
1336 
find(bool filterAction)1337 void DkNoMacs::find(bool filterAction) {
1338 
1339 	if (!getTabWidget()->getCurrentImageLoader())
1340 		return;
1341 
1342 	if (filterAction) {
1343 
1344 		int db = (QObject::sender() == DkActionManager::instance().action(DkActionManager::menu_tools_filter)) ? DkSearchDialog::filter_button : DkSearchDialog::find_button;
1345 
1346 		qDebug() << "default button: " << db;
1347 		DkSearchDialog* searchDialog = new DkSearchDialog(this);
1348 		searchDialog->setDefaultButton(db);
1349 
1350 		searchDialog->setFiles(getTabWidget()->getCurrentImageLoader()->getFileNames());
1351 		searchDialog->setPath(getTabWidget()->getCurrentImageLoader()->getDirPath());
1352 
1353 		connect(searchDialog, SIGNAL(filterSignal(const QString&)), getTabWidget()->getCurrentImageLoader().data(), SLOT(setFolderFilter(const QString&)));
1354 		connect(searchDialog, SIGNAL(loadFileSignal(const QString&)), getTabWidget(), SLOT(loadFile(const QString&)));
1355 		int answer = searchDialog->exec();
1356 
1357 		DkActionManager::instance().action(DkActionManager::menu_tools_filter)->setChecked(answer == DkSearchDialog::filter_button);
1358 	}
1359 	else {
1360 		// remove the filter
1361 		getTabWidget()->getCurrentImageLoader()->setFolderFilter(QString());
1362 	}
1363 
1364 }
1365 
changeSorting(bool change)1366 void DkNoMacs::changeSorting(bool change) {
1367 
1368 	// TODO: move to image loader?!
1369 
1370 	if (change) {
1371 
1372 		QString senderName = QObject::sender()->objectName();
1373 
1374 		if (senderName == "menu_sort_filename")
1375 			DkSettingsManager::param().global().sortMode = DkSettings::sort_filename;
1376 		else if (senderName == "menu_sort_date_created")
1377 			DkSettingsManager::param().global().sortMode = DkSettings::sort_date_created;
1378 		else if (senderName == "menu_sort_date_modified")
1379 			DkSettingsManager::param().global().sortMode = DkSettings::sort_date_modified;
1380 		else if (senderName == "menu_sort_random")
1381 			DkSettingsManager::param().global().sortMode = DkSettings::sort_random;
1382 		else if (senderName == "menu_sort_ascending")
1383 			DkSettingsManager::param().global().sortDir = DkSettings::sort_ascending;
1384 		else if (senderName == "menu_sort_descending")
1385 			DkSettingsManager::param().global().sortDir = DkSettings::sort_descending;
1386 
1387 		if (getTabWidget()->getCurrentImageLoader())
1388 			getTabWidget()->getCurrentImageLoader()->sort();
1389 	}
1390 
1391 	QVector<QAction*> sortActions = DkActionManager::instance().sortActions();
1392 	for (int idx = 0; idx < sortActions.size(); idx++) {
1393 
1394 		if (idx < DkActionManager::menu_sort_ascending)
1395 			sortActions[idx]->setChecked(idx == DkSettingsManager::param().global().sortMode);
1396 		else if (idx >= DkActionManager::menu_sort_ascending)
1397 			sortActions[idx]->setChecked(idx-DkActionManager::menu_sort_ascending == DkSettingsManager::param().global().sortDir);
1398 	}
1399 }
1400 
goTo()1401 void DkNoMacs::goTo() {
1402 
1403 	if (!getTabWidget()->getCurrentImageLoader())
1404 		return;
1405 
1406 	QSharedPointer<DkImageLoader> loader = getTabWidget()->getCurrentImageLoader();
1407 
1408 	bool ok = false;
1409 	int fileIdx = QInputDialog::getInt(this, tr("Go To Image"), tr("Image Index:"), 1, 1, loader->numFiles(), 1, &ok);
1410 
1411 	if (ok)
1412 		loader->loadFileAt(fileIdx-1);
1413 
1414 }
1415 
trainFormat()1416 void DkNoMacs::trainFormat() {
1417 
1418 	if (!mTrainDialog)
1419 		mTrainDialog = new DkTrainDialog(this);
1420 
1421 	mTrainDialog->setCurrentFile(getTabWidget()->getCurrentFilePath());
1422 	bool okPressed = mTrainDialog->exec() != 0;
1423 
1424 	if (okPressed && getTabWidget()->getCurrentImageLoader()) {
1425 		getTabWidget()->getCurrentImageLoader()->load(mTrainDialog->getAcceptedFile());
1426 		getTabWidget()->restart();	// quick & dirty, but currently he messes up the filteredFileList if the same folder was already loaded
1427 	}
1428 
1429 }
1430 
extractImagesFromArchive()1431 void DkNoMacs::extractImagesFromArchive() {
1432 #ifdef WITH_QUAZIP
1433 
1434 	if (!mArchiveExtractionDialog)
1435 		mArchiveExtractionDialog = new DkArchiveExtractionDialog(this);
1436 
1437 	if (getTabWidget()->getCurrentImage()) {
1438 		if (getTabWidget()->getCurrentImage()->isFromZip())
1439 			mArchiveExtractionDialog->setCurrentFile(getTabWidget()->getCurrentImage()->getZipData()->getZipFilePath(), true);
1440 		else
1441 			mArchiveExtractionDialog->setCurrentFile(getTabWidget()->getCurrentFilePath(), false);
1442 	}
1443 	else
1444 		mArchiveExtractionDialog->setCurrentFile(getTabWidget()->getCurrentFilePath(), false);
1445 
1446 	mArchiveExtractionDialog->exec();
1447 #endif
1448 }
1449 
exportTiff()1450 void DkNoMacs::exportTiff() {
1451 
1452 #ifdef WITH_LIBTIFF
1453 	if (!mExportTiffDialog)
1454 		mExportTiffDialog = new DkExportTiffDialog(this);
1455 
1456 	mExportTiffDialog->setFile(getTabWidget()->getCurrentFilePath());
1457 	mExportTiffDialog->exec();
1458 #endif
1459 }
1460 
computeThumbsBatch()1461 void DkNoMacs::computeThumbsBatch() {
1462 
1463 	if (!mForceDialog)
1464 		mForceDialog = new DkForceThumbDialog(this);
1465 	mForceDialog->setWindowTitle(tr("Save Thumbnails"));
1466 	mForceDialog->setDir(getTabWidget()->getCurrentDir());
1467 
1468 	if (!mForceDialog->exec())
1469 		return;
1470 
1471 	if (!mThumbSaver)
1472 		mThumbSaver = new DkThumbsSaver(this);
1473 
1474 	if (getTabWidget()->getCurrentImageLoader())
1475 		mThumbSaver->processDir(getTabWidget()->getCurrentImageLoader()->getImages(), mForceDialog->forceSave());
1476 }
1477 
aboutDialog()1478 void DkNoMacs::aboutDialog() {
1479 
1480 	DkSplashScreen* spScreen = new DkSplashScreen(this, 0);
1481 	spScreen->exec();
1482 	spScreen->deleteLater();
1483 }
1484 
openDocumentation()1485 void DkNoMacs::openDocumentation() {
1486 
1487 	QString url = QString("https://nomacs.org/documentation/");
1488 	QDesktopServices::openUrl(QUrl(url));
1489 }
1490 
bugReport()1491 void DkNoMacs::bugReport() {
1492 
1493 	QString url = "https://github.com/nomacs/nomacs/issues/new";
1494 	QDesktopServices::openUrl(QUrl(url));
1495 }
1496 
cleanSettings()1497 void DkNoMacs::cleanSettings() {
1498 
1499 	DefaultSettings settings;
1500 	settings.clear();
1501 
1502 	readSettings();
1503 	resize(400, 225);
1504 	move(100, 100);
1505 }
1506 
newInstance(const QString & filePath)1507 void DkNoMacs::newInstance(const QString& filePath) {
1508 
1509 	QString exe = QApplication::applicationFilePath();
1510 	QStringList args;
1511 
1512 	QAction* a = static_cast<QAction*>(sender());
1513 
1514 	if (a && a == DkActionManager::instance().action(DkActionManager::menu_file_private_instance))
1515 		args.append("-p");
1516 
1517 	if (filePath.isEmpty())
1518 		args.append(getTabWidget()->getCurrentFilePath());
1519 	else
1520 		args.append(filePath);
1521 
1522 	QProcess::startDetached(exe, args);
1523 }
1524 
tagWall(const std::list<std::string> & code)1525 void tagWall(const std::list<std::string>& code) {
1526 	for (auto line : code)
1527 		std::cout << line << std::endl;
1528 }
1529 
loadRecursion()1530 void DkNoMacs::loadRecursion() {
1531 
1532 	std::list<std::string> code;
1533 	code.push_back("void tagWall(const std::list<std::string>& code) {");
1534 	code.push_back("	for (auto line : code)");
1535 	code.push_back("		std::cout << line << std::endl;");
1536 	code.push_back("}");
1537 	tagWall(code);
1538 
1539 	QImage img = grab().toImage();
1540 
1541 	if (getTabWidget()->getViewPort())
1542 		getTabWidget()->getViewPort()->setImage(img);
1543 }
1544 
1545 // Added by fabian for transfer function:
1546 
restartWithPseudoColor(bool contrast)1547 void DkNoMacs::restartWithPseudoColor(bool contrast) {
1548 
1549 	qDebug() << "contrast: " << contrast;
1550 
1551 	QString exe = QApplication::applicationFilePath();
1552 	QStringList args;
1553 
1554 	if (contrast)
1555 		args << "-m" << "pseudocolor";
1556 	else
1557 		args << "-m" << "default";
1558 	args.append(getTabWidget()->getCurrentFilePath());
1559 
1560 	bool started = mProcess.startDetached(exe, args);
1561 
1562 	// close me if the new instance started
1563 	if (started)
1564 		close();
1565 
1566 	qDebug() << "contrast arguments: " << args;
1567 }
1568 
onWindowLoaded()1569 void DkNoMacs::onWindowLoaded() {
1570 
1571 	DefaultSettings settings;
1572 	bool firstTime = settings.value("AppSettings/firstTime.nomacs.3", true).toBool();
1573 
1574 	if (DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showExplorer))
1575 		showExplorer(true);
1576 	if (DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showMetaDataDock))
1577 		showMetaDataDock(true);
1578 	if (DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showEditDock))
1579 		showEditDock(true);
1580 	if (DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showHistoryDock))
1581 		showHistoryDock(true);
1582 	if (DkDockWidget::testDisplaySettings(DkSettingsManager::param().app().showLogDock))
1583 		showLogDock(true);
1584 
1585 	if (firstTime) {
1586 
1587 		// here are some first time requests
1588 		DkWelcomeDialog* wecomeDialog = new DkWelcomeDialog(this);
1589 		wecomeDialog->exec();
1590 
1591 		settings.setValue("AppSettings/firstTime.nomacs.3", false);
1592 
1593 		if (wecomeDialog->isLanguageChanged()) {
1594 			restartWithTranslationUpdate();
1595 		}
1596 	}
1597 
1598 	checkForUpdate(true);
1599 
1600 	// load settings AFTER everything is initialized
1601 	getTabWidget()->loadSettings();
1602 
1603 // init global taskbar
1604 #ifdef WIN32
1605 	QWinTaskbarButton *button = new QWinTaskbarButton(this);
1606 	button->setWindow(windowHandle());
1607 
1608 	DkGlobalProgress::instance().setProgressBar(button->progress());
1609 #endif
1610 
1611 	toggleDocks(DkSettingsManager::param().app().hideAllPanels);
1612 }
1613 
keyPressEvent(QKeyEvent * event)1614 void DkNoMacs::keyPressEvent(QKeyEvent *event) {
1615 
1616 	if (event->key() == Qt::Key_Alt) {
1617 		mPosGrabKey = QCursor::pos();
1618 		mOtherKeyPressed = false;
1619 	}
1620 	else
1621 		mOtherKeyPressed = true;
1622 
1623 }
1624 
keyReleaseEvent(QKeyEvent * event)1625 void DkNoMacs::keyReleaseEvent(QKeyEvent* event) {
1626 
1627 	if (event->key() == Qt::Key_Alt && !mOtherKeyPressed && (mPosGrabKey - QCursor::pos()).manhattanLength() == 0)
1628 		mMenu->showMenu();
1629 
1630 }
1631 
1632 // >DIR diem: eating shortcut overrides (this allows us to use navigation keys like arrows)
eventFilter(QObject *,QEvent * event)1633 bool DkNoMacs::eventFilter(QObject*, QEvent* event) {
1634 
1635 	if (event->type() == QEvent::ShortcutOverride) {
1636 		QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
1637 
1638 		// consume esc key if fullscreen is on
1639 		if (keyEvent->key() == Qt::Key_Escape && isFullScreen()) {
1640 			exitFullScreen();
1641 			return true;
1642 		}
1643 		else if (keyEvent->key() == Qt::Key_Escape && DkSettingsManager::param().app().closeOnEsc)
1644 			close();
1645 	}
1646 	//if (event->type() == QEvent::Gesture) {
1647 	//	return gestureEvent(static_cast<QGestureEvent*>(event));
1648 	//}
1649 
1650 	return false;
1651 }
1652 
showMenuBar(bool show)1653 void DkNoMacs::showMenuBar(bool show) {
1654 
1655 	DkSettingsManager::param().app().showMenuBar = show;
1656 
1657 	QAction* mp = DkActionManager::instance().action(DkActionManager::menu_panel_menu);
1658 	mp->blockSignals(true);
1659 	mp->setChecked(DkSettingsManager::param().app().showMenuBar);
1660 	mp->blockSignals(false);
1661 
1662 	int tts = (DkSettingsManager::param().app().showMenuBar) ? -1 : 5000;
1663 	mMenu->setTimeToShow(tts);
1664 
1665 	if (show)
1666 		mMenu->showMenu();
1667 	else if (!show)
1668 		mMenu->hide();
1669 }
1670 
openFileWith(QAction * action)1671 void DkNoMacs::openFileWith(QAction* action) {
1672 
1673 	if (!action)
1674 		return;
1675 
1676 	QFileInfo app(action->toolTip());
1677 
1678 	if (!app.exists())
1679 		getTabWidget()->setInfo("Sorry, " % app.fileName() % " does not exist");
1680 
1681 	QStringList args;
1682 
1683 	QString filePath = getTabWidget()->getCurrentFilePath();
1684 
1685 	if (app.fileName() == "explorer.exe")
1686 		args << "/select," << QDir::toNativeSeparators(filePath);
1687 	else if (app.fileName().toLower() == "outlook.exe") {
1688 		args << "/a" << QDir::toNativeSeparators(filePath);
1689 	}
1690 	else
1691 		args << QDir::toNativeSeparators(filePath);
1692 
1693 	//bool started = process.startDetached("psOpenImages.exe", args);	// already deprecated
1694 	bool started = mProcess.startDetached(app.absoluteFilePath(), args);
1695 
1696 	if (started)
1697 		qDebug() << "starting: " << app.fileName() << args;
1698 	else
1699 		getTabWidget()->setInfo("Sorry, I could not start: " % app.absoluteFilePath());
1700 }
1701 
setWindowTitle(QSharedPointer<DkImageContainerT> imgC)1702 void DkNoMacs::setWindowTitle(QSharedPointer<DkImageContainerT> imgC) {
1703 
1704 	if (!imgC) {
1705 		setWindowTitle(QString());
1706 		return;
1707 	}
1708 
1709 	setWindowTitle(imgC->filePath(), imgC->image().size(), imgC->isEdited(), imgC->getTitleAttribute());
1710 }
1711 
setWindowTitle(const QString & filePath,const QSize & size,bool edited,const QString & attr)1712 void DkNoMacs::setWindowTitle(const QString& filePath, const QSize& size, bool edited, const QString& attr) {
1713 
1714 	QString title;
1715 
1716 	if (DkSettingsManager::param().global().extendedTabs && (getTabWidget()->getTabs().count() > 1)) {
1717 		title.append(QString::number(getTabWidget()->getActiveTab() + 1) + "/" + QString::number(getTabWidget()->getTabs().count()) + " - ");
1718 	}
1719 
1720 	QFileInfo fInfo = filePath;
1721 	title.append(QFileInfo(filePath).fileName());
1722 	title = title.remove(".lnk");
1723 
1724 	if (filePath.isEmpty()) {
1725 		title = "nomacs - Image Lounge";
1726 		if (DkSettingsManager::param().app().privateMode)
1727 			title.append(tr(" [Private Mode]"));
1728 	}
1729 
1730 	if (edited)
1731 		title.append("[*]");
1732 
1733 	title.append(" ");
1734 	title.append(attr);	// append some attributes
1735 
1736 	QString attributes;
1737 	auto vp = getTabWidget()->getViewPort();
1738 
1739 	if (!size.isEmpty())
1740 		attributes.sprintf(" - %i x %i", size.width(), size.height());
1741 	if (size.isEmpty() && vp && !vp->getImageSize().isEmpty())
1742 		attributes.sprintf(" - %i x %i", vp->getImage().width(), vp->getImage().height());
1743 	if (DkSettingsManager::param().app().privateMode)
1744 		attributes.append(tr(" [Private Mode]"));
1745 
1746 	QMainWindow::setWindowTitle(title.append(attributes));
1747 	setWindowFilePath(filePath);
1748 	setWindowModified(edited);
1749 
1750 	auto cm = DkSyncManager::inst().client();
1751 	if (cm)
1752 		cm->sendTitle(windowTitle());
1753 
1754 	// TODO: move!
1755 	DkStatusBar* bar = DkStatusBarManager::instance().statusbar();
1756 
1757 	if (((vp && !vp->getController()->getFileInfoLabel()->isVisible()) ||
1758 		!DkSettingsManager::param().slideShow().display.testBit(DkSettings::display_creation_date)) && getTabWidget()->getCurrentImage()) {
1759 
1760 		// create statusbar info
1761 		QSharedPointer<DkMetaDataT> metaData = getTabWidget()->getCurrentImage()->getMetaData();
1762 		QString dateString = metaData->getExifValue("DateTimeOriginal");
1763 		dateString = DkUtils::convertDateString(dateString, fInfo);
1764 		bar->setMessage(dateString, DkStatusBar::status_time_info);
1765 	}
1766 	else
1767 		bar->setMessage("", DkStatusBar::status_time_info);	// hide label
1768 
1769 	if (fInfo.exists())
1770 		bar->setMessage(DkUtils::readableByte((float)fInfo.size()), DkStatusBar::status_filesize_info);
1771 	else
1772 		bar->setMessage("", DkStatusBar::status_filesize_info);
1773 
1774 }
1775 
settingsChanged()1776 void DkNoMacs::settingsChanged() {
1777 
1778 	if (!isFullScreen()) {
1779 		showMenuBar(DkSettingsManager::param().app().showMenuBar);
1780 
1781 		DkToolBarManager::inst().restore();
1782 		DkStatusBarManager::instance().show(DkSettingsManager::param().app().showStatusBar);
1783 	}
1784 }
1785 
checkForUpdate(bool silent)1786 void DkNoMacs::checkForUpdate(bool silent) {
1787 
1788 	// updates are supported on windows only
1789 #ifndef Q_OS_LINUX
1790 
1791 	// do we really need to check for update?
1792 	if (!silent ||
1793 		(!DkSettingsManager::param().sync().updateDialogShown &&
1794 		 QDate::currentDate() > DkSettingsManager::param().sync().lastUpdateCheck &&
1795 		 DkSettingsManager::param().sync().checkForUpdates)) {
1796 
1797 		DkTimer dt;
1798 
1799 		if (!mUpdater) {
1800 			mUpdater = new DkUpdater(this);
1801 			connect(mUpdater, SIGNAL(displayUpdateDialog(QString, QString)), this, SLOT(showUpdateDialog(QString, QString)));
1802 			connect(mUpdater, SIGNAL(showUpdaterMessage(QString, QString)), this, SLOT(showUpdaterMessage(QString, QString)));
1803 		}
1804 		mUpdater->silent = silent;
1805 		mUpdater->checkForUpdates();
1806 		qDebug() << "checking for updates takes: " << dt;
1807 	}
1808 #endif // !#ifndef Q_OS_LINUX
1809 }
1810 
showUpdaterMessage(QString msg,QString title)1811 void DkNoMacs::showUpdaterMessage(QString msg, QString title) {
1812 
1813 	QMessageBox infoDialog(this);
1814 	infoDialog.setWindowTitle(title);
1815 	infoDialog.setIcon(QMessageBox::Information);
1816 	infoDialog.setText(msg);
1817 	infoDialog.show();
1818 
1819 	infoDialog.exec();
1820 }
1821 
showUpdateDialog(QString msg,QString title)1822 void DkNoMacs::showUpdateDialog(QString msg, QString title) {
1823 
1824 	if (mProgressDialog != 0 && !mProgressDialog->isHidden()) { // check if the progress bar is already open
1825 		showUpdaterMessage(tr("Already downloading update"), "update");
1826 		return;
1827 	}
1828 
1829 	DkSettingsManager::param().sync().updateDialogShown = true;
1830 	DkSettingsManager::param().save();
1831 
1832 	if (!mUpdateDialog) {
1833 		mUpdateDialog = new DkUpdateDialog(this);
1834 		mUpdateDialog->setWindowTitle(title);
1835 		mUpdateDialog->upperLabel->setText(msg);
1836 		connect(mUpdateDialog, SIGNAL(startUpdate()), this, SLOT(performUpdate()));
1837 	}
1838 
1839 	mUpdateDialog->exec();
1840 }
1841 
performUpdate()1842 void DkNoMacs::performUpdate() {
1843 
1844 	if (!mUpdater) {
1845 		qDebug() << "WARNING updater is NULL where it should not be.";
1846 		return;
1847 	}
1848 
1849 	mUpdater->performUpdate();
1850 
1851 	if (!mProgressDialog) {
1852 		mProgressDialog = new QProgressDialog(tr("Downloading update..."), tr("Cancel Update"), 0, 100, this);
1853 		mProgressDialog->setWindowIcon(windowIcon());
1854 		connect(mProgressDialog, SIGNAL(canceled()), mUpdater, SLOT(cancelUpdate()));
1855 		connect(mUpdater, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(updateProgress(qint64, qint64)));
1856 		connect(mUpdater, SIGNAL(downloadFinished(QString)), mProgressDialog, SLOT(close()));
1857 		//connect(updater, SIGNAL(downloadFinished(QString)), progressDialog, SLOT(deleteLater()));
1858 		connect(mUpdater, SIGNAL(downloadFinished(QString)), this, SLOT(startSetup(QString)));
1859 	}
1860 	//mProgressDialog->setWindowModality(Qt::ApplicationModal);
1861 
1862 	mProgressDialog->show();
1863 	//progressDialog->raise();
1864 	//progressDialog->activateWindow();
1865 	//mProgressDialog->setWindowModality(Qt::NonModal);
1866 }
1867 
updateProgress(qint64 received,qint64 total)1868 void DkNoMacs::updateProgress(qint64 received, qint64 total) {
1869 	mProgressDialog->setMaximum((int)total);
1870 	mProgressDialog->setValue((int)received);
1871 }
1872 
updateProgressTranslations(qint64 received,qint64 total)1873 void DkNoMacs::updateProgressTranslations(qint64 received, qint64 total) {
1874 	qDebug() << "rec:" << received << "  total:" << total;
1875 	mProgressDialogTranslations->setMaximum((int)total);
1876 	mProgressDialogTranslations->setValue((int)received);
1877 }
1878 
startSetup(QString filePath)1879 void DkNoMacs::startSetup(QString filePath) {
1880 
1881 	qDebug() << "starting setup filePath:" << filePath;
1882 
1883 	if (!QFile::exists(filePath))
1884 		qDebug() << "file does not exist";
1885 	if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
1886 		QString msg = tr("Unable to install new version<br>") +
1887 			tr("You can download the new version from our web page") +
1888 			"<br><a href=\"https://nomacs.org/download/\">www.nomacs.org</a><br>";
1889 		showUpdaterMessage(msg, "update");
1890 	}
1891 }
1892 
updateTranslations()1893 void DkNoMacs::updateTranslations() {
1894 
1895 	if (!mTranslationUpdater) {
1896 		mTranslationUpdater = new DkTranslationUpdater(false, this);
1897 		connect(mTranslationUpdater, SIGNAL(showUpdaterMessage(QString, QString)), this, SLOT(showUpdaterMessage(QString, QString)));
1898 	}
1899 
1900 	if (!mProgressDialogTranslations) {
1901 		mProgressDialogTranslations = new QProgressDialog(tr("Downloading new translations..."), tr("Cancel"), 0, 100, this);
1902 		mProgressDialogTranslations->setWindowIcon(windowIcon());
1903 		connect(mProgressDialogTranslations, SIGNAL(canceled()), mTranslationUpdater, SLOT(cancelUpdate()));
1904 		//connect(progressDialogTranslations, SIGNAL(canceled()), translationUpdater, SLOT(cancelUpdate()));
1905 		connect(mTranslationUpdater, SIGNAL(downloadProgress(qint64, qint64)), this, SLOT(updateProgressTranslations(qint64, qint64)));
1906 		connect(mTranslationUpdater, SIGNAL(downloadFinished()), mProgressDialogTranslations, SLOT(close()));
1907 	}
1908 	//mProgressDialogTranslations->setWindowModality(Qt::ApplicationModal);
1909 
1910 	mProgressDialogTranslations->show();
1911 	//progressDialog->raise();
1912 	//progressDialog->activateWindow();
1913 	//mProgressDialogTranslations->setWindowModality(Qt::NonModal);
1914 
1915 	mTranslationUpdater->checkForUpdates();
1916 }
1917 
restartWithTranslationUpdate()1918 void DkNoMacs::restartWithTranslationUpdate() {
1919 
1920 	if (!mTranslationUpdater) {
1921 		mTranslationUpdater = new DkTranslationUpdater(false, this);
1922 		connect(mTranslationUpdater, SIGNAL(showUpdaterMessage(QString, QString)), this, SLOT(showUpdaterMessage(QString, QString)));
1923 	}
1924 
1925 	mTranslationUpdater->silent = true;
1926 	connect(mTranslationUpdater, SIGNAL(downloadFinished()), this, SLOT(restart()));
1927 	updateTranslations();
1928 }
1929 
openPluginManager()1930 void DkNoMacs::openPluginManager() {
1931 #ifdef WITH_PLUGINS
1932 
1933 	if (getTabWidget()->getViewPort())
1934 		getTabWidget()->getViewPort()->getController()->closePlugin(true);
1935 
1936 	if (DkPluginManager::instance().getRunningPlugin()) {
1937 
1938 		QMessageBox infoDialog(this);
1939 		infoDialog.setWindowTitle("Close plugin");
1940 		infoDialog.setIcon(QMessageBox::Information);
1941 		infoDialog.setText("Please close the currently opened plugin first.");
1942 		infoDialog.show();
1943 
1944 		infoDialog.exec();
1945 		return;
1946 	}
1947 
1948 	DkPluginManagerDialog* pluginDialog = new DkPluginManagerDialog(this);
1949 	pluginDialog->exec();
1950 	pluginDialog->deleteLater();
1951 
1952 	DkPluginActionManager* am = DkActionManager::instance().pluginActionManager();
1953 	am->updateMenu();
1954 
1955 #endif // WITH_PLUGINS
1956 }
1957 
1958 // DkNoMacsSync --------------------------------------------------------------------
DkNoMacsSync(QWidget * parent,Qt::WindowFlags flags)1959 DkNoMacsSync::DkNoMacsSync(QWidget *parent, Qt::WindowFlags flags) : DkNoMacs(parent, flags) {
1960 }
1961 
~DkNoMacsSync()1962 DkNoMacsSync::~DkNoMacsSync() {
1963 }
1964 
createActions()1965 void DkNoMacsSync::createActions() {
1966 
1967 	DkNoMacs::createActions();
1968 
1969 	DkActionManager& am = DkActionManager::instance();
1970 
1971 	// sync menu
1972 	connect(am.action(DkActionManager::menu_sync_pos), SIGNAL(triggered()), this, SLOT(tcpSendWindowRect()));
1973 	connect(am.action(DkActionManager::menu_sync_arrange), SIGNAL(triggered()), this, SLOT(tcpSendArrange()));
1974 
1975 	auto cm = DkSyncManager::inst().client();
1976 	assert(cm);
1977 
1978 	// just for local client
1979 	connect(this, SIGNAL(sendArrangeSignal(bool)), cm, SLOT(sendArrangeInstances(bool)));
1980 	connect(this, SIGNAL(sendQuitLocalClientsSignal()), cm, SLOT(sendQuitMessageToPeers()));
1981 
1982 	connect(cm, SIGNAL(clientConnectedSignal(bool)), this, SLOT(newClientConnected(bool)));
1983 	connect(cm, SIGNAL(receivedPosition(QRect, bool, bool)), this, SLOT(tcpSetWindowRect(QRect, bool, bool)));
1984 }
1985 
1986 // mouse events
mouseMoveEvent(QMouseEvent * event)1987 void DkNoMacsSync::mouseMoveEvent(QMouseEvent *event) {
1988 
1989 	int dist = QPoint(event->pos()-mMousePos).manhattanLength();
1990 
1991 	// create drag sync action
1992 	if (event->buttons() == Qt::LeftButton && dist > QApplication::startDragDistance() &&
1993 		event->modifiers() == (Qt::ControlModifier | Qt::AltModifier)) {
1994 
1995 			qDebug() << "generating a drag event...";
1996 
1997 			auto cm = dynamic_cast<DkLocalClientManager*>(DkSyncManager::inst().client());
1998 			assert(cm);
1999 			auto md = cm->mimeData();
2000 
2001 			QDrag* drag = new QDrag(this);
2002 			drag->setMimeData(md);
2003 			drag->exec(Qt::CopyAction | Qt::MoveAction);
2004 	}
2005 	else
2006 		DkNoMacs::mouseMoveEvent(event);
2007 
2008 }
2009 
dragEnterEvent(QDragEnterEvent * event)2010 void DkNoMacsSync::dragEnterEvent(QDragEnterEvent *event) {
2011 
2012 	if (event->mimeData()->hasFormat("network/sync-dir")) {
2013 		event->accept();
2014 	}
2015 
2016 	QMainWindow::dragEnterEvent(event);
2017 }
2018 
dropEvent(QDropEvent * event)2019 void DkNoMacsSync::dropEvent(QDropEvent *event) {
2020 
2021 	if (event->source() == this) {
2022 		event->accept();
2023 		return;
2024 	}
2025 
2026 	if (event->mimeData()->hasFormat("network/sync-dir")) {
2027 
2028 		QByteArray connectionData = event->mimeData()->data("network/sync-dir");
2029 		QDataStream dataStream(&connectionData, QIODevice::ReadOnly);
2030 		quint16 peerId;
2031 		dataStream >> peerId;
2032 
2033 		auto cm = DkSyncManager::inst().client();
2034 		cm->synchronizeWithServerPort(peerId);
2035 	}
2036 	else
2037 		QMainWindow::dropEvent(event);
2038 }
2039 
DkNoMacsIpl(QWidget * parent,Qt::WindowFlags flags)2040 DkNoMacsIpl::DkNoMacsIpl(QWidget *parent, Qt::WindowFlags flags) : DkNoMacsSync(parent, flags) {
2041 
2042 	// init members
2043 	DkCentralWidget* cw = new DkCentralWidget(this);
2044 	setCentralWidget(cw);
2045 
2046 	init();
2047 	setAcceptDrops(true);
2048 	setMouseTracking (true);	//receive mouse event everytime
2049 
2050 	DkSettingsManager::param().app().appMode = 0;
2051 	DkSettingsManager::param().app().appMode = DkSettings::mode_default;
2052 }
2053 
2054 // FramelessNoMacs --------------------------------------------------------------------
DkNoMacsFrameless(QWidget * parent,Qt::WindowFlags flags)2055 DkNoMacsFrameless::DkNoMacsFrameless(QWidget *parent, Qt::WindowFlags flags)
2056 	: DkNoMacs(parent, flags) {
2057 
2058 		setObjectName("DkNoMacsFrameless");
2059 		DkSettingsManager::param().app().appMode = DkSettings::mode_frameless;
2060 
2061 		setWindowFlags(Qt::FramelessWindowHint);
2062 		setAttribute(Qt::WA_TranslucentBackground, true);
2063 
2064 		// init members
2065 		DkCentralWidget* cw = new DkCentralWidget(this);
2066 		setCentralWidget(cw);
2067 
2068 		init();
2069 
2070 		setAcceptDrops(true);
2071 		setMouseTracking(true);	//receive mouse event everytime
2072 
2073 		// in frameless, you cannot control if menu is visible...
2074 		DkActionManager& am = DkActionManager::instance();
2075 		am.action(DkActionManager::menu_panel_menu)->setEnabled(false);
2076 		am.action(DkActionManager::menu_panel_statusbar)->setEnabled(false);
2077 		am.action(DkActionManager::menu_panel_statusbar)->setChecked(false);
2078 		am.action(DkActionManager::menu_panel_toolbar)->setChecked(false);
2079 
2080 		mMenu->setTimeToShow(5000);
2081 		mMenu->hide();
2082 
2083 		am.action(DkActionManager::menu_view_frameless)->blockSignals(true);
2084 		am.action(DkActionManager::menu_view_frameless)->setChecked(true);
2085 		am.action(DkActionManager::menu_view_frameless)->blockSignals(false);
2086 
2087 		mDesktop = QApplication::desktop();
2088 
2089 		chooseMonitor(false);
2090 		show();
2091 
2092         connect(mDesktop, SIGNAL(workAreaResized(int)), this, SLOT(chooseMonitor()));
2093 		connect(am.action(DkActionManager::menu_view_monitors), SIGNAL(triggered()), this, SLOT(chooseMonitor()));
2094 
2095 		setObjectName("DkNoMacsFrameless");
2096 		DkStatusBarManager::instance().show(false);	// fix
2097 
2098 		// actions that should always be disabled
2099 		DkActionManager::instance().action(DkActionManager::menu_view_fit_frame)->setEnabled(false);
2100 }
2101 
~DkNoMacsFrameless()2102 DkNoMacsFrameless::~DkNoMacsFrameless() {
2103 }
2104 
createContextMenu()2105 void DkNoMacsFrameless::createContextMenu() {
2106 
2107 	DkNoMacs::createContextMenu();
2108 
2109 	DkActionManager& am = DkActionManager::instance();
2110 	am.contextMenu()->addSeparator();
2111 	am.contextMenu()->addAction(am.action(DkActionManager::menu_file_exit));
2112 }
2113 
chooseMonitor(bool force)2114 void DkNoMacsFrameless::chooseMonitor(bool force) {
2115 
2116 	if (!mDesktop)
2117 		return;
2118 
2119 	QRect screenRect = mDesktop->availableGeometry();
2120 
2121 	// ask the user which monitor to use
2122 	if (mDesktop->screenCount() > 1) {
2123 		DkChooseMonitorDialog* cmd = new DkChooseMonitorDialog(this);
2124 		cmd->setWindowTitle(tr("Choose a Monitor"));
2125 
2126 		if (force || cmd->showDialog()) {
2127 			int answer = cmd->exec();
2128 			if (answer == QDialog::Accepted) {
2129 				screenRect = cmd->screenRect();
2130 			}
2131 		}
2132 		else {
2133 			screenRect = cmd->screenRect();
2134 		}
2135 	}
2136 
2137 	setGeometry(screenRect);
2138 }
2139 
2140 // >DIR diem: eating shortcut overrides
eventFilter(QObject *,QEvent * event)2141 bool DkNoMacsFrameless::eventFilter(QObject* , QEvent* event) {
2142 
2143 	if (event->type() == QEvent::ShortcutOverride) {
2144 		QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
2145 
2146 		// consume esc key if fullscreen is on
2147 		if (keyEvent->key() == Qt::Key_Escape && isFullScreen()) {
2148 			exitFullScreen();
2149 			return true;
2150 		}
2151 		else if (keyEvent->key() == Qt::Key_Escape) {
2152 			close();
2153 			return true;
2154 		}
2155 	}
2156 	//if (event->type() == QEvent::Gesture) {
2157 	//	return gestureEvent(static_cast<QGestureEvent*>(event));
2158 	//}
2159 
2160 	return false;
2161 }
2162 
closeEvent(QCloseEvent * event)2163 void DkNoMacsFrameless::closeEvent(QCloseEvent *event) {
2164 
2165 	// do not save the window size
2166 	if (mSaveSettings)
2167 		DkSettingsManager::param().save();
2168 
2169 	mSaveSettings = false;
2170 
2171 	DkNoMacs::closeEvent(event);
2172 }
2173 
2174 // Transfer function:
2175 
DkNoMacsContrast(QWidget * parent,Qt::WindowFlags flags)2176 DkNoMacsContrast::DkNoMacsContrast(QWidget *parent, Qt::WindowFlags flags)
2177 	: DkNoMacsSync(parent, flags) {
2178 
2179 		setObjectName("DkNoMacsContrast");
2180 
2181 		// init members
2182 		DkCentralWidget* cw = new DkCentralWidget(this);
2183 		setCentralWidget(cw);
2184 
2185 		init();
2186 
2187 		DkToolBarManager::inst().createTransferToolBar();
2188 
2189 		setAcceptDrops(true);
2190 		setMouseTracking (true);	//receive mouse event everytime
2191 
2192 		DkSettingsManager::param().app().appMode = DkSettings::mode_contrast;
2193 		setObjectName("DkNoMacsContrast");
2194 
2195 		// show it...
2196 		show();
2197 
2198 		// TODO: this should be checked but no event should be called
2199 		DkActionManager& am = DkActionManager::instance();
2200 		am.action(DkActionManager::menu_panel_transfertoolbar)->blockSignals(true);
2201 		am.action(DkActionManager::menu_panel_transfertoolbar)->setChecked(true);
2202 		am.action(DkActionManager::menu_panel_transfertoolbar)->blockSignals(false);
2203 }
2204 
~DkNoMacsContrast()2205 DkNoMacsContrast::~DkNoMacsContrast() {
2206 }
2207 
2208 }
2209