1# -*- coding: utf-8 -*- 2 3# Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> 4# 5 6""" 7Module implementing the Editor Highlighting Styles configuration page. 8""" 9 10import os 11 12from PyQt5.QtCore import pyqtSlot, Qt, QFileInfo, QFile, QIODevice 13from PyQt5.QtGui import QPalette, QFont, QColor 14from PyQt5.QtWidgets import ( 15 QColorDialog, QFontDialog, QInputDialog, QMenu, QTreeWidgetItem, QDialog 16) 17 18from .ConfigurationPageBase import ConfigurationPageBase 19from .Ui_EditorHighlightingStylesPage import Ui_EditorHighlightingStylesPage 20from ..SubstyleDefinitionDialog import SubstyleDefinitionDialog 21 22from E5Gui import E5MessageBox, E5FileDialog 23 24import UI.PixmapCache 25 26try: 27 MonospacedFontsOption = QFontDialog.FontDialogOption.MonospacedFonts 28except AttributeError: 29 MonospacedFontsOption = QFontDialog.FontDialogOptions(0x10) 30NoFontsOption = QFontDialog.FontDialogOptions(0) 31 32 33class EditorHighlightingStylesPage(ConfigurationPageBase, 34 Ui_EditorHighlightingStylesPage): 35 """ 36 Class implementing the Editor Highlighting Styles configuration page. 37 """ 38 FAMILYONLY = 0 39 SIZEONLY = 1 40 FAMILYANDSIZE = 2 41 FONT = 99 42 43 StyleRole = Qt.ItemDataRole.UserRole + 1 44 SubstyleRole = Qt.ItemDataRole.UserRole + 2 45 46 def __init__(self, lexers): 47 """ 48 Constructor 49 50 @param lexers reference to the lexers dictionary 51 """ 52 super().__init__() 53 self.setupUi(self) 54 self.setObjectName("EditorHighlightingStylesPage") 55 56 self.defaultSubstylesButton.setIcon(UI.PixmapCache.getIcon("editUndo")) 57 self.addSubstyleButton.setIcon(UI.PixmapCache.getIcon("plus")) 58 self.deleteSubstyleButton.setIcon(UI.PixmapCache.getIcon("minus")) 59 self.editSubstyleButton.setIcon(UI.PixmapCache.getIcon("edit")) 60 self.copySubstyleButton.setIcon(UI.PixmapCache.getIcon("editCopy")) 61 62 self.__fontButtonMenu = QMenu() 63 act = self.__fontButtonMenu.addAction(self.tr("Font")) 64 act.setData(self.FONT) 65 self.__fontButtonMenu.addSeparator() 66 act = self.__fontButtonMenu.addAction( 67 self.tr("Family and Size only")) 68 act.setData(self.FAMILYANDSIZE) 69 act = self.__fontButtonMenu.addAction(self.tr("Family only")) 70 act.setData(self.FAMILYONLY) 71 act = self.__fontButtonMenu.addAction(self.tr("Size only")) 72 act.setData(self.SIZEONLY) 73 self.__fontButtonMenu.triggered.connect(self.__fontButtonMenuTriggered) 74 self.fontButton.setMenu(self.__fontButtonMenu) 75 76 self.__allFontsButtonMenu = QMenu() 77 act = self.__allFontsButtonMenu.addAction(self.tr("Font")) 78 act.setData(self.FONT) 79 self.__allFontsButtonMenu.addSeparator() 80 act = self.__allFontsButtonMenu.addAction( 81 self.tr("Family and Size only")) 82 act.setData(self.FAMILYANDSIZE) 83 act = self.__allFontsButtonMenu.addAction(self.tr("Family only")) 84 act.setData(self.FAMILYONLY) 85 act = self.__allFontsButtonMenu.addAction(self.tr("Size only")) 86 act.setData(self.SIZEONLY) 87 self.__allFontsButtonMenu.triggered.connect( 88 self.__allFontsButtonMenuTriggered) 89 self.allFontsButton.setMenu(self.__allFontsButtonMenu) 90 91 self.lexer = None 92 self.lexers = lexers 93 94 # set initial values 95 import QScintilla.Lexers 96 languages = sorted([''] + list(self.lexers.keys())) 97 for language in languages: 98 self.lexerLanguageComboBox.addItem( 99 QScintilla.Lexers.getLanguageIcon(language, False), 100 language) 101 self.on_lexerLanguageComboBox_activated(0) 102 103 def save(self): 104 """ 105 Public slot to save the Editor Highlighting Styles configuration. 106 """ 107 for lexer in list(self.lexers.values()): 108 lexer.writeSettings() 109 110 @pyqtSlot(int) 111 def on_lexerLanguageComboBox_activated(self, index): 112 """ 113 Private slot to fill the style combo of the source page. 114 115 @param index index of the selected entry 116 @type int 117 """ 118 language = self.lexerLanguageComboBox.itemText(index) 119 120 self.styleElementList.clear() 121 self.styleGroup.setEnabled(False) 122 self.lexer = None 123 124 if not language: 125 return 126 127 try: 128 self.lexer = self.lexers[language] 129 except KeyError: 130 return 131 132 self.styleGroup.setEnabled(True) 133 for description, styleNo, subStyleNo in self.lexer.getStyles(): 134 if subStyleNo >= 0: 135 parent = self.styleElementList.findItems( 136 self.lexer.description(styleNo), 137 Qt.MatchFlag.MatchExactly)[0] 138 parent.setExpanded(True) 139 else: 140 parent = self.styleElementList 141 itm = QTreeWidgetItem(parent, [description]) 142 itm.setData(0, self.StyleRole, styleNo) 143 itm.setData(0, self.SubstyleRole, subStyleNo) 144 self.__styleAllItems() 145 self.styleElementList.setCurrentItem( 146 self.styleElementList.topLevelItem(0)) 147 148 def __stylesForItem(self, itm): 149 """ 150 Private method to get the style and sub-style number of the given item. 151 152 @param itm reference to the item to extract the styles from 153 @type QTreeWidgetItem 154 @return tuple containing the style and sub-style numbers 155 @rtype tuple of (int, int) 156 """ 157 style = itm.data(0, self.StyleRole) 158 substyle = itm.data(0, self.SubstyleRole) 159 160 return (style, substyle) 161 162 def __currentStyles(self): 163 """ 164 Private method to get the styles of the current item. 165 166 @return tuple containing the style and sub-style numbers 167 @rtype tuple of (int, int) 168 """ 169 itm = self.styleElementList.currentItem() 170 # return default style, if no current item 171 styles = (0, -1) if itm is None else self.__stylesForItem(itm) 172 173 return styles 174 175 def __styleOneItem(self, item, style, substyle): 176 """ 177 Private method to style one item of the style element list. 178 179 @param item reference to the item to be styled 180 @type QTreeWidgetItem 181 @param style base style number 182 @type int 183 @param substyle sub-style number 184 @type int 185 """ 186 colour = self.lexer.color(style, substyle) 187 paper = self.lexer.paper(style, substyle) 188 font = self.lexer.font(style, substyle) 189 eolfill = self.lexer.eolFill(style, substyle) 190 191 item.setFont(0, font) 192 item.setBackground(0, paper) 193 item.setForeground(0, colour) 194 if eolfill: 195 item.setCheckState(0, Qt.CheckState.Checked) 196 else: 197 item.setCheckState(0, Qt.CheckState.Unchecked) 198 199 def __styleAllItems(self): 200 """ 201 Private method to style all items of the style element list. 202 """ 203 itm = self.styleElementList.topLevelItem(0) 204 while itm is not None: 205 style, substyle = self.__stylesForItem(itm) 206 self.__styleOneItem(itm, style, substyle) 207 itm = self.styleElementList.itemBelow(itm) 208 209 @pyqtSlot(QTreeWidgetItem, QTreeWidgetItem) 210 def on_styleElementList_currentItemChanged(self, current, previous): 211 """ 212 Private method to handle a change of the current row. 213 214 @param current reference to the current item 215 @type QTreeWidgetItem 216 @param previous reference to the previous item 217 @type QTreeWidgetItem 218 """ 219 if current is None: 220 return 221 222 style, substyle = self.__stylesForItem(current) 223 colour = self.lexer.color(style, substyle) 224 paper = self.lexer.paper(style, substyle) 225 eolfill = self.lexer.eolFill(style, substyle) 226 font = self.lexer.font(style, substyle) 227 228 self.sampleText.setFont(font) 229 pl = self.sampleText.palette() 230 pl.setColor(QPalette.ColorRole.Text, colour) 231 pl.setColor(QPalette.ColorRole.Base, paper) 232 self.sampleText.setPalette(pl) 233 self.sampleText.repaint() 234 self.eolfillCheckBox.setChecked(eolfill) 235 236 selectedOne = len(self.styleElementList.selectedItems()) == 1 237 self.defaultSubstylesButton.setEnabled( 238 selectedOne and substyle < 0 and self.lexer.isBaseStyle(style)) 239 self.addSubstyleButton.setEnabled( 240 selectedOne and substyle < 0 and self.lexer.isBaseStyle(style)) 241 self.deleteSubstyleButton.setEnabled(selectedOne and substyle >= 0) 242 self.editSubstyleButton.setEnabled(selectedOne and substyle >= 0) 243 self.copySubstyleButton.setEnabled(selectedOne and substyle >= 0) 244 245 @pyqtSlot() 246 def on_foregroundButton_clicked(self): 247 """ 248 Private method used to select the foreground colour of the selected 249 style and lexer. 250 """ 251 style, substyle = self.__currentStyles() 252 colour = QColorDialog.getColor(self.lexer.color(style, substyle)) 253 if colour.isValid(): 254 pl = self.sampleText.palette() 255 pl.setColor(QPalette.ColorRole.Text, colour) 256 self.sampleText.setPalette(pl) 257 self.sampleText.repaint() 258 for selItem in self.styleElementList.selectedItems(): 259 style, substyle = self.__stylesForItem(selItem) 260 self.lexer.setColor(colour, style, substyle) 261 selItem.setForeground(0, colour) 262 263 @pyqtSlot() 264 def on_backgroundButton_clicked(self): 265 """ 266 Private method used to select the background colour of the selected 267 style and lexer. 268 """ 269 style, substyle = self.__currentStyles() 270 colour = QColorDialog.getColor(self.lexer.paper(style, substyle)) 271 if colour.isValid(): 272 pl = self.sampleText.palette() 273 pl.setColor(QPalette.ColorRole.Base, colour) 274 self.sampleText.setPalette(pl) 275 self.sampleText.repaint() 276 for selItem in self.styleElementList.selectedItems(): 277 style, substyle = self.__stylesForItem(selItem) 278 self.lexer.setPaper(colour, style, substyle) 279 selItem.setBackground(0, colour) 280 281 @pyqtSlot() 282 def on_allBackgroundColoursButton_clicked(self): 283 """ 284 Private method used to select the background colour of all styles of a 285 selected lexer. 286 """ 287 style, substyle = self.__currentStyles() 288 colour = QColorDialog.getColor(self.lexer.paper(style, substyle)) 289 if colour.isValid(): 290 pl = self.sampleText.palette() 291 pl.setColor(QPalette.ColorRole.Base, colour) 292 self.sampleText.setPalette(pl) 293 self.sampleText.repaint() 294 295 itm = self.styleElementList.topLevelItem(0) 296 while itm is not None: 297 style, substyle = self.__stylesForItem(itm) 298 self.lexer.setPaper(colour, style, substyle) 299 itm = self.styleElementList.itemBelow(itm) 300 self.__styleAllItems() 301 302 def __changeFont(self, doAll, familyOnly, sizeOnly): 303 """ 304 Private slot to change the highlighter font. 305 306 @param doAll flag indicating to change the font for all styles 307 (boolean) 308 @param familyOnly flag indicating to set the font family only (boolean) 309 @param sizeOnly flag indicating to set the font size only (boolean 310 """ 311 def setFont(font, style, substyle, familyOnly, sizeOnly): 312 """ 313 Local function to set the font. 314 315 @param font font to be set 316 @type QFont 317 @param style style number 318 @type int 319 @param substyle sub-style number 320 @type int 321 @param familyOnly flag indicating to set the font family only 322 @type bool 323 @param sizeOnly flag indicating to set the font size only 324 @type bool 325 """ 326 if familyOnly or sizeOnly: 327 newFont = QFont(self.lexer.font(style)) 328 if familyOnly: 329 newFont.setFamily(font.family()) 330 if sizeOnly: 331 newFont.setPointSize(font.pointSize()) 332 self.lexer.setFont(newFont, style, substyle) 333 else: 334 self.lexer.setFont(font, style, substyle) 335 336 def setSampleFont(font, familyOnly, sizeOnly): 337 """ 338 Local function to set the font of the sample text. 339 340 @param font font to be set (QFont) 341 @param familyOnly flag indicating to set the font family only 342 (boolean) 343 @param sizeOnly flag indicating to set the font size only (boolean 344 """ 345 if familyOnly or sizeOnly: 346 style, substyle = self.__currentStyles() 347 newFont = QFont(self.lexer.font(style, substyle)) 348 if familyOnly: 349 newFont.setFamily(font.family()) 350 if sizeOnly: 351 newFont.setPointSize(font.pointSize()) 352 self.sampleText.setFont(newFont) 353 else: 354 self.sampleText.setFont(font) 355 356 style, substyle = self.__currentStyles() 357 options = ( 358 MonospacedFontsOption 359 if self.monospacedButton.isChecked() else 360 NoFontsOption 361 ) 362 font, ok = QFontDialog.getFont(self.lexer.font(style, substyle), self, 363 "", options) 364 if ok: 365 setSampleFont(font, familyOnly, sizeOnly) 366 if doAll: 367 itm = self.styleElementList.topLevelItem(0) 368 while itm is not None: 369 style, substyle = self.__stylesForItem(itm) 370 setFont(font, style, substyle, familyOnly, sizeOnly) 371 itm = self.styleElementList.itemBelow(itm) 372 self.__styleAllItems() 373 else: 374 for selItem in self.styleElementList.selectedItems(): 375 style, substyle = self.__stylesForItem(selItem) 376 setFont(font, style, substyle, familyOnly, sizeOnly) 377 itmFont = self.lexer.font(style, substyle) 378 selItem.setFont(0, itmFont) 379 380 def __fontButtonMenuTriggered(self, act): 381 """ 382 Private slot used to select the font of the selected style and lexer. 383 384 @param act reference to the triggering action (QAction) 385 """ 386 if act is None: 387 return 388 389 familyOnly = act.data() in [self.FAMILYANDSIZE, self.FAMILYONLY] 390 sizeOnly = act.data() in [self.FAMILYANDSIZE, self.SIZEONLY] 391 self.__changeFont(False, familyOnly, sizeOnly) 392 393 def __allFontsButtonMenuTriggered(self, act): 394 """ 395 Private slot used to change the font of all styles of a selected lexer. 396 397 @param act reference to the triggering action (QAction) 398 """ 399 if act is None: 400 return 401 402 familyOnly = act.data() in [self.FAMILYANDSIZE, self.FAMILYONLY] 403 sizeOnly = act.data() in [self.FAMILYANDSIZE, self.SIZEONLY] 404 self.__changeFont(True, familyOnly, sizeOnly) 405 406 @pyqtSlot(bool) 407 def on_eolfillCheckBox_clicked(self, on): 408 """ 409 Private method used to set the eolfill for the selected style and 410 lexer. 411 412 @param on flag indicating enabled or disabled state (boolean) 413 """ 414 style, substyle = self.__currentStyles() 415 checkState = Qt.CheckState.Checked if on else Qt.CheckState.Unchecked 416 for selItem in self.styleElementList.selectedItems(): 417 style, substyle = self.__stylesForItem(selItem) 418 self.lexer.setEolFill(on, style, substyle) 419 selItem.setCheckState(0, checkState) 420 421 @pyqtSlot() 422 def on_allEolFillButton_clicked(self): 423 """ 424 Private method used to set the eolfill for all styles of a selected 425 lexer. 426 """ 427 on = self.tr("Enabled") 428 off = self.tr("Disabled") 429 selection, ok = QInputDialog.getItem( 430 self, 431 self.tr("Fill to end of line"), 432 self.tr("Select fill to end of line for all styles"), 433 [on, off], 434 0, False) 435 if ok: 436 enabled = selection == on 437 self.eolfillCheckBox.setChecked(enabled) 438 439 itm = self.styleElementList.topLevelItem(0) 440 while itm is not None: 441 style, substyle = self.__stylesForItem(itm) 442 self.lexer.setEolFill(enabled, style, substyle) 443 itm = self.styleElementList.itemBelow(itm) 444 self.__styleAllItems() 445 446 @pyqtSlot() 447 def on_defaultButton_clicked(self): 448 """ 449 Private method to set the current style to its default values. 450 """ 451 for selItem in self.styleElementList.selectedItems(): 452 style, substyle = self.__stylesForItem(selItem) 453 self.__setToDefault(style, substyle) 454 self.on_styleElementList_currentItemChanged( 455 self.styleElementList.currentItem(), None) 456 self.__styleAllItems() 457 458 @pyqtSlot() 459 def on_allDefaultButton_clicked(self): 460 """ 461 Private method to set all styles to their default values. 462 """ 463 itm = self.styleElementList.topLevelItem(0) 464 while itm is not None: 465 style, substyle = self.__stylesForItem(itm) 466 self.__setToDefault(style, substyle) 467 itm = self.styleElementList.itemBelow(itm) 468 self.on_styleElementList_currentItemChanged( 469 self.styleElementList.currentItem(), None) 470 self.__styleAllItems() 471 472 def __setToDefault(self, style, substyle): 473 """ 474 Private method to set a specific style to its default values. 475 476 @param style style number 477 @type int 478 @param substyle sub-style number 479 @type int 480 """ 481 self.lexer.setColor(self.lexer.defaultColor(style, substyle), 482 style, substyle) 483 self.lexer.setPaper(self.lexer.defaultPaper(style, substyle), 484 style, substyle) 485 self.lexer.setFont(self.lexer.defaultFont(style, substyle), 486 style, substyle) 487 self.lexer.setEolFill(self.lexer.defaultEolFill(style, substyle), 488 style, substyle) 489 490 ####################################################################### 491 ## Importing and exporting of styles 492 ####################################################################### 493 494 @pyqtSlot() 495 def on_importButton_clicked(self): 496 """ 497 Private slot to import styles to be selected. 498 """ 499 self.__importStyles(importAll=False) 500 501 @pyqtSlot() 502 def on_exportButton_clicked(self): 503 """ 504 Private slot to export styles to be selected. 505 """ 506 self.__exportStyles(exportAll=False) 507 508 @pyqtSlot() 509 def on_importAllButton_clicked(self): 510 """ 511 Private slot to import the styles of all lexers. 512 """ 513 self.__importStyles(importAll=True) 514 515 @pyqtSlot() 516 def on_exportAllButton_clicked(self): 517 """ 518 Private slot to export the styles of all lexers. 519 """ 520 self.__exportStyles(exportAll=True) 521 522 def __exportStyles(self, exportAll=False): 523 """ 524 Private method to export the styles of selectable lexers. 525 526 @param exportAll flag indicating to export all styles without asking 527 (defaults to False) 528 @type bool (optional) 529 """ 530 from eric6config import getConfig 531 stylesDir = getConfig("ericStylesDir") 532 533 lexerNames = list(self.lexers.keys()) 534 if not exportAll: 535 if self.lexer: 536 preselect = [self.lexer.language()] 537 else: 538 preselect = [] 539 from .EditorHighlightingStylesSelectionDialog import ( 540 EditorHighlightingStylesSelectionDialog) 541 dlg = EditorHighlightingStylesSelectionDialog( 542 lexerNames, forImport=False, preselect=preselect) 543 if dlg.exec() == QDialog.DialogCode.Accepted: 544 lexerNames = dlg.getLexerNames() 545 else: 546 # Cancelled by user 547 return 548 549 lexers = [self.lexers[name] for name in lexerNames] 550 551 fn, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( 552 self, 553 self.tr("Export Highlighting Styles"), 554 stylesDir, 555 self.tr("Highlighting Styles File (*.ehj);;" 556 "XML Highlighting Styles File (*.e6h)"), 557 "", 558 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) 559 560 if not fn: 561 return 562 563 ext = QFileInfo(fn).suffix() 564 if not ext: 565 ex = selectedFilter.split("(*")[1].split(")")[0] 566 if ex: 567 fn += ex 568 569 ok = ( 570 E5MessageBox.yesNo( 571 self, 572 self.tr("Export Highlighting Styles"), 573 self.tr("""<p>The highlighting styles file <b>{0}</b> exists""" 574 """ already. Overwrite it?</p>""").format(fn)) 575 if os.path.exists(fn) else 576 True 577 ) 578 579 if ok: 580 if fn.endswith(".ehj"): 581 from Preferences.HighlightingStylesFile import ( 582 HighlightingStylesFile 583 ) 584 highlightingStylesFile = HighlightingStylesFile() 585 highlightingStylesFile.writeFile(fn, lexers) 586 else: 587 f = QFile(fn) 588 if f.open(QIODevice.OpenModeFlag.WriteOnly): 589 from E5XML.HighlightingStylesWriter import ( 590 HighlightingStylesWriter 591 ) 592 HighlightingStylesWriter(f, lexers).writeXML() 593 f.close() 594 else: 595 E5MessageBox.critical( 596 self, 597 self.tr("Export Highlighting Styles"), 598 self.tr("<p>The highlighting styles file <b>{0}</b>" 599 " could not be written.</p><p>Reason: {1}</p>") 600 .format(fn, f.errorString()) 601 ) 602 603 def __importStyles(self, importAll=False): 604 """ 605 Private method to import the styles of lexers to be selected. 606 607 @param importAll flag indicating to import all styles without asking 608 (defaults to False) 609 @type bool (optional) 610 """ 611 from eric6config import getConfig 612 stylesDir = getConfig("ericStylesDir") 613 614 fn = E5FileDialog.getOpenFileName( 615 self, 616 self.tr("Import Highlighting Styles"), 617 stylesDir, 618 self.tr("Highlighting Styles File (*.ehj);;" 619 "XML Highlighting Styles File (*.e6h *.e4h)")) 620 621 if not fn: 622 return 623 624 if fn.endswith(".ehj"): 625 # new JSON based file 626 from Preferences.HighlightingStylesFile import ( 627 HighlightingStylesFile 628 ) 629 highlightingStylesFile = HighlightingStylesFile() 630 styles = highlightingStylesFile.readFile(fn) 631 if not styles: 632 return 633 else: 634 # old XML based file 635 f = QFile(fn) 636 if f.open(QIODevice.OpenModeFlag.ReadOnly): 637 from E5XML.HighlightingStylesReader import ( 638 HighlightingStylesReader 639 ) 640 reader = HighlightingStylesReader(f, self.lexers) 641 styles = reader.readXML() 642 f.close() 643 if not styles: 644 return 645 else: 646 E5MessageBox.critical( 647 self, 648 self.tr("Import Highlighting Styles"), 649 self.tr( 650 "<p>The highlighting styles file <b>{0}</b> could not" 651 " be read.</p><p>Reason: {1}</p>" 652 ).format(fn, f.errorString()) 653 ) 654 return 655 656 self.__applyStyles(styles, importAll=importAll) 657 self.on_lexerLanguageComboBox_activated( 658 self.lexerLanguageComboBox.currentIndex()) 659 660 def __applyStyles(self, stylesList, importAll=False): 661 """ 662 Private method to apply the imported styles to this dialog. 663 664 @param stylesList list of imported lexer styles 665 @type list of dict 666 @param importAll flag indicating to import all styles without asking 667 (defaults to False) 668 @type bool (optional) 669 """ 670 lexerNames = [d["name"] 671 for d in stylesList 672 if d["name"] in self.lexers] 673 674 if not importAll: 675 from .EditorHighlightingStylesSelectionDialog import ( 676 EditorHighlightingStylesSelectionDialog) 677 dlg = EditorHighlightingStylesSelectionDialog( 678 lexerNames, forImport=True) 679 if dlg.exec() == QDialog.DialogCode.Accepted: 680 lexerNames = dlg.getLexerNames() 681 else: 682 # Cancelled by user 683 return 684 685 for lexerDict in stylesList: 686 if lexerDict["name"] in lexerNames: 687 lexer = self.lexers[lexerDict["name"]] 688 for styleDict in lexerDict["styles"]: 689 style = styleDict["style"] 690 substyle = styleDict["substyle"] 691 lexer.setColor(QColor(styleDict["color"]), style, substyle) 692 lexer.setPaper(QColor(styleDict["paper"]), style, substyle) 693 font = QFont() 694 font.fromString(styleDict["font"]) 695 lexer.setFont(font, style, substyle) 696 lexer.setEolFill(styleDict["eolfill"], style, substyle) 697 if substyle >= 0: 698 # description and words can only be set for sub-styles 699 lexer.setDescription(styleDict["description"], 700 style, substyle) 701 lexer.setWords(styleDict["words"], style, substyle) 702 703 ####################################################################### 704 ## Methods to save and restore the state 705 ####################################################################### 706 707 def saveState(self): 708 """ 709 Public method to save the current state of the widget. 710 711 @return list containing the index of the selected lexer language 712 and a tuple containing the index of the parent selected lexer 713 entry and the index of the selected entry 714 @rtype list of int and tuple of (int, int) 715 """ 716 itm = self.styleElementList.currentItem() 717 if itm: 718 parent = itm.parent() 719 if parent is None: 720 currentData = ( 721 None, self.styleElementList.indexOfTopLevelItem(itm)) 722 else: 723 currentData = ( 724 self.styleElementList.indexOfTopLevelItem(parent), 725 parent.indexOfChild(itm) 726 ) 727 728 savedState = [ 729 self.lexerLanguageComboBox.currentIndex(), 730 currentData, 731 ] 732 else: 733 savedState = [] 734 return savedState 735 736 def setState(self, state): 737 """ 738 Public method to set the state of the widget. 739 740 @param state state data generated by saveState 741 """ 742 if state: 743 self.lexerLanguageComboBox.setCurrentIndex(state[0]) 744 self.on_lexerLanguageComboBox_activated( 745 self.lexerLanguageComboBox.currentIndex()) 746 747 parentIndex, index = state[1] 748 if parentIndex is None: 749 itm = self.styleElementList.topLevelItem(index) 750 else: 751 parent = self.styleElementList.topLevelItem(parentIndex) 752 itm = parent.child(index) 753 self.styleElementList.setCurrentItem(itm) 754 755 ####################################################################### 756 ## Methods to add, delete and edit sub-styles and their definitions 757 ####################################################################### 758 759 @pyqtSlot() 760 def on_addSubstyleButton_clicked(self): 761 """ 762 Private slot to add a new sub-style. 763 """ 764 style, substyle = self.__currentStyles() 765 dlg = SubstyleDefinitionDialog( 766 self.lexer, style, substyle, parent=self) 767 if dlg.exec() == QDialog.DialogCode.Accepted: 768 description, words = dlg.getData() 769 substyle = self.lexer.addSubstyle(style) 770 self.lexer.setDescription(description, style, substyle) 771 self.lexer.setWords(words, style, substyle) 772 773 parent = self.styleElementList.findItems( 774 self.lexer.description(style), Qt.MatchFlag.MatchExactly)[0] 775 parent.setExpanded(True) 776 itm = QTreeWidgetItem(parent, [description]) 777 itm.setData(0, self.StyleRole, style) 778 itm.setData(0, self.SubstyleRole, substyle) 779 self.__styleOneItem(itm, style, substyle) 780 781 @pyqtSlot() 782 def on_deleteSubstyleButton_clicked(self): 783 """ 784 Private slot to delete the selected sub-style. 785 """ 786 style, substyle = self.__currentStyles() 787 ok = E5MessageBox.yesNo( 788 self, 789 self.tr("Delete Sub-Style"), 790 self.tr("""<p>Shall the sub-style <b>{0}</b> really be""" 791 """ deleted?</p>""").format( 792 self.lexer.description(style, substyle)) 793 ) 794 if ok: 795 self.lexer.delSubstyle(style, substyle) 796 797 itm = self.styleElementList.currentItem() 798 parent = itm.parent() 799 index = parent.indexOfChild(itm) 800 parent.takeChild(index) 801 del itm 802 803 @pyqtSlot() 804 def on_editSubstyleButton_clicked(self): 805 """ 806 Private slot to edit the selected sub-style entry. 807 """ 808 style, substyle = self.__currentStyles() 809 dlg = SubstyleDefinitionDialog( 810 self.lexer, style, substyle, parent=self) 811 if dlg.exec() == QDialog.DialogCode.Accepted: 812 description, words = dlg.getData() 813 self.lexer.setDescription(description, style, substyle) 814 self.lexer.setWords(words, style, substyle) 815 816 itm = self.styleElementList.currentItem() 817 itm.setText(0, description) 818 819 @pyqtSlot() 820 def on_copySubstyleButton_clicked(self): 821 """ 822 Private slot to copy the selected sub-style. 823 """ 824 style, substyle = self.__currentStyles() 825 newSubstyle = self.lexer.addSubstyle(style) 826 827 description = self.tr("{0} - Copy").format( 828 self.lexer.description(style, substyle)) 829 self.lexer.setDescription(description, style, newSubstyle) 830 self.lexer.setWords(self.lexer.words(style, substyle), 831 style, newSubstyle) 832 self.lexer.setColor(self.lexer.color(style, substyle), 833 style, newSubstyle) 834 self.lexer.setPaper(self.lexer.paper(style, substyle), 835 style, newSubstyle) 836 self.lexer.setFont(self.lexer.font(style, substyle), 837 style, newSubstyle) 838 self.lexer.setEolFill(self.lexer.eolFill(style, substyle), 839 style, newSubstyle) 840 841 parent = self.styleElementList.findItems( 842 self.lexer.description(style), Qt.MatchFlag.MatchExactly)[0] 843 parent.setExpanded(True) 844 itm = QTreeWidgetItem(parent, [description]) 845 itm.setData(0, self.StyleRole, style) 846 itm.setData(0, self.SubstyleRole, newSubstyle) 847 self.__styleOneItem(itm, style, newSubstyle) 848 849 @pyqtSlot() 850 def on_defaultSubstylesButton_clicked(self): 851 """ 852 Private slot to reset all substyles to default values. 853 """ 854 style, substyle = self.__currentStyles() 855 ok = E5MessageBox.yesNo( 856 self, 857 self.tr("Reset Sub-Styles to Default"), 858 self.tr("<p>Do you really want to reset all defined sub-styles of" 859 " <b>{0}</b> to the default values?</p>""") 860 .format(self.lexer.description(style, substyle)) 861 ) 862 if ok: 863 # 1. reset sub-styles 864 self.lexer.loadDefaultSubStyles(style) 865 866 # 2. delete all existing sub-style items 867 parent = self.styleElementList.currentItem() 868 while parent.childCount() > 0: 869 itm = parent.takeChild(0) # __IGNORE_WARNING__ 870 del itm 871 872 # 3. create the new list of sub-style items 873 for description, _, substyle in self.lexer.getSubStyles(style): 874 itm = QTreeWidgetItem(parent, [description]) 875 itm.setData(0, self.StyleRole, style) 876 itm.setData(0, self.SubstyleRole, substyle) 877 self.__styleOneItem(itm, style, substyle) 878 879 880def create(dlg): 881 """ 882 Module function to create the configuration page. 883 884 @param dlg reference to the configuration dialog 885 @return reference to the instantiated page (ConfigurationPageBase) 886 """ 887 page = EditorHighlightingStylesPage(dlg.getLexers()) 888 return page 889