1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2006 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing the Editor General configuration page.
8"""
9
10from PyQt5.QtCore import pyqtSlot, Qt
11from PyQt5.QtWidgets import QTreeWidgetItem, QHeaderView, QDialog
12from PyQt5.Qsci import QsciScintillaBase
13
14from E5Gui import E5MessageBox
15
16from .ConfigurationPageBase import ConfigurationPageBase
17from .Ui_EditorGeneralPage import Ui_EditorGeneralPage
18from .EditorLanguageTabIndentOverrideDialog import (
19    EditorLanguageTabIndentOverrideDialog
20)
21
22from QScintilla.DocstringGenerator import getSupportedDocstringTypes
23
24import Preferences
25import UI.PixmapCache
26
27
28class EditorGeneralPage(ConfigurationPageBase, Ui_EditorGeneralPage):
29    """
30    Class implementing the Editor General configuration page.
31    """
32    def __init__(self):
33        """
34        Constructor
35        """
36        super().__init__()
37        self.setupUi(self)
38        self.setObjectName("EditorGeneralPage")
39
40        self.addButton.setIcon(UI.PixmapCache.getIcon("plus"))
41        self.deleteButton.setIcon(UI.PixmapCache.getIcon("minus"))
42        self.editButton.setIcon(UI.PixmapCache.getIcon("edit"))
43
44        for docstringType, docstringStyle in sorted(
45            getSupportedDocstringTypes()
46        ):
47            self.docstringStyleComboBox.addItem(docstringStyle, docstringType)
48
49        # set initial values
50        self.tabwidthSlider.setValue(
51            Preferences.getEditor("TabWidth"))
52        self.indentwidthSlider.setValue(
53            Preferences.getEditor("IndentWidth"))
54        self.tabforindentationCheckBox.setChecked(
55            Preferences.getEditor("TabForIndentation"))
56        self.tabindentsCheckBox.setChecked(
57            Preferences.getEditor("TabIndents"))
58        self.converttabsCheckBox.setChecked(
59            Preferences.getEditor("ConvertTabsOnLoad"))
60        self.autoindentCheckBox.setChecked(
61            Preferences.getEditor("AutoIndentation"))
62        self.comment0CheckBox.setChecked(
63            Preferences.getEditor("CommentColumn0"))
64
65        self.sourceOutlineGroupBox.setChecked(
66            Preferences.getEditor("ShowSourceOutline"))
67        self.sourceOutlineWidthSpinBox.setValue(
68            Preferences.getEditor("SourceOutlineWidth"))
69        self.sourceOutlineWidthStepSpinBox.setValue(
70            Preferences.getEditor("SourceOutlineStepSize"))
71        self.sourceOutlineShowCodingCheckBox.setChecked(
72            Preferences.getEditor("SourceOutlineShowCoding"))
73
74        index = self.docstringStyleComboBox.findData(
75            Preferences.getEditor("DocstringType"))
76        self.docstringStyleComboBox.setCurrentIndex(index)
77        self.docstringCompletionCheckBox.setChecked(
78            Preferences.getEditor("DocstringAutoGenerate"))
79
80        virtualSpaceOptions = Preferences.getEditor("VirtualSpaceOptions")
81        self.vsSelectionCheckBox.setChecked(
82            virtualSpaceOptions & QsciScintillaBase.SCVS_RECTANGULARSELECTION)
83        self.vsUserCheckBox.setChecked(
84            virtualSpaceOptions & QsciScintillaBase.SCVS_USERACCESSIBLE)
85
86        self.__populateLanguageOverrideWidget()
87
88    def save(self):
89        """
90        Public slot to save the Editor General configuration.
91        """
92        Preferences.setEditor(
93            "TabWidth",
94            self.tabwidthSlider.value())
95        Preferences.setEditor(
96            "IndentWidth",
97            self.indentwidthSlider.value())
98        Preferences.setEditor(
99            "TabForIndentation",
100            self.tabforindentationCheckBox.isChecked())
101        Preferences.setEditor(
102            "TabIndents",
103            self.tabindentsCheckBox.isChecked())
104        Preferences.setEditor(
105            "ConvertTabsOnLoad",
106            self.converttabsCheckBox.isChecked())
107        Preferences.setEditor(
108            "AutoIndentation",
109            self.autoindentCheckBox.isChecked())
110        Preferences.setEditor(
111            "CommentColumn0",
112            self.comment0CheckBox.isChecked())
113
114        Preferences.setEditor(
115            "ShowSourceOutline",
116            self.sourceOutlineGroupBox.isChecked())
117        Preferences.setEditor(
118            "SourceOutlineWidth",
119            self.sourceOutlineWidthSpinBox.value())
120        Preferences.setEditor(
121            "SourceOutlineStepSize",
122            self.sourceOutlineWidthStepSpinBox.value())
123        Preferences.setEditor(
124            "SourceOutlineShowCoding",
125            self.sourceOutlineShowCodingCheckBox.isChecked())
126
127        Preferences.setEditor(
128            "DocstringType",
129            self.docstringStyleComboBox.currentData())
130        Preferences.setEditor(
131            "DocstringAutoGenerate",
132            self.docstringCompletionCheckBox.isChecked())
133
134        virtualSpaceOptions = QsciScintillaBase.SCVS_NONE
135        if self.vsSelectionCheckBox.isChecked():
136            virtualSpaceOptions |= QsciScintillaBase.SCVS_RECTANGULARSELECTION
137        if self.vsUserCheckBox.isChecked():
138            virtualSpaceOptions |= QsciScintillaBase.SCVS_USERACCESSIBLE
139        Preferences.setEditor("VirtualSpaceOptions", virtualSpaceOptions)
140
141        self.__saveLanguageOverrides()
142
143    def on_tabforindentationCheckBox_toggled(self, checked):
144        """
145        Private slot used to set the tab conversion check box.
146
147        @param checked flag received from the signal (boolean)
148        """
149        if checked and self.converttabsCheckBox.isChecked():
150            self.converttabsCheckBox.setChecked(not checked)
151        self.converttabsCheckBox.setEnabled(not checked)
152
153    def __populateLanguageOverrideWidget(self):
154        """
155        Private method to populate the language specific indentation and tab
156        width override widget.
157        """
158        overrides = Preferences.getEditor("TabIndentOverride")
159        for language, (tabWidth, indentWidth) in overrides.items():
160            self.__createOverrideItem(language, tabWidth, indentWidth)
161        self.languageOverrideWidget.sortItems(0, Qt.SortOrder.AscendingOrder)
162        self.__resizeOverrideColumns()
163        self.on_languageOverrideWidget_itemSelectionChanged()
164
165    def __createOverrideItem(self, language, tabWidth, indentWidth):
166        """
167        Private method to create an entry for a language override.
168
169        @param language name of the language
170        @type str
171        @param tabWidth tabulator width
172        @type int
173        @param indentWidth indentation width
174        @type int
175        """
176        itm = QTreeWidgetItem(self.languageOverrideWidget, [
177            language,
178            "{0:2d}".format(tabWidth),
179            "{0:2d}".format(indentWidth)])
180        itm.setTextAlignment(1, Qt.AlignmentFlag.AlignHCenter)
181        itm.setTextAlignment(2, Qt.AlignmentFlag.AlignHCenter)
182
183    def __resizeOverrideColumns(self):
184        """
185        Private method to resize the list columns.
186        """
187        self.languageOverrideWidget.header().resizeSections(
188            QHeaderView.ResizeMode.ResizeToContents)
189        self.languageOverrideWidget.header().setStretchLastSection(True)
190
191    def __saveLanguageOverrides(self):
192        """
193        Private method to save the language specific indentation and tab width
194        overrides.
195        """
196        overrides = {}
197        for row in range(self.languageOverrideWidget.topLevelItemCount()):
198            itm = self.languageOverrideWidget.topLevelItem(row)
199            language = itm.text(0)
200            overrides[language] = [
201                int(itm.text(1)),
202                int(itm.text(2)),
203            ]
204
205        Preferences.setEditor("TabIndentOverride", overrides)
206
207    @pyqtSlot()
208    def on_languageOverrideWidget_itemSelectionChanged(self):
209        """
210        Private slot handling a change of the override selection.
211        """
212        self.deleteButton.setEnabled(
213            len(self.languageOverrideWidget.selectedItems()) > 0)
214        self.editButton.setEnabled(
215            len(self.languageOverrideWidget.selectedItems()) == 1)
216
217    @pyqtSlot()
218    def on_addButton_clicked(self):
219        """
220        Private slot to add a new override entry.
221        """
222        languages = []
223        for row in range(self.languageOverrideWidget.topLevelItemCount()):
224            itm = self.languageOverrideWidget.topLevelItem(row)
225            languages.append(itm.text(0))
226        dlg = EditorLanguageTabIndentOverrideDialog(
227            editMode=False,
228            languages=languages,
229            tabWidth=self.tabwidthSlider.value(),
230            indentWidth=self.indentwidthSlider.value(),
231        )
232        if dlg.exec() == QDialog.DialogCode.Accepted:
233            language, tabWidth, indentWidth = dlg.getData()
234            self.__createOverrideItem(language, tabWidth, indentWidth)
235            self.languageOverrideWidget.sortItems(
236                0, Qt.SortOrder.AscendingOrder)
237            self.__resizeOverrideColumns()
238
239    @pyqtSlot()
240    def on_deleteButton_clicked(self):
241        """
242        Private slot to delete the selected override entries.
243        """
244        ok = E5MessageBox.yesNo(
245            self,
246            self.tr("Tab and Indent Override"),
247            self.tr("""Shall the selected entries really be removed?"""))
248        if ok:
249            for itm in self.languageOverrideWidget.selectedItems():
250                index = self.languageOverrideWidget.indexOfTopLevelItem(itm)
251                self.languageOverrideWidget.takeTopLevelItem(index)
252                del itm
253
254    @pyqtSlot()
255    def on_editButton_clicked(self):
256        """
257        Private slot to edit the selected override entry.
258        """
259        itm = self.languageOverrideWidget.selectedItems()[0]
260        dlg = EditorLanguageTabIndentOverrideDialog(
261            editMode=True,
262            languages=[itm.text(0)],
263            tabWidth=int(itm.text(1)),
264            indentWidth=int(itm.text(2)),
265        )
266        if dlg.exec() == QDialog.DialogCode.Accepted:
267            language, tabWidth, indentWidth = dlg.getData()
268            itm.setText(1, "{0:2d}".format(tabWidth))
269            itm.setText(2, "{0:2d}".format(indentWidth))
270
271
272def create(dlg):
273    """
274    Module function to create the configuration page.
275
276    @param dlg reference to the configuration dialog
277    @return reference to the instantiated page (ConfigurationPageBase)
278    """
279    page = EditorGeneralPage()
280    return page
281