Krita Source Code Documentation
Loading...
Searching...
No Matches
KisViewManager.cpp
Go to the documentation of this file.
1/*
2 * This file is part of KimageShop^WKrayon^WKrita
3 *
4 * SPDX-FileCopyrightText: 1999 Matthias Elter <me@kde.org>
5 * SPDX-FileCopyrightText: 1999 Michael Koch <koch@kde.org>
6 * SPDX-FileCopyrightText: 1999 Carsten Pfeiffer <pfeiffer@kde.org>
7 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
8 * SPDX-FileCopyrightText: 2003-2011 Boudewijn Rempt <boud@valdyas.org>
9 * SPDX-FileCopyrightText: 2004 Clarence Dang <dang@kde.org>
10 * SPDX-FileCopyrightText: 2011 José Luis Vergara <pentalis@gmail.com>
11 * SPDX-FileCopyrightText: 2017 L. E. Segovia <amy@amyspark.me>
12 *
13 * SPDX-License-Identifier: GPL-2.0-or-later
14 */
15
16
17#include "KisViewManager.h"
18#include <QPrinter>
19
20#include <QAction>
21#include <QApplication>
22#include <QBuffer>
23#include <QByteArray>
24#include <QDeadlineTimer>
25#include <QStandardPaths>
26#include <QScreen>
27#include <QDesktopServices>
28#include <QGridLayout>
29#include <QMainWindow>
30#include <QMenu>
31#include <QMenuBar>
32#include <QMessageBox>
33#include <QObject>
34#include <QPoint>
35#include <QPrintDialog>
36#include <QPushButton>
37#include <QScreen>
38#include <QScrollBar>
39#include <QStatusBar>
40#include <QToolBar>
41#include <QUrl>
42#include <QWidget>
43#include <QActionGroup>
44#include <QRegExp>
45
46#include <kactioncollection.h>
47#include <klocalizedstring.h>
48#include <KoResourcePaths.h>
49#include <kselectaction.h>
50
51#include <KoCanvasController.h>
52#include <KoCompositeOp.h>
53#include <KoDockRegistry.h>
55#include <KoFileDialog.h>
56#include <KoProperties.h>
58#include <KoSelection.h>
59#include <KoStore.h>
60#include <KoToolManager.h>
61#include <KoToolRegistry.h>
62#include <KoViewConverter.h>
63#include <KoZoomHandler.h>
64#include <KoPluginLoader.h>
65#include <KoDocumentInfo.h>
67#include <KisResourceLocator.h>
68
70#include "canvas/kis_canvas2.h"
74#include "kis_action_manager.h"
75#include "kis_action.h"
79#include <KoProgressUpdater.h>
80#include "kis_config.h"
81#include "kis_config_notifier.h"
82#include "kis_control_frame.h"
83#include "KisDocument.h"
85#include "kis_filter_manager.h"
86#include "kis_group_layer.h"
87#include <kis_image.h>
88#include "kis_image_manager.h"
89#include <kis_layer.h>
91#include "kis_mask_manager.h"
92#include "kis_mirror_manager.h"
94#include "kis_node.h"
95#include "kis_node_manager.h"
97#include <kis_paint_layer.h>
98#include "kis_paintop_box.h"
100#include "KisPart.h"
101#include <KoUpdater.h>
102#include "kis_selection_mask.h"
104#include "kis_shape_controller.h"
105#include "kis_shape_layer.h"
107#include "kis_statusbar.h"
108#include <KisTemplateCreateDia.h>
109#include <kis_tool_freehand.h>
110#include <kis_undo_adapter.h>
111#include "KisView.h"
112#include "kis_zoom_manager.h"
115#include "kis_icon_utils.h"
116#include "kis_guides_manager.h"
120#include <KisMainWindow.h>
121#include "kis_signals_blocker.h"
122#include "imagesize/imagesize.h"
123#include <KoToolDocker.h>
124#include <KisIdleTasksManager.h>
125#include <KisImageBarrierLock.h>
127#include <kis_selection.h>
128
129#ifdef Q_OS_WIN
131#endif
132
133class BlockingUserInputEventFilter : public QObject
134{
135 bool eventFilter(QObject *watched, QEvent *event) override
136 {
137 Q_UNUSED(watched);
138 if(dynamic_cast<QWheelEvent*>(event)
139 || dynamic_cast<QKeyEvent*>(event)
140 || dynamic_cast<QMouseEvent*>(event)) {
141 return true;
142 }
143 else {
144 return false;
145 }
146 }
147};
148
150{
151
152public:
153
154 KisViewManagerPrivate(KisViewManager *_q, KisKActionCollection *_actionCollection, QWidget *_q_parent)
155 : filterManager(_q)
156 , selectionManager(_q)
157 , statusBar(_q)
158 , controlFrame(_q, _q_parent)
159 , nodeManager(_q)
160 , imageManager(_q)
161 , gridManager(_q)
164 , actionManager(_q, _actionCollection)
167 , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q)
168 , actionCollection(_actionCollection)
169 , mirrorManager(_q)
170 , inputManager(_q)
171 , zoomRotationMessageTimer(Qt::CoarseTimer)
172 {
174 }
175
176public:
190 QActionGroup *wrapAroundAxisActions {nullptr};
195 KisAction *zoomIn {nullptr};
196 KisAction *zoomOut {nullptr};
208
213
214 QScopedPointer<KoProgressUpdater> persistentUnthreadedProgressUpdaterRouter;
216
225 QMainWindow* mainWindow {nullptr};
237
239 KSelectAction *actionAuthor {nullptr}; // Select action for author profile.
241
243 QString zoomMessage;
245
248
274 std::optional<CanvasOnlyOptions> canvasOnlyOptions;
276
278};
279
280KisViewManager::KisViewManager(QWidget *parent, KisKActionCollection *_actionCollection)
281 : d(new KisViewManagerPrivate(this, _actionCollection, parent))
282{
283 d->actionCollection = _actionCollection;
284 d->mainWindow = dynamic_cast<QMainWindow*>(parent);
286 connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
287
290
291 // These initialization functions must wait until KisViewManager ctor is complete.
292 d->statusBar.setup();
294 d->statusBar.progressUpdater()->startSubtask(1, "", true);
295 // reset state to "completed"
296 d->persistentImageProgressUpdater->setRange(0,100);
297 d->persistentImageProgressUpdater->setValue(100);
298
300 d->statusBar.progressUpdater()->startSubtask(1, "", true);
301 // reset state to "completed"
302 d->persistentUnthreadedProgressUpdater->setRange(0,100);
304
308 d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true);
310
311 // just a clumsy way to mark the updater as completed, the subtask will
312 // be automatically deleted on completion...
313 d->persistentUnthreadedProgressUpdaterRouter->startSubtask()->setProgress(100);
314
315 d->controlFrame.setup(parent);
316
317
318 //Check to draw scrollbars after "Canvas only mode" toggle is created.
319 this->showHideScrollbars();
320
322
323 connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
324 d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
325
326 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)),
327 d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*)));
328
329 connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
330 canvasResourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
331
332 connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
333 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
334 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)),
335 d->controlFrame.paintopBox(), SLOT(updatePresetConfig()));
336
337 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
338 connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction()));
339
340 connect(KoToolManager::instance(), SIGNAL(createOpacityResource(bool, KoToolBase*)), SLOT(slotCreateOpacityResource(bool, KoToolBase*)));
341
343
344 KisConfig cfg(true);
347 KoColor foreground(Qt::black, cs);
348 d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
349 KoColor background(Qt::white, cs);
350 d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
351 d->canvasResourceProvider.setColorHistory(cfg.readKoColors("LastColorHistory"));
353
354 // Initialize the old imagesize plugin
355 new ImageSize(this);
356}
357
358
360{
361 KisConfig cfg(false);
362 if (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
363 cfg.writeKoColor("LastForeGroundColor",canvasResourceProvider()->fgColor());
364 cfg.writeKoColor("LastBackGroundColor",canvasResourceProvider()->bgColor());
365 }
366
368 cfg.writeKoColors("LastColorHistory", canvasResourceProvider()->colorHistory());
369 }
370
371 cfg.writeEntry("baseLength", KisResourceItemChooserSync::instance()->baseLength());
372 cfg.writeEntry("CanvasOnlyActive", false); // We never restart in CanvasOnlyMode
373 delete d;
374}
375
377
379{
394
395 resourceManager->addActiveCanvasResourceDependency(
399
400 resourceManager->addActiveCanvasResourceDependency(
404
405 resourceManager->addActiveCanvasResourceDependency(
409
410 KSharedConfigPtr config = KSharedConfig::openConfig();
411 KConfigGroup miscGroup = config->group("Misc");
412 const uint handleRadius = miscGroup.readEntry("HandleRadius", 5);
413 resourceManager->setHandleRadius(handleRadius);
414}
415
420
425
427{
428 // WARNING: this slot is called even when a view from another main windows is added!
429 // Don't expect \p view be a child of this view manager!
430
431 if (view->viewManager() == this && viewCount() == 0) {
433 }
434}
435
437{
438 // WARNING: this slot is called even when a view from another main windows is removed!
439 // Don't expect \p view be a child of this view manager!
440
441 if (view->viewManager() == this && viewCount() == 0) {
443 }
444}
445
447{
448 if (d->currentImageView) {
449 d->currentImageView->notifyCurrentStateChanged(false);
450
451 d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
452 KisDocument* doc = d->currentImageView->document();
453 if (doc) {
455 doc->disconnect(this);
456 }
457 d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar);
460 }
461
462 QPointer<KisView> imageView = qobject_cast<KisView*>(view);
463 d->currentImageView = imageView;
464
465 if (imageView) {
469
470 d->softProof->setChecked(imageView->softProofing());
471 d->gamutCheck->setChecked(imageView->gamutCheck());
472
473 // Wait for the async image to have loaded
474 KisDocument* doc = imageView->document();
475
476 if (KisConfig(true).readEntry<bool>("EnablePositionLabel", false)) {
477 connect(d->currentImageView->canvasController()->proxyObject,
478 SIGNAL(documentMousePositionChanged(QPointF)),
479 &d->statusBar,
480 SLOT(documentMousePositionChanged(QPointF)));
481 }
482
483 KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
484 KIS_ASSERT(canvasController);
485
486 d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode()));
487 d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
488 d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
489 d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
490
491 d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
492 d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
493 d->viewConnections.addUniqueConnection(d->wrapAroundHVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisHV()));
494 d->wrapAroundHVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_BOTH);
495 d->viewConnections.addUniqueConnection(d->wrapAroundHAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisH()));
496 d->wrapAroundHAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_HORIZONTAL);
497 d->viewConnections.addUniqueConnection(d->wrapAroundVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisV()));
498 d->wrapAroundVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_VERTICAL);
499
500 d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
501 d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
502
503 d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
504 d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool)));
505 d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
506 d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
507 d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomIn()));
508 d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomOut()));
509 d->viewConnections.addUniqueConnection(d->zoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFit()));
510 d->viewConnections.addUniqueConnection(d->zoomToFitWidth, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitWidth()));
511 d->viewConnections.addUniqueConnection(d->zoomToFitHeight, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitHeight()));
512 d->viewConnections.addUniqueConnection(d->toggleZoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotToggleZoomToFit()));
513
514 d->viewConnections.addUniqueConnection(d->resetDisplay, SIGNAL(triggered()), imageView->viewManager(), SLOT(slotResetDisplay()));
515
516 d->viewConnections.addConnection(imageView->canvasController(),
518 this,
519 [this](bool value) {
520 QSignalBlocker b(d->viewPrintSize);
521 d->viewPrintSize->setChecked(value);
522 });
523 d->viewPrintSize->setChecked(imageView->canvasController()->usePrintResolutionMode());
524 d->viewConnections.addUniqueConnection(d->viewPrintSize, &KisAction::toggled,
525 imageView->canvasController(), &KisCanvasController::setUsePrintResolutionMode);
526
527 d->viewConnections.addUniqueConnection(imageView->canvasController(),
529 imageView->zoomManager()->zoomAction(),
531 imageView->zoomManager()->zoomAction()->setUsePrintResolutionMode(imageView->canvasController()->usePrintResolutionMode());
532 d->viewConnections.addUniqueConnection(imageView->zoomManager()->zoomAction(),
534 imageView->canvasController(),
536
537 d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
538 d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
539
540 // set up progress reporting
542 d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation()));
543
544 d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool)));
545
546 imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
547 imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
548
550 }
551
552 d->filterManager.setView(imageView);
553 d->selectionManager.setView(imageView);
554 d->guidesManager.setView(imageView);
555 d->nodeManager.setView(imageView);
556 d->imageManager.setView(imageView);
557 d->canvasControlsManager.setView(imageView);
558 d->actionManager.setView(imageView);
559 d->gridManager.setView(imageView);
560 d->statusBar.setView(imageView);
562 d->mirrorManager.setView(imageView);
563
564 if (d->currentImageView) {
565 d->currentImageView->notifyCurrentStateChanged(true);
566 d->currentImageView->canvasController()->activate();
567 d->currentImageView->canvasController()->setFocus();
568
570 image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),
571 canvasResourceProvider(), SLOT(slotImageSizeChanged()));
572
574 image(), SIGNAL(sigResolutionChanged(double,double)),
575 canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
576
578 image(), SIGNAL(sigNodeChanged(KisNodeSP)),
579 this, SLOT(updateGUI()));
580
582 d->currentImageView->canvasController()->proxyObject,
586 }
587
589
592
593 Q_EMIT viewChanged();
594}
595
597{
598 if (document()) {
599 return document()->image();
600 }
601 return 0;
602}
603
608
610{
611 if (d && d->currentImageView) {
612 return d->currentImageView->canvasBase();
613 }
614 return 0;
615}
616
618{
619 if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
620 return d->currentImageView->canvasBase()->canvasWidget();
621 }
622 return 0;
623}
624
626{
627 return &d->statusBar;
628}
629
634
636{
637 return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
638}
639
641{
642 return d->statusBar.progressUpdater()->startSubtask(1, name, false);
643}
644
649
654
659
664
666{
667 if (d->currentImageView) {
668 return d->currentImageView->zoomManager();
669 }
670 return 0;
671}
672
677
682
687
692
697
699{
700 if (d->currentImageView) {
701 return d->currentImageView->selection();
702 }
703 return 0;
704
705}
706
708{
709 KisLayerSP layer = activeLayer();
710 if (layer) {
711 KisSelectionMaskSP mask = layer->selectionMask();
712 if (mask) {
713 return mask->isEditable();
714 }
715 }
716 // global selection is always editable
717 return true;
718}
719
721{
722 if (!document()) return 0;
723
725 Q_ASSERT(image);
726
727 return image->undoAdapter();
728}
729
731{
732 KisConfig cfg(true);
733
734 d->saveIncremental = actionManager()->createAction("save_incremental_version");
735 connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
736
737 d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
738 connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
739
740 connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
741
742 d->saveIncremental->setEnabled(false);
743 d->saveIncrementalBackup->setEnabled(false);
744
745 KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger");
746 connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
747
748 d->createTemplate = actionManager()->createAction("create_template");
749 connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
750
751 d->createCopy = actionManager()->createAction("create_copy");
752 connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
753
754 d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
755 connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
756
757 d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right");
758 d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left");
759 d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
760 d->wrapAroundAction = actionManager()->createAction("wrap_around_mode");
761 d->wrapAroundHVAxisAction = actionManager()->createAction("wrap_around_hv_axis");
762 d->wrapAroundHAxisAction = actionManager()->createAction("wrap_around_h_axis");
763 d->wrapAroundVAxisAction = actionManager()->createAction("wrap_around_v_axis");
764 d->wrapAroundAxisActions = new QActionGroup(this);
768 d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
769 d->softProof = actionManager()->createAction("softProof");
770 d->gamutCheck = actionManager()->createAction("gamutCheck");
771
772 KisAction *tAction = actionManager()->createAction("showStatusBar");
773 tAction->setChecked(cfg.showStatusBar());
774 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
775
776 tAction = actionManager()->createAction("view_show_canvas_only");
777 tAction->setChecked(false);
778 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
779
780 //Workaround, by default has the same shortcut as mirrorCanvas
781 KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
782 if (a) {
783 a->setDefaultShortcut(QKeySequence());
784 }
785
786 actionManager()->createAction("ruler_pixel_multiple2");
787 d->showRulersAction = actionManager()->createAction("view_ruler");
788 d->showRulersAction->setChecked(cfg.showRulers());
789 connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
790
791 d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
792 d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
793 connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
794
795 d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
796
799
800 d->zoomToFit = actionManager()->createAction("zoom_to_fit");
801 d->zoomToFitWidth = actionManager()->createAction("zoom_to_fit_width");
802 d->zoomToFitHeight = actionManager()->createAction("zoom_to_fit_height");
803 d->toggleZoomToFit = actionManager()->createAction("toggle_zoom_to_fit");
804
805 d->resetDisplay = actionManager()->createAction("reset_display");
806
807 d->viewPrintSize = actionManager()->createAction("view_print_size");
808
809 d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this);
810 connect(d->actionAuthor, SIGNAL(textTriggered(QString)), this, SLOT(changeAuthorProfile(QString)));
811 actionCollection()->addAction("settings_active_author", d->actionAuthor);
813
814 d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
816
817 d->toggleFgBg = actionManager()->createAction("toggle_fg_bg");
818 connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg()));
819
820 d->resetFgBg = actionManager()->createAction("reset_fg_bg");
821 connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg()));
822
823 d->toggleBrushOutline = actionManager()->createAction("toggle_brush_outline");
824 connect(d->toggleBrushOutline, SIGNAL(triggered(bool)), this, SLOT(slotToggleBrushOutline()));
825
826}
827
829{
830 // Create the managers for filters, selections, layers etc.
831 // XXX: When the current layer changes, call updateGUI on all
832 // managers
833
835
837
839
841
843
845
847
849
851}
852
857
859{
860 return &d->nodeManager;
861}
862
867
869{
870 return &d->gridManager;
871}
872
877
879{
880 if (d->currentImageView && d->currentImageView->document()) {
881 return d->currentImageView->document();
882 }
883 return 0;
884}
885
887{
888 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
889 if (mw) {
890 return mw->viewCount();
891 }
892 return 0;
893}
894
896{
897 const int busyWaitDelay = 1000;
899 dialog.blockIfImageIsBusy();
900
901 return dialog.result() == QDialog::Accepted;
902}
903
904
909
914
916{
917 if (!document()) return;
918 KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
919}
920
922{
923 KisDocument *srcDoc = document();
924 if (!srcDoc) return;
925
926 if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
927
928 KisDocument *doc = 0;
929 {
930 KisImageReadOnlyBarrierLock l(srcDoc->image());
931 doc = srcDoc->clone(true);
932 }
934
935 QString name = srcDoc->documentInfo()->aboutInfo("name");
936 if (name.isEmpty()) {
937 name = document()->path();
938 }
939 name = i18n("%1 (Copy)", name);
940 doc->documentInfo()->setAboutInfo("title", name);
941 doc->resetPath();
942
944 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
946}
947
948
950{
951 if (d->mainWindow)
952 return d->mainWindow;
953
954 //Fallback for when we have not yet set the main window.
955 QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
956 if(w)
957 return w;
958
959 return mainWindow();
960}
961
962void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
963{
964 d->mainWindow = newMainWindow;
965}
966
968{
969 d->saveIncremental->setEnabled(true);
970 d->saveIncrementalBackup->setEnabled(true);
971}
972
974{
975#ifdef Q_OS_ANDROID
976 QString path = QFileInfo(document()->localFilePath()).canonicalPath();
977 // if the path is based on a document tree then a directory would be returned. So check if it exists and more
978 // importantly check if we have permissions
979 if (QDir(path).exists()) {
980 return path;
981 } else {
982 KoFileDialog dialog(nullptr, KoFileDialog::ImportDirectory, "OpenDirectory");
983 dialog.setDirectoryUrl(QUrl(document()->localFilePath()));
984 return dialog.filename();
985 }
986#else
987 return QFileInfo(document()->localFilePath()).canonicalPath();
988#endif
989}
990
992{
993 if (!document()) return;
994
995 if (document()->path().isEmpty()) {
996 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
997 mw->saveDocument(document(), true, false);
998 return;
999 }
1000
1001 bool foundVersion;
1002 bool fileAlreadyExists;
1003 bool isBackup;
1004 QString version = "000";
1005 QString newVersion;
1006 QString letter;
1007 QString path = canonicalPath();
1008
1009 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1010
1011 // Find current version filenames
1012 // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
1013 // Considering our incremental version and backup scheme, format is filename_001~001.ext
1014 QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
1015 regex.indexIn(fileName); // Perform the search
1016 QStringList matches = regex.capturedTexts();
1017 foundVersion = matches.at(0).isEmpty() ? false : true;
1018
1019 // Ensure compatibility with Save Incremental Backup
1020 // If this regex is not kept separate, the entire algorithm needs modification;
1021 // It's simpler to just add this.
1022 QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
1023 regexAux.indexIn(fileName); // Perform the search
1024 QStringList matchesAux = regexAux.capturedTexts();
1025 isBackup = matchesAux.at(0).isEmpty() ? false : true;
1026
1027 // If the filename has a version, prepare it for incrementation
1028 if (foundVersion) {
1029 version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
1030#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1031 if (version.contains(QRegExp("[a-z]"))) {
1032#else
1033 if (QRegExp("[a-z]").containedIn(version)) {
1034#endif
1035
1036 version.chop(1); // Trim "."
1037 letter = version.right(1); // Save letter
1038 version.chop(1); // Trim letter
1039 } else {
1040 version.chop(1); // Trim "."
1041 }
1042 version.remove(0, 1); // Trim "_"
1043 } else {
1044 // TODO: this will not work with files extensions like jp2
1045 // ...else, simply add a version to it so the next loop works
1046 QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
1047 regex2.indexIn(fileName);
1048 QStringList matches2 = regex2.capturedTexts();
1049 QString extensionPlusVersion = matches2.at(0);
1050 extensionPlusVersion.prepend(version);
1051 extensionPlusVersion.prepend("_");
1052#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1053 fileName.replace(regex2, extensionPlusVersion);
1054#else
1055 regex2.replaceIn(fileName, extensionPlusVersion);
1056#endif
1057 }
1058
1059 // Prepare the base for new version filename
1060 int intVersion = version.toInt(0);
1061 ++intVersion;
1062 QString baseNewVersion = QString::number(intVersion);
1063 while (baseNewVersion.length() < version.length()) {
1064 baseNewVersion.prepend("0");
1065 }
1066
1067 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1068 do {
1069 newVersion = baseNewVersion;
1070 newVersion.prepend("_");
1071 if (!letter.isNull()) newVersion.append(letter);
1072 if (isBackup) {
1073 newVersion.append("~");
1074 } else {
1075 newVersion.append(".");
1076 }
1077#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1078 fileName.replace(regex, newVersion);
1079#else
1080 regex.replaceIn(fileName, newVersion);
1081#endif
1082 fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
1083 if (fileAlreadyExists) {
1084 if (!letter.isNull()) {
1085 char letterCh = letter.at(0).toLatin1();
1086 ++letterCh;
1087 letter = QString(QChar(letterCh));
1088 } else {
1089 letter = 'a';
1090 }
1091 }
1092 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1093
1094 if (letter == "{") {
1095 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1096 return;
1097 }
1098 QString newFilePath = path + '/' + fileName;
1099 document()->setFileBatchMode(true);
1100 document()->saveAs(newFilePath, document()->mimeType(), true);
1101 document()->setFileBatchMode(false);
1102 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath),
1103 QUrl::fromLocalFile(document()->path()));
1104}
1105
1107{
1108 if (!document()) return;
1109
1110 if (document()->path().isEmpty()) {
1111 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1112 mw->saveDocument(document(), true, false);
1113 return;
1114 }
1115
1116 bool workingOnBackup;
1117 bool fileAlreadyExists;
1118 QString version = "000";
1119 QString newVersion;
1120 QString letter;
1121 QString path = canonicalPath();
1122 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1123
1124 // First, discover if working on a backup file, or a normal file
1125 QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]");
1126 regex.indexIn(fileName); // Perform the search
1127 QStringList matches = regex.capturedTexts();
1128 workingOnBackup = matches.at(0).isEmpty() ? false : true;
1129
1130 if (workingOnBackup) {
1131 // Try to save incremental version (of backup), use letter for alt versions
1132 version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
1133#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1134 if (version.contains(QRegExp("[a-z]"))) {
1135#else
1136 if (QRegExp("[a-z]").containedIn(version)) {
1137#endif
1138 version.chop(1); // Trim "."
1139 letter = version.right(1); // Save letter
1140 version.chop(1); // Trim letter
1141 } else {
1142 version.chop(1); // Trim "."
1143 }
1144 version.remove(0, 1); // Trim "~"
1145
1146 // Prepare the base for new version filename
1147 int intVersion = version.toInt(0);
1148 ++intVersion;
1149 QString baseNewVersion = QString::number(intVersion);
1150 QString backupFileName = document()->localFilePath();
1151 while (baseNewVersion.length() < version.length()) {
1152 baseNewVersion.prepend("0");
1153 }
1154
1155 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1156 do {
1157 newVersion = baseNewVersion;
1158 newVersion.prepend("~");
1159 if (!letter.isNull()) newVersion.append(letter);
1160 newVersion.append(".");
1161#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1162 backupFileName.replace(regex, newVersion);
1163#else
1164 regex.replaceIn(backupFileName, newVersion);
1165#endif
1166 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1167 if (fileAlreadyExists) {
1168 if (!letter.isNull()) {
1169 char letterCh = letter.at(0).toLatin1();
1170 ++letterCh;
1171 letter = QString(QChar(letterCh));
1172 } else {
1173 letter = 'a';
1174 }
1175 }
1176 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1177
1178 if (letter == "{") {
1179 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1180 return;
1181 }
1182 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1183 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1184 }
1185 else { // if NOT working on a backup...
1186 // Navigate directory searching for latest backup version, ignore letters
1187 const quint8 HARDCODED_DIGIT_COUNT = 3;
1188 QString baseNewVersion = "000";
1189 QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1190 QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
1191 regex2.indexIn(backupFileName);
1192 QStringList matches2 = regex2.capturedTexts();
1193 QString extensionPlusVersion = matches2.at(0);
1194 extensionPlusVersion.prepend(baseNewVersion);
1195 extensionPlusVersion.prepend("~");
1196#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1197 backupFileName.replace(regex2, extensionPlusVersion);
1198#else
1199 regex2.replaceIn(backupFileName, extensionPlusVersion);
1200#endif
1201
1202 // Save version with 1 number higher than the highest version found ignoring letters
1203 do {
1204 newVersion = baseNewVersion;
1205 newVersion.prepend("~");
1206 newVersion.append(".");
1207#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1208 backupFileName.replace(regex, newVersion);
1209#else
1210 regex.replaceIn(backupFileName, newVersion);
1211#endif
1212 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1213 if (fileAlreadyExists) {
1214 // Prepare the base for new version filename, increment by 1
1215 int intVersion = baseNewVersion.toInt(0);
1216 ++intVersion;
1217 baseNewVersion = QString::number(intVersion);
1218 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1219 baseNewVersion.prepend("0");
1220 }
1221 }
1222 } while (fileAlreadyExists);
1223
1224 // Save both as backup and on current file for interapplication workflow
1225 document()->setFileBatchMode(true);
1226 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1227 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1228 document()->setFileBatchMode(false);
1229 }
1230}
1231
1233{
1234 // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1235 // this is for Bug 250944
1236 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1237 // see KisToolFreehand::initPaint() and endPaint()
1238 d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1239 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1240 child->installEventFilter(&d->blockingEventFilter);
1241 }
1242}
1243
1245{
1246 d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1247 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1248 child->removeEventFilter(&d->blockingEventFilter);
1249 }
1250}
1251
1253{
1254 KisMainWindow *mw = mainWindow();
1255 if(mw && mw->statusBar()) {
1256 mw->statusBar()->setVisible(toggled);
1257 KisConfig cfg(false);
1258 cfg.setShowStatusBar(toggled);
1259 }
1260}
1261
1263{
1264 d->canvasStateInNormalMode.clear();
1266 d->canvasOnlyOptions = std::nullopt;
1267}
1268
1270{
1271 KisConfig cfg(false);
1273
1274 if(!main) {
1275 dbgUI << "Unable to switch to canvas-only mode, main window not found";
1276 return;
1277 }
1278
1279 cfg.writeEntry("CanvasOnlyActive", toggled);
1280
1282
1283 if (toggled) {
1284 d->canvasStateInNormalMode = qtMainWindow()->saveState();
1285 } else {
1286 d->canvasStateInCanvasOnlyMode = qtMainWindow()->saveState();
1287 d->canvasOnlyOptions = options;
1288 }
1289
1290 const bool toggleFullscreen = (options.hideTitlebarFullscreen && !cfg.fullscreenMode());
1291 const bool useCanvasOffsetCompensation = d->currentImageView &&
1292 d->currentImageView->canvasController() &&
1293 d->currentImageView->isMaximized() &&
1294 !main->canvasDetached();
1295
1296 if (useCanvasOffsetCompensation) {
1297 // The offset is calculated in two steps; this is the first step.
1298 if (toggled) {
1328 QPoint origin;
1329 if (toggleFullscreen) {
1330 // We're windowed, so also capture the position of the window in the screen.
1331 origin = main->geometry().topLeft() - main->screen()->geometry().topLeft();
1332 }
1334 } else {
1335 // Restore the original canvas position. The result is more stable if we pan before showing the UI elements.
1336 d->currentImageView->canvasController()->pan(- d->canvasOnlyOffsetCompensation);
1337 }
1338 }
1339
1340 if (options.hideStatusbarFullscreen) {
1341 if (main->statusBar()) {
1342 if (!toggled) {
1343 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1344 if (main->statusBar()->property("wasvisible").toBool()) {
1345 main->statusBar()->setVisible(true);
1346 }
1347 }
1348 }
1349 else {
1350 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1351 main->statusBar()->setVisible(false);
1352 }
1353 }
1354 }
1355
1356 if (options.hideDockersFullscreen) {
1357 KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1358 if (action) {
1359 action->setCheckable(true);
1360 if (toggled) {
1361 if (action->isChecked()) {
1362 cfg.setShowDockers(action->isChecked());
1363 action->setChecked(false);
1364 } else {
1365 cfg.setShowDockers(false);
1366 }
1367 } else {
1368 action->setChecked(cfg.showDockers());
1369 }
1370 }
1371 }
1372
1373 // QT in windows does not return to maximized upon 4th tab in a row
1374 // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1375 if (toggleFullscreen) {
1376 if(toggled) {
1377 main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1378 } else {
1379 main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1380 }
1381 }
1382
1383 if (options.hideMenuFullscreen) {
1384 if (!toggled) {
1385 if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1386 if (main->menuBar()->property("wasvisible").toBool()) {
1387 main->menuBar()->setVisible(true);
1388 }
1389 }
1390 }
1391 else {
1392 main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1393 main->menuBar()->setVisible(false);
1394 }
1395 }
1396
1397 if (options.hideToolbarFullscreen) {
1398 QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1399 Q_FOREACH (QToolBar* toolbar, toolBars) {
1400 if (!toggled) {
1401 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1402 if (toolbar->property("wasvisible").toBool()) {
1403 toolbar->setVisible(true);
1404 }
1405 }
1406 }
1407 else {
1408 toolbar->setProperty("wasvisible", toolbar->isVisible());
1409 toolbar->setVisible(false);
1410 }
1411 }
1412 }
1413
1415
1416 if (toggled) {
1417 if (!d->canvasStateInCanvasOnlyMode.isEmpty() &&
1419 *d->canvasOnlyOptions == options) {
1420
1430 QTimer::singleShot(0, this, [this] () {
1431 this->mainWindow()->restoreState(d->canvasStateInCanvasOnlyMode);
1432 });
1433 }
1434
1435 // show a fading heads-up display about the shortcut to go back
1436 showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1437 actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon(),
1438 2000,
1440 }
1441 else {
1442 if (!d->canvasStateInNormalMode.isEmpty()) {
1443 main->restoreState(d->canvasStateInNormalMode);
1444 }
1445 }
1446
1447 if (useCanvasOffsetCompensation && toggled) {
1448 const KoZoomMode::Mode mode = d->currentImageView->canvasController()->zoomState().mode;
1449
1450 const bool allowedZoomMode =
1451 (mode == KoZoomMode::ZOOM_CONSTANT) ||
1452 (mode == KoZoomMode::ZOOM_HEIGHT);
1453
1454 if (allowedZoomMode) {
1455 // Defer the pan action until the layout is fully settled in (including the menu bars, etc.).
1456 QTimer::singleShot(0, this, [this] () {
1457 // Compensate by the difference of (after - before) layout.
1459 d->currentImageView->canvasController()->pan(d->canvasOnlyOffsetCompensation);
1460 });
1461 } else {
1462 // Nothing to restore.
1463 d->canvasOnlyOffsetCompensation = QPoint();
1464 }
1465 }
1466}
1467
1472
1474{
1475 QString resourcePath = KisResourceLocator::instance()->resourceLocationBase();
1476#ifdef Q_OS_WIN
1477
1478 QString folderInStandardAppData;
1479 QString folderInPrivateAppData;
1480 KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(folderInStandardAppData, folderInPrivateAppData);
1481
1482 if (!folderInPrivateAppData.isEmpty()) {
1483
1484 const auto pathToDisplay = [](const QString &path) {
1485 // Due to how Unicode word wrapping works, the string does not
1486 // wrap after backslashes in Qt 5.12. We don't want the path to
1487 // become too long, so we add a U+200B ZERO WIDTH SPACE to allow
1488 // wrapping. The downside is that we cannot let the user select
1489 // and copy the path because it now contains invisible unicode
1490 // code points.
1491 // See: https://bugreports.qt.io/browse/QTBUG-80892
1492 return QDir::toNativeSeparators(path).replace(QChar('\\'), QStringLiteral(u"\\\u200B"));
1493 };
1494
1495 QMessageBox mbox(qApp->activeWindow());
1496 mbox.setIcon(QMessageBox::Information);
1497 mbox.setWindowTitle(i18nc("@title:window resource folder", "Open Resource Folder"));
1498 // Similar text is also used in kis_dlg_preferences.cc
1499
1500 mbox.setText(i18nc("@info resource folder",
1501 "<p>You are using the Microsoft Store package version of Krita. "
1502 "Even though Krita can be configured to place resources under the "
1503 "user AppData location, Windows may actually store the files "
1504 "inside a private app location.</p>\n"
1505 "<p>You should check both locations to determine where "
1506 "the files are located.</p>\n"
1507 "<p><b>User AppData</b>:<br/>\n"
1508 "%1</p>\n"
1509 "<p><b>Private app location</b>:<br/>\n"
1510 "%2</p>",
1511 pathToDisplay(folderInStandardAppData),
1512 pathToDisplay(folderInPrivateAppData)
1513 ));
1514 mbox.setTextInteractionFlags(Qt::NoTextInteraction);
1515
1516 const auto *btnOpenUserAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &user AppData"), QMessageBox::AcceptRole);
1517 const auto *btnOpenPrivateAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &private app location"), QMessageBox::AcceptRole);
1518
1519 mbox.addButton(QMessageBox::Close);
1520 mbox.setDefaultButton(QMessageBox::Close);
1521 mbox.exec();
1522
1523 if (mbox.clickedButton() == btnOpenPrivateAppData) {
1524 resourcePath = folderInPrivateAppData;
1525 } else if (mbox.clickedButton() == btnOpenUserAppData) {
1526 // no-op: resourcePath = resourceDir.absolutePath();
1527 } else {
1528 return;
1529 }
1530
1531
1532 }
1533#endif
1534 QDesktopServices::openUrl(QUrl::fromLocalFile(resourcePath));
1535}
1536
1538{
1539 if (mainWindow()) {
1541 Q_FOREACH (QDockWidget* dock, dockers) {
1542 KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
1543 if (titlebar) {
1544 titlebar->updateIcons();
1545 }
1546 if (qobject_cast<KoToolDocker*>(dock)) {
1547 // Tool options widgets icons are updated by KoToolManager
1548 continue;
1549 }
1550 QObjectList objects;
1551 objects.append(dock);
1552 while (!objects.isEmpty()) {
1553 QObject* object = objects.takeFirst();
1554 objects.append(object->children());
1556 }
1557 }
1558 }
1559}
1560
1569
1570void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1571{
1572 if (!d->currentImageView) return;
1573 d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1574
1575 Q_EMIT floatingMessageRequested(message, icon.name());
1576}
1577
1579{
1580 d->zoomMessage = message;
1582}
1583
1589
1591{
1592 int timeoutMsec = 500;
1593
1594 if (d->zoomRotationMessageTimer.hasExpired()) {
1595 messageToClear.clear();
1596 }
1597 d->zoomRotationMessageTimer.setRemainingTime(timeoutMsec);
1598
1599 QString message;
1600 bool haveZoomMessage = !d->zoomMessage.isEmpty();
1601 bool haveRotationMessage = !d->rotationMessage.isEmpty();
1602 if (haveZoomMessage) {
1603 if (haveRotationMessage) {
1604 message = QStringLiteral("%1\n%2").arg(d->zoomMessage, d->rotationMessage);
1605 } else {
1606 message = d->zoomMessage;
1607 }
1608 } else if (haveRotationMessage) {
1609 message = d->rotationMessage;
1610 } else {
1611 return;
1612 }
1613
1614 showFloatingMessage(message, QIcon(), timeoutMsec, KisFloatingMessage::Low, Qt::AlignCenter);
1615}
1616
1618{
1619 return qobject_cast<KisMainWindow*>(d->mainWindow);
1620}
1621
1623{
1624 return mainWindow();
1625}
1626
1627
1629{
1630 if (!d->currentImageView) return;
1631 if (!d->currentImageView->canvasController()) return;
1632
1633 KisConfig cfg(true);
1634 bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1635
1636 if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1637 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1638 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1639 } else {
1640 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1641 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1642 }
1643}
1644
1646{
1647 KisConfig cfg(false);
1648 cfg.setShowRulers(value);
1649}
1650
1656
1658{
1659 d->showFloatingMessage = show;
1660}
1661
1662void KisViewManager::changeAuthorProfile(const QString &profileName)
1663{
1664 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1665 if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1666 appAuthorGroup.writeEntry("active-profile", "");
1667 } else {
1668 appAuthorGroup.writeEntry("active-profile", profileName);
1669 }
1670 appAuthorGroup.sync();
1671 Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1673 }
1674}
1675
1677{
1678 Q_ASSERT(d->actionAuthor);
1679 if (!d->actionAuthor) {
1680 return;
1681 }
1682 d->actionAuthor->clear();
1683 d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1684
1685 KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1686 QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1687 QString authorInfo = KoResourcePaths::getAppDataLocation() + "/authorinfo/";
1688 QStringList filters = QStringList() << "*.authorinfo";
1689 QDir dir(authorInfo);
1690 Q_FOREACH(QString entry, dir.entryList(filters)) {
1691 int ln = QString(".authorinfo").size();
1692 entry.chop(ln);
1693 if (!profiles.contains(entry)) {
1694 profiles.append(entry);
1695 }
1696 }
1697 Q_FOREACH (const QString &profile , profiles) {
1698 d->actionAuthor->addAction(profile);
1699 }
1700
1701 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1702 QString profileName = appAuthorGroup.readEntry("active-profile", "");
1703
1704 if (profileName == "anonymous" || profileName.isEmpty()) {
1705 d->actionAuthor->setCurrentItem(0);
1706 } else if (profiles.contains(profileName)) {
1707 d->actionAuthor->setCurrentAction(profileName);
1708 }
1709}
1710
1720
1722{
1723 if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1724 KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1725
1726 QSet<KoShape*> dummy;
1727 // Start a new stroke
1728 tool->deactivate();
1729 tool->activate(dummy);
1730 }
1731
1732 KoToolManager::instance()->switchToolRequested("KisToolTransform");
1733}
1734
1750
1752{
1753 // see a comment in slotToggleFgBg()
1756}
1757
1759{
1760 KisConfig cfg(true);
1761
1762 OutlineStyle style;
1763
1764 if (cfg.newOutlineStyle() != OUTLINE_NONE) {
1765 style = OUTLINE_NONE;
1767 } else {
1768 style = cfg.lastUsedOutlineStyle();
1770 }
1771
1772 cfg.setNewOutlineStyle(style);
1773
1774 Q_EMIT brushOutlineToggled();
1775}
1776
1778{
1779 KisCanvasController *canvasController = d->currentImageView->canvasController();
1780 canvasController->resetCanvasRotation();
1781}
1782
1784{
1785 KisCanvasController *canvasController = d->currentImageView->canvasController();
1786 canvasController->resetCanvasRotation();
1787 canvasController->mirrorCanvas(false);
1789}
1790
1791void KisViewManager::slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
1792{
1793 if (isOpacityPresetMode) {
1795 }
1796 else {
1798 }
1799}
float value(const T *src, size_t ch)
qreal u
QList< QString > QStringList
@ WRAPAROUND_HORIZONTAL
@ WRAPAROUND_BOTH
@ WRAPAROUND_VERTICAL
unsigned int uint
bool eventFilter(QObject *watched, QEvent *event) override
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
void setView(QPointer< KisView > imageView)
KisAction * createAction(const QString &name)
KisAction * createStandardAction(KStandardAction::StandardAction, const QObject *receiver, const char *member)
void setDefaultShortcut(const QKeySequence &shortcut)
WrapAroundAxis wrapAroundModeAxis() const
void setUsePrintResolutionMode(bool value)
void sigUsePrintResolutionModeChanged(bool value)
void setup(KisActionManager *actionManager)
void setView(QPointer< KisView >imageView)
void setColorHistory(const QList< KoColor > &colors)
void setResourceManager(KoCanvasResourceProvider *resourceManager)
void addProxy(KoProgressProxy *proxy)
void removeProxy(KoProgressProxy *proxy)
static KisConfigNotifier * instance()
bool hideScrollbars(bool defaultValue=false) const
void setShowDockers(const bool value) const
void setShowStatusBar(const bool value) const
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:809
void writeKoColors(const QString &name, const QList< KoColor > &colors) const
bool fullscreenMode(bool defaultValue=false) const
bool pixelGridEnabled(bool defaultValue=false) const
void setRulersTrackMouse(bool value) const
bool hideScrollbarsFullscreen(bool defaultValue=false) const
bool showCanvasMessages(bool defaultValue=false) const
QList< KoColor > readKoColors(const QString &name) const
OutlineStyle lastUsedOutlineStyle(bool defaultValue=false) const
KoColor readKoColor(const QString &name, const KoColor &color=KoColor()) const
OutlineStyle newOutlineStyle(bool defaultValue=false) const
void setNewOutlineStyle(OutlineStyle style)
void setShowRulers(bool rulers) const
bool showDockers(bool defaultValue=false) const
bool useOpenGL(bool defaultValue=false) const
bool rulersTrackMouse(bool defaultValue=false) const
bool showStatusBar(bool defaultValue=false) const
bool showRulers(bool defaultValue=false) const
void setLastUsedOutlineStyle(OutlineStyle style)
void writeKoColor(const QString &name, const KoColor &color) const
KisPaintopBox * paintopBox()
void setup(QWidget *parent)
void setView(QPointer< KisView > imageView)
void setup(KisActionManager *actionManager)
void setFileBatchMode(const bool batchMode)
KoDocumentInfo * documentInfo() const
KisImageSP image
QString localFilePath() const
bool saveAs(const QString &path, const QByteArray &mimeType, bool showWarnings, KisPropertiesConfigurationSP exportConfiguration=0)
QString path() const
KisDocument * clone(bool addStorage=false)
creates a clone of the document and returns it. Please make sure that you hold all the necessary lock...
void setup(KisKActionCollection *ac, KisActionManager *actionManager)
void setView(QPointer< KisView >imageView)
void setView(QPointer< KisView >imageView)
void setup(KisActionManager *actionManager)
void setView(QPointer< KisView > view)
void setup(KisActionManager *actionManager)
void setImage(KisImageSP image)
void setView(QPointer< KisView >imageView)
void setup(KisActionManager *actionManager)
KisUndoAdapter * undoAdapter() const
KisCompositeProgressProxy * compositeProgressProxy()
Definition kis_image.cc:746
Central object to manage canvas input.
static KisInputProfileManager * instance()
A container for a set of QAction objects.
Q_INVOKABLE QAction * addAction(const QString &name, QAction *action)
QAction * action(int index) const
Main window for Krita.
KisView * addViewAndNotifyLoadingCompleted(KisDocument *document, QMdiSubWindow *subWindow=0)
QList< QDockWidget * > dockWidgets() const
Return the list of dock widgets belonging to this main window.
bool saveDocument(KisDocument *document, bool saveas, bool isExporting, bool isAdvancedExporting=false)
int viewCount() const
void setView(QPointer< KisView > imageView)
void setup(KisKActionCollection *collection)
KisLayerSP activeLayer()
void setView(QPointer< KisView >imageView)
void setup(KisKActionCollection *collection, KisActionManager *actionManager)
KisNodeSP activeNode()
Convenience function to get the active layer or mask.
KisPaintDeviceSP activePaintDevice()
Get the paint device the user wants to paint on now.
static KisPart * instance()
Definition KisPart.cpp:131
void addDocument(KisDocument *document, bool notify=true)
Definition KisPart.cpp:211
void queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl url, QUrl oldUrl=QUrl())
Definition KisPart.cpp:606
QString resourceLocationBase() const
resourceLocationBase is the place where all resource storages (folder, bundles etc....
static KisResourceLocator * instance()
void setup(KisActionManager *actionManager)
void setView(QPointer< KisView >imageView)
void addUniqueConnection(Sender sender, Signal signal, Receiver receiver, Method method)
void addConnection(Sender sender, Signal signal, Receiver receiver, Method method, Qt::ConnectionType type=Qt::AutoConnection)
void setView(QPointer< KisView > imageView)
void hideAllStatusBarItems()
void showAllStatusBarItems()
KoProgressUpdater * progressUpdater()
static void createTemplate(const QString &templatesResourcePath, const char *suffix, KisDocument *document, QWidget *parent=0)
The KisTextPropertyManager class.
void setCanvasResourceProvider(KisCanvasResourceProvider *provider)
setCanvasResourceProvider set the canvas resource provider.
QScopedPointer< KoProgressUpdater > persistentUnthreadedProgressUpdaterRouter
QPointer< KisFloatingMessage > savedFloatingMessage
KisSignalAutoConnectionsStore viewConnections
BlockingUserInputEventFilter blockingEventFilter
KisCanvasControlsManager canvasControlsManager
QPointer< KoUpdater > persistentUnthreadedProgressUpdater
KisViewManagerPrivate(KisViewManager *_q, KisKActionCollection *_actionCollection, QWidget *_q_parent)
QPointer< KoUpdater > persistentImageProgressUpdater
KisDecorationsManager paintingAssistantsManager
KisCanvasResourceProvider canvasResourceProvider
KoCanvasResourceProvider canvasResourceManager
bool blockUntilOperationsFinishedImpl(KisImageSP image, bool force)
std::optional< CanvasOnlyOptions > canvasOnlyOptions
KisTextPropertiesManager textPropertyManager
void slotUpdatePixelGridAction()
bool blockUntilOperationsFinished(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
KisMainWindow * mainWindow() const
KisDocument * document() const
static void initializeResourceManager(KoCanvasResourceProvider *resourceManager)
KisFilterManager * filterManager()
The filtermanager handles everything action-related to filters.
void slotSaveRulersTrackMouseState(bool value)
int viewCount() const
KisActionManager * actionManager() const
void floatingMessageRequested(const QString &message, const QString &iconName)
void updateIcons()
Update the style of all the icons.
KisIdleTasksManager * idleTasksManager()
KisCanvas2 * canvasBase() const
Return the canvas base class.
void switchCanvasOnly(bool toggled)
KisNodeSP activeNode()
KisUndoAdapter * undoAdapter()
The undo adapter is used to add commands to the undo stack.
void showFloatingZoomMessage(const QString &message)
void slotActivateTransformTool()
void setCurrentView(KisView *view)
void brushOutlineToggled()
void setQtMainWindow(QMainWindow *newMainWindow)
KisImageManager * imageManager()
void handleFloatingZoomRotationMessage(QString &messageToClear)
KisSelectionSP selection()
void setShowFloatingMessage(bool show)
void slotUpdateAuthorProfileActions()
void enableControls()
disable and enable toolbar controls. used for disabling them during painting.
QPointer< KoUpdater > createThreadedUpdater(const QString &name)
QWidget * canvas() const
Return the actual widget that is displaying the current image.
void viewChanged()
viewChanged sent out when the view has changed.
KisLayerSP activeLayer()
Convenience method to get at the active layer.
KisPaintDeviceSP activeDevice()
Convenience method to get at the active paint device.
void slotViewAdded(KisView *view)
void blockUntilOperationsFinishedForced(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
virtual KisKActionCollection * actionCollection() const
void slotSaveIncrementalBackup()
void showStatusBar(bool toggled)
KisInputManager * inputManager() const
Filters events and sends them to canvas actions.
QPointer< KoUpdater > createUnthreadedUpdater(const QString &name)
create a new progress updater
QMainWindow * qtMainWindow() const
~KisViewManager() override
KisViewManagerPrivate *const d
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
void slotSaveShowRulersState(bool value)
QString canonicalPath()
KisSelectionManager * selectionManager()
void slotViewRemoved(KisView *view)
KisGuidesManager * guidesManager() const
QWidget * mainWindowAsQWidget() const
void slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
KisImageWSP image() const
Return the image this view is displaying.
KisViewManager(QWidget *parent, KisKActionCollection *actionCollection)
static void testingInitializeOpacityToPresetResourceConverter(KoCanvasResourceProvider *resourceManager)
void showFloatingRotationMessage(const QString &message)
KisGridManager * gridManager() const
KisZoomManager * zoomManager()
The zoommanager handles everything action-related to zooming.
KisCanvasResourceProvider * canvasResourceProvider()
KisStatusBar * statusBar() const
Return the wrapper class around the statusbar.
KisTextPropertiesManager * textPropertyManager() const
void changeAuthorProfile(const QString &profileName)
KisPaintopBox * paintOpBox() const
bool selectionEditable()
Checks if the current global or local selection is editable.
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
KisViewManager * viewManager
Definition KisView.cpp:129
void effectiveZoomChanged(qreal zoom)
void addActiveCanvasResourceDependency(KoActiveCanvasResourceDependencySP dep)
void setBackgroundColor(const KoColor &color)
void setForegroundColor(const KoColor &color)
void addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator)
void addDerivedResourceConverter(KoDerivedResourceConverterSP converter)
A custom title bar for dock widgets.
void setAboutInfo(const QString &info, const QString &data)
QString aboutInfo(const QString &info) const
QPointer< KoUpdater > startSubtask(int weight=1, const QString &name=QString(), bool isPersistent=false)
static void getAllUserResourceFoldersLocationsForWindowsStore(QString &standardLocation, QString &privateLocation)
getAllAppDataLocationsForWindowsStore Use this to get both private and general appdata folders which ...
static QString getAppDataLocation()
virtual void activate(const QSet< KoShape * > &shapes)
virtual void deactivate()
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
void switchToolRequested(const QString &id)
void setConverter(KoDerivedResourceConverterSP converter, KoToolBase *tool)
void setAbstractResource(KoAbstractCanvasResourceInterfaceSP abstractResource, KoToolBase *tool)
static KoToolManager * instance()
Return the toolmanager singleton.
void initializeToolActions()
void sigUsePrintResolutionModeChanged(bool value)
void setUsePrintResolutionMode(bool value)
@ ZOOM_CONSTANT
zoom x %
Definition KoZoomMode.h:24
@ ZOOM_HEIGHT
zoom pageheight
Definition KoZoomMode.h:27
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define dbgUI
Definition kis_debug.h:52
OutlineStyle
Definition kis_global.h:53
@ OUTLINE_NONE
Definition kis_global.h:54
QSharedPointer< T > toQShared(T *ptr)
int main(int argc, char **argv)
Definition main.cpp:26
QIcon loadIcon(const QString &name)
void updateIconCommon(QObject *object)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
bool isEditable(bool checkVisibility=true) const
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:498
static KisResourceItemChooserSync * instance()
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())