EventGhost/EventGhost

View on GitHub
eg/Classes/ActionItem.py

Summary

Maintainability
A
1 hr
Test Coverage
# -*- coding: utf-8 -*-
#
# This file is part of EventGhost.
# Copyright © 2005-2020 EventGhost Project <http://www.eventghost.net/>
#
# EventGhost 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.
#
# EventGhost 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 EventGhost. If not, see <http://www.gnu.org/licenses/>.

import wx

# Local imports
import eg
from TreeItem import (
    HINT_MOVE_BEFORE, HINT_MOVE_BEFORE_OR_AFTER, HINT_NO_DROP, TreeItem
)
from TreeLink import TreeLink

PATCHES = {
    "Registry.RegistryChange": "System.RegistryChange",
    "Registry.RegistryQuery": "System.RegistryQuery",
}

RENAMED_COLOUR = eg.colour.GetRenamedColor()

class ActionItem(TreeItem):
    xmlTag = "Action"
    icon = eg.Icons.ACTION_ICON
    executable = None
    compiled = None
    args = None
    isExecutable = True
    isConfigurable = True
    openConfigDialog = None
    helpDialog = None
    shouldSelectOnExecute = False
    url = None

    @eg.AssertInActionThread
    def __init__(self, parent, node):
        TreeItem.__init__(self, parent, node)
        text = node.text
        if not text:
            # this should never happen
            return
        text = text.strip()
        objStr, remainder = text.split('(', 1)
        objStr = PATCHES.get(objStr, objStr)
        argString, _ = remainder.rsplit(')', 1)
        pluginStr, actionStr = objStr.split(".", 1)
        plugin = getattr(eg.plugins, pluginStr).plugin
        try:
            action = plugin.info.actions[actionStr]
        except KeyError:
            eg.PrintError("Can't find action: " + text)
            action = None
        if action is None or not issubclass(action, eg.ActionBase):
            action = eg.plugins.EventGhost.plugin.info.actions["PythonCommand"]
            argString = repr(text)
        try:
            self.executable = action()
        except:
            eg.PrintTraceback(msg="Error in action: " + repr(text))
            self.executable = eg.plugins.EventGhost.PythonCommand
            argString = repr(text)

        self.icon = action.info.icon
        self.SetArgumentString(argString)

    def Configure(self, *args):
        return self.executable.Configure(*args)

    @eg.AssertInActionThread
    def Delete(self):
        TreeItem.Delete(self)
        for arg in self.GetArguments():
            if isinstance(arg, TreeLink):
                if arg.target:
                    arg.target.dependants.remove(arg)
                arg.owner = None
                arg.target = None
                del arg

    def DropTest(self, dropNode):
        if self.parent == self.document.autostartMacro:
            if dropNode.xmlTag == "Plugin":
                return HINT_MOVE_BEFORE_OR_AFTER
        elif dropNode.xmlTag == "Event":
            return HINT_MOVE_BEFORE
        if dropNode.xmlTag == "Action":
            return HINT_MOVE_BEFORE_OR_AFTER
        return HINT_NO_DROP

    @eg.AssertInActionThread
    def Execute(self):
        if not self.isEnabled:
            return
        if eg.config.logActions:
            self.Print(self.GetLabel())
        if self.shouldSelectOnExecute:
            self.Select()
        eg.currentItem = self
        action = self.executable
        if not action:
            return
        eg.indent += 1
        try:
            if not action.plugin.info.isStarted:
                self.PrintError(
                    eg.text.Error.pluginNotActivated % action.plugin.name
                )
                return
            try:
                eg.result = self.compiled()
            except eg.Exception, exc:
                self.PrintError(unicode(exc))
            except:
                label = self.GetLabel()
                eg.PrintTraceback(
                    eg.text.Error.InAction % label, 1, source=self
                )
        finally:
            eg.indent -= 1

    # The Find function calls this from MainThread, so we can't restrict this
    # to the ActionThread
    #@eg.AssertInActionThread
    def GetArguments(self):
        return self.args

    @eg.AssertInActionThread
    def GetArgumentString(self):
        return ", ".join([repr(arg) for arg in self.GetArguments()])

    def GetBasePath(self):
        """
        Returns the filesystem path, where additional files (like pictures)
        should be found.
        """
        return self.executable.plugin.info.path

    def GetData(self):
        attr, text = TreeItem.GetData(self)
        action = self.executable
        text = "%s.%s(%s)" % (
            action.plugin.info.evalName,
            action.__class__.__name__,
            ", ".join([repr(arg) for arg in self.args])
        )
        return attr, text

    def GetDescription(self):
        return self.executable.description

    def GetLabel(self):
        if self.name:
            return self.name

        # often the GetLabel() method of the executable can't handle
        # a call without arguments, because suitable default arguments
        # are missing. So we use a fallback in such cases.
        executable = self.executable
        try:
            name = executable.GetLabel(*self.args)
        except:
            name = executable.name
        pluginInfo = executable.plugin.info
        if pluginInfo.kind != "core":
            name = pluginInfo.label + ": " + name
        return name

    def GetTypeName(self):
        return "%s: %s" % (
            self.executable.plugin.info.label,
            self.executable.name
        )

    def NeedsStartupConfiguration(self):
        """
        Returns True if the item wants to be configured after creation.
        """
        # if the Configure method of the executable is overriden, we assume
        # the item wants to be configured after creation
        return (
            self.executable.Configure.im_func !=
            eg.ActionBase.Configure.im_func
        )

    @eg.AssertInActionThread
    def SetArguments(self, args):
        eg.currentItem = self
        if self.args != args:
            self.args = args
            try:
                self.compiled = self.executable.Compile(*args)
            except:
                eg.PrintTraceback(source=self)
                self.compiled = None

    @eg.AssertInActionThread
    def SetArgumentString(self, argString):
        try:
            args = eval(
                'returnArgs(%s)' % argString,
                eg.globals.__dict__,
                dict(
                    returnArgs=lambda *x: x,
                    XmlIdLink=lambda id: TreeLink.CreateFromArgument(self, id),
                )
            )
        except:
            eg.PrintTraceback()
            args = ()
        self.SetArguments(args)

    def SetAttributes(self, tree, treeId):
        if self.name:
            tree.SetItemTextColour(treeId, RENAMED_COLOUR)
            tree.SetItemFont(treeId, tree.italicfont)
        else:
            tree.SetItemTextColour(treeId, None)
            tree.SetItemFont(treeId, tree.normalfont)

    @eg.LogIt
    def ShowHelp(self, parent=None):
        if self.helpDialog:
            self.helpDialog.Raise()
            return
        action = self.executable
        self.helpDialog = eg.HtmlDialog(
            parent,
            action.name,
            action.description,
            action.info.icon.GetWxIcon(),
            self.GetBasePath()
        )

        def OnClose(dummyEvent):
            self.helpDialog.Destroy()
            del self.helpDialog

        self.helpDialog.Bind(wx.EVT_CLOSE, OnClose)
        self.helpDialog.buttonRow.okButton.Bind(wx.EVT_BUTTON, OnClose)
        self.helpDialog.Show()