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 bool inCanvasOnlyMode{false};
277
279};
280
281KisViewManager::KisViewManager(QWidget *parent, KisKActionCollection *_actionCollection)
282 : d(new KisViewManagerPrivate(this, _actionCollection, parent))
283{
284 d->actionCollection = _actionCollection;
285 d->mainWindow = dynamic_cast<QMainWindow*>(parent);
287 connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
288
291
292 // These initialization functions must wait until KisViewManager ctor is complete.
293 d->statusBar.setup();
295 d->statusBar.progressUpdater()->startSubtask(1, "", true);
296 // reset state to "completed"
297 d->persistentImageProgressUpdater->setRange(0,100);
298 d->persistentImageProgressUpdater->setValue(100);
299
301 d->statusBar.progressUpdater()->startSubtask(1, "", true);
302 // reset state to "completed"
303 d->persistentUnthreadedProgressUpdater->setRange(0,100);
305
309 d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true);
311
312 // just a clumsy way to mark the updater as completed, the subtask will
313 // be automatically deleted on completion...
314 d->persistentUnthreadedProgressUpdaterRouter->startSubtask()->setProgress(100);
315
316 d->controlFrame.setup(parent);
317
318
319 //Check to draw scrollbars after "Canvas only mode" toggle is created.
320 this->showHideScrollbars();
321
323
324 connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
325 d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
326
327 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)),
328 d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*)));
329
330 connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
331 canvasResourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
332
333 connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
334 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
335 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)),
336 d->controlFrame.paintopBox(), SLOT(updatePresetConfig()));
337
338 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
339 connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction()));
340
341 connect(KoToolManager::instance(), SIGNAL(createOpacityResource(bool, KoToolBase*)), SLOT(slotCreateOpacityResource(bool, KoToolBase*)));
342
344
345 KisConfig cfg(true);
348 KoColor foreground(Qt::black, cs);
349 d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
350 KoColor background(Qt::white, cs);
351 d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
352 d->canvasResourceProvider.setColorHistory(cfg.readKoColors("LastColorHistory"));
354
355 // Initialize the old imagesize plugin
356 new ImageSize(this);
357}
358
359
361{
362 KisConfig cfg(false);
363 if (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
364 cfg.writeKoColor("LastForeGroundColor",canvasResourceProvider()->fgColor());
365 cfg.writeKoColor("LastBackGroundColor",canvasResourceProvider()->bgColor());
366 }
367
369 cfg.writeKoColors("LastColorHistory", canvasResourceProvider()->colorHistory());
370 }
371
372 cfg.writeEntry("baseLength", KisResourceItemChooserSync::instance()->baseLength());
373 cfg.writeEntry("CanvasOnlyActive", false); // We never restart in CanvasOnlyMode
374 delete d;
375}
376
378
380{
395
396 resourceManager->addActiveCanvasResourceDependency(
400
401 resourceManager->addActiveCanvasResourceDependency(
405
406 resourceManager->addActiveCanvasResourceDependency(
410
411 KSharedConfigPtr config = KSharedConfig::openConfig();
412 KConfigGroup miscGroup = config->group("Misc");
413 const uint handleRadius = miscGroup.readEntry("HandleRadius", 5);
414 resourceManager->setHandleRadius(handleRadius);
415}
416
421
426
428{
429 // WARNING: this slot is called even when a view from another main windows is added!
430 // Don't expect \p view be a child of this view manager!
431
432 if (view->viewManager() == this && viewCount() == 0) {
434 }
435}
436
438{
439 // WARNING: this slot is called even when a view from another main windows is removed!
440 // Don't expect \p view be a child of this view manager!
441
442 if (view->viewManager() == this && viewCount() == 0) {
444 }
445}
446
448{
449 if (d->currentImageView) {
450 d->currentImageView->notifyCurrentStateChanged(false);
451
452 d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
453 KisDocument* doc = d->currentImageView->document();
454 if (doc) {
456 doc->disconnect(this);
457 }
458 d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar);
461 }
462
463 QPointer<KisView> imageView = qobject_cast<KisView*>(view);
464 d->currentImageView = imageView;
465
466 if (imageView) {
470
471 d->softProof->setChecked(imageView->softProofing());
472 d->gamutCheck->setChecked(imageView->gamutCheck());
473
474 // Wait for the async image to have loaded
475 KisDocument* doc = imageView->document();
476
477 if (KisConfig(true).readEntry<bool>("EnablePositionLabel", false)) {
478 connect(d->currentImageView->canvasController()->proxyObject,
479 SIGNAL(documentMousePositionChanged(QPointF)),
480 &d->statusBar,
481 SLOT(documentMousePositionChanged(QPointF)));
482 }
483
484 KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
485 KIS_ASSERT(canvasController);
486
487 d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode()));
488 d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
489 d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
490 d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
491
492 d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
493 d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
494 d->viewConnections.addUniqueConnection(d->wrapAroundHVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisHV()));
495 d->wrapAroundHVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_BOTH);
496 d->viewConnections.addUniqueConnection(d->wrapAroundHAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisH()));
497 d->wrapAroundHAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_HORIZONTAL);
498 d->viewConnections.addUniqueConnection(d->wrapAroundVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisV()));
499 d->wrapAroundVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_VERTICAL);
500
501 d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
502 d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
503
504 d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
505 d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool)));
506 d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
507 d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
508 d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomIn()));
509 d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomOut()));
510 d->viewConnections.addUniqueConnection(d->zoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFit()));
511 d->viewConnections.addUniqueConnection(d->zoomToFitWidth, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitWidth()));
512 d->viewConnections.addUniqueConnection(d->zoomToFitHeight, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitHeight()));
513 d->viewConnections.addUniqueConnection(d->toggleZoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotToggleZoomToFit()));
514
515 d->viewConnections.addUniqueConnection(d->resetDisplay, SIGNAL(triggered()), imageView->viewManager(), SLOT(slotResetDisplay()));
516
517 d->viewConnections.addConnection(imageView->canvasController(),
519 this,
520 [this](bool value) {
521 QSignalBlocker b(d->viewPrintSize);
522 d->viewPrintSize->setChecked(value);
523 });
524 d->viewPrintSize->setChecked(imageView->canvasController()->usePrintResolutionMode());
525 d->viewConnections.addUniqueConnection(d->viewPrintSize, &KisAction::toggled,
526 imageView->canvasController(), &KisCanvasController::setUsePrintResolutionMode);
527
528 d->viewConnections.addUniqueConnection(imageView->canvasController(),
530 imageView->zoomManager()->zoomAction(),
532 imageView->zoomManager()->zoomAction()->setUsePrintResolutionMode(imageView->canvasController()->usePrintResolutionMode());
533 d->viewConnections.addUniqueConnection(imageView->zoomManager()->zoomAction(),
535 imageView->canvasController(),
537
538 d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
539 d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
540
541 // set up progress reporting
543 d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation()));
544
545 d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool)));
546
547 imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
548 imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
549
551 }
552
553 d->filterManager.setView(imageView);
554 d->selectionManager.setView(imageView);
555 d->guidesManager.setView(imageView);
556 d->nodeManager.setView(imageView);
557 d->imageManager.setView(imageView);
558 d->canvasControlsManager.setView(imageView);
559 d->actionManager.setView(imageView);
560 d->gridManager.setView(imageView);
561 d->statusBar.setView(imageView);
563 d->mirrorManager.setView(imageView);
564
565 if (d->currentImageView) {
566 d->currentImageView->notifyCurrentStateChanged(true);
567 d->currentImageView->canvasController()->activate();
568 d->currentImageView->canvasController()->setFocus();
569
571 image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),
572 canvasResourceProvider(), SLOT(slotImageSizeChanged()));
573
575 image(), SIGNAL(sigResolutionChanged(double,double)),
576 canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
577
579 image(), SIGNAL(sigNodeChanged(KisNodeSP)),
580 this, SLOT(updateGUI()));
581
583 d->currentImageView->canvasController()->proxyObject,
587 }
588
590
593
594 Q_EMIT viewChanged();
595}
596
598{
599 if (document()) {
600 return document()->image();
601 }
602 return 0;
603}
604
609
611{
612 if (d && d->currentImageView) {
613 return d->currentImageView->canvasBase();
614 }
615 return 0;
616}
617
619{
620 if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
621 return d->currentImageView->canvasBase()->canvasWidget();
622 }
623 return 0;
624}
625
627{
628 return &d->statusBar;
629}
630
635
637{
638 return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
639}
640
642{
643 return d->statusBar.progressUpdater()->startSubtask(1, name, false);
644}
645
650
655
660
665
667{
668 if (d->currentImageView) {
669 return d->currentImageView->zoomManager();
670 }
671 return 0;
672}
673
678
683
688
693
698
700{
701 if (d->currentImageView) {
702 return d->currentImageView->selection();
703 }
704 return 0;
705
706}
707
709{
710 KisLayerSP layer = activeLayer();
711 if (layer) {
712 KisSelectionMaskSP mask = layer->selectionMask();
713 if (mask) {
714 return mask->isEditable();
715 }
716 }
717 // global selection is always editable
718 return true;
719}
720
722{
723 if (!document()) return 0;
724
726 Q_ASSERT(image);
727
728 return image->undoAdapter();
729}
730
732{
733 KisConfig cfg(true);
734
735 d->saveIncremental = actionManager()->createAction("save_incremental_version");
736 connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
737
738 d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
739 connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
740
741 connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
742
743 d->saveIncremental->setEnabled(false);
744 d->saveIncrementalBackup->setEnabled(false);
745
746 KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger");
747 connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
748
749 d->createTemplate = actionManager()->createAction("create_template");
750 connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
751
752 d->createCopy = actionManager()->createAction("create_copy");
753 connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
754
755 d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
756 connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
757
758 d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right");
759 d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left");
760 d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
761 d->wrapAroundAction = actionManager()->createAction("wrap_around_mode");
762 d->wrapAroundHVAxisAction = actionManager()->createAction("wrap_around_hv_axis");
763 d->wrapAroundHAxisAction = actionManager()->createAction("wrap_around_h_axis");
764 d->wrapAroundVAxisAction = actionManager()->createAction("wrap_around_v_axis");
765 d->wrapAroundAxisActions = new QActionGroup(this);
769 d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
770 d->softProof = actionManager()->createAction("softProof");
771 d->gamutCheck = actionManager()->createAction("gamutCheck");
772
773 KisAction *tAction = actionManager()->createAction("showStatusBar");
774 tAction->setChecked(cfg.showStatusBar());
775 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
776
777 tAction = actionManager()->createAction("view_show_canvas_only");
778 tAction->setChecked(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->toggleFgBg = actionManager()->createAction("toggle_fg_bg");
819 connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg()));
820
821 d->resetFgBg = actionManager()->createAction("reset_fg_bg");
822 connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg()));
823
824 d->toggleBrushOutline = actionManager()->createAction("toggle_brush_outline");
825 connect(d->toggleBrushOutline, SIGNAL(triggered(bool)), this, SLOT(slotToggleBrushOutline()));
826
827}
828
830{
831 // Create the managers for filters, selections, layers etc.
832 // XXX: When the current layer changes, call updateGUI on all
833 // managers
834
836
838
840
842
844
846
848
850
852}
853
858
860{
861 return &d->nodeManager;
862}
863
868
870{
871 return &d->gridManager;
872}
873
878
880{
881 if (d->currentImageView && d->currentImageView->document()) {
882 return d->currentImageView->document();
883 }
884 return 0;
885}
886
888{
889 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
890 if (mw) {
891 return mw->viewCount();
892 }
893 return 0;
894}
895
897{
898 const int busyWaitDelay = 1000;
900 dialog.blockIfImageIsBusy();
901
902 return dialog.result() == QDialog::Accepted;
903}
904
905
910
915
917{
918 if (!document()) return;
919 KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
920}
921
923{
924 KisDocument *srcDoc = document();
925 if (!srcDoc) return;
926
927 if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
928
929 KisDocument *doc = 0;
930 {
931 KisImageReadOnlyBarrierLock l(srcDoc->image());
932 doc = srcDoc->clone(true);
933 }
935
936 QString name = srcDoc->documentInfo()->aboutInfo("name");
937 if (name.isEmpty()) {
938 name = document()->path();
939 }
940 name = i18n("%1 (Copy)", name);
941 doc->documentInfo()->setAboutInfo("title", name);
942 doc->resetPath();
943
945 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
947}
948
949
951{
952 if (d->mainWindow)
953 return d->mainWindow;
954
955 //Fallback for when we have not yet set the main window.
956 QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
957 if(w)
958 return w;
959
960 return mainWindow();
961}
962
963void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
964{
965 d->mainWindow = newMainWindow;
966}
967
969{
970 d->saveIncremental->setEnabled(true);
971 d->saveIncrementalBackup->setEnabled(true);
972}
973
975{
976#ifdef Q_OS_ANDROID
977 QString path = QFileInfo(document()->localFilePath()).canonicalPath();
978 // if the path is based on a document tree then a directory would be returned. So check if it exists and more
979 // importantly check if we have permissions
980 if (QDir(path).exists()) {
981 return path;
982 } else {
983 KoFileDialog dialog(nullptr, KoFileDialog::ImportDirectory, "OpenDirectory");
984 dialog.setDirectoryUrl(QUrl(document()->localFilePath()));
985 return dialog.filename();
986 }
987#else
988 return QFileInfo(document()->localFilePath()).canonicalPath();
989#endif
990}
991
993{
994 if (!document()) return;
995
996 if (document()->path().isEmpty()) {
997 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
998 mw->saveDocument(document(), true, false);
999 return;
1000 }
1001
1002 bool foundVersion;
1003 bool fileAlreadyExists;
1004 bool isBackup;
1005 QString version = "000";
1006 QString newVersion;
1007 QString letter;
1008 QString path = canonicalPath();
1009
1010 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1011
1012 // Find current version filenames
1013 // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
1014 // Considering our incremental version and backup scheme, format is filename_001~001.ext
1015 QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
1016 regex.indexIn(fileName); // Perform the search
1017 QStringList matches = regex.capturedTexts();
1018 foundVersion = matches.at(0).isEmpty() ? false : true;
1019
1020 // Ensure compatibility with Save Incremental Backup
1021 // If this regex is not kept separate, the entire algorithm needs modification;
1022 // It's simpler to just add this.
1023 QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
1024 regexAux.indexIn(fileName); // Perform the search
1025 QStringList matchesAux = regexAux.capturedTexts();
1026 isBackup = matchesAux.at(0).isEmpty() ? false : true;
1027
1028 // If the filename has a version, prepare it for incrementation
1029 if (foundVersion) {
1030 version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
1031#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1032 if (version.contains(QRegExp("[a-z]"))) {
1033#else
1034 if (QRegExp("[a-z]").containedIn(version)) {
1035#endif
1036
1037 version.chop(1); // Trim "."
1038 letter = version.right(1); // Save letter
1039 version.chop(1); // Trim letter
1040 } else {
1041 version.chop(1); // Trim "."
1042 }
1043 version.remove(0, 1); // Trim "_"
1044 } else {
1045 // TODO: this will not work with files extensions like jp2
1046 // ...else, simply add a version to it so the next loop works
1047 QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
1048 regex2.indexIn(fileName);
1049 QStringList matches2 = regex2.capturedTexts();
1050 QString extensionPlusVersion = matches2.at(0);
1051 extensionPlusVersion.prepend(version);
1052 extensionPlusVersion.prepend("_");
1053#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1054 fileName.replace(regex2, extensionPlusVersion);
1055#else
1056 regex2.replaceIn(fileName, extensionPlusVersion);
1057#endif
1058 }
1059
1060 // Prepare the base for new version filename
1061 int intVersion = version.toInt(0);
1062 ++intVersion;
1063 QString baseNewVersion = QString::number(intVersion);
1064 while (baseNewVersion.length() < version.length()) {
1065 baseNewVersion.prepend("0");
1066 }
1067
1068 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1069 do {
1070 newVersion = baseNewVersion;
1071 newVersion.prepend("_");
1072 if (!letter.isNull()) newVersion.append(letter);
1073 if (isBackup) {
1074 newVersion.append("~");
1075 } else {
1076 newVersion.append(".");
1077 }
1078#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1079 fileName.replace(regex, newVersion);
1080#else
1081 regex.replaceIn(fileName, newVersion);
1082#endif
1083 fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
1084 if (fileAlreadyExists) {
1085 if (!letter.isNull()) {
1086 char letterCh = letter.at(0).toLatin1();
1087 ++letterCh;
1088 letter = QString(QChar(letterCh));
1089 } else {
1090 letter = 'a';
1091 }
1092 }
1093 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1094
1095 if (letter == "{") {
1096 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1097 return;
1098 }
1099 QString newFilePath = path + '/' + fileName;
1100 document()->setFileBatchMode(true);
1101 document()->saveAs(newFilePath, document()->mimeType(), true);
1102 document()->setFileBatchMode(false);
1103 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath),
1104 QUrl::fromLocalFile(document()->path()));
1105}
1106
1108{
1109 if (!document()) return;
1110
1111 if (document()->path().isEmpty()) {
1112 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1113 mw->saveDocument(document(), true, false);
1114 return;
1115 }
1116
1117 bool workingOnBackup;
1118 bool fileAlreadyExists;
1119 QString version = "000";
1120 QString newVersion;
1121 QString letter;
1122 QString path = canonicalPath();
1123 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1124
1125 // First, discover if working on a backup file, or a normal file
1126 QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]");
1127 regex.indexIn(fileName); // Perform the search
1128 QStringList matches = regex.capturedTexts();
1129 workingOnBackup = matches.at(0).isEmpty() ? false : true;
1130
1131 if (workingOnBackup) {
1132 // Try to save incremental version (of backup), use letter for alt versions
1133 version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
1134#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1135 if (version.contains(QRegExp("[a-z]"))) {
1136#else
1137 if (QRegExp("[a-z]").containedIn(version)) {
1138#endif
1139 version.chop(1); // Trim "."
1140 letter = version.right(1); // Save letter
1141 version.chop(1); // Trim letter
1142 } else {
1143 version.chop(1); // Trim "."
1144 }
1145 version.remove(0, 1); // Trim "~"
1146
1147 // Prepare the base for new version filename
1148 int intVersion = version.toInt(0);
1149 ++intVersion;
1150 QString baseNewVersion = QString::number(intVersion);
1151 QString backupFileName = document()->localFilePath();
1152 while (baseNewVersion.length() < version.length()) {
1153 baseNewVersion.prepend("0");
1154 }
1155
1156 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1157 do {
1158 newVersion = baseNewVersion;
1159 newVersion.prepend("~");
1160 if (!letter.isNull()) newVersion.append(letter);
1161 newVersion.append(".");
1162#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1163 backupFileName.replace(regex, newVersion);
1164#else
1165 regex.replaceIn(backupFileName, newVersion);
1166#endif
1167 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1168 if (fileAlreadyExists) {
1169 if (!letter.isNull()) {
1170 char letterCh = letter.at(0).toLatin1();
1171 ++letterCh;
1172 letter = QString(QChar(letterCh));
1173 } else {
1174 letter = 'a';
1175 }
1176 }
1177 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1178
1179 if (letter == "{") {
1180 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1181 return;
1182 }
1183 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1184 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1185 }
1186 else { // if NOT working on a backup...
1187 // Navigate directory searching for latest backup version, ignore letters
1188 const quint8 HARDCODED_DIGIT_COUNT = 3;
1189 QString baseNewVersion = "000";
1190 QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1191 QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
1192 regex2.indexIn(backupFileName);
1193 QStringList matches2 = regex2.capturedTexts();
1194 QString extensionPlusVersion = matches2.at(0);
1195 extensionPlusVersion.prepend(baseNewVersion);
1196 extensionPlusVersion.prepend("~");
1197#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1198 backupFileName.replace(regex2, extensionPlusVersion);
1199#else
1200 regex2.replaceIn(backupFileName, extensionPlusVersion);
1201#endif
1202
1203 // Save version with 1 number higher than the highest version found ignoring letters
1204 do {
1205 newVersion = baseNewVersion;
1206 newVersion.prepend("~");
1207 newVersion.append(".");
1208#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1209 backupFileName.replace(regex, newVersion);
1210#else
1211 regex.replaceIn(backupFileName, newVersion);
1212#endif
1213 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1214 if (fileAlreadyExists) {
1215 // Prepare the base for new version filename, increment by 1
1216 int intVersion = baseNewVersion.toInt(0);
1217 ++intVersion;
1218 baseNewVersion = QString::number(intVersion);
1219 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1220 baseNewVersion.prepend("0");
1221 }
1222 }
1223 } while (fileAlreadyExists);
1224
1225 // Save both as backup and on current file for interapplication workflow
1226 document()->setFileBatchMode(true);
1227 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1228 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1229 document()->setFileBatchMode(false);
1230 }
1231}
1232
1234{
1235 // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1236 // this is for Bug 250944
1237 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1238 // see KisToolFreehand::initPaint() and endPaint()
1239 d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1240 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1241 child->installEventFilter(&d->blockingEventFilter);
1242 }
1243}
1244
1246{
1247 d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1248 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1249 child->removeEventFilter(&d->blockingEventFilter);
1250 }
1251}
1252
1254{
1255 KisMainWindow *mw = mainWindow();
1256 if(mw && mw->statusBar()) {
1257 mw->statusBar()->setVisible(toggled);
1258 KisConfig cfg(false);
1259 cfg.setShowStatusBar(toggled);
1260 }
1261}
1262
1264{
1265 d->canvasStateInNormalMode.clear();
1267 d->canvasOnlyOptions = std::nullopt;
1268}
1269
1271{
1272 if (toggled == d->inCanvasOnlyMode) {
1274 return;
1275 }
1276
1277 KisConfig cfg(false);
1279
1280 if(!main) {
1281 dbgUI << "Unable to switch to canvas-only mode, main window not found";
1283 return;
1284 }
1285
1286#ifdef Q_OS_ANDROID
1287 // On Android, expanded tool bars will crash when canvas-only mode is
1288 // toggled. To avoid this, we go looking for expanded toolbars and close
1289 // them instead of switching the mode and hitting a crash. Note that this
1290 // only works properly because we turn off main window animations on
1291 // Android, since that means clicking the extension button will instantly
1292 // hide the menu. With animations, the user could still trigger a crash if
1293 // they tried switching the mode while the extension animation is running.
1294 QList<QToolBar *> toolBars = main->findChildren<QToolBar *>();
1295 bool wasToolBarPopupOpen = false;
1296 for (QToolBar *toolBar : toolBars) {
1297 for (QToolButton *button : toolBar->findChildren<QToolButton *>(QStringLiteral("qt_toolbar_ext_button"))) {
1298 if (button->isChecked()) {
1299 wasToolBarPopupOpen = true;
1300 button->click();
1301 }
1302 }
1303 }
1304
1305 if(wasToolBarPopupOpen) {
1307 return;
1308 }
1309#endif
1310
1311 cfg.writeEntry("CanvasOnlyActive", toggled);
1312 d->inCanvasOnlyMode = toggled;
1314
1316
1317 if (toggled) {
1318 d->canvasStateInNormalMode = qtMainWindow()->saveState();
1319 } else {
1320 d->canvasStateInCanvasOnlyMode = qtMainWindow()->saveState();
1321 d->canvasOnlyOptions = options;
1322 }
1323
1324 const bool toggleFullscreen = (options.hideTitlebarFullscreen && !cfg.fullscreenMode());
1325 const bool useCanvasOffsetCompensation = d->currentImageView &&
1326 d->currentImageView->canvasController() &&
1327 d->currentImageView->isMaximized() &&
1328 !main->canvasDetached();
1329
1330 if (useCanvasOffsetCompensation) {
1331 // The offset is calculated in two steps; this is the first step.
1332 if (toggled) {
1362 QPoint origin;
1363 if (toggleFullscreen) {
1364 // We're windowed, so also capture the position of the window in the screen.
1365 origin = main->geometry().topLeft() - main->screen()->geometry().topLeft();
1366 }
1368 } else {
1369 // Restore the original canvas position. The result is more stable if we pan before showing the UI elements.
1370 d->currentImageView->canvasController()->pan(- d->canvasOnlyOffsetCompensation);
1371 }
1372 }
1373
1374 if (options.hideStatusbarFullscreen) {
1375 if (main->statusBar()) {
1376 if (!toggled) {
1377 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1378 if (main->statusBar()->property("wasvisible").toBool()) {
1379 main->statusBar()->setVisible(true);
1380 }
1381 }
1382 }
1383 else {
1384 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1385 main->statusBar()->setVisible(false);
1386 }
1387 }
1388 }
1389
1390 if (options.hideDockersFullscreen) {
1391 KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1392 if (action) {
1393 action->setCheckable(true);
1394 if (toggled) {
1395 if (action->isChecked()) {
1396 cfg.setShowDockers(action->isChecked());
1397 action->setChecked(false);
1398 } else {
1399 cfg.setShowDockers(false);
1400 }
1401 } else {
1402 action->setChecked(cfg.showDockers());
1403 }
1404 }
1405 }
1406
1407 // QT in windows does not return to maximized upon 4th tab in a row
1408 // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1409 if (toggleFullscreen) {
1410 if(toggled) {
1411 main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1412 } else {
1413 main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1414 }
1415 }
1416
1417 if (options.hideMenuFullscreen) {
1418 if (!toggled) {
1419 if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1420 if (main->menuBar()->property("wasvisible").toBool()) {
1421 main->menuBar()->setVisible(true);
1422 }
1423 }
1424 }
1425 else {
1426 main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1427 main->menuBar()->setVisible(false);
1428 }
1429 }
1430
1431 if (options.hideToolbarFullscreen) {
1432 // We already went searching for these on Android above.
1433#ifndef Q_OS_ANDROID
1434 QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1435#endif
1436 Q_FOREACH (QToolBar* toolbar, toolBars) {
1437 if (!toggled) {
1438 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1439 if (toolbar->property("wasvisible").toBool()) {
1440 toolbar->setVisible(true);
1441 }
1442 }
1443 }
1444 else {
1445 toolbar->setProperty("wasvisible", toolbar->isVisible());
1446 toolbar->setVisible(false);
1447 }
1448 }
1449 }
1450
1452
1453 if (toggled) {
1454 if (!d->canvasStateInCanvasOnlyMode.isEmpty() &&
1456 *d->canvasOnlyOptions == options) {
1457
1467 QTimer::singleShot(0, this, [this] () {
1468 this->mainWindow()->restoreState(d->canvasStateInCanvasOnlyMode);
1469 });
1470 }
1471
1472 // show a fading heads-up display about the shortcut to go back
1473 showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1474 actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon(),
1475 2000,
1477 }
1478 else {
1479 if (!d->canvasStateInNormalMode.isEmpty()) {
1480 main->restoreState(d->canvasStateInNormalMode);
1481 }
1482 }
1483
1484 if (useCanvasOffsetCompensation && toggled) {
1485 const KoZoomMode::Mode mode = d->currentImageView->canvasController()->zoomState().mode;
1486
1487 const bool allowedZoomMode =
1488 (mode == KoZoomMode::ZOOM_CONSTANT) ||
1489 (mode == KoZoomMode::ZOOM_HEIGHT);
1490
1491 if (allowedZoomMode) {
1492 // Defer the pan action until the layout is fully settled in (including the menu bars, etc.).
1493 QTimer::singleShot(0, this, [this] () {
1494 // Compensate by the difference of (after - before) layout.
1496 d->currentImageView->canvasController()->pan(d->canvasOnlyOffsetCompensation);
1497 });
1498 } else {
1499 // Nothing to restore.
1500 d->canvasOnlyOffsetCompensation = QPoint();
1501 }
1502 }
1503}
1504
1506{
1507 QAction *action = actionManager()->actionByName(QStringLiteral("view_show_canvas_only"));
1508 if (action && action->isChecked() != d->inCanvasOnlyMode) {
1509 QSignalBlocker blocker(action);
1510 action->setChecked(d->inCanvasOnlyMode);
1511 }
1512}
1513
1518
1520{
1521 QString resourcePath = KisResourceLocator::instance()->resourceLocationBase();
1522#ifdef Q_OS_WIN
1523
1524 QString folderInStandardAppData;
1525 QString folderInPrivateAppData;
1526 KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(folderInStandardAppData, folderInPrivateAppData);
1527
1528 if (!folderInPrivateAppData.isEmpty()) {
1529
1530 const auto pathToDisplay = [](const QString &path) {
1531 // Due to how Unicode word wrapping works, the string does not
1532 // wrap after backslashes in Qt 5.12. We don't want the path to
1533 // become too long, so we add a U+200B ZERO WIDTH SPACE to allow
1534 // wrapping. The downside is that we cannot let the user select
1535 // and copy the path because it now contains invisible unicode
1536 // code points.
1537 // See: https://bugreports.qt.io/browse/QTBUG-80892
1538 return QDir::toNativeSeparators(path).replace(QChar('\\'), QStringLiteral(u"\\\u200B"));
1539 };
1540
1541 QMessageBox mbox(qApp->activeWindow());
1542 mbox.setIcon(QMessageBox::Information);
1543 mbox.setWindowTitle(i18nc("@title:window resource folder", "Open Resource Folder"));
1544 // Similar text is also used in kis_dlg_preferences.cc
1545
1546 mbox.setText(i18nc("@info resource folder",
1547 "<p>You are using the Microsoft Store package version of Krita. "
1548 "Even though Krita can be configured to place resources under the "
1549 "user AppData location, Windows may actually store the files "
1550 "inside a private app location.</p>\n"
1551 "<p>You should check both locations to determine where "
1552 "the files are located.</p>\n"
1553 "<p><b>User AppData</b>:<br/>\n"
1554 "%1</p>\n"
1555 "<p><b>Private app location</b>:<br/>\n"
1556 "%2</p>",
1557 pathToDisplay(folderInStandardAppData),
1558 pathToDisplay(folderInPrivateAppData)
1559 ));
1560 mbox.setTextInteractionFlags(Qt::NoTextInteraction);
1561
1562 const auto *btnOpenUserAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &user AppData"), QMessageBox::AcceptRole);
1563 const auto *btnOpenPrivateAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &private app location"), QMessageBox::AcceptRole);
1564
1565 mbox.addButton(QMessageBox::Close);
1566 mbox.setDefaultButton(QMessageBox::Close);
1567 mbox.exec();
1568
1569 if (mbox.clickedButton() == btnOpenPrivateAppData) {
1570 resourcePath = folderInPrivateAppData;
1571 } else if (mbox.clickedButton() == btnOpenUserAppData) {
1572 // no-op: resourcePath = resourceDir.absolutePath();
1573 } else {
1574 return;
1575 }
1576
1577
1578 }
1579#endif
1580 QDesktopServices::openUrl(QUrl::fromLocalFile(resourcePath));
1581}
1582
1584{
1585 if (mainWindow()) {
1587 Q_FOREACH (QDockWidget* dock, dockers) {
1588 KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
1589 if (titlebar) {
1590 titlebar->updateIcons();
1591 }
1592 if (qobject_cast<KoToolDocker*>(dock)) {
1593 // Tool options widgets icons are updated by KoToolManager
1594 continue;
1595 }
1596 QObjectList objects;
1597 objects.append(dock);
1598 while (!objects.isEmpty()) {
1599 QObject* object = objects.takeFirst();
1600 objects.append(object->children());
1602 }
1603 }
1604 }
1605}
1606
1615
1616void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1617{
1618 if (!d->currentImageView) return;
1619 d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1620
1621 Q_EMIT floatingMessageRequested(message, icon.name());
1622}
1623
1625{
1626 d->zoomMessage = message;
1628}
1629
1635
1637{
1638 int timeoutMsec = 500;
1639
1640 if (d->zoomRotationMessageTimer.hasExpired()) {
1641 messageToClear.clear();
1642 }
1643 d->zoomRotationMessageTimer.setRemainingTime(timeoutMsec);
1644
1645 QString message;
1646 bool haveZoomMessage = !d->zoomMessage.isEmpty();
1647 bool haveRotationMessage = !d->rotationMessage.isEmpty();
1648 if (haveZoomMessage) {
1649 if (haveRotationMessage) {
1650 message = QStringLiteral("%1\n%2").arg(d->zoomMessage, d->rotationMessage);
1651 } else {
1652 message = d->zoomMessage;
1653 }
1654 } else if (haveRotationMessage) {
1655 message = d->rotationMessage;
1656 } else {
1657 return;
1658 }
1659
1660 showFloatingMessage(message, QIcon(), timeoutMsec, KisFloatingMessage::Low, Qt::AlignCenter);
1661}
1662
1664{
1665 return qobject_cast<KisMainWindow*>(d->mainWindow);
1666}
1667
1669{
1670 return mainWindow();
1671}
1672
1673
1675{
1676 if (!d->currentImageView) return;
1677 if (!d->currentImageView->canvasController()) return;
1678
1679 KisConfig cfg(true);
1680 bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1681
1682 if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1683 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1684 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1685 } else {
1686 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1687 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1688 }
1689}
1690
1692{
1693 KisConfig cfg(false);
1694 cfg.setShowRulers(value);
1695}
1696
1702
1704{
1705 d->showFloatingMessage = show;
1706}
1707
1708void KisViewManager::changeAuthorProfile(const QString &profileName)
1709{
1710 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1711 if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1712 appAuthorGroup.writeEntry("active-profile", "");
1713 } else {
1714 appAuthorGroup.writeEntry("active-profile", profileName);
1715 }
1716 appAuthorGroup.sync();
1717 Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1719 }
1720}
1721
1723{
1724 Q_ASSERT(d->actionAuthor);
1725 if (!d->actionAuthor) {
1726 return;
1727 }
1728 d->actionAuthor->clear();
1729 d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1730
1731 KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1732 QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1733 QString authorInfo = KoResourcePaths::getAppDataLocation() + "/authorinfo/";
1734 QStringList filters = QStringList() << "*.authorinfo";
1735 QDir dir(authorInfo);
1736 Q_FOREACH(QString entry, dir.entryList(filters)) {
1737 int ln = QString(".authorinfo").size();
1738 entry.chop(ln);
1739 if (!profiles.contains(entry)) {
1740 profiles.append(entry);
1741 }
1742 }
1743 Q_FOREACH (const QString &profile , profiles) {
1744 d->actionAuthor->addAction(profile);
1745 }
1746
1747 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1748 QString profileName = appAuthorGroup.readEntry("active-profile", "");
1749
1750 if (profileName == "anonymous" || profileName.isEmpty()) {
1751 d->actionAuthor->setCurrentItem(0);
1752 } else if (profiles.contains(profileName)) {
1753 d->actionAuthor->setCurrentAction(profileName);
1754 }
1755}
1756
1766
1768{
1769 if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1770 KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1771
1772 QSet<KoShape*> dummy;
1773 // Start a new stroke
1774 tool->deactivate();
1775 tool->activate(dummy);
1776 }
1777
1778 KoToolManager::instance()->switchToolRequested("KisToolTransform");
1779}
1780
1796
1798{
1799 // see a comment in slotToggleFgBg()
1802}
1803
1805{
1806 KisConfig cfg(true);
1807
1808 OutlineStyle style;
1809
1810 if (cfg.newOutlineStyle() != OUTLINE_NONE) {
1811 style = OUTLINE_NONE;
1813 } else {
1814 style = cfg.lastUsedOutlineStyle();
1816 }
1817
1818 cfg.setNewOutlineStyle(style);
1819
1820 Q_EMIT brushOutlineToggled();
1821}
1822
1824{
1825 KisCanvasController *canvasController = d->currentImageView->canvasController();
1826 canvasController->resetCanvasRotation();
1827}
1828
1830{
1831 KisCanvasController *canvasController = d->currentImageView->canvasController();
1832 canvasController->resetCanvasRotation();
1833 canvasController->mirrorCanvas(false);
1835}
1836
1837void KisViewManager::slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
1838{
1839 if (isOpacityPresetMode) {
1841 }
1842 else {
1844 }
1845}
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 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:822
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.
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
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)
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())