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 <QRegularExpression>
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#include <KisUniqueColorSet.h>
129
130#ifdef Q_OS_WIN
132#endif
133
134class BlockingUserInputEventFilter : public QObject
135{
136 bool eventFilter(QObject *watched, QEvent *event) override
137 {
138 Q_UNUSED(watched);
139 if(dynamic_cast<QWheelEvent*>(event)
140 || dynamic_cast<QKeyEvent*>(event)
141 || dynamic_cast<QMouseEvent*>(event)) {
142 return true;
143 }
144 else {
145 return false;
146 }
147 }
148};
149
151{
152
153public:
154
155 KisViewManagerPrivate(KisViewManager *_q, KisKActionCollection *_actionCollection, QWidget *_q_parent)
156 : filterManager(_q)
157 , selectionManager(_q)
158 , statusBar(_q)
159 , controlFrame(_q, _q_parent)
160 , nodeManager(_q)
161 , imageManager(_q)
162 , gridManager(_q)
165 , actionManager(_q, _actionCollection)
168 , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q)
169 , actionCollection(_actionCollection)
170 , mirrorManager(_q)
171 , inputManager(_q)
172 , zoomRotationMessageTimer(Qt::CoarseTimer)
173 {
175 }
176
177public:
191 QActionGroup *wrapAroundAxisActions {nullptr};
196 KisAction *zoomIn {nullptr};
197 KisAction *zoomOut {nullptr};
207
212
213 QScopedPointer<KoProgressUpdater> persistentUnthreadedProgressUpdaterRouter;
215
224 QMainWindow* mainWindow {nullptr};
236
238 KSelectAction *actionAuthor {nullptr}; // Select action for author profile.
240
242 QString zoomMessage;
244
247
273 std::optional<CanvasOnlyOptions> canvasOnlyOptions;
275 bool inCanvasOnlyMode{false};
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));
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()->colorHistoryColors());
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 tAction->setAutoRepeat(false);
779 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
780
781 //Workaround, by default has the same shortcut as mirrorCanvas
782 KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
783 if (a) {
784 a->setDefaultShortcut(QKeySequence());
785 }
786
787 actionManager()->createAction("ruler_pixel_multiple2");
788 d->showRulersAction = actionManager()->createAction("view_ruler");
789 d->showRulersAction->setChecked(cfg.showRulers());
790 connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
791
792 d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
793 d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
794 connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
795
796 d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
797
800
801 d->zoomToFit = actionManager()->createAction("zoom_to_fit");
802 d->zoomToFitWidth = actionManager()->createAction("zoom_to_fit_width");
803 d->zoomToFitHeight = actionManager()->createAction("zoom_to_fit_height");
804 d->toggleZoomToFit = actionManager()->createAction("toggle_zoom_to_fit");
805
806 d->resetDisplay = actionManager()->createAction("reset_display");
807
808 d->viewPrintSize = actionManager()->createAction("view_print_size");
809
810 d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this);
811 connect(d->actionAuthor, SIGNAL(textTriggered(QString)), this, SLOT(changeAuthorProfile(QString)));
812 actionCollection()->addAction("settings_active_author", d->actionAuthor);
814
815 d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
817
818 d->toggleBrushOutline = actionManager()->createAction("toggle_brush_outline");
819 connect(d->toggleBrushOutline, SIGNAL(triggered(bool)), this, SLOT(slotToggleBrushOutline()));
820
821}
822
824{
825 // Create the managers for filters, selections, layers etc.
826 // XXX: When the current layer changes, call updateGUI on all
827 // managers
828
830
832
834
836
838
840
842
844
846}
847
852
854{
855 return &d->nodeManager;
856}
857
862
864{
865 return &d->gridManager;
866}
867
872
874{
875 if (d->currentImageView && d->currentImageView->document()) {
876 return d->currentImageView->document();
877 }
878 return 0;
879}
880
882{
883 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
884 if (mw) {
885 return mw->viewCount();
886 }
887 return 0;
888}
889
891{
892 const int busyWaitDelay = 1000;
894 dialog.blockIfImageIsBusy();
895
896 return dialog.result() == QDialog::Accepted;
897}
898
899
904
909
911{
912 if (!document()) return;
913 KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
914}
915
917{
918 KisDocument *srcDoc = document();
919 if (!srcDoc) return;
920
921 if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
922
923 KisDocument *doc = 0;
924 {
925 KisImageReadOnlyBarrierLock l(srcDoc->image());
926 doc = srcDoc->clone(true);
927 }
929
930 QString name = srcDoc->documentInfo()->aboutInfo("name");
931 if (name.isEmpty()) {
932 name = document()->path();
933 }
934 name = i18n("%1 (Copy)", name);
935 doc->documentInfo()->setAboutInfo("title", name);
936 doc->resetPath();
937
939 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
941}
942
943
945{
946 if (d->mainWindow)
947 return d->mainWindow;
948
949 //Fallback for when we have not yet set the main window.
950 QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
951 if(w)
952 return w;
953
954 return mainWindow();
955}
956
957void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
958{
959 d->mainWindow = newMainWindow;
960}
961
963{
964 d->saveIncremental->setEnabled(true);
965 d->saveIncrementalBackup->setEnabled(true);
966}
967
969{
970#ifdef Q_OS_ANDROID
971 QString path = QFileInfo(document()->localFilePath()).canonicalPath();
972 // if the path is based on a document tree then a directory would be returned. So check if it exists and more
973 // importantly check if we have permissions
974 if (QDir(path).exists()) {
975 return path;
976 } else {
977 KoFileDialog dialog(nullptr, KoFileDialog::ImportDirectory, "OpenDirectory");
978 dialog.setDirectoryUrl(QUrl(document()->localFilePath()));
979 return dialog.filename();
980 }
981#else
982 return QFileInfo(document()->localFilePath()).canonicalPath();
983#endif
984}
985
987{
988 if (!document()) return;
989
990 if (document()->path().isEmpty()) {
991 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
992 mw->saveDocument(document(), true, false);
993 return;
994 }
995
996 bool foundVersion;
997 bool fileAlreadyExists;
998 bool isBackup;
999 QString version = "000";
1000 QString newVersion;
1001 QString letter;
1002 QString path = canonicalPath();
1003
1004 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1005
1006 // Find current version filenames
1007 // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
1008 // Considering our incremental version and backup scheme, format is filename_001~001.ext
1009 QRegularExpression regex("_(\\d{1,4})([a-z])?([\\.|~])");
1010 QRegularExpressionMatch match;
1011 foundVersion = fileName.contains(regex, &match);
1012 isBackup = foundVersion ? match.captured(3) == "~" : false;
1013
1014 // If the filename has a version, prepare it for incrementation
1015 if (foundVersion) {
1016 version = match.captured(1);
1017 letter = match.captured(2);
1018 } else {
1019 // ...else, simply add a version to it so the next loop works
1020 QRegularExpression regex2("\\.\\w{2,4}$"); // Heuristic to find file extension
1021 QRegularExpressionMatch match = regex2.match(fileName);
1022 QString extensionPlusVersion = match.captured(0);
1023 extensionPlusVersion.prepend(version);
1024 extensionPlusVersion.prepend("_");
1025 fileName.replace(regex2, extensionPlusVersion);
1026 }
1027
1028 // Prepare the base for new version filename
1029 int intVersion = version.toInt(0);
1030 ++intVersion;
1031 QString baseNewVersion = QString::number(intVersion);
1032 while (baseNewVersion.length() < version.length()) {
1033 baseNewVersion.prepend("0");
1034 }
1035
1036 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1037 do {
1038 newVersion = baseNewVersion;
1039 newVersion.prepend("_");
1040 if (!letter.isNull()) newVersion.append(letter);
1041 if (isBackup) {
1042 newVersion.append("~");
1043 } else {
1044 newVersion.append(".");
1045 }
1046 fileName.replace(regex, newVersion);
1047 fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
1048 if (fileAlreadyExists) {
1049 if (!letter.isNull()) {
1050 char letterCh = letter.at(0).toLatin1();
1051 ++letterCh;
1052 letter = QString(QChar(letterCh));
1053 } else {
1054 letter = 'a';
1055 }
1056 }
1057 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1058
1059 if (letter == "{") {
1060 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1061 return;
1062 }
1063 QString newFilePath = path + '/' + fileName;
1064 document()->setFileBatchMode(true);
1065 document()->saveAs(newFilePath, document()->mimeType(), true);
1066 document()->setFileBatchMode(false);
1067 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath),
1068 QUrl::fromLocalFile(document()->path()));
1069}
1070
1072{
1073 if (!document()) return;
1074
1075 if (document()->path().isEmpty()) {
1076 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1077 mw->saveDocument(document(), true, false);
1078 return;
1079 }
1080
1081 bool workingOnBackup;
1082 bool fileAlreadyExists;
1083 QString version = "000";
1084 QString newVersion;
1085 QString letter;
1086 QString path = canonicalPath();
1087 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1088
1089 // First, discover if working on a backup file, or a normal file
1090 QRegularExpression regex("~(\\d{1,4})([a-z])?\\.");
1091 QRegularExpressionMatch match;
1092 workingOnBackup = fileName.contains(regex, &match);
1093
1094 if (workingOnBackup) {
1095 // Try to save incremental version (of backup), use letter for alt versions
1096 version = match.captured(1);
1097 letter = match.captured(2);
1098
1099 // Prepare the base for new version filename
1100 int intVersion = version.toInt(0);
1101 ++intVersion;
1102 QString baseNewVersion = QString::number(intVersion);
1103 QString backupFileName = document()->localFilePath();
1104 while (baseNewVersion.length() < version.length()) {
1105 baseNewVersion.prepend("0");
1106 }
1107
1108 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1109 do {
1110 newVersion = baseNewVersion;
1111 newVersion.prepend("~");
1112 if (!letter.isNull()) newVersion.append(letter);
1113 newVersion.append(".");
1114 backupFileName.replace(regex, newVersion);
1115 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1116 if (fileAlreadyExists) {
1117 if (!letter.isNull()) {
1118 char letterCh = letter.at(0).toLatin1();
1119 ++letterCh;
1120 letter = QString(QChar(letterCh));
1121 } else {
1122 letter = 'a';
1123 }
1124 }
1125 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1126
1127 if (letter == "{") {
1128 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1129 return;
1130 }
1131 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1132 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1133 }
1134 else { // if NOT working on a backup...
1135 // Navigate directory searching for latest backup version, ignore letters
1136 const quint8 HARDCODED_DIGIT_COUNT = 3;
1137 QString baseNewVersion = "000";
1138 QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1139 QRegularExpression regex2("\\.\\w{2,4}$"); // Heuristic to find file extension
1140 QRegularExpressionMatch match = regex2.match(fileName);
1141 QString extensionPlusVersion = match.captured(0);
1142 extensionPlusVersion.prepend(baseNewVersion);
1143 extensionPlusVersion.prepend("~");
1144 backupFileName.replace(regex2, extensionPlusVersion);
1145
1146 // Save version with 1 number higher than the highest version found ignoring letters
1147 do {
1148 newVersion = baseNewVersion;
1149 newVersion.prepend("~");
1150 newVersion.append(".");
1151 backupFileName.replace(regex, newVersion);
1152 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1153 if (fileAlreadyExists) {
1154 // Prepare the base for new version filename, increment by 1
1155 int intVersion = baseNewVersion.toInt(0);
1156 ++intVersion;
1157 baseNewVersion = QString::number(intVersion);
1158 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1159 baseNewVersion.prepend("0");
1160 }
1161 }
1162 } while (fileAlreadyExists);
1163
1164 // Save both as backup and on current file for interapplication workflow
1165 document()->setFileBatchMode(true);
1166 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1167 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1168 document()->setFileBatchMode(false);
1169 }
1170}
1171
1173{
1174 // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1175 // this is for Bug 250944
1176 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1177 // see KisToolFreehand::initPaint() and endPaint()
1178 d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1179 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1180 child->installEventFilter(&d->blockingEventFilter);
1181 }
1182}
1183
1185{
1186 d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1187 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1188 child->removeEventFilter(&d->blockingEventFilter);
1189 }
1190}
1191
1193{
1194 KisMainWindow *mw = mainWindow();
1195 if(mw && mw->statusBar()) {
1196 mw->statusBar()->setVisible(toggled);
1197 KisConfig cfg(false);
1198 cfg.setShowStatusBar(toggled);
1199 }
1200}
1201
1203{
1204 d->canvasStateInNormalMode.clear();
1206 d->canvasOnlyOptions = std::nullopt;
1207}
1208
1210{
1211 if (toggled == d->inCanvasOnlyMode) {
1213 return;
1214 }
1215
1216 KisConfig cfg(false);
1218
1219 if(!main) {
1220 dbgUI << "Unable to switch to canvas-only mode, main window not found";
1222 return;
1223 }
1224
1225#ifdef Q_OS_ANDROID
1226 // On Android, expanded tool bars will crash when canvas-only mode is
1227 // toggled. To avoid this, we go looking for expanded toolbars and close
1228 // them instead of switching the mode and hitting a crash. Note that this
1229 // only works properly because we turn off main window animations on
1230 // Android, since that means clicking the extension button will instantly
1231 // hide the menu. With animations, the user could still trigger a crash if
1232 // they tried switching the mode while the extension animation is running.
1233 QList<QToolBar *> toolBars = main->findChildren<QToolBar *>();
1234 bool wasToolBarPopupOpen = false;
1235 for (QToolBar *toolBar : toolBars) {
1236 for (QToolButton *button : toolBar->findChildren<QToolButton *>(QStringLiteral("qt_toolbar_ext_button"))) {
1237 if (button->isChecked()) {
1238 wasToolBarPopupOpen = true;
1239 button->click();
1240 }
1241 }
1242 }
1243
1244 if(wasToolBarPopupOpen) {
1246 return;
1247 }
1248#endif
1249
1250 cfg.writeEntry("CanvasOnlyActive", toggled);
1251 d->inCanvasOnlyMode = toggled;
1253
1255
1256 if (toggled) {
1257 d->canvasStateInNormalMode = qtMainWindow()->saveState();
1258 } else {
1259 d->canvasStateInCanvasOnlyMode = qtMainWindow()->saveState();
1260 d->canvasOnlyOptions = options;
1261 }
1262
1263 const bool toggleFullscreen = (options.hideTitlebarFullscreen && !cfg.fullscreenMode());
1264 const bool useCanvasOffsetCompensation = d->currentImageView &&
1265 d->currentImageView->canvasController() &&
1266 d->currentImageView->isMaximized() &&
1267 !main->canvasDetached();
1268
1269 if (useCanvasOffsetCompensation) {
1270 // The offset is calculated in two steps; this is the first step.
1271 if (toggled) {
1301 QPoint origin;
1302 if (toggleFullscreen) {
1303 // We're windowed, so also capture the position of the window in the screen.
1304 origin = main->geometry().topLeft() - main->screen()->geometry().topLeft();
1305 }
1307 } else {
1308 // Restore the original canvas position. The result is more stable if we pan before showing the UI elements.
1309 d->currentImageView->canvasController()->pan(- d->canvasOnlyOffsetCompensation);
1310 }
1311 }
1312
1313 if (options.hideStatusbarFullscreen) {
1314 if (main->statusBar()) {
1315 if (!toggled) {
1316 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1317 if (main->statusBar()->property("wasvisible").toBool()) {
1318 main->statusBar()->setVisible(true);
1319 }
1320 }
1321 }
1322 else {
1323 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1324 main->statusBar()->setVisible(false);
1325 }
1326 }
1327 }
1328
1329 if (options.hideDockersFullscreen) {
1330 KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1331 if (action) {
1332 action->setCheckable(true);
1333 if (toggled) {
1334 if (action->isChecked()) {
1335 cfg.setShowDockers(action->isChecked());
1336 action->setChecked(false);
1337 } else {
1338 cfg.setShowDockers(false);
1339 }
1340 } else {
1341 action->setChecked(cfg.showDockers());
1342 }
1343 }
1344 }
1345
1346 // QT in windows does not return to maximized upon 4th tab in a row
1347 // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1348 if (toggleFullscreen) {
1349 if(toggled) {
1350 main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1351 } else {
1352 main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1353 }
1354 }
1355
1356 if (options.hideMenuFullscreen) {
1357 if (!toggled) {
1358 if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1359 if (main->menuBar()->property("wasvisible").toBool()) {
1360 main->menuBar()->setVisible(true);
1361 }
1362 }
1363 }
1364 else {
1365 main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1366 main->menuBar()->setVisible(false);
1367 }
1368 }
1369
1370 if (options.hideToolbarFullscreen) {
1371 // We already went searching for these on Android above.
1372#ifndef Q_OS_ANDROID
1373 QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1374#endif
1375 Q_FOREACH (QToolBar* toolbar, toolBars) {
1376 if (!toggled) {
1377 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1378 if (toolbar->property("wasvisible").toBool()) {
1379 toolbar->setVisible(true);
1380 }
1381 }
1382 }
1383 else {
1384 toolbar->setProperty("wasvisible", toolbar->isVisible());
1385 toolbar->setVisible(false);
1386 }
1387 }
1388 }
1389
1391
1392 if (toggled) {
1393 if (!d->canvasStateInCanvasOnlyMode.isEmpty() &&
1395 *d->canvasOnlyOptions == options) {
1396
1406 QTimer::singleShot(0, this, [this] () {
1407 this->mainWindow()->restoreState(d->canvasStateInCanvasOnlyMode);
1408 });
1409 }
1410
1411 // show a fading heads-up display about the shortcut to go back
1412 showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1413 actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon(),
1414 2000,
1416 }
1417 else {
1418 if (!d->canvasStateInNormalMode.isEmpty()) {
1419 main->restoreState(d->canvasStateInNormalMode);
1420 }
1421 }
1422
1423 if (useCanvasOffsetCompensation && toggled) {
1424 const KoZoomMode::Mode mode = d->currentImageView->canvasController()->zoomState().mode;
1425
1426 const bool allowedZoomMode =
1427 (mode == KoZoomMode::ZOOM_CONSTANT) ||
1428 (mode == KoZoomMode::ZOOM_HEIGHT);
1429
1430 if (allowedZoomMode) {
1431 // Defer the pan action until the layout is fully settled in (including the menu bars, etc.).
1432 QTimer::singleShot(0, this, [this] () {
1433 // Compensate by the difference of (after - before) layout.
1435 d->currentImageView->canvasController()->pan(d->canvasOnlyOffsetCompensation);
1436 });
1437 } else {
1438 // Nothing to restore.
1439 d->canvasOnlyOffsetCompensation = QPoint();
1440 }
1441 }
1442}
1443
1445{
1446 QAction *action = actionManager()->actionByName(QStringLiteral("view_show_canvas_only"));
1447 if (action && action->isChecked() != d->inCanvasOnlyMode) {
1448 QSignalBlocker blocker(action);
1449 action->setChecked(d->inCanvasOnlyMode);
1450 }
1451}
1452
1457
1459{
1460 QString resourcePath = KisResourceLocator::instance()->resourceLocationBase();
1461#ifdef Q_OS_WIN
1462
1463 QString folderInStandardAppData;
1464 QString folderInPrivateAppData;
1465 KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(folderInStandardAppData, folderInPrivateAppData);
1466
1467 if (!folderInPrivateAppData.isEmpty()) {
1468
1469 const auto pathToDisplay = [](const QString &path) {
1470 // Due to how Unicode word wrapping works, the string does not
1471 // wrap after backslashes in Qt 5.12. We don't want the path to
1472 // become too long, so we add a U+200B ZERO WIDTH SPACE to allow
1473 // wrapping. The downside is that we cannot let the user select
1474 // and copy the path because it now contains invisible unicode
1475 // code points.
1476 // See: https://bugreports.qt.io/browse/QTBUG-80892
1477 return QDir::toNativeSeparators(path).replace(QChar('\\'), QStringLiteral(u"\\\u200B"));
1478 };
1479
1480 QMessageBox mbox(qApp->activeWindow());
1481 mbox.setIcon(QMessageBox::Information);
1482 mbox.setWindowTitle(i18nc("@title:window resource folder", "Open Resource Folder"));
1483 // Similar text is also used in kis_dlg_preferences.cc
1484
1485 mbox.setText(i18nc("@info resource folder",
1486 "<p>You are using the Microsoft Store package version of Krita. "
1487 "Even though Krita can be configured to place resources under the "
1488 "user AppData location, Windows may actually store the files "
1489 "inside a private app location.</p>\n"
1490 "<p>You should check both locations to determine where "
1491 "the files are located.</p>\n"
1492 "<p><b>User AppData</b>:<br/>\n"
1493 "%1</p>\n"
1494 "<p><b>Private app location</b>:<br/>\n"
1495 "%2</p>",
1496 pathToDisplay(folderInStandardAppData),
1497 pathToDisplay(folderInPrivateAppData)
1498 ));
1499 mbox.setTextInteractionFlags(Qt::NoTextInteraction);
1500
1501 const auto *btnOpenUserAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &user AppData"), QMessageBox::AcceptRole);
1502 const auto *btnOpenPrivateAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &private app location"), QMessageBox::AcceptRole);
1503
1504 mbox.addButton(QMessageBox::Close);
1505 mbox.setDefaultButton(QMessageBox::Close);
1506 mbox.exec();
1507
1508 if (mbox.clickedButton() == btnOpenPrivateAppData) {
1509 resourcePath = folderInPrivateAppData;
1510 } else if (mbox.clickedButton() == btnOpenUserAppData) {
1511 // no-op: resourcePath = resourceDir.absolutePath();
1512 } else {
1513 return;
1514 }
1515
1516
1517 }
1518#endif
1519 QDesktopServices::openUrl(QUrl::fromLocalFile(resourcePath));
1520}
1521
1523{
1524 if (mainWindow()) {
1526 Q_FOREACH (QDockWidget* dock, dockers) {
1527 KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
1528 if (titlebar) {
1529 titlebar->updateIcons();
1530 }
1531 if (qobject_cast<KoToolDocker*>(dock)) {
1532 // Tool options widgets icons are updated by KoToolManager
1533 continue;
1534 }
1535 QObjectList objects;
1536 objects.append(dock);
1537 while (!objects.isEmpty()) {
1538 QObject* object = objects.takeFirst();
1539 objects.append(object->children());
1541 }
1542 }
1543 }
1544}
1545
1554
1555void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1556{
1557 if (!d->currentImageView) return;
1558 d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1559
1560 Q_EMIT floatingMessageRequested(message, icon.name());
1561}
1562
1564{
1565 d->zoomMessage = message;
1567}
1568
1574
1576{
1577 int timeoutMsec = 500;
1578
1579 if (d->zoomRotationMessageTimer.hasExpired()) {
1580 messageToClear.clear();
1581 }
1582 d->zoomRotationMessageTimer.setRemainingTime(timeoutMsec);
1583
1584 QString message;
1585 bool haveZoomMessage = !d->zoomMessage.isEmpty();
1586 bool haveRotationMessage = !d->rotationMessage.isEmpty();
1587 if (haveZoomMessage) {
1588 if (haveRotationMessage) {
1589 message = QStringLiteral("%1\n%2").arg(d->zoomMessage, d->rotationMessage);
1590 } else {
1591 message = d->zoomMessage;
1592 }
1593 } else if (haveRotationMessage) {
1594 message = d->rotationMessage;
1595 } else {
1596 return;
1597 }
1598
1599 showFloatingMessage(message, QIcon(), timeoutMsec, KisFloatingMessage::Low, Qt::AlignCenter);
1600}
1601
1603{
1604 return qobject_cast<KisMainWindow*>(d->mainWindow);
1605}
1606
1608{
1609 return mainWindow();
1610}
1611
1612
1614{
1615 if (!d->currentImageView) return;
1616 if (!d->currentImageView->canvasController()) return;
1617
1618 KisConfig cfg(true);
1619 bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1620
1621 if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1622 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1623 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1624 } else {
1625 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1626 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1627 }
1628}
1629
1631{
1632 KisConfig cfg(false);
1633 cfg.setShowRulers(value);
1634}
1635
1641
1643{
1644 d->showFloatingMessage = show;
1645}
1646
1647void KisViewManager::changeAuthorProfile(const QString &profileName)
1648{
1649 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1650 if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1651 appAuthorGroup.writeEntry("active-profile", "");
1652 } else {
1653 appAuthorGroup.writeEntry("active-profile", profileName);
1654 }
1655 appAuthorGroup.sync();
1656 Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1658 }
1659}
1660
1662{
1663 Q_ASSERT(d->actionAuthor);
1664 if (!d->actionAuthor) {
1665 return;
1666 }
1667 d->actionAuthor->clear();
1668 d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1669
1670 KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1671 QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1672 QString authorInfo = KoResourcePaths::getAppDataLocation() + "/authorinfo/";
1673 QStringList filters = QStringList() << "*.authorinfo";
1674 QDir dir(authorInfo);
1675 Q_FOREACH(QString entry, dir.entryList(filters)) {
1676 int ln = QString(".authorinfo").size();
1677 entry.chop(ln);
1678 if (!profiles.contains(entry)) {
1679 profiles.append(entry);
1680 }
1681 }
1682 Q_FOREACH (const QString &profile , profiles) {
1683 d->actionAuthor->addAction(profile);
1684 }
1685
1686 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1687 QString profileName = appAuthorGroup.readEntry("active-profile", "");
1688
1689 if (profileName == "anonymous" || profileName.isEmpty()) {
1690 d->actionAuthor->setCurrentItem(0);
1691 } else if (profiles.contains(profileName)) {
1692 d->actionAuthor->setCurrentAction(profileName);
1693 }
1694}
1695
1705
1707{
1708 if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1709 KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1710
1711 QSet<KoShape*> dummy;
1712 // Start a new stroke
1713 tool->deactivate();
1714 tool->activate(dummy);
1715 }
1716
1717 KoToolManager::instance()->switchToolRequested("KisToolTransform");
1718}
1719
1735
1737{
1738 KisConfig cfg(true);
1739
1740 OutlineStyle style;
1741
1742 if (cfg.newOutlineStyle() != OUTLINE_NONE) {
1743 style = OUTLINE_NONE;
1745 } else {
1746 style = cfg.lastUsedOutlineStyle();
1748 }
1749
1750 cfg.setNewOutlineStyle(style);
1751
1752 Q_EMIT brushOutlineToggled();
1753}
1754
1756{
1757 KisCanvasController *canvasController = d->currentImageView->canvasController();
1758 canvasController->resetCanvasRotation();
1759}
1760
1762{
1763 KisCanvasController *canvasController = d->currentImageView->canvasController();
1764 canvasController->resetCanvasRotation();
1765 canvasController->mirrorCanvas(false);
1767}
1768
1769void KisViewManager::slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
1770{
1771 if (isOpacityPresetMode) {
1773 }
1774 else {
1776 }
1777}
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 * actionByName(const QString &name) const
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 setColorHistoryColors(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:887
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:773
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:130
void addDocument(KisDocument *document, bool notify=true)
Definition KisPart.cpp:209
void queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl url, QUrl oldUrl=QUrl())
Definition KisPart.cpp:604
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.
void updateCanvasOnlyActionState()
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
QPointer< 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:55
OutlineStyle
Definition kis_global.h:53
@ OUTLINE_NONE
Definition kis_global.h:54
QSharedPointer< T > toQShared(T *ptr)
QString button(const QWheelEvent &ev)
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:504
static KisResourceItemChooserSync * instance()
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())