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