diff --git a/krita/krita5.xmlgui b/krita/krita5.xmlgui
index 106802d024..e325777399 100644
--- a/krita/krita5.xmlgui
+++ b/krita/krita5.xmlgui
@@ -1,412 +1,413 @@
&View
&Canvas
&Snap To
&Image
&Rotate
&Layer
&New
&Import/Export
Import
&Convert
&Select
&Group
&Transform
&Rotate
Transform &All Layers
&Rotate
S&plit
S&plit Alpha
&Select
Select &Opaque
Filte&r
&Tools
Scripts
Setti&ngs
+
&Help
File
Brushes and Stuff
diff --git a/krita/kritamenu.action b/krita/kritamenu.action
index bc5a1844e8..af463440b6 100644
--- a/krita/kritamenu.action
+++ b/krita/kritamenu.action
@@ -1,1813 +1,1823 @@
File
document-new
&New...
Create new document
New
0
0
Ctrl+N
false
document-open
&Open...
Open an existing document
Open
0
0
Ctrl+O
false
document-open-recent
Open &Recent
Open a document which was recently opened
Open Recent
1
0
false
document-save
&Save
Save
Save
1
0
Ctrl+S
false
document-save-as
Save &As...
Save document under a new name
Save As
1
0
Ctrl+Shift+S
false
Sessions...
Open session manager
Sessions
0
0
false
document-import
Open ex&isting Document as Untitled Document...
Open existing Document as Untitled Document
Open existing Document as Untitled Document
0
0
false
document-export
E&xport...
Export
Export
1
0
false
Import animation frames...
Import animation frames
Import animation frames
1
0
false
&Render Animation...
Render Animation to GIF, Image Sequence or Video
Render Animation
1000
0
false
&Render Animation Again
Render Animation Again
Render Animation
1000
0
false
Save Incremental &Version
Save Incremental Version
Save Incremental Version
1
0
Ctrl+Alt+S
false
Save Incremental &Backup
Save Incremental Backup
Save Incremental Backup
1
0
F4
false
&Create Template From Image...
Create Template From Image
Create Template From Image
1
0
false
Create Copy &From Current Image
Create Copy From Current Image
Create Copy From Current Image
1
0
false
configure
&Document Information
Document Information
Document Information
1
0
false
&Close All
Close All
Close All
1
0
Ctrl+Shift+W
false
C&lose
Close
Close
1
0
Ctrl+W
false
&Quit
Quit application
Quit
0
0
Ctrl+Q
false
Edit
edit-undo
Undo
Undo last action
Undo
1
0
Ctrl+Z
false
edit-redo
Redo
Redo last undone action
Redo
1
0
Ctrl+Shift+Z
false
edit-cut
Cu&t
Cut selection to clipboard
Cut
0
0
Ctrl+X
false
edit-copy
&Copy
Copy selection to clipboard
Copy
0
0
Ctrl+C
false
C&opy (sharp)
Copy (sharp)
Copy (sharp)
100000000
0
false
Cut (&sharp)
Cut (sharp)
Cut (sharp)
100000000
0
false
Copy &merged
Copy merged
Copy merged
100000000
0
Ctrl+Shift+C
false
edit-paste
&Paste
Paste clipboard content
Paste
0
0
Ctrl+V
false
Paste at Cursor
Paste at cursor
Paste at cursor
0
0
Ctrl+Alt+V
false
Paste into &New Image
Paste into New Image
Paste into New Image
0
0
Ctrl+Shift+N
false
Paste as R&eference Image
Paste as Reference Image
Paste as Reference Image
1
0
Ctrl+Shift+R
false
edit-clear
C&lear
Clear
Clear
1
0
Del
false
&Fill with Foreground Color
Fill with Foreground Color
Fill with Foreground Color
10000
1
Shift+Backspace
false
Fill &with Background Color
Fill with Background Color
Fill with Background Color
10000
1
Backspace
false
F&ill with Pattern
Fill with Pattern
Fill with Pattern
10000
1
false
Fill Special
Fill with Foreground Color (Opacity)
Fill with Foreground Color (Opacity)
Fill with Foreground Color (Opacity)
10000
1
Ctrl+Shift+Backspace
false
Fill with Background Color (Opacity)
Fill with Background Color (Opacity)
Fill with Background Color (Opacity)
10000
1
Ctrl+Backspace
false
Fill with Pattern (Opacity)
Fill with Pattern (Opacity)
Fill with Pattern (Opacity)
10000
1
false
Stro&ke selected shapes
Stroke selected shapes
Stroke selected shapes
1000000000
0
false
Stroke Selec&tion...
Stroke selection
Stroke selection
10000000000
0
false
Delete keyframe
Delete keyframe
Delete keyframe
100000
0
false
Window
window-new
&New Window
New Window
New Window
0
0
false
N&ext
Next
Next
10
0
false
Previous
Previous
Previous
false
View
document-new
&Show Canvas Only
Show just the canvas or the whole window
Show Canvas Only
0
0
Tab
true
view-fullscreen
F&ull Screen Mode
Display the window in full screen
Full Screen Mode
0
0
Ctrl+Shift+F
true
Detach canvas
Show the canvas on a separate window
Detach canvas
0
0
true
&Wrap Around Mode
Wrap Around Mode
Wrap Around Mode
1
0
true
&Instant Preview Mode
Instant Preview Mode
Instant Preview Mode
1
0
Shift+L
true
Soft Proofing
Turns on Soft Proofing
Turns on Soft Proofing
Ctrl+Y
true
Out of Gamut Warnings
Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on.
Turns on warnings for colors out of proofed gamut, needs soft proofing to be turned on.
Ctrl+Shift+Y
true
mirror-view
Mirror View
Mirror View
Mirror View
M
false
zoom-original
&Reset zoom
Reset zoom
Reset zoom
1
0
Ctrl+0
false
zoom-in
Zoom &In
Zoom In
0
0
Ctrl++
false
zoom-out
Zoom &Out
Zoom Out
0
0
Ctrl+-
false
rotate-canvas-right
Rotate &Canvas Right
Rotate Canvas Right
Rotate Canvas Right
1
0
Ctrl+]
false
rotate-canvas-left
Rotate Canvas &Left
Rotate Canvas Left
Rotate Canvas Left
1
0
Ctrl+[
false
rotation-reset
Reset Canvas Rotation
Reset Canvas Rotation
Reset Canvas Rotation
1
0
false
Show &Rulers
The rulers show the horizontal and vertical positions of the mouse on the image and can be used to position your mouse at the right place on the canvas. <p>Uncheck this to hide the rulers.</p>
Show Rulers
Show Rulers
1
0
true
Rulers Track Pointer
The rulers will track current mouse position and show it on screen. It can cause suptle performance slowdown
Rulers Track Pointer
Rulers Track Pointer
1
0
true
Show Guides
Show or hide guides
Show Guides
1
0
true
Lock Guides
Lock or unlock guides
Lock Guides
1
0
true
Snap to Guides
Snap cursor to guides position
Snap to Guides
1
0
true
Show Status &Bar
Show or hide the status bar
Show Status Bar
0
0
true
Show Pixel Grid
Show Pixel Grid
Show Pixel Grid
1000
1000
true
view-grid
Show &Grid
Show Grid
Show Grid
1000
0
Ctrl+Shift+'
true
Snap To Grid
Snap To Grid
Snap To Grid
1000
Ctrl+Shift+;
true
Show Snap Options Popup
Show Snap Options Popup
Show Snap Options Popup
1000
Shift+s
false
Snap Orthogonal
Snap Orthogonal
Snap Orthogonal
1000
true
Snap Node
Snap Node
Snap Node
1000
true
Snap Extension
Snap Extension
Snap Extension
1000
true
Snap Pixel
Snap Pixel
Snap Pixel
1000
true
Snap Intersection
Snap Intersection
Snap Intersection
1000
true
Snap Bounding Box
Snap Bounding Box
Snap Bounding Box
1000
true
Snap Image Bounds
Snap Image Bounds
Snap Image Bounds
1000
true
Snap Image Center
Snap Image Center
Snap Image Center
1000
true
S&how Painting Assistants
Show Painting Assistants
Show Painting Assistants
1000
0
true
Show &Assistant Previews
Show Assistant Previews
Show Assistant Previews
1000
0
true
S&how Reference Images
Show Reference Images
Show Reference Images
1000
0
true
Image
document-properties
&Properties...
Properties
Properties
1000
0
false
format-stroke-color
&Image Background Color and Transparency...
Change the background color of the image
Image Background Color and Transparency
1000
0
false
&Convert Image Color Space...
Convert Image Color Space
Convert Image Color Space
1000
0
false
trim-to-image
&Trim to Image Size
Trim to Image Size
Trim to Image Size
1
0
false
Trim to Current &Layer
Trim to Current Layer
Trim to Current Layer
100000
0
false
Trim to S&election
Trim to Selection
Trim to Selection
100000000
0
false
&Rotate Image...
Rotate Image
Rotate Image
1000
0
false
object-rotate-right
Rotate &Image 90° to the Right
Rotate Image 90° to the Right
Rotate Image 90° to the Right
1000
0
false
object-rotate-left
Rotate Image &90° to the Left
Rotate Image 90° to the Left
Rotate Image 90° to the Left
1000
0
false
Rotate Image &180°
Rotate Image 180°
Rotate Image 180°
1000
0
false
&Shear Image...
Shear Image
Shear Image
1000
0
false
symmetry-horizontal
&Mirror Image Horizontally
Mirror Image Horizontally
Mirror Image Horizontally
1000
0
false
symmetry-vertical
Mirror Image &Vertically
Mirror Image Vertically
Mirror Image Vertically
1000
0
false
Scale Image To &New Size...
Scale Image To New Size
Scale Image To New Size
1000
0
Ctrl+Alt+I
false
&Offset Image...
Offset Image
Offset Image
1000
0
false
R&esize Canvas...
Resize Canvas
Resize Canvas
1000
0
Ctrl+Alt+C
false
Im&age Split
Image Split
Image Split
1000
0
false
Separate Ima&ge...
Separate Image
Separate Image
1000
0
false
Select
edit-select-all
Select &All
Select All
Select All
0
0
Ctrl+A
false
edit-select-none
&Deselect
Deselect
Deselect
1100000000
0
Ctrl+Shift+A
false
&Reselect
Reselect
Reselect
0
0
Ctrl+Shift+D
false
&Convert to Vector Selection
Convert to Vector Selection
Convert to Vector Selection
100000000000000000
0
false
&Convert to Raster Selection
Convert to Raster Selection
Convert to Raster Selection
10000000000000000
0
false
Edit Selection
Edit Selection
Edit Selection
10000000000
100
false
Convert Shapes to &Vector Selection
Convert Shapes to Vector Selection
Convert Shapes to Vector Selection
1000000000
0
false
&Feather Selection...
Feather Selection
Feather Selection
10000000000
100
Shift+F6
false
Dis&play Selection
Display Selection
Display Selection
1000
0
Ctrl+H
true
Sca&le...
Scale
Scale
100000000
100
false
S&elect from Color Range...
Select from Color Range
Select from Color Range
10000
100
false
Select &Opaque (Replace)
Select Opaque
Select Opaque
1
100
false
Select Opaque (&Add)
Select Opaque (Add)
Select Opaque (Add)
1
100
false
Select Opaque (&Subtract)
Select Opaque (Subtract)
Select Opaque (Subtract)
1
100
false
Select Opaque (&Intersect)
Select Opaque (Intersect)
Select Opaque (Intersect)
1
100
false
&Grow Selection...
Grow Selection
Grow Selection
10000000000
100
false
S&hrink Selection...
Shrink Selection
Shrink Selection
10000000000
100
false
&Border Selection...
Border Selection
Border Selection
10000000000
100
false
S&mooth
Smooth
Smooth
10000000000
100
false
Filter
&Apply Filter Again
Apply Filter Again
Apply Filter Again
0
0
Ctrl+F
false
Adjust
Adjust
Adjust
false
Artistic
Artistic
Artistic
false
Blur
Blur
Blur
false
Colors
Colors
Colors
false
Edge Detection
Edge Detection
Edge Detection
false
Enhance
Enhance
Enhance
false
Emboss
Emboss
Emboss
false
Map
Map
Map
false
Other
Other
Other
false
gmic
Start G'MIC-Qt
Start G'Mic-Qt
Start G'Mic-Qt
false
gmic
Re-apply the last G'MIC filter
Apply the last G'Mic-Qt action again
Apply the last G'Mic-Qt action again
false
Settings
configure
&Configure Krita...
Configure Krita
Configure Krita
0
0
false
&Manage Resources...
Manage Resources
Manage Resources
0
0
false
preferences-desktop-locale
Switch Application &Language...
Switch Application Language
Switch Application Language
false
&Show Dockers
Show Dockers
Show Dockers
0
0
true
configure
Configure Tool&bars...
Configure Toolbars
Configure Toolbars
0
0
false
Dockers
Dockers
Dockers
false
&Themes
Themes
Themes
false
+
+
+ &Styles
+
+ Styles
+ Styles
+
+ false
+
+
im-user
Active Author Profile
Active Author Profile
Active Author Profile
Reset Krita Configurations
Reset Krita Configurations
Reset Krita Configurations
false
configure-shortcuts
Configure S&hortcuts...
Configure Shortcuts
Configure Shortcuts
0
0
false
&Window
Window
Window
false
Help
help-contents
Krita &Handbook
Krita Handbook
Krita Handbook
F1
false
tools-report-bug
&Report Bug...
Report Bug
Report Bug
false
krita
&About Krita
About Krita
About Krita
false
kde
About &KDE
About KDE
About KDE
false
Brushes and Stuff
&Gradients
Gradients
Gradients
false
&Patterns
Patterns
Patterns
false
&Color
Color
Color
false
&Painter's Tools
Painter's Tools
Painter's Tools
false
Brush composite
Brush composite
Brush composite
false
Brush option slider 1
Brush option slider 1
Brush option slider 1
false
Brush option slider 2
Brush option slider 2
Brush option slider 2
false
Brush option slider 3
Brush option slider 3
Brush option slider 3
false
Mirror
Mirror
Mirror
false
Layouts
Select layout
false
Workspaces
Workspaces
Workspaces
false
diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp
index 4deb606b1a..73f6c8f6e3 100644
--- a/libs/ui/KisApplication.cpp
+++ b/libs/ui/KisApplication.cpp
@@ -1,1016 +1,1028 @@
/*
* Copyright (C) 1998, 1999 Torben Weis
* Copyright (C) 2012 Boudewijn Rempt
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisApplication.h"
#include
#ifdef Q_OS_WIN
#include
#include
#endif
#ifdef Q_OS_MACOS
#include "osx.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KoConfig.h"
#include
#include
#include "thememanager.h"
#include "KisDocument.h"
#include "KisMainWindow.h"
#include "KisAutoSaveRecoveryDialog.h"
#include "KisPart.h"
#include
#include "kis_splash_screen.h"
#include "kis_config.h"
#include "flake/kis_shape_selection.h"
#include
#include
#include
#include
#include
#include
#include
#include "kisexiv2/kis_exiv2.h"
#include "KisApplicationArguments.h"
#include
#include "kis_action_registry.h"
#include
#include
#include
#include "kis_image_barrier_locker.h"
#include "opengl/kis_opengl.h"
#include "kis_spin_box_unit_manager.h"
#include "kis_document_aware_spin_box_unit_manager.h"
#include "KisViewManager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "widgets/KisScreenColorPicker.h"
#include "KisDlgInternalColorSelector.h"
#include
#include
#include "kis_file_layer.h"
#include "kis_group_layer.h"
#include "kis_node_commands_adapter.h"
#include
namespace {
const QTime appStartTime(QTime::currentTime());
}
class KisApplication::Private
{
public:
Private() {}
QPointer splashScreen;
KisAutoSaveRecoveryDialog *autosaveDialog {0};
QPointer mainWindow; // The first mainwindow we create on startup
bool batchRun {false};
QVector earlyRemoteArguments;
};
class KisApplication::ResetStarting
{
public:
ResetStarting(KisSplashScreen *splash, int fileCount)
: m_splash(splash)
, m_fileCount(fileCount)
{
}
~ResetStarting() {
if (m_splash) {
m_splash->hide();
}
}
QPointer m_splash;
int m_fileCount;
};
KisApplication::KisApplication(const QString &key, int &argc, char **argv)
: QtSingleApplication(key, argc, argv)
, d(new Private)
{
#ifdef Q_OS_MACOS
setMouseCoalescingEnabled(false);
#endif
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
setApplicationDisplayName("Krita");
setApplicationName("krita");
// Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird.
// setOrganizationName("krita");
setOrganizationDomain("krita.org");
QString version = KritaVersionWrapper::versionString(true);
setApplicationVersion(version);
setWindowIcon(KisIconUtils::loadIcon("krita"));
+
+
if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) {
QStringList styles = QStringList() << "breeze" << "fusion" << "plastique";
if (!styles.contains(style()->objectName().toLower())) {
Q_FOREACH (const QString & style, styles) {
if (!setStyle(style)) {
qDebug() << "No" << style << "available.";
}
else {
qDebug() << "Set style" << style;
break;
}
}
}
+
+ // if style is set from config, try to load that
+ KisConfig cfg(true);
+ QString widgetStyleFromConfig = cfg.widgetStyle();
+ if(widgetStyleFromConfig != "") {
+ qApp->setStyle(widgetStyleFromConfig);
+ }
+
}
else {
qDebug() << "Style override disabled, using" << style()->objectName();
}
+
+
}
#if defined(Q_OS_WIN) && defined(ENV32BIT)
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
LPFN_ISWOW64PROCESS fnIsWow64Process;
BOOL isWow64()
{
BOOL bIsWow64 = FALSE;
//IsWow64Process is not available on all supported versions of Windows.
//Use GetModuleHandle to get a handle to the DLL that contains the function
//and GetProcAddress to get a pointer to the function if available.
fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(0 != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//handle error
}
}
return bIsWow64;
}
#endif
void KisApplication::initializeGlobals(const KisApplicationArguments &args)
{
int dpiX = args.dpiX();
int dpiY = args.dpiY();
if (dpiX > 0 && dpiY > 0) {
KoDpi::setDPI(dpiX, dpiY);
}
}
void KisApplication::addResourceTypes()
{
// qDebug() << "addResourceTypes();";
// All Krita's resource types
KoResourcePaths::addResourceType("markers", "data", "/styles/");
KoResourcePaths::addResourceType("kis_pics", "data", "/pics/");
KoResourcePaths::addResourceType("kis_images", "data", "/images/");
KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/");
KoResourcePaths::addResourceType(ResourceType::Brushes, "data", "/brushes/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/");
KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/");
KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/");
KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/");
KoResourcePaths::addResourceType(ResourceType::PaintOpPresets, "data", "/paintoppresets/");
KoResourcePaths::addResourceType(ResourceType::Workspaces, "data", "/workspaces/");
KoResourcePaths::addResourceType(ResourceType::WindowLayouts, "data", "/windowlayouts/");
KoResourcePaths::addResourceType(ResourceType::Sessions, "data", "/sessions/");
KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl");
KoResourcePaths::addResourceType(ResourceType::Patterns, "data", "/patterns/", true);
KoResourcePaths::addResourceType(ResourceType::Gradients, "data", "/gradients/");
KoResourcePaths::addResourceType(ResourceType::Gradients, "data", "/gradients/", true);
KoResourcePaths::addResourceType(ResourceType::Palettes, "data", "/palettes/", true);
KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/");
KoResourcePaths::addResourceType("kis_actions", "data", "/actions");
KoResourcePaths::addResourceType("kis_actions", "data", "/pykrita");
KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc");
KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/");
KoResourcePaths::addResourceType(ResourceType::FilterEffects, "data", "/effects/");
KoResourcePaths::addResourceType("tags", "data", "/tags/");
KoResourcePaths::addResourceType("templates", "data", "/templates");
KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita");
KoResourcePaths::addResourceType(ResourceType::Symbols, "data", "/symbols");
KoResourcePaths::addResourceType("preset_icons", "data", "/preset_icons");
KoResourcePaths::addResourceType(ResourceType::GamutMasks, "data", "/gamutmasks/", true);
// // Extra directories to look for create resources. (Does anyone actually use that anymore?)
// KoResourcePaths::addResourceDir(ResourceType::Gradients, "/usr/share/create/gradients/gimp");
// KoResourcePaths::addResourceDir(ResourceType::Gradients, QDir::homePath() + QString("/.create/gradients/gimp"));
// KoResourcePaths::addResourceDir(ResourceType::Patterns, "/usr/share/create/patterns/gimp");
// KoResourcePaths::addResourceDir(ResourceType::Patterns, QDir::homePath() + QString("/.create/patterns/gimp"));
// KoResourcePaths::addResourceDir(ResourceType::Brushes, "/usr/share/create/brushes/gimp");
// KoResourcePaths::addResourceDir(ResourceType::Brushes, QDir::homePath() + QString("/.create/brushes/gimp"));
// KoResourcePaths::addResourceDir(ResourceType::Palettes, "/usr/share/create/swatches");
// KoResourcePaths::addResourceDir(ResourceType::Palettes, QDir::homePath() + QString("/.create/swatches"));
// Make directories for all resources we can save, and tags
QDir d;
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/brushes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/");
// between 4.2.x and 4.3.0 there was a change from 'taskset' to 'tasksets'
// so to make older resource folders compatible with the new version, let's rename the folder
// so no tasksets are lost.
if (d.exists(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/")) {
d.rename(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/",
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tasksets/");
}
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tasksets/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/color-schemes/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/tool_icons/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/preset_icons/emblem_icons/");
d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gamutmasks/");
}
bool KisApplication::registerResources()
{
KisResourceLoaderRegistry *reg = KisResourceLoaderRegistry::instance();
reg->add(new KisResourceLoader(ResourceType::PaintOpPresets, ResourceType::PaintOpPresets, i18n("Brush presets"), QStringList() << "application/x-krita-paintoppreset"));
reg->add(new KisResourceLoader(ResourceSubType::GbrBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush"));
reg->add(new KisResourceLoader(ResourceSubType::GihBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/x-gimp-brush-animated"));
reg->add(new KisResourceLoader(ResourceSubType::SvgBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/svg+xml"));
reg->add(new KisResourceLoader(ResourceSubType::PngBrushes, ResourceType::Brushes, i18n("Brush tips"), QStringList() << "image/png"));
reg->add(new KisResourceLoader(ResourceSubType::SegmentedGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-gimp-gradient"));
reg->add(new KisResourceLoader(ResourceSubType::StopGradients, ResourceType::Gradients, i18n("Gradients"), QStringList() << "application/x-karbon-gradient" << "image/svg+xml"));
reg->add(new KisResourceLoader(ResourceType::Palettes, ResourceType::Palettes, i18n("Palettes"),
QStringList() << KisMimeDatabase::mimeTypeForSuffix("kpl")
<< KisMimeDatabase::mimeTypeForSuffix("gpl")
<< KisMimeDatabase::mimeTypeForSuffix("pal")
<< KisMimeDatabase::mimeTypeForSuffix("act")
<< KisMimeDatabase::mimeTypeForSuffix("aco")
<< KisMimeDatabase::mimeTypeForSuffix("css")
<< KisMimeDatabase::mimeTypeForSuffix("colors")
<< KisMimeDatabase::mimeTypeForSuffix("xml")
<< KisMimeDatabase::mimeTypeForSuffix("sbz")));
QList src = QImageReader::supportedMimeTypes();
QStringList allImageMimes;
Q_FOREACH(const QByteArray ba, src) {
if (QImageWriter::supportedMimeTypes().contains(ba)) {
allImageMimes << QString::fromUtf8(ba);
}
}
allImageMimes << KisMimeDatabase::mimeTypeForSuffix("pat");
reg->add(new KisResourceLoader(ResourceType::Patterns, ResourceType::Patterns, i18n("Patterns"), allImageMimes));
reg->add(new KisResourceLoader(ResourceType::Workspaces, ResourceType::Workspaces, i18n("Workspaces"), QStringList() << "application/x-krita-workspace"));
reg->add(new KisResourceLoader(ResourceType::Symbols, ResourceType::Symbols, i18n("SVG symbol libraries"), QStringList() << "image/svg+xml"));
reg->add(new KisResourceLoader(ResourceType::WindowLayouts, ResourceType::WindowLayouts, i18n("Window layouts"), QStringList() << "application/x-krita-windowlayout"));
reg->add(new KisResourceLoader(ResourceType::Sessions, ResourceType::Sessions, i18n("Sessions"), QStringList() << "application/x-krita-session"));
reg->add(new KisResourceLoader(ResourceType::GamutMasks, ResourceType::GamutMasks, i18n("Gamut masks"), QStringList() << "application/x-krita-gamutmasks"));
reg->add(new KisResourceLoader(ResourceType::LayerStyles,
ResourceType::LayerStyles,
ResourceType::LayerStyles,
QStringList() << "application/x-photoshop-style"));
if (!KisResourceCacheDb::initialize(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) {
QMessageBox::critical(0, i18nc("@title:window", "Krita: Fatal error"), i18n("%1\n\nKrita will quit now.", KisResourceCacheDb::lastError()));
//return false;
}
KisResourceLocator::LocatorError r = KisResourceLocator::instance()->initialize(KoResourcePaths::getApplicationRoot() + "/share/krita");
connect(KisResourceLocator::instance(), SIGNAL(progressMessage(const QString&)), this, SLOT(setSplashScreenLoadingText(const QString&)));
if (r != KisResourceLocator::LocatorError::Ok ) {
QMessageBox::critical(0, i18nc("@title:window", "Krita: Fatal error"), KisResourceLocator::instance()->errorMessages().join('\n') + i18n("\n\nKrita will quit now."));
//return false;
}
return true;
}
void KisApplication::loadPlugins()
{
// qDebug() << "loadPlugins();";
KoShapeRegistry* r = KoShapeRegistry::instance();
r->add(new KisShapeSelectionFactory());
KoColorSpaceRegistry::instance();
KisActionRegistry::instance();
KisFilterRegistry::instance();
KisGeneratorRegistry::instance();
KisPaintOpRegistry::instance();
KoToolRegistry::instance();
KoDockRegistry::instance();
}
void KisApplication::loadGuiPlugins()
{
// XXX_EXIV: make the exiv io backends real plugins
setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO..."));
processEvents();
// qDebug() << "loading exiv2";
KisExiv2::initialize();
}
bool KisApplication::start(const KisApplicationArguments &args)
{
KisConfig cfg(false);
#if defined(Q_OS_WIN)
#ifdef ENV32BIT
if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) {
QMessageBox::information(0,
i18nc("@title:window", "Krita: Warning"),
i18n("You are running a 32 bits build on a 64 bits Windows.\n"
"This is not recommended.\n"
"Please download and install the x64 build instead."));
cfg.writeEntry("WarnedAbout32Bits", true);
}
#endif
#endif
QString opengl = cfg.canvasState();
if (opengl == "OPENGL_NOT_TRIED" ) {
cfg.setCanvasState("TRY_OPENGL");
}
else if (opengl != "OPENGL_SUCCESS" && opengl != "TRY_OPENGL") {
cfg.setCanvasState("OPENGL_FAILED");
}
setSplashScreenLoadingText(i18n("Initializing Globals"));
processEvents();
initializeGlobals(args);
const bool doNewImage = args.doNewImage();
const bool doTemplate = args.doTemplate();
const bool exportAs = args.exportAs();
const bool exportSequence = args.exportSequence();
const QString exportFileName = args.exportFileName();
d->batchRun = (exportAs || exportSequence || !exportFileName.isEmpty());
const bool needsMainWindow = (!exportAs && !exportSequence);
// only show the mainWindow when no command-line mode option is passed
bool showmainWindow = (!exportAs && !exportSequence); // would be !batchRun;
const bool showSplashScreen = !d->batchRun && qEnvironmentVariableIsEmpty("NOSPLASH");
if (showSplashScreen && d->splashScreen) {
d->splashScreen->show();
d->splashScreen->repaint();
processEvents();
}
KConfigGroup group(KSharedConfig::openConfig(), "theme");
Digikam::ThemeManager themeManager;
themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark"));
ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done
Q_UNUSED(resetStarting);
// Make sure we can save resources and tags
setSplashScreenLoadingText(i18n("Adding resource types"));
processEvents();
addResourceTypes();
// Load the plugins
loadPlugins();
// Load all resources
if (!registerResources()) {
return false;
}
// Load the gui plugins
loadGuiPlugins();
KisPart *kisPart = KisPart::instance();
if (needsMainWindow) {
// show a mainWindow asap, if we want that
setSplashScreenLoadingText(i18n("Loading Main Window..."));
processEvents();
bool sessionNeeded = true;
auto sessionMode = cfg.sessionOnStartup();
if (!args.session().isEmpty()) {
sessionNeeded = !kisPart->restoreSession(args.session());
} else if (sessionMode == KisConfig::SOS_ShowSessionManager) {
showmainWindow = false;
sessionNeeded = false;
kisPart->showSessionManager();
} else if (sessionMode == KisConfig::SOS_PreviousSession) {
KConfigGroup sessionCfg = KSharedConfig::openConfig()->group("session");
const QString &sessionName = sessionCfg.readEntry("previousSession");
sessionNeeded = !kisPart->restoreSession(sessionName);
}
if (sessionNeeded) {
kisPart->startBlankSession();
}
if (!args.windowLayout().isEmpty()) {
KoResourceServer * rserver = KisResourceServerProvider::instance()->windowLayoutServer();
KisWindowLayoutResourceSP windowLayout = rserver->resourceByName(args.windowLayout());
if (windowLayout) {
windowLayout->applyLayout();
}
}
if (showmainWindow) {
d->mainWindow = kisPart->currentMainwindow();
if (!args.workspace().isEmpty()) {
KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResourceSP workspace = rserver->resourceByName(args.workspace());
if (workspace) {
d->mainWindow->restoreWorkspace(workspace->resourceId());
}
}
if (args.canvasOnly()) {
d->mainWindow->viewManager()->switchCanvasOnly(true);
}
if (args.fullScreen()) {
d->mainWindow->showFullScreen();
}
} else {
d->mainWindow = kisPart->createMainWindow();
}
}
short int numberOfOpenDocuments = 0; // number of documents open
// Check for autosave files that can be restored, if we're not running a batchrun (test)
if (!d->batchRun) {
checkAutosaveFiles();
}
setSplashScreenLoadingText(QString()); // done loading, so clear out label
processEvents();
//configure the unit manager
KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder());
connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave.
//the new syntax slot syntax allow to connect to a non q_object static method.
// Create a new image, if needed
if (doNewImage) {
KisDocument *doc = args.createDocumentFromArguments();
if (doc) {
kisPart->addDocument(doc);
d->mainWindow->addViewAndNotifyLoadingCompleted(doc);
}
}
// Get the command line arguments which we have to parse
int argsCount = args.filenames().count();
if (argsCount > 0) {
// Loop through arguments
for (int argNumber = 0; argNumber < argsCount; argNumber++) {
QString fileName = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
// called in mix with batch options? ignore and silently skip
if (d->batchRun) {
continue;
}
if (createNewDocFromTemplate(fileName, d->mainWindow)) {
++numberOfOpenDocuments;
}
// now try to load
}
else {
if (exportAs) {
QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName, false);
if (outputMimetype == "application/octetstream") {
dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl;
return false;
}
KisDocument *doc = kisPart->createDocument();
doc->setFileBatchMode(d->batchRun);
bool result = doc->openUrl(QUrl::fromLocalFile(fileName));
if (!result) {
errKrita << "Could not load " << fileName << ":" << doc->errorMessage();
QTimer::singleShot(0, this, SLOT(quit()));
return false;
}
if (exportFileName.isEmpty()) {
errKrita << "Export destination is not specified for" << fileName << "Please specify export destination with --export-filename option";
QTimer::singleShot(0, this, SLOT(quit()));
return false;
}
qApp->processEvents(); // For vector layers to be updated
doc->setFileBatchMode(true);
if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) {
errKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage();
}
QTimer::singleShot(0, this, SLOT(quit()));
return true;
}
else if (exportSequence) {
KisDocument *doc = kisPart->createDocument();
doc->setFileBatchMode(d->batchRun);
doc->openUrl(QUrl::fromLocalFile(fileName));
qApp->processEvents(); // For vector layers to be updated
if (!doc->image()->animationInterface()->hasAnimation()) {
errKrita << "This file has no animation." << endl;
QTimer::singleShot(0, this, SLOT(quit()));
return false;
}
doc->setFileBatchMode(true);
int sequenceStart = 0;
KisAsyncAnimationFramesSaveDialog exporter(doc->image(),
doc->image()->animationInterface()->fullClipRange(),
exportFileName,
sequenceStart,
false,
0);
exporter.setBatchMode(d->batchRun);
KisAsyncAnimationFramesSaveDialog::Result result =
exporter.regenerateRange(0);
if (result == KisAsyncAnimationFramesSaveDialog::RenderFailed) {
errKrita << i18n("Failed to render animation frames!") << endl;
}
QTimer::singleShot(0, this, SLOT(quit()));
return true;
}
else if (d->mainWindow) {
if (fileName.endsWith(".bundle")) {
d->mainWindow->installBundle(fileName);
}
else {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
if (d->mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) {
// Normal case, success
numberOfOpenDocuments++;
}
}
}
}
}
}
//add an image as file-layer
if (!args.fileLayer().isEmpty()){
if (d->mainWindow->viewManager()->image()){
KisFileLayer *fileLayer = new KisFileLayer(d->mainWindow->viewManager()->image(), "",
args.fileLayer(), KisFileLayer::None,
d->mainWindow->viewManager()->image()->nextLayerName(), OPACITY_OPAQUE_U8);
QFileInfo fi(fileLayer->path());
if (fi.exists()){
KisNodeCommandsAdapter adapter(d->mainWindow->viewManager());
adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(),
d->mainWindow->viewManager()->activeNode());
}
else{
QMessageBox::warning(nullptr, i18nc("@title:window", "Krita:Warning"),
i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path()));
}
}
else if (this->isRunning()){
QMessageBox::warning(nullptr, i18nc("@title:window", "Krita:Warning"),
i18n("Cannot add the file layer: no document is open.\n\n"
"You can create a new document using the --new-image option, or you can open an existing file.\n\n"
"If you instead want to add the file layer to a document in an already running instance of Krita, check the \"Allow only one instance of Krita\" checkbox in the settings (Settings -> General -> Window)."));
}
else {
QMessageBox::warning(nullptr, i18nc("@title:window", "Krita: Warning"),
i18n("Cannot add the file layer: no document is open.\n"
"You can either create a new file using the --new-image option, or you can open an existing file."));
}
}
// fixes BUG:369308 - Krita crashing on splash screen when loading.
// trying to open a file before Krita has loaded can cause it to hang and crash
if (d->splashScreen) {
d->splashScreen->displayLinks(true);
d->splashScreen->displayRecentFiles(true);
}
Q_FOREACH(const QByteArray &message, d->earlyRemoteArguments) {
executeRemoteArguments(message, d->mainWindow);
}
KisUsageLogger::writeSysInfo(KisUsageLogger::screenInformation());
// not calling this before since the program will quit there.
return true;
}
KisApplication::~KisApplication()
{
KisResourceCacheDb::deleteTemporaryResources();
}
void KisApplication::setSplashScreen(QWidget *splashScreen)
{
d->splashScreen = qobject_cast(splashScreen);
}
void KisApplication::setSplashScreenLoadingText(const QString &textToLoad)
{
if (d->splashScreen) {
d->splashScreen->setLoadingText(textToLoad);
d->splashScreen->repaint();
}
}
void KisApplication::hideSplashScreen()
{
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
}
bool KisApplication::notify(QObject *receiver, QEvent *event)
{
try {
return QApplication::notify(receiver, event);
} catch (std::exception &e) {
qWarning("Error %s sending event %i to object %s",
e.what(), event->type(), qPrintable(receiver->objectName()));
} catch (...) {
qWarning("Error sending event %i to object %s",
event->type(), qPrintable(receiver->objectName()));
}
return false;
}
void KisApplication::executeRemoteArguments(QByteArray message, KisMainWindow *mainWindow)
{
KisApplicationArguments args = KisApplicationArguments::deserialize(message);
const bool doTemplate = args.doTemplate();
const bool doNewImage = args.doNewImage();
const int argsCount = args.filenames().count();
bool documentCreated = false;
// Create a new image, if needed
if (doNewImage) {
KisDocument *doc = args.createDocumentFromArguments();
if (doc) {
KisPart::instance()->addDocument(doc);
d->mainWindow->addViewAndNotifyLoadingCompleted(doc);
}
}
if (argsCount > 0) {
// Loop through arguments
for (int argNumber = 0; argNumber < argsCount; ++argNumber) {
QString filename = args.filenames().at(argNumber);
// are we just trying to open a template?
if (doTemplate) {
documentCreated |= createNewDocFromTemplate(filename, mainWindow);
}
else if (QFile(filename).exists()) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
documentCreated |= mainWindow->openDocument(QUrl::fromLocalFile(filename), flags);
}
}
}
//add an image as file-layer if called in another process and singleApplication is enabled
if (!args.fileLayer().isEmpty()){
if (argsCount > 0 && !documentCreated){
//arg was passed but document was not created so don't add the file layer.
QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"),
i18n("Couldn't open file %1",args.filenames().at(argsCount - 1)));
}
else if (mainWindow->viewManager()->image()){
KisFileLayer *fileLayer = new KisFileLayer(mainWindow->viewManager()->image(), "",
args.fileLayer(), KisFileLayer::None,
mainWindow->viewManager()->image()->nextLayerName(), OPACITY_OPAQUE_U8);
QFileInfo fi(fileLayer->path());
if (fi.exists()){
KisNodeCommandsAdapter adapter(d->mainWindow->viewManager());
adapter.addNode(fileLayer, d->mainWindow->viewManager()->activeNode()->parent(),
d->mainWindow->viewManager()->activeNode());
}
else{
QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"),
i18n("Cannot add %1 as a file layer: the file does not exist.", fileLayer->path()));
}
}
else {
QMessageBox::warning(mainWindow, i18nc("@title:window", "Krita:Warning"),
i18n("Cannot add the file layer: no document is open."));
}
}
}
void KisApplication::remoteArguments(QByteArray message, QObject *socket)
{
Q_UNUSED(socket);
// check if we have any mainwindow
KisMainWindow *mw = qobject_cast(qApp->activeWindow());
if (!mw && KisPart::instance()->mainWindows().size() > 0) {
mw = KisPart::instance()->mainWindows().first();
}
if (!mw) {
d->earlyRemoteArguments << message;
return;
}
executeRemoteArguments(message, mw);
}
void KisApplication::fileOpenRequested(const QString &url)
{
KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first();
if (mainWindow) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
mainWindow->openDocument(QUrl::fromLocalFile(url), flags);
}
}
void KisApplication::checkAutosaveFiles()
{
if (d->batchRun) return;
#ifdef Q_OS_WIN
QDir dir = QDir::temp();
#else
QDir dir = QDir::home();
#endif
// Check for autosave files from a previous run. There can be several, and
// we want to offer a restore for every one. Including a nice thumbnail!
// Hidden autosave files
QStringList filters = QStringList() << QString(".krita-*-*-autosave.kra");
// all autosave files for our application
QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden);
// Visible autosave files
filters = QStringList() << QString("krita-*-*-autosave.kra");
autosaveFiles += dir.entryList(filters, QDir::Files);
// Allow the user to make their selection
if (autosaveFiles.size() > 0) {
if (d->splashScreen) {
// hide the splashscreen to see the dialog
d->splashScreen->hide();
}
d->autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow());
QDialog::DialogCode result = (QDialog::DialogCode) d->autosaveDialog->exec();
if (result == QDialog::Accepted) {
QStringList filesToRecover = d->autosaveDialog->recoverableFiles();
Q_FOREACH (const QString &autosaveFile, autosaveFiles) {
if (!filesToRecover.contains(autosaveFile)) {
KisUsageLogger::log(QString("Removing autosave file %1").arg(dir.absolutePath() + "/" + autosaveFile));
QFile::remove(dir.absolutePath() + "/" + autosaveFile);
}
}
autosaveFiles = filesToRecover;
} else {
autosaveFiles.clear();
}
if (autosaveFiles.size() > 0) {
QList autosaveUrls;
Q_FOREACH (const QString &autoSaveFile, autosaveFiles) {
const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile);
autosaveUrls << url;
}
if (d->mainWindow) {
Q_FOREACH (const QUrl &url, autosaveUrls) {
KisMainWindow::OpenFlags flags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
d->mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile);
}
}
}
// cleanup
delete d->autosaveDialog;
d->autosaveDialog = nullptr;
}
}
bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow)
{
QString templatePath;
const QUrl templateUrl = QUrl::fromLocalFile(fileName);
if (QFile::exists(fileName)) {
templatePath = templateUrl.toLocalFile();
dbgUI << "using full path...";
}
else {
QString desktopName(fileName);
const QString templatesResourcePath = QStringLiteral("templates/");
QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName);
if (paths.isEmpty()) {
paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName);
}
if (paths.isEmpty()) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("No template found for: %1", desktopName));
} else if (paths.count() > 1) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Too many templates found for: %1", desktopName));
} else {
templatePath = paths.at(0);
}
}
if (!templatePath.isEmpty()) {
QUrl templateBase;
templateBase.setPath(templatePath);
KDesktopFile templateInfo(templatePath);
QString templateName = templateInfo.readUrl();
QUrl templateURL;
templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName);
if (templateURL.scheme().isEmpty()) {
templateURL.setScheme("file");
}
KisMainWindow::OpenFlags batchFlags = d->batchRun ? KisMainWindow::BatchMode : KisMainWindow::None;
if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) {
dbgUI << "Template loaded...";
return true;
}
else {
QMessageBox::critical(0, i18nc("@title:window", "Krita"),
i18n("Template %1 failed to load.", templateURL.toDisplayString()));
}
}
return false;
}
void KisApplication::resetConfig()
{
KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread());
KSharedConfigPtr config = KSharedConfig::openConfig();
config->markAsClean();
// find user settings file
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
QString kritarcPath = configPath + QStringLiteral("/kritarc");
QFile kritarcFile(kritarcPath);
if (kritarcFile.exists()) {
if (kritarcFile.open(QFile::ReadWrite)) {
QString backupKritarcPath = kritarcPath + QStringLiteral(".backup");
QFile backupKritarcFile(backupKritarcPath);
if (backupKritarcFile.exists()) {
backupKritarcFile.remove();
}
QMessageBox::information(0,
i18nc("@title:window", "Krita"),
i18n("Krita configurations reset!\n\n"
"Backup file was created at: %1\n\n"
"Restart Krita for changes to take effect.",
backupKritarcPath),
QMessageBox::Ok, QMessageBox::Ok);
// clear file
kritarcFile.rename(backupKritarcPath);
kritarcFile.close();
}
else {
QMessageBox::warning(0,
i18nc("@title:window", "Krita"),
i18n("Failed to clear %1\n\n"
"Please make sure no other program is using the file and try again.",
kritarcPath),
QMessageBox::Ok, QMessageBox::Ok);
}
}
// reload from disk; with the user file settings cleared,
// this should load any default configuration files shipping with the program
config->reparseConfiguration();
config->sync();
// Restore to default workspace
KConfigGroup cfg = KSharedConfig::openConfig()->group("MainWindow");
QString currentWorkspace = cfg.readEntry("CurrentWorkspace", "Default");
KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResourceSP workspace = rserver->resourceByName(currentWorkspace);
if (workspace) {
d->mainWindow->restoreWorkspace(workspace->resourceId());
}
}
void KisApplication::askresetConfig()
{
bool ok = QMessageBox::question(0,
i18nc("@title:window", "Krita"),
i18n("Do you want to clear the settings file?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes;
if (ok) {
resetConfig();
}
}
diff --git a/libs/ui/KisMainWindow.cpp b/libs/ui/KisMainWindow.cpp
index 3af5e8dc8e..5f610cf094 100644
--- a/libs/ui/KisMainWindow.cpp
+++ b/libs/ui/KisMainWindow.cpp
@@ -1,2673 +1,2722 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis
Copyright (C) 2000-2006 David Faure
Copyright (C) 2007, 2009 Thomas zander
Copyright (C) 2010 Benjamin Port
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "KisMainWindow.h"
#include
// qt includes
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "kis_selection_manager.h"
#include "kis_icon_utils.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "KoDockFactoryBase.h"
#include "KoDocumentInfoDlg.h"
#include "KoDocumentInfo.h"
#include "KoFileDialog.h"
#include
#include
#include
#include "KoToolDocker.h"
#include "KoToolBoxDocker_p.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef Q_OS_ANDROID
#include
#endif
#include
#include
#include "dialogs/kis_about_application.h"
#include "dialogs/kis_delayed_save_dialog.h"
#include "dialogs/kis_dlg_preferences.h"
#include "kis_action_manager.h"
#include "KisApplication.h"
#include "kis_canvas2.h"
#include "kis_canvas_controller.h"
#include "kis_canvas_resource_provider.h"
#include "kis_clipboard.h"
#include "kis_config.h"
#include "kis_config_notifier.h"
#include "kis_custom_image_widget.h"
#include
#include "kis_group_layer.h"
#include "kis_image_from_clipboard_widget.h"
#include "kis_image.h"
#include
#include "KisImportExportManager.h"
#include "kis_mainwindow_observer.h"
#include "kis_memory_statistics_server.h"
#include "kis_node.h"
#include "KisOpenPane.h"
#include "kis_paintop_box.h"
#include "KisPart.h"
#include "KisResourceServerProvider.h"
#include "kis_signal_compressor_with_param.h"
#include "kis_statusbar.h"
#include "KisView.h"
#include "KisViewManager.h"
#include "thememanager.h"
#include "kis_animation_importer.h"
#include "dialogs/kis_dlg_import_image_sequence.h"
#include
#include "KisWindowLayoutManager.h"
#include
#include "KisWelcomePageWidget.h"
#include
#include
#include "KisCanvasWindow.h"
#include "kis_action.h"
#include
class ToolDockerFactory : public KoDockFactoryBase
{
public:
ToolDockerFactory() : KoDockFactoryBase() { }
QString id() const override {
return "sharedtooldocker";
}
QDockWidget* createDockWidget() override {
KoToolDocker* dockWidget = new KoToolDocker();
return dockWidget;
}
DockPosition defaultDockPosition() const override {
return DockRight;
}
};
class Q_DECL_HIDDEN KisMainWindow::Private
{
public:
Private(KisMainWindow *parent, QUuid id)
: q(parent)
, id(id)
+ , styleMenu(new KActionMenu(i18nc("@action:inmenu", "Styles"), parent))
, dockWidgetMenu(new KActionMenu(i18nc("@action:inmenu", "&Dockers"), parent))
, windowMenu(new KActionMenu(i18nc("@action:inmenu", "&Window"), parent))
, documentMenu(new KActionMenu(i18nc("@action:inmenu", "New &View"), parent))
, workspaceMenu(new KActionMenu(i18nc("@action:inmenu", "Wor&kspace"), parent))
, welcomePage(new KisWelcomePageWidget(parent))
, widgetStack(new QStackedWidget(parent))
, mdiArea(new QMdiArea(parent))
, windowMapper(new KisSignalMapper(parent))
, documentMapper(new KisSignalMapper(parent))
#ifdef Q_OS_ANDROID
, fileManager(new KisAndroidFileManager(parent))
#endif
{
if (id.isNull()) this->id = QUuid::createUuid();
welcomeScroller = new QScrollArea();
welcomeScroller->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
welcomeScroller->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
welcomeScroller->setWidget(welcomePage);
welcomeScroller->setWidgetResizable(true);
widgetStack->addWidget(welcomeScroller);
widgetStack->addWidget(mdiArea);
mdiArea->setTabsMovable(true);
mdiArea->setActivationOrder(QMdiArea::ActivationHistoryOrder);
}
~Private() {
qDeleteAll(toolbarList);
}
KisMainWindow *q {0};
QUuid id;
KisViewManager *viewManager {0};
QPointer activeView;
QList toolbarList;
bool firstTime {true};
bool windowSizeDirty {false};
bool readOnly {false};
KisAction *showDocumentInfo {0};
KisAction *saveAction {0};
KisAction *saveActionAs {0};
KisAction *importAnimation {0};
KisAction *closeAll {0};
KisAction *importFile {0};
KisAction *exportFile {0};
KisAction *undo {0};
KisAction *redo {0};
KisAction *newWindow {0};
KisAction *close {0};
KisAction *mdiCascade {0};
KisAction *mdiTile {0};
KisAction *mdiNextWindow {0};
KisAction *mdiPreviousWindow {0};
KisAction *toggleDockers {0};
KisAction *resetConfigurations {0};
KisAction *toggleDockerTitleBars {0};
KisAction *toggleDetachCanvas {0};
KisAction *fullScreenMode {0};
KisAction *showSessionManager {0};
KisAction *expandingSpacers[2];
+ KActionMenu *styleMenu;
+ QActionGroup* styleActions;
+ QMap actionMap;
+
KActionMenu *dockWidgetMenu;
KActionMenu *windowMenu;
KActionMenu *documentMenu;
KActionMenu *workspaceMenu;
KHelpMenu *helpMenu {0};
KRecentFilesAction *recentFiles {0};
KisResourceModel *workspacemodel {0};
QScopedPointer undoActionsUpdateManager;
QString lastExportLocation;
QMap dockWidgetsMap;
QByteArray dockerStateBeforeHiding;
KoToolDocker *toolOptionsDocker {0};
QCloseEvent *deferredClosingEvent {0};
Digikam::ThemeManager *themeManager {0};
QScrollArea *welcomeScroller {0};
KisWelcomePageWidget *welcomePage {0};
QStackedWidget *widgetStack {0};
QMdiArea *mdiArea;
QMdiSubWindow *activeSubWindow {0};
KisSignalMapper *windowMapper;
KisSignalMapper *documentMapper;
KisCanvasWindow *canvasWindow {0};
QByteArray lastExportedFormat;
QScopedPointer > tabSwitchCompressor;
QMutex savingEntryMutex;
KConfigGroup windowStateConfig;
QUuid workspaceBorrowedBy;
KisSignalAutoConnectionsStore screenConnectionsStore;
#ifdef Q_OS_ANDROID
KisAndroidFileManager *fileManager;
#endif
KisActionManager * actionManager() {
return viewManager->actionManager();
}
QTabBar* findTabBarHACK() {
QObjectList objects = mdiArea->children();
Q_FOREACH (QObject *object, objects) {
QTabBar *bar = qobject_cast(object);
if (bar) {
return bar;
}
}
return 0;
}
};
KisMainWindow::KisMainWindow(QUuid uuid)
: KXmlGuiWindow()
, d(new Private(this, uuid))
{
d->workspacemodel = KisResourceModelProvider::resourceModel(ResourceType::Workspaces);
connect(d->workspacemodel, SIGNAL(afterResourcesLayoutReset()), this, SLOT(updateWindowMenu()));
d->viewManager = new KisViewManager(this, actionCollection());
KConfigGroup group( KSharedConfig::openConfig(), "theme");
d->themeManager = new Digikam::ThemeManager(group.readEntry("Theme", "Krita dark"), this);
d->windowStateConfig = KSharedConfig::openConfig()->group("MainWindow");
setStandardToolBarMenuEnabled(true);
setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
setDockNestingEnabled(true);
qApp->setStartDragDistance(25); // 25 px is a distance that works well for Tablet and Mouse events
#ifdef Q_OS_MACOS
setUnifiedTitleAndToolBarOnMac(true);
#endif
connect(this, SIGNAL(restoringDone()), this, SLOT(forceDockTabFonts()));
connect(this, SIGNAL(themeChanged()), d->viewManager, SLOT(updateIcons()));
connect(KisPart::instance(), SIGNAL(documentClosed(QString)), SLOT(updateWindowMenu()));
connect(KisPart::instance(), SIGNAL(documentOpened(QString)), SLOT(updateWindowMenu()));
connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), this, SLOT(configChanged()));
actionCollection()->addAssociatedWidget(this);
KoPluginLoader::instance()->load("Krita/ViewPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), d->viewManager, false);
// Load the per-application plugins (Right now, only Python) We do this only once, when the first mainwindow is being created.
KoPluginLoader::instance()->load("Krita/ApplicationPlugin", "Type == 'Service' and ([X-Krita-Version] == 28)", KoPluginLoader::PluginsConfig(), qApp, true);
KoToolBoxFactory toolBoxFactory;
QDockWidget *toolbox = createDockWidget(&toolBoxFactory);
toolbox->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable);
KisConfig cfg(true);
if (cfg.toolOptionsInDocker()) {
ToolDockerFactory toolDockerFactory;
d->toolOptionsDocker = qobject_cast(createDockWidget(&toolDockerFactory));
d->toolOptionsDocker->toggleViewAction()->setEnabled(true);
}
QMap dockwidgetActions;
dockwidgetActions[toolbox->toggleViewAction()->text()] = toolbox->toggleViewAction();
Q_FOREACH (const QString & docker, KoDockRegistry::instance()->keys()) {
KoDockFactoryBase *factory = KoDockRegistry::instance()->value(docker);
QDockWidget *dw = createDockWidget(factory);
dockwidgetActions[dw->toggleViewAction()->text()] = dw->toggleViewAction();
}
if (d->toolOptionsDocker) {
dockwidgetActions[d->toolOptionsDocker->toggleViewAction()->text()] = d->toolOptionsDocker->toggleViewAction();
}
connect(KoToolManager::instance(), SIGNAL(toolOptionWidgetsChanged(KoCanvasController*,QList >)), this, SLOT(newOptionWidgets(KoCanvasController*,QList >)));
Q_FOREACH (QString title, dockwidgetActions.keys()) {
d->dockWidgetMenu->addAction(dockwidgetActions[title]);
}
+
+ // Style menu actions
+ d->styleActions = new QActionGroup(this);
+ QAction * action;
+ Q_FOREACH (QString styleName, QStyleFactory::keys()) {
+ action = new QAction(styleName, d->styleActions);
+ action->setCheckable(true);
+ d->actionMap.insert(styleName, action);
+ d->styleMenu->addAction(d->actionMap.value(styleName));
+ }
+
+
+ // select the config value, or the current style if that does not exist
+ QString styleFromConfig = cfg.widgetStyle().toLower();
+ QString styleToSelect = styleFromConfig == "" ? style()->objectName().toLower() : styleFromConfig;
+
+ Q_FOREACH (auto key, d->actionMap.keys()) {
+ if(key.toLower() == styleToSelect) { // does the key match selection
+ d->actionMap.value(key)->setChecked(true);
+ }
+ }
+
+ connect(d->styleActions, SIGNAL(triggered(QAction*)),
+ this, SLOT(slotUpdateWidgetStyle()));
+
+
+
+
Q_FOREACH (QDockWidget *wdg, dockWidgets()) {
if ((wdg->features() & QDockWidget::DockWidgetClosable) == 0) {
wdg->setVisible(true);
}
}
Q_FOREACH (KoCanvasObserverBase* observer, canvasObservers()) {
observer->setObservedCanvas(0);
KisMainwindowObserver* mainwindowObserver = dynamic_cast(observer);
if (mainwindowObserver) {
mainwindowObserver->setViewManager(d->viewManager);
}
}
// Load all the actions from the tool plugins
Q_FOREACH(KoToolFactoryBase *toolFactory, KoToolRegistry::instance()->values()) {
toolFactory->createActions(actionCollection());
}
d->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
d->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
d->mdiArea->setTabPosition(QTabWidget::North);
d->mdiArea->setTabsClosable(true);
// Tab close button override
// Windows just has a black X, and Ubuntu has a dark x that is hard to read
// just switch this icon out for all OSs so it is easier to see
d->mdiArea->setStyleSheet("QTabBar::close-button { image: url(:/pics/broken-preset.png) }");
setCentralWidget(d->widgetStack);
d->widgetStack->setCurrentIndex(0);
connect(d->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)), this, SLOT(subWindowActivated()));
connect(d->windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*)));
connect(d->documentMapper, SIGNAL(mapped(QObject*)), this, SLOT(newView(QObject*)));
d->canvasWindow = new KisCanvasWindow(this);
actionCollection()->addAssociatedWidget(d->canvasWindow);
createActions();
// the welcome screen needs to grab actions...so make sure this line goes after the createAction() so they exist
d->welcomePage->setMainWindow(this);
setAutoSaveSettings(d->windowStateConfig, false);
subWindowActivated();
updateWindowMenu();
if (isHelpMenuEnabled() && !d->helpMenu) {
// workaround for KHelpMenu (or rather KAboutData::applicationData()) internally
// not using the Q*Application metadata ATM, which results e.g. in the bugreport wizard
// not having the app version preset
// fixed hopefully in KF5 5.22.0, patch pending
QGuiApplication *app = qApp;
KAboutData aboutData(app->applicationName(), app->applicationDisplayName(), app->applicationVersion());
aboutData.setOrganizationDomain(app->organizationDomain().toUtf8());
d->helpMenu = new KHelpMenu(this, aboutData, false);
// workaround-less version:
// d->helpMenu = new KHelpMenu(this, QString()/*unused*/, false);
// The difference between using KActionCollection->addAction() is that
// these actions do not get tied to the MainWindow. What does this all do?
KActionCollection *actions = d->viewManager->actionCollection();
QAction *helpContentsAction = d->helpMenu->action(KHelpMenu::menuHelpContents);
QAction *whatsThisAction = d->helpMenu->action(KHelpMenu::menuWhatsThis);
QAction *reportBugAction = d->helpMenu->action(KHelpMenu::menuReportBug);
QAction *switchLanguageAction = d->helpMenu->action(KHelpMenu::menuSwitchLanguage);
QAction *aboutAppAction = d->helpMenu->action(KHelpMenu::menuAboutApp);
QAction *aboutKdeAction = d->helpMenu->action(KHelpMenu::menuAboutKDE);
if (helpContentsAction) {
actions->addAction(helpContentsAction->objectName(), helpContentsAction);
}
if (whatsThisAction) {
actions->addAction(whatsThisAction->objectName(), whatsThisAction);
}
if (reportBugAction) {
actions->addAction(reportBugAction->objectName(), reportBugAction);
}
if (switchLanguageAction) {
actions->addAction(switchLanguageAction->objectName(), switchLanguageAction);
}
if (aboutAppAction) {
actions->addAction(aboutAppAction->objectName(), aboutAppAction);
}
if (aboutKdeAction) {
actions->addAction(aboutKdeAction->objectName(), aboutKdeAction);
}
connect(d->helpMenu, SIGNAL(showAboutApplication()), SLOT(showAboutApplication()));
}
// KDE' libs 4''s help contents action is broken outside kde, for some reason... We can handle it just as easily ourselves
QAction *helpAction = actionCollection()->action("help_contents");
helpAction->disconnect();
connect(helpAction, SIGNAL(triggered()), this, SLOT(showManual()));
#if 0
//check for colliding shortcuts
QSet existingShortcuts;
Q_FOREACH (QAction* action, actionCollection()->actions()) {
if(action->shortcut() == QKeySequence(0)) {
continue;
}
dbgKrita << "shortcut " << action->text() << " " << action->shortcut();
Q_ASSERT(!existingShortcuts.contains(action->shortcut()));
existingShortcuts.insert(action->shortcut());
}
#endif
configChanged();
// Make sure the python plugins create their actions in time
KisPart::instance()->notifyMainWindowIsBeingCreated(this);
// If we have customized the toolbars, load that first
setLocalXMLFile(KoResourcePaths::locateLocal("data", "krita5.xmlgui"));
setXMLFile(":/kxmlgui5/krita5.xmlgui");
guiFactory()->addClient(this);
connect(guiFactory(), SIGNAL(makingChanges(bool)), SLOT(slotXmlGuiMakingChanges(bool)));
// Create and plug toolbar list for Settings menu
QList toolbarList;
Q_FOREACH (QWidget* it, guiFactory()->containers("ToolBar")) {
KToolBar * toolBar = ::qobject_cast(it);
if (toolBar) {
toolBar->setMovable(KisConfig(true).readEntry("LockAllDockerPanels", false));
if (toolBar->objectName() == "BrushesAndStuff") {
toolBar->setEnabled(false);
}
KToggleAction* act = new KToggleAction(i18n("Show %1 Toolbar", toolBar->windowTitle()), this);
actionCollection()->addAction(toolBar->objectName().toUtf8(), act);
act->setCheckedState(KGuiItem(i18n("Hide %1 Toolbar", toolBar->windowTitle())));
connect(act, SIGNAL(toggled(bool)), this, SLOT(slotToolbarToggled(bool)));
act->setChecked(!toolBar->isHidden());
toolbarList.append(act);
} else {
warnUI << "Toolbar list contains a " << it->metaObject()->className() << " which is not a toolbar!";
}
}
KToolBar::setToolBarsLocked(KisConfig(true).readEntry("LockAllDockerPanels", false));
plugActionList("toolbarlist", toolbarList);
d->toolbarList = toolbarList;
applyToolBarLayout();
d->viewManager->updateGUI();
d->viewManager->updateIcons();
QTimer::singleShot(1000, this, SLOT(checkSanity()));
{
using namespace std::placeholders; // For _1 placeholder
std::function callback(
std::bind(&KisMainWindow::switchTab, this, _1));
d->tabSwitchCompressor.reset(
new KisSignalCompressorWithParam(500, callback, KisSignalCompressor::FIRST_INACTIVE));
}
if (cfg.readEntry("CanvasOnlyActive", false)) {
QString currentWorkspace = cfg.readEntry("CurrentWorkspace", "Default");
KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResourceSP workspace = rserver->resourceByName(currentWorkspace);
if (workspace) {
restoreWorkspace(workspace->resourceId());
}
cfg.writeEntry("CanvasOnlyActive", false);
menuBar()->setVisible(true);
}
this->winId(); // Ensures the native window has been created.
QWindow *window = this->windowHandle();
connect(window, SIGNAL(screenChanged(QScreen *)), this, SLOT(windowScreenChanged(QScreen *)));
}
KisMainWindow::~KisMainWindow()
{
// Q_FOREACH (QAction *ac, actionCollection()->actions()) {
// QAction *action = qobject_cast(ac);
// if (action) {
// qDebug() << "objectName()
// << "\n\ticon=" << action->icon().name()
// << "\n\ttext=" << action->text().replace("&", "&")
// << "\n\twhatsThis=" << action->whatsThis()
// << "\n\ttoolTip=" << action->toolTip().replace("", "").replace("", "")
// << "\n\ticonText=" << action->iconText().replace("&", "&")
// << "\n\tshortcut=" << action->shortcut().toString()
// << "\n\tisCheckable=" << QString((action->isChecked() ? "true" : "false"))
// << "\n\tstatusTip=" << action->statusTip()
// << "\n/>\n" ;
// }
// else {
// dbgKrita << "Got a non-qaction:" << ac->objectName();
// }
// }
// The doc and view might still exist (this is the case when closing the window)
KisPart::instance()->removeMainWindow(this);
delete d->viewManager;
delete d;
}
QUuid KisMainWindow::id() const {
return d->id;
}
void KisMainWindow::addView(KisView *view, QMdiSubWindow *subWindow)
{
if (d->activeView == view && !subWindow) return;
if (d->activeView) {
d->activeView->disconnect(this);
}
// register the newly created view in the input manager
viewManager()->inputManager()->addTrackedCanvas(view->canvasBase());
showView(view, subWindow);
updateCaption();
emit restoringDone();
if (d->activeView) {
connect(d->activeView, SIGNAL(titleModified(QString,bool)), SLOT(slotDocumentTitleModified()));
connect(d->viewManager->statusBar(), SIGNAL(memoryStatusUpdated()), this, SLOT(updateCaption()));
}
}
void KisMainWindow::notifyChildViewDestroyed(KisView *view)
{
/**
* If we are the last view of the window, Qt will not activate another tab
* before destroying tab/window. In this case we should clear all the dangling
* pointers manually by setting the current view to null
*/
viewManager()->inputManager()->removeTrackedCanvas(view->canvasBase());
if (view->canvasBase() == viewManager()->canvasBase()) {
viewManager()->setCurrentView(0);
}
}
void KisMainWindow::showView(KisView *imageView, QMdiSubWindow *subwin)
{
if (imageView && activeView() != imageView) {
// XXX: find a better way to initialize this!
imageView->setViewManager(d->viewManager);
imageView->canvasBase()->setFavoriteResourceManager(d->viewManager->paintOpBox()->favoriteResourcesManager());
imageView->slotLoadingFinished();
if (!subwin) {
subwin = d->mdiArea->addSubWindow(imageView);
} else {
subwin->setWidget(imageView);
}
imageView->setSubWindow(subwin);
subwin->setAttribute(Qt::WA_DeleteOnClose, true);
connect(subwin, SIGNAL(destroyed()), SLOT(updateWindowMenu()));
KisConfig cfg(true);
subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL()));
subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL()));
subwin->setWindowIcon(qApp->windowIcon());
#ifdef Q_OS_MACOS
connect(subwin, SIGNAL(destroyed()), SLOT(updateSubwindowFlags()));
updateSubwindowFlags();
#endif
if (d->mdiArea->subWindowList().size() == 1) {
imageView->showMaximized();
}
else {
imageView->show();
}
/**
* Hack alert!
*
* Here we explicitly request KoToolManager to emit all the tool
* activation signals, to reinitialize the tool options docker.
*
* That is needed due to a design flaw we have in the
* initialization procedure. The tool in the KoToolManager is
* initialized in KisView::setViewManager() calls, which
* happens early enough. During this call the tool manager
* requests KoCanvasControllerWidget to emit the signal to
* update the widgets in the tool docker. *But* at that moment
* of time the view is not yet connected to the main window,
* because it happens in KisViewManager::setCurrentView a bit
* later. This fact makes the widgets updating signals be lost
* and never reach the tool docker.
*
* So here we just explicitly call the tool activation stub.
*/
KoToolManager::instance()->initializeCurrentToolForCanvas();
// No, no, no: do not try to call this _before_ the show() has
// been called on the view; only when that has happened is the
// opengl context active, and very bad things happen if we tell
// the dockers to update themselves with a view if the opengl
// context is not active.
setActiveView(imageView);
updateWindowMenu();
updateCaption();
}
}
void KisMainWindow::slotPreferences()
{
QScopedPointer dlgPreferences(new KisDlgPreferences(this));
if (dlgPreferences->editPreferences()) {
KisConfigNotifier::instance()->notifyConfigChanged();
KisConfigNotifier::instance()->notifyPixelGridModeChanged();
KisImageConfigNotifier::instance()->notifyConfigChanged();
// XXX: should this be changed for the views in other windows as well?
Q_FOREACH (QPointer koview, KisPart::instance()->views()) {
KisViewManager *view = qobject_cast(koview);
if (view) {
// Update the settings for all nodes -- they don't query
// KisConfig directly because they need the settings during
// compositing, and they don't connect to the config notifier
// because nodes are not QObjects (because only one base class
// can be a QObject).
KisNode* node = dynamic_cast(view->image()->rootLayer().data());
node->updateSettings();
}
}
updateWindowMenu();
d->viewManager->showHideScrollbars();
}
}
void KisMainWindow::slotThemeChanged()
{
// save theme changes instantly
KConfigGroup group( KSharedConfig::openConfig(), "theme");
group.writeEntry("Theme", d->themeManager->currentThemeName());
// reload action icons!
Q_FOREACH (QAction *action, actionCollection()->actions()) {
KisIconUtils::updateIcon(action);
}
if (d->mdiArea) {
d->mdiArea->setPalette(qApp->palette());
for (int i=0; imdiArea->subWindowList().size(); i++) {
QMdiSubWindow *window = d->mdiArea->subWindowList().at(i);
if (window) {
window->setPalette(qApp->palette());
KisView *view = qobject_cast(window->widget());
if (view) {
view->slotThemeChanged(qApp->palette());
}
}
}
}
emit themeChanged();
}
bool KisMainWindow::canvasDetached() const
{
return centralWidget() != d->widgetStack;
}
void KisMainWindow::setCanvasDetached(bool detach)
{
if (detach == canvasDetached()) return;
QWidget *outgoingWidget = centralWidget() ? takeCentralWidget() : nullptr;
QWidget *incomingWidget = d->canvasWindow->swapMainWidget(outgoingWidget);
if (incomingWidget) {
setCentralWidget(incomingWidget);
}
if (detach) {
d->canvasWindow->show();
} else {
d->canvasWindow->hide();
}
}
void KisMainWindow::slotFileSelected(QString path)
{
QString url = path;
if (!url.isEmpty()) {
bool res = openDocument(QUrl::fromLocalFile(url), Import);
if (!res) {
warnKrita << "Loading" << url << "failed";
}
}
}
void KisMainWindow::slotEmptyFilePath()
{
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The chosen file's location could not be found. Does it exist?"));
}
QWidget * KisMainWindow::canvasWindow() const
{
return d->canvasWindow;
}
void KisMainWindow::setReadWrite(bool readwrite)
{
d->saveAction->setEnabled(readwrite);
d->importFile->setEnabled(readwrite);
d->readOnly = !readwrite;
updateCaption();
}
void KisMainWindow::addRecentURL(const QUrl &url, const QUrl &oldUrl)
{
// Add entry to recent documents list
// (call coming from KisDocument because it must work with cmd line, template dlg, file/open, etc.)
if (!url.isEmpty()) {
bool ok = true;
if (url.isLocalFile()) {
QString path = url.adjusted(QUrl::StripTrailingSlash).toLocalFile();
const QStringList tmpDirs = KoResourcePaths::resourceDirs("tmp");
for (QStringList::ConstIterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it) {
if (path.contains(*it)) {
ok = false; // it's in the tmp resource
}
}
const QStringList templateDirs = KoResourcePaths::findDirs("templates");
for (QStringList::ConstIterator it = templateDirs.begin() ; ok && it != templateDirs.end() ; ++it) {
if (path.contains(*it)) {
ok = false; // it's in the templates directory.
break;
}
}
}
if (ok) {
if (!oldUrl.isEmpty()) {
d->recentFiles->removeUrl(oldUrl);
}
d->recentFiles->addUrl(url);
}
saveRecentFiles();
}
}
void KisMainWindow::saveRecentFiles()
{
// Save list of recent files
KSharedConfigPtr config = KSharedConfig::openConfig();
d->recentFiles->saveEntries(config->group("RecentFiles"));
config->sync();
// Tell all windows to reload their list, after saving
// Doesn't work multi-process, but it's a start
Q_FOREACH (KisMainWindow *mw, KisPart::instance()->mainWindows()) {
if (mw != this) {
mw->reloadRecentFileList();
}
}
}
QList KisMainWindow::recentFilesUrls()
{
return d->recentFiles->urls();
}
void KisMainWindow::clearRecentFiles()
{
d->recentFiles->clear();
d->welcomePage->populateRecentDocuments();
}
void KisMainWindow::removeRecentUrl(const QUrl &url)
{
d->recentFiles->removeUrl(url);
KSharedConfigPtr config = KSharedConfig::openConfig();
d->recentFiles->saveEntries(config->group("RecentFiles"));
config->sync();
}
void KisMainWindow::reloadRecentFileList()
{
d->recentFiles->loadEntries(KSharedConfig::openConfig()->group("RecentFiles"));
}
void KisMainWindow::updateCaption()
{
if (!d->mdiArea->activeSubWindow()) {
updateCaption(QString(), false);
}
else if (d->activeView && d->activeView->document() && d->activeView->image()){
KisDocument *doc = d->activeView->document();
QString caption(doc->caption());
caption = "RESOURCES REWRITE GOING ON " + caption;
if (d->readOnly) {
caption += " [" + i18n("Write Protected") + "] ";
}
if (doc->isRecovered()) {
caption += " [" + i18n("Recovered") + "] ";
}
// show the file size for the document
KisMemoryStatisticsServer::Statistics m_fileSizeStats = KisMemoryStatisticsServer::instance()->fetchMemoryStatistics(d->activeView ? d->activeView->image() : 0);
if (m_fileSizeStats.imageSize) {
caption += QString(" (").append( KFormat().formatByteSize(m_fileSizeStats.imageSize)).append( ")");
}
updateCaption(caption, doc->isModified());
if (!doc->url().fileName().isEmpty()) {
d->saveAction->setToolTip(i18n("Save as %1", doc->url().fileName()));
}
else {
d->saveAction->setToolTip(i18n("Save"));
}
}
}
void KisMainWindow::updateCaption(const QString &caption, bool modified)
{
QString versionString = KritaVersionWrapper::versionString(true);
QString title = caption;
if (!title.contains(QStringLiteral("[*]"))) { // append the placeholder so that the modified mechanism works
title.append(QStringLiteral(" [*]"));
}
if (d->mdiArea->activeSubWindow()) {
#if defined(KRITA_ALPHA) || defined (KRITA_BETA) || defined (KRITA_RC)
d->mdiArea->activeSubWindow()->setWindowTitle(QString("%1: %2").arg(versionString).arg(title));
#else
d->mdiArea->activeSubWindow()->setWindowTitle(title);
#endif
d->mdiArea->activeSubWindow()->setWindowModified(modified);
}
else {
#if defined(KRITA_ALPHA) || defined (KRITA_BETA) || defined (KRITA_RC)
setWindowTitle(QString("%1: %2").arg(versionString).arg(title));
#else
setWindowTitle(title);
#endif
}
setWindowModified(modified);
}
KisView *KisMainWindow::activeView() const
{
if (d->activeView) {
return d->activeView;
}
return 0;
}
bool KisMainWindow::openDocument(const QUrl &url, OpenFlags flags)
{
if (!QFile(url.toLocalFile()).exists()) {
if (!(flags & BatchMode)) {
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("The file %1 does not exist.", url.url()));
}
d->recentFiles->removeUrl(url); //remove the file from the recent-opened-file-list
saveRecentFiles();
return false;
}
return openDocumentInternal(url, flags);
}
bool KisMainWindow::openDocumentInternal(const QUrl &url, OpenFlags flags)
{
if (!url.isLocalFile()) {
qWarning() << "KisMainWindow::openDocumentInternal. Not a local file:" << url;
return false;
}
KisDocument *newdoc = KisPart::instance()->createDocument();
if (flags & BatchMode) {
newdoc->setFileBatchMode(true);
}
d->firstTime = true;
connect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
connect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
KisDocument::OpenFlags openFlags = KisDocument::None;
// XXX: Why this duplication of of OpenFlags...
if (flags & RecoveryFile) {
openFlags |= KisDocument::RecoveryFile;
}
bool openRet = !(flags & Import) ? newdoc->openUrl(url, openFlags) : newdoc->importDocument(url);
if (!openRet) {
delete newdoc;
return false;
}
KisPart::instance()->addDocument(newdoc);
if (!QFileInfo(url.toLocalFile()).isWritable()) {
setReadWrite(false);
}
// Try to determine whether this was an unnamed autosave
if (flags & RecoveryFile &&
( url.toLocalFile().startsWith(QDir::tempPath())
|| url.toLocalFile().startsWith(QDir::homePath())
) &&
( QFileInfo(url.toLocalFile()).fileName().startsWith(".krita")
|| QFileInfo(url.toLocalFile()).fileName().startsWith("krita")
)
)
{
QString path = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
if (!QFileInfo(path).exists()) {
path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
}
newdoc->setUrl(QUrl::fromLocalFile( path + "/" + newdoc->objectName() + ".kra"));
}
return true;
}
void KisMainWindow::showDocument(KisDocument *document) {
Q_FOREACH(QMdiSubWindow *subwindow, d->mdiArea->subWindowList()) {
KisView *view = qobject_cast(subwindow->widget());
KIS_SAFE_ASSERT_RECOVER_NOOP(view);
if (view) {
if (view->document() == document) {
setActiveSubWindow(subwindow);
return;
}
}
}
addViewAndNotifyLoadingCompleted(document);
}
KisView* KisMainWindow::addViewAndNotifyLoadingCompleted(KisDocument *document,
QMdiSubWindow *subWindow)
{
showWelcomeScreen(false); // see workaround in function header
KisView *view = KisPart::instance()->createView(document, d->viewManager, this);
addView(view, subWindow);
emit guiLoadingFinished();
return view;
}
QStringList KisMainWindow::showOpenFileDialog(bool isImporting)
{
KoFileDialog dialog(this, KoFileDialog::ImportFiles, "OpenDocument");
dialog.setDefaultDir(QStandardPaths::writableLocation(QStandardPaths::PicturesLocation));
dialog.setMimeTypeFilters(KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import));
dialog.setCaption(isImporting ? i18n("Import Images") : i18n("Open Images"));
return dialog.filenames();
}
// Separate from openDocument to handle async loading (remote URLs)
void KisMainWindow::slotLoadCompleted()
{
KisDocument *newdoc = qobject_cast(sender());
if (newdoc && newdoc->image()) {
addViewAndNotifyLoadingCompleted(newdoc);
disconnect(newdoc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
disconnect(newdoc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
emit loadCompleted();
}
}
void KisMainWindow::slotLoadCanceled(const QString & errMsg)
{
KisUsageLogger::log(QString("Loading canceled. Error:").arg(errMsg));
if (!errMsg.isEmpty()) // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
// ... can't delete the document, it's the one who emitted the signal...
KisDocument* doc = qobject_cast(sender());
Q_ASSERT(doc);
disconnect(doc, SIGNAL(completed()), this, SLOT(slotLoadCompleted()));
disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotLoadCanceled(QString)));
}
void KisMainWindow::slotSaveCanceled(const QString &errMsg)
{
KisUsageLogger::log(QString("Saving canceled. Error:").arg(errMsg));
if (!errMsg.isEmpty()) { // empty when canceled by user
QMessageBox::critical(this, i18nc("@title:window", "Krita"), errMsg);
}
slotSaveCompleted();
}
void KisMainWindow::slotSaveCompleted()
{
KisUsageLogger::log(QString("Saving Completed"));
KisDocument* doc = qobject_cast(sender());
Q_ASSERT(doc);
disconnect(doc, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
disconnect(doc, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
if (d->deferredClosingEvent) {
KXmlGuiWindow::closeEvent(d->deferredClosingEvent);
}
}
bool KisMainWindow::hackIsSaving() const
{
StdLockableWrapper wrapper(&d->savingEntryMutex);
std::unique_lock> l(wrapper, std::try_to_lock);
return !l.owns_lock();
}
bool KisMainWindow::installBundle(const QString &fileName) const
{
QFileInfo from(fileName);
QFileInfo to(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName());
if (to.exists()) {
QFile::remove(to.canonicalFilePath());
}
return QFile::copy(fileName, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/" + from.fileName());
}
QImage KisMainWindow::layoutThumbnail()
{
int size = 256;
qreal scale = qreal(size)/qreal(qMax(geometry().width(), geometry().height()));
QImage layoutThumbnail = QImage(qRound(geometry().width()*scale), qRound(geometry().height()*scale), QImage::Format_ARGB32);
QPainter gc(&layoutThumbnail);
gc.fillRect(0, 0, layoutThumbnail.width(), layoutThumbnail.height(), this->palette().dark());
for (int childW = 0; childW< children().size(); childW++) {
if (children().at(childW)->isWidgetType()) {
QWidget *w = dynamic_cast(children().at(childW));
if (w->isVisible()) {
QRect wRect = QRectF(w->geometry().x()*scale
, w->geometry().y()*scale
, w->geometry().width()*scale
, w->geometry().height()*scale
).toRect();
wRect = wRect.intersected(layoutThumbnail.rect().adjusted(-1, -1, -1, -1));
gc.setBrush(this->palette().window());
if (w == d->widgetStack) {
gc.setBrush(d->mdiArea->background());
}
gc.setPen(this->palette().windowText().color());
gc.drawRect(wRect);
}
}
}
gc.end();
return layoutThumbnail;
}
bool KisMainWindow::saveDocument(KisDocument *document, bool saveas, bool isExporting)
{
if (!document) {
return true;
}
/**
* Make sure that we cannot enter this method twice!
*
* The lower level functions may call processEvents() so
* double-entry is quite possible to achieve. Here we try to lock
* the mutex, and if it is failed, just cancel saving.
*/
StdLockableWrapper wrapper(&d->savingEntryMutex);
std::unique_lock> l(wrapper, std::try_to_lock);
if (!l.owns_lock()) return false;
// no busy wait for saving because it is dangerous!
KisDelayedSaveDialog dlg(document->image(), KisDelayedSaveDialog::SaveDialog, 0, this);
dlg.blockIfImageIsBusy();
if (dlg.result() == KisDelayedSaveDialog::Rejected) {
return false;
}
else if (dlg.result() == KisDelayedSaveDialog::Ignored) {
QMessageBox::critical(0,
i18nc("@title:window", "Krita"),
i18n("You are saving a file while the image is "
"still rendering. The saved file may be "
"incomplete or corrupted.\n\n"
"Please select a location where the original "
"file will not be overridden!"));
saveas = true;
}
if (document->isRecovered()) {
saveas = true;
}
if (document->url().isEmpty()) {
saveas = true;
}
connect(document, SIGNAL(completed()), this, SLOT(slotSaveCompleted()));
connect(document, SIGNAL(canceled(QString)), this, SLOT(slotSaveCanceled(QString)));
QByteArray nativeFormat = document->nativeFormatMimeType();
QByteArray oldMimeFormat = document->mimeType();
QUrl suggestedURL = document->url();
QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Export);
if (!mimeFilter.contains(oldMimeFormat)) {
dbgUI << "KisMainWindow::saveDocument no export filter for" << oldMimeFormat;
// --- don't setOutputMimeType in case the user cancels the Save As
// dialog and then tries to just plain Save ---
// suggest a different filename extension (yes, we fortunately don't all live in a world of magic :))
QString suggestedFilename = QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
if (!suggestedFilename.isEmpty()) { // ".kra" looks strange for a name
suggestedFilename = suggestedFilename + "." + KisMimeDatabase::suffixesForMimeType(KIS_MIME_TYPE).first();
suggestedURL = suggestedURL.adjusted(QUrl::RemoveFilename);
suggestedURL.setPath(suggestedURL.path() + suggestedFilename);
}
// force the user to choose outputMimeType
saveas = true;
}
bool ret = false;
if (document->url().isEmpty() || isExporting || saveas) {
// if you're just File/Save As'ing to change filter options you
// don't want to be reminded about overwriting files etc.
bool justChangingFilterOptions = false;
KoFileDialog dialog(this, KoFileDialog::SaveFile, "SaveAs");
dialog.setCaption(isExporting ? i18n("Exporting") : i18n("Saving As"));
//qDebug() << ">>>>>" << isExporting << d->lastExportLocation << d->lastExportedFormat << QString::fromLatin1(document->mimeType());
if (isExporting && !d->lastExportLocation.isEmpty() && !d->lastExportLocation.contains(QDir::tempPath())) {
// Use the location where we last exported to, if it's set, as the opening location for the file dialog
QString proposedPath = QFileInfo(d->lastExportLocation).absolutePath();
// If the document doesn't have a filename yet, use the title
QString proposedFileName = suggestedURL.isEmpty() ? document->documentInfo()->aboutInfo("title") : QFileInfo(suggestedURL.toLocalFile()).completeBaseName();
// Use the last mimetype we exported to by default
QString proposedMimeType = d->lastExportedFormat.isEmpty() ? "" : d->lastExportedFormat;
QString proposedExtension = KisMimeDatabase::suffixesForMimeType(proposedMimeType).first().remove("*,");
// Set the default dir: this overrides the one loaded from the config file, since we're exporting and the lastExportLocation is not empty
dialog.setDefaultDir(proposedPath + "/" + proposedFileName + "." + proposedExtension, true);
dialog.setMimeTypeFilters(mimeFilter, proposedMimeType);
}
else {
// Get the last used location for saving
KConfigGroup group = KSharedConfig::openConfig()->group("File Dialogs");
QString proposedPath = group.readEntry("SaveAs", "");
// if that is empty, get the last used location for loading
if (proposedPath.isEmpty()) {
proposedPath = group.readEntry("OpenDocument", "");
}
// If that is empty, too, use the Pictures location.
if (proposedPath.isEmpty()) {
proposedPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
}
// But only use that if the suggestedUrl, that is, the document's own url is empty, otherwise
// open the location where the document currently is.
dialog.setDefaultDir(suggestedURL.isEmpty() ? proposedPath : suggestedURL.toLocalFile(), true);
// If exporting, default to all supported file types if user is exporting
QByteArray default_mime_type = "";
if (!isExporting) {
// otherwise use the document's mimetype, or if that is empty, kra, which is the savest.
default_mime_type = document->mimeType().isEmpty() ? nativeFormat : document->mimeType();
}
dialog.setMimeTypeFilters(mimeFilter, QString::fromLatin1(default_mime_type));
}
QUrl newURL = QUrl::fromUserInput(dialog.filename());
if (newURL.isLocalFile()) {
QString fn = newURL.toLocalFile();
if (QFileInfo(fn).completeSuffix().isEmpty()) {
fn.append(KisMimeDatabase::suffixesForMimeType(nativeFormat).first());
newURL = QUrl::fromLocalFile(fn);
}
}
if (document->documentInfo()->aboutInfo("title") == i18n("Unnamed")) {
QString fn = newURL.toLocalFile();
QFileInfo info(fn);
document->documentInfo()->setAboutInfo("title", info.completeBaseName());
}
QByteArray outputFormat = nativeFormat;
QString outputFormatString = KisMimeDatabase::mimeTypeForFile(newURL.toLocalFile(), false);
outputFormat = outputFormatString.toLatin1();
if (!isExporting) {
justChangingFilterOptions = (newURL == document->url()) && (outputFormat == document->mimeType());
}
else {
QString path = QFileInfo(d->lastExportLocation).absolutePath();
QString filename = QFileInfo(document->url().toLocalFile()).completeBaseName();
justChangingFilterOptions = (QFileInfo(newURL.toLocalFile()).absolutePath() == path)
&& (QFileInfo(newURL.toLocalFile()).completeBaseName() == filename)
&& (outputFormat == d->lastExportedFormat);
}
bool bOk = true;
if (newURL.isEmpty()) {
bOk = false;
}
if (bOk) {
bool wantToSave = true;
// don't change this line unless you know what you're doing :)
if (!justChangingFilterOptions) {
if (!document->isNativeFormat(outputFormat))
wantToSave = true;
}
if (wantToSave) {
if (!isExporting) { // Save As
ret = document->saveAs(newURL, outputFormat, true);
if (ret) {
dbgUI << "Successful Save As!";
KisPart::instance()->addRecentURLToAllMainWindows(newURL);
setReadWrite(true);
} else {
dbgUI << "Failed Save As!";
}
}
else { // Export
ret = document->exportDocument(newURL, outputFormat);
if (ret) {
d->lastExportLocation = newURL.toLocalFile();
d->lastExportedFormat = outputFormat;
}
}
} // if (wantToSave) {
else
ret = false;
} // if (bOk) {
else
ret = false;
} else { // saving
// We cannot "export" into the currently
// opened document. We are not Gimp.
KIS_ASSERT_RECOVER_NOOP(!isExporting);
// be sure document has the correct outputMimeType!
if (document->isModified()) {
ret = document->save(true, 0);
}
if (!ret) {
dbgUI << "Failed Save!";
}
}
updateCaption();
return ret;
}
void KisMainWindow::undo()
{
if (activeView()) {
activeView()->document()->undoStack()->undo();
}
}
void KisMainWindow::redo()
{
if (activeView()) {
activeView()->document()->undoStack()->redo();
}
}
void KisMainWindow::closeEvent(QCloseEvent *e)
{
if (hackIsSaving()) {
e->setAccepted(false);
return;
}
if (!KisPart::instance()->closingSession()) {
QAction *action= d->viewManager->actionCollection()->action("view_show_canvas_only");
if ((action) && (action->isChecked())) {
action->setChecked(false);
}
// Save session when last window is closed
if (KisPart::instance()->mainwindowCount() == 1) {
bool closeAllowed = KisPart::instance()->closeSession();
if (!closeAllowed) {
e->setAccepted(false);
return;
}
}
}
d->mdiArea->closeAllSubWindows();
QList childrenList = d->mdiArea->subWindowList();
if (childrenList.isEmpty()) {
d->deferredClosingEvent = e;
saveWindowState(true);
d->canvasWindow->close();
} else {
e->setAccepted(false);
}
}
void KisMainWindow::saveWindowSettings()
{
KSharedConfigPtr config = KSharedConfig::openConfig();
if (d->windowSizeDirty ) {
dbgUI << "KisMainWindow::saveWindowSettings";
KConfigGroup group = d->windowStateConfig;
KWindowConfig::saveWindowSize(windowHandle(), group);
config->sync();
d->windowSizeDirty = false;
}
if (!d->activeView || d->activeView->document()) {
// Save toolbar position into the config file of the app, under the doc's component name
KConfigGroup group = d->windowStateConfig;
saveMainWindowSettings(group);
// Save collapsible state of dock widgets
for (QMap::const_iterator i = d->dockWidgetsMap.constBegin();
i != d->dockWidgetsMap.constEnd(); ++i) {
if (i.value()->widget()) {
KConfigGroup dockGroup = group.group(QString("DockWidget ") + i.key());
dockGroup.writeEntry("Collapsed", i.value()->widget()->isHidden());
dockGroup.writeEntry("Locked", i.value()->property("Locked").toBool());
dockGroup.writeEntry("DockArea", (int) dockWidgetArea(i.value()));
dockGroup.writeEntry("xPosition", (int) i.value()->widget()->x());
dockGroup.writeEntry("yPosition", (int) i.value()->widget()->y());
dockGroup.writeEntry("width", (int) i.value()->widget()->width());
dockGroup.writeEntry("height", (int) i.value()->widget()->height());
}
}
}
KSharedConfig::openConfig()->sync();
resetAutoSaveSettings(); // Don't let KMainWindow override the good stuff we wrote down
}
void KisMainWindow::resizeEvent(QResizeEvent * e)
{
d->windowSizeDirty = true;
KXmlGuiWindow::resizeEvent(e);
}
void KisMainWindow::setActiveView(KisView* view)
{
d->activeView = view;
updateCaption();
if (d->undoActionsUpdateManager) {
d->undoActionsUpdateManager->setCurrentDocument(view ? view->document() : 0);
}
d->viewManager->setCurrentView(view);
KisWindowLayoutManager::instance()->activeDocumentChanged(view->document());
}
void KisMainWindow::dragMove(QDragMoveEvent * event)
{
QTabBar *tabBar = d->findTabBarHACK();
if (!tabBar && d->mdiArea->viewMode() == QMdiArea::TabbedView) {
qWarning() << "WARNING!!! Cannot find QTabBar in the main window! Looks like Qt has changed behavior. Drag & Drop between multiple tabs might not work properly (tabs will not switch automatically)!";
}
if (tabBar && tabBar->isVisible()) {
QPoint pos = tabBar->mapFromGlobal(mapToGlobal(event->pos()));
if (tabBar->rect().contains(pos)) {
const int tabIndex = tabBar->tabAt(pos);
if (tabIndex >= 0 && tabBar->currentIndex() != tabIndex) {
d->tabSwitchCompressor->start(tabIndex);
}
} else if (d->tabSwitchCompressor->isActive()) {
d->tabSwitchCompressor->stop();
}
}
}
void KisMainWindow::dragLeave()
{
if (d->tabSwitchCompressor->isActive()) {
d->tabSwitchCompressor->stop();
}
}
void KisMainWindow::switchTab(int index)
{
QTabBar *tabBar = d->findTabBarHACK();
if (!tabBar) return;
tabBar->setCurrentIndex(index);
}
void KisMainWindow::showWelcomeScreen(bool show)
{
d->widgetStack->setCurrentIndex(!show);
}
void KisMainWindow::slotFileNew()
{
const QStringList mimeFilter = KisImportExportManager::supportedMimeTypes(KisImportExportManager::Import);
KisOpenPane *startupWidget = new KisOpenPane(this, mimeFilter, QStringLiteral("templates/"));
startupWidget->setWindowModality(Qt::WindowModal);
startupWidget->setWindowTitle(i18n("Create new document"));
KisConfig cfg(true);
int w = cfg.defImageWidth();
int h = cfg.defImageHeight();
const double resolution = cfg.defImageResolution();
const QString colorModel = cfg.defColorModel();
const QString colorDepth = cfg.defaultColorDepth();
const QString colorProfile = cfg.defColorProfile();
CustomDocumentWidgetItem item;
item.widget = new KisCustomImageWidget(startupWidget,
w,
h,
resolution,
colorModel,
colorDepth,
colorProfile,
i18n("Unnamed"));
item.icon = "document-new";
item.title = i18n("Custom Document");
startupWidget->addCustomDocumentWidget(item.widget, item.title, "Custom Document", item.icon);
QSize sz = KisClipboard::instance()->clipSize();
if (sz.isValid() && sz.width() != 0 && sz.height() != 0) {
w = sz.width();
h = sz.height();
}
item.widget = new KisImageFromClipboard(startupWidget,
w,
h,
resolution,
colorModel,
colorDepth,
colorProfile,
i18n("Unnamed"));
item.title = i18n("Create from Clipboard");
item.icon = "tab-new";
startupWidget->addCustomDocumentWidget(item.widget, item.title, "Create from ClipBoard", item.icon);
// calls deleteLater
connect(startupWidget, SIGNAL(documentSelected(KisDocument*)), KisPart::instance(), SLOT(startCustomDocument(KisDocument*)));
// calls deleteLater
connect(startupWidget, SIGNAL(openTemplate(QUrl)), KisPart::instance(), SLOT(openTemplate(QUrl)));
startupWidget->exec();
// Cancel calls deleteLater...
}
void KisMainWindow::slotImportFile()
{
dbgUI << "slotImportFile()";
slotFileOpen(true);
}
void KisMainWindow::slotFileOpen(bool isImporting)
{
#ifndef Q_OS_ANDROID
QStringList urls = showOpenFileDialog(isImporting);
if (urls.isEmpty())
return;
Q_FOREACH (const QString& url, urls) {
if (!url.isEmpty()) {
OpenFlags flags = isImporting ? Import : None;
bool res = openDocument(QUrl::fromLocalFile(url), flags);
if (!res) {
warnKrita << "Loading" << url << "failed";
}
}
}
#else
Q_UNUSED(isImporting)
d->fileManager->openImportFile();
connect(d->fileManager, SIGNAL(sigFileSelected(QString)), this, SLOT(slotFileSelected(QString)));
connect(d->fileManager, SIGNAL(sigEmptyFilePath()), this, SLOT(slotEmptyFilePath()));
#endif
}
void KisMainWindow::slotFileOpenRecent(const QUrl &url)
{
(void) openDocument(QUrl::fromLocalFile(url.toLocalFile()), None);
}
void KisMainWindow::slotFileSave()
{
if (saveDocument(d->activeView->document(), false, false)) {
emit documentSaved();
}
}
void KisMainWindow::slotFileSaveAs()
{
if (saveDocument(d->activeView->document(), true, false)) {
emit documentSaved();
}
}
void KisMainWindow::slotExportFile()
{
if (saveDocument(d->activeView->document(), true, true)) {
emit documentSaved();
}
}
void KisMainWindow::slotShowSessionManager() {
KisPart::instance()->showSessionManager();
}
KoCanvasResourceProvider *KisMainWindow::resourceManager() const
{
return d->viewManager->canvasResourceProvider()->resourceManager();
}
int KisMainWindow::viewCount() const
{
return d->mdiArea->subWindowList().size();
}
const KConfigGroup &KisMainWindow::windowStateConfig() const
{
return d->windowStateConfig;
}
void KisMainWindow::saveWindowState(bool restoreNormalState)
{
if (restoreNormalState) {
QAction *showCanvasOnly = d->viewManager->actionCollection()->action("view_show_canvas_only");
if (showCanvasOnly && showCanvasOnly->isChecked()) {
showCanvasOnly->setChecked(false);
}
d->windowStateConfig.writeEntry("ko_geometry", saveGeometry().toBase64());
d->windowStateConfig.writeEntry("State", saveState().toBase64());
if (!d->dockerStateBeforeHiding.isEmpty()) {
restoreState(d->dockerStateBeforeHiding);
}
statusBar()->setVisible(true);
menuBar()->setVisible(true);
saveWindowSettings();
} else {
saveMainWindowSettings(d->windowStateConfig);
}
}
bool KisMainWindow::restoreWorkspaceState(const QByteArray &state)
{
QByteArray oldState = saveState();
// needed because otherwise the layout isn't correctly restored in some situations
Q_FOREACH (QDockWidget *dock, dockWidgets()) {
dock->toggleViewAction()->setEnabled(true);
dock->hide();
}
bool success = KXmlGuiWindow::restoreState(state);
if (!success) {
KXmlGuiWindow::restoreState(oldState);
return false;
}
return success;
}
bool KisMainWindow::restoreWorkspace(int workspaceId)
{
KisWorkspaceResourceSP workspace =
KisResourceModelProvider::resourceModel(ResourceType::Workspaces)
->resourceForId(workspaceId).dynamicCast();
bool success = restoreWorkspaceState(workspace->dockerState());
if (activeKisView()) {
activeKisView()->resourceProvider()->notifyLoadingWorkspace(workspace);
}
return success;
}
QByteArray KisMainWindow::borrowWorkspace(KisMainWindow *other)
{
QByteArray currentWorkspace = saveState();
if (!d->workspaceBorrowedBy.isNull()) {
if (other->id() == d->workspaceBorrowedBy) {
// We're swapping our original workspace back
d->workspaceBorrowedBy = QUuid();
return currentWorkspace;
} else {
// Get our original workspace back before swapping with a third window
KisMainWindow *borrower = KisPart::instance()->windowById(d->workspaceBorrowedBy);
if (borrower) {
QByteArray originalLayout = borrower->borrowWorkspace(this);
borrower->restoreWorkspaceState(currentWorkspace);
d->workspaceBorrowedBy = other->id();
return originalLayout;
}
}
}
d->workspaceBorrowedBy = other->id();
return currentWorkspace;
}
void KisMainWindow::swapWorkspaces(KisMainWindow *a, KisMainWindow *b)
{
QByteArray workspaceA = a->borrowWorkspace(b);
QByteArray workspaceB = b->borrowWorkspace(a);
a->restoreWorkspaceState(workspaceB);
b->restoreWorkspaceState(workspaceA);
}
KisViewManager *KisMainWindow::viewManager() const
{
return d->viewManager;
}
void KisMainWindow::slotDocumentInfo()
{
if (!d->activeView->document())
return;
KoDocumentInfo *docInfo = d->activeView->document()->documentInfo();
if (!docInfo)
return;
KoDocumentInfoDlg *dlg = d->activeView->document()->createDocumentInfoDialog(this, docInfo);
if (dlg->exec()) {
if (dlg->isDocumentSaved()) {
d->activeView->document()->setModified(false);
} else {
d->activeView->document()->setModified(true);
}
d->activeView->document()->setTitleModified();
}
delete dlg;
}
bool KisMainWindow::slotFileCloseAll()
{
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
if (subwin) {
if(!subwin->close())
return false;
}
}
updateCaption();
return true;
}
void KisMainWindow::slotFileQuit()
{
// Do not close while KisMainWindow has the savingEntryMutex locked, bug409395.
// After the background saving job is initiated, KisDocument blocks closing
// while it saves itself.
if (hackIsSaving()) {
return;
}
KisPart::instance()->closeSession();
}
void KisMainWindow::importAnimation()
{
if (!activeView()) return;
KisDocument *document = activeView()->document();
if (!document) return;
KisDlgImportImageSequence dlg(this, document);
if (dlg.exec() == QDialog::Accepted) {
QStringList files = dlg.files();
int firstFrame = dlg.firstFrame();
int step = dlg.step();
KoUpdaterPtr updater =
!document->fileBatchMode() ? viewManager()->createUnthreadedUpdater(i18n("Import frames")) : 0;
KisAnimationImporter importer(document->image(), updater);
KisImportExportErrorCode status = importer.import(files, firstFrame, step);
if (!status.isOk() && !status.isInternalError()) {
QString msg = status.errorMessage();
if (!msg.isEmpty())
QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not finish import animation:\n%1", msg));
}
activeView()->canvasBase()->refetchDataFromImage();
}
}
void KisMainWindow::slotConfigureToolbars()
{
saveWindowState();
KEditToolBar edit(factory(), this);
connect(&edit, SIGNAL(newToolBarConfig()), this, SLOT(slotNewToolbarConfig()));
(void) edit.exec();
applyToolBarLayout();
}
void KisMainWindow::slotResetConfigurations()
{
KisApplication *kisApp = static_cast(qApp);
kisApp->askresetConfig();
}
void KisMainWindow::slotNewToolbarConfig()
{
applyMainWindowSettings(d->windowStateConfig);
KXMLGUIFactory *factory = guiFactory();
Q_UNUSED(factory);
// Check if there's an active view
if (!d->activeView)
return;
plugActionList("toolbarlist", d->toolbarList);
applyToolBarLayout();
}
void KisMainWindow::slotToolbarToggled(bool toggle)
{
//dbgUI <<"KisMainWindow::slotToolbarToggled" << sender()->name() <<" toggle=" << true;
// The action (sender) and the toolbar have the same name
KToolBar * bar = toolBar(sender()->objectName());
if (bar) {
if (toggle) {
bar->show();
}
else {
bar->hide();
}
if (d->activeView && d->activeView->document()) {
saveWindowState();
}
} else
warnUI << "slotToolbarToggled : Toolbar " << sender()->objectName() << " not found!";
}
void KisMainWindow::viewFullscreen(bool fullScreen)
{
KisConfig cfg(false);
cfg.setFullscreenMode(fullScreen);
if (fullScreen) {
setWindowState(windowState() | Qt::WindowFullScreen); // set
} else {
setWindowState(windowState() & ~Qt::WindowFullScreen); // reset
}
d->fullScreenMode->setChecked(isFullScreen());
}
void KisMainWindow::setMaxRecentItems(uint _number)
{
d->recentFiles->setMaxItems(_number);
}
QDockWidget* KisMainWindow::createDockWidget(KoDockFactoryBase* factory)
{
QDockWidget* dockWidget = 0;
bool lockAllDockers = KisConfig(true).readEntry("LockAllDockerPanels", false);
if (!d->dockWidgetsMap.contains(factory->id())) {
dockWidget = factory->createDockWidget();
// It is quite possible that a dock factory cannot create the dock; don't
// do anything in that case.
if (!dockWidget) {
warnKrita << "Could not create docker for" << factory->id();
return 0;
}
dockWidget->setFont(KoDockRegistry::dockFont());
dockWidget->setObjectName(factory->id());
dockWidget->setParent(this);
if (lockAllDockers) {
if (dockWidget->titleBarWidget()) {
dockWidget->titleBarWidget()->setVisible(false);
}
dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
}
if (dockWidget->widget() && dockWidget->widget()->layout())
dockWidget->widget()->layout()->setContentsMargins(1, 1, 1, 1);
Qt::DockWidgetArea side = Qt::RightDockWidgetArea;
bool visible = true;
switch (factory->defaultDockPosition()) {
case KoDockFactoryBase::DockTornOff:
dockWidget->setFloating(true); // position nicely?
break;
case KoDockFactoryBase::DockTop:
side = Qt::TopDockWidgetArea; break;
case KoDockFactoryBase::DockLeft:
side = Qt::LeftDockWidgetArea; break;
case KoDockFactoryBase::DockBottom:
side = Qt::BottomDockWidgetArea; break;
case KoDockFactoryBase::DockRight:
side = Qt::RightDockWidgetArea; break;
case KoDockFactoryBase::DockMinimized:
default:
side = Qt::RightDockWidgetArea;
visible = false;
}
KConfigGroup group = d->windowStateConfig.group("DockWidget " + factory->id());
side = static_cast(group.readEntry("DockArea", static_cast(side)));
if (side == Qt::NoDockWidgetArea) side = Qt::RightDockWidgetArea;
addDockWidget(side, dockWidget);
if (!visible) {
dockWidget->hide();
}
d->dockWidgetsMap.insert(factory->id(), dockWidget);
}
else {
dockWidget = d->dockWidgetsMap[factory->id()];
}
#ifdef Q_OS_MACOS
dockWidget->setAttribute(Qt::WA_MacSmallSize, true);
#endif
dockWidget->setFont(KoDockRegistry::dockFont());
connect(dockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(forceDockTabFonts()));
return dockWidget;
}
void KisMainWindow::forceDockTabFonts()
{
Q_FOREACH (QObject *child, children()) {
if (child->inherits("QTabBar")) {
((QTabBar *)child)->setFont(KoDockRegistry::dockFont());
}
}
}
+void KisMainWindow::slotUpdateWidgetStyle()
+{
+ KisConfig cfg(true);
+ QString themeFromConfig = cfg.widgetStyle();
+
+ Q_FOREACH (auto key, d->actionMap.keys()) { // find checked style to save to config
+ if(d->actionMap.value(key)->isChecked()) {
+ cfg.setWidgetStyle(key);
+ qApp->setStyle(key);
+ }
+ }
+}
+
QList KisMainWindow::dockWidgets() const
{
return d->dockWidgetsMap.values();
}
QDockWidget* KisMainWindow::dockWidget(const QString &id)
{
if (!d->dockWidgetsMap.contains(id)) return 0;
return d->dockWidgetsMap[id];
}
QList KisMainWindow::canvasObservers() const
{
QList observers;
Q_FOREACH (QDockWidget *docker, dockWidgets()) {
KoCanvasObserverBase *observer = dynamic_cast(docker);
if (observer) {
observers << observer;
}
else {
warnKrita << docker << "is not a canvas observer";
}
}
return observers;
}
void KisMainWindow::toggleDockersVisibility(bool visible)
{
if (!visible) {
d->dockerStateBeforeHiding = saveState();
Q_FOREACH (QObject* widget, children()) {
if (widget->inherits("QDockWidget")) {
QDockWidget* dw = static_cast(widget);
if (dw->isVisible()) {
dw->hide();
}
}
}
}
else {
restoreState(d->dockerStateBeforeHiding);
}
}
void KisMainWindow::slotDocumentTitleModified()
{
updateCaption();
}
void KisMainWindow::subWindowActivated()
{
bool enabled = (activeKisView() != 0);
d->mdiCascade->setEnabled(enabled);
d->mdiNextWindow->setEnabled(enabled);
d->mdiPreviousWindow->setEnabled(enabled);
d->mdiTile->setEnabled(enabled);
d->close->setEnabled(enabled);
d->closeAll->setEnabled(enabled);
setActiveSubWindow(d->mdiArea->activeSubWindow());
Q_FOREACH (QToolBar *tb, toolBars()) {
if (tb->objectName() == "BrushesAndStuff") {
tb->setEnabled(enabled);
}
}
/**
* Qt has a weirdness, it has hardcoded shortcuts added to an action
* in the window menu. We need to reset the shortcuts for that menu
* to nothing, otherwise the shortcuts cannot be made configurable.
*
* See: https://bugs.kde.org/show_bug.cgi?id=352205
* https://bugs.kde.org/show_bug.cgi?id=375524
* https://bugs.kde.org/show_bug.cgi?id=398729
*/
QMdiSubWindow *subWindow = d->mdiArea->currentSubWindow();
if (subWindow) {
QMenu *menu = subWindow->systemMenu();
if (menu && menu->actions().size() == 8) {
Q_FOREACH (QAction *action, menu->actions()) {
action->setShortcut(QKeySequence());
}
menu->actions().last()->deleteLater();
}
}
updateCaption();
d->actionManager()->updateGUI();
}
void KisMainWindow::windowFocused()
{
/**
* Notify selection manager so that it could update selection mask overlay
*/
if (viewManager() && viewManager()->selectionManager()) {
viewManager()->selectionManager()->selectionChanged();
}
KisPart *kisPart = KisPart::instance();
KisWindowLayoutManager *layoutManager = KisWindowLayoutManager::instance();
if (!layoutManager->primaryWorkspaceFollowsFocus()) return;
QUuid primary = layoutManager->primaryWindowId();
if (primary.isNull()) return;
if (d->id == primary) {
if (!d->workspaceBorrowedBy.isNull()) {
KisMainWindow *borrower = kisPart->windowById(d->workspaceBorrowedBy);
if (!borrower) return;
swapWorkspaces(this, borrower);
}
} else {
if (d->workspaceBorrowedBy == primary) return;
KisMainWindow *primaryWindow = kisPart->windowById(primary);
if (!primaryWindow) return;
swapWorkspaces(this, primaryWindow);
}
}
void KisMainWindow::updateWindowMenu()
{
QMenu *menu = d->windowMenu->menu();
menu->clear();
menu->addAction(d->newWindow);
menu->addAction(d->documentMenu);
QMenu *docMenu = d->documentMenu->menu();
docMenu->clear();
QFontMetrics fontMetrics = docMenu->fontMetrics();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QRect geom = this->geometry();
QPoint p(geom.width() / 2 + geom.left(), geom.height() / 2 + geom.top());
QScreen *screen = qApp->screenAt(p);
int fileStringWidth = 300;
if (screen) {
fileStringWidth = int(screen->availableGeometry().width() * .40f);
}
#else
int fileStringWidth = int(QApplication::desktop()->screenGeometry(this).width() * .40f);
#endif
Q_FOREACH (QPointer doc, KisPart::instance()->documents()) {
if (doc) {
QString title = fontMetrics.elidedText(doc->url().toDisplayString(QUrl::PreferLocalFile), Qt::ElideMiddle, fileStringWidth);
if (title.isEmpty() && doc->image()) {
title = doc->image()->objectName();
}
QAction *action = docMenu->addAction(title);
action->setIcon(qApp->windowIcon());
connect(action, SIGNAL(triggered()), d->documentMapper, SLOT(map()));
d->documentMapper->setMapping(action, doc);
}
}
menu->addAction(d->workspaceMenu);
QMenu *workspaceMenu = d->workspaceMenu->menu();
workspaceMenu->clear();
KisResourceIterator resourceIterator(KisResourceModelProvider::resourceModel(ResourceType::Workspaces));
KisMainWindow *m_this = this;
while (resourceIterator.hasNext()) {
KisResourceItemSP resource = resourceIterator.next();
QAction *action = workspaceMenu->addAction(resource->name());
connect(action, &QAction::triggered, this, [=]() {
m_this->restoreWorkspace(resource->id());
});
}
workspaceMenu->addSeparator();
connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&Import Workspace...")),
&QAction::triggered,
this,
[&]()
{
QStringList mimeTypes = KisResourceLoaderRegistry::instance()->mimeTypes(ResourceType::Workspaces);
KoFileDialog dialog(0, KoFileDialog::OpenFile, "OpenDocument");
dialog.setMimeTypeFilters(mimeTypes);
dialog.setCaption(i18nc("@title:window", "Choose File to Add"));
QString filename = dialog.filename();
d->workspacemodel->importResourceFile(filename);
});
connect(workspaceMenu->addAction(i18nc("@action:inmenu", "&New Workspace...")),
&QAction::triggered,
[=]() {
QString name;
auto rserver = KisResourceServerProvider::instance()->workspaceServer();
KisWorkspaceResourceSP workspace(new KisWorkspaceResource(""));
workspace->setDockerState(m_this->saveState());
d->viewManager->canvasResourceProvider()->notifySavingWorkspace(workspace);
workspace->setValid(true);
QString saveLocation = rserver->saveLocation();
QFileInfo fileInfo(saveLocation + name + workspace->defaultFileExtension());
bool fileOverWriteAccepted = false;
while(!fileOverWriteAccepted) {
name = QInputDialog::getText(this, i18nc("@title:window", "New Workspace..."),
i18nc("@label:textbox", "Name:"));
if (name.isNull() || name.isEmpty()) {
return;
} else {
fileInfo = QFileInfo(saveLocation + name.split(" ").join("_") + workspace->defaultFileExtension());
if (fileInfo.exists()) {
int res = QMessageBox::warning(this, i18nc("@title:window", "Name Already Exists")
, i18n("The name '%1' already exists, do you wish to overwrite it?", name)
, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
if (res == QMessageBox::Yes) fileOverWriteAccepted = true;
} else {
fileOverWriteAccepted = true;
}
}
}
workspace->setFilename(fileInfo.fileName());
workspace->setName(name);
rserver->addResource(workspace);
});
// TODO: What to do about delete?
// workspaceMenu->addAction(i18nc("@action:inmenu", "&Delete Workspace..."));
menu->addSeparator();
menu->addAction(d->close);
menu->addAction(d->closeAll);
if (d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
menu->addSeparator();
menu->addAction(d->mdiTile);
menu->addAction(d->mdiCascade);
}
menu->addSeparator();
menu->addAction(d->mdiNextWindow);
menu->addAction(d->mdiPreviousWindow);
menu->addSeparator();
QList windows = d->mdiArea->subWindowList();
for (int i = 0; i < windows.size(); ++i) {
QPointerchild = qobject_cast(windows.at(i)->widget());
if (child && child->document()) {
QString text;
if (i < 9) {
text = i18n("&%1 %2", i + 1, fontMetrics.elidedText(child->document()->url().toDisplayString(QUrl::PreferLocalFile), Qt::ElideMiddle, fileStringWidth));
}
else {
text = i18n("%1 %2", i + 1, fontMetrics.elidedText(child->document()->url().toDisplayString(QUrl::PreferLocalFile), Qt::ElideMiddle, fileStringWidth));
}
QAction *action = menu->addAction(text);
action->setIcon(qApp->windowIcon());
action->setCheckable(true);
action->setChecked(child == activeKisView());
connect(action, SIGNAL(triggered()), d->windowMapper, SLOT(map()));
d->windowMapper->setMapping(action, windows.at(i));
}
}
bool showMdiArea = windows.count( ) > 0;
if (!showMdiArea) {
showWelcomeScreen(true); // see workaround in function in header
// keep the recent file list updated when going back to welcome screen
reloadRecentFileList();
d->welcomePage->populateRecentDocuments();
}
// enable/disable the toolbox docker if there are no documents open
Q_FOREACH (QObject* widget, children()) {
if (widget->inherits("QDockWidget")) {
QDockWidget* dw = static_cast(widget);
if ( dw->objectName() == "ToolBox") {
dw->setEnabled(showMdiArea);
}
}
}
updateCaption();
}
void KisMainWindow::updateSubwindowFlags()
{
bool onlyOne = false;
if (d->mdiArea->subWindowList().size() == 1 && d->mdiArea->viewMode() == QMdiArea::SubWindowView) {
onlyOne = true;
}
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
if (onlyOne) {
subwin->setWindowFlags(subwin->windowFlags() | Qt::FramelessWindowHint);
subwin->showMaximized();
} else {
subwin->setWindowFlags((subwin->windowFlags() | Qt::FramelessWindowHint) ^ Qt::FramelessWindowHint);
}
}
}
void KisMainWindow::setActiveSubWindow(QWidget *window)
{
if (!window) return;
QMdiSubWindow *subwin = qobject_cast(window);
//dbgKrita << "setActiveSubWindow();" << subwin << d->activeSubWindow;
if (subwin && subwin != d->activeSubWindow) {
KisView *view = qobject_cast(subwin->widget());
//dbgKrita << "\t" << view << activeView();
if (view && view != activeView()) {
d->mdiArea->setActiveSubWindow(subwin);
setActiveView(view);
}
d->activeSubWindow = subwin;
}
updateWindowMenu();
d->actionManager()->updateGUI();
}
void KisMainWindow::configChanged()
{
KisConfig cfg(true);
QMdiArea::ViewMode viewMode = (QMdiArea::ViewMode)cfg.readEntry("mdi_viewmode", (int)QMdiArea::TabbedView);
d->mdiArea->setViewMode(viewMode);
Q_FOREACH (QMdiSubWindow *subwin, d->mdiArea->subWindowList()) {
subwin->setOption(QMdiSubWindow::RubberBandMove, cfg.readEntry("mdi_rubberband", cfg.useOpenGL()));
subwin->setOption(QMdiSubWindow::RubberBandResize, cfg.readEntry("mdi_rubberband", cfg.useOpenGL()));
/**
* Dirty workaround for a bug in Qt (checked on Qt 5.6.1):
*
* If you make a window "Show on top" and then switch to the tabbed mode
* the window will continue to be painted in its initial "mid-screen"
* position. It will persist here until you explicitly switch to its tab.
*/
if (viewMode == QMdiArea::TabbedView) {
Qt::WindowFlags oldFlags = subwin->windowFlags();
Qt::WindowFlags flags = oldFlags;
flags &= ~Qt::WindowStaysOnTopHint;
flags &= ~Qt::WindowStaysOnBottomHint;
if (flags != oldFlags) {
subwin->setWindowFlags(flags);
subwin->showMaximized();
}
}
}
#ifdef Q_OS_MACOS
updateSubwindowFlags();
#endif
KConfigGroup group( KSharedConfig::openConfig(), "theme");
d->themeManager->setCurrentTheme(group.readEntry("Theme", "Krita dark"));
d->actionManager()->updateGUI();
QString s = cfg.getMDIBackgroundColor();
KoColor c = KoColor::fromXML(s);
QBrush brush(c.toQColor());
d->mdiArea->setBackground(brush);
QString backgroundImage = cfg.getMDIBackgroundImage();
if (backgroundImage != "") {
QImage image(backgroundImage);
QBrush brush(image);
d->mdiArea->setBackground(brush);
}
d->mdiArea->update();
}
KisView* KisMainWindow::newView(QObject *document, QMdiSubWindow *subWindow)
{
KisDocument *doc = qobject_cast(document);
KisView *view = addViewAndNotifyLoadingCompleted(doc, subWindow);
d->actionManager()->updateGUI();
return view;
}
void KisMainWindow::newWindow()
{
KisMainWindow *mainWindow = KisPart::instance()->createMainWindow();
mainWindow->initializeGeometry();
mainWindow->show();
}
void KisMainWindow::closeCurrentWindow()
{
if (d->mdiArea->currentSubWindow()) {
d->mdiArea->currentSubWindow()->close();
d->actionManager()->updateGUI();
}
}
void KisMainWindow::checkSanity()
{
// print error if the lcms engine is not available
if (!KoColorSpaceEngineRegistry::instance()->contains("icc")) {
// need to wait 1 event since exiting here would not work.
m_errorMessage = i18n("The Krita LittleCMS color management plugin is not installed. Krita will quit now.");
m_dieOnError = true;
QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
return;
}
KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer();
if (rserver->resourceCount() == 0) {
m_errorMessage = i18n("Krita cannot find any brush presets! Krita will quit now.");
m_dieOnError = true;
QTimer::singleShot(0, this, SLOT(showErrorAndDie()));
return;
}
}
void KisMainWindow::showErrorAndDie()
{
QMessageBox::critical(0, i18nc("@title:window", "Installation error"), m_errorMessage);
if (m_dieOnError) {
exit(10);
}
}
void KisMainWindow::showAboutApplication()
{
KisAboutApplication dlg(this);
dlg.exec();
}
QPointer KisMainWindow::activeKisView()
{
if (!d->mdiArea) return 0;
QMdiSubWindow *activeSubWindow = d->mdiArea->activeSubWindow();
//dbgKrita << "activeKisView" << activeSubWindow;
if (!activeSubWindow) return 0;
return qobject_cast(activeSubWindow->widget());
}
void KisMainWindow::newOptionWidgets(KoCanvasController *controller, const QList > &optionWidgetList)
{
KIS_ASSERT_RECOVER_NOOP(controller == KoToolManager::instance()->activeCanvasController());
bool isOurOwnView = false;
Q_FOREACH (QPointer view, KisPart::instance()->views()) {
if (view && view->canvasController() == controller) {
isOurOwnView = view->mainWindow() == this;
}
}
if (!isOurOwnView) return;
Q_FOREACH (QWidget *w, optionWidgetList) {
#ifdef Q_OS_MACOS
w->setAttribute(Qt::WA_MacSmallSize, true);
#endif
w->setFont(KoDockRegistry::dockFont());
}
if (d->toolOptionsDocker) {
d->toolOptionsDocker->setOptionWidgets(optionWidgetList);
}
else {
d->viewManager->paintOpBox()->newOptionWidgets(optionWidgetList);
}
}
void KisMainWindow::createActions()
{
KisActionManager *actionManager = d->actionManager();
actionManager->createStandardAction(KStandardAction::New, this, SLOT(slotFileNew()));
actionManager->createStandardAction(KStandardAction::Open, this, SLOT(slotFileOpen()));
actionManager->createStandardAction(KStandardAction::Quit, this, SLOT(slotFileQuit()));
actionManager->createStandardAction(KStandardAction::ConfigureToolbars, this, SLOT(slotConfigureToolbars()));
d->fullScreenMode = actionManager->createStandardAction(KStandardAction::FullScreen, this, SLOT(viewFullscreen(bool)));
d->recentFiles = KStandardAction::openRecent(this, SLOT(slotFileOpenRecent(QUrl)), actionCollection());
connect(d->recentFiles, SIGNAL(recentListCleared()), this, SLOT(saveRecentFiles()));
KSharedConfigPtr configPtr = KSharedConfig::openConfig();
d->recentFiles->loadEntries(configPtr->group("RecentFiles"));
d->saveAction = actionManager->createStandardAction(KStandardAction::Save, this, SLOT(slotFileSave()));
d->saveAction->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->saveActionAs = actionManager->createStandardAction(KStandardAction::SaveAs, this, SLOT(slotFileSaveAs()));
d->saveActionAs->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->undo = actionManager->createStandardAction(KStandardAction::Undo, this, SLOT(undo()));
d->undo->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->redo = actionManager->createStandardAction(KStandardAction::Redo, this, SLOT(redo()));
d->redo->setActivationFlags(KisAction::ACTIVE_IMAGE);
d->undoActionsUpdateManager.reset(new KisUndoActionsUpdateManager(d->undo, d->redo));
d->undoActionsUpdateManager->setCurrentDocument(d->activeView ? d->activeView->document() : 0);
d->importAnimation = actionManager->createAction("file_import_animation");
connect(d->importAnimation, SIGNAL(triggered()), this, SLOT(importAnimation()));
d->closeAll = actionManager->createAction("file_close_all");
connect(d->closeAll, SIGNAL(triggered()), this, SLOT(slotFileCloseAll()));
d->importFile = actionManager->createAction("file_import_file");
connect(d->importFile, SIGNAL(triggered(bool)), this, SLOT(slotImportFile()));
d->exportFile = actionManager->createAction("file_export_file");
connect(d->exportFile, SIGNAL(triggered(bool)), this, SLOT(slotExportFile()));
/* The following entry opens the document information dialog. Since the action is named so it
intends to show data this entry should not have a trailing ellipses (...). */
d->showDocumentInfo = actionManager->createAction("file_documentinfo");
connect(d->showDocumentInfo, SIGNAL(triggered(bool)), this, SLOT(slotDocumentInfo()));
d->themeManager->setThemeMenuAction(new KActionMenu(i18nc("@action:inmenu", "&Themes"), this));
d->themeManager->registerThemeActions(actionCollection());
connect(d->themeManager, SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()));
connect(d->themeManager, SIGNAL(signalThemeChanged()), d->welcomePage, SLOT(slotUpdateThemeColors()));
d->toggleDockers = actionManager->createAction("view_toggledockers");
KisConfig(true).showDockers(true);
d->toggleDockers->setChecked(true);
connect(d->toggleDockers, SIGNAL(toggled(bool)), SLOT(toggleDockersVisibility(bool)));
d->resetConfigurations = actionManager->createAction("reset_configurations");
connect(d->resetConfigurations, SIGNAL(triggered()), this, SLOT(slotResetConfigurations()));
d->toggleDetachCanvas = actionManager->createAction("view_detached_canvas");
d->toggleDetachCanvas->setChecked(false);
connect(d->toggleDetachCanvas, SIGNAL(toggled(bool)), SLOT(setCanvasDetached(bool)));
setCanvasDetached(false);
actionCollection()->addAction("settings_dockers_menu", d->dockWidgetMenu);
actionCollection()->addAction("window", d->windowMenu);
+ actionCollection()->addAction("style_menu", d->styleMenu); // for widget styles: breeze, fusion, etc
+
d->mdiCascade = actionManager->createAction("windows_cascade");
connect(d->mdiCascade, SIGNAL(triggered()), d->mdiArea, SLOT(cascadeSubWindows()));
d->mdiTile = actionManager->createAction("windows_tile");
connect(d->mdiTile, SIGNAL(triggered()), d->mdiArea, SLOT(tileSubWindows()));
d->mdiNextWindow = actionManager->createAction("windows_next");
connect(d->mdiNextWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activateNextSubWindow()));
d->mdiPreviousWindow = actionManager->createAction("windows_previous");
connect(d->mdiPreviousWindow, SIGNAL(triggered()), d->mdiArea, SLOT(activatePreviousSubWindow()));
d->newWindow = actionManager->createAction("view_newwindow");
connect(d->newWindow, SIGNAL(triggered(bool)), this, SLOT(newWindow()));
d->close = actionManager->createStandardAction(KStandardAction::Close, this, SLOT(closeCurrentWindow()));
d->showSessionManager = actionManager->createAction("file_sessions");
connect(d->showSessionManager, SIGNAL(triggered(bool)), this, SLOT(slotShowSessionManager()));
actionManager->createStandardAction(KStandardAction::Preferences, this, SLOT(slotPreferences()));
for (int i = 0; i < 2; i++) {
d->expandingSpacers[i] = new KisAction(i18n("Expanding Spacer"));
d->expandingSpacers[i]->setDefaultWidget(new QWidget(this));
d->expandingSpacers[i]->defaultWidget()->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
actionManager->addAction(QString("expanding_spacer_%1").arg(i), d->expandingSpacers[i]);
}
}
void KisMainWindow::applyToolBarLayout()
{
const bool isPlastiqueStyle = style()->objectName() == "plastique";
Q_FOREACH (KToolBar *toolBar, toolBars()) {
toolBar->layout()->setSpacing(4);
if (isPlastiqueStyle) {
toolBar->setContentsMargins(0, 0, 0, 2);
}
//Hide text for buttons with an icon in the toolbar
Q_FOREACH (QAction *ac, toolBar->actions()){
if (ac->icon().pixmap(QSize(1,1)).isNull() == false){
ac->setPriority(QAction::LowPriority);
}else {
ac->setIcon(QIcon());
}
}
}
}
void KisMainWindow::initializeGeometry()
{
// if the user didn's specify the geometry on the command line (does anyone do that still?),
// we first figure out some good default size and restore the x,y position. See bug 285804Z.
KConfigGroup cfg = d->windowStateConfig;
QByteArray geom = QByteArray::fromBase64(cfg.readEntry("ko_geometry", QByteArray()));
if (!restoreGeometry(geom)) {
const int scnum = QApplication::desktop()->screenNumber(parentWidget());
QRect desk = QGuiApplication::screens().at(scnum)->availableVirtualGeometry();
quint32 x = desk.x();
quint32 y = desk.y();
quint32 w = 0;
quint32 h = 0;
// Default size -- maximize on small screens, something useful on big screens
const int deskWidth = desk.width();
if (deskWidth > 1024) {
// a nice width, and slightly less than total available
// height to compensate for the window decs
w = (deskWidth / 3) * 2;
h = (desk.height() / 3) * 2;
}
else {
w = desk.width();
h = desk.height();
}
x += (desk.width() - w) / 2;
y += (desk.height() - h) / 2;
move(x,y);
setGeometry(geometry().x(), geometry().y(), w, h);
}
d->fullScreenMode->setChecked(isFullScreen());
}
void KisMainWindow::showManual()
{
QDesktopServices::openUrl(QUrl("https://docs.krita.org"));
}
void KisMainWindow::windowScreenChanged(QScreen *screen)
{
emit screenChanged();
d->screenConnectionsStore.clear();
d->screenConnectionsStore.addConnection(screen, SIGNAL(physicalDotsPerInchChanged(qreal)),
this, SIGNAL(screenChanged()));
}
void KisMainWindow::slotXmlGuiMakingChanges(bool finished)
{
if (finished) {
subWindowActivated();
}
}
#include
diff --git a/libs/ui/KisMainWindow.h b/libs/ui/KisMainWindow.h
index 79496225fa..e67fdf77be 100644
--- a/libs/ui/KisMainWindow.h
+++ b/libs/ui/KisMainWindow.h
@@ -1,515 +1,517 @@
/* This file is part of the KDE project
Copyright (C) 1998, 1999 Torben Weis
Copyright (C) 2000-2004 David Faure
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KIS_MAIN_WINDOW_H
#define KIS_MAIN_WINDOW_H
#include "kritaui_export.h"
#include
#include