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
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 QRegularExpression regex("_(\\d{1,4})([a-z])?([\\.|~])");
1016 QRegularExpressionMatch match;
1017 foundVersion = fileName.contains(regex, &match);
1018 isBackup = foundVersion ? match.captured(3) == "~" : false;
1019
1020 // If the filename has a version, prepare it for incrementation
1021 if (foundVersion) {
1022 version = match.captured(1);
1023 letter = match.captured(2);
1024 } else {
1025 // ...else, simply add a version to it so the next loop works
1026 QRegularExpression regex2("\\.\\w{2,4}$"); // Heuristic to find file extension
1027 QRegularExpressionMatch match = regex2.match(fileName);
1028 QString extensionPlusVersion = match.captured(0);
1029 extensionPlusVersion.prepend(version);
1030 extensionPlusVersion.prepend("_");
1031 fileName.replace(regex2, extensionPlusVersion);
1032 }
1033
1034 // Prepare the base for new version filename
1035 int intVersion = version.toInt(0);
1036 ++intVersion;
1037 QString baseNewVersion = QString::number(intVersion);
1038 while (baseNewVersion.length() < version.length()) {
1039 baseNewVersion.prepend("0");
1040 }
1041
1042 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1043 do {
1044 newVersion = baseNewVersion;
1045 newVersion.prepend("_");
1046 if (!letter.isNull()) newVersion.append(letter);
1047 if (isBackup) {
1048 newVersion.append("~");
1049 } else {
1050 newVersion.append(".");
1051 }
1052 fileName.replace(regex, newVersion);
1053 fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
1054 if (fileAlreadyExists) {
1055 if (!letter.isNull()) {
1056 char letterCh = letter.at(0).toLatin1();
1057 ++letterCh;
1058 letter = QString(QChar(letterCh));
1059 } else {
1060 letter = 'a';
1061 }
1062 }
1063 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1064
1065 if (letter == "{") {
1066 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1067 return;
1068 }
1069 QString newFilePath = path + '/' + fileName;
1070 document()->setFileBatchMode(true);
1071 document()->saveAs(newFilePath, document()->mimeType(), true);
1072 document()->setFileBatchMode(false);
1073 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath),
1074 QUrl::fromLocalFile(document()->path()));
1075}
1076
1078{
1079 if (!document()) return;
1080
1081 if (document()->path().isEmpty()) {
1082 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1083 mw->saveDocument(document(), true, false);
1084 return;
1085 }
1086
1087 bool workingOnBackup;
1088 bool fileAlreadyExists;
1089 QString version = "000";
1090 QString newVersion;
1091 QString letter;
1092 QString path = canonicalPath();
1093 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1094
1095 // First, discover if working on a backup file, or a normal file
1096 QRegularExpression regex("~(\\d{1,4})([a-z])?\\.");
1097 QRegularExpressionMatch match;
1098 workingOnBackup = fileName.contains(regex, &match);
1099
1100 if (workingOnBackup) {
1101 // Try to save incremental version (of backup), use letter for alt versions
1102 version = match.captured(1);
1103 letter = match.captured(2);
1104
1105 // Prepare the base for new version filename
1106 int intVersion = version.toInt(0);
1107 ++intVersion;
1108 QString baseNewVersion = QString::number(intVersion);
1109 QString backupFileName = document()->localFilePath();
1110 while (baseNewVersion.length() < version.length()) {
1111 baseNewVersion.prepend("0");
1112 }
1113
1114 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1115 do {
1116 newVersion = baseNewVersion;
1117 newVersion.prepend("~");
1118 if (!letter.isNull()) newVersion.append(letter);
1119 newVersion.append(".");
1120 backupFileName.replace(regex, newVersion);
1121 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1122 if (fileAlreadyExists) {
1123 if (!letter.isNull()) {
1124 char letterCh = letter.at(0).toLatin1();
1125 ++letterCh;
1126 letter = QString(QChar(letterCh));
1127 } else {
1128 letter = 'a';
1129 }
1130 }
1131 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1132
1133 if (letter == "{") {
1134 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1135 return;
1136 }
1137 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1138 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1139 }
1140 else { // if NOT working on a backup...
1141 // Navigate directory searching for latest backup version, ignore letters
1142 const quint8 HARDCODED_DIGIT_COUNT = 3;
1143 QString baseNewVersion = "000";
1144 QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1145 QRegularExpression regex2("\\.\\w{2,4}$"); // Heuristic to find file extension
1146 QRegularExpressionMatch match = regex2.match(fileName);
1147 QString extensionPlusVersion = match.captured(0);
1148 extensionPlusVersion.prepend(baseNewVersion);
1149 extensionPlusVersion.prepend("~");
1150 backupFileName.replace(regex2, extensionPlusVersion);
1151
1152 // Save version with 1 number higher than the highest version found ignoring letters
1153 do {
1154 newVersion = baseNewVersion;
1155 newVersion.prepend("~");
1156 newVersion.append(".");
1157 backupFileName.replace(regex, newVersion);
1158 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1159 if (fileAlreadyExists) {
1160 // Prepare the base for new version filename, increment by 1
1161 int intVersion = baseNewVersion.toInt(0);
1162 ++intVersion;
1163 baseNewVersion = QString::number(intVersion);
1164 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1165 baseNewVersion.prepend("0");
1166 }
1167 }
1168 } while (fileAlreadyExists);
1169
1170 // Save both as backup and on current file for interapplication workflow
1171 document()->setFileBatchMode(true);
1172 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1173 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1174 document()->setFileBatchMode(false);
1175 }
1176}
1177
1179{
1180 // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1181 // this is for Bug 250944
1182 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1183 // see KisToolFreehand::initPaint() and endPaint()
1184 d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1185 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1186 child->installEventFilter(&d->blockingEventFilter);
1187 }
1188}
1189
1191{
1192 d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1193 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1194 child->removeEventFilter(&d->blockingEventFilter);
1195 }
1196}
1197
1199{
1200 KisMainWindow *mw = mainWindow();
1201 if(mw && mw->statusBar()) {
1202 mw->statusBar()->setVisible(toggled);
1203 KisConfig cfg(false);
1204 cfg.setShowStatusBar(toggled);
1205 }
1206}
1207
1209{
1210 d->canvasStateInNormalMode.clear();
1212 d->canvasOnlyOptions = std::nullopt;
1213}
1214
1216{
1217 if (toggled == d->inCanvasOnlyMode) {
1219 return;
1220 }
1221
1222 KisConfig cfg(false);
1224
1225 if(!main) {
1226 dbgUI << "Unable to switch to canvas-only mode, main window not found";
1228 return;
1229 }
1230
1231#ifdef Q_OS_ANDROID
1232 // On Android, expanded tool bars will crash when canvas-only mode is
1233 // toggled. To avoid this, we go looking for expanded toolbars and close
1234 // them instead of switching the mode and hitting a crash. Note that this
1235 // only works properly because we turn off main window animations on
1236 // Android, since that means clicking the extension button will instantly
1237 // hide the menu. With animations, the user could still trigger a crash if
1238 // they tried switching the mode while the extension animation is running.
1239 QList<QToolBar *> toolBars = main->findChildren<QToolBar *>();
1240 bool wasToolBarPopupOpen = false;
1241 for (QToolBar *toolBar : toolBars) {
1242 for (QToolButton *button : toolBar->findChildren<QToolButton *>(QStringLiteral("qt_toolbar_ext_button"))) {
1243 if (button->isChecked()) {
1244 wasToolBarPopupOpen = true;
1245 button->click();
1246 }
1247 }
1248 }
1249
1250 if(wasToolBarPopupOpen) {
1252 return;
1253 }
1254#endif
1255
1256 cfg.writeEntry("CanvasOnlyActive", toggled);
1257 d->inCanvasOnlyMode = toggled;
1259
1261
1262 if (toggled) {
1263 d->canvasStateInNormalMode = qtMainWindow()->saveState();
1264 } else {
1265 d->canvasStateInCanvasOnlyMode = qtMainWindow()->saveState();
1266 d->canvasOnlyOptions = options;
1267 }
1268
1269 const bool toggleFullscreen = (options.hideTitlebarFullscreen && !cfg.fullscreenMode());
1270 const bool useCanvasOffsetCompensation = d->currentImageView &&
1271 d->currentImageView->canvasController() &&
1272 d->currentImageView->isMaximized() &&
1273 !main->canvasDetached();
1274
1275 if (useCanvasOffsetCompensation) {
1276 // The offset is calculated in two steps; this is the first step.
1277 if (toggled) {
1307 QPoint origin;
1308 if (toggleFullscreen) {
1309 // We're windowed, so also capture the position of the window in the screen.
1310 origin = main->geometry().topLeft() - main->screen()->geometry().topLeft();
1311 }
1313 } else {
1314 // Restore the original canvas position. The result is more stable if we pan before showing the UI elements.
1315 d->currentImageView->canvasController()->pan(- d->canvasOnlyOffsetCompensation);
1316 }
1317 }
1318
1319 if (options.hideStatusbarFullscreen) {
1320 if (main->statusBar()) {
1321 if (!toggled) {
1322 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1323 if (main->statusBar()->property("wasvisible").toBool()) {
1324 main->statusBar()->setVisible(true);
1325 }
1326 }
1327 }
1328 else {
1329 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1330 main->statusBar()->setVisible(false);
1331 }
1332 }
1333 }
1334
1335 if (options.hideDockersFullscreen) {
1336 KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1337 if (action) {
1338 action->setCheckable(true);
1339 if (toggled) {
1340 if (action->isChecked()) {
1341 cfg.setShowDockers(action->isChecked());
1342 action->setChecked(false);
1343 } else {
1344 cfg.setShowDockers(false);
1345 }
1346 } else {
1347 action->setChecked(cfg.showDockers());
1348 }
1349 }
1350 }
1351
1352 // QT in windows does not return to maximized upon 4th tab in a row
1353 // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1354 if (toggleFullscreen) {
1355 if(toggled) {
1356 main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1357 } else {
1358 main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1359 }
1360 }
1361
1362 if (options.hideMenuFullscreen) {
1363 if (!toggled) {
1364 if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1365 if (main->menuBar()->property("wasvisible").toBool()) {
1366 main->menuBar()->setVisible(true);
1367 }
1368 }
1369 }
1370 else {
1371 main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1372 main->menuBar()->setVisible(false);
1373 }
1374 }
1375
1376 if (options.hideToolbarFullscreen) {
1377 // We already went searching for these on Android above.
1378#ifndef Q_OS_ANDROID
1379 QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1380#endif
1381 Q_FOREACH (QToolBar* toolbar, toolBars) {
1382 if (!toggled) {
1383 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1384 if (toolbar->property("wasvisible").toBool()) {
1385 toolbar->setVisible(true);
1386 }
1387 }
1388 }
1389 else {
1390 toolbar->setProperty("wasvisible", toolbar->isVisible());
1391 toolbar->setVisible(false);
1392 }
1393 }
1394 }
1395
1397
1398 if (toggled) {
1399 if (!d->canvasStateInCanvasOnlyMode.isEmpty() &&
1401 *d->canvasOnlyOptions == options) {
1402
1412 QTimer::singleShot(0, this, [this] () {
1413 this->mainWindow()->restoreState(d->canvasStateInCanvasOnlyMode);
1414 });
1415 }
1416
1417 // show a fading heads-up display about the shortcut to go back
1418 showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1419 actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon(),
1420 2000,
1422 }
1423 else {
1424 if (!d->canvasStateInNormalMode.isEmpty()) {
1425 main->restoreState(d->canvasStateInNormalMode);
1426 }
1427 }
1428
1429 if (useCanvasOffsetCompensation && toggled) {
1430 const KoZoomMode::Mode mode = d->currentImageView->canvasController()->zoomState().mode;
1431
1432 const bool allowedZoomMode =
1433 (mode == KoZoomMode::ZOOM_CONSTANT) ||
1434 (mode == KoZoomMode::ZOOM_HEIGHT);
1435
1436 if (allowedZoomMode) {
1437 // Defer the pan action until the layout is fully settled in (including the menu bars, etc.).
1438 QTimer::singleShot(0, this, [this] () {
1439 // Compensate by the difference of (after - before) layout.
1441 d->currentImageView->canvasController()->pan(d->canvasOnlyOffsetCompensation);
1442 });
1443 } else {
1444 // Nothing to restore.
1445 d->canvasOnlyOffsetCompensation = QPoint();
1446 }
1447 }
1448}
1449
1451{
1452 QAction *action = actionManager()->actionByName(QStringLiteral("view_show_canvas_only"));
1453 if (action && action->isChecked() != d->inCanvasOnlyMode) {
1454 QSignalBlocker blocker(action);
1455 action->setChecked(d->inCanvasOnlyMode);
1456 }
1457}
1458
1463
1465{
1466 QString resourcePath = KisResourceLocator::instance()->resourceLocationBase();
1467#ifdef Q_OS_WIN
1468
1469 QString folderInStandardAppData;
1470 QString folderInPrivateAppData;
1471 KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(folderInStandardAppData, folderInPrivateAppData);
1472
1473 if (!folderInPrivateAppData.isEmpty()) {
1474
1475 const auto pathToDisplay = [](const QString &path) {
1476 // Due to how Unicode word wrapping works, the string does not
1477 // wrap after backslashes in Qt 5.12. We don't want the path to
1478 // become too long, so we add a U+200B ZERO WIDTH SPACE to allow
1479 // wrapping. The downside is that we cannot let the user select
1480 // and copy the path because it now contains invisible unicode
1481 // code points.
1482 // See: https://bugreports.qt.io/browse/QTBUG-80892
1483 return QDir::toNativeSeparators(path).replace(QChar('\\'), QStringLiteral(u"\\\u200B"));
1484 };
1485
1486 QMessageBox mbox(qApp->activeWindow());
1487 mbox.setIcon(QMessageBox::Information);
1488 mbox.setWindowTitle(i18nc("@title:window resource folder", "Open Resource Folder"));
1489 // Similar text is also used in kis_dlg_preferences.cc
1490
1491 mbox.setText(i18nc("@info resource folder",
1492 "<p>You are using the Microsoft Store package version of Krita. "
1493 "Even though Krita can be configured to place resources under the "
1494 "user AppData location, Windows may actually store the files "
1495 "inside a private app location.</p>\n"
1496 "<p>You should check both locations to determine where "
1497 "the files are located.</p>\n"
1498 "<p><b>User AppData</b>:<br/>\n"
1499 "%1</p>\n"
1500 "<p><b>Private app location</b>:<br/>\n"
1501 "%2</p>",
1502 pathToDisplay(folderInStandardAppData),
1503 pathToDisplay(folderInPrivateAppData)
1504 ));
1505 mbox.setTextInteractionFlags(Qt::NoTextInteraction);
1506
1507 const auto *btnOpenUserAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &user AppData"), QMessageBox::AcceptRole);
1508 const auto *btnOpenPrivateAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &private app location"), QMessageBox::AcceptRole);
1509
1510 mbox.addButton(QMessageBox::Close);
1511 mbox.setDefaultButton(QMessageBox::Close);
1512 mbox.exec();
1513
1514 if (mbox.clickedButton() == btnOpenPrivateAppData) {
1515 resourcePath = folderInPrivateAppData;
1516 } else if (mbox.clickedButton() == btnOpenUserAppData) {
1517 // no-op: resourcePath = resourceDir.absolutePath();
1518 } else {
1519 return;
1520 }
1521
1522
1523 }
1524#endif
1525 QDesktopServices::openUrl(QUrl::fromLocalFile(resourcePath));
1526}
1527
1529{
1530 if (mainWindow()) {
1532 Q_FOREACH (QDockWidget* dock, dockers) {
1533 KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
1534 if (titlebar) {
1535 titlebar->updateIcons();
1536 }
1537 if (qobject_cast<KoToolDocker*>(dock)) {
1538 // Tool options widgets icons are updated by KoToolManager
1539 continue;
1540 }
1541 QObjectList objects;
1542 objects.append(dock);
1543 while (!objects.isEmpty()) {
1544 QObject* object = objects.takeFirst();
1545 objects.append(object->children());
1547 }
1548 }
1549 }
1550}
1551
1560
1561void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1562{
1563 if (!d->currentImageView) return;
1564 d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1565
1566 Q_EMIT floatingMessageRequested(message, icon.name());
1567}
1568
1570{
1571 d->zoomMessage = message;
1573}
1574
1580
1582{
1583 int timeoutMsec = 500;
1584
1585 if (d->zoomRotationMessageTimer.hasExpired()) {
1586 messageToClear.clear();
1587 }
1588 d->zoomRotationMessageTimer.setRemainingTime(timeoutMsec);
1589
1590 QString message;
1591 bool haveZoomMessage = !d->zoomMessage.isEmpty();
1592 bool haveRotationMessage = !d->rotationMessage.isEmpty();
1593 if (haveZoomMessage) {
1594 if (haveRotationMessage) {
1595 message = QStringLiteral("%1\n%2").arg(d->zoomMessage, d->rotationMessage);
1596 } else {
1597 message = d->zoomMessage;
1598 }
1599 } else if (haveRotationMessage) {
1600 message = d->rotationMessage;
1601 } else {
1602 return;
1603 }
1604
1605 showFloatingMessage(message, QIcon(), timeoutMsec, KisFloatingMessage::Low, Qt::AlignCenter);
1606}
1607
1609{
1610 return qobject_cast<KisMainWindow*>(d->mainWindow);
1611}
1612
1614{
1615 return mainWindow();
1616}
1617
1618
1620{
1621 if (!d->currentImageView) return;
1622 if (!d->currentImageView->canvasController()) return;
1623
1624 KisConfig cfg(true);
1625 bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1626
1627 if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1628 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1629 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1630 } else {
1631 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1632 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1633 }
1634}
1635
1637{
1638 KisConfig cfg(false);
1639 cfg.setShowRulers(value);
1640}
1641
1647
1649{
1650 d->showFloatingMessage = show;
1651}
1652
1653void KisViewManager::changeAuthorProfile(const QString &profileName)
1654{
1655 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1656 if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1657 appAuthorGroup.writeEntry("active-profile", "");
1658 } else {
1659 appAuthorGroup.writeEntry("active-profile", profileName);
1660 }
1661 appAuthorGroup.sync();
1662 Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1664 }
1665}
1666
1668{
1669 Q_ASSERT(d->actionAuthor);
1670 if (!d->actionAuthor) {
1671 return;
1672 }
1673 d->actionAuthor->clear();
1674 d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1675
1676 KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1677 QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1678 QString authorInfo = KoResourcePaths::getAppDataLocation() + "/authorinfo/";
1679 QStringList filters = QStringList() << "*.authorinfo";
1680 QDir dir(authorInfo);
1681 Q_FOREACH(QString entry, dir.entryList(filters)) {
1682 int ln = QString(".authorinfo").size();
1683 entry.chop(ln);
1684 if (!profiles.contains(entry)) {
1685 profiles.append(entry);
1686 }
1687 }
1688 Q_FOREACH (const QString &profile , profiles) {
1689 d->actionAuthor->addAction(profile);
1690 }
1691
1692 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1693 QString profileName = appAuthorGroup.readEntry("active-profile", "");
1694
1695 if (profileName == "anonymous" || profileName.isEmpty()) {
1696 d->actionAuthor->setCurrentItem(0);
1697 } else if (profiles.contains(profileName)) {
1698 d->actionAuthor->setCurrentAction(profileName);
1699 }
1700}
1701
1711
1713{
1714 if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1715 KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1716
1717 QSet<KoShape*> dummy;
1718 // Start a new stroke
1719 tool->deactivate();
1720 tool->activate(dummy);
1721 }
1722
1723 KoToolManager::instance()->switchToolRequested("KisToolTransform");
1724}
1725
1741
1743{
1744 // see a comment in slotToggleFgBg()
1747}
1748
1750{
1751 KisConfig cfg(true);
1752
1753 OutlineStyle style;
1754
1755 if (cfg.newOutlineStyle() != OUTLINE_NONE) {
1756 style = OUTLINE_NONE;
1758 } else {
1759 style = cfg.lastUsedOutlineStyle();
1761 }
1762
1763 cfg.setNewOutlineStyle(style);
1764
1765 Q_EMIT brushOutlineToggled();
1766}
1767
1769{
1770 KisCanvasController *canvasController = d->currentImageView->canvasController();
1771 canvasController->resetCanvasRotation();
1772}
1773
1775{
1776 KisCanvasController *canvasController = d->currentImageView->canvasController();
1777 canvasController->resetCanvasRotation();
1778 canvasController->mirrorCanvas(false);
1780}
1781
1782void KisViewManager::slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
1783{
1784 if (isOpacityPresetMode) {
1786 }
1787 else {
1789 }
1790}
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:825
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())