1# -*- coding: utf-8 -*-
2
3# Copyright (c) 2004 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4#
5
6"""
7Module implementing a dialog to enter the parameters for eric6_api.
8"""
9
10import sys
11import os
12import copy
13
14from PyQt5.QtCore import pyqtSlot, Qt
15from PyQt5.QtWidgets import QDialog, QDialogButtonBox
16
17from E5Gui.E5PathPicker import E5PathPickerModes
18
19from .Ui_EricapiConfigDialog import Ui_EricapiConfigDialog
20import Utilities
21import DocumentationTools
22
23from eric6config import getConfig
24
25
26class EricapiConfigDialog(QDialog, Ui_EricapiConfigDialog):
27    """
28    Class implementing a dialog to enter the parameters for eric6_api.
29    """
30    def __init__(self, project, parms=None, parent=None):
31        """
32        Constructor
33
34        @param project reference to the project object (Project.Project)
35        @param parms parameters to set in the dialog
36        @param parent parent widget of this dialog
37        """
38        super().__init__(parent)
39        self.setupUi(self)
40
41        self.outputFilePicker.setMode(E5PathPickerModes.SaveFileMode)
42        self.outputFilePicker.setDefaultDirectory(project.getProjectPath())
43        self.outputFilePicker.setFilters(self.tr(
44            "API files (*.api);;All files (*)"))
45
46        self.ignoreDirPicker.setMode(E5PathPickerModes.DirectoryMode)
47        self.ignoreDirPicker.setDefaultDirectory(project.getProjectPath())
48
49        self.buttonBox.button(
50            QDialogButtonBox.StandardButton.Ok).setEnabled(False)
51        for language in sorted(
52                DocumentationTools.supportedExtensionsDictForApis.keys()):
53            self.languagesList.addItem(language)
54
55        self.ppath = project.getProjectPath()
56        self.project = project
57
58        self.__initializeDefaults()
59
60        # get a copy of the defaults to store the user settings
61        self.parameters = copy.deepcopy(self.defaults)
62
63        # combine it with the values of parms
64        if parms is not None:
65            self.parameters.update(parms)
66        self.parameters['outputFile'] = Utilities.toNativeSeparators(
67            self.parameters['outputFile'])
68
69        self.recursionCheckBox.setChecked(self.parameters['useRecursion'])
70        self.includePrivateCheckBox.setChecked(
71            self.parameters['includePrivate'])
72        self.outputFilePicker.setText(self.parameters['outputFile'])
73        self.baseEdit.setText(self.parameters['basePackage'])
74        self.ignoreDirsList.clear()
75        for d in self.parameters['ignoreDirectories']:
76            self.ignoreDirsList.addItem(d)
77        self.sourceExtEdit.setText(
78            ", ".join(self.parameters['sourceExtensions']))
79        self.excludeFilesEdit.setText(
80            ", ".join(self.parameters['ignoreFilePatterns']))
81        for language in self.parameters['languages']:
82            if language == "Python":
83                # convert Python to the more specific Python3
84                language = "Python3"
85            items = self.languagesList.findItems(
86                language, Qt.MatchFlags(Qt.MatchFlag.MatchExactly))
87            items and items[0].setSelected(True)
88
89    def __initializeDefaults(self):
90        """
91        Private method to set the default values.
92
93        These are needed later on to generate the commandline
94        parameters.
95        """
96        self.defaults = {
97            'useRecursion': False,
98            'includePrivate': False,
99            'outputFile': '',
100            'basePackage': '',
101            'ignoreDirectories': [],
102            'ignoreFilePatterns': [],
103            'sourceExtensions': [],
104        }
105
106        lang = self.project.getProjectLanguage()
107        if lang in DocumentationTools.supportedExtensionsDictForApis:
108            self.defaults['languages'] = [lang]
109        else:
110            self.defaults['languages'] = ["Python3"]
111
112    def generateParameters(self):
113        """
114        Public method that generates the commandline parameters.
115
116        It generates a list of strings to be used
117        to set the QProcess arguments for the ericapi call and
118        a dictionary containing the non default parameters. This
119        dictionary can be passed back upon object generation to overwrite
120        the default settings.
121
122        @return a tuple of the commandline parameters and non default
123            parameters (list of strings, dictionary)
124        """
125        parms = {}
126        args = []
127
128        # 1. the program name
129        args.append(sys.executable)
130        args.append(
131            Utilities.normabsjoinpath(getConfig('ericDir'), "eric6_api.py"))
132
133        # 2. the commandline options
134        if self.parameters['outputFile'] != self.defaults['outputFile']:
135            parms['outputFile'] = Utilities.fromNativeSeparators(
136                self.project.getRelativePath(self.parameters['outputFile']))
137            args.append('-o')
138            if os.path.isabs(self.parameters['outputFile']):
139                args.append(self.parameters['outputFile'])
140            else:
141                args.append(
142                    os.path.join(self.ppath, self.parameters['outputFile']))
143        else:
144            self.parameters['outputFile'] = self.defaults['outputFile']
145        if self.parameters['basePackage'] != self.defaults['basePackage']:
146            parms['basePackage'] = self.parameters['basePackage']
147            args.append('-b')
148            args.append(self.parameters['basePackage'])
149        if (
150            self.parameters['ignoreDirectories'] !=
151            self.defaults['ignoreDirectories']
152        ):
153            parms['ignoreDirectories'] = (
154                self.parameters['ignoreDirectories'][:]
155            )
156            for d in self.parameters['ignoreDirectories']:
157                args.append('-x')
158                args.append(d)
159        if (
160            self.parameters['ignoreFilePatterns'] !=
161            self.defaults['ignoreFilePatterns']
162        ):
163            parms['ignoreFilePatterns'] = (
164                self.parameters['ignoreFilePatterns'][:]
165            )
166            for pattern in self.parameters['ignoreFilePatterns']:
167                args.append("--exclude-file={0}".format(pattern))
168        if self.parameters['useRecursion'] != self.defaults['useRecursion']:
169            parms['useRecursion'] = self.parameters['useRecursion']
170            args.append('-r')
171        if (
172            self.parameters['sourceExtensions'] !=
173            self.defaults['sourceExtensions']
174        ):
175            parms['sourceExtensions'] = self.parameters['sourceExtensions'][:]
176            for ext in self.parameters['sourceExtensions']:
177                args.append('-t')
178                args.append(ext)
179        if (
180            self.parameters['includePrivate'] !=
181            self.defaults['includePrivate']
182        ):
183            parms['includePrivate'] = self.parameters['includePrivate']
184            args.append('-p')
185        parms['languages'] = self.parameters['languages'][:]
186        for lang in self.parameters['languages']:
187            args.append('--language={0}'.format(lang))
188
189        return (args, parms)
190
191    @pyqtSlot()
192    def on_outputFilePicker_aboutToShowPathPickerDialog(self):
193        """
194        Private slot called before the file selection dialog is shown.
195        """
196        startFile = self.outputFilePicker.text()
197        if not startFile:
198            self.outputFilePicker.setText(
199                self.project.getProjectName() + ".api")
200
201    @pyqtSlot(str)
202    def on_outputFilePicker_pathSelected(self, path):
203        """
204        Private slot handling the selection of an output file.
205
206        @param path path of the output file
207        @type str
208        """
209        # make it relative, if it is in a subdirectory of the project path
210        fn = self.project.getRelativePath(path)
211        self.outputFilePicker.setText(fn)
212
213    def on_outputFilePicker_textChanged(self, filename):
214        """
215        Private slot to enable/disable the "OK" button.
216
217        @param filename name of the file (string)
218        """
219        self.buttonBox.button(
220            QDialogButtonBox.StandardButton.Ok).setEnabled(filename != "")
221
222    @pyqtSlot(str)
223    def on_ignoreDirPicker_pathSelected(self, path):
224        """
225        Private slot handling the selection of a directory to be ignored.
226
227        @param path path of the directory to be ignored
228        @type str
229        """
230        # make it relative, if it is in a subdirectory of the project path
231        dn = self.project.getRelativePath(path)
232        while dn.endswith(os.sep):
233            dn = dn[:-1]
234        self.ignoreDirPicker.setText(dn)
235
236    @pyqtSlot()
237    def on_addButton_clicked(self):
238        """
239        Private slot to add the directory displayed to the listview.
240
241        The directory in the ignore directories
242        line edit is moved to the listbox above and the edit is cleared.
243        """
244        basename = os.path.basename(self.ignoreDirPicker.text())
245        if basename:
246            self.ignoreDirsList.addItem(basename)
247            self.ignoreDirPicker.clear()
248
249    @pyqtSlot()
250    def on_deleteButton_clicked(self):
251        """
252        Private slot to delete the currently selected directory of the listbox.
253        """
254        itm = self.ignoreDirsList.takeItem(self.ignoreDirsList.currentRow())
255        del itm
256
257    def accept(self):
258        """
259        Public slot called by the Ok button.
260
261        It saves the values in the parameters dictionary.
262        """
263        self.parameters['useRecursion'] = self.recursionCheckBox.isChecked()
264        self.parameters['includePrivate'] = (
265            self.includePrivateCheckBox.isChecked()
266        )
267        outfile = self.outputFilePicker.text()
268        if outfile != '':
269            outfile = os.path.normpath(outfile)
270        self.parameters['outputFile'] = outfile
271        self.parameters['basePackage'] = self.baseEdit.text()
272        self.parameters['ignoreDirectories'] = []
273        for row in range(0, self.ignoreDirsList.count()):
274            itm = self.ignoreDirsList.item(row)
275            self.parameters['ignoreDirectories'].append(
276                os.path.normpath(itm.text()))
277        extensions = self.sourceExtEdit.text().split(',')
278        self.parameters['sourceExtensions'] = [
279            ext.strip() for ext in extensions if len(ext) > 0
280        ]
281        patterns = self.excludeFilesEdit.text().split(',')
282        self.parameters['ignoreFilePatterns'] = [
283            pattern.strip() for pattern in patterns
284        ]
285        self.parameters['languages'] = []
286        for itm in self.languagesList.selectedItems():
287            self.parameters['languages'].append(itm.text())
288
289        # call the accept slot of the base class
290        super().accept()
291