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