eric7/Templates/TemplateViewer.py

branch
eric7
changeset 8312
800c432b34c8
parent 8260
2161475d9639
child 8314
e3642a6a1e71
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric7/Templates/TemplateViewer.py	Sat May 15 18:45:04 2021 +0200
@@ -0,0 +1,1086 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2005 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a template viewer and associated classes.
+"""
+
+import datetime
+import os
+import re
+
+from PyQt5.QtCore import QFile, QFileInfo, QIODevice, Qt, QCoreApplication
+from PyQt5.QtWidgets import (
+    QTreeWidget, QDialog, QApplication, QMenu, QTreeWidgetItem
+)
+
+from E5Gui.E5Application import e5App
+from E5Gui import E5MessageBox, E5FileDialog
+
+import Preferences
+
+import UI.PixmapCache
+import Utilities
+
+from .TemplatesFile import TemplatesFile
+
+
+class TemplateGroup(QTreeWidgetItem):
+    """
+    Class implementing a template group.
+    """
+    def __init__(self, parent, name, language="All"):
+        """
+        Constructor
+        
+        @param parent parent widget of the template group (QWidget)
+        @param name name of the group (string)
+        @param language programming language for the group (string)
+        """
+        self.name = name
+        self.language = language
+        self.entries = {}
+        
+        super().__init__(parent, [name])
+        
+        if Preferences.getTemplates("ShowTooltip"):
+            self.setToolTip(0, language)
+    
+    def setName(self, name):
+        """
+        Public method to update the name of the group.
+        
+        @param name name of the group (string)
+        """
+        self.name = name
+        self.setText(0, name)
+
+    def getName(self):
+        """
+        Public method to get the name of the group.
+        
+        @return name of the group (string)
+        """
+        return self.name
+        
+    def setLanguage(self, language):
+        """
+        Public method to update the name of the group.
+        
+        @param language programming language for the group (string)
+        """
+        self.language = language
+        if Preferences.getTemplates("ShowTooltip"):
+            self.setToolTip(0, language)
+
+    def getLanguage(self):
+        """
+        Public method to get the name of the group.
+        
+        @return language of the group (string)
+        """
+        return self.language
+        
+    def addEntry(self, name, description, template, quiet=False):
+        """
+        Public method to add a template entry to this group.
+        
+        @param name name of the entry (string)
+        @param description description of the entry to add (string)
+        @param template template text of the entry (string)
+        @param quiet flag indicating quiet operation (boolean)
+        """
+        if name in self.entries:
+            if not quiet:
+                E5MessageBox.critical(
+                    None,
+                    QCoreApplication.translate("TemplateGroup",
+                                               "Add Template"),
+                    QCoreApplication.translate(
+                        "TemplateGroup",
+                        """<p>The group <b>{0}</b> already contains a"""
+                        """ template named <b>{1}</b>.</p>""")
+                    .format(self.name, name))
+            return
+        
+        self.entries[name] = TemplateEntry(self, name, description, template)
+        
+        if (
+            Preferences.getTemplates("AutoOpenGroups") and
+            not self.isExpanded()
+        ):
+            self.setExpanded(True)
+    
+    def removeEntry(self, name):
+        """
+        Public method to remove a template entry from this group.
+        
+        @param name name of the entry to be removed (string)
+        """
+        if name in self.entries:
+            index = self.indexOfChild(self.entries[name])
+            self.takeChild(index)
+            del self.entries[name]
+            
+            if (
+                len(self.entries) == 0 and
+                Preferences.getTemplates("AutoOpenGroups") and
+                self.isExpanded()
+            ):
+                self.setExpanded(False)
+    
+    def removeAllEntries(self):
+        """
+        Public method to remove all template entries of this group.
+        """
+        for name in list(self.entries.keys())[:]:
+            self.removeEntry(name)
+
+    def hasEntry(self, name):
+        """
+        Public method to check, if the group has an entry with the given name.
+        
+        @param name name of the entry to check for (string)
+        @return flag indicating existence (boolean)
+        """
+        return name in self.entries
+    
+    def getEntry(self, name):
+        """
+        Public method to get an entry.
+        
+        @param name name of the entry to retrieve (string)
+        @return reference to the entry (TemplateEntry)
+        """
+        try:
+            return self.entries[name]
+        except KeyError:
+            return None
+
+    def getEntryNames(self, beginning):
+        """
+        Public method to get the names of all entries, who's name starts with
+        the given string.
+        
+        @param beginning string denoting the beginning of the template name
+            (string)
+        @return list of entry names found (list of strings)
+        """
+        names = []
+        for name in self.entries:
+            if name.startswith(beginning):
+                names.append(name)
+        
+        return names
+
+    def getAllEntries(self):
+        """
+        Public method to retrieve all entries.
+        
+        @return list of all entries (list of TemplateEntry)
+        """
+        return list(self.entries.values())
+
+
+class TemplateEntry(QTreeWidgetItem):
+    """
+    Class immplementing a template entry.
+    """
+    def __init__(self, parent, name, description, templateText):
+        """
+        Constructor
+        
+        @param parent parent widget of the template entry (QWidget)
+        @param name name of the entry (string)
+        @param description descriptive text for the template (string)
+        @param templateText text of the template entry (string)
+        """
+        self.name = name
+        self.description = description
+        self.template = templateText
+        self.__extractVariables()
+        
+        super().__init__(parent, [self.__displayText()])
+        if Preferences.getTemplates("ShowTooltip"):
+            self.setToolTip(0, self.template)
+
+    def __displayText(self):
+        """
+        Private method to generate the display text.
+        
+        @return display text (string)
+        """
+        txt = (
+            "{0} - {1}".format(self.name, self.description)
+            if self.description else
+            self.name
+        )
+        return txt
+    
+    def setName(self, name):
+        """
+        Public method to update the name of the entry.
+        
+        @param name name of the entry (string)
+        """
+        self.name = name
+        self.setText(0, self.__displayText())
+
+    def getName(self):
+        """
+        Public method to get the name of the entry.
+        
+        @return name of the entry (string)
+        """
+        return self.name
+
+    def setDescription(self, description):
+        """
+        Public method to update the description of the entry.
+        
+        @param description description of the entry (string)
+        """
+        self.description = description
+        self.setText(0, self.__displayText())
+
+    def getDescription(self):
+        """
+        Public method to get the description of the entry.
+        
+        @return description of the entry (string)
+        """
+        return self.description
+
+    def getGroupName(self):
+        """
+        Public method to get the name of the group this entry belongs to.
+        
+        @return name of the group containing this entry (string)
+        """
+        return self.parent().getName()
+        
+    def setTemplateText(self, templateText):
+        """
+        Public method to update the template text.
+        
+        @param templateText text of the template entry (string)
+        """
+        self.template = templateText
+        self.__extractVariables()
+        if Preferences.getTemplates("ShowTooltip"):
+            self.setToolTip(0, self.template)
+
+    def getTemplateText(self):
+        """
+        Public method to get the template text.
+        
+        @return the template text (string)
+        """
+        return self.template
+
+    def getExpandedText(self, varDict, indent):
+        """
+        Public method to get the template text with all variables expanded.
+        
+        @param varDict dictionary containing the texts of each variable
+            with the variable name as key.
+        @param indent indentation of the line receiving he expanded
+            template text (string)
+        @return a tuple of the expanded template text (string), the
+            number of lines (integer) and the length of the last line (integer)
+        """
+        txt = self.template
+        for var, val in list(varDict.items()):
+            txt = (
+                self.__expandFormattedVariable(var, val, txt)
+                if var in self.formatedVariables else
+                txt.replace(var, val)
+            )
+        sepchar = Preferences.getTemplates("SeparatorChar")
+        txt = txt.replace("{0}{1}".format(sepchar, sepchar), sepchar)
+        prefix = "{0}{1}".format(os.linesep, indent)
+        trailingEol = txt.endswith(os.linesep)
+        lines = txt.splitlines()
+        lineCount = len(lines)
+        lineLen = len(lines[-1])
+        txt = prefix.join(lines).lstrip()
+        if trailingEol:
+            txt = "{0}{1}".format(txt, os.linesep)
+            lineCount += 1
+            lineLen = 0
+        return txt, lineCount, lineLen
+
+    def __expandFormattedVariable(self, var, val, txt):
+        """
+        Private method to expand a template variable with special formatting.
+        
+        @param var template variable name (string)
+        @param val value of the template variable (string)
+        @param txt template text (string)
+        @return expanded and formatted variable (string)
+        """
+        t = ""
+        for line in txt.splitlines():
+            ind = line.find(var)
+            if ind >= 0:
+                variableFormat = var[1:-1].split(':', 1)[1]
+                if variableFormat == 'rl':
+                    prefix = line[:ind]
+                    postfix = line[ind + len(var):]
+                    for v in val.splitlines():
+                        t = "{0}{1}{2}{3}{4}".format(
+                            t, os.linesep, prefix, v, postfix)
+                elif variableFormat == 'ml':
+                    indent = line.replace(line.lstrip(), "")
+                    prefix = line[:ind]
+                    postfix = line[ind + len(var):]
+                    for count, v in enumerate(val.splitlines()):
+                        t = (
+                            "{0}{1}{2}{3}".format(t, os.linesep, indent, v)
+                            if count else
+                            "{0}{1}{2}{3}".format(t, os.linesep, prefix, v)
+                        )
+                    t = "{0}{1}".format(t, postfix)
+                else:
+                    t = "{0}{1}{2}".format(t, os.linesep, line)
+            else:
+                t = "{0}{1}{2}".format(t, os.linesep, line)
+        return "".join(t.splitlines(1)[1:])
+
+    def getVariables(self):
+        """
+        Public method to get the list of variables.
+        
+        @return list of variables (list of strings)
+        """
+        return self.variables
+
+    def __extractVariables(self):
+        """
+        Private method to retrieve the list of variables.
+        """
+        sepchar = Preferences.getTemplates("SeparatorChar")
+        variablesPattern = re.compile(
+            r"""\{0}[a-zA-Z][a-zA-Z0-9_]*(?::(?:ml|rl))?\{1}""".format(
+                sepchar, sepchar)
+        )
+        variables = variablesPattern.findall(self.template)
+        self.variables = []
+        self.formatedVariables = []
+        for var in variables:
+            if var not in self.variables:
+                self.variables.append(var)
+            if var.find(':') >= 0 and var not in self.formatedVariables:
+                self.formatedVariables.append(var)
+
+
+class TemplateViewer(QTreeWidget):
+    """
+    Class implementing the template viewer.
+    """
+    def __init__(self, parent, viewmanager):
+        """
+        Constructor
+        
+        @param parent the parent (QWidget)
+        @param viewmanager reference to the viewmanager object
+        """
+        super().__init__(parent)
+        
+        self.viewmanager = viewmanager
+        self.groups = {}
+        
+        self.setHeaderLabels(["Template"])
+        self.header().hide()
+        self.header().setSortIndicator(0, Qt.SortOrder.AscendingOrder)
+        self.setRootIsDecorated(True)
+        self.setAlternatingRowColors(True)
+        
+        self.__menu = QMenu(self)
+        self.applyAct = self.__menu.addAction(
+            self.tr("Apply"), self.__templateItemActivated)
+        self.__menu.addSeparator()
+        self.__menu.addAction(self.tr("Add entry..."), self.__addEntry)
+        self.__menu.addAction(self.tr("Add group..."), self.__addGroup)
+        self.__menu.addAction(self.tr("Edit..."), self.__edit)
+        self.__menu.addAction(self.tr("Remove"), self.__remove)
+        self.__menu.addSeparator()
+        self.saveAct = self.__menu.addAction(self.tr("Save"), self.save)
+        self.__menu.addAction(self.tr("Import..."), self.__import)
+        self.__menu.addAction(self.tr("Export..."), self.__export)
+        self.__menu.addAction(self.tr("Reload"), self.__reload)
+        self.__menu.addSeparator()
+        self.__menu.addAction(
+            self.tr("Help about Templates..."), self.__showHelp)
+        self.__menu.addSeparator()
+        self.__menu.addAction(self.tr("Configure..."), self.__configure)
+        
+        self.__backMenu = QMenu(self)
+        self.__backMenu.addAction(self.tr("Add group..."), self.__addGroup)
+        self.__backMenu.addSeparator()
+        self.bmSaveAct = self.__backMenu.addAction(self.tr("Save"), self.save)
+        self.__backMenu.addAction(self.tr("Import..."), self.__import)
+        self.bmExportAct = self.__backMenu.addAction(
+            self.tr("Export..."), self.__export)
+        self.__backMenu.addAction(self.tr("Reload"), self.__reload)
+        self.__backMenu.addSeparator()
+        self.__backMenu.addAction(
+            self.tr("Help about Templates..."), self.__showHelp)
+        self.__backMenu.addSeparator()
+        self.__backMenu.addAction(
+            self.tr("Configure..."), self.__configure)
+        
+        self.__activating = False
+        self.__dirty = False
+        
+        self.__templatesFile = TemplatesFile(self)
+        
+        self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
+        self.customContextMenuRequested.connect(self.__showContextMenu)
+        self.itemActivated.connect(self.__templateItemActivated)
+        
+        self.setWindowIcon(UI.PixmapCache.getIcon("eric"))
+        
+    def __resort(self):
+        """
+        Private method to resort the tree.
+        """
+        self.sortItems(self.sortColumn(), self.header().sortIndicatorOrder())
+        
+    def __templateItemActivated(self, itm=None, col=0):
+        """
+        Private slot to handle the activation of an item.
+        
+        @param itm reference to the activated item (QTreeWidgetItem)
+        @param col column the item was activated in (integer)
+        """
+        if not self.__activating:
+            self.__activating = True
+            itm = self.currentItem()
+            if isinstance(itm, TemplateEntry):
+                self.applyTemplate(itm)
+            self.__activating = False
+        
+    def __showContextMenu(self, coord):
+        """
+        Private slot to show the context menu of the list.
+        
+        @param coord the position of the mouse pointer (QPoint)
+        """
+        itm = self.itemAt(coord)
+        coord = self.mapToGlobal(coord)
+        if itm is None:
+            self.bmSaveAct.setEnabled(self.__dirty)
+            self.bmExportAct.setEnabled(self.topLevelItemCount() != 0)
+            self.__backMenu.popup(coord)
+        else:
+            self.applyAct.setEnabled(
+                self.viewmanager.activeWindow() is not None and
+                isinstance(itm, TemplateEntry))
+            self.saveAct.setEnabled(self.__dirty)
+            self.__menu.popup(coord)
+    
+    def __addEntry(self):
+        """
+        Private slot to handle the Add Entry context menu action.
+        """
+        itm = self.currentItem()
+        groupName = (
+            itm.getName()
+            if isinstance(itm, TemplateGroup) else
+            itm.getGroupName()
+        )
+        
+        from .TemplatePropertiesDialog import TemplatePropertiesDialog
+        dlg = TemplatePropertiesDialog(self)
+        dlg.setSelectedGroup(groupName)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            name, description, groupName, template = dlg.getData()
+            self.addEntry(groupName, name, description, template)
+            self.__dirty = True
+        
+    def __addGroup(self):
+        """
+        Private slot to handle the Add Group context menu action.
+        """
+        from .TemplatePropertiesDialog import TemplatePropertiesDialog
+        dlg = TemplatePropertiesDialog(self, True)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            name, language = dlg.getData()
+            self.addGroup(name, language)
+            self.__dirty = True
+        
+    def __edit(self):
+        """
+        Private slot to handle the Edit context menu action.
+        """
+        itm = self.currentItem()
+        editGroup = not isinstance(itm, TemplateEntry)
+        
+        from .TemplatePropertiesDialog import TemplatePropertiesDialog
+        dlg = TemplatePropertiesDialog(self, editGroup, itm)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            if editGroup:
+                name, language = dlg.getData()
+                self.changeGroup(itm.getName(), name, language)
+            else:
+                name, description, groupName, template = dlg.getData()
+                self.changeEntry(itm, name, groupName, description, template)
+            self.__dirty = True
+        
+    def __remove(self):
+        """
+        Private slot to handle the Remove context menu action.
+        """
+        itm = self.currentItem()
+        res = E5MessageBox.yesNo(
+            self,
+            self.tr("Remove Template"),
+            self.tr("""<p>Do you really want to remove <b>{0}</b>?</p>""")
+            .format(itm.getName()))
+        if not res:
+            return
+
+        if isinstance(itm, TemplateGroup):
+            self.removeGroup(itm)
+        else:
+            self.removeEntry(itm)
+        self.__dirty = True
+
+    def save(self):
+        """
+        Public slot to save the templates.
+        """
+        if self.__dirty:
+            ok = self.writeTemplates()
+            if ok:
+                self.__dirty = False
+
+    def __import(self):
+        """
+        Private slot to handle the Import context menu action.
+        """
+        fn = E5FileDialog.getOpenFileName(
+            self,
+            self.tr("Import Templates"),
+            "",
+            self.tr("Templates Files (*.ecj);;"
+                    "XML Templates Files (*.e4c);;"
+                    "All Files (*)"))
+        
+        if fn:
+            self.readTemplates(fn)
+            self.__dirty = True
+
+    def __export(self):
+        """
+        Private slot to handle the Export context menu action.
+        """
+        fn, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
+            self,
+            self.tr("Export Templates"),
+            "",
+            self.tr("Templates Files (*.ecj);;"
+                    "XML Templates Files (*.e4c);;"
+                    "All Files (*)"),
+            "",
+            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
+        
+        if fn:
+            ext = QFileInfo(fn).suffix()
+            if not ext:
+                ex = selectedFilter.split("(*")[1].split(")")[0]
+                if ex:
+                    fn += ex
+            if os.path.exists(fn):
+                ok = E5MessageBox.yesNo(
+                    self,
+                    self.tr("Export Templates"),
+                    self.tr("""<p>The templates file <b>{0}</b> exists"""
+                            """ already. Overwrite it?</p>""").format(fn))
+            else:
+                ok = True
+            
+            if ok:
+                self.writeTemplates(fn)
+    
+    def __reload(self):
+        """
+        Private slot to reload the templates.
+        """
+        if self.__dirty:
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr("Reload Templates"),
+                self.tr(
+                    """The templates contain unsaved changes. Shall these"""
+                    """ changes be discarded?"""),
+                icon=E5MessageBox.Warning)
+            if not res:
+                return
+        
+        self.clear()
+        self.groups = {}
+        
+        self.readTemplates()
+    
+    def __showHelp(self):
+        """
+        Private method to show some help.
+        """
+        E5MessageBox.information(
+            self,
+            self.tr("Template Help"),
+            self.tr(
+                """<p><b>Template groups</b> are a means of grouping"""
+                """ individual templates. Groups have an attribute that"""
+                """ specifies, which programming language they apply for."""
+                """ In order to add template entries, at least one group"""
+                """ has to be defined.</p>"""
+                """<p><b>Template entries</b> are the actual templates."""
+                """ They are grouped by the template groups. Help about"""
+                """ how to define them is available in the template edit"""
+                """ dialog.</p>"""))
+
+    def __getPredefinedVars(self):
+        """
+        Private method to return predefined variables.
+        
+        @return dictionary of predefined variables and their values
+        """
+        project = e5App().getObject("Project")
+        editor = self.viewmanager.activeWindow()
+        now = datetime.datetime.now()
+        sepchar = Preferences.getTemplates("SeparatorChar")
+        keyfmt = sepchar + "{0}" + sepchar
+        varValues = {
+            keyfmt.format('date'): now.date().isoformat(),
+            keyfmt.format('year'): str(now.date().year),
+            keyfmt.format('time'): now.time().strftime("%H:%M:%S"),
+        }
+
+        if project.name:
+            varValues[keyfmt.format('project_name')] = project.name
+
+        if project.ppath:
+            varValues[keyfmt.format('project_path')] = project.ppath
+
+        path_name = editor.getFileName()
+        if path_name:
+            dir_name, file_name = os.path.split(path_name)
+            base_name, ext = os.path.splitext(file_name)
+            if ext:
+                ext = ext[1:]
+            path_name_rel = project.getRelativePath(path_name)
+            dir_name_rel = project.getRelativePath(dir_name)
+            varValues.update({
+                keyfmt.format('path_name'): path_name,
+                keyfmt.format('path_name_rel'): path_name_rel,
+                keyfmt.format('dir_name'): dir_name,
+                keyfmt.format('dir_name_rel'): dir_name_rel,
+                keyfmt.format('file_name'): file_name,
+                keyfmt.format('base_name'): base_name,
+                keyfmt.format('ext'): ext
+            })
+        
+        varValues[keyfmt.format('clipboard:ml')] = (
+            QApplication.clipboard().text()
+        )
+        varValues[keyfmt.format('clipboard')] = QApplication.clipboard().text()
+
+        if editor.hasSelectedText():
+            varValues[keyfmt.format('cur_select:ml')] = editor.selectedText()
+            varValues[keyfmt.format('cur_select')] = editor.selectedText()
+        else:
+            varValues[keyfmt.format('cur_select:ml')] = os.linesep
+            varValues[keyfmt.format('cur_select')] = ""
+
+        varValues[keyfmt.format('insertion')] = "i_n_s_e_r_t_i_o_n"
+        
+        varValues[keyfmt.format('select_start')] = "s_e_l_e_c_t_s_t_a_r_t"
+        varValues[keyfmt.format('select_end')] = "s_e_l_e_c_t_e_n_d"
+
+        return varValues
+
+    def applyTemplate(self, itm):
+        """
+        Public method to apply the template.
+        
+        @param itm reference to the template item to apply (TemplateEntry)
+        """
+        editor = self.viewmanager.activeWindow()
+        if editor is None:
+            return
+        
+        ok = False
+        variables = itm.getVariables()
+        varValues = self.__getPredefinedVars()
+        
+        # Remove predefined variables from list so user doesn't have to fill
+        # these values out in the dialog.
+        for v in list(varValues.keys()):
+            if v in variables:
+                variables.remove(v)
+        
+        if variables:
+            if Preferences.getTemplates("SingleDialog"):
+                from .TemplateMultipleVariablesDialog import (
+                    TemplateMultipleVariablesDialog
+                )
+                dlg = TemplateMultipleVariablesDialog(variables, self)
+                if dlg.exec() == QDialog.DialogCode.Accepted:
+                    varValues.update(dlg.getVariables())
+                    ok = True
+            else:
+                from .TemplateSingleVariableDialog import (
+                    TemplateSingleVariableDialog
+                )
+                for var in variables:
+                    dlg = TemplateSingleVariableDialog(var, self)
+                    if dlg.exec() == QDialog.DialogCode.Accepted:
+                        varValues[var] = dlg.getVariable()
+                    else:
+                        return
+                    del dlg
+                ok = True
+        else:
+            ok = True
+        
+        if ok:
+            line = editor.text(
+                editor.getCursorPosition()[0]).replace(os.linesep, "")
+            indent = line.replace(line.lstrip(), "")
+            txt, lines, count = itm.getExpandedText(varValues, indent)
+            # It should be done in this way to allow undo
+            editor.beginUndoAction()
+            if editor.hasSelectedText():
+                line, index = editor.getSelection()[0:2]
+                editor.removeSelectedText()
+            else:
+                line, index = editor.getCursorPosition()
+            
+            if lines == 1:
+                count += index
+            else:
+                if len(indent) > 0:
+                    count += len(indent)
+            
+            if "i_n_s_e_r_t_i_o_n" in txt and "s_e_l_e_c_t" in txt:
+                txt = (
+                    "'Insertion and selection can not be in"
+                    " template together'"
+                )
+            
+            if "i_n_s_e_r_t_i_o_n" in txt:
+                lines = 1
+                for aline in txt.splitlines():
+                    count = aline.find("i_n_s_e_r_t_i_o_n")
+                    if count >= 0:
+                        txt = txt.replace("i_n_s_e_r_t_i_o_n", "")
+                        if lines == 1:
+                            count += index
+                        else:
+                            if len(indent) > 0:
+                                count += len(indent)
+                        break
+                    else:
+                        lines += 1
+            
+            setselect = False
+            if "s_e_l_e_c_t_s_t_a_r_t" in txt and "s_e_l_e_c_t_e_n_d" in txt:
+                setselect = True
+                linea = 1
+                for aline in txt.splitlines():
+                    posa = aline.find("s_e_l_e_c_t_s_t_a_r_t")
+                    if posa >= 0:
+                        txt = txt.replace("s_e_l_e_c_t_s_t_a_r_t", "")
+                        break
+                    else:
+                        linea += 1
+                lineb = 1
+                for aline in txt.splitlines():
+                    posb = aline.find("s_e_l_e_c_t_e_n_d")
+                    if posb >= 0:
+                        txt = txt.replace("s_e_l_e_c_t_e_n_d", "")
+                        break
+                    else:
+                        lineb += 1
+            
+            editor.insert(txt)
+            
+            if setselect:
+                editor.setSelection(line + linea - 1, posa,
+                                    line + lineb - 1, posb)
+            else:
+                editor.setCursorPosition(line + lines - 1, count)
+                
+            editor.endUndoAction()
+            editor.setFocus()
+
+    def applyNamedTemplate(self, templateName, groupName=None):
+        """
+        Public method to apply a template given a template name.
+        
+        @param templateName name of the template item to apply (string)
+        @param groupName name of the group to get the entry from (string).
+            None or empty means to apply the first template found with the
+            given name.
+        """
+        if groupName:
+            if self.hasGroup(groupName):
+                groups = [self.groups[groupName]]
+            else:
+                return
+        else:
+            groups = list(self.groups.values())
+        for group in groups:
+            template = group.getEntry(templateName)
+            if template is not None:
+                self.applyTemplate(template)
+                break
+    
+    def addEntry(self, groupName, name, description, template, quiet=False):
+        """
+        Public method to add a template entry.
+        
+        @param groupName name of the group to add to (string)
+        @param name name of the entry to add (string)
+        @param description description of the entry to add (string)
+        @param template template text of the entry (string)
+        @param quiet flag indicating quiet operation (boolean)
+        """
+        self.groups[groupName].addEntry(
+            name, description, template, quiet=quiet)
+        self.__resort()
+    
+    def hasGroup(self, name):
+        """
+        Public method to check, if a group with the given name exists.
+        
+        @param name name of the group to be checked for (string)
+        @return flag indicating an existing group (boolean)
+        """
+        return name in self.groups
+    
+    def addGroup(self, name, language="All"):
+        """
+        Public method to add a group.
+        
+        @param name name of the group to be added (string)
+        @param language programming language for the group (string)
+        """
+        if name not in self.groups:
+            self.groups[name] = TemplateGroup(self, name, language)
+        self.__resort()
+
+    def changeGroup(self, oldname, newname, language="All"):
+        """
+        Public method to rename a group.
+        
+        @param oldname old name of the group (string)
+        @param newname new name of the group (string)
+        @param language programming language for the group (string)
+        """
+        if oldname != newname:
+            if newname in self.groups:
+                E5MessageBox.warning(
+                    self,
+                    self.tr("Edit Template Group"),
+                    self.tr("""<p>A template group with the name"""
+                            """ <b>{0}</b> already exists.</p>""")
+                    .format(newname))
+                return
+            
+            self.groups[newname] = self.groups[oldname]
+            del self.groups[oldname]
+            self.groups[newname].setName(newname)
+        
+        self.groups[newname].setLanguage(language)
+        self.__resort()
+
+    def getAllGroups(self):
+        """
+        Public method to get all groups.
+        
+        @return list of all groups (list of TemplateGroup)
+        """
+        return list(self.groups.values())
+    
+    def getGroupNames(self):
+        """
+        Public method to get all group names.
+        
+        @return list of all group names (list of strings)
+        """
+        groups = sorted(list(self.groups.keys())[:])
+        return groups
+
+    def removeGroup(self, itm):
+        """
+        Public method to remove a group.
+        
+        @param itm template group to be removed (TemplateGroup)
+        """
+        name = itm.getName()
+        itm.removeAllEntries()
+        index = self.indexOfTopLevelItem(itm)
+        self.takeTopLevelItem(index)
+        del self.groups[name]
+
+    def removeEntry(self, itm):
+        """
+        Public method to remove a template entry.
+        
+        @param itm template entry to be removed (TemplateEntry)
+        """
+        groupName = itm.getGroupName()
+        self.groups[groupName].removeEntry(itm.getName())
+
+    def changeEntry(self, itm, name, groupName, description, template):
+        """
+        Public method to change a template entry.
+        
+        @param itm template entry to be changed (TemplateEntry)
+        @param name new name for the entry (string)
+        @param groupName name of the group the entry should belong to
+            (string)
+        @param description description of the entry (string)
+        @param template template text of the entry (string)
+        """
+        if itm.getGroupName() != groupName:
+            # move entry to another group
+            self.groups[itm.getGroupName()].removeEntry(itm.getName())
+            self.groups[groupName].addEntry(name, description, template)
+            return
+        
+        if itm.getName() != name:
+            # entry was renamed
+            self.groups[groupName].removeEntry(itm.getName())
+            self.groups[groupName].addEntry(name, description, template)
+            return
+        
+        tmpl = self.groups[groupName].getEntry(name)
+        tmpl.setDescription(description)
+        tmpl.setTemplateText(template)
+        self.__resort()
+    
+    def writeTemplates(self, filename=None):
+        """
+        Public method to write the templates data to a JSON file (.ecj).
+        
+        @param filename name of a templates file to write
+        @type str
+        @return flag indicating success
+        @rtype bool
+        """
+        if filename is None:
+            filename = os.path.join(
+                Utilities.getConfigDir(), "eric6templates.ecj")
+        if filename.endswith(".ecj"):
+            # new JSON based file
+            res = self.__templatesFile.writeFile(filename)
+        else:
+            # old XML based file
+            f = QFile(filename)
+            ok = f.open(QIODevice.OpenModeFlag.WriteOnly)
+            if not ok:
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Save Templates"),
+                    self.tr(
+                        "<p>The templates file <b>{0}</b> could not be"
+                        " written.</p>")
+                    .format(filename))
+                res = False
+            else:
+                from E5XML.TemplatesWriter import TemplatesWriter
+                TemplatesWriter(f, self).writeXML()
+                f.close()
+                res = True
+        
+        return res
+    
+    def readTemplates(self, filename=None):
+        """
+        Public method to read in the templates file (.e4c).
+        
+        @param filename name of a templates file to read
+        @type str
+        """
+        if filename is None:
+            # new JSON based file first
+            filename = os.path.join(
+                Utilities.getConfigDir(), "eric6templates.ecj")
+            if not os.path.exists(filename):
+                # old XML based file second
+                filename = os.path.join(
+                    Utilities.getConfigDir(), "eric6templates.e4c")
+                if not os.path.exists(filename):
+                    return
+        
+        if filename.endswith(".ecj"):
+            self.__templatesFile.readFile(filename)
+        else:
+            f = QFile(filename)
+            if f.open(QIODevice.OpenModeFlag.ReadOnly):
+                from E5XML.TemplatesReader import TemplatesReader
+                reader = TemplatesReader(f, viewer=self)
+                reader.readXML()
+                f.close()
+            else:
+                E5MessageBox.critical(
+                    self,
+                    self.tr("Read Templates"),
+                    self.tr(
+                        "<p>The templates file <b>{0}</b> could not be read."
+                        "</p>")
+                    .format(filename))
+    
+    def __configure(self):
+        """
+        Private method to open the configuration dialog.
+        """
+        e5App().getObject("UserInterface").showPreferences("templatesPage")
+    
+    def hasTemplate(self, entryName, groupName=None):
+        """
+        Public method to check, if an entry of the given name exists.
+        
+        @param entryName name of the entry to check for (string)
+        @param groupName name of the group to check for the entry (string).
+            None or empty means to check all groups.
+        @return flag indicating the existence (boolean)
+        """
+        if groupName:
+            if self.hasGroup(groupName):
+                groups = [self.groups[groupName]]
+            else:
+                groups = []
+        else:
+            groups = list(self.groups.values())
+        
+        return any(group.hasEntry(entryName) for group in groups)
+    
+    def getTemplateNames(self, start, groupName=None):
+        """
+        Public method to get the names of templates starting with the
+        given string.
+        
+        @param start start string of the name (string)
+        @param groupName name of the group to get the entry from (string).
+            None or empty means to look in all groups.
+        @return sorted list of matching template names (list of strings)
+        """
+        names = []
+        if groupName:
+            if self.hasGroup(groupName):
+                groups = [self.groups[groupName]]
+            else:
+                groups = []
+        else:
+            groups = list(self.groups.values())
+        for group in groups:
+            names.extend(group.getEntryNames(start))
+        return sorted(names)

eric ide

mercurial