1 /*
2  *  This file is part of KimageShop^WKrayon^WKrita
3  *
4  *  Copyright (c) 1999 Matthias Elter  <me@kde.org>
5  *                1999 Michael Koch    <koch@kde.org>
6  *                1999 Carsten Pfeiffer <pfeiffer@kde.org>
7  *                2002 Patrick Julien <freak@codepimps.org>
8  *                2003-2011 Boudewijn Rempt <boud@valdyas.org>
9  *                2004 Clarence Dang <dang@kde.org>
10  *                2011 José Luis Vergara <pentalis@gmail.com>
11  *                2017 L. E. Segovia <amy@amyspark.me>
12  *
13  *  This program 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 2 of the License, or
16  *  (at your option) any later version.
17  *
18  *  This program 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, write to the Free Software
25  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  */
27 
28 #include <stdio.h>
29 
30 #include "KisViewManager.h"
31 #include <QPrinter>
32 
33 #include <QAction>
34 #include <QApplication>
35 #include <QBuffer>
36 #include <QByteArray>
37 #include <QStandardPaths>
38 #include <QDesktopWidget>
39 #include <QDesktopServices>
40 #include <QGridLayout>
41 #include <QMainWindow>
42 #include <QMenu>
43 #include <QMenuBar>
44 #include <QMessageBox>
45 #include <QObject>
46 #include <QPoint>
47 #include <QPrintDialog>
48 #include <QRect>
49 #include <QScrollBar>
50 #include <QStatusBar>
51 #include <QToolBar>
52 #include <QUrl>
53 #include <QWidget>
54 
55 #include <kactioncollection.h>
56 #include <klocalizedstring.h>
57 #include <KoResourcePaths.h>
58 #include <kselectaction.h>
59 
60 #include <KoCanvasController.h>
61 #include <KoCompositeOp.h>
62 #include <KoDockRegistry.h>
63 #include <KoProperties.h>
64 #include <KoResourceItemChooserSync.h>
65 #include <KoSelection.h>
66 #include <KoStore.h>
67 #include <KoToolManager.h>
68 #include <KoToolRegistry.h>
69 #include <KoViewConverter.h>
70 #include <KoZoomHandler.h>
71 #include <KoPluginLoader.h>
72 #include <KoDocumentInfo.h>
73 #include <KoColorSpaceRegistry.h>
74 
75 #include "input/kis_input_manager.h"
76 #include "canvas/kis_canvas2.h"
77 #include "canvas/kis_canvas_controller.h"
78 #include "canvas/kis_grid_manager.h"
79 #include "dialogs/kis_dlg_blacklist_cleanup.h"
80 #include "input/kis_input_profile_manager.h"
81 #include "kis_action_manager.h"
82 #include "kis_action.h"
83 #include "kis_canvas_controls_manager.h"
84 #include "kis_canvas_resource_provider.h"
85 #include "kis_composite_progress_proxy.h"
86 #include <KoProgressUpdater.h>
87 #include "kis_config.h"
88 #include "kis_config_notifier.h"
89 #include "kis_control_frame.h"
90 #include "kis_coordinates_converter.h"
91 #include "KisDocument.h"
92 #include "kis_favorite_resource_manager.h"
93 #include "kis_filter_manager.h"
94 #include "kis_group_layer.h"
95 #include <kis_image.h>
96 #include <kis_image_barrier_locker.h>
97 #include "kis_image_manager.h"
98 #include <kis_layer.h>
99 #include "kis_mainwindow_observer.h"
100 #include "kis_mask_manager.h"
101 #include "kis_mimedata.h"
102 #include "kis_mirror_manager.h"
103 #include "kis_node_commands_adapter.h"
104 #include "kis_node.h"
105 #include "kis_node_manager.h"
106 #include "KisDecorationsManager.h"
107 #include <kis_paint_layer.h>
108 #include "kis_paintop_box.h"
109 #include <brushengine/kis_paintop_preset.h>
110 #include "KisPart.h"
111 #include "KisPrintJob.h"
112 #include <KoUpdater.h>
113 #include "KisResourceServerProvider.h"
114 #include "kis_selection.h"
115 #include "kis_selection_mask.h"
116 #include "kis_selection_manager.h"
117 #include "kis_shape_controller.h"
118 #include "kis_shape_layer.h"
119 #include <kis_signal_compressor.h>
120 #include "kis_statusbar.h"
121 #include <KisTemplateCreateDia.h>
122 #include <kis_tool_freehand.h>
123 #include "kis_tooltip_manager.h"
124 #include <kis_undo_adapter.h>
125 #include "KisView.h"
126 #include "kis_zoom_manager.h"
127 #include "widgets/kis_floating_message.h"
128 #include "kis_signal_auto_connection.h"
129 #include "kis_icon_utils.h"
130 #include "kis_guides_manager.h"
131 #include "kis_derived_resources.h"
132 #include "dialogs/kis_delayed_save_dialog.h"
133 #include <KisMainWindow.h>
134 #include "kis_signals_blocker.h"
135 
136 
137 class BlockingUserInputEventFilter : public QObject
138 {
eventFilter(QObject * watched,QEvent * event)139     bool eventFilter(QObject *watched, QEvent *event) override
140     {
141         Q_UNUSED(watched);
142         if(dynamic_cast<QWheelEvent*>(event)
143                 || dynamic_cast<QKeyEvent*>(event)
144                 || dynamic_cast<QMouseEvent*>(event)) {
145             return true;
146         }
147         else {
148             return false;
149         }
150     }
151 };
152 
153 class KisViewManager::KisViewManagerPrivate
154 {
155 
156 public:
157 
KisViewManagerPrivate(KisViewManager * _q,KActionCollection * _actionCollection,QWidget * _q_parent)158     KisViewManagerPrivate(KisViewManager *_q, KActionCollection *_actionCollection, QWidget *_q_parent)
159         : filterManager(_q)
160         , createTemplate(0)
161         , saveIncremental(0)
162         , saveIncrementalBackup(0)
163         , openResourcesDirectory(0)
164         , rotateCanvasRight(0)
165         , rotateCanvasLeft(0)
166         , resetCanvasRotation(0)
167         , wrapAroundAction(0)
168         , levelOfDetailAction(0)
169         , showRulersAction(0)
170         , rulersTrackMouseAction(0)
171         , zoomTo100pct(0)
172         , zoomIn(0)
173         , zoomOut(0)
174         , selectionManager(_q)
175         , statusBar(_q)
176         , controlFrame(_q, _q_parent)
177         , nodeManager(_q)
178         , imageManager(_q)
179         , gridManager(_q)
180         , canvasControlsManager(_q)
181         , paintingAssistantsManager(_q)
182         , actionManager(_q, _actionCollection)
183         , mainWindow(0)
184         , showFloatingMessage(true)
185         , currentImageView(0)
186         , canvasResourceProvider(_q)
187         , canvasResourceManager()
188         , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q)
189         , actionCollection(_actionCollection)
190         , mirrorManager(_q)
191         , inputManager(_q)
192         , actionAuthor(0)
193         , showPixelGrid(0)
194     {
195         KisViewManager::initializeResourceManager(&canvasResourceManager);
196     }
197 
198 public:
199     KisFilterManager filterManager;
200     KisAction *createTemplate;
201     KisAction *createCopy;
202     KisAction *saveIncremental;
203     KisAction *saveIncrementalBackup;
204     KisAction *openResourcesDirectory;
205     KisAction *rotateCanvasRight;
206     KisAction *rotateCanvasLeft;
207     KisAction *resetCanvasRotation;
208     KisAction *wrapAroundAction;
209     KisAction *levelOfDetailAction;
210     KisAction *showRulersAction;
211     KisAction *rulersTrackMouseAction;
212     KisAction *zoomTo100pct;
213     KisAction *zoomIn;
214     KisAction *zoomOut;
215     KisAction *softProof;
216     KisAction *gamutCheck;
217     KisAction *toggleFgBg;
218     KisAction *resetFgBg;
219 
220     KisSelectionManager selectionManager;
221     KisGuidesManager guidesManager;
222     KisStatusBar statusBar;
223     QPointer<KoUpdater> persistentImageProgressUpdater;
224 
225     QScopedPointer<KoProgressUpdater> persistentUnthreadedProgressUpdaterRouter;
226     QPointer<KoUpdater> persistentUnthreadedProgressUpdater;
227 
228     KisControlFrame controlFrame;
229     KisNodeManager nodeManager;
230     KisImageManager imageManager;
231     KisGridManager gridManager;
232     KisCanvasControlsManager canvasControlsManager;
233     KisDecorationsManager paintingAssistantsManager;
234     BlockingUserInputEventFilter blockingEventFilter;
235     KisActionManager actionManager;
236     QMainWindow* mainWindow;
237     QPointer<KisFloatingMessage> savedFloatingMessage;
238     bool showFloatingMessage;
239     QPointer<KisView> currentImageView;
240     KisCanvasResourceProvider canvasResourceProvider;
241     KoCanvasResourceProvider canvasResourceManager;
242     KisSignalCompressor guiUpdateCompressor;
243     KActionCollection *actionCollection;
244     KisMirrorManager mirrorManager;
245     KisInputManager inputManager;
246 
247     KisSignalAutoConnectionsStore viewConnections;
248     KSelectAction *actionAuthor; // Select action for author profile.
249     KisAction *showPixelGrid;
250 
251     QByteArray canvasState;
252 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
253     QFlags<Qt::WindowState> windowFlags;
254 #endif
255 
256     bool blockUntilOperationsFinishedImpl(KisImageSP image, bool force);
257 };
258 
KisViewManager(QWidget * parent,KActionCollection * _actionCollection)259 KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection)
260     : d(new KisViewManagerPrivate(this, _actionCollection, parent))
261 {
262     d->actionCollection = _actionCollection;
263     d->mainWindow = dynamic_cast<QMainWindow*>(parent);
264     d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager);
265     connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
266 
267     createActions();
268     setupManagers();
269 
270     // These initialization functions must wait until KisViewManager ctor is complete.
271     d->statusBar.setup();
272     d->persistentImageProgressUpdater =
273             d->statusBar.progressUpdater()->startSubtask(1, "", true);
274     // reset state to "completed"
275     d->persistentImageProgressUpdater->setRange(0,100);
276     d->persistentImageProgressUpdater->setValue(100);
277 
278     d->persistentUnthreadedProgressUpdater =
279             d->statusBar.progressUpdater()->startSubtask(1, "", true);
280     // reset state to "completed"
281     d->persistentUnthreadedProgressUpdater->setRange(0,100);
282     d->persistentUnthreadedProgressUpdater->setValue(100);
283 
284     d->persistentUnthreadedProgressUpdaterRouter.reset(
285                 new KoProgressUpdater(d->persistentUnthreadedProgressUpdater,
286                                       KoProgressUpdater::Unthreaded));
287     d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true);
288 
289     d->controlFrame.setup(parent);
290 
291 
292     //Check to draw scrollbars after "Canvas only mode" toggle is created.
293     this->showHideScrollbars();
294 
295     QScopedPointer<KoDummyCanvasController> dummy(new KoDummyCanvasController(actionCollection()));
296     KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data());
297 
298     QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility()));
299 
300     connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
301             d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
302 
303     connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)),
304             d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int)));
305 
306     connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
307             canvasResourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
308 
309     connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
310     connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
311 
312     connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
313     connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction()));
314 
315     KisInputProfileManager::instance()->loadProfiles();
316 
317     KisConfig cfg(true);
318     d->showFloatingMessage = cfg.showCanvasMessages();
319     const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
320     KoColor foreground(Qt::black, cs);
321     d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
322     KoColor background(Qt::white, cs);
323     d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
324 }
325 
326 
~KisViewManager()327 KisViewManager::~KisViewManager()
328 {
329     KisConfig cfg(false);
330     if (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
331         cfg.writeKoColor("LastForeGroundColor",canvasResourceProvider()->fgColor());
332         cfg.writeKoColor("LastBackGroundColor",canvasResourceProvider()->bgColor());
333     }
334 
335     cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength());
336     cfg.writeEntry("CanvasOnlyActive", false); // We never restart in CavnasOnlyMode
337     delete d;
338 }
339 
initializeResourceManager(KoCanvasResourceProvider * resourceManager)340 void KisViewManager::initializeResourceManager(KoCanvasResourceProvider *resourceManager)
341 {
342     resourceManager->addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter));
343     resourceManager->addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter));
344     resourceManager->addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter));
345     resourceManager->addDerivedResourceConverter(toQShared(new KisFlowResourceConverter));
346     resourceManager->addDerivedResourceConverter(toQShared(new KisSizeResourceConverter));
347     resourceManager->addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter));
348     resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdResourceConverter));
349     resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdSupportedResourceConverter));
350     resourceManager->addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter));
351     resourceManager->addDerivedResourceConverter(toQShared(new KisPatternSizeResourceConverter));
352     resourceManager->addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator));
353 }
354 
actionCollection() const355 KActionCollection *KisViewManager::actionCollection() const
356 {
357     return d->actionCollection;
358 }
359 
slotViewAdded(KisView * view)360 void KisViewManager::slotViewAdded(KisView *view)
361 {
362     // WARNING: this slot is called even when a view from another main windows is added!
363     //          Don't expect \p view be a child of this view manager!
364 
365     if (view->viewManager() == this && viewCount() == 0) {
366         d->statusBar.showAllStatusBarItems();
367     }
368 }
369 
slotViewRemoved(KisView * view)370 void KisViewManager::slotViewRemoved(KisView *view)
371 {
372     // WARNING: this slot is called even when a view from another main windows is removed!
373     //          Don't expect \p view be a child of this view manager!
374 
375     if (view->viewManager() == this && viewCount() == 0) {
376         d->statusBar.hideAllStatusBarItems();
377     }
378 
379     KisConfig cfg(false);
380     if (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
381         cfg.writeEntry("LastPreset", canvasResourceProvider()->currentPreset()->name());
382     }
383 }
384 
setCurrentView(KisView * view)385 void KisViewManager::setCurrentView(KisView *view)
386 {
387     bool first = true;
388     if (d->currentImageView) {
389         d->currentImageView->notifyCurrentStateChanged(false);
390 
391         d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
392         first = false;
393         KisDocument* doc = d->currentImageView->document();
394         if (doc) {
395             doc->image()->compositeProgressProxy()->removeProxy(d->persistentImageProgressUpdater);
396             doc->disconnect(this);
397         }
398         d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar);
399         d->viewConnections.clear();
400     }
401 
402 
403     QPointer<KisView> imageView = qobject_cast<KisView*>(view);
404     d->currentImageView = imageView;
405 
406     if (imageView) {
407         d->softProof->setChecked(imageView->softProofing());
408         d->gamutCheck->setChecked(imageView->gamutCheck());
409 
410         // Wait for the async image to have loaded
411         KisDocument* doc = imageView->document();
412 
413         if (KisConfig(true).readEntry<bool>("EnablePositionLabel", false)) {
414             connect(d->currentImageView->canvasController()->proxyObject,
415                     SIGNAL(documentMousePositionChanged(QPointF)),
416                     &d->statusBar,
417                     SLOT(documentMousePositionChanged(QPointF)));
418         }
419 
420         // Restore the last used brush preset, color and background color.
421         if (first) {
422             KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
423             QString defaultPresetName = "basic_tip_default";
424             bool foundTip = false;
425             for (int i=0; i<rserver->resourceCount(); i++) {
426                 KisPaintOpPresetSP resource = rserver->resources().at(i);
427                 if (resource->name().toLower().contains("basic_tip_default")) {
428                     defaultPresetName = resource->name();
429                     foundTip = true;
430                 } else if (foundTip == false && (resource->name().toLower().contains("default") ||
431                                                  resource->filename().toLower().contains("default"))) {
432                     defaultPresetName = resource->name();
433                     foundTip = true;
434                 }
435             }
436             KisConfig cfg(true);
437             QString lastPreset = cfg.readEntry("LastPreset", defaultPresetName);
438             KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset);
439             if (!preset) {
440                 preset = rserver->resourceByName(defaultPresetName);
441             }
442 
443             if (!preset && !rserver->resources().isEmpty()) {
444                 preset = rserver->resources().first();
445             }
446             if (preset) {
447                 paintOpBox()->restoreResource(preset.data());
448                 canvasResourceProvider()->setCurrentCompositeOp(preset->settings()->paintOpCompositeOp());
449             }
450         }
451 
452         KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
453 
454         d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode()));
455         d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
456         d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
457         d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
458 
459         d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
460         d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
461 
462         d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
463         d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
464 
465         d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
466         d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool)));
467         d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
468         d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
469         d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn()));
470         d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut()));
471 
472         d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
473         d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
474 
475         // set up progrress reporting
476         doc->image()->compositeProgressProxy()->addProxy(d->persistentImageProgressUpdater);
477         d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation()));
478 
479         d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool)));
480 
481         imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
482         imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
483 
484         showHideScrollbars();
485     }
486 
487     d->filterManager.setView(imageView);
488     d->selectionManager.setView(imageView);
489     d->guidesManager.setView(imageView);
490     d->nodeManager.setView(imageView);
491     d->imageManager.setView(imageView);
492     d->canvasControlsManager.setView(imageView);
493     d->actionManager.setView(imageView);
494     d->gridManager.setView(imageView);
495     d->statusBar.setView(imageView);
496     d->paintingAssistantsManager.setView(imageView);
497     d->mirrorManager.setView(imageView);
498 
499     if (d->currentImageView) {
500         d->currentImageView->notifyCurrentStateChanged(true);
501         d->currentImageView->canvasController()->activate();
502         d->currentImageView->canvasController()->setFocus();
503         d->currentImageView->zoomManager()->updateCurrentZoomResource();
504 
505         d->viewConnections.addUniqueConnection(
506                     image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),
507                     canvasResourceProvider(), SLOT(slotImageSizeChanged()));
508 
509         d->viewConnections.addUniqueConnection(
510                     image(), SIGNAL(sigResolutionChanged(double,double)),
511                     canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
512 
513         d->viewConnections.addUniqueConnection(
514                     image(), SIGNAL(sigNodeChanged(KisNodeSP)),
515                     this, SLOT(updateGUI()));
516 
517         d->viewConnections.addUniqueConnection(
518                     d->currentImageView->zoomManager()->zoomController(),
519                     SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)),
520                     canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
521 
522     }
523 
524     d->actionManager.updateGUI();
525 
526     canvasResourceProvider()->slotImageSizeChanged();
527     canvasResourceProvider()->slotOnScreenResolutionChanged();
528 
529     Q_EMIT viewChanged();
530 }
531 
532 
zoomController() const533 KoZoomController *KisViewManager::zoomController() const
534 {
535     if (d->currentImageView) {
536         return d->currentImageView->zoomController();
537     }
538     return 0;
539 }
540 
image() const541 KisImageWSP KisViewManager::image() const
542 {
543     if (document()) {
544         return document()->image();
545     }
546     return 0;
547 }
548 
canvasResourceProvider()549 KisCanvasResourceProvider * KisViewManager::canvasResourceProvider()
550 {
551     return &d->canvasResourceProvider;
552 }
553 
canvasBase() const554 KisCanvas2 * KisViewManager::canvasBase() const
555 {
556     if (d && d->currentImageView) {
557         return d->currentImageView->canvasBase();
558     }
559     return 0;
560 }
561 
canvas() const562 QWidget* KisViewManager::canvas() const
563 {
564     if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
565         return d->currentImageView->canvasBase()->canvasWidget();
566     }
567     return 0;
568 }
569 
statusBar() const570 KisStatusBar * KisViewManager::statusBar() const
571 {
572     return &d->statusBar;
573 }
574 
paintOpBox() const575 KisPaintopBox* KisViewManager::paintOpBox() const
576 {
577     return d->controlFrame.paintopBox();
578 }
579 
createUnthreadedUpdater(const QString & name)580 QPointer<KoUpdater> KisViewManager::createUnthreadedUpdater(const QString &name)
581 {
582     return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
583 }
584 
createThreadedUpdater(const QString & name)585 QPointer<KoUpdater> KisViewManager::createThreadedUpdater(const QString &name)
586 {
587     return d->statusBar.progressUpdater()->startSubtask(1, name, false);
588 }
589 
selectionManager()590 KisSelectionManager * KisViewManager::selectionManager()
591 {
592     return &d->selectionManager;
593 }
594 
activeNode()595 KisNodeSP KisViewManager::activeNode()
596 {
597     return d->nodeManager.activeNode();
598 }
599 
activeLayer()600 KisLayerSP KisViewManager::activeLayer()
601 {
602     return d->nodeManager.activeLayer();
603 }
604 
activeDevice()605 KisPaintDeviceSP KisViewManager::activeDevice()
606 {
607     return d->nodeManager.activePaintDevice();
608 }
609 
zoomManager()610 KisZoomManager * KisViewManager::zoomManager()
611 {
612     if (d->currentImageView) {
613         return d->currentImageView->zoomManager();
614     }
615     return 0;
616 }
617 
filterManager()618 KisFilterManager * KisViewManager::filterManager()
619 {
620     return &d->filterManager;
621 }
622 
imageManager()623 KisImageManager * KisViewManager::imageManager()
624 {
625     return &d->imageManager;
626 }
627 
inputManager() const628 KisInputManager* KisViewManager::inputManager() const
629 {
630     return &d->inputManager;
631 }
632 
selection()633 KisSelectionSP KisViewManager::selection()
634 {
635     if (d->currentImageView) {
636         return d->currentImageView->selection();
637     }
638     return 0;
639 
640 }
641 
selectionEditable()642 bool KisViewManager::selectionEditable()
643 {
644     KisLayerSP layer = activeLayer();
645     if (layer) {
646         KisSelectionMaskSP mask = layer->selectionMask();
647         if (mask) {
648             return mask->isEditable();
649         }
650     }
651     // global selection is always editable
652     return true;
653 }
654 
undoAdapter()655 KisUndoAdapter * KisViewManager::undoAdapter()
656 {
657     if (!document()) return 0;
658 
659     KisImageWSP image = document()->image();
660     Q_ASSERT(image);
661 
662     return image->undoAdapter();
663 }
664 
createActions()665 void KisViewManager::createActions()
666 {
667     KisConfig cfg(true);
668 
669     d->saveIncremental = actionManager()->createAction("save_incremental_version");
670     connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
671 
672     d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
673     connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
674 
675     connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
676 
677     d->saveIncremental->setEnabled(false);
678     d->saveIncrementalBackup->setEnabled(false);
679 
680     KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger");
681     connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
682 
683     d->createTemplate = actionManager()->createAction("create_template");
684     connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
685 
686     d->createCopy = actionManager()->createAction("create_copy");
687     connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
688 
689     d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
690     connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
691 
692     d->rotateCanvasRight   = actionManager()->createAction("rotate_canvas_right");
693     d->rotateCanvasLeft    = actionManager()->createAction("rotate_canvas_left");
694     d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
695     d->wrapAroundAction    = actionManager()->createAction("wrap_around_mode");
696     d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
697     d->softProof           = actionManager()->createAction("softProof");
698     d->gamutCheck          = actionManager()->createAction("gamutCheck");
699 
700     KisAction *tAction = actionManager()->createAction("showStatusBar");
701     tAction->setChecked(cfg.showStatusBar());
702     connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
703 
704     tAction = actionManager()->createAction("view_show_canvas_only");
705     tAction->setChecked(false);
706     connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
707 
708     //Workaround, by default has the same shortcut as mirrorCanvas
709     KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
710     if (a) {
711         a->setDefaultShortcut(QKeySequence());
712     }
713 
714     a = actionManager()->createAction("edit_blacklist_cleanup");
715     connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup()));
716 
717     actionManager()->createAction("ruler_pixel_multiple2");
718     d->showRulersAction = actionManager()->createAction("view_ruler");
719     d->showRulersAction->setChecked(cfg.showRulers());
720     connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
721 
722     d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
723     d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
724     connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
725 
726     d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
727 
728     d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, "");
729     d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, "");
730 
731     d->actionAuthor  = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this);
732     connect(d->actionAuthor, SIGNAL(triggered(QString)), this, SLOT(changeAuthorProfile(QString)));
733     actionCollection()->addAction("settings_active_author", d->actionAuthor);
734     slotUpdateAuthorProfileActions();
735 
736     d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
737     slotUpdatePixelGridAction();
738 
739     d->toggleFgBg = actionManager()->createAction("toggle_fg_bg");
740     connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg()));
741 
742     d->resetFgBg =  actionManager()->createAction("reset_fg_bg");
743     connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg()));
744 
745 }
746 
setupManagers()747 void KisViewManager::setupManagers()
748 {
749     // Create the managers for filters, selections, layers etc.
750     // XXX: When the currentlayer changes, call updateGUI on all
751     // managers
752 
753     d->filterManager.setup(actionCollection(), actionManager());
754 
755     d->selectionManager.setup(actionManager());
756 
757     d->guidesManager.setup(actionManager());
758 
759     d->nodeManager.setup(actionCollection(), actionManager());
760 
761     d->imageManager.setup(actionManager());
762 
763     d->gridManager.setup(actionManager());
764 
765     d->paintingAssistantsManager.setup(actionManager());
766 
767     d->canvasControlsManager.setup(actionManager());
768 
769     d->mirrorManager.setup(actionCollection());
770 }
771 
updateGUI()772 void KisViewManager::updateGUI()
773 {
774     d->guiUpdateCompressor.start();
775 }
776 
slotBlacklistCleanup()777 void KisViewManager::slotBlacklistCleanup()
778 {
779     KisDlgBlacklistCleanup dialog;
780     dialog.exec();
781 }
782 
nodeManager() const783 KisNodeManager * KisViewManager::nodeManager() const
784 {
785     return &d->nodeManager;
786 }
787 
actionManager() const788 KisActionManager* KisViewManager::actionManager() const
789 {
790     return &d->actionManager;
791 }
792 
gridManager() const793 KisGridManager * KisViewManager::gridManager() const
794 {
795     return &d->gridManager;
796 }
797 
guidesManager() const798 KisGuidesManager * KisViewManager::guidesManager() const
799 {
800     return &d->guidesManager;
801 }
802 
document() const803 KisDocument *KisViewManager::document() const
804 {
805     if (d->currentImageView && d->currentImageView->document()) {
806         return d->currentImageView->document();
807     }
808     return 0;
809 }
810 
viewCount() const811 int KisViewManager::viewCount() const
812 {
813     KisMainWindow *mw  = qobject_cast<KisMainWindow*>(d->mainWindow);
814     if (mw) {
815         return mw->viewCount();
816     }
817     return 0;
818 }
819 
blockUntilOperationsFinishedImpl(KisImageSP image,bool force)820 bool KisViewManager::KisViewManagerPrivate::blockUntilOperationsFinishedImpl(KisImageSP image, bool force)
821 {
822     const int busyWaitDelay = 1000;
823     KisDelayedSaveDialog dialog(image, !force ? KisDelayedSaveDialog::GeneralDialog : KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, mainWindow);
824     dialog.blockIfImageIsBusy();
825 
826     return dialog.result() == QDialog::Accepted;
827 }
828 
829 
blockUntilOperationsFinished(KisImageSP image)830 bool KisViewManager::blockUntilOperationsFinished(KisImageSP image)
831 {
832     return d->blockUntilOperationsFinishedImpl(image, false);
833 }
834 
blockUntilOperationsFinishedForced(KisImageSP image)835 void KisViewManager::blockUntilOperationsFinishedForced(KisImageSP image)
836 {
837     d->blockUntilOperationsFinishedImpl(image, true);
838 }
839 
slotCreateTemplate()840 void KisViewManager::slotCreateTemplate()
841 {
842     if (!document()) return;
843     KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
844 }
845 
slotCreateCopy()846 void KisViewManager::slotCreateCopy()
847 {
848     KisDocument *srcDoc = document();
849     if (!srcDoc) return;
850 
851     if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
852 
853     KisDocument *doc = 0;
854     {
855         KisImageBarrierLocker l(srcDoc->image());
856         doc = srcDoc->clone();
857     }
858     KIS_SAFE_ASSERT_RECOVER_RETURN(doc);
859 
860     QString name = srcDoc->documentInfo()->aboutInfo("name");
861     if (name.isEmpty()) {
862         name = document()->url().toLocalFile();
863     }
864     name = i18n("%1 (Copy)", name);
865     doc->documentInfo()->setAboutInfo("title", name);
866 
867     KisPart::instance()->addDocument(doc);
868     KisMainWindow *mw  = qobject_cast<KisMainWindow*>(d->mainWindow);
869     mw->addViewAndNotifyLoadingCompleted(doc);
870 }
871 
872 
qtMainWindow() const873 QMainWindow* KisViewManager::qtMainWindow() const
874 {
875     if (d->mainWindow)
876         return d->mainWindow;
877 
878     //Fallback for when we have not yet set the main window.
879     QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
880     if(w)
881         return w;
882 
883     return mainWindow();
884 }
885 
setQtMainWindow(QMainWindow * newMainWindow)886 void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
887 {
888     d->mainWindow = newMainWindow;
889 }
890 
slotDocumentSaved()891 void KisViewManager::slotDocumentSaved()
892 {
893     d->saveIncremental->setEnabled(true);
894     d->saveIncrementalBackup->setEnabled(true);
895 }
896 
slotSaveIncremental()897 void KisViewManager::slotSaveIncremental()
898 {
899     if (!document()) return;
900 
901     if (document()->url().isEmpty()) {
902         KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
903         mw->saveDocument(document(), true, false);
904         return;
905     }
906 
907     bool foundVersion;
908     bool fileAlreadyExists;
909     bool isBackup;
910     QString version = "000";
911     QString newVersion;
912     QString letter;
913     QString path = QFileInfo(document()->localFilePath()).canonicalPath();
914     QString fileName = QFileInfo(document()->localFilePath()).fileName();
915 
916     // Find current version filenames
917     // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
918     // Considering our incremental version and backup scheme, format is filename_001~001.ext
919     QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
920     regex.indexIn(fileName);     //  Perform the search
921     QStringList matches = regex.capturedTexts();
922     foundVersion = matches.at(0).isEmpty() ? false : true;
923 
924     // Ensure compatibility with Save Incremental Backup
925     // If this regex is not kept separate, the entire algorithm needs modification;
926     // It's simpler to just add this.
927     QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
928     regexAux.indexIn(fileName);     //  Perform the search
929     QStringList matchesAux = regexAux.capturedTexts();
930     isBackup = matchesAux.at(0).isEmpty() ? false : true;
931 
932     // If the filename has a version, prepare it for incrementation
933     if (foundVersion) {
934         version = matches.at(matches.count() - 1);     //  Look at the last index, we don't care about other matches
935         if (version.contains(QRegExp("[a-z]"))) {
936             version.chop(1);             //  Trim "."
937             letter = version.right(1);   //  Save letter
938             version.chop(1);             //  Trim letter
939         } else {
940             version.chop(1);             //  Trim "."
941         }
942         version.remove(0, 1);            //  Trim "_"
943     } else {
944         // TODO: this will not work with files extensions like jp2
945         // ...else, simply add a version to it so the next loop works
946         QRegExp regex2("[.][a-z]{2,4}$");  //  Heuristic to find file extension
947         regex2.indexIn(fileName);
948         QStringList matches2 = regex2.capturedTexts();
949         QString extensionPlusVersion = matches2.at(0);
950         extensionPlusVersion.prepend(version);
951         extensionPlusVersion.prepend("_");
952         fileName.replace(regex2, extensionPlusVersion);
953     }
954 
955     // Prepare the base for new version filename
956     int intVersion = version.toInt(0);
957     ++intVersion;
958     QString baseNewVersion = QString::number(intVersion);
959     while (baseNewVersion.length() < version.length()) {
960         baseNewVersion.prepend("0");
961     }
962 
963     // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
964     do {
965         newVersion = baseNewVersion;
966         newVersion.prepend("_");
967         if (!letter.isNull()) newVersion.append(letter);
968         if (isBackup) {
969             newVersion.append("~");
970         } else {
971             newVersion.append(".");
972         }
973         fileName.replace(regex, newVersion);
974         fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
975         if (fileAlreadyExists) {
976             if (!letter.isNull()) {
977                 char letterCh = letter.at(0).toLatin1();
978                 ++letterCh;
979                 letter = QString(QChar(letterCh));
980             } else {
981                 letter = 'a';
982             }
983         }
984     } while (fileAlreadyExists && letter != "{");  // x, y, z, {...
985 
986     if (letter == "{") {
987         QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
988         return;
989     }
990     QUrl newUrl = QUrl::fromUserInput(path + '/' + fileName);
991     document()->setFileBatchMode(true);
992     document()->saveAs(newUrl, document()->mimeType(), true);
993     document()->setFileBatchMode(false);
994     KisPart::instance()->addRecentURLToAllMainWindows(newUrl, document()->url());
995 
996     if (mainWindow()) {
997         mainWindow()->updateCaption();
998     }
999 
1000 }
1001 
slotSaveIncrementalBackup()1002 void KisViewManager::slotSaveIncrementalBackup()
1003 {
1004     if (!document()) return;
1005 
1006     if (document()->url().isEmpty()) {
1007         KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1008         mw->saveDocument(document(), true, false);
1009         return;
1010     }
1011 
1012     bool workingOnBackup;
1013     bool fileAlreadyExists;
1014     QString version = "000";
1015     QString newVersion;
1016     QString letter;
1017     QString path = QFileInfo(document()->localFilePath()).canonicalPath();
1018     QString fileName = QFileInfo(document()->localFilePath()).fileName();
1019 
1020     // First, discover if working on a backup file, or a normal file
1021     QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]");
1022     regex.indexIn(fileName);     //  Perform the search
1023     QStringList matches = regex.capturedTexts();
1024     workingOnBackup = matches.at(0).isEmpty() ? false : true;
1025 
1026     if (workingOnBackup) {
1027         // Try to save incremental version (of backup), use letter for alt versions
1028         version = matches.at(matches.count() - 1);     //  Look at the last index, we don't care about other matches
1029         if (version.contains(QRegExp("[a-z]"))) {
1030             version.chop(1);             //  Trim "."
1031             letter = version.right(1);   //  Save letter
1032             version.chop(1);             //  Trim letter
1033         } else {
1034             version.chop(1);             //  Trim "."
1035         }
1036         version.remove(0, 1);            //  Trim "~"
1037 
1038         // Prepare the base for new version filename
1039         int intVersion = version.toInt(0);
1040         ++intVersion;
1041         QString baseNewVersion = QString::number(intVersion);
1042         QString backupFileName = document()->localFilePath();
1043         while (baseNewVersion.length() < version.length()) {
1044             baseNewVersion.prepend("0");
1045         }
1046 
1047         // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1048         do {
1049             newVersion = baseNewVersion;
1050             newVersion.prepend("~");
1051             if (!letter.isNull()) newVersion.append(letter);
1052             newVersion.append(".");
1053             backupFileName.replace(regex, newVersion);
1054             fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1055             if (fileAlreadyExists) {
1056                 if (!letter.isNull()) {
1057                     char letterCh = letter.at(0).toLatin1();
1058                     ++letterCh;
1059                     letter = QString(QChar(letterCh));
1060                 } else {
1061                     letter = 'a';
1062                 }
1063             }
1064         } while (fileAlreadyExists && letter != "{");  // x, y, z, {...
1065 
1066         if (letter == "{") {
1067             QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1068             return;
1069         }
1070         QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1071         document()->saveAs(QUrl::fromUserInput(path + '/' + fileName), document()->mimeType(), true);
1072 
1073         if (mainWindow()) mainWindow()->updateCaption();
1074     }
1075     else { // if NOT working on a backup...
1076         // Navigate directory searching for latest backup version, ignore letters
1077         const quint8 HARDCODED_DIGIT_COUNT = 3;
1078         QString baseNewVersion = "000";
1079         QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1080         QRegExp regex2("[.][a-z]{2,4}$");  //  Heuristic to find file extension
1081         regex2.indexIn(backupFileName);
1082         QStringList matches2 = regex2.capturedTexts();
1083         QString extensionPlusVersion = matches2.at(0);
1084         extensionPlusVersion.prepend(baseNewVersion);
1085         extensionPlusVersion.prepend("~");
1086         backupFileName.replace(regex2, extensionPlusVersion);
1087 
1088         // Save version with 1 number higher than the highest version found ignoring letters
1089         do {
1090             newVersion = baseNewVersion;
1091             newVersion.prepend("~");
1092             newVersion.append(".");
1093             backupFileName.replace(regex, newVersion);
1094             fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1095             if (fileAlreadyExists) {
1096                 // Prepare the base for new version filename, increment by 1
1097                 int intVersion = baseNewVersion.toInt(0);
1098                 ++intVersion;
1099                 baseNewVersion = QString::number(intVersion);
1100                 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1101                     baseNewVersion.prepend("0");
1102                 }
1103             }
1104         } while (fileAlreadyExists);
1105 
1106         // Save both as backup and on current file for interapplication workflow
1107         document()->setFileBatchMode(true);
1108         QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1109         document()->saveAs(QUrl::fromUserInput(path + '/' + fileName), document()->mimeType(), true);
1110         document()->setFileBatchMode(false);
1111 
1112         if (mainWindow()) mainWindow()->updateCaption();
1113     }
1114 }
1115 
disableControls()1116 void KisViewManager::disableControls()
1117 {
1118     // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1119     // this is for Bug 250944
1120     // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1121     // see KisToolFreehand::initPaint() and endPaint()
1122     d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1123     Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1124         child->installEventFilter(&d->blockingEventFilter);
1125     }
1126 }
1127 
enableControls()1128 void KisViewManager::enableControls()
1129 {
1130     d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1131     Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1132         child->removeEventFilter(&d->blockingEventFilter);
1133     }
1134 }
1135 
showStatusBar(bool toggled)1136 void KisViewManager::showStatusBar(bool toggled)
1137 {
1138     KisMainWindow *mw = mainWindow();
1139     if(mw && mw->statusBar()) {
1140         mw->statusBar()->setVisible(toggled);
1141         KisConfig cfg(false);
1142         cfg.setShowStatusBar(toggled);
1143     }
1144 }
1145 
switchCanvasOnly(bool toggled)1146 void KisViewManager::switchCanvasOnly(bool toggled)
1147 {
1148     KisConfig cfg(false);
1149     KisMainWindow *main = mainWindow();
1150 
1151     if(!main) {
1152         dbgUI << "Unable to switch to canvas-only mode, main window not found";
1153         return;
1154     }
1155 
1156     cfg.writeEntry("CanvasOnlyActive", toggled);
1157 
1158     if (toggled) {
1159         d->canvasState = qtMainWindow()->saveState();
1160 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
1161         d->windowFlags = main->windowState();
1162 #endif
1163     }
1164 
1165     if (cfg.hideStatusbarFullscreen()) {
1166         if (main->statusBar()) {
1167             if (!toggled) {
1168                 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1169                     if (main->statusBar()->property("wasvisible").toBool()) {
1170                         main->statusBar()->setVisible(true);
1171                     }
1172                 }
1173             }
1174             else {
1175                 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1176                 main->statusBar()->setVisible(false);
1177             }
1178         }
1179     }
1180 
1181     if (cfg.hideDockersFullscreen()) {
1182         KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1183         if (action) {
1184             action->setCheckable(true);
1185             if (toggled) {
1186                 if (action->isChecked()) {
1187                     cfg.setShowDockers(action->isChecked());
1188                     action->setChecked(false);
1189                 } else {
1190                     cfg.setShowDockers(false);
1191                 }
1192             } else {
1193                 action->setChecked(cfg.showDockers());
1194             }
1195         }
1196     }
1197 
1198     // QT in windows does not return to maximized upon 4th tab in a row
1199     // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1200     if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) {
1201         if(toggled) {
1202             main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1203         } else {
1204             main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1205 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0)
1206             // If window was maximized prior to fullscreen, restore that
1207             if (d->windowFlags & Qt::WindowMaximized) {
1208                 main->setWindowState( main->windowState() | Qt::WindowMaximized);
1209             }
1210 #endif
1211         }
1212     }
1213 
1214     if (cfg.hideMenuFullscreen()) {
1215         if (!toggled) {
1216             if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1217                 if (main->menuBar()->property("wasvisible").toBool()) {
1218                     main->menuBar()->setVisible(true);
1219                 }
1220             }
1221         }
1222         else {
1223             main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1224             main->menuBar()->setVisible(false);
1225         }
1226     }
1227 
1228     if (cfg.hideToolbarFullscreen()) {
1229         QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1230         Q_FOREACH (QToolBar* toolbar, toolBars) {
1231             if (!toggled) {
1232                 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1233                     if (toolbar->property("wasvisible").toBool()) {
1234                         toolbar->setVisible(true);
1235                     }
1236                 }
1237             }
1238             else {
1239                 toolbar->setProperty("wasvisible", toolbar->isVisible());
1240                 toolbar->setVisible(false);
1241             }
1242         }
1243     }
1244 
1245     showHideScrollbars();
1246 
1247     if (toggled) {
1248         // show a fading heads-up display about the shortcut to go back
1249 
1250         showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1251                                  actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon());
1252     }
1253     else {
1254         main->restoreState(d->canvasState);
1255     }
1256 
1257 }
1258 
toggleTabletLogger()1259 void KisViewManager::toggleTabletLogger()
1260 {
1261     d->inputManager.toggleTabletLogger();
1262 }
1263 
openResourcesDirectory()1264 void KisViewManager::openResourcesDirectory()
1265 {
1266     QString dir = KoResourcePaths::locateLocal("data", "");
1267     QDesktopServices::openUrl(QUrl::fromLocalFile(dir));
1268 }
1269 
updateIcons()1270 void KisViewManager::updateIcons()
1271 {
1272     if (mainWindow()) {
1273         QList<QDockWidget*> dockers = mainWindow()->dockWidgets();
1274         Q_FOREACH (QDockWidget* dock, dockers) {
1275             QObjectList objects;
1276             objects.append(dock);
1277             while (!objects.isEmpty()) {
1278                 QObject* object = objects.takeFirst();
1279                 objects.append(object->children());
1280                 KisIconUtils::updateIconCommon(object);
1281             }
1282         }
1283     }
1284 }
initializeStatusBarVisibility()1285 void KisViewManager::initializeStatusBarVisibility()
1286 {
1287     KisConfig cfg(true);
1288     d->mainWindow->statusBar()->setVisible(cfg.showStatusBar());
1289 }
1290 
guiUpdateTimeout()1291 void KisViewManager::guiUpdateTimeout()
1292 {
1293     d->nodeManager.updateGUI();
1294     d->selectionManager.updateGUI();
1295     d->filterManager.updateGUI();
1296     if (zoomManager()) {
1297         zoomManager()->updateGuiAfterDocumentSize();
1298     }
1299     d->gridManager.updateGUI();
1300     d->actionManager.updateGUI();
1301 }
1302 
showFloatingMessage(const QString & message,const QIcon & icon,int timeout,KisFloatingMessage::Priority priority,int alignment)1303 void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1304 {
1305     if (!d->currentImageView) return;
1306     d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1307 
1308     emit floatingMessageRequested(message, icon.name());
1309 }
1310 
mainWindow() const1311 KisMainWindow *KisViewManager::mainWindow() const
1312 {
1313     return qobject_cast<KisMainWindow*>(d->mainWindow);
1314 }
1315 
1316 
showHideScrollbars()1317 void KisViewManager::showHideScrollbars()
1318 {
1319     if (!d->currentImageView) return;
1320     if (!d->currentImageView->canvasController()) return;
1321 
1322     KisConfig cfg(true);
1323     bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1324 
1325     if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1326         d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1327         d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1328     } else {
1329         d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1330         d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1331     }
1332 }
1333 
slotSaveShowRulersState(bool value)1334 void KisViewManager::slotSaveShowRulersState(bool value)
1335 {
1336     KisConfig cfg(false);
1337     cfg.setShowRulers(value);
1338 }
1339 
slotSaveRulersTrackMouseState(bool value)1340 void KisViewManager::slotSaveRulersTrackMouseState(bool value)
1341 {
1342     KisConfig cfg(false);
1343     cfg.setRulersTrackMouse(value);
1344 }
1345 
setShowFloatingMessage(bool show)1346 void KisViewManager::setShowFloatingMessage(bool show)
1347 {
1348     d->showFloatingMessage = show;
1349 }
1350 
changeAuthorProfile(const QString & profileName)1351 void KisViewManager::changeAuthorProfile(const QString &profileName)
1352 {
1353     KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1354     if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1355         appAuthorGroup.writeEntry("active-profile", "");
1356     } else {
1357         appAuthorGroup.writeEntry("active-profile", profileName);
1358     }
1359     appAuthorGroup.sync();
1360     Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1361         doc->documentInfo()->updateParameters();
1362     }
1363 }
1364 
slotUpdateAuthorProfileActions()1365 void KisViewManager::slotUpdateAuthorProfileActions()
1366 {
1367     Q_ASSERT(d->actionAuthor);
1368     if (!d->actionAuthor) {
1369         return;
1370     }
1371     d->actionAuthor->clear();
1372     d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1373 
1374     KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1375     QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1376     QString authorInfo = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/authorinfo/";
1377     QStringList filters = QStringList() << "*.authorinfo";
1378     QDir dir(authorInfo);
1379     Q_FOREACH(QString entry, dir.entryList(filters)) {
1380         int ln = QString(".authorinfo").size();
1381         entry.chop(ln);
1382         if (!profiles.contains(entry)) {
1383             profiles.append(entry);
1384         }
1385     }
1386     Q_FOREACH (const QString &profile , profiles) {
1387         d->actionAuthor->addAction(profile);
1388     }
1389 
1390     KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1391     QString profileName = appAuthorGroup.readEntry("active-profile", "");
1392 
1393     if (profileName == "anonymous" || profileName.isEmpty()) {
1394         d->actionAuthor->setCurrentItem(0);
1395     } else if (profiles.contains(profileName)) {
1396         d->actionAuthor->setCurrentAction(profileName);
1397     }
1398 }
1399 
slotUpdatePixelGridAction()1400 void KisViewManager::slotUpdatePixelGridAction()
1401 {
1402     KIS_SAFE_ASSERT_RECOVER_RETURN(d->showPixelGrid);
1403 
1404     KisSignalsBlocker b(d->showPixelGrid);
1405 
1406     KisConfig cfg(true);
1407     d->showPixelGrid->setChecked(cfg.pixelGridEnabled() && cfg.useOpenGL());
1408 }
1409 
slotActivateTransformTool()1410 void KisViewManager::slotActivateTransformTool()
1411 {
1412     if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1413         KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1414 
1415         QSet<KoShape*> dummy;
1416         // Start a new stroke
1417         tool->deactivate();
1418         tool->activate(KoToolBase::DefaultActivation, dummy);
1419     }
1420 
1421     KoToolManager::instance()->switchToolRequested("KisToolTransform");
1422 }
1423 
slotToggleFgBg()1424 void KisViewManager::slotToggleFgBg()
1425 {
1426 
1427     KoColor newFg = d->canvasResourceManager.backgroundColor();
1428     KoColor newBg = d->canvasResourceManager.foregroundColor();
1429 
1430     /**
1431      * NOTE: Some of color selectors do not differentiate foreground
1432      *       and background colors, so if one wants them to end up
1433      *       being set up to foreground color, it should be set the
1434      *       last.
1435      */
1436     d->canvasResourceManager.setBackgroundColor(newBg);
1437     d->canvasResourceManager.setForegroundColor(newFg);
1438 }
1439 
slotResetFgBg()1440 void KisViewManager::slotResetFgBg()
1441 {
1442     // see a comment in slotToggleFgBg()
1443     d->canvasResourceManager.setBackgroundColor(KoColor(Qt::white, KoColorSpaceRegistry::instance()->rgb8()));
1444     d->canvasResourceManager.setForegroundColor(KoColor(Qt::black, KoColorSpaceRegistry::instance()->rgb8()));
1445 }
1446 
slotResetRotation()1447 void KisViewManager::slotResetRotation()
1448 {
1449     KisCanvasController *canvasController = d->currentImageView->canvasController();
1450     canvasController->resetCanvasRotation();
1451 }
1452 
slotToggleFullscreen()1453 void KisViewManager::slotToggleFullscreen()
1454 {
1455     KisConfig cfg(false);
1456     KisMainWindow *main = mainWindow();
1457     main->viewFullscreen(!main->isFullScreen());
1458     cfg.fullscreenMode(main->isFullScreen());
1459 }
1460