diff --git a/krita/krita4.xmlgui b/krita/krita4.xmlgui index b933f80cf4..1e8ec06428 100644 --- a/krita/krita4.xmlgui +++ b/krita/krita4.xmlgui @@ -1,386 +1,386 @@ &File &Edit Fill Special &View &Canvas &Snap To &Image &Rotate &Layer New &Import/Export Import &Convert &Select &Group &Transform &Rotate S&plit S&plit Alpha &Select Filte&r &Tools - + Scripting Recording Macros Setti&ngs &Help File Brushes and Stuff diff --git a/libs/libkis/Action.cpp b/libs/libkis/Action.cpp index 75005dce79..faafc23113 100644 --- a/libs/libkis/Action.cpp +++ b/libs/libkis/Action.cpp @@ -1,169 +1,158 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Action.h" struct Action::Private { Private() {} QAction *action {0}; }; Action::Action(QObject *parent) : QObject(parent) , d(new Private) { d->action = new KisAction(this); connect(d->action, SIGNAL(triggered(bool)), SIGNAL(triggered(bool))); } Action::Action(const QString &name, QAction *action, QObject *parent) : QObject(parent) , d(new Private) { d->action = action; d->action->setObjectName(name); connect(d->action, SIGNAL(triggered(bool)), SIGNAL(triggered(bool))); } Action::~Action() { delete d; } bool Action::operator==(const Action &other) const { return (d->action == other.d->action); } bool Action::operator!=(const Action &other) const { return !(operator==(other)); } QString Action::text() const { if (!d->action) return ""; return d->action->text(); } void Action::setText(QString text) { if (!d->action) return; d->action->setText(text); } QString Action::name() const { if (!d->action) return ""; return d->action->objectName(); } void Action::setName(QString name) { if (!d->action) return; d->action->setObjectName(name); } bool Action::isCheckable() const { if (!d->action) return false; return d->action->isCheckable(); } void Action::setCheckable(bool value) { if (!d->action) return; d->action->setCheckable(value); } bool Action::isChecked() const { if (!d->action) return false; return d->action->isChecked(); } void Action::setChecked(bool value) { if (!d->action) return; d->action->setChecked(value); } QString Action::shortcut() const { if (!d->action) return QString(); return d->action->shortcut().toString(); } void Action::setShortcut(QString value) { if (!d->action) return; d->action->setShortcut(QKeySequence::fromString(value)); } bool Action::isVisible() const { if (!d->action) return false; return d->action->isVisible(); } void Action::setVisible(bool value) { if (!d->action) return; d->action->setVisible(value); } bool Action::isEnabled() const { if (!d->action) return false; return d->action->isEnabled(); } void Action::setEnabled(bool value) { if (!d->action) return; d->action->setEnabled(value); } void Action::setToolTip(QString tooltip) { if (!d->action) return; d->action->setToolTip(tooltip); } QString Action::tooltip() const { return d->action->toolTip(); } void Action::trigger() { d->action->trigger(); } - - -void Action::setMenu(const QString menu) -{ - d->action->setProperty("menu", menu); -} - -QString Action::menu() const -{ - return d->action->property("menu").toString(); -} diff --git a/libs/libkis/Action.h b/libs/libkis/Action.h index 3d8b470013..9546fc644d 100644 --- a/libs/libkis/Action.h +++ b/libs/libkis/Action.h @@ -1,165 +1,153 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_ACTION_H #define LIBKIS_ACTION_H #include #include "kritalibkis_export.h" #include "libkis.h" /** * Action encapsulates a KisAction. By default, actions are put in the Tools/Scripts menu. */ class KRITALIBKIS_EXPORT Action : public QObject { Q_OBJECT public: /** * @brief Action Create a new action object * @param parent the parent if it's in a QObject hierarchy */ explicit Action(QObject *parent = 0); /** * @brief Action Create a new action object * @param name the name of the action * @param action the QAction it wraps * @param parent the parent if it's in a QObject hierarchy */ Action(const QString &name, QAction *action, QObject *parent = 0); ~Action() override; bool operator==(const Action &other) const; bool operator!=(const Action &other) const; public Q_SLOTS: /** * @return the user-visible text of the action. */ QString text() const; /** * set the user-visible text of the action to @param text. */ void setText(QString text); /** * @return the internal name of the action. */ QString name() const; /** * set the name of the action to @param name. This is not the user-visible name, but the internal one */ void setName(QString name); /** * @return true if the action is checkable, false if it isn't* */ bool isCheckable() const; /** * Set the action action checkable if @param value is true, unchecked if it's false */ void setCheckable(bool value); /** * @return true if the action is checked, false if it isn't */ bool isChecked() const; /** * Set the action checked if @param value is true, unchecked if it's false */ void setChecked(bool value); /** * Return the action's shortcut as a string */ QString shortcut() const; /** * set the action's shortcut to the given string. * @code * action.setShortcut("CTRL+SHIFT+S") * @endcode */ void setShortcut(QString value); bool isVisible() const; /** * @brief setVisible determines whether the action will be visible in the scripting menu. * @param value true if the action is to be shown in the menu, false otherwise */ void setVisible(bool value); /** * @return true if the action is enabled, false if not */ bool isEnabled() const; /** * Set the action enabled or disabled according to @param value */ void setEnabled(bool value); /** * Set the tooltip to the given @param tooltip */ void setToolTip(QString tooltip); /** * @return the tooltip text */ QString tooltip() const; /** * Trigger this action */ void trigger(); - /** - * @brief setMenu determines in which menu the action will be placed. The default is tools/scripts - * @param menu the menu where the action should go, / -separated to drill down the hierarchy - */ - void setMenu(const QString menu); - - /** - * @return the menu in which this action is to be placed. - */ - QString menu() const; - - Q_SIGNALS: /** * Emitted whenever the action is triggered. */ void triggered(bool); private: struct Private; Private *const d; }; #endif // LIBKIS_ACTION_H diff --git a/libs/libkis/Extension.h b/libs/libkis/Extension.h index fc9d76c47e..eb9502a953 100644 --- a/libs/libkis/Extension.h +++ b/libs/libkis/Extension.h @@ -1,78 +1,86 @@ /* * Copyright (c) 2015 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_EXTENSION_H #define LIBKIS_EXTENSION_H #include "kritalibkis_export.h" #include +#include /** - * An Extension is the base for classes that extend Krita. An Extension + * An Extension is the base for classes that extend Krita. An Extension * is loaded on startup, when the setup() method will be executed. - * - * The extension instance should be added to the Krita Application object + * + * The extension instance should be added to the Krita Application object * using Krita.instance().addViewExtension or Application.addViewExtension * or Scripter.addViewExtension. - * + * * Example: - * + * * @code * import sys * from PyQt5.QtGui import * * from PyQt5.QtWidgets import * * from krita import * * class HelloExtension(Extension): * * def __init__(self, parent): * super().__init__(parent) * * def hello(self): * QMessageBox.information(QWidget(), "Test", "Hello! This is Krita " + Application.version()) - * + * * def setup(self): * qDebug("Hello Setup") - * action = Krita.instance().createAction("hello") + * + * def createActions(self, window) + * action = window.createAction("hello") * action.triggered.connect(self.hello) * * Scripter.addExtension(HelloExtension(Krita.instance())) - * + * * @endcode */ class KRITALIBKIS_EXPORT Extension : public QObject { Q_OBJECT public: - + /** - * Create a new extension. The extension will be + * Create a new extension. The extension will be * owned by @param parent. */ explicit Extension(QObject *parent = 0); ~Extension() override; - + /** - * Override this function to setup your Extension. You can use it to add - * Actions to the action collection or integrate in any other way with - * the application. + * Override this function to setup your Extension. You can use it to integrate + * with the Krita application instance. */ virtual void setup() = 0; + + virtual void createActions(Window *window) = 0; + }; + + + #endif diff --git a/libs/libkis/Krita.cpp b/libs/libkis/Krita.cpp index b9421a573e..a90f5ec0ea 100644 --- a/libs/libkis/Krita.cpp +++ b/libs/libkis/Krita.cpp @@ -1,433 +1,423 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Krita.h" #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 "View.h" #include "Document.h" #include "Window.h" #include "Extension.h" #include "DockWidgetFactoryBase.h" #include "Filter.h" #include "InfoObject.h" #include "Resource.h" Krita* Krita::s_instance = 0; struct Krita::Private { Private() {} QList extensions; bool batchMode {false}; Notifier *notifier{new Notifier()}; }; Krita::Krita(QObject *parent) : QObject(parent) , d(new Private) { qRegisterMetaType(); + connect(KisPart::instance(), SIGNAL(sigWindowAdded(KisMainWindow*)), SLOT(mainWindowAdded(KisMainWindow*))); } Krita::~Krita() { qDeleteAll(d->extensions); delete d->notifier; delete d; } QList Krita::actions() const { QList actionList; KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return actionList; } KActionCollection *actionCollection = mainWindow->actionCollection(); Q_FOREACH(QAction *action, actionCollection->actions()) { actionList << new Action(action->objectName(), action); } return actionList; } Action *Krita::action(const QString &name) const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } KActionCollection *actionCollection = mainWindow->actionCollection(); QAction *action = actionCollection->action(name); if (action) { return new Action(name, action); } return 0; } Document* Krita::activeDocument() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } KisView *view = mainWindow->activeView(); if (!view) { return 0; } KisDocument *document = view->document(); return new Document(document); } void Krita::setActiveDocument(Document* value) { Q_FOREACH(KisView *view, KisPart::instance()->views()) { if (view->document() == value->document().data()) { view->activateWindow(); break; } } } bool Krita::batchmode() const { return d->batchMode; } void Krita::setBatchmode(bool value) { d->batchMode = value; } QList Krita::documents() const { QList ret; foreach(QPointer doc, KisPart::instance()->documents()) { ret << new Document(doc); } return ret; } QStringList Krita::filters() const { QStringList ls = KisFilterRegistry::instance()->keys(); std::sort(ls.begin(), ls.end()); return ls; } Filter *Krita::filter(const QString &name) const { if (!filters().contains(name)) return 0; Filter *filter = new Filter(); filter->setName(name); KisFilterSP f = KisFilterRegistry::instance()->value(name); KisFilterConfigurationSP fc = f->defaultConfiguration(); InfoObject *info = new InfoObject(fc); filter->setConfiguration(info); return filter; } QStringList Krita::colorModels() const { QSet colorModelsIds; QList ids = KoColorSpaceRegistry::instance()->colorModelsList(KoColorSpaceRegistry::AllColorSpaces); Q_FOREACH(KoID id, ids) { colorModelsIds << id.id(); } return colorModelsIds.toList();; } QStringList Krita::colorDepths(const QString &colorModel) const { QSet colorDepthsIds; QList ids = KoColorSpaceRegistry::instance()->colorDepthList(colorModel, KoColorSpaceRegistry::AllColorSpaces); Q_FOREACH(KoID id, ids) { colorDepthsIds << id.id(); } return colorDepthsIds.toList();; } QStringList Krita::filterStrategies() const { return KisFilterStrategyRegistry::instance()->keys(); } QStringList Krita::profiles(const QString &colorModel, const QString &colorDepth) const { QSet profileNames; QString id = KoColorSpaceRegistry::instance()->colorSpaceId(colorModel, colorDepth); QList profiles = KoColorSpaceRegistry::instance()->profilesFor(id); Q_FOREACH(const KoColorProfile *profile, profiles) { profileNames << profile->name(); } return profileNames.toList(); } bool Krita::addProfile(const QString &profilePath) { KoColorSpaceEngine *iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); return iccEngine->addProfile(profilePath); } Notifier* Krita::notifier() const { return d->notifier; } QString Krita::version() const { return KritaVersionWrapper::versionString(true); } QList Krita::views() const { QList ret; foreach(QPointer view, KisPart::instance()->views()) { ret << new View(view); } return ret; } Window *Krita::activeWindow() const { KisMainWindow *mainWindow = KisPart::instance()->currentMainwindow(); if (!mainWindow) { return 0; } return new Window(mainWindow); } QList Krita::windows() const { QList ret; foreach(QPointer mainWin, KisPart::instance()->mainWindows()) { ret << new Window(mainWin); } return ret; } QMap Krita::resources(const QString &type) const { QMap resources = QMap (); if (type.toLower() == "pattern") { KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type.toLower() == "gradient") { KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type.toLower() == "brush") { KisBrushResourceServer* server = KisBrushServer::instance()->brushServer(); Q_FOREACH (KisBrushSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type.toLower() == "preset") { KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type.toLower() == "palette") { KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type.toLower() == "workspace") { KoResourceServer< KisWorkspaceResource >* server = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } return resources; } QStringList Krita::recentDocuments() const { KConfigGroup grp = KSharedConfig::openConfig()->group(QString("RecentFiles")); QStringList keys = grp.keyList(); QStringList recentDocuments; for(int i = 0; i <= keys.filter("File").count(); i++) recentDocuments << grp.readEntry(QString("File%1").arg(i), QString("")); return recentDocuments; } Document* Krita::createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution) { KisDocument *document = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(document); const KoColorSpace *cs = KoColorSpaceRegistry::instance()->colorSpace(colorModel, colorDepth, profile); Q_ASSERT(cs); QColor qc(Qt::white); qc.setAlpha(0); KoColor bgColor(qc, cs); if (!document->newImage(name, width, height, cs, bgColor, true, 1, "", double(resolution / 72) )) { qDebug() << "Could not create a new image"; return 0; } Q_ASSERT(document->image()); qDebug() << document->image()->objectName(); return new Document(document); } Document* Krita::openDocument(const QString &filename) { KisDocument *document = KisPart::instance()->createDocument(); KisPart::instance()->addDocument(document); document->openUrl(QUrl::fromLocalFile(filename), KisDocument::DontAddToRecent); return new Document(document); } Window* Krita::openWindow() { KisMainWindow *mw = KisPart::instance()->createMainWindow(); return new Window(mw); } -Action *Krita::createAction(const QString &id, const QString &text, bool addToScriptMenu) -{ - KisAction *action = new KisAction(text, this); - action->setObjectName(id); - - KisActionRegistry *actionRegistry = KisActionRegistry::instance(); - actionRegistry->propertizeAction(action->objectName(), action); - bool ok; // We will skip this check - int activationFlags = actionRegistry->getActionProperty(id, "activationFlags").toInt(&ok, 2); - int activationConditions = actionRegistry->getActionProperty(id, "activationConditions").toInt(&ok, 2); - action->setActivationFlags((KisAction::ActivationFlags) activationFlags); - action->setActivationConditions((KisAction::ActivationConditions) activationConditions); - - if (addToScriptMenu) { - KisPart::instance()->addScriptAction(action); - } - return new Action(action->objectName(), action); -} - void Krita::addExtension(Extension* extension) { d->extensions.append(extension); } QList< Extension* > Krita::extensions() { return d->extensions; } void Krita::writeSetting(const QString &group, const QString &name, const QString &value) { KConfigGroup grp = KSharedConfig::openConfig()->group(group); grp.writeEntry(name, value); } QString Krita::readSetting(const QString &group, const QString &name, const QString &defaultValue) { KConfigGroup grp = KSharedConfig::openConfig()->group(group); return grp.readEntry(name, defaultValue); } QIcon Krita::icon(QString &iconName) const { return KisIconUtils::loadIcon(iconName); } void Krita::addDockWidgetFactory(DockWidgetFactoryBase* factory) { KoDockRegistry::instance()->add(factory); } Krita* Krita::instance() { if (!s_instance) { s_instance = new Krita; } return s_instance; } /** * Scripter.fromVariant(variant) * variant is a QVariant * returns instance of QObject-subclass * * This is a helper method for PyQt because PyQt cannot cast a variant to a QObject or QWidget */ QObject *Krita::fromVariant(const QVariant& v) { if (v.canConvert< QWidget* >()) { QObject* obj = qvariant_cast< QWidget* >(v); return obj; } else if (v.canConvert< QObject* >()) { QObject* obj = qvariant_cast< QObject* >(v); return obj; } else return 0; } + +void Krita::mainWindowAdded(KisMainWindow *kisWindow) +{ + Q_FOREACH(Extension *extension, d->extensions) { + Window window(kisWindow); + extension->createActions(&window); + } +} diff --git a/libs/libkis/Krita.h b/libs/libkis/Krita.h index fc4f5b6a36..5fd47153fb 100644 --- a/libs/libkis/Krita.h +++ b/libs/libkis/Krita.h @@ -1,350 +1,344 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_KRITA_H #define LIBKIS_KRITA_H #include #include "kritalibkis_export.h" #include "libkis.h" #include "Extension.h" #include "Document.h" #include "Window.h" #include "View.h" #include "Action.h" #include "Notifier.h" class QAction; /** * Krita is a singleton class that offers the root access to the Krita object hierarchy. * * The Krita.instance() is aliased as two builtins: Scripter and Application. */ class KRITALIBKIS_EXPORT Krita : public QObject { Q_OBJECT public: explicit Krita(QObject *parent = 0); ~Krita() override; public Q_SLOTS: /** * @return the currently active document, if there is one. */ Document* activeDocument() const; /** * @brief setActiveDocument activates the first view that shows the given document * @param value the document we want to activate */ void setActiveDocument(Document* value); /** * @brief batchmode determines whether the script is run in batch mode. If batchmode * is true, scripts should now show messageboxes or dialog boxes. * * Note that this separate from Document.setBatchmode(), which determines whether * export/save option dialogs are shown. * * @return true if the script is run in batchmode */ bool batchmode() const; /** * @brief setBatchmode sets the batchmode to @param value; if true, scripts should * not show dialogs or messageboxes. */ void setBatchmode(bool value); /** * @return return a list of all actions for the currently active mainWindow. */ QList actions() const; /** * @return the action that has been registered under the given name, or 0 if no such action exists. */ Action *action(const QString &name) const; /** * @return a list of all open Documents */ QList documents() const; /** * @brief Filters are identified by an internal name. This function returns a list * of all existing registered filters. * @return a list of all registered filters */ QStringList filters() const; /** * @brief filter construct a Filter object with a default configuration. * @param name the name of the filter. Use Krita.instance().filters() to get * a list of all possible filters. * @return the filter or None if there is no such filter. */ Filter *filter(const QString &name) const; /** * @brief colorModels creates a list with all color models id's registered. * @return a list of all color models or a empty list if there is no such color models. */ QStringList colorModels() const; /** * @brief colorDepths creates a list with the names of all color depths * compatible with the given color model. * @param colorModel the id of a color model. * @return a list of all color depths or a empty list if there is no such * color depths. */ QStringList colorDepths(const QString &colorModel) const; /** * @brief filterStrategies Retrieves all installed filter strategies. A filter * strategy is used when transforming (scaling, shearing, rotating) an image to * calculate the value of the new pixels. You can use th * @return the id's of all available filters. */ QStringList filterStrategies() const; /** * @brief profiles creates a list with the names of all color profiles compatible * with the given color model and color depth. * @param colorModel A string describing the color model of the image: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @param colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @return a list with valid names */ QStringList profiles(const QString &colorModel, const QString &colorDepth) const; /** * @brief addProfile load the given profile into the profile registry. * @param profilePath the path to the profile. * @return true if adding the profile succeeded. */ bool addProfile(const QString &profilePath); /** * @brief notifier the Notifier singleton emits signals when documents are opened and * closed, the configuration changes, views are opened and closed or windows are opened. * @return the notifier object */ Notifier *notifier() const; /** * @brief version Determine the version of Krita * * Usage: print(Application.version ()) * * @return the version string including git sha1 if Krita was built from git */ QString version() const; /** * @return a list of all views. A Document can be shown in more than one view. */ QList views() const; /** * @return the currently active window or None if there is no window */ Window *activeWindow() const; /** * @return a list of all windows */ QList windows() const; /** * @brief resources returns a list of Resource objects of the given type * @param type Valid types are: * *
    *
  • pattern
  • *
  • gradient
  • *
  • brush
  • *
  • preset
  • *
  • palette
  • *
  • workspace
  • *
*/ QMap resources(const QString &type) const; /** * @brief return all recent documents registered in the RecentFiles group of the kritarc */ QStringList recentDocuments() const; /** * @brief createDocument creates a new document and image and registers * the document with the Krita application. * * Unless you explicitly call Document::close() the document wil remain * known to the Krita document registry. The document and its image will * only be deleted when Krita exits. * * The document will have one transparent layer. * * To create a new document and show it, do something like: @code from Krita import * def add_document_to_window(): d = Application.createDocument(100, 100, "Test", "RGBA", "U8", "", 120.0) Application.activeWindow().addView(d) add_document_to_window() @endcode * * @param width the width in pixels * @param height the height in pixels * @param name the name of the image (not the filename of the document) * @param colorModel A string describing the color model of the image: *
    *
  • A: Alpha mask
  • *
  • RGBA: RGB with alpha channel (The actual order of channels is most often BGR!)
  • *
  • XYZA: XYZ with alpha channel
  • *
  • LABA: LAB with alpha channel
  • *
  • CMYKA: CMYK with alpha channel
  • *
  • GRAYA: Gray with alpha channel
  • *
  • YCbCrA: YCbCr with alpha channel
  • *
* @param colorDepth A string describing the color depth of the image: *
    *
  • U8: unsigned 8 bits integer, the most common type
  • *
  • U16: unsigned 16 bits integer
  • *
  • F16: half, 16 bits floating point. Only available if Krita was built with OpenEXR
  • *
  • F32: 32 bits floating point
  • *
* @param profile The name of an icc profile that is known to Krita. If an empty string is passed, the default is * taken. * @param resolution the resolution in points per inch. * @return the created document. */ Document *createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution); /** * @brief openDocument creates a new Document, registers it with the Krita application and loads the given file. * @param filename the file to open in the document * @return the document */ Document *openDocument(const QString &filename); /** * @brief openWindow create a new main window. The window is not shown by default. */ Window *openWindow(); - /** - * @brief createAction creates an action with the given text and passes it to Krita. Every newly created - * mainwindow will create an instance of this action. This means that actions need to be created in the - * setup phase of the plugin, not on the fly. - * @param id the unique id for this action - * @param text the user-visible text - * @return the Action you can connect a slot to. - */ - Action *createAction(const QString &name, const QString &text, bool addToScriptMenu = true); - /** * @brief addExtension add the given plugin to Krita. There will be a single instance of each Extension in the Krita process. * @param extension the extension to add. */ void addExtension(Extension* extension); /** * return a list with all registered extension objects. */ QList extensions(); /** * @brief addDockWidgetFactory Add the given docker factory to the application. For scripts * loaded on startup, this means that every window will have one of the dockers created by the * factory. * @param factory The factory object. */ void addDockWidgetFactory(DockWidgetFactoryBase* factory ); /** * @brief writeSetting write the given setting under the given name to the kritarc file in * the given settings group. * @param group The group the setting belongs to. If empty, then the setting is written in the * general section * @param name The name of the setting * @param value The value of the setting. Script settings are always written as strings. */ void writeSetting(const QString &group, const QString &name, const QString &value); /** * @brief readSetting read the given setting value from the kritarc file. * @param group The group the setting is part of. If empty, then the setting is read from * the general group. * @param name The name of the setting * @param defaultValue The default value of the setting * @return a string representing the setting. */ QString readSetting(const QString &group, const QString &name, const QString &defaultValue); /** * @brief icon * This allows you to get icons from Krita's internal icons. * @param iconName name of the icon. * @return the icon related to this name. */ QIcon icon(QString &iconName) const; /** * @brief instance retrieve the singleton instance of the Application object. */ static Krita* instance(); // Internal only: for use with mikro.py static QObject *fromVariant(const QVariant& v); +private Q_SLOTS: + + void mainWindowAdded(KisMainWindow *window); + private: struct Private; Private *const d; static Krita* s_instance; }; Q_DECLARE_METATYPE(Notifier*); #endif // LIBKIS_KRITA_H diff --git a/libs/libkis/Mainpage.dox b/libs/libkis/Mainpage.dox index 03b09767d3..c98cbcf7c8 100644 --- a/libs/libkis/Mainpage.dox +++ b/libs/libkis/Mainpage.dox @@ -1,147 +1,150 @@ /* * Copyright (C) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** \mainpage Krita Scripting and Plugin Wrapper Library: libkis Libkis is a QObject-based wrapper around Krita's internal libraries. The wrapper can be used from C++ plugins that do not need the advanced and volatile internal libraries of Krita, or can be bound to scripting languages like Python or Javascript. All classes are based on QObject, so QMetaObject introspection can be used to discover properties, slots and signals and automatically expose all functionality to the client. Note that all objects that are created are wrapper objects that are owned by the scripting environment or the plugin. Using the functionality in this library, either through a scripting environment like Python or Javascript, or directly from C++, you can, among other things achieve the following functionality:
  • Open, save, export, rename files.
  • Access the layers and masks in a file
  • Read and write pixel data
  • Add menu items, toolbar items and docker palettes
The reference implementation of scripting in Krita is implemented in Python 3. All examples in the documentation for scripting will be provided using Python, although the api documentation is generated from C++ header files and shows c++ syntax for arguments. Autostarting Scripts ==================== Autostarting scripts or script-based plugins are scripts that Krita loads on startup. You can add autostarting scripts to Krita by placing them in the pykrita folder in the resources folder: go to settings/ manage resources and press the Open Resources Folder to open your local resources folder. Scripts are identified by a file that ends in a `.desktop` extension that contain information about the script itself. For example: @code [Desktop Entry] Type=Service ServiceTypes=Krita/PythonPlugin X-KDE-Library=hello X-Python-2-Compatible=false Name=Hello World Comment=Basic plugin to test PyKrita @endcode The Python code itself should be placed in the pykrita/hello folder. Your Python plugin needs to be a module, so needs to have a `__init__.py` file: @code # let's make a module from .hello import * @endcode Krita is a Qt-based application. In principle, you can use any Python binding to Qt as long as it's using exactly the same version of Qt that Krita uses. In our examples we will be using [PyQt](https://www.riverbankcomputing.com/software/pyqt/intro). The easiest access to the Krita api is by simply importing the "krita" module. This automatically adds two built-ins: Scripter and Application. This is an alias for Krita.instance(), which is the first place from which to access the running Krita instance. @code from PyQt5.QtGui import * from PyQt5.QtWidgets import * from krita import * def hello(): QMessageBox.information(QWidget(), "Test", "Hello World") class HelloExtension(Extension): def __init__(self, parent): super().__init__(parent) - def setup(self, viewManager): - action = viewManager.createAction("Hello") + def setup(self): + pass + + def createActions(self, window): + action = window.createAction("Hello") action.triggered.connect(hello) Krita.instance().addExtension(HelloExtension(Krita.instance())) @endcode The Krita Object Model ====================== The starting point is the @see Krita class, which provides a singleton object for easy reference. From the Krita class, a hierarchy is provided where you can access windows, lviews, documents, nodes and channels. You can access the Krita class as * Krita.instance() * Application * Scripter For ease of use. The Document class is provided to allow access to the images Krita has loaded. *Note*: internally, Krita distinguishes between images and documents. A document contains an image and knows the filename of the image, the image itself only knows about the layers and masks it contains. The generic name for layers and masks is *node*. A Node can be a group layer, a file layer, a vector layer, a filter layer, a generator layer, a clone layer, a paint layer or a transform mask, a selection mask, a transparency mask or a colorize mask. You can change some properties of Nodes, but not all of them. The Window class is provided to allow access to the windows and views Krita has open. A given Document can be shown in more than one View, and in more than one Window. You can open and close windows and views. */ // DOXYGEN_SET_PROJECT_NAME = Krita // DOXYGEN_SET_IGNORE_PREFIX = Kis Ko K diff --git a/libs/libkis/Window.cpp b/libs/libkis/Window.cpp index e5b2189e77..59d273e1b1 100644 --- a/libs/libkis/Window.cpp +++ b/libs/libkis/Window.cpp @@ -1,117 +1,151 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "Window.h" +#include +#include + #include #include #include +#include +#include #include #include - +#include struct Window::Private { Private() {} QPointer window; }; Window::Window(KisMainWindow *window, QObject *parent) : QObject(parent) , d(new Private) { d->window = window; connect(window, SIGNAL(destroyed(QObject*)), SIGNAL(windowClosed())); } Window::~Window() { delete d; } bool Window::operator==(const Window &other) const { return (d->window == other.d->window); } bool Window::operator!=(const Window &other) const { return !(operator==(other)); } QMainWindow *Window::qwindow() const { return d->window; } QList Window::views() const { QList ret; if (d->window) { foreach(QPointer view, KisPart::instance()->views()) { if (view->mainWindow() == d->window) { ret << new View(view); } } } return ret; } View *Window::addView(Document *document) { if (d->window) { KisView *view = d->window->newView(document->document()); return new View(view); } return 0; } void Window::showView(View *view) { if (views().contains(view)) { KisView *v = view->view(); d->window->showView(v); } } View *Window::activeView() const { if (d->window) { return new View(d->window->activeView()); } return 0; } void Window::activate() { if (d->window) { d->window->activateWindow(); } } void Window::close() { if (d->window) { KisPart::instance()->removeMainWindow(d->window); d->window->close(); } } +Action *Window::createAction(const QString &id, const QString &text, const QString &menuLocation) +{ + KisAction *action = d->window->viewManager()->actionManager()->createAction(id); + action->setText(text); + action->setObjectName(id); + if (!menuLocation.isEmpty()) { + QAction *found = 0; + QList candidates = d->window->menuBar()->actions(); + Q_FOREACH(const QString &name, menuLocation.split("/")) { + Q_FOREACH(QAction *candidate, candidates) { + if (candidate->objectName() == name) { + found = candidate; + candidates = candidate->menu()->actions(); + break; + } + } + if (candidates.isEmpty()) { + break; + } + } + + if (found && found->menu()) { + found->menu()->addAction(action); + } + } + return new Action(action->objectName(), action); +} + + diff --git a/libs/libkis/Window.h b/libs/libkis/Window.h index f6a5b0eac4..53c5f13942 100644 --- a/libs/libkis/Window.h +++ b/libs/libkis/Window.h @@ -1,95 +1,112 @@ /* * Copyright (c) 2016 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef LIBKIS_WINDOW_H #define LIBKIS_WINDOW_H #include +#include #include #include "kritalibkis_export.h" #include "libkis.h" #include + +class Action; + /** * Window represents one Krita mainwindow. A window can have any number * of views open on any number of documents. */ class KRITALIBKIS_EXPORT Window : public QObject { Q_OBJECT public: explicit Window(KisMainWindow *window, QObject *parent = 0); ~Window() override; bool operator==(const Window &other) const; bool operator!=(const Window &other) const; public Q_SLOTS: /** * Return a handle to the QMainWindow widget. This is useful * to e.g. parent dialog boxes and message box. */ QMainWindow *qwindow() const; /** * @return a list of open views in this window */ QList views() const; /** * Open a new view on the given document in this window */ View *addView(Document *document); /** * Make the given view active in this window. If the view * does not belong to this window, nothing happens. */ void showView(View *view); /** * @return the currently active view or 0 if no view is active */ View *activeView() const; /** * @brief activate activates this Window. */ void activate(); /** * @brief close the active window and all its Views. If there * are no Views left for a given Document, that Document will * also be closed. */ void close(); + /** + * @brief createAction creates an Action object and adds it to the action + * manager for this Window. + * @param id The unique id for the action. This will be used to + * propertize the action if any .action file is present + * @param text The user-visible text of the action. If empty, the text from the + * .action file is used. + * @param menu a /-separated string that describes which menu the action should + * be places in. Default is "tools/scripts" + * @return the new action. + */ + Action *createAction(const QString &id, const QString &text = QString(), const QString &menuLocation = QString("tools/scripts")); + Q_SIGNALS: /// Emitted when the window is closed. void windowClosed(); private: struct Private; Private *const d; }; #endif // LIBKIS_WINDOW_H diff --git a/libs/ui/CMakeLists.txt b/libs/ui/CMakeLists.txt index e1fa6a93e2..6d69cf303a 100644 --- a/libs/ui/CMakeLists.txt +++ b/libs/ui/CMakeLists.txt @@ -1,577 +1,576 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/qtlockedfile ${EXIV2_INCLUDE_DIR} ) include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR} ${OCIO_INCLUDE_DIR} ) add_subdirectory( tests ) if (APPLE) find_library(FOUNDATION_LIBRARY Foundation) find_library(APPKIT_LIBRARY AppKit) endif () set(kritaui_LIB_SRCS canvas/kis_canvas_widget_base.cpp canvas/kis_canvas2.cpp canvas/kis_canvas_updates_compressor.cpp canvas/kis_canvas_controller.cpp canvas/kis_paintop_transformation_connector.cpp canvas/kis_display_color_converter.cpp canvas/kis_display_filter.cpp canvas/kis_exposure_gamma_correction_interface.cpp canvas/kis_tool_proxy.cpp canvas/kis_canvas_decoration.cc canvas/kis_coordinates_converter.cpp canvas/kis_grid_manager.cpp canvas/kis_grid_decoration.cpp canvas/kis_grid_config.cpp canvas/kis_prescaled_projection.cpp canvas/kis_qpainter_canvas.cpp canvas/kis_projection_backend.cpp canvas/kis_update_info.cpp canvas/kis_image_patch.cpp canvas/kis_image_pyramid.cpp canvas/kis_infinity_manager.cpp canvas/kis_change_guides_command.cpp canvas/kis_guides_decoration.cpp canvas/kis_guides_manager.cpp canvas/kis_guides_config.cpp canvas/kis_snap_config.cpp canvas/kis_snap_line_strategy.cpp canvas/KisSnapPointStrategy.cpp dialogs/kis_about_application.cpp dialogs/kis_dlg_adj_layer_props.cc dialogs/kis_dlg_adjustment_layer.cc dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_generator_layer.cpp dialogs/kis_dlg_file_layer.cpp dialogs/kis_dlg_filter.cpp dialogs/kis_dlg_stroke_selection_properties.cpp dialogs/kis_dlg_image_properties.cc dialogs/kis_dlg_layer_properties.cc dialogs/kis_dlg_preferences.cc dialogs/slider_and_spin_box_sync.cpp dialogs/kis_dlg_blacklist_cleanup.cpp dialogs/kis_dlg_layer_style.cpp dialogs/kis_dlg_png_import.cpp dialogs/kis_dlg_import_image_sequence.cpp dialogs/kis_delayed_save_dialog.cpp dialogs/kis_dlg_internal_color_selector.cpp flake/kis_node_dummies_graph.cpp flake/kis_dummies_facade_base.cpp flake/kis_dummies_facade.cpp flake/kis_node_shapes_graph.cpp flake/kis_node_shape.cpp flake/kis_shape_controller.cpp flake/kis_shape_layer.cc flake/kis_shape_layer_canvas.cpp flake/kis_shape_selection.cpp flake/kis_shape_selection_canvas.cpp flake/kis_shape_selection_model.cpp flake/kis_take_all_shapes_command.cpp brushhud/kis_uniform_paintop_property_widget.cpp brushhud/kis_brush_hud.cpp brushhud/kis_round_hud_button.cpp brushhud/kis_dlg_brush_hud_config.cpp brushhud/kis_brush_hud_properties_list.cpp brushhud/kis_brush_hud_properties_config.cpp kis_aspect_ratio_locker.cpp kis_autogradient.cc kis_bookmarked_configurations_editor.cc kis_bookmarked_configurations_model.cc kis_bookmarked_filter_configurations_model.cc kis_base_option.cpp kis_canvas_resource_provider.cpp kis_derived_resources.cpp kis_categories_mapper.cpp kis_categorized_list_model.cpp kis_categorized_item_delegate.cpp kis_clipboard.cc kis_config.cc kis_config_notifier.cpp kis_control_frame.cpp kis_composite_ops_model.cc kis_paint_ops_model.cpp kis_cursor.cc kis_cursor_cache.cpp kis_custom_pattern.cc kis_file_layer.cpp kis_change_file_layer_command.h kis_safe_document_loader.cpp kis_splash_screen.cpp kis_filter_manager.cc kis_filters_model.cc kis_histogram_view.cc KisImageBarrierLockerWithFeedback.cpp kis_image_manager.cc kis_image_view_converter.cpp kis_import_catcher.cc kis_layer_manager.cc kis_mask_manager.cc kis_mimedata.cpp kis_node_commands_adapter.cpp kis_node_manager.cpp kis_node_juggler_compressed.cpp kis_node_selection_adapter.cpp kis_node_insertion_adapter.cpp kis_node_model.cpp kis_node_filter_proxy_model.cpp kis_model_index_converter_base.cpp kis_model_index_converter.cpp kis_model_index_converter_show_all.cpp kis_painting_assistant.cc kis_painting_assistants_decoration.cpp kis_painting_assistants_manager.cpp kis_paintop_box.cc kis_paintop_option.cpp kis_paintop_options_model.cpp kis_paintop_settings_widget.cpp kis_popup_palette.cpp kis_png_converter.cpp kis_preference_set_registry.cpp - kis_script_manager.cpp kis_resource_server_provider.cpp KisSelectedShapesProxy.cpp kis_selection_decoration.cc kis_selection_manager.cc kis_statusbar.cc kis_zoom_manager.cc kis_favorite_resource_manager.cpp kis_workspace_resource.cpp kis_action.cpp kis_action_manager.cpp KisActionPlugin.cpp kis_canvas_controls_manager.cpp kis_tooltip_manager.cpp kis_multinode_property.cpp kis_stopgradient_editor.cpp kisexiv2/kis_exif_io.cpp kisexiv2/kis_exiv2.cpp kisexiv2/kis_iptc_io.cpp kisexiv2/kis_xmp_io.cpp opengl/kis_opengl.cpp opengl/kis_opengl_canvas2.cpp opengl/kis_opengl_canvas_debugger.cpp opengl/kis_opengl_image_textures.cpp opengl/kis_texture_tile.cpp opengl/kis_opengl_shader_loader.cpp opengl/kis_texture_tile_info_pool.cpp kis_fps_decoration.cpp recorder/kis_node_query_path_editor.cc recorder/kis_recorded_action_creator.cc recorder/kis_recorded_action_creator_factory.cc recorder/kis_recorded_action_creator_factory_registry.cc recorder/kis_recorded_action_editor_factory.cc recorder/kis_recorded_action_editor_factory_registry.cc recorder/kis_recorded_filter_action_editor.cc recorder/kis_recorded_filter_action_creator.cpp recorder/kis_recorded_paint_action_editor.cc tool/kis_selection_tool_helper.cpp tool/kis_selection_tool_config_widget_helper.cpp tool/kis_rectangle_constraint_widget.cpp tool/kis_shape_tool_helper.cpp tool/kis_tool.cc tool/kis_delegated_tool_policies.cpp tool/kis_tool_freehand.cc tool/kis_speed_smoother.cpp tool/kis_painting_information_builder.cpp tool/kis_stabilized_events_sampler.cpp tool/kis_tool_freehand_helper.cpp tool/kis_tool_multihand_helper.cpp tool/kis_figure_painting_tool_helper.cpp tool/kis_recording_adapter.cpp tool/kis_tool_paint.cc tool/kis_tool_shape.cc tool/kis_tool_ellipse_base.cpp tool/kis_tool_rectangle_base.cpp tool/kis_tool_polyline_base.cpp tool/kis_tool_utils.cpp tool/kis_resources_snapshot.cpp tool/kis_smoothing_options.cpp tool/KisStabilizerDelayedPaintHelper.cpp tool/KisStrokeSpeedMonitor.cpp tool/strokes/freehand_stroke.cpp tool/strokes/KisStrokeEfficiencyMeasurer.cpp tool/strokes/kis_painter_based_stroke_strategy.cpp tool/strokes/kis_filter_stroke_strategy.cpp tool/strokes/kis_color_picker_stroke_strategy.cpp tool/strokes/KisFreehandStrokeInfo.cpp tool/strokes/KisMaskedFreehandStrokePainter.cpp tool/strokes/KisMaskingBrushRenderer.cpp tool/strokes/KisMaskingBrushCompositeOpFactory.cpp widgets/kis_cmb_composite.cc widgets/kis_cmb_contour.cpp widgets/kis_cmb_gradient.cpp widgets/kis_paintop_list_widget.cpp widgets/kis_cmb_idlist.cc widgets/kis_color_space_selector.cc widgets/kis_advanced_color_space_selector.cc widgets/kis_cie_tongue_widget.cpp widgets/kis_tone_curve_widget.cpp widgets/kis_curve_widget.cpp widgets/kis_custom_image_widget.cc widgets/kis_image_from_clipboard_widget.cpp widgets/kis_double_widget.cc widgets/kis_filter_selector_widget.cc widgets/kis_gradient_chooser.cc widgets/kis_gradient_slider_widget.cc widgets/kis_gradient_slider.cpp widgets/kis_iconwidget.cc widgets/kis_mask_widgets.cpp widgets/kis_meta_data_merge_strategy_chooser_widget.cc widgets/kis_multi_bool_filter_widget.cc widgets/kis_multi_double_filter_widget.cc widgets/kis_multi_integer_filter_widget.cc widgets/kis_multipliers_double_slider_spinbox.cpp widgets/kis_paintop_presets_popup.cpp widgets/kis_tool_options_popup.cpp widgets/kis_paintop_presets_chooser_popup.cpp widgets/kis_paintop_presets_save.cpp widgets/kis_paintop_preset_icon_library.cpp widgets/kis_pattern_chooser.cc widgets/kis_popup_button.cc widgets/kis_preset_chooser.cpp widgets/kis_progress_widget.cpp widgets/kis_selection_options.cc widgets/kis_scratch_pad.cpp widgets/kis_scratch_pad_event_filter.cpp widgets/kis_preset_selector_strip.cpp widgets/kis_slider_spin_box.cpp widgets/kis_size_group.cpp widgets/kis_size_group_p.cpp widgets/kis_wdg_generator.cpp widgets/kis_workspace_chooser.cpp widgets/kis_categorized_list_view.cpp widgets/kis_widget_chooser.cpp widgets/kis_tool_button.cpp widgets/kis_floating_message.cpp widgets/kis_lod_availability_widget.cpp widgets/kis_color_label_selector_widget.cpp widgets/kis_color_filter_combo.cpp widgets/kis_elided_label.cpp widgets/kis_stopgradient_slider_widget.cpp widgets/kis_spinbox_color_selector.cpp widgets/kis_screen_color_picker.cpp widgets/kis_preset_live_preview_view.cpp widgets/KoDualColorButton.cpp widgets/kis_color_input.cpp widgets/kis_color_button.cpp widgets/KisVisualColorSelector.cpp widgets/KisVisualColorSelectorShape.cpp widgets/KisVisualEllipticalSelectorShape.cpp widgets/KisVisualRectangleSelectorShape.cpp widgets/KisVisualTriangleSelectorShape.cpp widgets/KoStrokeConfigWidget.cpp widgets/KoFillConfigWidget.cpp utils/kis_document_aware_spin_box_unit_manager.cpp input/kis_input_manager.cpp input/kis_input_manager_p.cpp input/kis_extended_modifiers_mapper.cpp input/kis_abstract_input_action.cpp input/kis_tool_invocation_action.cpp input/kis_pan_action.cpp input/kis_alternate_invocation_action.cpp input/kis_rotate_canvas_action.cpp input/kis_zoom_action.cpp input/kis_change_frame_action.cpp input/kis_gamma_exposure_action.cpp input/kis_show_palette_action.cpp input/kis_change_primary_setting_action.cpp input/kis_abstract_shortcut.cpp input/kis_native_gesture_shortcut.cpp input/kis_single_action_shortcut.cpp input/kis_stroke_shortcut.cpp input/kis_shortcut_matcher.cpp input/kis_select_layer_action.cpp input/KisQtWidgetsTweaker.cpp input/KisInputActionGroup.cpp operations/kis_operation.cpp operations/kis_operation_configuration.cpp operations/kis_operation_registry.cpp operations/kis_operation_ui_factory.cpp operations/kis_operation_ui_widget.cpp operations/kis_filter_selection_operation.cpp actions/kis_selection_action_factories.cpp actions/KisPasteActionFactory.cpp input/kis_touch_shortcut.cpp kis_document_undo_store.cpp kis_transaction_based_command.cpp kis_gui_context_command.cpp kis_gui_context_command_p.cpp input/kis_tablet_debugger.cpp input/kis_input_profile_manager.cpp input/kis_input_profile.cpp input/kis_shortcut_configuration.cpp input/config/kis_input_configuration_page.cpp input/config/kis_edit_profiles_dialog.cpp input/config/kis_input_profile_model.cpp input/config/kis_input_configuration_page_item.cpp input/config/kis_action_shortcuts_model.cpp input/config/kis_input_type_delegate.cpp input/config/kis_input_mode_delegate.cpp input/config/kis_input_button.cpp input/config/kis_input_editor_delegate.cpp input/config/kis_mouse_input_editor.cpp input/config/kis_wheel_input_editor.cpp input/config/kis_key_input_editor.cpp processing/fill_processing_visitor.cpp kis_asl_layer_style_serializer.cpp kis_psd_layer_style_resource.cpp canvas/kis_mirror_axis.cpp kis_abstract_perspective_grid.cpp KisApplication.cpp KisAutoSaveRecoveryDialog.cpp KisDetailsPane.cpp KisDocument.cpp KisNodeDelegate.cpp kis_node_view_visibility_delegate.cpp KisNodeToolTip.cpp KisNodeView.cpp kis_node_view_color_scheme.cpp KisImportExportFilter.cpp KisFilterEntry.cpp KisImportExportManager.cpp KisImportExportUtils.cpp kis_async_action_feedback.cpp KisMainWindow.cpp KisOpenPane.cpp KisPart.cpp KisPrintJob.cpp KisTemplate.cpp KisTemplateCreateDia.cpp KisTemplateGroup.cpp KisTemplates.cpp KisTemplatesPane.cpp KisTemplateTree.cpp KisUndoStackAction.cpp KisView.cpp thememanager.cpp kis_mainwindow_observer.cpp KisViewManager.cpp kis_mirror_manager.cpp qtlockedfile/qtlockedfile.cpp qtsingleapplication/qtlocalpeer.cpp qtsingleapplication/qtsingleapplication.cpp KisResourceBundle.cpp KisResourceBundleManifest.cpp kis_md5_generator.cpp KisApplicationArguments.cpp KisNetworkAccessManager.cpp KisMultiFeedRSSModel.cpp KisRemoteFileFetcher.cpp KisPaletteModel.cpp kis_palette_delegate.cpp kis_palette_view.cpp KisColorsetChooser.cpp KisSaveGroupVisitor.cpp ) if(WIN32) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support_win.cpp input/wintab/kis_screen_size_choice_dialog.cpp qtlockedfile/qtlockedfile_win.cpp input/wintab/kis_tablet_support_win8.cpp opengl/kis_opengl_win.cpp ) endif() set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} kis_animation_frame_cache.cpp kis_animation_cache_populator.cpp KisAsyncAnimationRendererBase.cpp KisAsyncAnimationCacheRenderer.cpp KisAsyncAnimationFramesSavingRenderer.cpp dialogs/KisAsyncAnimationRenderDialogBase.cpp dialogs/KisAsyncAnimationCacheRenderDialog.cpp dialogs/KisAsyncAnimationFramesSaveDialog.cpp canvas/kis_animation_player.cpp kis_animation_importer.cpp KisSyncedAudioPlayback.cpp ) if(UNIX) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/kis_tablet_event.cpp input/wintab/kis_tablet_support.cpp qtlockedfile/qtlockedfile_unix.cpp ) if(NOT APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} input/wintab/qxcbconnection_xi2.cpp input/wintab/qxcbconnection.cpp input/wintab/kis_xi2_event_filter.cpp ) endif() endif() if(APPLE) set(kritaui_LIB_SRCS ${kritaui_LIB_SRCS} osx.mm ) endif() ki18n_wrap_ui(kritaui_LIB_SRCS widgets/KoFillConfigWidget.ui widgets/KoStrokeConfigWidget.ui forms/wdgdlgpngimport.ui forms/wdgfullscreensettings.ui forms/wdgautogradient.ui forms/wdggeneralsettings.ui forms/wdgperformancesettings.ui forms/wdggenerators.ui forms/wdgbookmarkedconfigurationseditor.ui forms/wdgapplyprofile.ui forms/wdgcustompattern.ui forms/wdglayerproperties.ui forms/wdgcolorsettings.ui forms/wdgtabletsettings.ui forms/wdgcolorspaceselector.ui forms/wdgcolorspaceselectoradvanced.ui forms/wdgdisplaysettings.ui forms/kis_previewwidgetbase.ui forms/kis_matrix_widget.ui forms/wdgselectionoptions.ui forms/wdggeometryoptions.ui forms/wdgnewimage.ui forms/wdgimageproperties.ui forms/wdgmaskfromselection.ui forms/wdgmasksource.ui forms/wdgfilterdialog.ui forms/wdgmetadatamergestrategychooser.ui forms/wdgpaintoppresets.ui forms/wdgpaintopsettings.ui forms/wdgdlggeneratorlayer.ui forms/wdgdlgfilelayer.ui forms/wdgfilterselector.ui forms/wdgfilternodecreation.ui forms/wdgpaintactioneditor.ui forms/wdgmultipliersdoublesliderspinbox.ui forms/wdgnodequerypatheditor.ui forms/wdgpresetselectorstrip.ui forms/wdgsavebrushpreset.ui forms/wdgpreseticonlibrary.ui forms/wdgdlgblacklistcleanup.ui forms/wdgrectangleconstraints.ui forms/wdgimportimagesequence.ui forms/wdgstrokeselectionproperties.ui forms/KisDetailsPaneBase.ui forms/KisOpenPaneBase.ui forms/wdgstopgradienteditor.ui brushhud/kis_dlg_brush_hud_config.ui forms/wdgdlginternalcolorselector.ui dialogs/kis_delayed_save_dialog.ui input/config/kis_input_configuration_page.ui input/config/kis_edit_profiles_dialog.ui input/config/kis_input_configuration_page_item.ui input/config/kis_mouse_input_editor.ui input/config/kis_wheel_input_editor.ui input/config/kis_key_input_editor.ui layerstyles/wdgBevelAndEmboss.ui layerstyles/wdgblendingoptions.ui layerstyles/WdgColorOverlay.ui layerstyles/wdgContour.ui layerstyles/wdgdropshadow.ui layerstyles/WdgGradientOverlay.ui layerstyles/wdgInnerGlow.ui layerstyles/wdglayerstyles.ui layerstyles/WdgPatternOverlay.ui layerstyles/WdgSatin.ui layerstyles/WdgStroke.ui layerstyles/wdgstylesselector.ui layerstyles/wdgTexture.ui wdgsplash.ui input/wintab/kis_screen_size_choice_dialog.ui ) QT5_WRAP_CPP(kritaui_HEADERS_MOC KisNodePropertyAction_p.h) add_library(kritaui SHARED ${kritaui_HEADERS_MOC} ${kritaui_LIB_SRCS} ) generate_export_header(kritaui BASE_NAME kritaui) target_link_libraries(kritaui KF5::CoreAddons KF5::Completion KF5::I18n KF5::ItemViews Qt5::Network kritaimpex kritacolor kritaimage kritalibbrush kritawidgets kritawidgetutils ${PNG_LIBRARIES} ${EXIV2_LIBRARIES} ) if (HAVE_QT_MULTIMEDIA) target_link_libraries(kritaui Qt5::Multimedia) endif() if (HAVE_KIO) target_link_libraries(kritaui KF5::KIOCore) endif() if (NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${X11_X11_LIB} ${X11_Xinput_LIB} ${XCB_LIBRARIES}) endif() if(APPLE) target_link_libraries(kritaui ${FOUNDATION_LIBRARY}) target_link_libraries(kritaui ${APPKIT_LIBRARY}) endif () target_link_libraries(kritaui ${OPENEXR_LIBRARIES}) # Add VSync disable workaround if(NOT WIN32 AND NOT APPLE) target_link_libraries(kritaui ${CMAKE_DL_LIBS} Qt5::X11Extras) endif() if(X11_FOUND) target_link_libraries(kritaui Qt5::X11Extras ${X11_LIBRARIES}) endif() target_include_directories(kritaui PUBLIC $ $ $ $ $ $ $ ) set_target_properties(kritaui PROPERTIES VERSION ${GENERIC_KRITA_LIB_VERSION} SOVERSION ${GENERIC_KRITA_LIB_SOVERSION} ) install(TARGETS kritaui ${INSTALL_TARGETS_DEFAULT_ARGS}) if (APPLE) install(FILES osx.stylesheet DESTINATION ${DATA_INSTALL_DIR}/krita) endif () diff --git a/libs/ui/KisPart.cpp b/libs/ui/KisPart.cpp index 645991d798..cf34bffca3 100644 --- a/libs/ui/KisPart.cpp +++ b/libs/ui/KisPart.cpp @@ -1,479 +1,468 @@ /* This file is part of the KDE project * Copyright (C) 1998-1999 Torben Weis * Copyright (C) 2000-2005 David Faure * Copyright (C) 2007-2008 Thorsten Zachmann * Copyright (C) 2010-2012 Boudewijn Rempt * Copyright (C) 2011 Inge Wallin * Copyright (C) 2015 Michael Abrahams * * 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 "KisPart.h" #include "KoProgressProxy.h" #include #include #include #include #include #include #include #include #include "KisApplication.h" #include "KisDocument.h" #include "KisView.h" #include "KisViewManager.h" #include "KisImportExportManager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KisView.h" #include "KisDocument.h" #include "kis_config.h" #include "kis_shape_controller.h" #include "kis_resource_server_provider.h" #include "kis_animation_cache_populator.h" #include "kis_idle_watcher.h" #include "kis_image.h" #include "KisImportExportManager.h" #include "KisDocument.h" #include "KoToolManager.h" #include "KisViewManager.h" -#include "kis_script_manager.h" #include "KisOpenPane.h" #include "kis_color_manager.h" #include "kis_debug.h" #include "kis_action.h" #include "kis_action_registry.h" Q_GLOBAL_STATIC(KisPart, s_instance) class Q_DECL_HIDDEN KisPart::Private { public: Private(KisPart *_part) : part(_part) , idleWatcher(2500) , animationCachePopulator(_part) { } ~Private() { } KisPart *part; QList > views; QList > mainWindows; QList > documents; - QList scriptActions; - KActionCollection *actionCollection{0}; - KisIdleWatcher idleWatcher; KisAnimationCachePopulator animationCachePopulator; }; KisPart* KisPart::instance() { return s_instance; } KisPart::KisPart() : d(new Private(this)) { // Preload all the resources in the background Q_UNUSED(KoResourceServerProvider::instance()); Q_UNUSED(KisResourceServerProvider::instance()); Q_UNUSED(KisColorManager::instance()); connect(this, SIGNAL(documentOpened(QString)), this, SLOT(updateIdleWatcherConnections())); connect(this, SIGNAL(documentClosed(QString)), this, SLOT(updateIdleWatcherConnections())); connect(KisActionRegistry::instance(), SIGNAL(shortcutsUpdated()), this, SLOT(updateShortcuts())); connect(&d->idleWatcher, SIGNAL(startedIdleMode()), &d->animationCachePopulator, SLOT(slotRequestRegeneration())); d->animationCachePopulator.slotRequestRegeneration(); } KisPart::~KisPart() { while (!d->documents.isEmpty()) { delete d->documents.takeFirst(); } while (!d->views.isEmpty()) { delete d->views.takeFirst(); } while (!d->mainWindows.isEmpty()) { delete d->mainWindows.takeFirst(); } delete d; } void KisPart::updateIdleWatcherConnections() { QVector images; Q_FOREACH (QPointer document, documents()) { if (document->image()) { images << document->image(); } } d->idleWatcher.setTrackedImages(images); } void KisPart::addDocument(KisDocument *document) { //dbgUI << "Adding document to part list" << document; Q_ASSERT(document); if (!d->documents.contains(document)) { d->documents.append(document); emit documentOpened('/'+objectName()); emit sigDocumentAdded(document); connect(document, SIGNAL(sigSavingFinished()), SLOT(slotDocumentSaved())); } } QList > KisPart::documents() const { return d->documents; } KisDocument *KisPart::createDocument() const { KisDocument *doc = new KisDocument(); return doc; } int KisPart::documentCount() const { return d->documents.size(); } void KisPart::removeDocument(KisDocument *document) { d->documents.removeAll(document); emit documentClosed('/'+objectName()); emit sigDocumentRemoved(document->url().toLocalFile()); document->deleteLater(); } KisMainWindow *KisPart::createMainWindow() { KisMainWindow *mw = new KisMainWindow(); - Q_FOREACH(KisAction *action, d->scriptActions) { - mw->viewManager()->scriptManager()->addAction(action); - } dbgUI <<"mainWindow" << (void*)mw << "added to view" << this; d->mainWindows.append(mw); emit sigWindowAdded(mw); return mw; } KisView *KisPart::createView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent) { // If creating the canvas fails, record this and disable OpenGL next time KisConfig cfg; KConfigGroup grp( KSharedConfig::openConfig(), "crashprevention"); if (grp.readEntry("CreatingCanvas", false)) { cfg.setUseOpenGL(false); } if (cfg.canvasState() == "OPENGL_FAILED") { cfg.setUseOpenGL(false); } grp.writeEntry("CreatingCanvas", true); grp.sync(); QApplication::setOverrideCursor(Qt::WaitCursor); KisView *view = new KisView(document, resourceManager, actionCollection, parent); QApplication::restoreOverrideCursor(); // Record successful canvas creation grp.writeEntry("CreatingCanvas", false); grp.sync(); addView(view); return view; } void KisPart::addView(KisView *view) { if (!view) return; if (!d->views.contains(view)) { d->views.append(view); } emit sigViewAdded(view); } void KisPart::removeView(KisView *view) { if (!view) return; /** * HACK ALERT: we check here explicitly if the document (or main * window), is saving the stuff. If we close the * document *before* the saving is completed, a crash * will happen. */ KIS_ASSERT_RECOVER_RETURN(!view->mainWindow()->hackIsSaving()); emit sigViewRemoved(view); QPointer doc = view->document(); d->views.removeAll(view); if (doc) { bool found = false; Q_FOREACH (QPointer view, d->views) { if (view && view->document() == doc) { found = true; break; } } if (!found) { removeDocument(doc); } } } QList > KisPart::views() const { return d->views; } int KisPart::viewCount(KisDocument *doc) const { if (!doc) { return d->views.count(); } else { int count = 0; Q_FOREACH (QPointer view, d->views) { if (view && view->isVisible() && view->document() == doc) { count++; } } return count; } } void KisPart::slotDocumentSaved() { KisDocument *doc = qobject_cast(sender()); emit sigDocumentSaved(doc->url().toLocalFile()); } void KisPart::removeMainWindow(KisMainWindow *mainWindow) { dbgUI <<"mainWindow" << (void*)mainWindow <<"removed from doc" << this; if (mainWindow) { d->mainWindows.removeAll(mainWindow); } } const QList > &KisPart::mainWindows() const { return d->mainWindows; } int KisPart::mainwindowCount() const { return d->mainWindows.count(); } KisMainWindow *KisPart::currentMainwindow() const { QWidget *widget = qApp->activeWindow(); KisMainWindow *mainWindow = qobject_cast(widget); while (!mainWindow && widget) { widget = widget->parentWidget(); mainWindow = qobject_cast(widget); } if (!mainWindow && mainWindows().size() > 0) { mainWindow = mainWindows().first(); } return mainWindow; } -void KisPart::addScriptAction(KisAction *action) -{ - d->scriptActions << action; -} KisIdleWatcher* KisPart::idleWatcher() const { return &d->idleWatcher; } KisAnimationCachePopulator* KisPart::cachePopulator() const { return &d->animationCachePopulator; } void KisPart::openExistingFile(const QUrl &url) { // TODO: refactor out this method! KisMainWindow *mw = currentMainwindow(); KIS_SAFE_ASSERT_RECOVER_RETURN(mw); mw->openDocument(url, KisMainWindow::None); } void KisPart::updateShortcuts() { // Update any non-UI actionCollections. That includes: // - Shortcuts called inside of tools // - Perhaps other things? KoToolManager::instance()->updateToolShortcuts(); // Now update the UI actions. Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { KActionCollection *ac = mainWindow->actionCollection(); ac->updateShortcuts(); // Loop through mainWindow->actionCollections() to modify tooltips // so that they list shortcuts at the end in parentheses Q_FOREACH ( QAction* action, ac->actions()) { // Remove any existing suffixes from the tooltips. // Note this regexp starts with a space, e.g. " (Ctrl-a)" QString strippedTooltip = action->toolTip().remove(QRegExp("\\s\\(.*\\)")); // Now update the tooltips with the new shortcut info. if(action->shortcut() == QKeySequence(0)) action->setToolTip(strippedTooltip); else action->setToolTip( strippedTooltip + " (" + action->shortcut().toString() + ")"); } } } void KisPart::openTemplate(const QUrl &url) { qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); bool ok = document->loadNativeFormat(url.toLocalFile()); document->setModified(false); document->undoStack()->clear(); if (ok) { QString mimeType = KisMimeDatabase::mimeTypeForFile(url.toLocalFile()); // in case this is a open document template remove the -template from the end mimeType.remove( QRegExp( "-template$" ) ); document->setMimeTypeAfterLoading(mimeType); document->resetURL(); } else { if (document->errorMessage().isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1", document->localFilePath())); } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Could not create document from template\n%1\nReason: %2", document->localFilePath(), document->errorMessage())); } delete document; return; } addDocument(document); KisMainWindow *mw = currentMainwindow(); mw->addViewAndNotifyLoadingCompleted(document); KisOpenPane *pane = qobject_cast(sender()); if (pane) { pane->hide(); pane->deleteLater(); } qApp->restoreOverrideCursor(); } void KisPart::addRecentURLToAllMainWindows(QUrl url) { // Add to recent actions list in our mainWindows Q_FOREACH (KisMainWindow *mainWindow, d->mainWindows) { mainWindow->addRecentURL(url); } } void KisPart::startCustomDocument(KisDocument* doc) { addDocument(doc); KisMainWindow *mw = currentMainwindow(); KisOpenPane *pane = qobject_cast(sender()); if (pane) { pane->hide(); pane->deleteLater(); } mw->addViewAndNotifyLoadingCompleted(doc); } KisInputManager* KisPart::currentInputManager() { KisMainWindow *mw = currentMainwindow(); KisViewManager *manager = mw ? mw->viewManager() : 0; return manager ? manager->inputManager() : 0; } diff --git a/libs/ui/KisPart.h b/libs/ui/KisPart.h index b0c71af228..434792e2da 100644 --- a/libs/ui/KisPart.h +++ b/libs/ui/KisPart.h @@ -1,259 +1,248 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2005 David Faure Copyright (C) 2007 Thorsten Zachmann Copyright (C) 2010 Boudewijn Rempt Copyright (C) 2015 Michael Abrahams 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_PART_H #define KIS_PART_H #include #include #include #include "kritaui_export.h" #include #include namespace KIO { } class KisAction; class KisDocument; class KisView; class KisDocument; class KisIdleWatcher; class KisAnimationCachePopulator; /** * KisPart is the Great Deku Tree of Krita. * * It is a singleton class which provides the main entry point to the application. * Krita supports multiple documents, multiple main windows, and multiple * components. KisPart manages these resources and provides them to the rest of * Krita. It manages lists of Actions and shortcuts as well. * * The terminology comes from KParts, which is a system allowing one KDE app * to be run from inside another, like pressing F4 inside dophin to run konsole. * * Needless to say, KisPart hasn't got much to do with KParts anymore. */ class KRITAUI_EXPORT KisPart : public QObject { Q_OBJECT public: static KisPart *instance(); /** * Constructor. * * @param parent may be another KisDocument, or anything else. * Usually passed by KPluginFactory::create. */ explicit KisPart(); /** * Destructor. * * The destructor does not delete any attached KisView objects and it does not * delete the attached widget as returned by widget(). */ ~KisPart() override; // ----------------- Document management ----------------- /** * create an empty document. The document is not automatically registered with the part. */ KisDocument *createDocument() const; /** * Add the specified document to the list of documents this KisPart manages. */ void addDocument(KisDocument *document); /** * @return a list of all documents this part manages */ QList > documents() const; /** * @return number of documents this part manages. */ int documentCount() const; void removeDocument(KisDocument *document); // ----------------- MainWindow management ----------------- /** * Create a new main window. */ KisMainWindow *createMainWindow(); /** * Removes a main window from the list of managed windows. * * This is called by the MainWindow after it finishes its shutdown routine. */ void removeMainWindow(KisMainWindow *mainWindow); /** * @return the list of main windows. */ const QList >& mainWindows() const; /** * @return the number of shells for the main window */ int mainwindowCount() const; void addRecentURLToAllMainWindows(QUrl url); /** * @return the currently active main window. */ KisMainWindow *currentMainwindow() const; - /** - * Add a given action to the list of dynamically defined actions. On creating - * a mainwindow, all these actions will be added to the script manager. - */ - void addScriptAction(KisAction *); - - /** - * Load actions for currently active main window into KisActionRegistry. - */ - void loadActions(); - /** * @return the application-wide KisIdleWatcher. */ KisIdleWatcher *idleWatcher() const; /** * @return the application-wide AnimationCachePopulator. */ KisAnimationCachePopulator *cachePopulator() const; public Q_SLOTS: /** * This slot loads an existing file. * @param url the file to load */ void openExistingFile(const QUrl &url); /** * This slot loads a template and deletes the sender. * @param url the template to load */ void openTemplate(const QUrl &url); /** * @brief startCustomDocument adds the given document to the document list and deletes the sender() * @param doc */ void startCustomDocument(KisDocument *doc); private Q_SLOTS: void updateIdleWatcherConnections(); void updateShortcuts(); Q_SIGNALS: /** * emitted when a new document is opened. (for the idle watcher) */ void documentOpened(const QString &ref); /** * emitted when an old document is closed. (for the idle watcher) */ void documentClosed(const QString &ref); // These signals are for libkis or sketch void sigViewAdded(KisView *view); void sigViewRemoved(KisView *view); void sigDocumentAdded(KisDocument *document); void sigDocumentSaved(const QString &url); void sigDocumentRemoved(const QString &filename); void sigWindowAdded(KisMainWindow *window); public: KisInputManager *currentInputManager(); //------------------ View management ------------------ /** * Create a new view for the document. The view is added to the list of * views, and if the document wasn't known yet, it's registered as well. */ KisView *createView(KisDocument *document, KoCanvasResourceManager *resourceManager, KActionCollection *actionCollection, QWidget *parent); /** * Adds a view to the document. If the part doesn't know yet about * the document, it is registered. * * This calls KisView::updateReadWrite to tell the new view * whether the document is readonly or not. */ void addView(KisView *view); /** * Removes a view of the document. */ void removeView(KisView *view); /** * @return a list of views this document is displayed in */ QList > views() const; /** * @return number of views this document is displayed in */ int viewCount(KisDocument *doc) const; private Q_SLOTS: void slotDocumentSaved(); private: Q_DISABLE_COPY(KisPart) class Private; Private *const d; }; #endif diff --git a/libs/ui/KisViewManager.cpp b/libs/ui/KisViewManager.cpp index 3a129a4536..643857bd6f 100644 --- a/libs/ui/KisViewManager.cpp +++ b/libs/ui/KisViewManager.cpp @@ -1,1417 +1,1407 @@ /* * This file is part of KimageShop^WKrayon^WKrita * * Copyright (c) 1999 Matthias Elter * 1999 Michael Koch * 1999 Carsten Pfeiffer * 2002 Patrick Julien * 2003-2011 Boudewijn Rempt * 2004 Clarence Dang * 2011 José Luis Vergara * 2017 L. E. Segovia * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "KisViewManager.h" #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 "input/kis_input_manager.h" #include "canvas/kis_canvas2.h" #include "canvas/kis_canvas_controller.h" #include "canvas/kis_grid_manager.h" #include "dialogs/kis_dlg_blacklist_cleanup.h" #include "input/kis_input_profile_manager.h" #include "kis_action_manager.h" #include "kis_action.h" #include "kis_canvas_controls_manager.h" #include "kis_canvas_resource_provider.h" #include "kis_composite_progress_proxy.h" #include #include "kis_config.h" #include "kis_config_notifier.h" #include "kis_control_frame.h" #include "kis_coordinates_converter.h" #include "KisDocument.h" #include "kis_favorite_resource_manager.h" #include "kis_filter_manager.h" #include "kis_group_layer.h" #include #include #include "kis_image_manager.h" #include #include "kis_mainwindow_observer.h" #include "kis_mask_manager.h" #include "kis_mimedata.h" #include "kis_mirror_manager.h" #include "kis_node_commands_adapter.h" #include "kis_node.h" #include "kis_node_manager.h" #include "kis_painting_assistants_manager.h" #include #include "kis_paintop_box.h" #include #include "KisPart.h" #include "KisPrintJob.h" #include #include "kis_resource_server_provider.h" #include "kis_selection.h" #include "kis_selection_manager.h" #include "kis_shape_controller.h" #include "kis_shape_layer.h" #include #include "kis_statusbar.h" #include #include #include "kis_tooltip_manager.h" #include #include "KisView.h" #include "kis_zoom_manager.h" #include "widgets/kis_floating_message.h" #include "kis_signal_auto_connection.h" -#include "kis_script_manager.h" #include "kis_icon_utils.h" #include "kis_guides_manager.h" #include "kis_derived_resources.h" #include "dialogs/kis_delayed_save_dialog.h" #include #include #include "kis_signals_blocker.h" class BlockingUserInputEventFilter : public QObject { bool eventFilter(QObject *watched, QEvent *event) override { Q_UNUSED(watched); if(dynamic_cast(event) || dynamic_cast(event) || dynamic_cast(event)) { return true; } else { return false; } } }; class KisViewManager::KisViewManagerPrivate { public: KisViewManagerPrivate(KisViewManager *_q, KActionCollection *_actionCollection, QWidget *_q_parent) : filterManager(_q) , createTemplate(0) , saveIncremental(0) , saveIncrementalBackup(0) , openResourcesDirectory(0) , rotateCanvasRight(0) , rotateCanvasLeft(0) , resetCanvasRotation(0) , wrapAroundAction(0) , levelOfDetailAction(0) , showRulersAction(0) , rulersTrackMouseAction(0) , zoomTo100pct(0) , zoomIn(0) , zoomOut(0) , selectionManager(_q) , statusBar(_q) , controlFrame(_q, _q_parent) , nodeManager(_q) , imageManager(_q) , gridManager(_q) , canvasControlsManager(_q) , paintingAssistantsManager(_q) , actionManager(_q, _actionCollection) , mainWindow(0) , showFloatingMessage(true) , currentImageView(0) , canvasResourceProvider(_q) , canvasResourceManager() , guiUpdateCompressor(30, KisSignalCompressor::POSTPONE, _q) , actionCollection(_actionCollection) , mirrorManager(_q) , inputManager(_q) - , scriptManager(_q) , actionAuthor(0) , showPixelGrid(0) { KisViewManager::initializeResourceManager(&canvasResourceManager); } public: KisFilterManager filterManager; KisAction *createTemplate; KisAction *createCopy; KisAction *saveIncremental; KisAction *saveIncrementalBackup; KisAction *openResourcesDirectory; KisAction *rotateCanvasRight; KisAction *rotateCanvasLeft; KisAction *resetCanvasRotation; KisAction *wrapAroundAction; KisAction *levelOfDetailAction; KisAction *showRulersAction; KisAction *rulersTrackMouseAction; KisAction *zoomTo100pct; KisAction *zoomIn; KisAction *zoomOut; KisAction *softProof; KisAction *gamutCheck; KisSelectionManager selectionManager; KisGuidesManager guidesManager; KisStatusBar statusBar; QPointer persistentImageProgressUpdater; QScopedPointer persistentUnthreadedProgressUpdaterRouter; QPointer persistentUnthreadedProgressUpdater; KisControlFrame controlFrame; KisNodeManager nodeManager; KisImageManager imageManager; KisGridManager gridManager; KisCanvasControlsManager canvasControlsManager; KisPaintingAssistantsManager paintingAssistantsManager; BlockingUserInputEventFilter blockingEventFilter; KisActionManager actionManager; QMainWindow* mainWindow; QPointer savedFloatingMessage; bool showFloatingMessage; QPointer currentImageView; KisCanvasResourceProvider canvasResourceProvider; KoCanvasResourceManager canvasResourceManager; KisSignalCompressor guiUpdateCompressor; KActionCollection *actionCollection; KisMirrorManager mirrorManager; KisInputManager inputManager; KisSignalAutoConnectionsStore viewConnections; - KisScriptManager scriptManager; KSelectAction *actionAuthor; // Select action for author profile. KisAction *showPixelGrid; QByteArray canvasState; #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) QFlags windowFlags; #endif bool blockUntilOperationsFinishedImpl(KisImageSP image, bool force); }; KisViewManager::KisViewManager(QWidget *parent, KActionCollection *_actionCollection) : d(new KisViewManagerPrivate(this, _actionCollection, parent)) { d->actionCollection = _actionCollection; d->mainWindow = dynamic_cast(parent); d->canvasResourceProvider.setResourceManager(&d->canvasResourceManager); connect(&d->guiUpdateCompressor, SIGNAL(timeout()), this, SLOT(guiUpdateTimeout())); createActions(); setupManagers(); // These initialization functions must wait until KisViewManager ctor is complete. d->statusBar.setup(); d->persistentImageProgressUpdater = d->statusBar.progressUpdater()->startSubtask(1, "", true); // reset state to "completed" d->persistentImageProgressUpdater->setRange(0,100); d->persistentImageProgressUpdater->setValue(100); d->persistentUnthreadedProgressUpdater = d->statusBar.progressUpdater()->startSubtask(1, "", true); // reset state to "completed" d->persistentUnthreadedProgressUpdater->setRange(0,100); d->persistentUnthreadedProgressUpdater->setValue(100); d->persistentUnthreadedProgressUpdaterRouter.reset( new KoProgressUpdater(d->persistentUnthreadedProgressUpdater, KoProgressUpdater::Unthreaded)); d->persistentUnthreadedProgressUpdaterRouter->setAutoNestNames(true); d->controlFrame.setup(parent); //Check to draw scrollbars after "Canvas only mode" toggle is created. this->showHideScrollbars(); QScopedPointer dummy(new KoDummyCanvasController(actionCollection())); KoToolManager::instance()->registerToolActions(actionCollection(), dummy.data()); QTimer::singleShot(0, this, SLOT(initializeStatusBarVisibility())); connect(KoToolManager::instance(), SIGNAL(inputDeviceChanged(KoInputDevice)), d->controlFrame.paintopBox(), SLOT(slotInputDeviceChanged(KoInputDevice))); connect(KoToolManager::instance(), SIGNAL(changedTool(KoCanvasController*,int)), d->controlFrame.paintopBox(), SLOT(slotToolChanged(KoCanvasController*,int))); connect(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), resourceProvider(), SLOT(slotNodeActivated(KisNodeSP))); connect(resourceProvider()->resourceManager(), SIGNAL(canvasResourceChanged(int,QVariant)), d->controlFrame.paintopBox(), SLOT(slotCanvasResourceChanged(int,QVariant))); connect(KisPart::instance(), SIGNAL(sigViewAdded(KisView*)), SLOT(slotViewAdded(KisView*))); connect(KisPart::instance(), SIGNAL(sigViewRemoved(KisView*)), SLOT(slotViewRemoved(KisView*))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotUpdateAuthorProfileActions())); connect(KisConfigNotifier::instance(), SIGNAL(pixelGridModeChanged()), SLOT(slotUpdatePixelGridAction())); KisInputProfileManager::instance()->loadProfiles(); KisConfig cfg; d->showFloatingMessage = cfg.showCanvasMessages(); } KisViewManager::~KisViewManager() { KisConfig cfg; if (resourceProvider() && resourceProvider()->currentPreset()) { cfg.writeEntry("LastPreset", resourceProvider()->currentPreset()->name()); cfg.writeKoColor("LastForeGroundColor",resourceProvider()->fgColor()); cfg.writeKoColor("LastBackGroundColor",resourceProvider()->bgColor()); } cfg.writeEntry("baseLength", KoResourceItemChooserSync::instance()->baseLength()); delete d; } void KisViewManager::initializeResourceManager(KoCanvasResourceManager *resourceManager) { resourceManager->addDerivedResourceConverter(toQShared(new KisCompositeOpResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisEffectiveCompositeOpResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisOpacityResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisFlowResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisSizeResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisLodAvailabilityResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisLodSizeThresholdSupportedResourceConverter)); resourceManager->addDerivedResourceConverter(toQShared(new KisEraserModeResourceConverter)); resourceManager->addResourceUpdateMediator(toQShared(new KisPresetUpdateMediator)); } KActionCollection *KisViewManager::actionCollection() const { return d->actionCollection; } void KisViewManager::slotViewAdded(KisView *view) { // WARNING: this slot is called even when a view from another main windows is added! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.showAllStatusBarItems(); } } void KisViewManager::slotViewRemoved(KisView *view) { // WARNING: this slot is called even when a view from another main windows is removed! // Don't expect \p view be a child of this view manager! Q_UNUSED(view); if (viewCount() == 0) { d->statusBar.hideAllStatusBarItems(); } } void KisViewManager::setCurrentView(KisView *view) { bool first = true; if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(false); d->currentImageView->canvasBase()->setCursor(QCursor(Qt::ArrowCursor)); first = false; KisDocument* doc = d->currentImageView->document(); if (doc) { doc->image()->compositeProgressProxy()->removeProxy(d->persistentImageProgressUpdater); doc->disconnect(this); } d->currentImageView->canvasController()->proxyObject->disconnect(&d->statusBar); d->viewConnections.clear(); } QPointer imageView = qobject_cast(view); d->currentImageView = imageView; if (imageView) { d->softProof->setChecked(imageView->softProofing()); d->gamutCheck->setChecked(imageView->gamutCheck()); // Wait for the async image to have loaded KisDocument* doc = view->document(); // connect(canvasController()->proxyObject, SIGNAL(documentMousePositionChanged(QPointF)), d->statusBar, SLOT(documentMousePositionChanged(QPointF))); // Restore the last used brush preset, color and background color. if (first) { KisConfig cfg; KisPaintOpPresetResourceServer * rserver = KisResourceServerProvider::instance()->paintOpPresetServer(); QString defaultPresetName = "basic_tip_default"; bool foundTip = false; for (int i=0; iresourceCount(); i++) { KisPaintOpPresetSP resource = rserver->resources().at(i); if (resource->name().toLower().contains("basic_tip_default")) { defaultPresetName = resource->name(); foundTip = true; } else if (foundTip == false && (resource->name().toLower().contains("default") || resource->filename().toLower().contains("default"))) { defaultPresetName = resource->name(); foundTip = true; } } QString lastPreset = cfg.readEntry("LastPreset", defaultPresetName); KisPaintOpPresetSP preset = rserver->resourceByName(lastPreset); if (!preset) { preset = rserver->resourceByName(defaultPresetName); } if (!preset) { preset = rserver->resources().first(); } if (preset) { paintOpBox()->restoreResource(preset.data()); } const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); KoColor foreground(Qt::black, cs); d->canvasResourceProvider.setFGColor(cfg.readKoColor("LastForeGroundColor",foreground)); KoColor background(Qt::white, cs); d->canvasResourceProvider.setBGColor(cfg.readKoColor("LastBackGroundColor",background)); } KisCanvasController *canvasController = dynamic_cast(d->currentImageView->canvasController()); d->viewConnections.addUniqueConnection(&d->nodeManager, SIGNAL(sigNodeActivated(KisNodeSP)), doc->image(), SLOT(requestStrokeEndActiveNode())); d->viewConnections.addUniqueConnection(d->rotateCanvasRight, SIGNAL(triggered()), canvasController, SLOT(rotateCanvasRight15())); d->viewConnections.addUniqueConnection(d->rotateCanvasLeft, SIGNAL(triggered()),canvasController, SLOT(rotateCanvasLeft15())); d->viewConnections.addUniqueConnection(d->resetCanvasRotation, SIGNAL(triggered()),canvasController, SLOT(resetCanvasRotation())); d->viewConnections.addUniqueConnection(d->wrapAroundAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleWrapAroundMode(bool))); d->wrapAroundAction->setChecked(canvasController->wrapAroundMode()); d->viewConnections.addUniqueConnection(d->levelOfDetailAction, SIGNAL(toggled(bool)), canvasController, SLOT(slotToggleLevelOfDetailMode(bool))); d->levelOfDetailAction->setChecked(canvasController->levelOfDetailMode()); d->viewConnections.addUniqueConnection(d->currentImageView->image(), SIGNAL(sigColorSpaceChanged(const KoColorSpace*)), d->controlFrame.paintopBox(), SLOT(slotColorSpaceChanged(const KoColorSpace*))); d->viewConnections.addUniqueConnection(d->showRulersAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setShowRulers(bool))); d->viewConnections.addUniqueConnection(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), imageView->zoomManager(), SLOT(setRulersTrackMouse(bool))); d->viewConnections.addUniqueConnection(d->zoomTo100pct, SIGNAL(triggered()), imageView->zoomManager(), SLOT(zoomTo100())); d->viewConnections.addUniqueConnection(d->zoomIn, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomIn())); d->viewConnections.addUniqueConnection(d->zoomOut, SIGNAL(triggered()), imageView->zoomController()->zoomAction(), SLOT(zoomOut())); d->viewConnections.addUniqueConnection(d->softProof, SIGNAL(toggled(bool)), view, SLOT(slotSoftProofing(bool)) ); d->viewConnections.addUniqueConnection(d->gamutCheck, SIGNAL(toggled(bool)), view, SLOT(slotGamutCheck(bool)) ); // set up progrress reporting doc->image()->compositeProgressProxy()->addProxy(d->persistentImageProgressUpdater); d->viewConnections.addUniqueConnection(&d->statusBar, SIGNAL(sigCancellationRequested()), doc->image(), SLOT(requestStrokeCancellation())); d->viewConnections.addUniqueConnection(d->showPixelGrid, SIGNAL(toggled(bool)), canvasController, SLOT(slotTogglePixelGrid(bool))); imageView->zoomManager()->setShowRulers(d->showRulersAction->isChecked()); imageView->zoomManager()->setRulersTrackMouse(d->rulersTrackMouseAction->isChecked()); showHideScrollbars(); } d->filterManager.setView(imageView); d->selectionManager.setView(imageView); d->guidesManager.setView(imageView); d->nodeManager.setView(imageView); d->imageManager.setView(imageView); d->canvasControlsManager.setView(imageView); d->actionManager.setView(imageView); d->gridManager.setView(imageView); d->statusBar.setView(imageView); d->paintingAssistantsManager.setView(imageView); d->mirrorManager.setView(imageView); if (d->currentImageView) { d->currentImageView->notifyCurrentStateChanged(true); d->currentImageView->canvasController()->activate(); d->currentImageView->canvasController()->setFocus(); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigSizeChanged(const QPointF&, const QPointF&)), resourceProvider(), SLOT(slotImageSizeChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigResolutionChanged(double,double)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); d->viewConnections.addUniqueConnection( image(), SIGNAL(sigNodeChanged(KisNodeSP)), this, SLOT(updateGUI())); d->viewConnections.addUniqueConnection( d->currentImageView->zoomManager()->zoomController(), SIGNAL(zoomChanged(KoZoomMode::Mode,qreal)), resourceProvider(), SLOT(slotOnScreenResolutionChanged())); } d->actionManager.updateGUI(); resourceProvider()->slotImageSizeChanged(); resourceProvider()->slotOnScreenResolutionChanged(); Q_EMIT viewChanged(); } KoZoomController *KisViewManager::zoomController() const { if (d->currentImageView) { return d->currentImageView->zoomController(); } return 0; } KisImageWSP KisViewManager::image() const { if (document()) { return document()->image(); } return 0; } KisCanvasResourceProvider * KisViewManager::resourceProvider() { return &d->canvasResourceProvider; } KisCanvas2 * KisViewManager::canvasBase() const { if (d && d->currentImageView) { return d->currentImageView->canvasBase(); } return 0; } QWidget* KisViewManager::canvas() const { if (d && d->currentImageView && d->currentImageView->canvasBase()->canvasWidget()) { return d->currentImageView->canvasBase()->canvasWidget(); } return 0; } KisStatusBar * KisViewManager::statusBar() const { return &d->statusBar; } void KisViewManager::addStatusBarItem(QWidget *widget, int stretch, bool permanent) { d->statusBar.addStatusBarItem(widget, stretch, permanent); } void KisViewManager::removeStatusBarItem(QWidget *widget) { d->statusBar.removeStatusBarItem(widget); } KisPaintopBox* KisViewManager::paintOpBox() const { return d->controlFrame.paintopBox(); } QPointer KisViewManager::createUnthreadedUpdater(const QString &name) { return d->persistentUnthreadedProgressUpdaterRouter->startSubtask(1, name, false); } QPointer KisViewManager::createThreadedUpdater(const QString &name) { return d->statusBar.progressUpdater()->startSubtask(1, name, false); } KisSelectionManager * KisViewManager::selectionManager() { return &d->selectionManager; } KisNodeSP KisViewManager::activeNode() { return d->nodeManager.activeNode(); } KisLayerSP KisViewManager::activeLayer() { return d->nodeManager.activeLayer(); } KisPaintDeviceSP KisViewManager::activeDevice() { return d->nodeManager.activePaintDevice(); } KisZoomManager * KisViewManager::zoomManager() { if (d->currentImageView) { return d->currentImageView->zoomManager(); } return 0; } KisFilterManager * KisViewManager::filterManager() { return &d->filterManager; } KisImageManager * KisViewManager::imageManager() { return &d->imageManager; } KisInputManager* KisViewManager::inputManager() const { return &d->inputManager; } KisSelectionSP KisViewManager::selection() { if (d->currentImageView) { return d->currentImageView->selection(); } return 0; } bool KisViewManager::selectionEditable() { KisLayerSP layer = activeLayer(); if (layer) { KoProperties properties; QList masks = layer->childNodes(QStringList("KisSelectionMask"), properties); if (masks.size() == 1) { return masks[0]->isEditable(); } } // global selection is always editable return true; } KisUndoAdapter * KisViewManager::undoAdapter() { if (!document()) return 0; KisImageWSP image = document()->image(); Q_ASSERT(image); return image->undoAdapter(); } void KisViewManager::createActions() { KisConfig cfg; d->saveIncremental = actionManager()->createAction("save_incremental_version"); connect(d->saveIncremental, SIGNAL(triggered()), this, SLOT(slotSaveIncremental())); d->saveIncrementalBackup = actionManager()->createAction("save_incremental_backup"); connect(d->saveIncrementalBackup, SIGNAL(triggered()), this, SLOT(slotSaveIncrementalBackup())); connect(mainWindow(), SIGNAL(documentSaved()), this, SLOT(slotDocumentSaved())); d->saveIncremental->setEnabled(false); d->saveIncrementalBackup->setEnabled(false); KisAction *tabletDebugger = actionManager()->createAction("tablet_debugger"); connect(tabletDebugger, SIGNAL(triggered()), this, SLOT(toggleTabletLogger())); d->createTemplate = actionManager()->createAction("create_template"); connect(d->createTemplate, SIGNAL(triggered()), this, SLOT(slotCreateTemplate())); d->createCopy = actionManager()->createAction("create_copy"); connect(d->createCopy, SIGNAL(triggered()), this, SLOT(slotCreateCopy())); d->openResourcesDirectory = actionManager()->createAction("open_resources_directory"); connect(d->openResourcesDirectory, SIGNAL(triggered()), SLOT(openResourcesDirectory())); d->rotateCanvasRight = actionManager()->createAction("rotate_canvas_right"); d->rotateCanvasLeft = actionManager()->createAction("rotate_canvas_left"); d->resetCanvasRotation = actionManager()->createAction("reset_canvas_rotation"); d->wrapAroundAction = actionManager()->createAction("wrap_around_mode"); d->levelOfDetailAction = actionManager()->createAction("level_of_detail_mode"); d->softProof = actionManager()->createAction("softProof"); d->gamutCheck = actionManager()->createAction("gamutCheck"); KisAction *tAction = actionManager()->createAction("showStatusBar"); tAction->setChecked(cfg.showStatusBar()); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(showStatusBar(bool))); tAction = actionManager()->createAction("view_show_canvas_only"); tAction->setChecked(false); connect(tAction, SIGNAL(toggled(bool)), this, SLOT(switchCanvasOnly(bool))); //Workaround, by default has the same shortcut as mirrorCanvas KisAction *a = dynamic_cast(actionCollection()->action("format_italic")); if (a) { a->setDefaultShortcut(QKeySequence()); } a = actionManager()->createAction("edit_blacklist_cleanup"); connect(a, SIGNAL(triggered()), this, SLOT(slotBlacklistCleanup())); actionManager()->createAction("ruler_pixel_multiple2"); d->showRulersAction = actionManager()->createAction("view_ruler"); d->showRulersAction->setChecked(cfg.showRulers()); connect(d->showRulersAction, SIGNAL(toggled(bool)), SLOT(slotSaveShowRulersState(bool))); d->rulersTrackMouseAction = actionManager()->createAction("rulers_track_mouse"); d->rulersTrackMouseAction->setChecked(cfg.rulersTrackMouse()); connect(d->rulersTrackMouseAction, SIGNAL(toggled(bool)), SLOT(slotSaveRulersTrackMouseState(bool))); d->zoomTo100pct = actionManager()->createAction("zoom_to_100pct"); d->zoomIn = actionManager()->createStandardAction(KStandardAction::ZoomIn, 0, ""); d->zoomOut = actionManager()->createStandardAction(KStandardAction::ZoomOut, 0, ""); d->actionAuthor = new KSelectAction(KisIconUtils::loadIcon("im-user"), i18n("Active Author Profile"), this); connect(d->actionAuthor, SIGNAL(triggered(const QString &)), this, SLOT(changeAuthorProfile(const QString &))); actionCollection()->addAction("settings_active_author", d->actionAuthor); slotUpdateAuthorProfileActions(); d->showPixelGrid = actionManager()->createAction("view_pixel_grid"); slotUpdatePixelGridAction(); } void KisViewManager::setupManagers() { // Create the managers for filters, selections, layers etc. // XXX: When the currentlayer changes, call updateGUI on all // managers d->filterManager.setup(actionCollection(), actionManager()); d->selectionManager.setup(actionManager()); d->guidesManager.setup(actionManager()); d->nodeManager.setup(actionCollection(), actionManager()); d->imageManager.setup(actionManager()); d->gridManager.setup(actionManager()); d->paintingAssistantsManager.setup(actionManager()); d->canvasControlsManager.setup(actionManager()); d->mirrorManager.setup(actionCollection()); - - d->scriptManager.setup(actionCollection(), actionManager()); } void KisViewManager::updateGUI() { d->guiUpdateCompressor.start(); } void KisViewManager::slotBlacklistCleanup() { KisDlgBlacklistCleanup dialog; dialog.exec(); } KisNodeManager * KisViewManager::nodeManager() const { return &d->nodeManager; } KisActionManager* KisViewManager::actionManager() const { return &d->actionManager; } KisGridManager * KisViewManager::gridManager() const { return &d->gridManager; } KisGuidesManager * KisViewManager::guidesManager() const { return &d->guidesManager; } KisDocument *KisViewManager::document() const { if (d->currentImageView && d->currentImageView->document()) { return d->currentImageView->document(); } return 0; } -KisScriptManager *KisViewManager::scriptManager() const -{ - return &d->scriptManager; -} - int KisViewManager::viewCount() const { KisMainWindow *mw = qobject_cast(d->mainWindow); if (mw) { return mw->viewCount(); } return 0; } bool KisViewManager::KisViewManagerPrivate::blockUntilOperationsFinishedImpl(KisImageSP image, bool force) { const int busyWaitDelay = 1000; KisDelayedSaveDialog dialog(image, !force ? KisDelayedSaveDialog::GeneralDialog : KisDelayedSaveDialog::ForcedDialog, busyWaitDelay, mainWindow); dialog.blockIfImageIsBusy(); return dialog.result() == QDialog::Accepted; } bool KisViewManager::blockUntilOperationsFinished(KisImageSP image) { return d->blockUntilOperationsFinishedImpl(image, false); } void KisViewManager::blockUntilOperationsFinishedForced(KisImageSP image) { d->blockUntilOperationsFinishedImpl(image, true); } void KisViewManager::slotCreateTemplate() { if (!document()) return; KisTemplateCreateDia::createTemplate( QStringLiteral("templates/"), ".kra", document(), mainWindow()); } void KisViewManager::slotCreateCopy() { KisDocument *srcDoc = document(); if (!srcDoc) return; if (!this->blockUntilOperationsFinished(srcDoc->image())) return; KisDocument *doc = 0; { KisImageBarrierLocker l(srcDoc->image()); doc = srcDoc->clone(); } KIS_SAFE_ASSERT_RECOVER_RETURN(doc); QString name = srcDoc->documentInfo()->aboutInfo("name"); if (name.isEmpty()) { name = document()->url().toLocalFile(); } name = i18n("%1 (Copy)", name); doc->documentInfo()->setAboutInfo("title", name); KisPart::instance()->addDocument(doc); KisMainWindow *mw = qobject_cast(d->mainWindow); mw->addViewAndNotifyLoadingCompleted(doc); } QMainWindow* KisViewManager::qtMainWindow() const { if (d->mainWindow) return d->mainWindow; //Fallback for when we have not yet set the main window. QMainWindow* w = qobject_cast(qApp->activeWindow()); if(w) return w; return mainWindow(); } void KisViewManager::setQtMainWindow(QMainWindow* newMainWindow) { d->mainWindow = newMainWindow; } void KisViewManager::slotDocumentSaved() { d->saveIncremental->setEnabled(true); d->saveIncrementalBackup->setEnabled(true); } void KisViewManager::slotSaveIncremental() { if (!document()) return; if (document()->url().isEmpty()) { KisMainWindow *mw = qobject_cast(d->mainWindow); mw->saveDocument(document(), true, false); return; } bool foundVersion; bool fileAlreadyExists; bool isBackup; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // Find current version filenames // v v Regexp to find incremental versions in the filename, taking our backup scheme into account as well // Considering our incremental version and backup scheme, format is filename_001~001.ext QRegExp regex("_\\d{1,4}[.]|_\\d{1,4}[a-z][.]|_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); foundVersion = matches.at(0).isEmpty() ? false : true; // Ensure compatibility with Save Incremental Backup // If this regex is not kept separate, the entire algorithm needs modification; // It's simpler to just add this. QRegExp regexAux("_\\d{1,4}[~]|_\\d{1,4}[a-z][~]"); regexAux.indexIn(fileName); // Perform the search QStringList matchesAux = regexAux.capturedTexts(); isBackup = matchesAux.at(0).isEmpty() ? false : true; // If the filename has a version, prepare it for incrementation if (foundVersion) { version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "_" } else { // TODO: this will not work with files extensions like jp2 // ...else, simply add a version to it so the next loop works QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(fileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(version); extensionPlusVersion.prepend("_"); fileName.replace(regex2, extensionPlusVersion); } // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("_"); if (!letter.isNull()) newVersion.append(letter); if (isBackup) { newVersion.append("~"); } else { newVersion.append("."); } fileName.replace(regex, newVersion); fileAlreadyExists = QFile(fileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental version"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } document()->setFileBatchMode(true); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) { mainWindow()->updateCaption(); } } void KisViewManager::slotSaveIncrementalBackup() { if (!document()) return; if (document()->url().isEmpty()) { KisMainWindow *mw = qobject_cast(d->mainWindow); mw->saveDocument(document(), true, false); return; } bool workingOnBackup; bool fileAlreadyExists; QString version = "000"; QString newVersion; QString letter; QString fileName = document()->localFilePath(); // First, discover if working on a backup file, or a normal file QRegExp regex("~\\d{1,4}[.]|~\\d{1,4}[a-z][.]"); regex.indexIn(fileName); // Perform the search QStringList matches = regex.capturedTexts(); workingOnBackup = matches.at(0).isEmpty() ? false : true; if (workingOnBackup) { // Try to save incremental version (of backup), use letter for alt versions version = matches.at(matches.count() - 1); // Look at the last index, we don't care about other matches if (version.contains(QRegExp("[a-z]"))) { version.chop(1); // Trim "." letter = version.right(1); // Save letter version.chop(1); // Trim letter } else { version.chop(1); // Trim "." } version.remove(0, 1); // Trim "~" // Prepare the base for new version filename int intVersion = version.toInt(0); ++intVersion; QString baseNewVersion = QString::number(intVersion); QString backupFileName = document()->localFilePath(); while (baseNewVersion.length() < version.length()) { baseNewVersion.prepend("0"); } // Check if the file exists under the new name and search until options are exhausted (test appending a to z) do { newVersion = baseNewVersion; newVersion.prepend("~"); if (!letter.isNull()) newVersion.append(letter); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { if (!letter.isNull()) { char letterCh = letter.at(0).toLatin1(); ++letterCh; letter = QString(QChar(letterCh)); } else { letter = 'a'; } } } while (fileAlreadyExists && letter != "{"); // x, y, z, {... if (letter == "{") { QMessageBox::critical(mainWindow(), i18nc("@title:window", "Couldn't save incremental backup"), i18n("Alternative names exhausted, try manually saving with a higher number")); return; } QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); if (mainWindow()) mainWindow()->updateCaption(); } else { // if NOT working on a backup... // Navigate directory searching for latest backup version, ignore letters const quint8 HARDCODED_DIGIT_COUNT = 3; QString baseNewVersion = "000"; QString backupFileName = document()->localFilePath(); QRegExp regex2("[.][a-z]{2,4}$"); // Heuristic to find file extension regex2.indexIn(backupFileName); QStringList matches2 = regex2.capturedTexts(); QString extensionPlusVersion = matches2.at(0); extensionPlusVersion.prepend(baseNewVersion); extensionPlusVersion.prepend("~"); backupFileName.replace(regex2, extensionPlusVersion); // Save version with 1 number higher than the highest version found ignoring letters do { newVersion = baseNewVersion; newVersion.prepend("~"); newVersion.append("."); backupFileName.replace(regex, newVersion); fileAlreadyExists = QFile(backupFileName).exists(); if (fileAlreadyExists) { // Prepare the base for new version filename, increment by 1 int intVersion = baseNewVersion.toInt(0); ++intVersion; baseNewVersion = QString::number(intVersion); while (baseNewVersion.length() < HARDCODED_DIGIT_COUNT) { baseNewVersion.prepend("0"); } } } while (fileAlreadyExists); // Save both as backup and on current file for interapplication workflow document()->setFileBatchMode(true); QFile::copy(fileName, backupFileName); document()->saveAs(QUrl::fromUserInput(fileName), document()->mimeType(), true); document()->setFileBatchMode(false); if (mainWindow()) mainWindow()->updateCaption(); } } void KisViewManager::disableControls() { // prevents possible crashes, if somebody changes the paintop during dragging by using the mousewheel // this is for Bug 250944 // the solution blocks all wheel, mouse and key event, while dragging with the freehand tool // see KisToolFreehand::initPaint() and endPaint() d->controlFrame.paintopBox()->installEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->installEventFilter(&d->blockingEventFilter); } } void KisViewManager::enableControls() { d->controlFrame.paintopBox()->removeEventFilter(&d->blockingEventFilter); Q_FOREACH (QObject* child, d->controlFrame.paintopBox()->children()) { child->removeEventFilter(&d->blockingEventFilter); } } void KisViewManager::showStatusBar(bool toggled) { KisMainWindow *mw = mainWindow(); if(mw && mw->statusBar()) { mw->statusBar()->setVisible(toggled); KisConfig cfg; cfg.setShowStatusBar(toggled); } } void KisViewManager::switchCanvasOnly(bool toggled) { KisConfig cfg; KisMainWindow* main = mainWindow(); if(!main) { dbgUI << "Unable to switch to canvas-only mode, main window not found"; return; } if (toggled) { d->canvasState = qtMainWindow()->saveState(); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) d->windowFlags = main->windowState(); #endif } if (cfg.hideStatusbarFullscreen()) { if (main->statusBar()) { if (!toggled) { if (main->statusBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->statusBar()->property("wasvisible").toBool()) { main->statusBar()->setVisible(true); } } } else { main->statusBar()->setProperty("wasvisible", main->statusBar()->isVisible()); main->statusBar()->setVisible(false); } } } if (cfg.hideDockersFullscreen()) { KisAction* action = qobject_cast(main->actionCollection()->action("view_toggledockers")); if (action) { action->setCheckable(true); if (toggled) { if (action->isChecked()) { cfg.setShowDockers(action->isChecked()); action->setChecked(false); } else { cfg.setShowDockers(false); } } else { action->setChecked(cfg.showDockers()); } } } // QT in windows does not return to maximized upon 4th tab in a row // https://bugreports.qt.io/browse/QTBUG-57882, https://bugreports.qt.io/browse/QTBUG-52555, https://codereview.qt-project.org/#/c/185016/ if (cfg.hideTitlebarFullscreen() && !cfg.fullscreenMode()) { if(toggled) { main->setWindowState( main->windowState() | Qt::WindowFullScreen); } else { main->setWindowState( main->windowState() & ~Qt::WindowFullScreen); #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) // If window was maximized prior to fullscreen, restore that if (d->windowFlags & Qt::WindowMaximized) { main->setWindowState( main->windowState() | Qt::WindowMaximized); } #endif } } if (cfg.hideMenuFullscreen()) { if (!toggled) { if (main->menuBar()->dynamicPropertyNames().contains("wasvisible")) { if (main->menuBar()->property("wasvisible").toBool()) { main->menuBar()->setVisible(true); } } } else { main->menuBar()->setProperty("wasvisible", main->menuBar()->isVisible()); main->menuBar()->setVisible(false); } } if (cfg.hideToolbarFullscreen()) { QList toolBars = main->findChildren(); Q_FOREACH (QToolBar* toolbar, toolBars) { if (!toggled) { if (toolbar->dynamicPropertyNames().contains("wasvisible")) { if (toolbar->property("wasvisible").toBool()) { toolbar->setVisible(true); } } } else { toolbar->setProperty("wasvisible", toolbar->isVisible()); toolbar->setVisible(false); } } } showHideScrollbars(); if (toggled) { // show a fading heads-up display about the shortcut to go back showFloatingMessage(i18n("Going into Canvas-Only mode.\nPress %1 to go back.", actionCollection()->action("view_show_canvas_only")->shortcut().toString()), QIcon()); } else { main->restoreState(d->canvasState); } } void KisViewManager::toggleTabletLogger() { d->inputManager.toggleTabletLogger(); } void KisViewManager::openResourcesDirectory() { QString dir = KoResourcePaths::locateLocal("data", ""); QDesktopServices::openUrl(QUrl::fromLocalFile(dir)); } void KisViewManager::updateIcons() { if (mainWindow()) { QList dockers = mainWindow()->dockWidgets(); Q_FOREACH (QDockWidget* dock, dockers) { dbgKrita << "name " << dock->objectName(); KoDockWidgetTitleBar* titlebar = dynamic_cast(dock->titleBarWidget()); if (titlebar) { titlebar->updateIcons(); } QObjectList objects; objects.append(dock); while (!objects.isEmpty()) { QObject* object = objects.takeFirst(); objects.append(object->children()); KisIconUtils::updateIconCommon(object); } } } } void KisViewManager::initializeStatusBarVisibility() { KisConfig cfg; d->mainWindow->statusBar()->setVisible(cfg.showStatusBar()); } void KisViewManager::guiUpdateTimeout() { d->nodeManager.updateGUI(); d->selectionManager.updateGUI(); d->filterManager.updateGUI(); if (zoomManager()) { zoomManager()->updateGUI(); } d->gridManager.updateGUI(); d->actionManager.updateGUI(); } void KisViewManager::showFloatingMessage(const QString &message, const QIcon& icon, int timeout, KisFloatingMessage::Priority priority, int alignment) { if (!d->currentImageView) return; d->currentImageView->showFloatingMessageImpl(message, icon, timeout, priority, alignment); emit floatingMessageRequested(message, icon.name()); } KisMainWindow *KisViewManager::mainWindow() const { return qobject_cast(d->mainWindow); } void KisViewManager::showHideScrollbars() { if (!d->currentImageView) return; if (!d->currentImageView->canvasController()) return; KisConfig cfg; bool toggled = actionCollection()->action("view_show_canvas_only")->isChecked(); if ( (toggled && cfg.hideScrollbarsFullscreen()) || (!toggled && cfg.hideScrollbars()) ) { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); } else { d->currentImageView->canvasController()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); d->currentImageView->canvasController()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } } void KisViewManager::slotSaveShowRulersState(bool value) { KisConfig cfg; cfg.setShowRulers(value); } void KisViewManager::slotSaveRulersTrackMouseState(bool value) { KisConfig cfg; cfg.setRulersTrackMouse(value); } void KisViewManager::setShowFloatingMessage(bool show) { d->showFloatingMessage = show; } void KisViewManager::changeAuthorProfile(const QString &profileName) { KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); if (profileName.isEmpty() || profileName == i18nc("choice for author profile", "Anonymous")) { appAuthorGroup.writeEntry("active-profile", ""); } else { appAuthorGroup.writeEntry("active-profile", profileName); } appAuthorGroup.sync(); Q_FOREACH (KisDocument *doc, KisPart::instance()->documents()) { doc->documentInfo()->updateParameters(); } } void KisViewManager::slotUpdateAuthorProfileActions() { Q_ASSERT(d->actionAuthor); if (!d->actionAuthor) { return; } d->actionAuthor->clear(); d->actionAuthor->addAction(i18nc("choice for author profile", "Anonymous")); KConfigGroup authorGroup(KoGlobal::calligraConfig(), "Author"); QStringList profiles = authorGroup.readEntry("profile-names", QStringList()); QString authorInfo = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/authorinfo/"; QStringList filters = QStringList() << "*.authorinfo"; QDir dir(authorInfo); Q_FOREACH(QString entry, dir.entryList(filters)) { int ln = QString(".authorinfo").size(); entry.chop(ln); if (!profiles.contains(entry)) { profiles.append(entry); } } Q_FOREACH (const QString &profile , profiles) { d->actionAuthor->addAction(profile); } KConfigGroup appAuthorGroup(KoGlobal::calligraConfig(), "Author"); QString profileName = appAuthorGroup.readEntry("active-profile", ""); if (profileName == "anonymous" || profileName.isEmpty()) { d->actionAuthor->setCurrentItem(0); } else if (profiles.contains(profileName)) { d->actionAuthor->setCurrentAction(profileName); } } void KisViewManager::slotUpdatePixelGridAction() { KIS_SAFE_ASSERT_RECOVER_RETURN(d->showPixelGrid); KisSignalsBlocker b(d->showPixelGrid); KisConfig cfg; d->showPixelGrid->setChecked(cfg.pixelGridEnabled()); } diff --git a/libs/ui/KisViewManager.h b/libs/ui/KisViewManager.h index 98c90d265b..861916e52f 100644 --- a/libs/ui/KisViewManager.h +++ b/libs/ui/KisViewManager.h @@ -1,274 +1,271 @@ /* * Copyright (c) 2006 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_GUI_CLIENT_H #define KIS_GUI_CLIENT_H #include #include #include #include #include #include #include #include "kis_floating_message.h" class QPoint; class KisView; class KisCanvas2; class KisCanvasResourceProvider; class KisDocument; class KisFilterManager; class KisGridManager; class KisGuidesManager; class KisImageManager; class KisNodeManager; class KisPaintingAssistantsManager; class KisPaintopBox; class KisSelectionManager; class KisStatusBar; class KisUndoAdapter; class KisZoomManager; class KisPaintopBox; class KisActionManager; -class KisScriptManager; class KisInputManager; class KoUpdater; class KoProgressUpdater; /** * KisViewManager manages the collection of views shown in a single mainwindow. */ class KRITAUI_EXPORT KisViewManager : public QObject { Q_OBJECT public: /** * Construct a new view on the krita document. * @param document the document we show. * @param parent a parent widget we show ourselves in. */ KisViewManager(QWidget *parent, KActionCollection *actionCollection); ~KisViewManager() override; /** * Retrieves the entire action collection. */ virtual KActionCollection* actionCollection() const; public: // Krita specific interfaces void setCurrentView(KisView *view); /// Return the image this view is displaying KisImageWSP image() const; KoZoomController *zoomController() const; /// The resource provider contains all per-view settings, such as /// current color, current paint op etc. KisCanvasResourceProvider *resourceProvider(); /// Return the canvasbase class KisCanvas2 *canvasBase() const; /// Return the actual widget that is displaying the current image QWidget* canvas() const; /// Return the wrapper class around the statusbar KisStatusBar *statusBar() const; /** * This adds a widget to the statusbar for this view. * If you use this method instead of using statusBar() directly, * KisView will take care of removing the items when the view GUI is deactivated * and readding them when it is reactivated. * The parameters are the same as QStatusBar::addWidget(). */ void addStatusBarItem(QWidget * widget, int stretch = 0, bool permanent = false); /** * Remove a widget from the statusbar for this view. */ void removeStatusBarItem(QWidget * widget); KisPaintopBox* paintOpBox() const; /// create a new progress updater QPointer createUnthreadedUpdater(const QString &name); QPointer createThreadedUpdater(const QString &name); /// The selection manager handles everything action related to /// selections. KisSelectionManager *selectionManager(); /// The node manager handles everything about nodes KisNodeManager *nodeManager() const; KisActionManager *actionManager() const; /** * Convenience method to get at the active node, which may be * a layer or a mask or a selection */ KisNodeSP activeNode(); /// Convenience method to get at the active layer KisLayerSP activeLayer(); /// Convenience method to get at the active paint device KisPaintDeviceSP activeDevice(); /// The filtermanager handles everything action-related to filters KisFilterManager *filterManager(); /// The image manager handles everything action-related to the /// current image KisImageManager *imageManager(); /// Filters events and sends them to canvas actions KisInputManager *inputManager() const; /// Convenience method to get at the active selection (the /// selection of the current layer, or, if that does not exist, /// the global selection. KisSelectionSP selection(); /// Checks if the current global or local selection is editable bool selectionEditable(); /// The undo adapter is used to add commands to the undo stack KisUndoAdapter *undoAdapter(); KisDocument *document() const; - KisScriptManager *scriptManager() const; - int viewCount() const; /** * @brief blockUntilOperationsFinished blocks the GUI of the application until execution * of actions on \p image is finished * @param image the image which we should wait for * @return true if the image has finished execution of the actions, false if * the user cancelled operation */ bool blockUntilOperationsFinished(KisImageSP image); /** * @brief blockUntilOperationsFinished blocks the GUI of the application until execution * of actions on \p image is finished. Does *not* provide a "Cancel" button. So the * user is forced to wait. * @param image the image which we should wait for */ void blockUntilOperationsFinishedForced(KisImageSP image); public: KisGridManager * gridManager() const; KisGuidesManager * guidesManager() const; /// disable and enable toolbar controls. used for disabling them during painting. void enableControls(); void disableControls(); /// shows a floating message in the top right corner of the canvas void showFloatingMessage(const QString &message, const QIcon& icon, int timeout = 4500, KisFloatingMessage::Priority priority = KisFloatingMessage::Medium, int alignment = Qt::AlignCenter | Qt::TextWordWrap); /// @return the KoMaindow this view is in, or 0 KisMainWindow *mainWindow() const; /// The QMainWindow associated with this view. This is most likely going to be shell(), but /// when running as Gemini or Sketch, this will be set to the applications' own QMainWindow. /// This can be checked by qobject_casting to KisMainWindow to check the difference. QMainWindow* qtMainWindow() const; /// The mainWindow function will return the shell() value, unless this function is called /// with a non-null value. To make it return shell() again, simply pass null to this function. void setQtMainWindow(QMainWindow* newMainWindow); static void initializeResourceManager(KoCanvasResourceManager *resourceManager); public Q_SLOTS: void switchCanvasOnly(bool toggled); void setShowFloatingMessage(bool show); void showHideScrollbars(); /// Visit all managers to update gui elements, e.g. enable / disable actions. /// This is heavy-duty call, so it uses a compressor. void updateGUI(); /// Update the style of all the icons void updateIcons(); void slotViewAdded(KisView *view); void slotViewRemoved(KisView *view); Q_SIGNALS: void floatingMessageRequested(const QString &message, const QString &iconName); /** * @brief viewChanged * sent out when the view has changed. */ void viewChanged(); private Q_SLOTS: void slotBlacklistCleanup(); void slotCreateTemplate(); void slotCreateCopy(); void slotDocumentSaved(); void slotSaveIncremental(); void slotSaveIncrementalBackup(); void showStatusBar(bool toggled); void toggleTabletLogger(); void openResourcesDirectory(); void initializeStatusBarVisibility(); void guiUpdateTimeout(); void changeAuthorProfile(const QString &profileName); void slotUpdateAuthorProfileActions(); void slotUpdatePixelGridAction(); void slotSaveShowRulersState(bool value); void slotSaveRulersTrackMouseState(bool value); private: void createActions(); void setupManagers(); /// The zoommanager handles everything action-related to zooming KisZoomManager * zoomManager(); private: class KisViewManagerPrivate; KisViewManagerPrivate * const d; }; #endif diff --git a/libs/ui/kis_script_manager.cpp b/libs/ui/kis_script_manager.cpp deleted file mode 100644 index a93d544a5a..0000000000 --- a/libs/ui/kis_script_manager.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2014 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_script_manager.h" - -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include "KisViewManager.h" - -struct KisScriptManager::Private { - Private() - : actionCollection(0) - , actionManager(0) - , viewManager(0) - , scriptMenu(0) - { - } - - KActionCollection *actionCollection; - KisActionManager *actionManager; - KisViewManager *viewManager; - KActionMenu *scriptMenu; - KActionMenu *hiddenMenu; -}; - - -KisScriptManager::KisScriptManager(KisViewManager *view) - : QObject(view) - , d(new Private()) -{ - d->viewManager = view; -} - -KisScriptManager::~KisScriptManager() -{ - delete d; -} - - -void KisScriptManager::setup(KActionCollection * ac, KisActionManager *actionManager) -{ - d->actionCollection = ac; - d->actionManager = actionManager; - - d->scriptMenu = new KActionMenu(i18n("Scripts"), this); - d->actionCollection->addAction("scripts", d->scriptMenu); - d->hiddenMenu = new KActionMenu("hidden", this); -} - -void KisScriptManager::updateGUI() -{ - if (!d->viewManager) return; -} - -void KisScriptManager::addAction(KisAction *action) -{ - d->actionManager->addAction(action->objectName(), action); - if (action->property("menu").toString() != "None") { - // XXX: find the right menu and add it there. - d->scriptMenu->addAction(action); - } - else { - d->hiddenMenu->addAction(action); - action->setShortcutContext(Qt::ApplicationShortcut); - } -} diff --git a/libs/ui/kis_script_manager.h b/libs/ui/kis_script_manager.h deleted file mode 100644 index b1099473e9..0000000000 --- a/libs/ui/kis_script_manager.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2014 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef KIS_SCRIPT_MANAGER_H -#define KIS_SCRIPT_MANAGER_H - -#include - -#include - -class KisAction; - -class KisActionManager; -class KisViewManager; -class KActionCollection; - -/** - * @brief The KisScriptManager class is responsible for adding scripts to the menu - */ -class KRITAUI_EXPORT KisScriptManager : public QObject -{ - Q_OBJECT -public: - explicit KisScriptManager(KisViewManager * view); - ~KisScriptManager() override; - - void setup(KActionCollection * ac, KisActionManager *actionManager); - void updateGUI(); - - void addAction(KisAction *action); - -private: - struct Private; - Private * const d; -}; - -#endif // KIS_SCRIPT_MANAGER_H diff --git a/plugins/extensions/pykrita/plugin/plugin.cpp b/plugins/extensions/pykrita/plugin/plugin.cpp index 0bf434d1e7..f97c9f6045 100644 --- a/plugins/extensions/pykrita/plugin/plugin.cpp +++ b/plugins/extensions/pykrita/plugin/plugin.cpp @@ -1,95 +1,95 @@ /* * Copyright (c) 2014 Boudewijn Rempt (boud@valdyas.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plugin.h" #include #include #include #include #include "pyqtpluginsettings.h" #include K_PLUGIN_FACTORY_WITH_JSON(KritaPyQtPluginFactory, "kritapykrita.json", registerPlugin();) KritaPyQtPlugin::KritaPyQtPlugin(QObject *parent, const QVariantList &) : KisActionPlugin(parent) , m_autoReload(false) { qDebug() << "Loading Python plugin"; PyKrita::InitResult initResult = PyKrita::initialize(); switch (initResult) { case PyKrita::INIT_OK: break; case PyKrita::INIT_CANNOT_LOAD_PYTHON_LIBRARY: qWarning() << i18n("Cannot load Python library"); return; case PyKrita::INIT_CANNOT_SET_PYTHON_PATHS: qWarning() << i18n("Cannot set Python paths"); return; case PyKrita::INIT_CANNOT_LOAD_PYKRITA_MODULE: qWarning() << i18n("Cannot load built-in pykrita module"); return; default: qWarning() << i18n("Unexpected error initializing python plugin."); return; } pluginManager = PyKrita::pluginManager(); KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); PyQtPluginSettingsFactory* settingsFactory = new PyQtPluginSettingsFactory(pluginManager); //load and save preferences //if something in kritarc is missing, then the default from this load function will be used and saved back to kconfig. //this way, cfg.readEntry() in any part won't be able to set its own default KisPreferenceSet* settings = settingsFactory->createPreferenceSet(); KIS_SAFE_ASSERT_RECOVER_RETURN(settings); settings->loadPreferences(); settings->savePreferences(); delete settings; preferenceSetRegistry->add("PyQtPluginSettingsFactory", settingsFactory); // Try to import the `pykrita` module PyKrita::Python py = PyKrita::Python(); PyObject* pykritaPackage = py.moduleImport("pykrita"); pykritaPackage = py.moduleImport("krita"); if (pykritaPackage) { dbgScript << "Loaded pykrita, now load plugins"; pluginManager->scanPlugins(); pluginManager->tryLoadEnabledPlugins(); //py.functionCall("_pykritaLoaded", PyKrita::Python::PYKRITA_ENGINE); } else { dbgScript << "Cannot load pykrita module"; } - Q_FOREACH (Extension* ext, Krita::instance()->extensions()) { - ext->setup(); + Q_FOREACH (Extension *extension, Krita::instance()->extensions()) { + extension->setup(); } } KritaPyQtPlugin::~KritaPyQtPlugin() { PyKrita::finalize(); } #include "plugin.moc" diff --git a/plugins/extensions/pykrita/sip/krita/Action.sip b/plugins/extensions/pykrita/sip/krita/Action.sip index 28a2b4e51d..f5f1e1c4bb 100644 --- a/plugins/extensions/pykrita/sip/krita/Action.sip +++ b/plugins/extensions/pykrita/sip/krita/Action.sip @@ -1,38 +1,37 @@ class Action : QObject { %TypeHeaderCode #include "Action.h" %End public: explicit Action(QObject *parent /TransferThis/ = 0); Action(const QString &name, QAction* action, QObject *parent /TransferThis/ = 0); virtual ~Action(); bool operator==(const Action &other) const; bool operator!=(const Action &other) const; public Q_SLOTS: QString text() const; void setText(QString text); QString name() const; void setName(QString value); bool isCheckable() const; void setCheckable(bool value); bool isChecked() const; void setChecked(bool value); QString shortcut() const; void setShortcut(QString value); bool isVisible() const; void setVisible(bool value); bool isEnabled() const; void setEnabled(bool value); void setToolTip(QString tooltip); QString tooltip() const; void trigger(); - void setMenu(const QString menu); - QString menu() const; + Q_SIGNALS: void triggered(bool); private: private: Action(const Action &); // Generated }; diff --git a/plugins/extensions/pykrita/sip/krita/Extension.sip b/plugins/extensions/pykrita/sip/krita/Extension.sip index 78ffa9d6f0..92babb2855 100644 --- a/plugins/extensions/pykrita/sip/krita/Extension.sip +++ b/plugins/extensions/pykrita/sip/krita/Extension.sip @@ -1,11 +1,12 @@ class Extension : QObject { %TypeHeaderCode #include "Extension.h" %End public: explicit Extension(QObject *parent /TransferThis/ = 0); virtual void setup() = 0; + virtual void createActions(Window *window) = 0; }; diff --git a/plugins/extensions/pykrita/sip/krita/Krita.sip b/plugins/extensions/pykrita/sip/krita/Krita.sip index 21e8f12900..1d77a00b20 100644 --- a/plugins/extensions/pykrita/sip/krita/Krita.sip +++ b/plugins/extensions/pykrita/sip/krita/Krita.sip @@ -1,64 +1,63 @@ class Krita : QObject { %TypeHeaderCode #include "Krita.h" %End public: Krita(QObject* parent /TransferThis/ = 0); virtual ~Krita(); public Q_SLOTS: Document * activeDocument() const /Factory/; void setActiveDocument(Document* value); bool batchmode() const; void setBatchmode(bool value); QList actions() const /Factory/; Action * action(const QString & name) const; QList documents() const /Factory/; QStringList filters() const; Filter * filter(const QString &name) const /Factory/; QStringList colorModels() const; QStringList colorDepths(const QString &colorModel) const; QStringList filterStrategies() const; QStringList profiles(const QString &colorModel, const QString &ColorDepth) const; bool addProfile(const QString &profilePath); Notifier * notifier() const; QString version() const; QList views() const /Factory/; Window * activeWindow() const /Factory/; QList windows() const /Factory/; QMap resources(const QString &type) const /Factory/; QStringList recentDocuments() const; Document * createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile, double resolution) /Factory/; QList extensions() /Factory/; Document * openDocument(const QString &filename) /Factory/; Window * openWindow(); - Action * createAction(const QString &id, const QString & text, bool addToScriptMenu = true); QIcon icon(QString &iconName) const; void addExtension(Extension* _extension /GetWrapper/); %MethodCode Py_BEGIN_ALLOW_THREADS sipCpp->addExtension(a0); Py_END_ALLOW_THREADS sipTransferTo(a0Wrapper, Py_None); %End void addDockWidgetFactory(DockWidgetFactoryBase* _factory /GetWrapper/); %MethodCode Py_BEGIN_ALLOW_THREADS sipCpp->addDockWidgetFactory(a0); Py_END_ALLOW_THREADS sipTransferTo(a0Wrapper, Py_None); %End void writeSetting(const QString &group, const QString &name, const QString &value); QString readSetting(const QString &group, const QString &name, const QString &defaultValue); static Krita * instance(); static QObject * fromVariant(const QVariant & v); private: Krita(const Krita &); // Generated }; diff --git a/plugins/extensions/pykrita/sip/krita/Window.sip b/plugins/extensions/pykrita/sip/krita/Window.sip index fca6cbed71..aa578cb58b 100644 --- a/plugins/extensions/pykrita/sip/krita/Window.sip +++ b/plugins/extensions/pykrita/sip/krita/Window.sip @@ -1,22 +1,23 @@ class Window : QObject { %TypeHeaderCode #include "Window.h" %End Window(const Window & __0); public: bool operator==(const Window &other) const; bool operator!=(const Window &other) const; public Q_SLOTS: QMainWindow *qwindow() const; QList views() const /Factory/; View *addView(Document *document) /Factory/; void showView(View *view); View *activeView() const /Factory/; void activate(); void close(); + QAction *createAction(const QString &id, const QString &text = QString(), const QString &menuLocation = QString("tools/scripts")); Q_SIGNALS: void windowClosed(); private: }; diff --git a/plugins/python/assignprofiledialog/assignprofiledialog.py b/plugins/python/assignprofiledialog/assignprofiledialog.py index 1b3f2c7ddc..c4b92bec31 100644 --- a/plugins/python/assignprofiledialog/assignprofiledialog.py +++ b/plugins/python/assignprofiledialog/assignprofiledialog.py @@ -1,56 +1,59 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import sys from PyQt5.QtGui import * from PyQt5.QtWidgets import * from krita import * class AssignProfileDialog(Extension): def __init__(self, parent): super().__init__(parent) def assignProfile(self): doc = Application.activeDocument() if doc == None: QMessageBox.information(Application.activeWindow().qwindow(), "Assign Profile", "There is no active document.") return self.dialog = QDialog(Application.activeWindow().qwindow()) self.cmbProfile = QComboBox(self.dialog) for profile in sorted(Application.profiles(doc.colorModel(), doc.colorDepth())): self.cmbProfile.addItem(profile) vbox = QVBoxLayout(self.dialog) vbox.addWidget(self.cmbProfile) self.buttonBox = QDialogButtonBox(self.dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.dialog.accept) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.dialog.reject) vbox.addWidget(self.buttonBox) self.dialog.show() self.dialog.activateWindow() self.dialog.exec_() def accept(self): doc = Application.activeDocument() doc.setColorProfile(self.cmbProfile.currentText()) def setup(self): - action = Application.createAction("assing_profile_to_image", "Assign Profile to Image") + pass + + def createActions(self, window): + action = window.createAction("assing_profile_to_image", "Assign Profile to Image") action.triggered.connect(self.assignProfile) Scripter.addExtension(AssignProfileDialog(Application)) diff --git a/plugins/python/colorspace/colorspace.py b/plugins/python/colorspace/colorspace.py index 39d0cd5bb3..938326960b 100644 --- a/plugins/python/colorspace/colorspace.py +++ b/plugins/python/colorspace/colorspace.py @@ -1,31 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita from colorspace import uicolorspace class ColorSpaceExtension(krita.Extension): def __init__(self, parent): super(ColorSpaceExtension, self).__init__(parent) def setup(self): - action = krita.Krita.instance().createAction("color_space", "Color Space") + pass + + def createActions(self, window): + action = window.createAction("color_space", "Color Space") action.setToolTip("Plugin to change color space to selected documents") action.triggered.connect(self.initialize) def initialize(self): self.uicolorspace = uicolorspace.UIColorSpace() self.uicolorspace.initialize() Scripter.addExtension(ColorSpaceExtension(krita.Krita.instance())) diff --git a/plugins/python/documenttools/documenttools.py b/plugins/python/documenttools/documenttools.py index 5bede48f4c..9432c9309e 100644 --- a/plugins/python/documenttools/documenttools.py +++ b/plugins/python/documenttools/documenttools.py @@ -1,31 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita from documenttools import uidocumenttools class DocumentToolsExtension(krita.Extension): def __init__(self, parent): super(DocumentToolsExtension, self).__init__(parent) def setup(self): - action = krita.Krita.instance().createAction("document_tools", "Document Tools") + pass + + def createActions(self, window): + action = window.createAction("document_tools", "Document Tools") action.setToolTip("Plugin to manipulate properties of selected documents") action.triggered.connect(self.initialize) def initialize(self): self.uidocumenttools = uidocumenttools.UIDocumentTools() self.uidocumenttools.initialize() Scripter.addExtension(DocumentToolsExtension(krita.Krita.instance())) diff --git a/plugins/python/exportlayers/exportlayers.py b/plugins/python/exportlayers/exportlayers.py index f98ffa155a..de0b65af49 100644 --- a/plugins/python/exportlayers/exportlayers.py +++ b/plugins/python/exportlayers/exportlayers.py @@ -1,31 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita from exportlayers import uiexportlayers class ExportLayersExtension(krita.Extension): def __init__(self, parent): super(ExportLayersExtension, self).__init__(parent) def setup(self): - action = krita.Krita.instance().createAction("export_layers", "Export Layers") + pass + + def createActions(self, window): + action = window.createAction("export_layers", "Export Layers") action.setToolTip("Plugin to export layers from a document") action.triggered.connect(self.initialize) def initialize(self): self.uiexportlayers = uiexportlayers.UIExportLayers() self.uiexportlayers.initialize() Scripter.addExtension(ExportLayersExtension(krita.Krita.instance())) diff --git a/plugins/python/filtermanager/filtermanager.py b/plugins/python/filtermanager/filtermanager.py index dfc7d76582..0e9e3fd8d5 100644 --- a/plugins/python/filtermanager/filtermanager.py +++ b/plugins/python/filtermanager/filtermanager.py @@ -1,31 +1,34 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita from filtermanager import uifiltermanager class FilterManagerExtension(krita.Extension): def __init__(self, parent): super(FilterManagerExtension, self).__init__(parent) def setup(self): - action = krita.Krita.instance().createAction("filter_manager", "Filter Manager") + pass + + def createActions(self, window): + action = window.createAction("filter_manager", "Filter Manager") action.setToolTip("Plugin to filters management") action.triggered.connect(self.initialize) def initialize(self): self.uifiltermanager = uifiltermanager.UIFilterManager() self.uifiltermanager.initialize() Scripter.addExtension(FilterManagerExtension(krita.Krita.instance())) diff --git a/plugins/python/hello/hello.py b/plugins/python/hello/hello.py index e4966c6599..bd31a90068 100644 --- a/plugins/python/hello/hello.py +++ b/plugins/python/hello/hello.py @@ -1,86 +1,88 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' """ This is a simple example of a Python script for Krita. It demonstrates how to set up a custom extension and a custom docker! """ from PyQt5.QtCore import qDebug from PyQt5.QtWidgets import QWidget, QLabel, QMessageBox from krita import Krita, Extension, DockWidget, DockWidgetFactory, DockWidgetFactoryBase def hello(): """ Show a test message box. """ QMessageBox.information(QWidget(), i18n("Test"), i18n("Hello! This is Krita version %s") % Application.version()) class HelloExtension(Extension): """ HelloExtension is a small example extension demonstrating basic Python scripting support in Krita! """ def __init__(self, parent): """ Standard Krita Python extension constructor. Most of the initialization happens in :func:`setup` :param parent: Parent widget :type parent: :class:`QWidget` or None """ super().__init__(parent) def setup(self): + pass + + def createActions(self, window): """ This is where most of the setup takes place! """ - #qDebug("Hello Setup") - action = Krita.instance().createAction("hello_python", "hello", False) + action = window.createAction("hello_python", "hello") action.triggered.connect(hello) # Initialize and add the extension Scripter.addExtension(HelloExtension(Krita.instance())) class HelloDocker(DockWidget): """ The HelloDocker is an example of a simple Python-based docker. """ def __init__(self): """ Constructs an instance of HelloDocker and the widget it contains """ super().__init__() # The window title is also used in the Docker menu, # so it should be set to something sensible! self.setWindowTitle("HelloDocker") label = QLabel("Hello", self) self.setWidget(label) self._label = label def canvasChanged(self, canvas): """ Override canvasChanged from :class:`DockWidget`. This gets called when the canvas changes. You can also access the active canvas via :func:`DockWidget.canvas` Parameter `canvas` can be null if the last document is closed """ self._label.setText("HelloDocker: canvas changed") # Register the docker so Krita can use it! Application.addDockWidgetFactory(DockWidgetFactory("hello", DockWidgetFactoryBase.DockRight, HelloDocker)) diff --git a/plugins/python/highpass/highpass.py b/plugins/python/highpass/highpass.py index 24aec83f34..a5a92a24da 100644 --- a/plugins/python/highpass/highpass.py +++ b/plugins/python/highpass/highpass.py @@ -1,127 +1,130 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import sys from PyQt5.QtGui import * from PyQt5.QtWidgets import * from krita import * class HighpassExtension(Extension): def __init__(self, parent): super().__init__(parent) def setup(self): - action = Application.createAction("high_pass_filter", "High Pass") + pass + + def createActions(self, window): + action = window.createAction("high_pass_filter", "High Pass") action.triggered.connect(self.showDialog) def showDialog(self): doc = Application.activeDocument() if doc == None: QMessageBox.information(Application.activeWindow().qwindow(), "Highpass Filter", "There is no active image.") return self.dialog = QDialog(Application.activeWindow().qwindow()) self.intRadius = QSpinBox() self.intRadius.setValue(10) self.intRadius.setRange(2, 200) self.cmbMode = QComboBox() self.cmbMode.addItems(["Color", "Preserve DC", "Greyscale", "Greyscale, Apply Chroma", "Redrobes"]) self.keepOriginal = QCheckBox("Keep Original Layer") self.keepOriginal.setChecked(True) form = QFormLayout() form.addRow("Filter Radius", self.intRadius) form.addRow("Mode", self.cmbMode) form.addRow("", self.keepOriginal) self.buttonBox = QDialogButtonBox(self.dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.dialog.accept) self.buttonBox.accepted.connect(self.highpass) self.buttonBox.rejected.connect(self.dialog.reject) vbox = QVBoxLayout(self.dialog) vbox.addLayout(form) vbox.addWidget(self.buttonBox) self.dialog.show() self.dialog.activateWindow() self.dialog.exec_() def highpass(self): # XXX: Start undo macro image = Application.activeDocument() original = image.activeNode() working_layer = original # We can only highpass on paint layers if self.keepOriginal.isChecked() or original.type() != "paintlayer": working_layer = image.createNode("working", "paintlayer") working_layer.setColorSpace(original.colorModel(), original.colorSpace(), original.profile()) working_layer.writeBytes(original.readBytes(0, 0, image.width(), image.height()), 0, 0, image.width(), image.height()) original.parentNode().addChildNode(working_layer, original) # XXX: Unimplemented image.setActiveNode(working_layer) colors_layer = None # if keeping colors if self.cmbMode.currentIndex() == 1 or self.cmbMode.currentIndex() == 3: colors_layer = working_layer.duplicate() # XXX: Unimplemented colors_layer.setName("colors") original.parentNode().addChildNode(working_layer, colors_layer) # XXX: Unimplemented # if greyscale, desature if (self.cmbMode.currentIndex() == 2 or self.cmbMode.currentIndex() == 3): filter = Application.filter("desaturate") filter.apply(working_layer, 0, 0, image.width(), image.height()) # Duplicate on top and blur blur_layer = working_layer.duplicate() blur_layer.setName("blur") original.parentNode().addChildNode(blur_layer, working_layer) # XXX: Unimplemented # blur filter = Application.filter("gaussian blur") filter_configuration = filter.configuration() filter_configuration.setProperty("horizRadius", self.intRadius.value()) filter_configuration.setProperty("vertRadius", self.intRadius.value()) filter_configuration.setProperty("lockAspect", true) filter.setConfiguration(filter_configuration) filter.apply(blur_layer, 0, 0, image.width(), image.height()) if self.cmbMode.currentIndex() <= 3: blur_layer.setBlendingMode("grain_extract") working_layer = image.mergeDown(blur_layer) # if preserve chroma, change set the mode to value and merge down with the layer we kept earlier. if self.cmbMode.currentIndex() == 3: working_layer.setBlendingMode("value") working_layer = image.mergeDown(working_layer) # if preserve DC, change set the mode to overlay and merge down with the average colour of the layer we kept earlier. if self.cmbMode.currentIndex() == 1: # get the average color of the entire image # clear the colors layer to the given color working_layer = image.mergeDown(working_layer) else: # Mode == 4, RedRobes image.setActiveNode(blur_layer) # Get the average color of the input layer # copy the solid colour layer # copy the blurred layer # XXX: End undo macro Scripter.addExtension(HighpassExtension(Krita.instance())) diff --git a/plugins/python/scripter/scripter.py b/plugins/python/scripter/scripter.py index 5f2a3a2f67..2653fe788e 100644 --- a/plugins/python/scripter/scripter.py +++ b/plugins/python/scripter/scripter.py @@ -1,42 +1,45 @@ """ Copyright (c) 2017 Eliakin Costa This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. """ from PyQt5.QtWidgets import QDialog from PyQt5.QtCore import QSettings, QStandardPaths from krita import * from scripter import uicontroller, documentcontroller, debugcontroller class ScripterExtension(Extension): def __init__(self, parent): super().__init__(parent) def setup(self): - action = Krita.instance().createAction("python_scripter", "Scripter") + pass + + def createActions(self, window): + action = window.createAction("python_scripter", "Scripter") action.triggered.connect(self.initialize) def initialize(self): configPath = QStandardPaths.writableLocation(QStandardPaths.GenericConfigLocation) self.settings = QSettings(configPath + '/krita-scripterrc', QSettings.IniFormat) self.uicontroller = uicontroller.UIController() self.documentcontroller = documentcontroller.DocumentController() self.debugcontroller = debugcontroller.DebugController(self) self.uicontroller.initialize(self) Krita.instance().addExtension(ScripterExtension(Krita.instance())) diff --git a/plugins/python/tenbrushes/tenbrushes.py b/plugins/python/tenbrushes/tenbrushes.py index aa280bcf39..d21c66a6fb 100644 --- a/plugins/python/tenbrushes/tenbrushes.py +++ b/plugins/python/tenbrushes/tenbrushes.py @@ -1,71 +1,69 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' import krita from tenbrushes import uitenbrushes class TenBrushesExtension(krita.Extension): def __init__(self, parent): super(TenBrushesExtension, self).__init__(parent) self.actions = [] self.buttons = [] self.selectedPresets = [] def setup(self): - action = Application.createAction("ten_brushes", "Ten Brushes") + self.readSettings() + + def createActions(self, window): + action = window.createAction("ten_brushes", "Ten Brushes") action.setToolTip("Assign ten brush presets to ten shortcuts.") action.triggered.connect(self.initialize) - - self.readSettings() - self.loadActions() + self.loadActions(window) def initialize(self): self.uitenbrushes = uitenbrushes.UITenBrushes() self.uitenbrushes.initialize(self) def readSettings(self): self.selectedPresets = Application.readSetting("", "tenbrushes", "").split(',') def writeSettings(self): presets = [] for index, button in enumerate(self.buttons): self.actions[index].preset = button.preset presets.append(button.preset) Application.writeSetting("", "tenbrushes", ','.join(map(str, presets))) - def loadActions(self): + def loadActions(self, window): allPresets = Application.resources("preset") for index, item in enumerate(['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']): - action = Application.createAction("activate_preset_" + item, "Activate Brush Preset " + item) - action.setMenu("None") + action = window.createAction("activate_preset_" + item, "Activate Brush Preset " + item, "") action.triggered.connect(self.activatePreset) - - if index < len(self.selectedPresets) and self.selectedPresets[index] in allPresets: action.preset = self.selectedPresets[index] else: action.preset = None self.actions.append(action) def activatePreset(self): allPresets = Application.resources("preset") if Application.activeWindow() and len(Application.activeWindow().views()) > 0 and self.sender().preset in allPresets: Application.activeWindow().views()[0].activateResource(allPresets[self.sender().preset]) Scripter.addExtension(TenBrushesExtension(Application)) diff --git a/plugins/python/tenscripts/tenscripts.py b/plugins/python/tenscripts/tenscripts.py index 100edaa40c..b556580aae 100644 --- a/plugins/python/tenscripts/tenscripts.py +++ b/plugins/python/tenscripts/tenscripts.py @@ -1,84 +1,84 @@ ''' This script is licensed CC 0 1.0, so that you can learn from it. ------ CC 0 1.0 --------------- The person who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. https://creativecommons.org/publicdomain/zero/1.0/legalcode ''' from PyQt5.QtWidgets import QMessageBox import krita from tenscripts import uitenscripts import importlib class TenScriptsExtension(krita.Extension): def __init__(self, parent): super(TenScriptsExtension, self).__init__(parent) self.actions = [] self.scripts = [] def setup(self): - action = Application.createAction("ten_scripts", "Ten Scripts") + self.readSettings() + + def createActions(self, window): + action = window.createAction("ten_scripts", "Ten Scripts") action.setToolTip("Assign ten scripts to ten shortcuts.") action.triggered.connect(self.initialize) - - self.readSettings() - self.loadActions() + self.loadActions(window) def initialize(self): self.uitenscripts = uitenscripts.UITenScripts() self.uitenscripts.initialize(self) def readSettings(self): self.scripts = Application.readSetting("tenscripts", "scripts", "").split(',') def writeSettings(self): saved_scripts = self.uitenscripts.saved_scripts() for index, script in enumerate(saved_scripts): self.actions[index].script = script Application.writeSetting("tenscripts", "scripts", ','.join(map(str, saved_scripts))) - def loadActions(self): + def loadActions(self, window): for index, item in enumerate(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']): - action = Application.createAction("execute_script_" + item, "Execute Script " + item) + action = window.createAction("execute_script_" + item, "Execute Script " + item, "") action.script = None - action.setMenu("None") action.triggered.connect(self._executeScript) if index < len(self.scripts): action.script = self.scripts[index] self.actions.append(action) def _executeScript(self): script = self.sender().script if script: try: spec = importlib.util.spec_from_file_location("users_script", script) users_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(users_module) if hasattr(users_module, 'main') and callable(users_module.main): users_module.main() self.showMessage('script {0} executed'.format(self.sender().script)) except Exception as e: self.showMessage(str(e)) else: self.showMessage("You didn't assign a script to that action") def showMessage(self, message): self.msgBox = QMessageBox(Application.activeWindow().qwindow()) self.msgBox.setText(message) self.msgBox.exec_() Scripter.addExtension(TenScriptsExtension(Application))