Sat, 07 Jan 2017 19:35:40 +0100
Continued implementing a format button bar and provider classes for various markup languages.
--- 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>