1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2017 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the HTML markup provider.
8"""
9
10from PyQt5.QtCore import QCoreApplication
11from PyQt5.QtWidgets import QDialog, QInputDialog
12
13from .MarkupBase import MarkupBase
14
15
16class HtmlProvider(MarkupBase):
17    """
18    Class implementing the HTML markup provider.
19    """
20    def __init__(self):
21        """
22        Constructor
23        """
24        super().__init__()
25
26    def kind(self):
27        """
28        Public method to get the markup kind.
29
30        @return string with markup kind
31        @rtype str
32        """
33        return "html"
34
35    def hasBold(self):
36        """
37        Public method to indicate the availability of bold markup.
38
39        @return flag indicating the availability of bold markup
40        @rtype bool
41        """
42        return True
43
44    def bold(self, editor):
45        """
46        Public method to generate bold text.
47
48        @param editor reference to the editor to work on
49        @type Editor
50        """
51        self.__insertMarkup("b", editor)
52
53    def hasItalic(self):
54        """
55        Public method to indicate the availability of italic markup.
56
57        @return flag indicating the availability of italic markup
58        @rtype bool
59        """
60        return True
61
62    def italic(self, editor):
63        """
64        Public method to generate italic text.
65
66        @param editor reference to the editor to work on
67        @type Editor
68        """
69        self.__insertMarkup("i", editor)
70
71    def hasStrikethrough(self):
72        """
73        Public method to indicate the availability of strikethrough markup.
74
75        @return flag indicating the availability of strikethrough markup
76        @rtype bool
77        """
78        return True
79
80    def strikethrough(self, editor):
81        """
82        Public method to generate strikethrough text.
83
84        @param editor reference to the editor to work on
85        @type Editor
86        """
87        self.__insertMarkup("del", editor)
88
89    def headerLevels(self):
90        """
91        Public method to determine the available header levels.
92
93        @return supported header levels
94        @rtype int
95        """
96        return 6
97
98    def header(self, editor, level):
99        """
100        Public method to generate a header.
101
102        @param editor reference to the editor to work on
103        @type Editor
104        @param level header level
105        @type int
106        """
107        if level <= 6:
108            self.__insertMarkup("h{0}".format(level), editor)
109
110    def hasCode(self):
111        """
112        Public method to indicate the availability of inline code markup.
113
114        @return flag indicating the availability of inline code markup
115        @rtype bool
116        """
117        return True
118
119    def code(self, editor):
120        """
121        Public method to generate inline code text.
122
123        @param editor reference to the editor to work on
124        @type Editor
125        """
126        self.__insertMarkup("code", editor)
127
128    def hasCodeBlock(self):
129        """
130        Public method to indicate the availability of code block markup.
131
132        @return flag indicating the availability of code block markup
133        @rtype bool
134        """
135        return True
136
137    def codeBlock(self, editor):
138        """
139        Public method to generate code block text.
140
141        @param editor reference to the editor to work on
142        @type Editor
143        """
144        if editor is None:
145            return
146
147        lineSeparator = editor.getLineSeparator()
148        editor.beginUndoAction()
149        if editor.hasSelectedText():
150            newText = "<pre><code>{0}{1}</code></pre>{0}".format(
151                lineSeparator, editor.selectedText())
152            editor.replaceSelectedText(newText)
153        else:
154            editor.insert("<pre><code>{0}{0}</code></pre>{0}".format(
155                lineSeparator))
156            cline, cindex = editor.getCursorPosition()
157            editor.setCursorPosition(cline + 1, 0)
158        editor.endUndoAction()
159
160    def __insertMarkup(self, markup, editor, addEol=False):
161        """
162        Private method to insert the specified markup.
163
164        If the editor has selected text, this text is enclosed by the given
165        markup. If no text is selected, the markup is inserted at the cursor
166        position and the cursor is positioned in between.
167
168        @param markup markup string to be inserted
169        @type str
170        @param editor reference to the editor to work on
171        @type Editor
172        @param addEol flag indicating to add an eol string after the tag
173        @type bool
174        """
175        if editor is None:
176            return
177
178        lineSeparator = editor.getLineSeparator() if addEol else ""
179        editor.beginUndoAction()
180        if editor.hasSelectedText():
181            newText = "<{0}>{2}{1}</{0}>{2}".format(
182                markup, editor.selectedText(), lineSeparator)
183            editor.replaceSelectedText(newText)
184        else:
185            editor.insert("<{0}>{1}{1}</{0}>{1}".format(markup, lineSeparator))
186            cline, cindex = editor.getCursorPosition()
187            if addEol:
188                editor.setCursorPosition(cline + 1, 0)
189            else:
190                editor.setCursorPosition(cline, cindex + len(markup) + 2)
191        editor.endUndoAction()
192
193    def hasHyperlink(self):
194        """
195        Public method to indicate the availability of hyperlink markup.
196
197        @return flag indicating the availability of hyperlink markup
198        @rtype bool
199        """
200        return True
201
202    def hyperlink(self, editor):
203        """
204        Public method to generate hyperlink text.
205
206        @param editor reference to the editor to work on
207        @type Editor
208        """
209        if editor is None:
210            return
211
212        from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
213        dlg = HyperlinkMarkupDialog(True, False)
214        if dlg.exec() == QDialog.DialogCode.Accepted:
215            text, target, title = dlg.getData()
216            if not text:
217                text = target
218
219            if title:
220                link = '<a href="{0}" title="{2}">{1}</a>'.format(
221                    target, text, title)
222            else:
223                link = '<a href="{0}">{1}</a>'.format(target, text)
224
225            editor.beginUndoAction()
226            cline, cindex = editor.getCursorPosition()
227            editor.insert(link)
228            editor.setCursorPosition(cline, cindex + len(link))
229            editor.endUndoAction()
230
231    def hasLine(self):
232        """
233        Public method to indicate the availability of a horizontal line markup.
234
235        @return flag indicating the availability of a horizontal line markup
236        @rtype bool
237        """
238        return True
239
240    def line(self, editor):
241        """
242        Public method to generate a horizontal line text.
243
244        @param editor reference to the editor to work on
245        @type Editor
246        """
247        if editor is None:
248            return
249
250        editor.beginUndoAction()
251        markup = "<hr />"
252        editor.insert(markup)
253        cline, cindex = editor.getCursorPosition()
254        editor.setCursorPosition(cline, cindex + len(markup))
255        editor.endUndoAction()
256
257    def hasQuote(self):
258        """
259        Public method to indicate the availability of block quote markup.
260
261        @return flag indicating the availability of block quote markup
262        @rtype bool
263        """
264        return True
265
266    def quote(self, editor):
267        """
268        Public method to generate block quote text.
269
270        @param editor reference to the editor to work on
271        @type Editor
272        """
273        self.__insertMarkup("blockquote", editor, True)
274
275    def hasImage(self):
276        """
277        Public method to indicate the availability of image markup.
278
279        @return flag indicating the availability of image markup
280        @rtype bool
281        """
282        return True
283
284    def image(self, editor):
285        """
286        Public method to generate image text.
287
288        @param editor reference to the editor to work on
289        @type Editor
290        """
291        if editor is None:
292            return
293
294        from .ImageMarkupDialog import ImageMarkupDialog
295        dlg = ImageMarkupDialog(ImageMarkupDialog.HtmlMode)
296        if dlg.exec() == QDialog.DialogCode.Accepted:
297            address, altText, title, originalSize, width, height = (
298                dlg.getData()
299            )
300
301            markup = '<img src="{0}"'.format(address)
302            if altText:
303                markup = '{0} alt="{1}"'.format(markup, altText)
304            if title:
305                markup = '{0} title="{1}"'.format(markup, title)
306            if not originalSize:
307                markup = '{0} width="{1}" height="{2}"'.format(
308                    markup, width, height)
309            markup = '{0} />'.format(markup)
310
311            editor.beginUndoAction()
312            editor.insert(markup)
313            cline, cindex = editor.getCursorPosition()
314            editor.setCursorPosition(cline, cindex + len(markup))
315            editor.endUndoAction()
316
317    def hasBulletedList(self):
318        """
319        Public method to indicate the availability of bulleted list markup.
320
321        @return flag indicating the availability of bulleted list markup
322        @rtype bool
323        """
324        return True
325
326    def bulletedList(self, editor):
327        """
328        Public method to generate bulleted list text.
329
330        @param editor reference to the editor to work on
331        @type Editor
332        """
333        self.__makeList(editor, "ul")
334
335    def hasNumberedList(self):
336        """
337        Public method to indicate the availability of numbered list markup.
338
339        @return flag indicating the availability of numbered list markup
340        @rtype bool
341        """
342        return True
343
344    def numberedList(self, editor):
345        """
346        Public method to generate numbered list text.
347
348        @param editor reference to the editor to work on
349        @type Editor
350        """
351        self.__makeList(editor, "ol")
352
353    def __makeList(self, editor, listType):
354        """
355        Private method to generate the desired list markup.
356
357        @param editor reference to the editor to work on
358        @type Editor
359        @param listType type of the desired list (should be ul or ol)
360        @type str
361        """
362        if editor is None:
363            return
364
365        lineSeparator = editor.getLineSeparator()
366        editor.beginUndoAction()
367        if editor.hasSelectedText():
368            startLine, startIndex, endLine, endIndex = (
369                editor.getSelection()
370            )
371            if endIndex == 0:
372                endLine -= 1
373            for line in range(startLine, endLine + 1):
374                editor.insertAt("</li>", line, len(editor.text(line).rstrip()))
375                editor.insertAt("  <li>", line, 0)
376            if line == editor.lines() - 1:
377                editor.insertAt(lineSeparator, line, 1000)
378            editor.insertAt("</{1}>{0}".format(lineSeparator, listType),
379                            endLine + 1, 0)
380            editor.insertAt("<{1}>{0}".format(lineSeparator, listType),
381                            startLine, 0)
382            editor.setCursorPosition(endLine + 3, 0)
383        else:
384            listElements, ok = QInputDialog.getInt(
385                None,
386                QCoreApplication.translate(
387                    "HtmlProvider", "Create List"),
388                QCoreApplication.translate(
389                    "HtmlProvider", "Enter desired number of list elements:"),
390                0, 0, 99, 1)
391            if ok:
392                if listElements == 0:
393                    listElements = 1
394                cline, cindex = editor.getCursorPosition()
395                listBody = (
396                    listElements * "  <li></li>{0}".format(lineSeparator)
397                )
398                markup = "<{1}>{0}{2}</{1}>{0}".format(
399                    lineSeparator, listType, listBody)
400                if cindex == 0:
401                    editor.insertAt(markup, cline, cindex)
402                    editor.setCursorPosition(cline + 1, 6)
403                else:
404                    if cline == editor.lines() - 1:
405                        editor.insertAt(lineSeparator, cline, 1000)
406                    editor.insertAt(markup, cline + 1, 0)
407                    editor.setCursorPosition(cline + 2, 6)
408        editor.endUndoAction()
409