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