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

Sun, 08 Jan 2017 18:52:16 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 08 Jan 2017 18:52:16 +0100
changeset 5407
f833f89571b8
parent 5406
8d09a23a8fdd
child 5408
b67e07566aa1

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/ImageMarkupDialog.py file | annotate | diff | comparison | revisions
QScintilla/MarkupProviders/ImageMarkupDialog.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/formatTextBulletedList.png file | annotate | diff | comparison | revisions
icons/default/formatTextImage.png file | annotate | diff | comparison | revisions
icons/default/formatTextNumberedList.png file | annotate | diff | comparison | revisions
icons/default/formatTextQuote.png file | annotate | diff | comparison | revisions
--- a/QScintilla/EditorButtonsWidget.py	Sun Jan 08 18:51:55 2017 +0100
+++ b/QScintilla/EditorButtonsWidget.py	Sun Jan 08 18:52:16 2017 +0100
@@ -35,9 +35,10 @@
         super(EditorButtonsWidget, self).__init__(parent)
         
         margin = 2
+        spacing = 3
         self.__layout = QVBoxLayout(self)
         self.__layout.setContentsMargins(margin, margin, margin, margin)
-        self.__layout.setSpacing(2)
+        self.__layout.setSpacing(spacing)
         
         self.__provider = None
         
@@ -63,26 +64,46 @@
         self.__separators = []
         self.__headerMenu = QMenu()
         
-        self.__addButton("bold", "formatTextBold.png")
-        self.__addButton("italic", "formatTextItalic.png")
-        self.__addButton("strikethrough", "formatTextStrikethrough.png")
+        self.__addButton("bold", "formatTextBold.png",
+                         self.tr("Bold"))
+        self.__addButton("italic", "formatTextItalic.png",
+                         self.tr("Italic"))
+        self.__addButton("strikethrough", "formatTextStrikethrough.png",
+                         self.tr("Strike Through"))
         self.__addSeparator()
-        self.__addButton("header1", "formatTextHeader1.png")
-        self.__addButton("header2", "formatTextHeader2.png")
-        self.__addButton("header3", "formatTextHeader3.png")
-        button = self.__addButton("header", "formatTextHeader.png")
+        self.__addButton("header1", "formatTextHeader1.png",
+                         self.tr("Header 1"))
+        self.__addButton("header2", "formatTextHeader2.png",
+                         self.tr("Header 2"))
+        self.__addButton("header3", "formatTextHeader3.png",
+                         self.tr("Header 3"))
+        button = self.__addButton("header", "formatTextHeader.png",
+                                  self.tr("Header"))
         button.setPopupMode(QToolButton.InstantPopup)
         button.setMenu(self.__headerMenu)
         self.__addSeparator()
-        self.__addButton("code", "formatTextInlineCode.png")
-        self.__addButton("codeBlock", "formatTextCodeBlock.png")
+        self.__addButton("code", "formatTextInlineCode.png",
+                         self.tr("Inline Code"))
+        self.__addButton("codeBlock", "formatTextCodeBlock.png",
+                         self.tr("Code Block"))
+        self.__addButton("quote", "formatTextQuote.png",
+                         self.tr("Quote"))
         self.__addSeparator()
-        self.__addButton("hyperlink", "formatTextHyperlink.png")
-        self.__addButton("line", "formatTextHorizontalLine.png")
+        self.__addButton("hyperlink", "formatTextHyperlink.png",
+                         self.tr("Add Hyperlink"))
+        self.__addButton("line", "formatTextHorizontalLine.png",
+                         self.tr("Add Horizontal Line"))
+        self.__addButton("image", "formatTextImage.png",
+                         self.tr("Add Image"))
+        self.__addSeparator()
+        self.__addButton("bulletedList", "formatTextBulletedList.png",
+                         self.tr("Add Bulleted List"))
+        self.__addButton("numberedList", "formatTextNumberedList.png",
+                         self.tr("Add Numbered List"))
         
         self.__headerMenu.triggered.connect(self.__headerMenuTriggered)
     
-    def __addButton(self, format, iconName):
+    def __addButton(self, format, iconName, toolTip):
         """
         Private method to add a format button.
         
@@ -90,11 +111,14 @@
         @type str
         @param iconName name of the icon for the button
         @type str
+        @param toolTip text for the tool tip
+        @type str
         @return generated button
         @rtype QToolButton
         """
         button = QToolButton(self)
         button.setIcon(UI.PixmapCache.getIcon(iconName))
+        button.setToolTip(toolTip)
         button.clicked.connect(lambda: self.__formatClicked(format))
         self.__layout.addWidget(button)
         self.__buttons[format] = button
@@ -174,10 +198,14 @@
             self.__provider.code(self.__editor)
         elif format == "codeBlock":
             self.__provider.codeBlock(self.__editor)
+        elif format == "quote":
+            self.__provider.quote(self.__editor)
         elif format == "hyperlink":
             self.__provider.hyperlink(self.__editor)
         elif format == "line":
             self.__provider.line(self.__editor)
+        elif format == "image":
+            self.__provider.image(self.__editor)
     
     def __headerMenuTriggered(self, act):
         """
@@ -195,7 +223,12 @@
         """
         hasSelection = self.__editor.hasSelectedText()
         if self.__provider:
+            self.__buttons["quote"].setEnabled(
+                self.__provider.hasQuote() and (
+                self.__provider.kind() == "html" or hasSelection))
             self.__buttons["hyperlink"].setEnabled(
                 self.__provider.hasHyperlink() and not hasSelection)
             self.__buttons["line"].setEnabled(
                 self.__provider.hasLine() and not hasSelection)
+            self.__buttons["image"].setEnabled(
+                self.__provider.hasImage() and not hasSelection)
--- a/QScintilla/MarkupProviders/HtmlProvider.py	Sun Jan 08 18:51:55 2017 +0100
+++ b/QScintilla/MarkupProviders/HtmlProvider.py	Sun Jan 08 18:52:16 2017 +0100
@@ -158,7 +158,7 @@
             editor.setCursorPosition(cline + 1, 0)
         editor.endUndoAction()
     
-    def __insertMarkup(self, markup, editor):
+    def __insertMarkup(self, markup, editor, addEol=False):
         """
         Private method to insert the specified markup.
         
@@ -170,18 +170,28 @@
         @type str
         @param editor reference to the editor to work on
         @type Editor
+        @param addEol flag indicating to add an eol string after the tag
+        @type bool
         """
         if editor is None:
             return
         
+        if addEol:
+            lineSeparator = editor.getLineSeparator()
+        else:
+            lineSeparator = ""
         editor.beginUndoAction()
         if editor.hasSelectedText():
-            newText = "<{0}>{1}</{0}>".format(markup, editor.selectedText())
+            newText = "<{0}>{2}{1}</{0}>{2}".format(
+                markup, editor.selectedText(), lineSeparator)
             editor.replaceSelectedText(newText)
         else:
-            editor.insert("<{0}></{0}>".format(markup))
+            editor.insert("<{0}>{1}{1}</{0}>{1}".format(markup, lineSeparator))
             cline, cindex = editor.getCursorPosition()
-            editor.setCursorPosition(cline, cindex + len(markup) + 2)
+            if addEol:
+                editor.setCursorPosition(cline + 1, 0)
+            else:
+                editor.setCursorPosition(cline, cindex + len(markup) + 2)
         editor.endUndoAction()
     
     def hasHyperlink(self):
@@ -200,6 +210,9 @@
         @param editor reference to the editor to work on
         @type Editor
         """
+        if editor is None:
+            return
+        
         from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
         dlg = HyperlinkMarkupDialog(True, False)
         if dlg.exec_() == QDialog.Accepted:
@@ -244,3 +257,62 @@
         cline, cindex = editor.getCursorPosition()
         editor.setCursorPosition(cline, cindex + len(markup))
         editor.endUndoAction()
+    
+    def hasQuote(self):
+        """
+        Public method to indicate the availability of block quote markup.
+        
+        @return flag indicating the availability of block quote markup
+        @rtype bool
+        """
+        return True
+    
+    def quote(self, editor):
+        """
+        Public method to generate block quote text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        self.__insertMarkup("blockquote", editor, True)
+    
+    def hasImage(self):
+        """
+        Public method to indicate the availability of image markup.
+        
+        @return flag indicating the availability of image markup
+        @rtype bool
+        """
+        return True
+    
+    def image(self, editor):
+        """
+        Public method to generate image text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        from .ImageMarkupDialog import ImageMarkupDialog
+        dlg = ImageMarkupDialog(ImageMarkupDialog.HtmlMode)
+        if dlg.exec_() == QDialog.Accepted:
+            address, altText, title, originalSize, width, height = \
+                dlg.getData()
+            
+            markup = '<img src="{0}"'.format(address)
+            if altText:
+                markup = '{0} alt="{1}"'.format(markup, altText)
+            if title:
+                markup = '{0} title="{1}"'.format(markup, title)
+            if not originalSize:
+                markup = '{0} width="{1}" height="{2}"'.format(
+                    markup, width, height)
+            markup = '{0} />'.format(markup)
+            
+            editor.beginUndoAction()
+            editor.insert(markup)
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline, cindex + len(markup))
+            editor.endUndoAction()
--- a/QScintilla/MarkupProviders/HyperlinkMarkupDialog.py	Sun Jan 08 18:51:55 2017 +0100
+++ b/QScintilla/MarkupProviders/HyperlinkMarkupDialog.py	Sun Jan 08 18:52:16 2017 +0100
@@ -42,6 +42,9 @@
         self.titelEdit.setEnabled(not noTitle)
         
         self.__updateOkButton()
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
     
     def __updateOkButton(self):
         """
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QScintilla/MarkupProviders/ImageMarkupDialog.py	Sun Jan 08 18:52:16 2017 +0100
@@ -0,0 +1,218 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to enter data for an image markup.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSlot, QSize
+from PyQt5.QtGui import QImage, QImageReader
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox
+
+from E5Gui.E5PathPicker import E5PathPickerModes
+
+from .Ui_ImageMarkupDialog import Ui_ImageMarkupDialog
+
+
+class ImageMarkupDialog(QDialog, Ui_ImageMarkupDialog):
+    """
+    Class implementing a dialog to enter data for an image markup.
+    """
+    HtmlMode = 0
+    MarkDownMode = 1
+    RestMode = 2
+    
+    def __init__(self, mode, parent=None):
+        """
+        Constructor
+        
+        @param parent reference to the parent widget
+        @type QWidget
+        """
+        super(ImageMarkupDialog, self).__init__(parent)
+        self.setupUi(self)
+        
+        if mode == ImageMarkupDialog.MarkDownMode:
+            self.sizeCheckBox.setEnabled(False)
+            self.aspectRatioCheckBox.setEnabled(False)
+            self.widthSpinBox.setEnabled(False)
+            self.heightSpinBox.setEnabled(False)
+        elif mode == ImageMarkupDialog.RestMode:
+            self.titleEdit.setEnabled(False)
+        
+        self.__mode = mode
+        self.__originalImageSize = QSize()
+    
+        filters = {
+            'bmp': self.tr("Windows Bitmap File (*.bmp)"),
+            'cur': self.tr("Windows Cursor File (*.cur)"),
+            'dds': self.tr("DirectDraw-Surface File (*.dds)"),
+            'gif': self.tr("Graphic Interchange Format File (*.gif)"),
+            'icns': self.tr("Apple Icon File (*.icns)"),
+            'ico': self.tr("Windows Icon File (*.ico)"),
+            'jp2': self.tr("JPEG2000 File (*.jp2)"),
+            'jpg': self.tr("JPEG File (*.jpg)"),
+            'jpeg': self.tr("JPEG File (*.jpeg)"),
+            'mng': self.tr("Multiple-Image Network Graphics File (*.mng)"),
+            'pbm': self.tr("Portable Bitmap File (*.pbm)"),
+            'pcx': self.tr("Paintbrush Bitmap File (*.pcx)"),
+            'pgm': self.tr("Portable Graymap File (*.pgm)"),
+            'png': self.tr("Portable Network Graphics File (*.png)"),
+            'ppm': self.tr("Portable Pixmap File (*.ppm)"),
+            'sgi': self.tr("Silicon Graphics Image File (*.sgi)"),
+            'svg': self.tr("Scalable Vector Graphics File (*.svg)"),
+            'svgz': self.tr("Compressed Scalable Vector Graphics File"
+                            " (*.svgz)"),
+            'tga': self.tr("Targa Graphic File (*.tga)"),
+            'tif': self.tr("TIFF File (*.tif)"),
+            'tiff': self.tr("TIFF File (*.tiff)"),
+            'wbmp': self.tr("WAP Bitmap File (*.wbmp)"),
+            'webp': self.tr("WebP Image File (*.webp)"),
+            'xbm': self.tr("X11 Bitmap File (*.xbm)"),
+            'xpm': self.tr("X11 Pixmap File (*.xpm)"),
+        }
+        
+        inputFormats = []
+        readFormats = QImageReader.supportedImageFormats()
+        for readFormat in readFormats:
+            try:
+                inputFormats.append(filters[bytes(readFormat).decode()])
+            except KeyError:
+                pass
+        inputFormats.sort()
+        inputFormats.append(self.tr("All Files (*)"))
+        if filters["png"] in inputFormats:
+            inputFormats.remove(filters["png"])
+            inputFormats.insert(0, filters["png"])
+        self.imagePicker.setFilters(';;'.join(inputFormats))
+        self.imagePicker.setMode(E5PathPickerModes.OpenFileMode)
+        
+        self.sizeCheckBox.setChecked(True)
+        self.aspectRatioCheckBox.setChecked(True)
+        
+        msh = self.minimumSizeHint()
+        self.resize(max(self.width(), msh.width()), msh.height())
+        
+        self.__updateOkButton()
+    
+    def __updateOkButton(self):
+        """
+        Private slot to set the state of the OK button.
+        """
+        enable = bool(self.imagePicker.text())
+        if self.__mode == ImageMarkupDialog.MarkDownMode:
+            enable = enable and bool(self.altTextEdit.text())
+        
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable)
+    
+    @pyqtSlot(str)
+    def on_imagePicker_textChanged(self, address):
+        """
+        Private slot handling changes of the image path.
+        
+        @param address image address (URL or local path)
+        @type str
+        """
+        if address and "://" not in address:
+            image = QImage(address)
+            # load the file to set the size spin boxes
+            if image.isNull():
+                self.widthSpinBox.setValue(0)
+                self.heightSpinBox.setValue(0)
+                self.__originalImageSize = QSize()
+                self.__aspectRatio = 1
+            else:
+                self.widthSpinBox.setValue(image.width())
+                self.heightSpinBox.setValue(image.height())
+                self.__originalImageSize = image.size()
+                self.__aspectRatio = \
+                    float(self.__originalImageSize.height()) / \
+                    self.__originalImageSize.width()
+        else:
+            self.widthSpinBox.setValue(0)
+            self.heightSpinBox.setValue(0)
+            self.__originalImageSize = QSize()
+            self.__aspectRatio = 1
+        
+        self.__updateOkButton()
+    
+    @pyqtSlot(str)
+    def on_altTextEdit_textChanged(self, txt):
+        """
+        Private slot handling changes of the alternative text.
+        
+        @param txt alternative text
+        @type str
+        """
+        self.__updateOkButton()
+
+    @pyqtSlot(bool)
+    def on_sizeCheckBox_toggled(self, checked):
+        """
+        Public slot to reset the width and height spin boxes.
+        
+        @param checked flag indicating the state of the check box
+        @type bool
+        """
+        if checked:
+            self.widthSpinBox.setValue(self.__originalImageSize.width())
+            self.heightSpinBox.setValue(self.__originalImageSize.height())
+    
+    @pyqtSlot(bool)
+    def on_aspectRatioCheckBox_toggled(self, checked):
+        """
+        Public slot to adjust the height to match the original aspect ratio.
+        
+        @param checked flag indicating the state of the check box
+        @type bool
+        """
+        if checked and self.__originalImageSize.isValid():
+            height = self.widthSpinBox.value() * self.__aspectRatio
+            self.heightSpinBox.setValue(height)
+    
+    @pyqtSlot(int)
+    def on_widthSpinBox_valueChanged(self, width):
+        """
+        Private slot to adjust the height spin box.
+        
+        @param width width for the image
+        @type int
+        """
+        if self.aspectRatioCheckBox.isChecked() and \
+                self.widthSpinBox.hasFocus():
+            height = width * self.__aspectRatio
+            self.heightSpinBox.setValue(height)
+    
+    @pyqtSlot(int)
+    def on_heightSpinBox_valueChanged(self, height):
+        """
+        Private slot to adjust the width spin box.
+        
+        @param height height for the image
+        @type int
+        """
+        if self.aspectRatioCheckBox.isChecked() and \
+                self.heightSpinBox.hasFocus():
+            width = height / self.__aspectRatio
+            self.widthSpinBox.setValue(width)
+    
+    def getData(self):
+        """
+        Public method to get the entered data.
+        
+        @return tuple containing the image address, alternative text,
+            title text, flag to keep the original size, width and height
+        @rtype tuple of (str, str, str, bool, int, int)
+        """
+        return (
+            self.imagePicker.text(),
+            self.altTextEdit.text(),
+            self.titleEdit.text(),
+            self.sizeCheckBox.isChecked(),
+            self.widthSpinBox.value(),
+            self.heightSpinBox.value(),
+        )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QScintilla/MarkupProviders/ImageMarkupDialog.ui	Sun Jan 08 18:52:16 2017 +0100
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ImageMarkupDialog</class>
+ <widget class="QDialog" name="ImageMarkupDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>500</width>
+    <height>231</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Add Image</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Image Address:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="E5PathPicker" name="imagePicker" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="focusPolicy">
+        <enum>Qt::StrongFocus</enum>
+       </property>
+       <property name="toolTip">
+        <string>Enter the image path or URL</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="text">
+        <string>Title:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QLineEdit" name="titleEdit"/>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Alternative Text:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="altTextEdit"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="sizeCheckBox">
+     <property name="toolTip">
+      <string/>
+     </property>
+     <property name="text">
+      <string>Keep Original Size</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="aspectRatioCheckBox">
+     <property name="text">
+      <string>Keep Aspect Ratio</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Width:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="widthSpinBox">
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+       <property name="suffix">
+        <string> px</string>
+       </property>
+       <property name="maximum">
+        <number>9999</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Height:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QSpinBox" name="heightSpinBox">
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+       <property name="suffix">
+        <string> px</string>
+       </property>
+       <property name="maximum">
+        <number>9999</number>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <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>
+ <customwidgets>
+  <customwidget>
+   <class>E5PathPicker</class>
+   <extends>QWidget</extends>
+   <header>E5Gui/E5PathPicker.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>imagePicker</tabstop>
+  <tabstop>titleEdit</tabstop>
+  <tabstop>altTextEdit</tabstop>
+  <tabstop>sizeCheckBox</tabstop>
+  <tabstop>aspectRatioCheckBox</tabstop>
+  <tabstop>widthSpinBox</tabstop>
+  <tabstop>heightSpinBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>ImageMarkupDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>228</x>
+     <y>179</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>200</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>ImageMarkupDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>296</x>
+     <y>185</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>200</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sizeCheckBox</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>aspectRatioCheckBox</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>62</x>
+     <y>51</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>63</x>
+     <y>76</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sizeCheckBox</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>widthSpinBox</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>108</x>
+     <y>51</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>105</x>
+     <y>103</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>sizeCheckBox</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>heightSpinBox</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>221</x>
+     <y>53</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>223</x>
+     <y>103</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- a/QScintilla/MarkupProviders/MarkdownProvider.py	Sun Jan 08 18:51:55 2017 +0100
+++ b/QScintilla/MarkupProviders/MarkdownProvider.py	Sun Jan 08 18:52:16 2017 +0100
@@ -205,10 +205,13 @@
         @param editor reference to the editor to work on
         @type Editor
         """
+        if editor is None:
+            return
+        
         from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
         dlg = HyperlinkMarkupDialog(False, True)
         if dlg.exec_() == QDialog.Accepted:
-            text,  target, title = dlg.getData()
+            text, target, title = dlg.getData()
             
             link = "[{0}]".format(text)
             if target and title:
@@ -250,3 +253,65 @@
         cline, cindex = editor.getCursorPosition()
         editor.setCursorPosition(cline + 3, 0)
         editor.endUndoAction()
+    
+    def hasQuote(self):
+        """
+        Public method to indicate the availability of block quote markup.
+        
+        @return flag indicating the availability of block quote markup
+        @rtype bool
+        """
+        return True
+    
+    def quote(self, editor):
+        """
+        Public method to generate block quote text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        editor.beginUndoAction()
+        markup = "> "
+        sline, sindex, eline, eindex = editor.getSelection()
+        for line in range(sline, eline + 1 if eindex > 0 else eline):
+            editor.insertAt(markup, line, 0)
+        editor.endUndoAction()
+    
+    def hasImage(self):
+        """
+        Public method to indicate the availability of image markup.
+        
+        @return flag indicating the availability of image markup
+        @rtype bool
+        """
+        return True
+    
+    def image(self, editor):
+        """
+        Public method to generate image text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        from .ImageMarkupDialog import ImageMarkupDialog
+        dlg = ImageMarkupDialog(ImageMarkupDialog.MarkDownMode)
+        if dlg.exec_() == QDialog.Accepted:
+            address, altText, title, originalSize, width, height = \
+                dlg.getData()
+            
+            if title:
+                markup = '![{0}]({1} "{2}")'.format(altText, address, title)
+            else:
+                markup = '![{0}]({1})'.format(altText, address)
+            
+            editor.beginUndoAction()
+            editor.insert(markup)
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline, cindex + len(markup))
+            editor.endUndoAction()
--- a/QScintilla/MarkupProviders/MarkupBase.py	Sun Jan 08 18:51:55 2017 +0100
+++ b/QScintilla/MarkupProviders/MarkupBase.py	Sun Jan 08 18:52:16 2017 +0100
@@ -178,4 +178,39 @@
         @type Editor
         """
         pass
-
+    
+    def hasQuote(self):
+        """
+        Public method to indicate the availability of block quote markup.
+        
+        @return flag indicating the availability of block quote markup
+        @rtype bool
+        """
+        return False
+    
+    def quote(self, editor):
+        """
+        Public method to generate block quote text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        pass
+    
+    def hasImage(self):
+        """
+        Public method to indicate the availability of image markup.
+        
+        @return flag indicating the availability of image markup
+        @rtype bool
+        """
+        return False
+    
+    def image(self, editor):
+        """
+        Public method to generate image text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        pass
--- a/QScintilla/MarkupProviders/RestructuredTextProvider.py	Sun Jan 08 18:51:55 2017 +0100
+++ b/QScintilla/MarkupProviders/RestructuredTextProvider.py	Sun Jan 08 18:52:16 2017 +0100
@@ -94,6 +94,8 @@
         
         editor.beginUndoAction()
         cline, cindex = editor.getCursorPosition()
+        if editor.hasSelection() and cindex == 0:
+            cline -= 1
         lineSeparator = editor.getLineSeparator()
         if not editor.text(cline).endswith(lineSeparator):
             editor.insertAt(lineSeparator, cline, len(editor.text(cline)))
@@ -200,6 +202,9 @@
         @param editor reference to the editor to work on
         @type Editor
         """
+        if editor is None:
+            return
+        
         from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
         dlg = HyperlinkMarkupDialog(False, True, noTitle=True)
         if dlg.exec_() == QDialog.Accepted:
@@ -268,3 +273,74 @@
         cline, cindex = editor.getCursorPosition()
         editor.setCursorPosition(cline + 3, 0)
         editor.endUndoAction()
+    
+    def hasQuote(self):
+        """
+        Public method to indicate the availability of block quote markup.
+        
+        @return flag indicating the availability of block quote markup
+        @rtype bool
+        """
+        return True
+    
+    def quote(self, editor):
+        """
+        Public method to generate block quote text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        markup = "> "
+        sline, sindex, eline, eindex = editor.getSelection()
+        for line in range(sline, eline + 1 if eindex > 0 else eline):
+            editor.insertAt(markup, line, 0)
+        editor.insertAt("::{0}{0}".format(lineSeparator), sline, 0)
+        editor.setCursorPosition(eline + 2, eindex)
+        editor.endUndoAction()
+    
+    def hasImage(self):
+        """
+        Public method to indicate the availability of image markup.
+        
+        @return flag indicating the availability of image markup
+        @rtype bool
+        """
+        return True
+    
+    def image(self, editor):
+        """
+        Public method to generate image text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        from .ImageMarkupDialog import ImageMarkupDialog
+        dlg = ImageMarkupDialog(ImageMarkupDialog.RestMode)
+        if dlg.exec_() == QDialog.Accepted:
+            address, altText, title, originalSize, width, height = \
+                dlg.getData()
+            
+            lineSeparator = editor.getLineSeparator()
+            markup = ".. image:: {0}{1}".format(address, lineSeparator)
+            lines = 1
+            if altText:
+                markup += "   :alt: {0}{1}".format(altText, lineSeparator)
+                lines += 1
+            if not originalSize:
+                markup += "   :height: {0}px{1}".format(height, lineSeparator)
+                markup += "   :width: {0}px{1}".format(width, lineSeparator)
+                lines += 2
+            
+            editor.beginUndoAction()
+            editor.insert(markup)
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline + lines, 0)
+            editor.endUndoAction()
--- a/eric6.e4p	Sun Jan 08 18:51:55 2017 +0100
+++ b/eric6.e4p	Sun Jan 08 18:52:16 2017 +0100
@@ -884,6 +884,7 @@
     <Source>QScintilla/Lexers/__init__.py</Source>
     <Source>QScintilla/MarkupProviders/HtmlProvider.py</Source>
     <Source>QScintilla/MarkupProviders/HyperlinkMarkupDialog.py</Source>
+    <Source>QScintilla/MarkupProviders/ImageMarkupDialog.py</Source>
     <Source>QScintilla/MarkupProviders/MarkdownProvider.py</Source>
     <Source>QScintilla/MarkupProviders/MarkupBase.py</Source>
     <Source>QScintilla/MarkupProviders/RestructuredTextProvider.py</Source>
@@ -1827,6 +1828,7 @@
     <Form>PyUnit/UnittestStacktraceDialog.ui</Form>
     <Form>QScintilla/GotoDialog.ui</Form>
     <Form>QScintilla/MarkupProviders/HyperlinkMarkupDialog.ui</Form>
+    <Form>QScintilla/MarkupProviders/ImageMarkupDialog.ui</Form>
     <Form>QScintilla/ReplaceWidget.ui</Form>
     <Form>QScintilla/SearchWidget.ui</Form>
     <Form>QScintilla/ShellHistoryDialog.ui</Form>
Binary file icons/default/formatTextBulletedList.png has changed
Binary file icons/default/formatTextImage.png has changed
Binary file icons/default/formatTextNumberedList.png has changed
Binary file icons/default/formatTextQuote.png has changed

eric ide

mercurial