diff --git a/libs/libkis/Action.cpp b/libs/libkis/Action.cpp index 00df6f3667..8c42d9beee 100644 --- a/libs/libkis/Action.cpp +++ b/libs/libkis/Action.cpp @@ -1,160 +1,164 @@ /* * 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); - d->action->setProperty("menu", "tools/scripts"); 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); - d->action->setProperty("menu", "tools/scripts"); 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->property("menu").toString() == "tools/scripts"; + return d->action->isVisible(); } void Action::setVisible(bool value) { if (!d->action) return; - if (value) { - d->action->setProperty("menu", "tools/scripts"); - } - else { - d->action->setProperty("menu", ""); - } + 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); } 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 16006e097a..e20d20371f 100644 --- a/libs/libkis/Action.h +++ b/libs/libkis/Action.h @@ -1,148 +1,160 @@ /* * 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); /** * 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/Krita.cpp b/libs/libkis/Krita.cpp index 9012f5f1a7..7a2e47490c 100644 --- a/libs/libkis/Krita.cpp +++ b/libs/libkis/Krita.cpp @@ -1,383 +1,394 @@ /* * 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 "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(); } 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(); qSort(ls); 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::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 == "pattern") { KoResourceServer* server = KoResourceServerProvider::instance()->patternServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type == "gradient") { KoResourceServer* server = KoResourceServerProvider::instance()->gradientServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type == "brush") { KisBrushResourceServer* server = KisBrushServer::instance()->brushServer(); Q_FOREACH (KisBrushSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type == "preset") { KisPaintOpPresetResourceServer* server = KisResourceServerProvider::instance()->paintOpPresetServer(); Q_FOREACH (KisPaintOpPresetSP res, server->resources()) { resources[res->name()] = new Resource(res.data()); } } else if (type == "palette") { KoResourceServer* server = KoResourceServerProvider::instance()->paletteServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } else if (type == "workspace") { KoResourceServer< KisWorkspaceResource >* server = KisResourceServerProvider::instance()->workspaceServer(); Q_FOREACH (KoResource *res, server->resources()) { resources[res->name()] = new Resource(res); } } return resources; } Document* Krita::createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile) { 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, "", 100.0)) { 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::OPEN_URL_FLAG_DO_NOT_ADD_TO_RECENT_FILES); return new Document(document); } Window* Krita::openWindow() { KisMainWindow *mw = KisPart::instance()->createMainWindow(); return new Window(mw); } -Action *Krita::createAction(const QString &text) +Action *Krita::createAction(const QString &id, const QString &text) { 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); + 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); } 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; } diff --git a/libs/libkis/Krita.h b/libs/libkis/Krita.h index 94376fdaa2..a132d997c6 100644 --- a/libs/libkis/Krita.h +++ b/libs/libkis/Krita.h @@ -1,301 +1,303 @@ /* * 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 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 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 createDocument creates a new document and image and registers the document with the Krita application. * * The document will have one transparent layer. * * @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. * @return the created document. */ Document *createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile); /** * @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. + * 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 &text); + Action *createAction(const QString &name, const QString &text); /** * @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 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: struct Private; Private *const d; static Krita* s_instance; }; Q_DECLARE_METATYPE(Notifier*); #endif // LIBKIS_KRITA_H diff --git a/libs/ui/KisPart.cpp b/libs/ui/KisPart.cpp index 5ea6bb6866..77a4eb4365 100644 --- a/libs/ui/KisPart.cpp +++ b/libs/ui/KisPart.cpp @@ -1,491 +1,491 @@ /* 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(QAction *action, d->scriptActions) { + 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) { Q_ASSERT(url.isLocalFile()); qApp->setOverrideCursor(Qt::BusyCursor); KisDocument *document = createDocument(); if (!document->openUrl(url)) { delete document; return; } if (!document->image()) { delete document; return; } document->setModified(false); addDocument(document); KisMainWindow *mw = currentMainwindow(); mw->addViewAndNotifyLoadingCompleted(document); qApp->restoreOverrideCursor(); } 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/kis_action_manager.h b/libs/ui/kis_action_manager.h index fbb59de377..c481469f07 100644 --- a/libs/ui/kis_action_manager.h +++ b/libs/ui/kis_action_manager.h @@ -1,113 +1,108 @@ /* * Copyright (c) 2013 Sven Langkamp * * 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_ACTION_MANAGER_H #define KIS_ACTION_MANAGER_H #include #include #include "KisView.h" #include "kstandardaction.h" #include "kis_action_registry.h" #include "operations/kis_operation_configuration.h" class KisViewManager; class KisAction; class KisOperationUIFactory; class KisOperation; /** * @brief A KisActionManager class keeps track of KisActions. * These actions are always associated with the GUI. That means each MainWindow * will create its own duplicate of these actions. * * KisActionManager enables and disables actions, to grey out buttons according * to the state of the application. * * Some of the primitive actions (load/save and so on) are not defined as * KisActions, but instead KActions, automacially registered through KXMLGUI. * It tracks these actions through the KActionCollection owned by the window. * Ultimately it would be nice to unify these things more fully. * */ class KRITAUI_EXPORT KisActionManager : public QObject { Q_OBJECT public: KisActionManager(KisViewManager* viewManager, KActionCollection *actionCollection); ~KisActionManager() override; void setView(QPointer imageView); /** * Add an existing action to the action manager. */ void addAction(const QString& name, KisAction* action); /** * Stop managing an action. */ void takeAction(KisAction* action); /** * Create a new KisAction. Looks up data from the .action data files. */ KisAction *createAction(const QString &name); /** * Look up an action by name. */ KisAction *actionByName(const QString &name) const; - - - - void registerOperationUIFactory(KisOperationUIFactory* factory); void registerOperation(KisOperation* operation); void runOperation(const QString &id); void runOperationFromConfiguration(KisOperationConfigurationSP config); - /** * Update actions handled by kis_action_manager to set enabled. * This is used to grey out buttons that can't be pressed. */ void updateGUI(); /** * Create a KisAction based on a KStandardAction. The KStandardAction is deleted. */ KisAction *createStandardAction(KStandardAction::StandardAction, const QObject *receiver, const char *member); private Q_SLOTS: void slotActionAddedToCollection(QAction *action); private: void dumpActionFlags(); class Private; Private* const d; }; #endif // KIS_ACTION_MANAGER_H diff --git a/libs/ui/kis_categorized_item_delegate.cpp b/libs/ui/kis_categorized_item_delegate.cpp index bae0a43987..c8ee171242 100644 --- a/libs/ui/kis_categorized_item_delegate.cpp +++ b/libs/ui/kis_categorized_item_delegate.cpp @@ -1,161 +1,159 @@ /* * Copyright (c) 2009 Cyrille Berger * Copyright (c) 2011 Silvio Heinrich * Copyright (c) 2014 Mohit Goyal * * This library 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.1 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 Lesser 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_categorized_item_delegate.h" #include "kis_categorized_list_model.h" #include #include #include #include #include #include #include #include #include "kis_debug.h" KisCategorizedItemDelegate::KisCategorizedItemDelegate(QObject *parent) : QStyledItemDelegate(parent), m_minimumItemHeight(0) { } void KisCategorizedItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { painter->resetTransform(); if(!index.data(__CategorizedListModelBase::IsHeaderRole).toBool()) { QStyleOptionViewItem sovi(option); - - if (index.data(__CategorizedListModelBase::isLockableRole).toBool()) { const int iconSize = qMax(16, m_minimumItemHeight - 2); bool locked = index.data(__CategorizedListModelBase::isLockedRole).toBool(); QIcon icon = locked ? KisIconUtils::loadIcon(koIconName("layer-locked")) : KisIconUtils::loadIcon(koIconName("layer-unlocked")); // to be able to make an icon more transparent. we need to create a new image // and use the QPainter to make a more transparent version of it. QImage image(iconSize, iconSize, QImage::Format_ARGB32_Premultiplied); image.fill(Qt::transparent); // needs to happen, otherwise 'junk' pixels are used (looks bad) QPainter p(&image); p.setCompositionMode(QPainter::CompositionMode_Source); // make unlocked icons more hidden if (locked) { p.setOpacity(1.0); } else { p.setOpacity(0.14); } p.drawPixmap(0, 0, icon.pixmap(iconSize, QIcon::Normal) ); p.end(); icon = QIcon( QPixmap::fromImage(image) ); // the new icon is ready sovi.decorationPosition = QStyleOptionViewItem::Right; sovi.decorationAlignment = Qt::AlignRight; sovi.decorationSize = QSize(iconSize, iconSize); sovi.features |= QStyleOptionViewItem::HasDecoration; sovi.icon = icon; } QStyledItemDelegate::paint(painter, sovi, index); painter->setOpacity(1); } else { QPalette palette = QApplication::palette(); if(option.state & QStyle::State_MouseOver) painter->fillRect(option.rect, palette.midlight()); else painter->fillRect(option.rect, palette.button()); painter->setBrush(palette.buttonText()); painter->drawText(option.rect, index.data().toString(), QTextOption(Qt::AlignVCenter|Qt::AlignHCenter)); paintTriangle( painter, option.rect.x(), option.rect.y(), option.rect.height(), !index.data(__CategorizedListModelBase::ExpandCategoryRole).toBool() ); } painter->resetTransform(); } QSize KisCategorizedItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { //on first calling this calculates the minimal height of the items if (m_minimumItemHeight == 0) { for(int i=0; irowCount(); i++) { QSize indexSize = QStyledItemDelegate::sizeHint(option, index.model()->index(i, 0)); m_minimumItemHeight = qMax(m_minimumItemHeight, indexSize.height()); /** * Make all the items, including the ones having * checkboxes look the same. */ QStyle *style = QApplication::style(); QStyleOptionButton so; QSize size = style->sizeFromContents(QStyle::CT_CheckBox, &so, QSize(), 0); m_minimumItemHeight = qMax(size.height(), m_minimumItemHeight); } } int width = QStyledItemDelegate::sizeHint(option, index).width(); if (index.data(__CategorizedListModelBase::isLockableRole).toBool()) { width += m_minimumItemHeight; } return QSize(width, m_minimumItemHeight); } void KisCategorizedItemDelegate::paintTriangle(QPainter* painter, qint32 x, qint32 y, qint32 size, bool rotate) const { QPolygonF triangle; triangle.push_back(QPointF(-0.2,-0.2)); triangle.push_back(QPointF( 0.2,-0.2)); triangle.push_back(QPointF( 0.0, 0.2)); QTransform transform; transform.translate(x + size/2, y + size/2); transform.scale(size, size); if(rotate) transform.rotate(-90); QPalette palette = QApplication::palette(); painter->setBrush(palette.buttonText()); painter->drawPolygon(transform.map(triangle)); } diff --git a/libs/ui/kis_palette_view.cpp b/libs/ui/kis_palette_view.cpp index 44bfb108c0..5ea11a10b3 100644 --- a/libs/ui/kis_palette_view.cpp +++ b/libs/ui/kis_palette_view.cpp @@ -1,99 +1,102 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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 "kis_palette_view.h" #include #include #include "kis_palette_delegate.h" #include "KisPaletteModel.h" #include "kis_config.h" struct KisPaletteView::Private { KisPaletteModel *model = 0; }; KisPaletteView::KisPaletteView(QWidget *parent) : KoTableView(parent), m_d(new Private) { setShowGrid(false); horizontalHeader()->setVisible(false); verticalHeader()->setVisible(false); setItemDelegate(new KisPaletteDelegate()); + setDragEnabled(true); + setDragDropMode(QAbstractItemView::InternalMove); + KisConfig cfg; QPalette pal(palette()); pal.setColor(QPalette::Base, cfg.getMDIBackgroundColor()); setAutoFillBackground(true); setPalette(pal); int defaultSectionSize = cfg.paletteDockerPaletteViewSectionSize(); horizontalHeader()->setDefaultSectionSize(defaultSectionSize); verticalHeader()->setDefaultSectionSize(defaultSectionSize); } KisPaletteView::~KisPaletteView() { } void KisPaletteView::setCrossedKeyword(const QString &value) { KisPaletteDelegate *delegate = dynamic_cast(itemDelegate()); KIS_ASSERT_RECOVER_RETURN(delegate); delegate->setCrossedKeyword(value); } void KisPaletteView::setPaletteModel(KisPaletteModel *model) { m_d->model = model; setModel(model); } KisPaletteModel* KisPaletteView::paletteModel() const { return m_d->model; } void KisPaletteView::wheelEvent(QWheelEvent *event) { if (event->modifiers() & Qt::ControlModifier) { int numDegrees = event->delta() / 8; int numSteps = numDegrees / 7; int curSize = horizontalHeader()->sectionSize(0); int setSize = numSteps + curSize; if ( setSize >= 12 ) { horizontalHeader()->setDefaultSectionSize(setSize); verticalHeader()->setDefaultSectionSize(setSize); KisConfig cfg; cfg.setPaletteDockerPaletteViewSectionSize(setSize); } event->accept(); } else { KoTableView::wheelEvent(event); } } diff --git a/libs/ui/kis_script_manager.cpp b/libs/ui/kis_script_manager.cpp index 205ae77d75..a93d544a5a 100644 --- a/libs/ui/kis_script_manager.cpp +++ b/libs/ui/kis_script_manager.cpp @@ -1,80 +1,90 @@ /* * 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) - , view(0) + , viewManager(0) , scriptMenu(0) { } KActionCollection *actionCollection; KisActionManager *actionManager; - KisViewManager *view; + KisViewManager *viewManager; KActionMenu *scriptMenu; + KActionMenu *hiddenMenu; }; KisScriptManager::KisScriptManager(KisViewManager *view) : QObject(view) , d(new Private()) { - d->view = view; + 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->scriptMenu = new KActionMenu(i18n("Scripts"), this); d->actionCollection->addAction("scripts", d->scriptMenu); + d->hiddenMenu = new KActionMenu("hidden", this); } void KisScriptManager::updateGUI() { - if (!d->view) return; + if (!d->viewManager) return; } -void KisScriptManager::addAction(QAction *action) +void KisScriptManager::addAction(KisAction *action) { - Q_ASSERT(d->actionCollection); - if (action->property("menu").toString().toLower() == "tools/scripts") { + 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 index db4049f263..b1099473e9 100644 --- a/libs/ui/kis_script_manager.h +++ b/libs/ui/kis_script_manager.h @@ -1,52 +1,52 @@ /* * 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 QAction; +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(QAction *action); + void addAction(KisAction *action); private: struct Private; Private * const d; }; #endif // KIS_SCRIPT_MANAGER_H diff --git a/libs/widgetutils/kis_action_registry.cpp b/libs/widgetutils/kis_action_registry.cpp index a7cae46a10..944741a267 100644 --- a/libs/widgetutils/kis_action_registry.cpp +++ b/libs/widgetutils/kis_action_registry.cpp @@ -1,410 +1,406 @@ /* * Copyright (c) 2015 Michael Abrahams * * 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 3 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 #include #include #include #include #include #include #include #include "kis_debug.h" #include "KoResourcePaths.h" #include "kis_icon_utils.h" #include "kis_action_registry.h" #include "kshortcutschemeshelper_p.h" namespace { /** * We associate several pieces of information with each shortcut. The first * piece of information is a QDomElement, containing the raw data from the * .action XML file. The second and third are QKeySequences, the first of * which is the default shortcut, the last of which is any custom shortcut. * The last two are the KActionCollection and KActionCategory used to * organize the shortcut editor. */ struct ActionInfoItem { QDomElement xmlData; QString collectionName; QString categoryName; inline QList defaultShortcuts() const { return m_defaultShortcuts; } inline void setDefaultShortcuts(const QList &value) { m_defaultShortcuts = value; } inline QList customShortcuts() const { return m_customShortcuts; } inline void setCustomShortcuts(const QList &value, bool explicitlyReset) { m_customShortcuts = value; m_explicitlyReset = explicitlyReset; } inline QList effectiveShortcuts() const { return m_customShortcuts.isEmpty() && !m_explicitlyReset ? m_defaultShortcuts : m_customShortcuts; } private: QList m_defaultShortcuts; QList m_customShortcuts; bool m_explicitlyReset = false; }; // Convenience macros to extract text of a child node. QString getChildContent(QDomElement xml, QString node) { return xml.firstChildElement(node).text(); } // Use Krita debug logging categories instead of KDE's default qDebug() for // harmless empty strings and translations QString quietlyTranslate(const QString &s) { if (s.isEmpty()) { return s; } QString translatedString = i18nc("action", s.toUtf8()); if (translatedString == s) { translatedString = i18n(s.toUtf8()); } if (translatedString.isEmpty()) { dbgAction << "No translation found for" << s; return s; } return translatedString; } } class Q_DECL_HIDDEN KisActionRegistry::Private { public: Private(KisActionRegistry *_q) : q(_q) {} // This is the main place containing ActionInfoItems. QMap actionInfoList; void loadActionFiles(); void loadCustomShortcuts(QString filename = QStringLiteral("kritashortcutsrc")); ActionInfoItem &actionInfo(const QString &name) { if (!actionInfoList.contains(name)) { dbgAction << "Tried to look up info for unknown action" << name; } return actionInfoList[name]; } KisActionRegistry *q; QSet sanityPropertizedShortcuts; }; Q_GLOBAL_STATIC(KisActionRegistry, s_instance) KisActionRegistry *KisActionRegistry::instance() { return s_instance; } KisActionRegistry::KisActionRegistry() : d(new KisActionRegistry::Private(this)) { - d->loadActionFiles(); KConfigGroup cg = KSharedConfig::openConfig()->group("Shortcut Schemes"); QString schemeName = cg.readEntry("Current Scheme", "Default"); loadShortcutScheme(schemeName); loadCustomShortcuts(); } KisActionRegistry::ActionCategory KisActionRegistry::fetchActionCategory(const QString &name) const { if (!d->actionInfoList.contains(name)) return ActionCategory(); const ActionInfoItem info = d->actionInfoList.value(name); return ActionCategory(info.collectionName, info.categoryName); } void KisActionRegistry::notifySettingsUpdated() { d->loadCustomShortcuts(); } void KisActionRegistry::loadCustomShortcuts() { d->loadCustomShortcuts(); } void KisActionRegistry::loadShortcutScheme(const QString &schemeName) { // Load scheme file if (schemeName != QStringLiteral("Default")) { QString schemeFileName = KShortcutSchemesHelper::schemeFileLocations().value(schemeName); if (schemeFileName.isEmpty()) { return; } KConfig schemeConfig(schemeFileName, KConfig::SimpleConfig); applyShortcutScheme(&schemeConfig); } else { // Apply default scheme, updating KisActionRegistry data applyShortcutScheme(); } } QAction * KisActionRegistry::makeQAction(const QString &name, QObject *parent) { QAction * a = new QAction(parent); if (!d->actionInfoList.contains(name)) { dbgAction << "Warning: requested data for unknown action" << name; return a; } propertizeAction(name, a); return a; } void KisActionRegistry::settingsPageSaved() { // For now, custom shortcuts are dealt with by writing to file and reloading. loadCustomShortcuts(); // Announce UI should reload current shortcuts. emit shortcutsUpdated(); } void KisActionRegistry::applyShortcutScheme(const KConfigBase *config) { // First, update the things in KisActionRegistry if (config == 0) { // Use default shortcut scheme. Simplest just to reload everything. d->actionInfoList.clear(); d->loadActionFiles(); loadCustomShortcuts(); } else { const auto schemeEntries = config->group(QStringLiteral("Shortcuts")).entryMap(); // Load info item for each shortcut, reset custom shortcuts auto it = schemeEntries.constBegin(); while (it != schemeEntries.end()) { ActionInfoItem &info = d->actionInfo(it.key()); info.setDefaultShortcuts(QKeySequence::listFromString(it.value())); it++; } } } void KisActionRegistry::updateShortcut(const QString &name, QAction *action) { const ActionInfoItem &info = d->actionInfo(name); - action->setShortcuts(info.effectiveShortcuts()); action->setProperty("defaultShortcuts", qVariantFromValue(info.defaultShortcuts())); d->sanityPropertizedShortcuts.insert(name); } bool KisActionRegistry::sanityCheckPropertized(const QString &name) { return d->sanityPropertizedShortcuts.contains(name); } QList KisActionRegistry::registeredShortcutIds() const { return d->actionInfoList.keys(); } bool KisActionRegistry::propertizeAction(const QString &name, QAction * a) { if (!d->actionInfoList.contains(name)) { - dbgAction << "No XML data found for action" << name; + qDebug() << "No XML data found for action" << name; return false; } const ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (!actionXml.text().isEmpty()) { // i18n requires converting format from QString. auto getChildContent_i18n = [=](QString node){return quietlyTranslate(getChildContent(actionXml, node));}; // Note: the fields in the .action documents marked for translation are determined by extractrc. QString icon = getChildContent(actionXml, "icon"); QString text = getChildContent_i18n("text"); QString whatsthis = getChildContent_i18n("whatsThis"); QString toolTip = getChildContent_i18n("toolTip"); QString statusTip = getChildContent_i18n("statusTip"); QString iconText = getChildContent_i18n("iconText"); bool isCheckable = getChildContent(actionXml, "isCheckable") == QString("true"); a->setObjectName(name); // This is helpful, should be added more places in Krita a->setIcon(KisIconUtils::loadIcon(icon.toLatin1())); a->setText(text); a->setObjectName(name); a->setWhatsThis(whatsthis); a->setToolTip(toolTip); a->setStatusTip(statusTip); a->setIconText(iconText); a->setCheckable(isCheckable); } updateShortcut(name, a); return true; } QString KisActionRegistry::getActionProperty(const QString &name, const QString &property) { ActionInfoItem info = d->actionInfo(name); QDomElement actionXml = info.xmlData; if (actionXml.text().isEmpty()) { dbgAction << "No XML data found for action" << name; return QString(); } return getChildContent(actionXml, property); } void KisActionRegistry::Private::loadActionFiles() { QStringList actionDefinitions = KoResourcePaths::findAllResources("kis_actions", "*.action", KoResourcePaths::Recursive); - // Extract actions all XML .action files. Q_FOREACH (const QString &actionDefinition, actionDefinitions) { QDomDocument doc; QFile f(actionDefinition); f.open(QFile::ReadOnly); doc.setContent(f.readAll()); QDomElement base = doc.documentElement(); // "ActionCollection" outer group QString collectionName = base.attribute("name"); QString version = base.attribute("version"); if (version != "2") { errAction << ".action XML file" << actionDefinition << "has incorrect version; skipping."; continue; } // Loop over nodes. Each of these corresponds to a // KActionCategory, producing a group of actions in the shortcut dialog. QDomElement actions = base.firstChild().toElement(); while (!actions.isNull()) { // field QDomElement categoryTextNode = actions.firstChild().toElement(); QString categoryName = quietlyTranslate(categoryTextNode.text()); // tags QDomElement actionXml = categoryTextNode.nextSiblingElement(); // Loop over individual actions while (!actionXml.isNull()) { if (actionXml.tagName() == "Action") { // Read name from format QString name = actionXml.attribute("name"); // Bad things if (name.isEmpty()) { errAction << "Unnamed action in definitions file " << actionDefinition; } else if (actionInfoList.contains(name)) { - // errAction << "NOT COOL: Duplicated action name from xml data: " << name; + errAction << "NOT COOL: Duplicated action name from xml data: " << name; } else { ActionInfoItem info; info.xmlData = actionXml; // Use empty list to signify no shortcut QString shortcutText = getChildContent(actionXml, "shortcut"); if (!shortcutText.isEmpty()) { info.setDefaultShortcuts(QKeySequence::listFromString(shortcutText)); } info.categoryName = categoryName; info.collectionName = collectionName; - // dbgAction << "default shortcut for" << name << " - " << info.defaultShortcut; actionInfoList.insert(name,info); } } actionXml = actionXml.nextSiblingElement(); } actions = actions.nextSiblingElement(); } } } void KisActionRegistry::Private::loadCustomShortcuts(QString filename) { const KConfigGroup localShortcuts(KSharedConfig::openConfig(filename), QStringLiteral("Shortcuts")); if (!localShortcuts.exists()) { return; } // Distinguish between two "null" states for custom shortcuts. for (auto i = actionInfoList.begin(); i != actionInfoList.end(); ++i) { if (localShortcuts.hasKey(i.key())) { QString entry = localShortcuts.readEntry(i.key(), QString()); if (entry == QStringLiteral("none")) { i.value().setCustomShortcuts(QList(), true); } else { i.value().setCustomShortcuts(QKeySequence::listFromString(entry), false); } } else { i.value().setCustomShortcuts(QList(), false); } } } KisActionRegistry::ActionCategory::ActionCategory() { } KisActionRegistry::ActionCategory::ActionCategory(const QString &_componentName, const QString &_categoryName) : componentName(_componentName), categoryName(_categoryName), m_isValid(true) { } bool KisActionRegistry::ActionCategory::isValid() const { return m_isValid && !categoryName.isEmpty() && !componentName.isEmpty(); } diff --git a/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt b/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt index 2a79fe5f9f..61bc5eb185 100644 --- a/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt +++ b/plugins/extensions/pykrita/plugin/plugins/CMakeLists.txt @@ -1,94 +1,98 @@ # Copyright (C) 2012, 2013 Shaheed Haque # Copyright (C) 2013 Alex Turbov # Copyright (C) 2014-2016 Boudewijn Rempt # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. include(CMakeParseArguments) # # Simple helper function to install plugin and related files # having only a name of the plugin... # (just to reduce syntactic noise when a lot of plugins get installed) # function(install_pykrita_plugin name) set(_options) set(_one_value_args) set(_multi_value_args PATTERNS FILE) cmake_parse_arguments(install_pykrita_plugin "${_options}" "${_one_value_args}" "${_multi_value_args}" ${ARGN}) if(NOT name) message(FATAL_ERROR "Plugin filename is not given") endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${name}.py) install(FILES kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) foreach(_f ${name}.py ${name}.ui ${install_pykrita_plugin_FILE}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${_f}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${_f} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) endif() endforeach() elseif(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${name}) install(FILES ${name}/kritapykrita_${name}.desktop DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "*.ui" PATTERN "__pycache__*" EXCLUDE ) # TODO Is there any way to form a long PATTERN options string # and use it in a single install() call? # NOTE Install specified patterns one-by-one... foreach(_pattern ${install_pykrita_plugin_PATTERNS}) install( DIRECTORY ${name} DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "${_pattern}" PATTERN "__pycache__*" EXCLUDE ) endforeach() else() message(FATAL_ERROR "Do not know what to do with ${name}") endif() endfunction() install_pykrita_plugin(hello) install_pykrita_plugin(assignprofiledialog) install_pykrita_plugin(scripter) #install_pykrita_plugin(highpass) install_pykrita_plugin(tenbrushes) +install( FILES + tenbrushes/tenbrushes.action +DESTINATION ${DATA_INSTALL_DIR}/krita/actions) + install_pykrita_plugin(palette_docker) # if(PYTHON_VERSION_MAJOR VERSION_EQUAL 3) # install_pykrita_plugin(cmake_utils) # install_pykrita_plugin(js_utils PATTERNS "*.json") # install_pykrita_plugin(expand PATTERNS "*.expand" "templates/*.tpl") # endif() install( DIRECTORY libkritapykrita DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita FILES_MATCHING PATTERN "*.py" PATTERN "__pycache__*" EXCLUDE ) diff --git a/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/assignprofiledialog.py b/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/assignprofiledialog.py index c18777c99d..ae06885a17 100644 --- a/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/assignprofiledialog.py +++ b/plugins/extensions/pykrita/plugin/plugins/assignprofiledialog/assignprofiledialog.py @@ -1,44 +1,44 @@ 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("Assign Profile to Image") + action = Application.createAction("assing_profile_to_image", "Assign Profile to Image") action.triggered.connect(self.assignProfile) Scripter.addExtension(AssignProfileDialog(Application)) diff --git a/plugins/extensions/pykrita/plugin/plugins/hello/hello.py b/plugins/extensions/pykrita/plugin/plugins/hello/hello.py index 9f1ef780bc..c4bc7036f6 100644 --- a/plugins/extensions/pykrita/plugin/plugins/hello/hello.py +++ b/plugins/extensions/pykrita/plugin/plugins/hello/hello.py @@ -1,31 +1,31 @@ import sys from PyQt5.QtGui import * from PyQt5.QtWidgets import * from krita import * def hello(): QMessageBox.information(QWidget(), "Test", "Hello! This is Krita " + Application.version()) class HelloExtension(Extension): def __init__(self, parent): super().__init__(parent) def setup(self): qDebug("Hello Setup") - action = Krita.instance().createAction("hello") + action = Krita.instance().createAction("hello_python", "hello") action.triggered.connect(hello) Scripter.addExtension(HelloExtension(Krita.instance())) class HelloDocker(DockWidget): def __init__(self): super().__init__() label = QLabel("Hello", self) self.setWidget(label) self.label = label def canvasChanged(self, canvas): self.label.setText("Hellodocker: canvas changed"); Application.addDockWidgetFactory(DockWidgetFactory("hello", DockWidgetFactoryBase.DockRight, HelloDocker)) diff --git a/plugins/extensions/pykrita/plugin/plugins/highpass/highpass.py b/plugins/extensions/pykrita/plugin/plugins/highpass/highpass.py index 853cd8884c..61abc85096 100644 --- a/plugins/extensions/pykrita/plugin/plugins/highpass/highpass.py +++ b/plugins/extensions/pykrita/plugin/plugins/highpass/highpass.py @@ -1,117 +1,117 @@ 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") + action = Application.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/extensions/pykrita/plugin/plugins/scripter/scripter.py b/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py index 825316d876..cc3d0f2baa 100644 --- a/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/scripter.py @@ -1,25 +1,25 @@ 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("Scripter") + action = Krita.instance().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/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.action b/plugins/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.action new file mode 100644 index 0000000000..3e0d27ed78 --- /dev/null +++ b/plugins/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.action @@ -0,0 +1,137 @@ + + + + Ten Brushes + + + + Activate Brush Preset 1 + + + + 10000 + 0 + ctrl+alt+1 + false + + + + + + Activate Brush Preset 2 + + + + 10000 + 0 + ctrl+alt+2 + false + + + + + + Activate Brush Preset 3 + + + + 10000 + 0 + ctrl+alt+3 + false + + + + + + Activate Brush Preset 4 + + + + 10000 + 0 + ctrl+alt+4 + false + + + + + + Activate Brush Preset 5 + + + + 10000 + 0 + ctrl+alt+5 + false + + + + + + Activate Brush Preset 6 + + + + 10000 + 0 + ctrl+alt+6 + false + + + + + + Activate Brush Preset 7 + + + + 10000 + 0 + ctrl+alt+7 + false + + + + + + Activate Brush Preset 8 + + + + 10000 + 0 + ctrl+alt+8 + false + + + + + + Activate Brush Preset + + + + 10000 + 0 + ctrl+alt+9 + false + + + + + + Activate Brush Preset 10 + + + + 10000 + 0 + ctrl+alt+0 + false + + + + + diff --git a/plugins/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.py b/plugins/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.py index d0faa3e4c2..09476c168d 100644 --- a/plugins/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.py +++ b/plugins/extensions/pykrita/plugin/plugins/tenbrushes/tenbrushes.py @@ -1,112 +1,113 @@ import sys from PyQt5.QtGui import * from PyQt5.QtWidgets import * from krita import * class DropButton(QPushButton): def __init__(self, parent): super().__init__(parent) self.setFixedSize(64, 64) self.setIconSize(QSize(64, 64)) self.preset = None - + def selectPreset(self): self.preset = self.presetChooser.currentPreset().name() self.setIcon(QIcon(QPixmap.fromImage(self.presetChooser.currentPreset().image()))) - + class TenBrushesExtension(Extension): def __init__(self, parent): super().__init__(parent) self.buttons = [] self.actions = [] - + def setup(self): - action = Application.createAction("Ten Brushes") + action = Application.createAction("ten_brushes", "Ten Brushes") action.setToolTip("Assign ten brush presets to ten shortcuts.") action.triggered.connect(self.showDialog) - + # Read the ten selected brush presets from the settings selectedPresets = Application.readSetting("", "tenbrushes", "").split(',') allPresets = Application.resources("preset") # Setup up to ten actions and give them default shortcuts j = 0 self.actions = [] for i in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']: - action = Application.createAction("Activate Preset " + i) - action.setVisible(False) - action.setShortcut("CTRL+" + i) + action = Application.createAction("activate_preset_" + i, "Activate Preset " + i) + #action.setVisible(False) + action.setMenu("None") action.triggered.connect(self.activatePreset) if j < len(selectedPresets) and selectedPresets[j] in allPresets: action.preset = selectedPresets[j] else: action.preset = None self.actions.append(action) j = j + 1 - + def activatePreset(self): + print("activatePreset", self.sender().preset) 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]) - + def showDialog(self): self.dialog = QDialog(Application.activeWindow().qwindow()) - + self.buttonBox = QDialogButtonBox(self.dialog) self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.dialog.reject) - + vbox = QVBoxLayout(self.dialog) hbox = QHBoxLayout(self.dialog) - + self.presetChooser = PresetChooser(self.dialog) - + allPresets = Application.resources("preset") j = 0 - self.buttons = [] + self.buttons = [] for i in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']: buttonBox = QVBoxLayout() button = DropButton(self.dialog) button.setObjectName(i) button.clicked.connect(button.selectPreset) button.presetChooser = self.presetChooser - + if self.actions[j] and self.actions[j].preset and self.actions[j].preset in allPresets: p = allPresets[self.actions[j].preset]; button.preset = p.name() button.setIcon(QIcon(QPixmap.fromImage(p.image()))) - + buttonBox.addWidget(button) - label = QLabel("Ctrl+" + i) + label = QLabel("Ctrl+Alt+" + i) label.setAlignment(Qt.AlignHCenter) buttonBox.addWidget(label) hbox.addLayout(buttonBox) self.buttons.append(button) j = j + 1 - + vbox.addLayout(hbox) vbox.addWidget(self.presetChooser) vbox.addWidget(self.buttonBox) vbox.addWidget(QLabel("Select the brush preset, then click on the button you want to use to select the preset")) - + self.dialog.show() self.dialog.activateWindow() self.dialog.exec_() - - + + def accept(self): i = 0 presets = [] for button in self.buttons: self.actions[i].preset = button.preset presets.append(button.preset) i = i + 1 Application.writeSetting("", "tenbrushes", ','.join(map(str, presets))) self.dialog.accept() Scripter.addExtension(TenBrushesExtension(Application)) diff --git a/plugins/extensions/pykrita/sip/krita/Action.sip b/plugins/extensions/pykrita/sip/krita/Action.sip index d33fdddf54..8702596105 100644 --- a/plugins/extensions/pykrita/sip/krita/Action.sip +++ b/plugins/extensions/pykrita/sip/krita/Action.sip @@ -1,35 +1,37 @@ class Action : QObject { %TypeHeaderCode #include "Action.h" %End public: 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); 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/Krita.sip b/plugins/extensions/pykrita/sip/krita/Krita.sip index 98d2cb8d49..e18a4aa32c 100644 --- a/plugins/extensions/pykrita/sip/krita/Krita.sip +++ b/plugins/extensions/pykrita/sip/krita/Krita.sip @@ -1,58 +1,58 @@ class Krita : QObject { %TypeHeaderCode #include "Krita.h" %End public: public Q_SLOTS: Krita(QObject* parent /TransferThis/ = 0); virtual ~Krita(); 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 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/; Document * createDocument(int width, int height, const QString &name, const QString &colorModel, const QString &colorDepth, const QString &profile) /Factory/; Document * openDocument(const QString &filename) /Factory/; Window * openWindow(); - Action * createAction(const QString & text); + Action * createAction(const QString &id, const QString & text); 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 };