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};
209
214
215 QScopedPointer<KoProgressUpdater> persistentUnthreadedProgressUpdaterRouter;
217
226 QMainWindow* mainWindow {nullptr};
238
240 KSelectAction *actionAuthor {nullptr}; // Select action for author profile.
242
244 QString zoomMessage;
246
249
275 std::optional<CanvasOnlyOptions> canvasOnlyOptions;
277 bool inCanvasOnlyMode{false};
278
280};
281
282KisViewManager::KisViewManager(QWidget *parent, KisKActionCollection *_actionCollection)
283 : d(new KisViewManagerPrivate(this, _actionCollection, parent))
284{
285 d->actionCollection = _actionCollection;
286 d->mainWindow = dynamic_cast<QMainWindow*>(parent);
288 connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
289
292
293 // These initialization functions must wait until KisViewManager ctor is complete.
294 d->statusBar.setup();
296 d->statusBar.progressUpdater()->startSubtask(1, "", true);
297 // reset state to "completed"
298 d->persistentImageProgressUpdater->setRange(0,100);
299 d->persistentImageProgressUpdater->setValue(100);
300
302 d->statusBar.progressUpdater()->startSubtask(1, "", true);
303 // reset state to "completed"
304 d->persistentUnthreadedProgressUpdater->setRange(0,100);
306
310 d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true);
312
313 // just a clumsy way to mark the updater as completed, the subtask will
314 // be automatically deleted on completion...
315 d->persistentUnthreadedProgressUpdaterRouter->startSubtask()->setProgress(100);
316
317 d->controlFrame.setup(parent);
318
319
320 //Check to draw scrollbars after "Canvas only mode" toggle is created.
321 this->showHideScrollbars();
322
324
325 connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
326 d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
327
328 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)),
329 d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*)));
330
331 connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
332 canvasResourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
333
334 connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
335 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
336 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)),
337 d->controlFrame.paintopBox(), SLOT(updatePresetConfig()));
338
339 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
340 connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction()));
341
342 connect(KoToolManager::instance(), SIGNAL(createOpacityResource(bool, KoToolBase*)), SLOT(slotCreateOpacityResource(bool, KoToolBase*)));
343
345
346 KisConfig cfg(true);
349 KoColor foreground(Qt::black, cs);
350 d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
351 KoColor background(Qt::white, cs);
352 d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
355
356 // Initialize the old imagesize plugin
357 new ImageSize(this);
358}
359
360
362{
363 KisConfig cfg(false);
364 if (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
365 cfg.writeKoColor("LastForeGroundColor",canvasResourceProvider()->fgColor());
366 cfg.writeKoColor("LastBackGroundColor",canvasResourceProvider()->bgColor());
367 }
368
370 cfg.writeKoColors("LastColorHistory", canvasResourceProvider()->colorHistoryColors());
371 }
372
373 cfg.writeEntry("baseLength", KisResourceItemChooserSync::instance()->baseLength());
374 cfg.writeEntry("CanvasOnlyActive", false); // We never restart in CanvasOnlyMode
375 delete d;
376}
377
379
381{
396
397 resourceManager->addActiveCanvasResourceDependency(
401
402 resourceManager->addActiveCanvasResourceDependency(
406
407 resourceManager->addActiveCanvasResourceDependency(
411
412 KSharedConfigPtr config = KSharedConfig::openConfig();
413 KConfigGroup miscGroup = config->group("Misc");
414 const uint handleRadius = miscGroup.readEntry("HandleRadius", 5);
415 resourceManager->setHandleRadius(handleRadius);
416}
417
422
427
429{
430 // WARNING: this slot is called even when a view from another main windows is added!
431 // Don't expect \p view be a child of this view manager!
432
433 if (view->viewManager() == this && viewCount() == 0) {
435 }
436}
437
439{
440 // WARNING: this slot is called even when a view from another main windows is removed!
441 // Don't expect \p view be a child of this view manager!
442
443 if (view->viewManager() == this && viewCount() == 0) {
445 }
446}
447
449{
450 if (d->currentImageView) {
451 d->currentImageView->notifyCurrentStateChanged(false);
452
453 d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
454 KisDocument* doc = d->currentImageView->document();
455 if (doc) {
457 doc->disconnect(this);
458 }
459 d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar);
462 }
463
464 QPointer<KisView> imageView = qobject_cast<KisView*>(view);
465 d->currentImageView = imageView;
466
467 if (imageView) {
471
472 d->softProof->setChecked(imageView->softProofing());
473 d->gamutCheck->setChecked(imageView->gamutCheck());
474
475 // Wait for the async image to have loaded
476 KisDocument* doc = imageView->document();
477
478 if (KisConfig(true).readEntry<bool>("EnablePositionLabel", false)) {
479 connect(d->currentImageView->canvasController()->proxyObject,
480 SIGNAL(documentMousePositionChanged(QPointF)),
481 &d->statusBar,
482 SLOT(documentMousePositionChanged(QPointF)));
483 }
484
485 KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
486 KIS_ASSERT(canvasController);
487
488 d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode()));
489 d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
490 d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
491 d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
492
493 d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
494 d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
495 d->viewConnections.addUniqueConnection(d->wrapAroundHVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisHV()));
496 d->wrapAroundHVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_BOTH);
497 d->viewConnections.addUniqueConnection(d->wrapAroundHAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisH()));
498 d->wrapAroundHAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_HORIZONTAL);
499 d->viewConnections.addUniqueConnection(d->wrapAroundVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisV()));
500 d->wrapAroundVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_VERTICAL);
501
502 d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
503 d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
504
505 d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
506 d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool)));
507 d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
508 d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
509 d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomIn()));
510 d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomOut()));
511 d->viewConnections.addUniqueConnection(d->zoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFit()));
512 d->viewConnections.addUniqueConnection(d->zoomToFitWidth, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitWidth()));
513 d->viewConnections.addUniqueConnection(d->zoomToFitHeight, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitHeight()));
514 d->viewConnections.addUniqueConnection(d->toggleZoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotToggleZoomToFit()));
515
516 d->viewConnections.addUniqueConnection(d->resetDisplay, SIGNAL(triggered()), imageView->viewManager(), SLOT(slotResetDisplay()));
517
518 d->viewConnections.addConnection(imageView->canvasController(),
520 this,
521 [this](bool value) {
522 QSignalBlocker b(d->viewPrintSize);
523 d->viewPrintSize->setChecked(value);
524 });
525 d->viewPrintSize->setChecked(imageView->canvasController()->usePrintResolutionMode());
526 d->viewConnections.addUniqueConnection(d->viewPrintSize, &KisAction::toggled,
527 imageView->canvasController(), &KisCanvasController::setUsePrintResolutionMode);
528
529 d->viewConnections.addUniqueConnection(imageView->canvasController(),
531 imageView->zoomManager()->zoomAction(),
533 imageView->zoomManager()->zoomAction()->setUsePrintResolutionMode(imageView->canvasController()->usePrintResolutionMode());
534 d->viewConnections.addUniqueConnection(imageView->zoomManager()->zoomAction(),
536 imageView->canvasController(),
538
539 d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
540 d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
541
542 // set up progress reporting
544 d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation()));
545
546 d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool)));
547
548 imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
549 imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
550
552 }
553
554 d->filterManager.setView(imageView);
555 d->selectionManager.setView(imageView);
556 d->guidesManager.setView(imageView);
557 d->nodeManager.setView(imageView);
558 d->imageManager.setView(imageView);
559 d->canvasControlsManager.setView(imageView);
560 d->actionManager.setView(imageView);
561 d->gridManager.setView(imageView);
562 d->statusBar.setView(imageView);
564 d->mirrorManager.setView(imageView);
565
566 if (d->currentImageView) {
567 d->currentImageView->notifyCurrentStateChanged(true);
568 d->currentImageView->canvasController()->activate();
569 d->currentImageView->canvasController()->setFocus();
570
572 image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),
573 canvasResourceProvider(), SLOT(slotImageSizeChanged()));
574
576 image(), SIGNAL(sigResolutionChanged(double,double)),
577 canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
578
580 image(), SIGNAL(sigNodeChanged(KisNodeSP)),
581 this, SLOT(updateGUI()));
582
584 d->currentImageView->canvasController()->proxyObject,
588 }
589
591
594
595 Q_EMIT viewChanged();
596}
597
599{
600 if (document()) {
601 return document()->image();
602 }
603 return 0;
604}
605
610
612{
613 if (d && d->currentImageView) {
614 return d->currentImageView->canvasBase();
615 }
616 return 0;
617}
618
620{
621 if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
622 return d->currentImageView->canvasBase()->canvasWidget();
623 }
624 return 0;
625}
626
628{
629 return &d->statusBar;
630}
631
636
638{
639 return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
640}
641
643{
644 return d->statusBar.progressUpdater()->startSubtask(1, name, false);
645}
646
651
656
661
666
668{
669 if (d->currentImageView) {
670 return d->currentImageView->zoomManager();
671 }
672 return 0;
673}
674
679
684
689
694
699
701{
702 if (d->currentImageView) {
703 return d->currentImageView->selection();
704 }
705 return 0;
706
707}
708
710{
711 KisLayerSP layer = activeLayer();
712 if (layer) {
713 KisSelectionMaskSP mask = layer->selectionMask();
714 if (mask) {
715 return mask->isEditable();
716 }
717 }
718 // global selection is always editable
719 return true;
720}
721
723{
724 if (!document()) return 0;
725
727 Q_ASSERT(image);
728
729 return image->undoAdapter();
730}
731
733{
734 KisConfig cfg(true);
735
736 d->saveIncremental = actionManager()->createAction("save_incremental_version");
737 connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
738
739 d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
740 connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
741
742 connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
743
744 d->saveIncremental->setEnabled(false);
745 d->saveIncrementalBackup->setEnabled(false);
746
747 KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger");
748 connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
749
750 d->createTemplate = actionManager()->createAction("create_template");
751 connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
752
753 d->createCopy = actionManager()->createAction("create_copy");
754 connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
755
756 d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
757 connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
758
759 d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right");
760 d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left");
761 d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
762 d->wrapAroundAction = actionManager()->createAction("wrap_around_mode");
763 d->wrapAroundHVAxisAction = actionManager()->createAction("wrap_around_hv_axis");
764 d->wrapAroundHAxisAction = actionManager()->createAction("wrap_around_h_axis");
765 d->wrapAroundVAxisAction = actionManager()->createAction("wrap_around_v_axis");
766 d->wrapAroundAxisActions = new QActionGroup(this);
770 d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
771 d->softProof = actionManager()->createAction("softProof");
772 d->gamutCheck = actionManager()->createAction("gamutCheck");
773
774 KisAction *tAction = actionManager()->createAction("showStatusBar");
775 tAction->setChecked(cfg.showStatusBar());
776 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
777
778 tAction = actionManager()->createAction("view_show_canvas_only");
779 tAction->setChecked(false);
780 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
781
782 //Workaround, by default has the same shortcut as mirrorCanvas
783 KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
784 if (a) {
785 a->setDefaultShortcut(QKeySequence());
786 }
787
788 actionManager()->createAction("ruler_pixel_multiple2");
789 d->showRulersAction = actionManager()->createAction("view_ruler");
790 d->showRulersAction->setChecked(cfg.showRulers());
791 connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
792
793 d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
794 d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
795 connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
796
797 d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
798
801
802 d->zoomToFit = actionManager()->createAction("zoom_to_fit");
803 d->zoomToFitWidth = actionManager()->createAction("zoom_to_fit_width");
804 d->zoomToFitHeight = actionManager()->createAction("zoom_to_fit_height");
805 d->toggleZoomToFit = actionManager()->createAction("toggle_zoom_to_fit");
806
807 d->resetDisplay = actionManager()->createAction("reset_display");
808
809 d->viewPrintSize = actionManager()->createAction("view_print_size");
810
811 d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this);
812 connect(d->actionAuthor, SIGNAL(textTriggered(QString)), this, SLOT(changeAuthorProfile(QString)));
813 actionCollection()->addAction("settings_active_author", d->actionAuthor);
815
816 d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
818
819 d->toggleFgBg = actionManager()->createAction("toggle_fg_bg");
820 connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg()));
821
822 d->resetFgBg = actionManager()->createAction("reset_fg_bg");
823 connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg()));
824
825 d->toggleBrushOutline = actionManager()->createAction("toggle_brush_outline");
826 connect(d->toggleBrushOutline, SIGNAL(triggered(bool)), this, SLOT(slotToggleBrushOutline()));
827
828}
829
831{
832 // Create the managers for filters, selections, layers etc.
833 // XXX: When the current layer changes, call updateGUI on all
834 // managers
835
837
839
841
843
845
847
849
851
853}
854
859
861{
862 return &d->nodeManager;
863}
864
869
871{
872 return &d->gridManager;
873}
874
879
881{
882 if (d->currentImageView && d->currentImageView->document()) {
883 return d->currentImageView->document();
884 }
885 return 0;
886}
887
889{
890 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
891 if (mw) {
892 return mw->viewCount();
893 }
894 return 0;
895}
896
898{
899 const int busyWaitDelay = 1000;
901 dialog.blockIfImageIsBusy();
902
903 return dialog.result() == QDialog::Accepted;
904}
905
906
911
916
918{
919 if (!document()) return;
920 KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
921}
922
924{
925 KisDocument *srcDoc = document();
926 if (!srcDoc) return;
927
928 if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
929
930 KisDocument *doc = 0;
931 {
932 KisImageReadOnlyBarrierLock l(srcDoc->image());
933 doc = srcDoc->clone(true);
934 }
936
937 QString name = srcDoc->documentInfo()->aboutInfo("name");
938 if (name.isEmpty()) {
939 name = document()->path();
940 }
941 name = i18n("%1 (Copy)", name);
942 doc->documentInfo()->setAboutInfo("title", name);
943 doc->resetPath();
944
946 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
948}
949
950
952{
953 if (d->mainWindow)
954 return d->mainWindow;
955
956 //Fallback for when we have not yet set the main window.
957 QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
958 if(w)
959 return w;
960
961 return mainWindow();
962}
963
964void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
965{
966 d->mainWindow = newMainWindow;
967}
968
970{
971 d->saveIncremental->setEnabled(true);
972 d->saveIncrementalBackup->setEnabled(true);
973}
974
976{
977#ifdef Q_OS_ANDROID
978 QString path = QFileInfo(document()->localFilePath()).canonicalPath();
979 // if the path is based on a document tree then a directory would be returned. So check if it exists and more
980 // importantly check if we have permissions
981 if (QDir(path).exists()) {
982 return path;
983 } else {
984 KoFileDialog dialog(nullptr, KoFileDialog::ImportDirectory, "OpenDirectory");
985 dialog.setDirectoryUrl(QUrl(document()->localFilePath()));
986 return dialog.filename();
987 }
988#else
989 return QFileInfo(document()->localFilePath()).canonicalPath();
990#endif
991}
992
994{
995 if (!document()) return;
996
997 if (document()->path().isEmpty()) {
998 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
999 mw->saveDocument(document(), true, false);
1000 return;
1001 }
1002
1003 bool foundVersion;
1004 bool fileAlreadyExists;
1005 bool isBackup;
1006 QString version = "000";
1007 QString newVersion;
1008 QString letter;
1009 QString path = canonicalPath();
1010
1011 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1012
1013 // Find current version filenames
1014 // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
1015 // Considering our incremental version and backup scheme, format is filename_001~001.ext
1016 QRegularExpression regex("_(\\d{1,4})([a-z])?([\\.|~])");
1017 QRegularExpressionMatch match;
1018 foundVersion = fileName.contains(regex, &match);
1019 isBackup = foundVersion ? match.captured(3) == "~" : false;
1020
1021 // If the filename has a version, prepare it for incrementation
1022 if (foundVersion) {
1023 version = match.captured(1);
1024 letter = match.captured(2);
1025 } else {
1026 // ...else, simply add a version to it so the next loop works
1027 QRegularExpression regex2("\\.\\w{2,4}$"); // Heuristic to find file extension
1028 QRegularExpressionMatch match = regex2.match(fileName);
1029 QString extensionPlusVersion = match.captured(0);
1030 extensionPlusVersion.prepend(version);
1031 extensionPlusVersion.prepend("_");
1032 fileName.replace(regex2, extensionPlusVersion);
1033 }
1034
1035 // Prepare the base for new version filename
1036 int intVersion = version.toInt(0);
1037 ++intVersion;
1038 QString baseNewVersion = QString::number(intVersion);
1039 while (baseNewVersion.length() < version.length()) {
1040 baseNewVersion.prepend("0");
1041 }
1042
1043 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1044 do {
1045 newVersion = baseNewVersion;
1046 newVersion.prepend("_");
1047 if (!letter.isNull()) newVersion.append(letter);
1048 if (isBackup) {
1049 newVersion.append("~");
1050 } else {
1051 newVersion.append(".");
1052 }
1053 fileName.replace(regex, newVersion);
1054 fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
1055 if (fileAlreadyExists) {
1056 if (!letter.isNull()) {
1057 char letterCh = letter.at(0).toLatin1();
1058 ++letterCh;
1059 letter = QString(QChar(letterCh));
1060 } else {
1061 letter = 'a';
1062 }
1063 }
1064 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1065
1066 if (letter == "{") {
1067 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1068 return;
1069 }
1070 QString newFilePath = path + '/' + fileName;
1071 document()->setFileBatchMode(true);
1072 document()->saveAs(newFilePath, document()->mimeType(), true);
1073 document()->setFileBatchMode(false);
1074 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath),
1075 QUrl::fromLocalFile(document()->path()));
1076}
1077
1079{
1080 if (!document()) return;
1081
1082 if (document()->path().isEmpty()) {
1083 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1084 mw->saveDocument(document(), true, false);
1085 return;
1086 }
1087
1088 bool workingOnBackup;
1089 bool fileAlreadyExists;
1090 QString version = "000";
1091 QString newVersion;
1092 QString letter;
1093 QString path = canonicalPath();
1094 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1095
1096 // First, discover if working on a backup file, or a normal file
1097 QRegularExpression regex("~(\\d{1,4})([a-z])?\\.");
1098 QRegularExpressionMatch match;
1099 workingOnBackup = fileName.contains(regex, &match);
1100
1101 if (workingOnBackup) {
1102 // Try to save incremental version (of backup), use letter for alt versions
1103 version = match.captured(1);
1104 letter = match.captured(2);
1105
1106 // Prepare the base for new version filename
1107 int intVersion = version.toInt(0);
1108 ++intVersion;
1109 QString baseNewVersion = QString::number(intVersion);
1110 QString backupFileName = document()->localFilePath();
1111 while (baseNewVersion.length() < version.length()) {
1112 baseNewVersion.prepend("0");
1113 }
1114
1115 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1116 do {
1117 newVersion = baseNewVersion;
1118 newVersion.prepend("~");
1119 if (!letter.isNull()) newVersion.append(letter);
1120 newVersion.append(".");
1121 backupFileName.replace(regex, newVersion);
1122 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1123 if (fileAlreadyExists) {
1124 if (!letter.isNull()) {
1125 char letterCh = letter.at(0).toLatin1();
1126 ++letterCh;
1127 letter = QString(QChar(letterCh));
1128 } else {
1129 letter = 'a';
1130 }
1131 }
1132 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1133
1134 if (letter == "{") {
1135 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1136 return;
1137 }
1138 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1139 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1140 }
1141 else { // if NOT working on a backup...
1142 // Navigate directory searching for latest backup version, ignore letters
1143 const quint8 HARDCODED_DIGIT_COUNT = 3;
1144 QString baseNewVersion = "000";
1145 QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1146 QRegularExpression regex2("\\.\\w{2,4}$"); // Heuristic to find file extension
1147 QRegularExpressionMatch match = regex2.match(fileName);
1148 QString extensionPlusVersion = match.captured(0);
1149 extensionPlusVersion.prepend(baseNewVersion);
1150 extensionPlusVersion.prepend("~");
1151 backupFileName.replace(regex2, extensionPlusVersion);
1152
1153 // Save version with 1 number higher than the highest version found ignoring letters
1154 do {
1155 newVersion = baseNewVersion;
1156 newVersion.prepend("~");
1157 newVersion.append(".");
1158 backupFileName.replace(regex, newVersion);
1159 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1160 if (fileAlreadyExists) {
1161 // Prepare the base for new version filename, increment by 1
1162 int intVersion = baseNewVersion.toInt(0);
1163 ++intVersion;
1164 baseNewVersion = QString::number(intVersion);
1165 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1166 baseNewVersion.prepend("0");
1167 }
1168 }
1169 } while (fileAlreadyExists);
1170
1171 // Save both as backup and on current file for interapplication workflow
1172 document()->setFileBatchMode(true);
1173 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1174 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1175 document()->setFileBatchMode(false);
1176 }
1177}
1178
1180{
1181 // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1182 // this is for Bug 250944
1183 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1184 // see KisToolFreehand::initPaint() and endPaint()
1185 d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1186 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1187 child->installEventFilter(&d->blockingEventFilter);
1188 }
1189}
1190
1192{
1193 d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1194 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1195 child->removeEventFilter(&d->blockingEventFilter);
1196 }
1197}
1198
1200{
1201 KisMainWindow *mw = mainWindow();
1202 if(mw && mw->statusBar()) {
1203 mw->statusBar()->setVisible(toggled);
1204 KisConfig cfg(false);
1205 cfg.setShowStatusBar(toggled);
1206 }
1207}
1208
1210{
1211 d->canvasStateInNormalMode.clear();
1213 d->canvasOnlyOptions = std::nullopt;
1214}
1215
1217{
1218 if (toggled == d->inCanvasOnlyMode) {
1220 return;
1221 }
1222
1223 KisConfig cfg(false);
1225
1226 if(!main) {
1227 dbgUI << "Unable to switch to canvas-only mode, main window not found";
1229 return;
1230 }
1231
1232#ifdef Q_OS_ANDROID
1233 // On Android, expanded tool bars will crash when canvas-only mode is
1234 // toggled. To avoid this, we go looking for expanded toolbars and close
1235 // them instead of switching the mode and hitting a crash. Note that this
1236 // only works properly because we turn off main window animations on
1237 // Android, since that means clicking the extension button will instantly
1238 // hide the menu. With animations, the user could still trigger a crash if
1239 // they tried switching the mode while the extension animation is running.
1240 QList<QToolBar *> toolBars = main->findChildren<QToolBar *>();
1241 bool wasToolBarPopupOpen = false;
1242 for (QToolBar *toolBar : toolBars) {
1243 for (QToolButton *button : toolBar->findChildren<QToolButton *>(QStringLiteral("qt_toolbar_ext_button"))) {
1244 if (button->isChecked()) {
1245 wasToolBarPopupOpen = true;
1246 button->click();
1247 }
1248 }
1249 }
1250
1251 if(wasToolBarPopupOpen) {
1253 return;
1254 }
1255#endif
1256
1257 cfg.writeEntry("CanvasOnlyActive", toggled);
1258 d->inCanvasOnlyMode = toggled;
1260
1262
1263 if (toggled) {
1264 d->canvasStateInNormalMode = qtMainWindow()->saveState();
1265 } else {
1266 d->canvasStateInCanvasOnlyMode = qtMainWindow()->saveState();
1267 d->canvasOnlyOptions = options;
1268 }
1269
1270 const bool toggleFullscreen = (options.hideTitlebarFullscreen && !cfg.fullscreenMode());
1271 const bool useCanvasOffsetCompensation = d->currentImageView &&
1272 d->currentImageView->canvasController() &&
1273 d->currentImageView->isMaximized() &&
1274 !main->canvasDetached();
1275
1276 if (useCanvasOffsetCompensation) {
1277 // The offset is calculated in two steps; this is the first step.
1278 if (toggled) {
1308 QPoint origin;
1309 if (toggleFullscreen) {
1310 // We're windowed, so also capture the position of the window in the screen.
1311 origin = main->geometry().topLeft() - main->screen()->geometry().topLeft();
1312 }
1314 } else {
1315 // Restore the original canvas position. The result is more stable if we pan before showing the UI elements.
1316 d->currentImageView->canvasController()->pan(- d->canvasOnlyOffsetCompensation);
1317 }
1318 }
1319
1320 if (options.hideStatusbarFullscreen) {
1321 if (main->statusBar()) {
1322 if (!toggled) {
1323 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1324 if (main->statusBar()->property("wasvisible").toBool()) {
1325 main->statusBar()->setVisible(true);
1326 }
1327 }
1328 }
1329 else {
1330 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1331 main->statusBar()->setVisible(false);
1332 }
1333 }
1334 }
1335
1336 if (options.hideDockersFullscreen) {
1337 KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1338 if (action) {
1339 action->setCheckable(true);
1340 if (toggled) {
1341 if (action->isChecked()) {
1342 cfg.setShowDockers(action->isChecked());
1343 action->setChecked(false);
1344 } else {
1345 cfg.setShowDockers(false);
1346 }
1347 } else {
1348 action->setChecked(cfg.showDockers());
1349 }
1350 }
1351 }
1352
1353 // QT in windows does not return to maximized upon 4th tab in a row
1354 // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1355 if (toggleFullscreen) {
1356 if(toggled) {
1357 main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1358 } else {
1359 main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1360 }
1361 }
1362
1363 if (options.hideMenuFullscreen) {
1364 if (!toggled) {
1365 if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1366 if (main->menuBar()->property("wasvisible").toBool()) {
1367 main->menuBar()->setVisible(true);
1368 }
1369 }
1370 }
1371 else {
1372 main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1373 main->menuBar()->setVisible(false);
1374 }
1375 }
1376
1377 if (options.hideToolbarFullscreen) {
1378 // We already went searching for these on Android above.
1379#ifndef Q_OS_ANDROID
1380 QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1381#endif
1382 Q_FOREACH (QToolBar* toolbar, toolBars) {
1383 if (!toggled) {
1384 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1385 if (toolbar->property("wasvisible").toBool()) {
1386 toolbar->setVisible(true);
1387 }
1388 }
1389 }
1390 else {
1391 toolbar->setProperty("wasvisible", toolbar->isVisible());
1392 toolbar->setVisible(false);
1393 }
1394 }
1395 }
1396
1398
1399 if (toggled) {
1400 if (!d->canvasStateInCanvasOnlyMode.isEmpty() &&
1402 *d->canvasOnlyOptions == options) {
1403
1413 QTimer::singleShot(0, this, [this] () {
1414 this->mainWindow()->restoreState(d->canvasStateInCanvasOnlyMode);
1415 });
1416 }
1417
1418 // show a fading heads-up display about the shortcut to go back
1419 showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1420 actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon(),
1421 2000,
1423 }
1424 else {
1425 if (!d->canvasStateInNormalMode.isEmpty()) {
1426 main->restoreState(d->canvasStateInNormalMode);
1427 }
1428 }
1429
1430 if (useCanvasOffsetCompensation && toggled) {
1431 const KoZoomMode::Mode mode = d->currentImageView->canvasController()->zoomState().mode;
1432
1433 const bool allowedZoomMode =
1434 (mode == KoZoomMode::ZOOM_CONSTANT) ||
1435 (mode == KoZoomMode::ZOOM_HEIGHT);
1436
1437 if (allowedZoomMode) {
1438 // Defer the pan action until the layout is fully settled in (including the menu bars, etc.).
1439 QTimer::singleShot(0, this, [this] () {
1440 // Compensate by the difference of (after - before) layout.
1442 d->currentImageView->canvasController()->pan(d->canvasOnlyOffsetCompensation);
1443 });
1444 } else {
1445 // Nothing to restore.
1446 d->canvasOnlyOffsetCompensation = QPoint();
1447 }
1448 }
1449}
1450
1452{
1453 QAction *action = actionManager()->actionByName(QStringLiteral("view_show_canvas_only"));
1454 if (action && action->isChecked() != d->inCanvasOnlyMode) {
1455 QSignalBlocker blocker(action);
1456 action->setChecked(d->inCanvasOnlyMode);
1457 }
1458}
1459
1464
1466{
1467 QString resourcePath = KisResourceLocator::instance()->resourceLocationBase();
1468#ifdef Q_OS_WIN
1469
1470 QString folderInStandardAppData;
1471 QString folderInPrivateAppData;
1472 KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(folderInStandardAppData, folderInPrivateAppData);
1473
1474 if (!folderInPrivateAppData.isEmpty()) {
1475
1476 const auto pathToDisplay = [](const QString &path) {
1477 // Due to how Unicode word wrapping works, the string does not
1478 // wrap after backslashes in Qt 5.12. We don't want the path to
1479 // become too long, so we add a U+200B ZERO WIDTH SPACE to allow
1480 // wrapping. The downside is that we cannot let the user select
1481 // and copy the path because it now contains invisible unicode
1482 // code points.
1483 // See: https://bugreports.qt.io/browse/QTBUG-80892
1484 return QDir::toNativeSeparators(path).replace(QChar('\\'), QStringLiteral(u"\\\u200B"));
1485 };
1486
1487 QMessageBox mbox(qApp->activeWindow());
1488 mbox.setIcon(QMessageBox::Information);
1489 mbox.setWindowTitle(i18nc("@title:window resource folder", "Open Resource Folder"));
1490 // Similar text is also used in kis_dlg_preferences.cc
1491
1492 mbox.setText(i18nc("@info resource folder",
1493 "<p>You are using the Microsoft Store package version of Krita. "
1494 "Even though Krita can be configured to place resources under the "
1495 "user AppData location, Windows may actually store the files "
1496 "inside a private app location.</p>\n"
1497 "<p>You should check both locations to determine where "
1498 "the files are located.</p>\n"
1499 "<p><b>User AppData</b>:<br/>\n"
1500 "%1</p>\n"
1501 "<p><b>Private app location</b>:<br/>\n"
1502 "%2</p>",
1503 pathToDisplay(folderInStandardAppData),
1504 pathToDisplay(folderInPrivateAppData)
1505 ));
1506 mbox.setTextInteractionFlags(Qt::NoTextInteraction);
1507
1508 const auto *btnOpenUserAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &user AppData"), QMessageBox::AcceptRole);
1509 const auto *btnOpenPrivateAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &private app location"), QMessageBox::AcceptRole);
1510
1511 mbox.addButton(QMessageBox::Close);
1512 mbox.setDefaultButton(QMessageBox::Close);
1513 mbox.exec();
1514
1515 if (mbox.clickedButton() == btnOpenPrivateAppData) {
1516 resourcePath = folderInPrivateAppData;
1517 } else if (mbox.clickedButton() == btnOpenUserAppData) {
1518 // no-op: resourcePath = resourceDir.absolutePath();
1519 } else {
1520 return;
1521 }
1522
1523
1524 }
1525#endif
1526 QDesktopServices::openUrl(QUrl::fromLocalFile(resourcePath));
1527}
1528
1530{
1531 if (mainWindow()) {
1533 Q_FOREACH (QDockWidget* dock, dockers) {
1534 KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
1535 if (titlebar) {
1536 titlebar->updateIcons();
1537 }
1538 if (qobject_cast<KoToolDocker*>(dock)) {
1539 // Tool options widgets icons are updated by KoToolManager
1540 continue;
1541 }
1542 QObjectList objects;
1543 objects.append(dock);
1544 while (!objects.isEmpty()) {
1545 QObject* object = objects.takeFirst();
1546 objects.append(object->children());
1548 }
1549 }
1550 }
1551}
1552
1561
1562void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1563{
1564 if (!d->currentImageView) return;
1565 d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1566
1567 Q_EMIT floatingMessageRequested(message, icon.name());
1568}
1569
1571{
1572 d->zoomMessage = message;
1574}
1575
1581
1583{
1584 int timeoutMsec = 500;
1585
1586 if (d->zoomRotationMessageTimer.hasExpired()) {
1587 messageToClear.clear();
1588 }
1589 d->zoomRotationMessageTimer.setRemainingTime(timeoutMsec);
1590
1591 QString message;
1592 bool haveZoomMessage = !d->zoomMessage.isEmpty();
1593 bool haveRotationMessage = !d->rotationMessage.isEmpty();
1594 if (haveZoomMessage) {
1595 if (haveRotationMessage) {
1596 message = QStringLiteral("%1\n%2").arg(d->zoomMessage, d->rotationMessage);
1597 } else {
1598 message = d->zoomMessage;
1599 }
1600 } else if (haveRotationMessage) {
1601 message = d->rotationMessage;
1602 } else {
1603 return;
1604 }
1605
1606 showFloatingMessage(message, QIcon(), timeoutMsec, KisFloatingMessage::Low, Qt::AlignCenter);
1607}
1608
1610{
1611 return qobject_cast<KisMainWindow*>(d->mainWindow);
1612}
1613
1615{
1616 return mainWindow();
1617}
1618
1619
1621{
1622 if (!d->currentImageView) return;
1623 if (!d->currentImageView->canvasController()) return;
1624
1625 KisConfig cfg(true);
1626 bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1627
1628 if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1629 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1630 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1631 } else {
1632 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1633 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1634 }
1635}
1636
1638{
1639 KisConfig cfg(false);
1640 cfg.setShowRulers(value);
1641}
1642
1648
1650{
1651 d->showFloatingMessage = show;
1652}
1653
1654void KisViewManager::changeAuthorProfile(const QString &profileName)
1655{
1656 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1657 if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1658 appAuthorGroup.writeEntry("active-profile", "");
1659 } else {
1660 appAuthorGroup.writeEntry("active-profile", profileName);
1661 }
1662 appAuthorGroup.sync();
1663 Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1665 }
1666}
1667
1669{
1670 Q_ASSERT(d->actionAuthor);
1671 if (!d->actionAuthor) {
1672 return;
1673 }
1674 d->actionAuthor->clear();
1675 d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1676
1677 KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1678 QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1679 QString authorInfo = KoResourcePaths::getAppDataLocation() + "/authorinfo/";
1680 QStringList filters = QStringList() << "*.authorinfo";
1681 QDir dir(authorInfo);
1682 Q_FOREACH(QString entry, dir.entryList(filters)) {
1683 int ln = QString(".authorinfo").size();
1684 entry.chop(ln);
1685 if (!profiles.contains(entry)) {
1686 profiles.append(entry);
1687 }
1688 }
1689 Q_FOREACH (const QString &profile , profiles) {
1690 d->actionAuthor->addAction(profile);
1691 }
1692
1693 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1694 QString profileName = appAuthorGroup.readEntry("active-profile", "");
1695
1696 if (profileName == "anonymous" || profileName.isEmpty()) {
1697 d->actionAuthor->setCurrentItem(0);
1698 } else if (profiles.contains(profileName)) {
1699 d->actionAuthor->setCurrentAction(profileName);
1700 }
1701}
1702
1712
1714{
1715 if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1716 KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1717
1718 QSet<KoShape*> dummy;
1719 // Start a new stroke
1720 tool->deactivate();
1721 tool->activate(dummy);
1722 }
1723
1724 KoToolManager::instance()->switchToolRequested("KisToolTransform");
1725}
1726
1742
1744{
1745 // see a comment in slotToggleFgBg()
1748}
1749
1751{
1752 KisConfig cfg(true);
1753
1754 OutlineStyle style;
1755
1756 if (cfg.newOutlineStyle() != OUTLINE_NONE) {
1757 style = OUTLINE_NONE;
1759 } else {
1760 style = cfg.lastUsedOutlineStyle();
1762 }
1763
1764 cfg.setNewOutlineStyle(style);
1765
1766 Q_EMIT brushOutlineToggled();
1767}
1768
1770{
1771 KisCanvasController *canvasController = d->currentImageView->canvasController();
1772 canvasController->resetCanvasRotation();
1773}
1774
1776{
1777 KisCanvasController *canvasController = d->currentImageView->canvasController();
1778 canvasController->resetCanvasRotation();
1779 canvasController->mirrorCanvas(false);
1781}
1782
1783void KisViewManager::slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
1784{
1785 if (isOpacityPresetMode) {
1787 }
1788 else {
1790 }
1791}
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:865
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:747
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
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: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())