1"""
2Copyright (c) 2017 Eliakin Costa <eliakim170@gmail.com>
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17"""
18from PyQt5.QtCore import Qt, QObject, QFileInfo, QRect
19from PyQt5.QtGui import QTextCursor, QPalette
20from PyQt5.QtWidgets import (QToolBar, QMenuBar, QTabWidget,
21                             QLabel, QVBoxLayout, QMessageBox,
22                             QSplitter, QSizePolicy)
23from .ui_scripter.syntax import syntax, syntaxstyles
24from .ui_scripter.editor import pythoneditor
25from . import scripterdialog
26import importlib
27import krita
28
29KEY_GEOMETRY = "geometry"
30DEFAULT_GEOMETRY = QRect(600, 200, 400, 500)
31# essentially randomly placed
32
33
34class Elided_Text_Label(QLabel):
35    mainText = str()
36
37    def __init__(self, parent=None):
38        super(QLabel, self).__init__(parent)
39        self.setMinimumWidth(self.fontMetrics().width("..."))
40        self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
41
42    def setMainText(self, text=str()):
43        self.mainText = text
44        self.elideText()
45
46    def elideText(self):
47        self.setText(self.fontMetrics().elidedText(self.mainText, Qt.ElideRight, self.width()))
48
49    def resizeEvent(self, event):
50        self.elideText()
51
52
53class UIController(object):
54    documentModifiedText = ""
55    documentStatusBarText = "untitled"
56
57    def __init__(self):
58        self.mainWidget = scripterdialog.ScripterDialog(self)
59        self.actionToolbar = QToolBar('toolBar', self.mainWidget)
60        self.menu_bar = QMenuBar(self.mainWidget)
61
62        self.actionToolbar.setObjectName('toolBar')
63        self.menu_bar.setObjectName('menuBar')
64
65        self.actions = []
66
67        self.mainWidget.setWindowModality(Qt.NonModal)
68
69    def initialize(self, scripter):
70        self.editor = pythoneditor.CodeEditor(scripter)
71        self.tabWidget = QTabWidget()
72        self.statusBar = Elided_Text_Label()
73        self.statusBar.setMainText('untitled')
74        self.splitter = QSplitter()
75        self.splitter.setOrientation(Qt.Vertical)
76        self.highlight = syntax.PythonHighlighter(self.editor.document(), syntaxstyles.DefaultSyntaxStyle())
77        p = self.editor.palette()
78        p.setColor(QPalette.Base, syntaxstyles.DefaultSyntaxStyle()['background'].foreground().color())
79        p.setColor(QPalette.Text, syntaxstyles.DefaultSyntaxStyle()['foreground'].foreground().color())
80        self.editor.setPalette(p)
81
82        self.scripter = scripter
83
84        self.loadMenus()
85        self.loadWidgets()
86        self.loadActions()
87        self._readSettings()  # sets window size
88
89        vbox = QVBoxLayout(self.mainWidget)
90        vbox.addWidget(self.menu_bar)
91        vbox.addWidget(self.actionToolbar)
92        self.splitter.addWidget(self.editor)
93        self.splitter.addWidget(self.tabWidget)
94        vbox.addWidget(self.splitter)
95        vbox.addWidget(self.statusBar)
96
97        self.mainWidget.setWindowTitle(i18n("Scripter"))
98        self.mainWidget.setSizeGripEnabled(True)
99        self.mainWidget.show()
100        self.mainWidget.activateWindow()
101
102        self.editor.undoAvailable.connect(self.setStatusModified)
103
104    def loadMenus(self):
105        self.addMenu(i18n('File'), i18n('File'))
106
107    def addMenu(self, menuName, parentName):
108        parent = self.menu_bar.findChild(QObject, parentName)
109        self.newMenu = None
110
111        if parent:
112            self.newMenu = parent.addMenu(menuName)
113        else:
114            self.newMenu = self.menu_bar.addMenu(menuName)
115
116        self.newMenu.setObjectName(menuName)
117
118        return self.newMenu
119
120    def loadActions(self):
121        module_path = 'scripter.ui_scripter.actions'
122        actions_module = importlib.import_module(module_path)
123        modules = []
124
125        for class_path in actions_module.action_classes:
126            _module = class_path[:class_path.rfind(".")]
127            _klass = class_path[class_path.rfind(".") + 1:]
128            modules.append(dict(module='{0}.{1}'.format(module_path, _module),
129                                klass=_klass))
130
131        for module in modules:
132            m = importlib.import_module(module['module'])
133            action_class = getattr(m, module['klass'])
134            obj = action_class(self.scripter)
135            obj_parent = obj.parent
136            for name in obj_parent:
137                parent = self.mainWidget.findChild(QObject, i18n(name))
138                self.actions.append(dict(action=obj, parent=parent))
139
140        for action in self.actions:
141            action['parent'].addAction(action['action'])
142
143    def loadWidgets(self):
144        modulePath = 'scripter.ui_scripter.tabwidgets'
145        widgetsModule = importlib.import_module(modulePath)
146        modules = []
147
148        for class_path in widgetsModule.widgetClasses:
149            _module = class_path[:class_path.rfind(".")]
150            _klass = class_path[class_path.rfind(".") + 1:]
151            modules.append(dict(module='{0}.{1}'.format(modulePath, _module),
152                                klass=_klass))
153
154        for module in modules:
155            m = importlib.import_module(module['module'])
156            widgetClass = getattr(m, module['klass'])
157            obj = widgetClass(self.scripter)
158            self.tabWidget.addTab(obj, obj.objectName())
159
160    def invokeAction(self, actionName):
161        for action in self.actions:
162            if action['action'].objectName() == actionName:
163                method = getattr(action['action'], actionName)
164                if method:
165                    return method()
166
167    def findTabWidget(self, widgetName, childName=''):
168        for index in range(self.tabWidget.count()):
169            widget = self.tabWidget.widget(index)
170            if widget.objectName() == widgetName:
171                if childName:
172                    widget = widget.findChild(QObject, childName)
173                return widget
174
175    def showException(self, exception):
176        QMessageBox.critical(self.editor, i18n("Error Running Script"), str(exception))
177
178    def setDocumentEditor(self, document):
179        self.editor.clear()
180        self.editor.moveCursor(QTextCursor.Start)
181        self.editor._documentModified = False
182        self.editor.setPlainText(document.data)
183        self.editor.moveCursor(QTextCursor.End)
184
185    def setStatusBar(self, value='untitled'):
186        self.documentStatusBarText = value
187        self.statusBar.setMainText(self.documentStatusBarText + self.documentModifiedText)
188
189    def setStatusModified(self):
190        self.documentModifiedText = ""
191        if (self.editor._documentModified is True):
192            self.documentModifiedText = " [Modified]"
193        self.statusBar.setMainText(self.documentStatusBarText + self.documentModifiedText)
194
195    def setActiveWidget(self, widgetName):
196        widget = self.findTabWidget(widgetName)
197
198        if widget:
199            self.tabWidget.setCurrentWidget(widget)
200
201    def setStepped(self, status):
202        self.editor.setStepped(status)
203
204    def clearEditor(self):
205        self.editor.clear()
206
207    def repaintDebugArea(self):
208        self.editor.repaintDebugArea()
209
210    def closeScripter(self):
211        self.mainWidget.close()
212
213    def _writeSettings(self):
214        """ _writeSettings is a method invoked when the scripter starts, making
215            control inversion. Actions can implement a writeSettings method to
216            save your own settings without this method to know about it. """
217
218        self.scripter.settings.beginGroup('scripter')
219
220        document = self.scripter.documentcontroller.activeDocument
221        if document:
222            self.scripter.settings.setValue('activeDocumentPath', document.filePath)
223        else:
224            self.scripter.settings.setValue('activeDocumentPath', '')
225
226        self.scripter.settings.setValue('editorFontSize', self.editor.fontInfo().pointSize())
227
228        for action in self.actions:
229            writeSettings = getattr(action['action'], "writeSettings", None)
230            if callable(writeSettings):
231                writeSettings()
232
233        #  Window Geometry
234        rect = self.mainWidget.geometry()
235        self.scripter.settings.setValue(KEY_GEOMETRY, rect)
236
237        self.scripter.settings.endGroup()
238
239    def _readSettings(self):
240        """ It's similar to _writeSettings, but reading the settings when the ScripterDialog is closed. """
241
242        self.scripter.settings.beginGroup('scripter')
243
244        activeDocumentPath = self.scripter.settings.value('activeDocumentPath', '')
245
246        if activeDocumentPath:
247            if QFileInfo(activeDocumentPath).exists():
248                document = self.scripter.documentcontroller.openDocument(activeDocumentPath)
249                self.setStatusBar(document.filePath)
250                self.setDocumentEditor(document)
251
252        for action in self.actions:
253            readSettings = getattr(action['action'], "readSettings", None)
254            if callable(readSettings):
255                readSettings()
256
257        pointSize = self.scripter.settings.value('editorFontSize', str(self.editor.fontInfo().pointSize()))
258        self.editor.setFontSize(int(pointSize))
259
260        # Window Geometry
261        rect = self.scripter.settings.value(KEY_GEOMETRY, DEFAULT_GEOMETRY)
262        self.mainWidget.setGeometry(rect)
263
264        self.scripter.settings.endGroup()
265
266    def _saveSettings(self):
267        self.scripter.settings.sync()
268