Continued implementing a format button bar and provider classes for various markup languages.

Sat, 07 Jan 2017 19:35:40 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 07 Jan 2017 19:35:40 +0100
changeset 5404
6b19ad5470a3
parent 5403
d6b43ecf2488
child 5405
2129035cd437

Continued implementing a format button bar and provider classes for various markup languages.

QScintilla/EditorButtonsWidget.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/HtmlProvider.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/HyperlinkMarkupDialog.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/HyperlinkMarkupDialog.ui file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/MarkdownProvider.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/MarkupBase.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/RestructuredTextProvider.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
icons/default/formatTextCodeBlock.png file | annotate | diff | comparison | revisions
icons/default/formatTextHorizontalLine.png file | annotate | diff | comparison | revisions
icons/default/formatTextHyperlink.png file | annotate | diff | comparison | revisions
icons/default/formatTextInlineCode.png file | annotate | diff | comparison | revisions
--- a/QScintilla/EditorButtonsWidget.py	Sat Jan 07 19:00:58 2017 +0100
+++ b/QScintilla/EditorButtonsWidget.py	Sat Jan 07 19:35:40 2017 +0100
@@ -45,6 +45,7 @@
         self.__editor.languageChanged.connect(self.__updateButtonStates)
         self.__editor.editorSaved.connect(self.__updateButtonStates)
         self.__editor.editorRenamed.connect(self.__updateButtonStates)
+        self.__editor.selectionChanged.connect(self.__editorSelectionChanged)
         
         self.__createButtons()
         
@@ -74,6 +75,10 @@
         button.setMenu(self.__headerMenu)
         self.__addSeparator()
         self.__addButton("code", "formatTextInlineCode.png")
+        self.__addButton("codeBlock", "formatTextCodeBlock.png")
+        self.__addSeparator()
+        self.__addButton("hyperlink", "formatTextHyperlink.png")
+        self.__addButton("line", "formatTextHorizontalLine.png")
         
         self.__headerMenu.triggered.connect(self.__headerMenuTriggered)
     
@@ -138,6 +143,10 @@
                 act.setData("header{0}".format(level))
             
             self.__buttons["code"].setEnabled(self.__provider.hasCode())
+            self.__buttons["codeBlock"].setEnabled(
+                self.__provider.hasCodeBlock())
+            
+            self.__editorSelectionChanged()
             
             # TODO: make this configurable
             self.setVisible(self.__provider.kind() != "none")
@@ -163,6 +172,12 @@
                 pass
         elif format == "code":
             self.__provider.code(self.__editor)
+        elif format == "codeBlock":
+            self.__provider.codeBlock(self.__editor)
+        elif format == "hyperlink":
+            self.__provider.hyperlink(self.__editor)
+        elif format == "line":
+            self.__provider.line(self.__editor)
     
     def __headerMenuTriggered(self, act):
         """
@@ -173,3 +188,14 @@
         """
         format = act.data()
         self.__formatClicked(format)
+    
+    def __editorSelectionChanged(self):
+        """
+        Private slot to handle a change of the editor's selection.
+        """
+        hasSelection = self.__editor.hasSelectedText()
+        if self.__provider:
+            self.__buttons["hyperlink"].setEnabled(
+                self.__provider.hasHyperlink() and not hasSelection)
+            self.__buttons["line"].setEnabled(
+                self.__provider.hasLine() and not hasSelection)
--- a/QScintilla/MarkupProviders/HtmlProvider.py	Sat Jan 07 19:00:58 2017 +0100
+++ b/QScintilla/MarkupProviders/HtmlProvider.py	Sat Jan 07 19:35:40 2017 +0100
@@ -9,6 +9,8 @@
 
 from __future__ import unicode_literals
 
+from PyQt5.QtWidgets import QDialog
+
 from .MarkupBase import MarkupBase
 
 
@@ -124,6 +126,38 @@
         """
         self.__insertMarkup("code", editor)
     
+    def hasCodeBlock(self):
+        """
+        Public method to indicate the availability of code block markup.
+        
+        @return flag indicating the availability of code block markup
+        @rtype bool
+        """
+        return True
+    
+    def codeBlock(self, editor):
+        """
+        Public method to generate code block text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        if editor.hasSelectedText():
+            newText = "<pre><code>{0}{1}</code></pre>{0}".format(
+                lineSeparator, editor.selectedText())
+            editor.replaceSelectedText(newText)
+        else:
+            editor.insert("<pre><code>{0}{0}</code></pre>{0}".format(
+                lineSeparator))
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline + 1, 0)
+        editor.endUndoAction()
+    
     def __insertMarkup(self, markup, editor):
         """
         Private method to insert the specified markup.
@@ -149,3 +183,64 @@
             cline, cindex = editor.getCursorPosition()
             editor.setCursorPosition(cline, cindex + len(markup) + 2)
         editor.endUndoAction()
+    
+    def hasHyperlink(self):
+        """
+        Public method to indicate the availability of hyperlink markup.
+        
+        @return flag indicating the availability of hyperlink markup
+        @rtype bool
+        """
+        return True
+    
+    def hyperlink(self, editor):
+        """
+        Public method to generate hyperlink text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
+        dlg = HyperlinkMarkupDialog(True, False)
+        if dlg.exec_() == QDialog.Accepted:
+            text,  target, title = dlg.getData()
+            if not text:
+                text = target
+            
+            if title:
+                link = '<a href="{0}" title="{2}">{1}</a>'.format(
+                    target, text, title)
+            else:
+                link = '<a href="{0}">{1}</a>'.format(target, text)
+            
+            editor.beginUndoAction()
+            cline, cindex = editor.getCursorPosition()
+            editor.insert(link)
+            editor.setCursorPosition(cline, cindex + len(link))
+            editor.endUndoAction()
+    
+    def hasLine(self):
+        """
+        Public method to indicate the availability of a horizontal line markup.
+        
+        @return flag indicating the availability of a horizontal line markup
+        @rtype bool
+        """
+        return True
+    
+    def line(self, editor):
+        """
+        Public method to generate a horizontal line text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        editor.beginUndoAction()
+        markup = "<hr />"
+        editor.insert(markup)
+        cline, cindex = editor.getCursorPosition()
+        editor.setCursorPosition(cline, cindex + len(markup))
+        editor.endUndoAction()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QScintilla/MarkupProviders/HyperlinkMarkupDialog.py	Sat Jan 07 19:35:40 2017 +0100
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to enter data to insert a hyperlink.
+"""
+
+from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox
+
+from .Ui_HyperlinkMarkupDialog import Ui_HyperlinkMarkupDialog
+
+
+class HyperlinkMarkupDialog(QDialog, Ui_HyperlinkMarkupDialog):
+    """
+    Class implementing a dialog to enter data to insert a hyperlink.
+    """
+    def __init__(self, textMayBeEmpty, targetMayBeEmpty, noTitle=False,
+                 parent=None):
+        """
+        Constructor
+        
+        @param textMayBeEmpty flag indicating, that the link text may
+            be empty
+        @type bool
+        @param targetMayBeEmpty flag indicating, that the link target may
+            be empty
+        @type bool
+        @param noTitle flag indicating, that no title is supported
+        @type bool
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(HyperlinkMarkupDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        self.__allowEmptyText = textMayBeEmpty
+        self.__allowEmptyTarget = targetMayBeEmpty
+        
+        self.titelEdit.setEnabled(not noTitle)
+        
+        self.__updateOkButton()
+    
+    def __updateOkButton(self):
+        """
+        Private method to update the state of the OK button.
+        """
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(
+            (bool(self.textEdit.text()) or self.__allowEmptyText) and 
+            (bool(self.targetEdit.text()) or self.__allowEmptyTarget)
+        )
+    
+    @pyqtSlot(str)
+    def on_textEdit_textChanged(self, txt):
+        """
+        Private slot handling a change of the link text.
+        
+        @param txt link text
+        @type str
+        """
+        self.__updateOkButton()
+    
+    @pyqtSlot(str)
+    def on_targetEdit_textChanged(self, tyt):
+        """
+        Private slot handling a change of the link target.
+        
+        @param txt link target
+        @type str
+        """
+        self.__updateOkButton()
+    
+    def getData(self):
+        """
+        Public method to get the entered data.
+        
+        @return tuple containing the link text, link target and the optional
+            link title
+        @rtype tuple of (str, str, str)
+        """
+        return (
+            self.textEdit.text(),
+            self.targetEdit.text(),
+            self.titelEdit.text()
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QScintilla/MarkupProviders/HyperlinkMarkupDialog.ui	Sat Jan 07 19:35:40 2017 +0100
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HyperlinkMarkupDialog</class>
+ <widget class="QDialog" name="HyperlinkMarkupDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>142</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Insert Hyperlink</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Link Text:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QLineEdit" name="textEdit"/>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Link Target:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QLineEdit" name="targetEdit"/>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="text">
+      <string>Link Title:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QLineEdit" name="titelEdit"/>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>HyperlinkMarkupDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>HyperlinkMarkupDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/QScintilla/MarkupProviders/MarkdownProvider.py	Sat Jan 07 19:00:58 2017 +0100
+++ b/QScintilla/MarkupProviders/MarkdownProvider.py	Sat Jan 07 19:35:40 2017 +0100
@@ -9,6 +9,8 @@
 
 from __future__ import unicode_literals
 
+from PyQt5.QtWidgets import QDialog
+
 from .MarkupBase import MarkupBase
 
 
@@ -130,6 +132,37 @@
         """
         self.__insertMarkup("`", editor)
     
+    def hasCodeBlock(self):
+        """
+        Public method to indicate the availability of code block markup.
+        
+        @return flag indicating the availability of code block markup
+        @rtype bool
+        """
+        return True
+    
+    def codeBlock(self, editor):
+        """
+        Public method to generate code block text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        if editor.hasSelectedText():
+            newText = "```{0}{1}```{0}".format(
+                lineSeparator, editor.selectedText())
+            editor.replaceSelectedText(newText)
+        else:
+            editor.insert("```{0}{0}```{0}".format(lineSeparator))
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline + 1, 0)
+        editor.endUndoAction()
+    
     def __insertMarkup(self, markup, editor):
         """
         Private method to insert the specified markup.
@@ -155,3 +188,65 @@
             cline, cindex = editor.getCursorPosition()
             editor.setCursorPosition(cline, cindex + len(markup))
         editor.endUndoAction()
+    
+    def hasHyperlink(self):
+        """
+        Public method to indicate the availability of hyperlink markup.
+        
+        @return flag indicating the availability of hyperlink markup
+        @rtype bool
+        """
+        return True
+    
+    def hyperlink(self, editor):
+        """
+        Public method to generate hyperlink text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
+        dlg = HyperlinkMarkupDialog(False, True)
+        if dlg.exec_() == QDialog.Accepted:
+            text,  target, title = dlg.getData()
+            
+            link = "[{0}]".format(text)
+            if target and title:
+                link = '{0}({1} "{2}")'.format(link, target, title)
+            elif target:
+                link = '{0}({1})'.format(link, target)
+            elif title:
+                link = '{0}("{1}")'.format(link, title)
+            
+            editor.beginUndoAction()
+            cline, cindex = editor.getCursorPosition()
+            editor.insert(link)
+            editor.setCursorPosition(cline, cindex + len(link))
+            editor.endUndoAction()
+    
+    def hasLine(self):
+        """
+        Public method to indicate the availability of a horizontal line markup.
+        
+        @return flag indicating the availability of a horizontal line markup
+        @rtype bool
+        """
+        return True
+    
+    def line(self, editor):
+        """
+        Public method to generate a horizontal line text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        markup = "{0}-----{0}{0}".format(lineSeparator)
+        editor.insert(markup)
+        cline, cindex = editor.getCursorPosition()
+        editor.setCursorPosition(cline + 3, 0)
+        editor.endUndoAction()
--- a/QScintilla/MarkupProviders/MarkupBase.py	Sat Jan 07 19:00:58 2017 +0100
+++ b/QScintilla/MarkupProviders/MarkupBase.py	Sat Jan 07 19:35:40 2017 +0100
@@ -124,3 +124,58 @@
         @type Editor
         """
         pass
+    
+    def hasCodeBlock(self):
+        """
+        Public method to indicate the availability of code block markup.
+        
+        @return flag indicating the availability of code block markup
+        @rtype bool
+        """
+        return False
+    
+    def codeBlock(self, editor):
+        """
+        Public method to generate code block text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        pass
+    
+    def hasHyperlink(self):
+        """
+        Public method to indicate the availability of hyperlink markup.
+        
+        @return flag indicating the availability of hyperlink markup
+        @rtype bool
+        """
+        return False
+    
+    def hyperlink(self, editor):
+        """
+        Public method to generate hyperlink text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        pass
+    
+    def hasLine(self):
+        """
+        Public method to indicate the availability of a horizontal line markup.
+        
+        @return flag indicating the availability of a horizontal line markup
+        @rtype bool
+        """
+        return False
+    
+    def line(self, editor):
+        """
+        Public method to generate a horizontal line text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        pass
+
--- a/QScintilla/MarkupProviders/RestructuredTextProvider.py	Sat Jan 07 19:00:58 2017 +0100
+++ b/QScintilla/MarkupProviders/RestructuredTextProvider.py	Sat Jan 07 19:35:40 2017 +0100
@@ -9,6 +9,8 @@
 
 from __future__ import unicode_literals
 
+from PyQt5.QtWidgets import QDialog
+
 from .MarkupBase import MarkupBase
 
 
@@ -120,6 +122,42 @@
         """
         self.__insertMarkup("``", editor)
     
+    def hasCodeBlock(self):
+        """
+        Public method to indicate the availability of code block markup.
+        
+        @return flag indicating the availability of code block markup
+        @rtype bool
+        """
+        return True
+    
+    def codeBlock(self, editor):
+        """
+        Public method to generate code block text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        if editor.hasSelectedText():
+            sline, sindex, eline, eindex = editor.getSelection()
+            if not editor.text(sline).startswith((" ", "\t")):
+                # assume that all selected lines need indentation,
+                # if first line needs it
+                endLine = eline if eindex > 0 else eline - 1
+                for line in range(sline, endLine + 1):
+                    editor.insertAt("    ", line, 0)
+            editor.insertAt("::{0}{0}".format(lineSeparator), sline, 0)
+        else:
+            editor.insert("::{0}{0}    ".format(lineSeparator))
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline + 2, 4)
+        editor.endUndoAction()
+    
     def __insertMarkup(self, markup, editor):
         """
         Private method to insert the specified markup.
@@ -145,3 +183,88 @@
             cline, cindex = editor.getCursorPosition()
             editor.setCursorPosition(cline, cindex + len(markup))
         editor.endUndoAction()
+    
+    def hasHyperlink(self):
+        """
+        Public method to indicate the availability of hyperlink markup.
+        
+        @return flag indicating the availability of hyperlink markup
+        @rtype bool
+        """
+        return True
+    
+    def hyperlink(self, editor):
+        """
+        Public method to generate hyperlink text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
+        dlg = HyperlinkMarkupDialog(False, True, noTitle=True)
+        if dlg.exec_() == QDialog.Accepted:
+            text,  target, _ = dlg.getData()
+            
+            link1 = "`{0}`_".format(text)
+            link2 = ".. _`{0}`:".format(text)
+            if target:
+                link2 = "{0} {1}".format(link2, target)
+            
+            lineSeparator = editor.getLineSeparator()
+            editor.beginUndoAction()
+            cline, cindex = editor.getCursorPosition()
+            editor.insert(link1)
+            
+            line = cline
+            while line < editor.lines():
+                if editor.text(line).strip() == "":
+                    # found end of block
+                    break
+                line += 1
+            if line == editor.lines():
+                # reached end of document
+                editor.insertAt(2 * lineSeparator, line, 0)
+                editor.insertAt(link2, line + 2, 0)
+            else:
+                # find end of link block or start of next block
+                line += 1
+                while line < editor.lines():
+                    if not editor.text(line).startswith(".. _"):
+                        break
+                    line += 1
+                print("x", editor.text(line), "x")
+                if editor.text(line).strip():
+                    sep = 2 * lineSeparator
+                else:
+                    sep = lineSeparator
+                editor.insertAt("{0}{1}".format(link2, sep), line, 0)
+            
+            editor.setCursorPosition(cline, cindex + len(link1))
+            editor.endUndoAction()
+    
+    def hasLine(self):
+        """
+        Public method to indicate the availability of a horizontal line markup.
+        
+        @return flag indicating the availability of a horizontal line markup
+        @rtype bool
+        """
+        return True
+    
+    def line(self, editor):
+        """
+        Public method to generate a horizontal line text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        markup = "{0}-----{0}{0}".format(lineSeparator)
+        editor.insert(markup)
+        cline, cindex = editor.getCursorPosition()
+        editor.setCursorPosition(cline + 3, 0)
+        editor.endUndoAction()
--- a/eric6.e4p	Sat Jan 07 19:00:58 2017 +0100
+++ b/eric6.e4p	Sat Jan 07 19:35:40 2017 +0100
@@ -883,6 +883,7 @@
     <Source>QScintilla/Lexers/LexerYAML.py</Source>
     <Source>QScintilla/Lexers/__init__.py</Source>
     <Source>QScintilla/MarkupProviders/HtmlProvider.py</Source>
+    <Source>QScintilla/MarkupProviders/HyperlinkMarkupDialog.py</Source>
     <Source>QScintilla/MarkupProviders/MarkdownProvider.py</Source>
     <Source>QScintilla/MarkupProviders/MarkupBase.py</Source>
     <Source>QScintilla/MarkupProviders/RestructuredTextProvider.py</Source>
@@ -1825,6 +1826,7 @@
     <Form>PyUnit/UnittestDialog.ui</Form>
     <Form>PyUnit/UnittestStacktraceDialog.ui</Form>
     <Form>QScintilla/GotoDialog.ui</Form>
+    <Form>QScintilla/MarkupProviders/HyperlinkMarkupDialog.ui</Form>
     <Form>QScintilla/ReplaceWidget.ui</Form>
     <Form>QScintilla/SearchWidget.ui</Form>
     <Form>QScintilla/ShellHistoryDialog.ui</Form>
Binary file icons/default/formatTextCodeBlock.png has changed
Binary file icons/default/formatTextHorizontalLine.png has changed
Binary file icons/default/formatTextHyperlink.png has changed
Binary file icons/default/formatTextInlineCode.png has changed

eric ide

mercurial