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 <QStandardPaths>
25#include <QScreen>
26#include <QDesktopServices>
27#include <QGridLayout>
28#include <QMainWindow>
29#include <QMenu>
30#include <QMenuBar>
31#include <QMessageBox>
32#include <QObject>
33#include <QPoint>
34#include <QPrintDialog>
35#include <QPushButton>
36#include <QScreen>
37#include <QScrollBar>
38#include <QStatusBar>
39#include <QToolBar>
40#include <QUrl>
41#include <QWidget>
42#include <QActionGroup>
43#include <QRegExp>
44
45#include <kactioncollection.h>
46#include <klocalizedstring.h>
47#include <KoResourcePaths.h>
48#include <kselectaction.h>
49
50#include <KoCanvasController.h>
51#include <KoCompositeOp.h>
52#include <KoDockRegistry.h>
54#include <KoFileDialog.h>
55#include <KoProperties.h>
57#include <KoSelection.h>
58#include <KoStore.h>
59#include <KoToolManager.h>
60#include <KoToolRegistry.h>
61#include <KoViewConverter.h>
62#include <KoZoomHandler.h>
63#include <KoPluginLoader.h>
64#include <KoDocumentInfo.h>
66#include <KisResourceLocator.h>
67
69#include "canvas/kis_canvas2.h"
73#include "kis_action_manager.h"
74#include "kis_action.h"
78#include <KoProgressUpdater.h>
79#include "kis_config.h"
80#include "kis_config_notifier.h"
81#include "kis_control_frame.h"
82#include "KisDocument.h"
84#include "kis_filter_manager.h"
85#include "kis_group_layer.h"
86#include <kis_image.h>
87#include "kis_image_manager.h"
88#include <kis_layer.h>
90#include "kis_mask_manager.h"
91#include "kis_mirror_manager.h"
93#include "kis_node.h"
94#include "kis_node_manager.h"
96#include <kis_paint_layer.h>
97#include "kis_paintop_box.h"
99#include "KisPart.h"
100#include <KoUpdater.h>
101#include "kis_selection_mask.h"
103#include "kis_shape_controller.h"
104#include "kis_shape_layer.h"
106#include "kis_statusbar.h"
107#include <KisTemplateCreateDia.h>
108#include <kis_tool_freehand.h>
109#include <kis_undo_adapter.h>
110#include "KisView.h"
111#include "kis_zoom_manager.h"
114#include "kis_icon_utils.h"
115#include "kis_guides_manager.h"
119#include <KisMainWindow.h>
120#include "kis_signals_blocker.h"
121#include "imagesize/imagesize.h"
122#include <KoToolDocker.h>
123#include <KisIdleTasksManager.h>
124#include <KisImageBarrierLock.h>
126#include <kis_selection.h>
127
128#ifdef Q_OS_WIN
130#endif
131
132class BlockingUserInputEventFilter : public QObject
133{
134 bool eventFilter(QObject *watched, QEvent *event) override
135 {
136 Q_UNUSED(watched);
137 if(dynamic_cast<QWheelEvent*>(event)
138 || dynamic_cast<QKeyEvent*>(event)
139 || dynamic_cast<QMouseEvent*>(event)) {
140 return true;
141 }
142 else {
143 return false;
144 }
145 }
146};
147
149{
150
151public:
152
153 KisViewManagerPrivate(KisViewManager *_q, KisKActionCollection *_actionCollection, QWidget *_q_parent)
154 : filterManager(_q)
155 , selectionManager(_q)
156 , statusBar(_q)
157 , controlFrame(_q, _q_parent)
158 , nodeManager(_q)
159 , imageManager(_q)
160 , gridManager(_q)
163 , actionManager(_q, _actionCollection)
166 , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q)
167 , actionCollection(_actionCollection)
168 , mirrorManager(_q)
169 , inputManager(_q)
170 {
172 }
173
174public:
188 QActionGroup *wrapAroundAxisActions {nullptr};
193 KisAction *zoomIn {nullptr};
194 KisAction *zoomOut {nullptr};
206
211
212 QScopedPointer<KoProgressUpdater> persistentUnthreadedProgressUpdaterRouter;
214
223 QMainWindow* mainWindow {nullptr};
235
237 KSelectAction *actionAuthor {nullptr}; // Select action for author profile.
239
242
268 std::optional<CanvasOnlyOptions> canvasOnlyOptions;
270
272};
273
274KisViewManager::KisViewManager(QWidget *parent, KisKActionCollection *_actionCollection)
275 : d(new KisViewManagerPrivate(this, _actionCollection, parent))
276{
277 d->actionCollection = _actionCollection;
278 d->mainWindow = dynamic_cast<QMainWindow*>(parent);
280 connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout()));
281
284
285 // These initialization functions must wait until KisViewManager ctor is complete.
286 d->statusBar.setup();
288 d->statusBar.progressUpdater()->startSubtask(1, "", true);
289 // reset state to "completed"
290 d->persistentImageProgressUpdater->setRange(0,100);
291 d->persistentImageProgressUpdater->setValue(100);
292
294 d->statusBar.progressUpdater()->startSubtask(1, "", true);
295 // reset state to "completed"
296 d->persistentUnthreadedProgressUpdater->setRange(0,100);
298
302 d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true);
304
305 // just a clumsy way to mark the updater as completed, the subtask will
306 // be automatically deleted on completion...
307 d->persistentUnthreadedProgressUpdaterRouter->startSubtask()->setProgress(100);
308
309 d->controlFrame.setup(parent);
310
311
312 //Check to draw scrollbars after "Canvas only mode" toggle is created.
313 this->showHideScrollbars();
314
316
317 connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)),
318 d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice)));
319
320 connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*)),
321 d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*)));
322
323 connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)),
324 canvasResourceProvider(), SLOT(slotNodeActivated(KisNodeSP)));
325
326 connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*)));
327 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*)));
328 connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)),
329 d->controlFrame.paintopBox(), SLOT(updatePresetConfig()));
330
331 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions()));
332 connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction()));
333
334 connect(KoToolManager::instance(), SIGNAL(createOpacityResource(bool, KoToolBase*)), SLOT(slotCreateOpacityResource(bool, KoToolBase*)));
335
337
338 KisConfig cfg(true);
341 KoColor foreground(Qt::black, cs);
342 d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground));
343 KoColor background(Qt::white, cs);
344 d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background));
345 d->canvasResourceProvider.setColorHistory(cfg.readKoColors("LastColorHistory"));
347
348 // Initialize the old imagesize plugin
349 new ImageSize(this);
350}
351
352
354{
355 KisConfig cfg(false);
356 if (canvasResourceProvider() && canvasResourceProvider()->currentPreset()) {
357 cfg.writeKoColor("LastForeGroundColor",canvasResourceProvider()->fgColor());
358 cfg.writeKoColor("LastBackGroundColor",canvasResourceProvider()->bgColor());
359 }
360
362 cfg.writeKoColors("LastColorHistory", canvasResourceProvider()->colorHistory());
363 }
364
365 cfg.writeEntry("baseLength", KisResourceItemChooserSync::instance()->baseLength());
366 cfg.writeEntry("CanvasOnlyActive", false); // We never restart in CanvasOnlyMode
367 delete d;
368}
369
371
373{
388
389 resourceManager->addActiveCanvasResourceDependency(
393
394 resourceManager->addActiveCanvasResourceDependency(
398
399 resourceManager->addActiveCanvasResourceDependency(
403
404 KSharedConfigPtr config = KSharedConfig::openConfig();
405 KConfigGroup miscGroup = config->group("Misc");
406 const uint handleRadius = miscGroup.readEntry("HandleRadius", 5);
407 resourceManager->setHandleRadius(handleRadius);
408}
409
414
419
421{
422 // WARNING: this slot is called even when a view from another main windows is added!
423 // Don't expect \p view be a child of this view manager!
424
425 if (view->viewManager() == this && viewCount() == 0) {
427 }
428}
429
431{
432 // WARNING: this slot is called even when a view from another main windows is removed!
433 // Don't expect \p view be a child of this view manager!
434
435 if (view->viewManager() == this && viewCount() == 0) {
437 }
438}
439
441{
442 if (d->currentImageView) {
443 d->currentImageView->notifyCurrentStateChanged(false);
444
445 d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor));
446 KisDocument* doc = d->currentImageView->document();
447 if (doc) {
449 doc->disconnect(this);
450 }
451 d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar);
454 }
455
456 QPointer<KisView> imageView = qobject_cast<KisView*>(view);
457 d->currentImageView = imageView;
458
459 if (imageView) {
463
464 d->softProof->setChecked(imageView->softProofing());
465 d->gamutCheck->setChecked(imageView->gamutCheck());
466
467 // Wait for the async image to have loaded
468 KisDocument* doc = imageView->document();
469
470 if (KisConfig(true).readEntry<bool>("EnablePositionLabel", false)) {
471 connect(d->currentImageView->canvasController()->proxyObject,
472 SIGNAL(documentMousePositionChanged(QPointF)),
473 &d->statusBar,
474 SLOT(documentMousePositionChanged(QPointF)));
475 }
476
477 KisCanvasController *canvasController = dynamic_cast<KisCanvasController*>(d->currentImageView->canvasController());
478 KIS_ASSERT(canvasController);
479
480 d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode()));
481 d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15()));
482 d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15()));
483 d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation()));
484
485 d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool)));
486 d->wrapAroundAction->setChecked(canvasController->wrapAroundMode());
487 d->viewConnections.addUniqueConnection(d->wrapAroundHVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisHV()));
488 d->wrapAroundHVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_BOTH);
489 d->viewConnections.addUniqueConnection(d->wrapAroundHAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisH()));
490 d->wrapAroundHAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_HORIZONTAL);
491 d->viewConnections.addUniqueConnection(d->wrapAroundVAxisAction, SIGNAL(triggered()), canvasController, SLOT(slotSetWrapAroundModeAxisV()));
492 d->wrapAroundVAxisAction->setChecked(canvasController->wrapAroundModeAxis() == WRAPAROUND_VERTICAL);
493
494 d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool)));
495 d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode());
496
497 d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*)));
498 d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool)));
499 d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool)));
500 d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100()));
501 d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomIn()));
502 d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomOut()));
503 d->viewConnections.addUniqueConnection(d->zoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFit()));
504 d->viewConnections.addUniqueConnection(d->zoomToFitWidth, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitWidth()));
505 d->viewConnections.addUniqueConnection(d->zoomToFitHeight, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotZoomToFitHeight()));
506 d->viewConnections.addUniqueConnection(d->toggleZoomToFit, SIGNAL(triggered()), imageView->zoomManager(), SLOT(slotToggleZoomToFit()));
507
508 d->viewConnections.addUniqueConnection(d->resetDisplay, SIGNAL(triggered()), imageView->viewManager(), SLOT(slotResetDisplay()));
509
510 d->viewConnections.addConnection(imageView->canvasController(),
512 this,
513 [this](bool value) {
514 QSignalBlocker b(d->viewPrintSize);
515 d->viewPrintSize->setChecked(value);
516 });
517 d->viewPrintSize->setChecked(imageView->canvasController()->usePrintResolutionMode());
518 d->viewConnections.addUniqueConnection(d->viewPrintSize, &KisAction::toggled,
519 imageView->canvasController(), &KisCanvasController::setUsePrintResolutionMode);
520
521 d->viewConnections.addUniqueConnection(imageView->canvasController(),
523 imageView->zoomManager()->zoomAction(),
525 imageView->zoomManager()->zoomAction()->setUsePrintResolutionMode(imageView->canvasController()->usePrintResolutionMode());
526 d->viewConnections.addUniqueConnection(imageView->zoomManager()->zoomAction(),
528 imageView->canvasController(),
530
531 d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) );
532 d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) );
533
534 // set up progress reporting
536 d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation()));
537
538 d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool)));
539
540 imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked());
541 imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked());
542
544 }
545
546 d->filterManager.setView(imageView);
547 d->selectionManager.setView(imageView);
548 d->guidesManager.setView(imageView);
549 d->nodeManager.setView(imageView);
550 d->imageManager.setView(imageView);
551 d->canvasControlsManager.setView(imageView);
552 d->actionManager.setView(imageView);
553 d->gridManager.setView(imageView);
554 d->statusBar.setView(imageView);
556 d->mirrorManager.setView(imageView);
557
558 if (d->currentImageView) {
559 d->currentImageView->notifyCurrentStateChanged(true);
560 d->currentImageView->canvasController()->activate();
561 d->currentImageView->canvasController()->setFocus();
562
564 image(), SIGNAL(sigSizeChanged(QPointF,QPointF)),
565 canvasResourceProvider(), SLOT(slotImageSizeChanged()));
566
568 image(), SIGNAL(sigResolutionChanged(double,double)),
569 canvasResourceProvider(), SLOT(slotOnScreenResolutionChanged()));
570
572 image(), SIGNAL(sigNodeChanged(KisNodeSP)),
573 this, SLOT(updateGUI()));
574
576 d->currentImageView->canvasController()->proxyObject,
580 }
581
583
586
587 Q_EMIT viewChanged();
588}
589
591{
592 if (document()) {
593 return document()->image();
594 }
595 return 0;
596}
597
602
604{
605 if (d && d->currentImageView) {
606 return d->currentImageView->canvasBase();
607 }
608 return 0;
609}
610
612{
613 if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) {
614 return d->currentImageView->canvasBase()->canvasWidget();
615 }
616 return 0;
617}
618
620{
621 return &d->statusBar;
622}
623
628
630{
631 return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false);
632}
633
635{
636 return d->statusBar.progressUpdater()->startSubtask(1, name, false);
637}
638
643
648
653
658
660{
661 if (d->currentImageView) {
662 return d->currentImageView->zoomManager();
663 }
664 return 0;
665}
666
671
676
681
686
691
693{
694 if (d->currentImageView) {
695 return d->currentImageView->selection();
696 }
697 return 0;
698
699}
700
702{
703 KisLayerSP layer = activeLayer();
704 if (layer) {
705 KisSelectionMaskSP mask = layer->selectionMask();
706 if (mask) {
707 return mask->isEditable();
708 }
709 }
710 // global selection is always editable
711 return true;
712}
713
715{
716 if (!document()) return 0;
717
719 Q_ASSERT(image);
720
721 return image->undoAdapter();
722}
723
725{
726 KisConfig cfg(true);
727
728 d->saveIncremental = actionManager()->createAction("save_incremental_version");
729 connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental()));
730
731 d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup");
732 connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup()));
733
734 connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved()));
735
736 d->saveIncremental->setEnabled(false);
737 d->saveIncrementalBackup->setEnabled(false);
738
739 KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger");
740 connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger()));
741
742 d->createTemplate = actionManager()->createAction("create_template");
743 connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate()));
744
745 d->createCopy = actionManager()->createAction("create_copy");
746 connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy()));
747
748 d->openResourcesDirectory = actionManager()->createAction("open_resources_directory");
749 connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory()));
750
751 d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right");
752 d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left");
753 d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation");
754 d->wrapAroundAction = actionManager()->createAction("wrap_around_mode");
755 d->wrapAroundHVAxisAction = actionManager()->createAction("wrap_around_hv_axis");
756 d->wrapAroundHAxisAction = actionManager()->createAction("wrap_around_h_axis");
757 d->wrapAroundVAxisAction = actionManager()->createAction("wrap_around_v_axis");
758 d->wrapAroundAxisActions = new QActionGroup(this);
762 d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode");
763 d->softProof = actionManager()->createAction("softProof");
764 d->gamutCheck = actionManager()->createAction("gamutCheck");
765
766 KisAction *tAction = actionManager()->createAction("showStatusBar");
767 tAction->setChecked(cfg.showStatusBar());
768 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool)));
769
770 tAction = actionManager()->createAction("view_show_canvas_only");
771 tAction->setChecked(false);
772 connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool)));
773
774 //Workaround, by default has the same shortcut as mirrorCanvas
775 KisAction *a = dynamic_cast<KisAction*>(actionCollection()->action("format_italic"));
776 if (a) {
777 a->setDefaultShortcut(QKeySequence());
778 }
779
780 actionManager()->createAction("ruler_pixel_multiple2");
781 d->showRulersAction = actionManager()->createAction("view_ruler");
782 d->showRulersAction->setChecked(cfg.showRulers());
783 connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool)));
784
785 d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse");
786 d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse());
787 connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool)));
788
789 d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct");
790
793
794 d->zoomToFit = actionManager()->createAction("zoom_to_fit");
795 d->zoomToFitWidth = actionManager()->createAction("zoom_to_fit_width");
796 d->zoomToFitHeight = actionManager()->createAction("zoom_to_fit_height");
797 d->toggleZoomToFit = actionManager()->createAction("toggle_zoom_to_fit");
798
799 d->resetDisplay = actionManager()->createAction("reset_display");
800
801 d->viewPrintSize = actionManager()->createAction("view_print_size");
802
803 d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this);
804 connect(d->actionAuthor, SIGNAL(textTriggered(QString)), this, SLOT(changeAuthorProfile(QString)));
805 actionCollection()->addAction("settings_active_author", d->actionAuthor);
807
808 d->showPixelGrid = actionManager()->createAction("view_pixel_grid");
810
811 d->toggleFgBg = actionManager()->createAction("toggle_fg_bg");
812 connect(d->toggleFgBg, SIGNAL(triggered(bool)), this, SLOT(slotToggleFgBg()));
813
814 d->resetFgBg = actionManager()->createAction("reset_fg_bg");
815 connect(d->resetFgBg, SIGNAL(triggered(bool)), this, SLOT(slotResetFgBg()));
816
817 d->toggleBrushOutline = actionManager()->createAction("toggle_brush_outline");
818 connect(d->toggleBrushOutline, SIGNAL(triggered(bool)), this, SLOT(slotToggleBrushOutline()));
819
820}
821
823{
824 // Create the managers for filters, selections, layers etc.
825 // XXX: When the current layer changes, call updateGUI on all
826 // managers
827
829
831
833
835
837
839
841
843
845}
846
851
853{
854 return &d->nodeManager;
855}
856
861
863{
864 return &d->gridManager;
865}
866
871
873{
874 if (d->currentImageView && d->currentImageView->document()) {
875 return d->currentImageView->document();
876 }
877 return 0;
878}
879
881{
882 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
883 if (mw) {
884 return mw->viewCount();
885 }
886 return 0;
887}
888
890{
891 const int busyWaitDelay = 1000;
893 dialog.blockIfImageIsBusy();
894
895 return dialog.result() == QDialog::Accepted;
896}
897
898
903
908
910{
911 if (!document()) return;
912 KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow());
913}
914
916{
917 KisDocument *srcDoc = document();
918 if (!srcDoc) return;
919
920 if (!this->blockUntilOperationsFinished(srcDoc->image())) return;
921
922 KisDocument *doc = 0;
923 {
924 KisImageReadOnlyBarrierLock l(srcDoc->image());
925 doc = srcDoc->clone(true);
926 }
928
929 QString name = srcDoc->documentInfo()->aboutInfo("name");
930 if (name.isEmpty()) {
931 name = document()->path();
932 }
933 name = i18n("%1 (Copy)", name);
934 doc->documentInfo()->setAboutInfo("title", name);
935 doc->resetPath();
936
938 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
940}
941
942
944{
945 if (d->mainWindow)
946 return d->mainWindow;
947
948 //Fallback for when we have not yet set the main window.
949 QMainWindow* w = qobject_cast<QMainWindow*>(qApp->activeWindow());
950 if(w)
951 return w;
952
953 return mainWindow();
954}
955
956void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow)
957{
958 d->mainWindow = newMainWindow;
959}
960
962{
963 d->saveIncremental->setEnabled(true);
964 d->saveIncrementalBackup->setEnabled(true);
965}
966
968{
969#ifdef Q_OS_ANDROID
970 QString path = QFileInfo(document()->localFilePath()).canonicalPath();
971 // if the path is based on a document tree then a directory would be returned. So check if it exists and more
972 // importantly check if we have permissions
973 if (QDir(path).exists()) {
974 return path;
975 } else {
976 KoFileDialog dialog(nullptr, KoFileDialog::ImportDirectory, "OpenDirectory");
977 dialog.setDirectoryUrl(QUrl(document()->localFilePath()));
978 return dialog.filename();
979 }
980#else
981 return QFileInfo(document()->localFilePath()).canonicalPath();
982#endif
983}
984
986{
987 if (!document()) return;
988
989 if (document()->path().isEmpty()) {
990 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
991 mw->saveDocument(document(), true, false);
992 return;
993 }
994
995 bool foundVersion;
996 bool fileAlreadyExists;
997 bool isBackup;
998 QString version = "000";
999 QString newVersion;
1000 QString letter;
1001 QString path = canonicalPath();
1002
1003 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1004
1005 // Find current version filenames
1006 // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well
1007 // Considering our incremental version and backup scheme, format is filename_001~001.ext
1008 QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
1009 regex.indexIn(fileName); // Perform the search
1010 QStringList matches = regex.capturedTexts();
1011 foundVersion = matches.at(0).isEmpty() ? false : true;
1012
1013 // Ensure compatibility with Save Incremental Backup
1014 // If this regex is not kept separate, the entire algorithm needs modification;
1015 // It's simpler to just add this.
1016 QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]");
1017 regexAux.indexIn(fileName); // Perform the search
1018 QStringList matchesAux = regexAux.capturedTexts();
1019 isBackup = matchesAux.at(0).isEmpty() ? false : true;
1020
1021 // If the filename has a version, prepare it for incrementation
1022 if (foundVersion) {
1023 version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
1024#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1025 if (version.contains(QRegExp("[a-z]"))) {
1026#else
1027 if (QRegExp("[a-z]").containedIn(version)) {
1028#endif
1029
1030 version.chop(1); // Trim "."
1031 letter = version.right(1); // Save letter
1032 version.chop(1); // Trim letter
1033 } else {
1034 version.chop(1); // Trim "."
1035 }
1036 version.remove(0, 1); // Trim "_"
1037 } else {
1038 // TODO: this will not work with files extensions like jp2
1039 // ...else, simply add a version to it so the next loop works
1040 QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
1041 regex2.indexIn(fileName);
1042 QStringList matches2 = regex2.capturedTexts();
1043 QString extensionPlusVersion = matches2.at(0);
1044 extensionPlusVersion.prepend(version);
1045 extensionPlusVersion.prepend("_");
1046#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1047 fileName.replace(regex2, extensionPlusVersion);
1048#else
1049 regex2.replaceIn(fileName, extensionPlusVersion);
1050#endif
1051 }
1052
1053 // Prepare the base for new version filename
1054 int intVersion = version.toInt(0);
1055 ++intVersion;
1056 QString baseNewVersion = QString::number(intVersion);
1057 while (baseNewVersion.length() < version.length()) {
1058 baseNewVersion.prepend("0");
1059 }
1060
1061 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1062 do {
1063 newVersion = baseNewVersion;
1064 newVersion.prepend("_");
1065 if (!letter.isNull()) newVersion.append(letter);
1066 if (isBackup) {
1067 newVersion.append("~");
1068 } else {
1069 newVersion.append(".");
1070 }
1071#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1072 fileName.replace(regex, newVersion);
1073#else
1074 regex.replaceIn(fileName, newVersion);
1075#endif
1076 fileAlreadyExists = QFileInfo(path + '/' + fileName).exists();
1077 if (fileAlreadyExists) {
1078 if (!letter.isNull()) {
1079 char letterCh = letter.at(0).toLatin1();
1080 ++letterCh;
1081 letter = QString(QChar(letterCh));
1082 } else {
1083 letter = 'a';
1084 }
1085 }
1086 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1087
1088 if (letter == "{") {
1089 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1090 return;
1091 }
1092 QString newFilePath = path + '/' + fileName;
1093 document()->setFileBatchMode(true);
1094 document()->saveAs(newFilePath, document()->mimeType(), true);
1095 document()->setFileBatchMode(false);
1096 KisPart::instance()->queueAddRecentURLToAllMainWindowsOnFileSaved(QUrl::fromLocalFile(newFilePath),
1097 QUrl::fromLocalFile(document()->path()));
1098}
1099
1101{
1102 if (!document()) return;
1103
1104 if (document()->path().isEmpty()) {
1105 KisMainWindow *mw = qobject_cast<KisMainWindow*>(d->mainWindow);
1106 mw->saveDocument(document(), true, false);
1107 return;
1108 }
1109
1110 bool workingOnBackup;
1111 bool fileAlreadyExists;
1112 QString version = "000";
1113 QString newVersion;
1114 QString letter;
1115 QString path = canonicalPath();
1116 QString fileName = QFileInfo(document()->localFilePath()).fileName();
1117
1118 // First, discover if working on a backup file, or a normal file
1119 QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]");
1120 regex.indexIn(fileName); // Perform the search
1121 QStringList matches = regex.capturedTexts();
1122 workingOnBackup = matches.at(0).isEmpty() ? false : true;
1123
1124 if (workingOnBackup) {
1125 // Try to save incremental version (of backup), use letter for alt versions
1126 version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches
1127#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1128 if (version.contains(QRegExp("[a-z]"))) {
1129#else
1130 if (QRegExp("[a-z]").containedIn(version)) {
1131#endif
1132 version.chop(1); // Trim "."
1133 letter = version.right(1); // Save letter
1134 version.chop(1); // Trim letter
1135 } else {
1136 version.chop(1); // Trim "."
1137 }
1138 version.remove(0, 1); // Trim "~"
1139
1140 // Prepare the base for new version filename
1141 int intVersion = version.toInt(0);
1142 ++intVersion;
1143 QString baseNewVersion = QString::number(intVersion);
1144 QString backupFileName = document()->localFilePath();
1145 while (baseNewVersion.length() < version.length()) {
1146 baseNewVersion.prepend("0");
1147 }
1148
1149 // Check if the file exists under the new name and search until options are exhausted (test appending a to z)
1150 do {
1151 newVersion = baseNewVersion;
1152 newVersion.prepend("~");
1153 if (!letter.isNull()) newVersion.append(letter);
1154 newVersion.append(".");
1155#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1156 backupFileName.replace(regex, newVersion);
1157#else
1158 regex.replaceIn(backupFileName, newVersion);
1159#endif
1160 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1161 if (fileAlreadyExists) {
1162 if (!letter.isNull()) {
1163 char letterCh = letter.at(0).toLatin1();
1164 ++letterCh;
1165 letter = QString(QChar(letterCh));
1166 } else {
1167 letter = 'a';
1168 }
1169 }
1170 } while (fileAlreadyExists && letter != "{"); // x, y, z, {...
1171
1172 if (letter == "{") {
1173 QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number"));
1174 return;
1175 }
1176 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1177 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1178 }
1179 else { // if NOT working on a backup...
1180 // Navigate directory searching for latest backup version, ignore letters
1181 const quint8 HARDCODED_DIGIT_COUNT = 3;
1182 QString baseNewVersion = "000";
1183 QString backupFileName = QFileInfo(document()->localFilePath()).fileName();
1184 QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension
1185 regex2.indexIn(backupFileName);
1186 QStringList matches2 = regex2.capturedTexts();
1187 QString extensionPlusVersion = matches2.at(0);
1188 extensionPlusVersion.prepend(baseNewVersion);
1189 extensionPlusVersion.prepend("~");
1190#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1191 backupFileName.replace(regex2, extensionPlusVersion);
1192#else
1193 regex2.replaceIn(backupFileName, extensionPlusVersion);
1194#endif
1195
1196 // Save version with 1 number higher than the highest version found ignoring letters
1197 do {
1198 newVersion = baseNewVersion;
1199 newVersion.prepend("~");
1200 newVersion.append(".");
1201#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
1202 backupFileName.replace(regex, newVersion);
1203#else
1204 regex.replaceIn(backupFileName, newVersion);
1205#endif
1206 fileAlreadyExists = QFile(path + '/' + backupFileName).exists();
1207 if (fileAlreadyExists) {
1208 // Prepare the base for new version filename, increment by 1
1209 int intVersion = baseNewVersion.toInt(0);
1210 ++intVersion;
1211 baseNewVersion = QString::number(intVersion);
1212 while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) {
1213 baseNewVersion.prepend("0");
1214 }
1215 }
1216 } while (fileAlreadyExists);
1217
1218 // Save both as backup and on current file for interapplication workflow
1219 document()->setFileBatchMode(true);
1220 QFile::copy(path + '/' + fileName, path + '/' + backupFileName);
1221 document()->saveAs(path + '/' + fileName, document()->mimeType(), true);
1222 document()->setFileBatchMode(false);
1223 }
1224}
1225
1227{
1228 // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel
1229 // this is for Bug 250944
1230 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool
1231 // see KisToolFreehand::initPaint() and endPaint()
1232 d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter);
1233 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1234 child->installEventFilter(&d->blockingEventFilter);
1235 }
1236}
1237
1239{
1240 d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter);
1241 Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) {
1242 child->removeEventFilter(&d->blockingEventFilter);
1243 }
1244}
1245
1247{
1248 KisMainWindow *mw = mainWindow();
1249 if(mw && mw->statusBar()) {
1250 mw->statusBar()->setVisible(toggled);
1251 KisConfig cfg(false);
1252 cfg.setShowStatusBar(toggled);
1253 }
1254}
1255
1257{
1258 d->canvasStateInNormalMode.clear();
1260 d->canvasOnlyOptions = std::nullopt;
1261}
1262
1264{
1265 KisConfig cfg(false);
1267
1268 if(!main) {
1269 dbgUI << "Unable to switch to canvas-only mode, main window not found";
1270 return;
1271 }
1272
1273 cfg.writeEntry("CanvasOnlyActive", toggled);
1274
1276
1277 if (toggled) {
1278 d->canvasStateInNormalMode = qtMainWindow()->saveState();
1279 } else {
1280 d->canvasStateInCanvasOnlyMode = qtMainWindow()->saveState();
1281 d->canvasOnlyOptions = options;
1282 }
1283
1284 const bool toggleFullscreen = (options.hideTitlebarFullscreen && !cfg.fullscreenMode());
1285 const bool useCanvasOffsetCompensation = d->currentImageView &&
1286 d->currentImageView->canvasController() &&
1287 d->currentImageView->isMaximized() &&
1288 !main->canvasDetached();
1289
1290 if (useCanvasOffsetCompensation) {
1291 // The offset is calculated in two steps; this is the first step.
1292 if (toggled) {
1322 QPoint origin;
1323 if (toggleFullscreen) {
1324 // We're windowed, so also capture the position of the window in the screen.
1325 origin = main->geometry().topLeft() - main->screen()->geometry().topLeft();
1326 }
1328 } else {
1329 // Restore the original canvas position. The result is more stable if we pan before showing the UI elements.
1330 d->currentImageView->canvasController()->pan(- d->canvasOnlyOffsetCompensation);
1331 }
1332 }
1333
1334 if (options.hideStatusbarFullscreen) {
1335 if (main->statusBar()) {
1336 if (!toggled) {
1337 if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) {
1338 if (main->statusBar()->property("wasvisible").toBool()) {
1339 main->statusBar()->setVisible(true);
1340 }
1341 }
1342 }
1343 else {
1344 main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible());
1345 main->statusBar()->setVisible(false);
1346 }
1347 }
1348 }
1349
1350 if (options.hideDockersFullscreen) {
1351 KisAction* action = qobject_cast<KisAction*>(main->actionCollection()->action("view_toggledockers"));
1352 if (action) {
1353 action->setCheckable(true);
1354 if (toggled) {
1355 if (action->isChecked()) {
1356 cfg.setShowDockers(action->isChecked());
1357 action->setChecked(false);
1358 } else {
1359 cfg.setShowDockers(false);
1360 }
1361 } else {
1362 action->setChecked(cfg.showDockers());
1363 }
1364 }
1365 }
1366
1367 // QT in windows does not return to maximized upon 4th tab in a row
1368 // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/
1369 if (toggleFullscreen) {
1370 if(toggled) {
1371 main->setWindowState( main->windowState() | Qt::WindowFullScreen);
1372 } else {
1373 main->setWindowState( main->windowState() & ~Qt::WindowFullScreen);
1374 }
1375 }
1376
1377 if (options.hideMenuFullscreen) {
1378 if (!toggled) {
1379 if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) {
1380 if (main->menuBar()->property("wasvisible").toBool()) {
1381 main->menuBar()->setVisible(true);
1382 }
1383 }
1384 }
1385 else {
1386 main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible());
1387 main->menuBar()->setVisible(false);
1388 }
1389 }
1390
1391 if (options.hideToolbarFullscreen) {
1392 QList<QToolBar*> toolBars = main->findChildren<QToolBar*>();
1393 Q_FOREACH (QToolBar* toolbar, toolBars) {
1394 if (!toggled) {
1395 if (toolbar->dynamicPropertyNames().contains("wasvisible")) {
1396 if (toolbar->property("wasvisible").toBool()) {
1397 toolbar->setVisible(true);
1398 }
1399 }
1400 }
1401 else {
1402 toolbar->setProperty("wasvisible", toolbar->isVisible());
1403 toolbar->setVisible(false);
1404 }
1405 }
1406 }
1407
1409
1410 if (toggled) {
1411 if (!d->canvasStateInCanvasOnlyMode.isEmpty() &&
1413 *d->canvasOnlyOptions == options) {
1414
1424 QTimer::singleShot(0, this, [this] () {
1425 this->mainWindow()->restoreState(d->canvasStateInCanvasOnlyMode);
1426 });
1427 }
1428
1429 // show a fading heads-up display about the shortcut to go back
1430 showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.",
1431 actionCollection()->action("view_show_canvas_only")->shortcut().toString(QKeySequence::NativeText)), QIcon(),
1432 2000,
1434 }
1435 else {
1436 if (!d->canvasStateInNormalMode.isEmpty()) {
1437 main->restoreState(d->canvasStateInNormalMode);
1438 }
1439 }
1440
1441 if (useCanvasOffsetCompensation && toggled) {
1442 const KoZoomMode::Mode mode = d->currentImageView->canvasController()->zoomState().mode;
1443
1444 const bool allowedZoomMode =
1445 (mode == KoZoomMode::ZOOM_CONSTANT) ||
1446 (mode == KoZoomMode::ZOOM_HEIGHT);
1447
1448 if (allowedZoomMode) {
1449 // Defer the pan action until the layout is fully settled in (including the menu bars, etc.).
1450 QTimer::singleShot(0, this, [this] () {
1451 // Compensate by the difference of (after - before) layout.
1453 d->currentImageView->canvasController()->pan(d->canvasOnlyOffsetCompensation);
1454 });
1455 } else {
1456 // Nothing to restore.
1457 d->canvasOnlyOffsetCompensation = QPoint();
1458 }
1459 }
1460}
1461
1466
1468{
1469 QString resourcePath = KisResourceLocator::instance()->resourceLocationBase();
1470#ifdef Q_OS_WIN
1471
1472 QString folderInStandardAppData;
1473 QString folderInPrivateAppData;
1474 KoResourcePaths::getAllUserResourceFoldersLocationsForWindowsStore(folderInStandardAppData, folderInPrivateAppData);
1475
1476 if (!folderInPrivateAppData.isEmpty()) {
1477
1478 const auto pathToDisplay = [](const QString &path) {
1479 // Due to how Unicode word wrapping works, the string does not
1480 // wrap after backslashes in Qt 5.12. We don't want the path to
1481 // become too long, so we add a U+200B ZERO WIDTH SPACE to allow
1482 // wrapping. The downside is that we cannot let the user select
1483 // and copy the path because it now contains invisible unicode
1484 // code points.
1485 // See: https://bugreports.qt.io/browse/QTBUG-80892
1486 return QDir::toNativeSeparators(path).replace(QChar('\\'), QStringLiteral(u"\\\u200B"));
1487 };
1488
1489 QMessageBox mbox(qApp->activeWindow());
1490 mbox.setIcon(QMessageBox::Information);
1491 mbox.setWindowTitle(i18nc("@title:window resource folder", "Open Resource Folder"));
1492 // Similar text is also used in kis_dlg_preferences.cc
1493
1494 mbox.setText(i18nc("@info resource folder",
1495 "<p>You are using the Microsoft Store package version of Krita. "
1496 "Even though Krita can be configured to place resources under the "
1497 "user AppData location, Windows may actually store the files "
1498 "inside a private app location.</p>\n"
1499 "<p>You should check both locations to determine where "
1500 "the files are located.</p>\n"
1501 "<p><b>User AppData</b>:<br/>\n"
1502 "%1</p>\n"
1503 "<p><b>Private app location</b>:<br/>\n"
1504 "%2</p>",
1505 pathToDisplay(folderInStandardAppData),
1506 pathToDisplay(folderInPrivateAppData)
1507 ));
1508 mbox.setTextInteractionFlags(Qt::NoTextInteraction);
1509
1510 const auto *btnOpenUserAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &user AppData"), QMessageBox::AcceptRole);
1511 const auto *btnOpenPrivateAppData = mbox.addButton(i18nc("@action:button resource folder", "Open in &private app location"), QMessageBox::AcceptRole);
1512
1513 mbox.addButton(QMessageBox::Close);
1514 mbox.setDefaultButton(QMessageBox::Close);
1515 mbox.exec();
1516
1517 if (mbox.clickedButton() == btnOpenPrivateAppData) {
1518 resourcePath = folderInPrivateAppData;
1519 } else if (mbox.clickedButton() == btnOpenUserAppData) {
1520 // no-op: resourcePath = resourceDir.absolutePath();
1521 } else {
1522 return;
1523 }
1524
1525
1526 }
1527#endif
1528 QDesktopServices::openUrl(QUrl::fromLocalFile(resourcePath));
1529}
1530
1532{
1533 if (mainWindow()) {
1535 Q_FOREACH (QDockWidget* dock, dockers) {
1536 KoDockWidgetTitleBar* titlebar = dynamic_cast<KoDockWidgetTitleBar*>(dock->titleBarWidget());
1537 if (titlebar) {
1538 titlebar->updateIcons();
1539 }
1540 if (qobject_cast<KoToolDocker*>(dock)) {
1541 // Tool options widgets icons are updated by KoToolManager
1542 continue;
1543 }
1544 QObjectList objects;
1545 objects.append(dock);
1546 while (!objects.isEmpty()) {
1547 QObject* object = objects.takeFirst();
1548 objects.append(object->children());
1550 }
1551 }
1552 }
1553}
1554
1563
1564void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment)
1565{
1566 if (!d->currentImageView) return;
1567 d->currentImageView->showFloatingMessage(message, icon, timeout, priority, alignment);
1568
1569 Q_EMIT floatingMessageRequested(message, icon.name());
1570}
1571
1573{
1574 return qobject_cast<KisMainWindow*>(d->mainWindow);
1575}
1576
1578{
1579 return mainWindow();
1580}
1581
1582
1584{
1585 if (!d->currentImageView) return;
1586 if (!d->currentImageView->canvasController()) return;
1587
1588 KisConfig cfg(true);
1589 bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked();
1590
1591 if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) {
1592 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1593 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1594 } else {
1595 d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1596 d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
1597 }
1598}
1599
1601{
1602 KisConfig cfg(false);
1603 cfg.setShowRulers(value);
1604}
1605
1611
1613{
1614 d->showFloatingMessage = show;
1615}
1616
1617void KisViewManager::changeAuthorProfile(const QString &profileName)
1618{
1619 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1620 if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) {
1621 appAuthorGroup.writeEntry("active-profile", "");
1622 } else {
1623 appAuthorGroup.writeEntry("active-profile", profileName);
1624 }
1625 appAuthorGroup.sync();
1626 Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) {
1628 }
1629}
1630
1632{
1633 Q_ASSERT(d->actionAuthor);
1634 if (!d->actionAuthor) {
1635 return;
1636 }
1637 d->actionAuthor->clear();
1638 d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous"));
1639
1640 KConfigGroup authorGroup(KSharedConfig::openConfig(), "Author");
1641 QStringList profiles = authorGroup.readEntry("profile-names", QStringList());
1642 QString authorInfo = KoResourcePaths::getAppDataLocation() + "/authorinfo/";
1643 QStringList filters = QStringList() << "*.authorinfo";
1644 QDir dir(authorInfo);
1645 Q_FOREACH(QString entry, dir.entryList(filters)) {
1646 int ln = QString(".authorinfo").size();
1647 entry.chop(ln);
1648 if (!profiles.contains(entry)) {
1649 profiles.append(entry);
1650 }
1651 }
1652 Q_FOREACH (const QString &profile , profiles) {
1653 d->actionAuthor->addAction(profile);
1654 }
1655
1656 KConfigGroup appAuthorGroup(KSharedConfig::openConfig(), "Author");
1657 QString profileName = appAuthorGroup.readEntry("active-profile", "");
1658
1659 if (profileName == "anonymous" || profileName.isEmpty()) {
1660 d->actionAuthor->setCurrentItem(0);
1661 } else if (profiles.contains(profileName)) {
1662 d->actionAuthor->setCurrentAction(profileName);
1663 }
1664}
1665
1675
1677{
1678 if(KoToolManager::instance()->activeToolId() == "KisToolTransform") {
1679 KoToolBase* tool = KoToolManager::instance()->toolById(canvasBase(), "KisToolTransform");
1680
1681 QSet<KoShape*> dummy;
1682 // Start a new stroke
1683 tool->deactivate();
1684 tool->activate(dummy);
1685 }
1686
1687 KoToolManager::instance()->switchToolRequested("KisToolTransform");
1688}
1689
1705
1707{
1708 // see a comment in slotToggleFgBg()
1711}
1712
1714{
1715 KisConfig cfg(true);
1716
1717 OutlineStyle style;
1718
1719 if (cfg.newOutlineStyle() != OUTLINE_NONE) {
1720 style = OUTLINE_NONE;
1722 } else {
1723 style = cfg.lastUsedOutlineStyle();
1725 }
1726
1727 cfg.setNewOutlineStyle(style);
1728
1729 Q_EMIT brushOutlineToggled();
1730}
1731
1733{
1734 KisCanvasController *canvasController = d->currentImageView->canvasController();
1735 canvasController->resetCanvasRotation();
1736}
1737
1739{
1740 KisCanvasController *canvasController = d->currentImageView->canvasController();
1741 canvasController->resetCanvasRotation();
1742 canvasController->mirrorCanvas(false);
1744}
1745
1746void KisViewManager::slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
1747{
1748 if (isOpacityPresetMode) {
1750 }
1751 else {
1753 }
1754}
float value(const T *src, size_t ch)
qreal u
QList< QString > QStringList
@ WRAPAROUND_HORIZONTAL
@ WRAPAROUND_BOTH
@ WRAPAROUND_VERTICAL
unsigned int uint
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
bool eventFilter(QObject *watched, QEvent *event) override
A KisActionManager class keeps track of KisActions. These actions are always associated with the GUI....
void setView(QPointer< KisView > imageView)
KisAction * createAction(const QString &name)
KisAction * createStandardAction(KStandardAction::StandardAction, const QObject *receiver, const char *member)
void setDefaultShortcut(const QKeySequence &shortcut)
WrapAroundAxis wrapAroundModeAxis() const
void setUsePrintResolutionMode(bool value)
void sigUsePrintResolutionModeChanged(bool value)
void setup(KisActionManager *actionManager)
void setView(QPointer< KisView >imageView)
void setColorHistory(const QList< KoColor > &colors)
void setResourceManager(KoCanvasResourceProvider *resourceManager)
void addProxy(KoProgressProxy *proxy)
void removeProxy(KoProgressProxy *proxy)
static KisConfigNotifier * instance()
bool hideScrollbars(bool defaultValue=false) const
void setShowDockers(const bool value) const
void setShowStatusBar(const bool value) const
void writeEntry(const QString &name, const T &value)
Definition kis_config.h:779
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:630
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 slotActivateTransformTool()
void setCurrentView(KisView *view)
void brushOutlineToggled()
void setQtMainWindow(QMainWindow *newMainWindow)
KisImageManager * imageManager()
KisSelectionSP selection()
void setShowFloatingMessage(bool show)
void slotUpdateAuthorProfileActions()
void enableControls()
disable and enable toolbar controls. used for disabling them during painting.
QPointer< KoUpdater > createThreadedUpdater(const QString &name)
QWidget * canvas() const
Return the actual widget that is displaying the current image.
void viewChanged()
viewChanged sent out when the view has changed.
KisLayerSP activeLayer()
Convenience method to get at the active layer.
KisPaintDeviceSP activeDevice()
Convenience method to get at the active paint device.
void slotViewAdded(KisView *view)
void blockUntilOperationsFinishedForced(KisImageSP image)
blockUntilOperationsFinished blocks the GUI of the application until execution of actions on image is...
virtual KisKActionCollection * actionCollection() const
void slotSaveIncrementalBackup()
void showStatusBar(bool toggled)
KisInputManager * inputManager() const
Filters events and sends them to canvas actions.
QPointer< KoUpdater > createUnthreadedUpdater(const QString &name)
create a new progress updater
QMainWindow * qtMainWindow() const
~KisViewManager() override
KisViewManagerPrivate *const d
KisNodeManager * nodeManager() const
The node manager handles everything about nodes.
void slotSaveShowRulersState(bool value)
QString canonicalPath()
KisSelectionManager * selectionManager()
void slotViewRemoved(KisView *view)
KisGuidesManager * guidesManager() const
QWidget * mainWindowAsQWidget() const
void slotCreateOpacityResource(bool isOpacityPresetMode, KoToolBase *tool)
KisImageWSP image() const
Return the image this view is displaying.
KisViewManager(QWidget *parent, KisKActionCollection *actionCollection)
static void testingInitializeOpacityToPresetResourceConverter(KoCanvasResourceProvider *resourceManager)
KisGridManager * gridManager() const
KisZoomManager * zoomManager()
The zoommanager handles everything action-related to zooming.
KisCanvasResourceProvider * canvasResourceProvider()
KisStatusBar * statusBar() const
Return the wrapper class around the statusbar.
KisTextPropertiesManager * textPropertyManager() const
void changeAuthorProfile(const QString &profileName)
KisPaintopBox * paintOpBox() const
bool selectionEditable()
Checks if the current global or local selection is editable.
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
KisViewManager * viewManager
Definition KisView.cpp:129
void effectiveZoomChanged(qreal zoom)
void addActiveCanvasResourceDependency(KoActiveCanvasResourceDependencySP dep)
void setBackgroundColor(const KoColor &color)
void setForegroundColor(const KoColor &color)
void addResourceUpdateMediator(KoResourceUpdateMediatorSP mediator)
void addDerivedResourceConverter(KoDerivedResourceConverterSP converter)
A custom title bar for dock widgets.
void setAboutInfo(const QString &info, const QString &data)
QString aboutInfo(const QString &info) const
QPointer< KoUpdater > startSubtask(int weight=1, const QString &name=QString(), bool isPersistent=false)
static void getAllUserResourceFoldersLocationsForWindowsStore(QString &standardLocation, QString &privateLocation)
getAllAppDataLocationsForWindowsStore Use this to get both private and general appdata folders which ...
static QString getAppDataLocation()
virtual void activate(const QSet< KoShape * > &shapes)
virtual void deactivate()
KoToolBase * toolById(KoCanvasBase *canvas, const QString &id) const
void switchToolRequested(const QString &id)
void setConverter(KoDerivedResourceConverterSP converter, KoToolBase *tool)
void setAbstractResource(KoAbstractCanvasResourceInterfaceSP abstractResource, KoToolBase *tool)
static KoToolManager * instance()
Return the toolmanager singleton.
void initializeToolActions()
void sigUsePrintResolutionModeChanged(bool value)
void setUsePrintResolutionMode(bool value)
@ ZOOM_CONSTANT
zoom x %
Definition KoZoomMode.h:24
@ ZOOM_HEIGHT
zoom pageheight
Definition KoZoomMode.h:27
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define dbgUI
Definition kis_debug.h:52
OutlineStyle
Definition kis_global.h:53
@ OUTLINE_NONE
Definition kis_global.h:54
QSharedPointer< T > toQShared(T *ptr)
int main(int argc, char **argv)
Definition main.cpp:26
QIcon loadIcon(const QString &name)
void updateIconCommon(QObject *object)
@ BackgroundColor
The active background color selected for this canvas.
@ ForegroundColor
The active foreground color selected for this canvas.
bool isEditable(bool checkVisibility=true) const
virtual KisSelectionMaskSP selectionMask() const
Definition kis_layer.cc:498
static KisResourceItemChooserSync * instance()
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())