Coverage for src/robotide/ui/filedialogs.py: 22%
306 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-06 10:40 +0100
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-06 10:40 +0100
1# Copyright 2008-2015 Nokia Networks
2# Copyright 2016- Robot Framework Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
16import builtins 1ab
17import os 1ab
19import wx 1ab
20from wx import Colour 1ab
21from wx.lib.filebrowsebutton import DirBrowseButton 1ab
22from multiprocessing import shared_memory 1ab
24from ..controller.ctrlcommands import (CreateNewResource, AddTestDataDirectory, AddTestCaseFile, 1ab
25 CreateNewDirectoryProject, CreateNewFileProject, SetFileFormat,
26 SetFileFormatRecuresively)
27from ..robotapi import ROBOT_VERSION 1ab
28from ..preferences.general import read_languages, set_colors 1ab
29from .preferences_dialogs import boolean_editor, StringChoiceEditor 1ab
30from ..validators import NonEmptyValidator, NewSuitePathValidator, SuiteFileNameValidator 1ab
31from ..widgets import Label, RIDEDialog 1ab
32try: 1ab
33 from robot.conf.languages import Language 1ab
34except ImportError:
35 try:
36 from ..lib.compat.parsing.languages import Language
37 except ImportError:
38 Language = None
41_ = wx.GetTranslation # To keep linter/code analyser happy 1ab
42builtins.__dict__['_'] = wx.GetTranslation 1ab
44# This hack needed to set same label width as with other labels
45DirBrowseButton.createLabel = lambda self: Label(self, size=(110, -1), label=self.labelText) 1ab
47DEFAULT_FFORMAT = "default file format" 1ab
48DOC_LANGUAGE = 'doc language' 1ab
51class _CreationDialog(RIDEDialog): 1ab
53 formats = ["ROBOT", "TXT", "TSV"] # Removed "HTML" 1ab
55 def __init__(self, default_dir, title): 1ab
56 sizer = self._init_dialog(title)
57 label_sizer = wx.BoxSizer(wx.VERTICAL)
58 self._title = title
59 self._name_editor = self._create_name_editor(label_sizer)
60 self._parent_chooser = self._create_parent_chooser(
61 label_sizer, default_dir)
62 self._path_display = self._create_path_display(
63 label_sizer, default_dir)
64 radio_group_sizer = wx.BoxSizer(wx.VERTICAL)
65 self._type_chooser = self._create_type_chooser(radio_group_sizer)
66 self._format_chooser = self._create_format_chooser(radio_group_sizer)
67 edit_sizer = wx.BoxSizer(wx.HORIZONTAL)
68 edit_sizer.Add(label_sizer, 1, wx.EXPAND)
69 edit_sizer.Add(radio_group_sizer)
70 sizer.Add(edit_sizer) # , 1, wx.EXPAND)
71 content_sizer = wx.BoxSizer(wx.HORIZONTAL)
72 self._task_chooser = self._create_task_chooser(self, content_sizer)
73 self._language_chooser = self._create_lang_chooser(content_sizer)
74 sizer.Add(content_sizer, 1, wx.EXPAND)
75 self._finalize_dialog(sizer)
76 self._name_editor.SetFocus()
78 def _init_dialog(self, title): 1ab
79 RIDEDialog.__init__(self, title)
80 # set Left to Right direction (while we don't have localization)
81 self.SetLayoutDirection(wx.Layout_LeftToRight)
82 return wx.FlexGridSizer(rows=4, cols=1, vgap=10, hgap=10) # wx.BoxSizer(wx.VERTICAL)
84 def _finalize_dialog(self, sizer): 1ab
85 self._create_horizontal_line(sizer)
86 self._create_buttons(sizer)
87 sizer.Fit(self)
88 self.SetSizer(sizer)
90 def _create_name_editor(self, sizer): 1ab
91 disp_sizer = wx.BoxSizer(wx.HORIZONTAL)
92 self._add_label(disp_sizer, _("Name"))
93 name_editor = wx.TextCtrl(self)
94 name_editor.SetValidator(NonEmptyValidator(_("Name")))
95 name_editor.SetBackgroundColour(Colour(self.color_secondary_background))
96 name_editor.SetForegroundColour(Colour(self.color_secondary_foreground))
97 self.Bind(wx.EVT_TEXT, self.on_path_changed, name_editor)
98 if wx.VERSION < (4, 1, 0):
99 disp_sizer.Add(name_editor, 1, wx.ALIGN_CENTRE | wx.ALL | wx.EXPAND, 3)
100 else:
101 disp_sizer.Add(name_editor, 1, wx.ALL | wx.EXPAND, 3)
102 sizer.Add(disp_sizer, 1, wx.EXPAND)
103 return name_editor
105 def _add_label(self, sizer, text): 1ab
106 label = Label(self, label=text, size=(110, -1))
107 sizer.Add(label, flag=wx.CENTER | wx.ALL, border=3)
109 def _create_type_chooser(self, sizer): 1ab
110 return self._create_radiobuttons(sizer, _("Type"), [_("File"), _("Directory")])
112 def _create_task_chooser(self, window, sizer): 1ab
113 from ..preferences import RideSettings
114 _settings = RideSettings()
115 label, selector = boolean_editor(window, _settings, 'tasks',
116 ' '+_("Is Task?")+' ', _("Default for Tasks or Tests sections."))
117 selector.SetBackgroundColour(Colour(self.color_background))
118 selector.SetForegroundColour(Colour(self.color_foreground))
119 # self.Bind(wx.EVT_CHECKBOX, self.on_path_changed, selector)
120 task_box = wx.BoxSizer(wx.HORIZONTAL)
121 task_box.AddMany([label, selector])
122 sizer.Add(task_box, flag=wx.ALIGN_LEFT)
123 return selector
125 def _create_lang_chooser(self, sizer): 1ab
126 from ..preferences import RideSettings
127 from ..lib.compat.parsing.language import get_language_name
128 _settings = RideSettings()
129 lang = _settings.get(DOC_LANGUAGE, '')
130 languages = read_languages()
131 if languages[0] != '':
132 languages.insert(0, '')
133 # Remove non-existing languages
134 for value in languages:
135 if ROBOT_VERSION < (6, 1, 0) and value == 'Vietnamese':
136 languages.remove(value)
137 if ROBOT_VERSION < (7, 0, 1) and value == 'Japanese':
138 languages.remove(value)
139 if ROBOT_VERSION < (7, 1, ) and value == 'Korean':
140 languages.remove(value)
141 if isinstance(lang, list) and len(lang) > 0:
142 _settings[DOC_LANGUAGE] = lang[0]
143 lang = lang[0]
144 elif lang and len(lang) > 0:
145 _settings[DOC_LANGUAGE] = lang
146 else:
147 _settings[DOC_LANGUAGE] = ''
148 ll = StringChoiceEditor(_settings, DOC_LANGUAGE, _('Language')+' ', languages)
149 l_lang = ll.label(self)
150 set_colors(l_lang, Colour(self.color_background), Colour(self.color_foreground))
151 lang_box = wx.BoxSizer(wx.HORIZONTAL)
152 chooser = ll.chooser(self)
153 lang_box.AddMany([l_lang, chooser])
154 sizer.Add(lang_box)
155 sizer.Layout()
156 # Force no selection if lang code is en
157 if lang in ('en', ): # We will only consider English as the effective setting
158 lang = ''
159 lang_name = get_language_name(lang)
160 if lang_name in languages:
161 index = languages.index(lang_name)
162 ll.SetSelection(chooser, index)
163 return ll
165 def _create_format_chooser(self, sizer, callback=True): 1ab
166 from ..controller.filecontrollers import ResourceFileController
168 formats = list(self.formats)
169 if ((hasattr(self, '_controller') and isinstance(self._controller, ResourceFileController)) or
170 (hasattr(self, '_title') and self._title == _("New Resource File"))):
171 formats += ["RESOURCE"]
172 return self._create_radiobuttons(sizer, _("Format"), formats, callback)
174 def _create_radiobuttons(self, sizer, label, choices, callback=True): 1ab
175 radios = wx.RadioBox(self, label=label, choices=choices, majorDimension=4)
176 radios.SetBackgroundColour(Colour(self.color_background))
177 radios.SetForegroundColour(Colour(self.color_foreground))
178 if callback:
179 self.Bind(wx.EVT_RADIOBOX, self.on_path_changed, radios)
180 sizer.Add(radios, flag=wx.ALIGN_LEFT | wx.RA_SPECIFY_ROWS | wx.ALL, border=5)
181 return radios
183 def _create_parent_chooser(self, sizer, default_dir): 1ab
184 browser = DirBrowseButton(self, labelText=_("Parent Directory"),
185 dialogTitle=_("Choose Parent Directory"),
186 startDirectory=default_dir,
187 size=(600, -1), newDirectory=True,
188 changeCallback=self.on_path_changed)
189 browser.SetBackgroundColour(Colour(self.color_background))
190 browser.SetForegroundColour(Colour(self.color_foreground))
191 # DEBUG: Change colors on buttons and text field
192 # browser.SetOwnBackgroundColour(Colour(self.color_secondary_background))
193 # browser.SetOwnForegroundColour(Colour(self.color_secondary_foreground))
194 browser.SetValue(default_dir)
195 sizer.Add(browser, 1, wx.EXPAND)
196 return browser
198 def _create_parent_display(self, sizer, path): 1ab
199 return self._create_display(sizer, _("Parent Directory"), path)
201 def _create_path_display(self, sizer, path): 1ab
202 return self._create_display(sizer, _("Created Path"), path,
203 NewSuitePathValidator())
205 def _create_display(self, sizer, title, value, validator=None): 1ab
206 disp_sizer = wx.BoxSizer(wx.HORIZONTAL)
207 self._add_label(disp_sizer, title)
208 disp = wx.TextCtrl(self, value=value)
209 disp.SetBackgroundColour(Colour(self.color_background))
210 disp.SetForegroundColour(Colour(self.color_foreground))
211 disp.SetSizeHints(self.GetTextExtent(value)[0]+100, -1)
212 disp.SetEditable(False)
213 if validator:
214 disp.SetValidator(validator)
215 disp_sizer.Add(disp, 1, wx.ALL | wx.EXPAND, 3)
216 sizer.Add(disp_sizer, 1, wx.EXPAND)
217 return disp
219 def _get_path(self): 1ab
220 name = self._name_editor.GetValue()
221 path = os.path.join(self._parent_chooser.GetValue(),
222 name.replace(' ', '_'))
223 if self._is_dir_type():
224 path = os.path.join(path, '__init__')
225 return path + '.' + self._get_extension()
227 def _is_dir_type(self): 1ab
228 if not self._type_chooser:
229 return False
230 return self._type_chooser.GetStringSelection() == _("Directory")
232 @staticmethod 1ab
233 def _is_task_type(): 1ab
234 # if not self._task_chooser:
235 # return False
236 from ..preferences import RideSettings
237 _settings = RideSettings()
238 task = _settings.get('tasks', False)
239 # print(f"DEBUG: filedialogs.py _CreationDialog _is_task_type task={task}")
240 return task # self._task_chooser.GetValue()
242 def selected_language(self): 1ab
243 if not self._language_chooser:
244 return ['']
245 from ..preferences import RideSettings
246 _settings = RideSettings()
247 lang = _settings.get(DOC_LANGUAGE, '')
248 set_lang = shared_memory.ShareableList(name="language")
249 if lang and len(lang) > 0:
250 if isinstance(lang, list):
251 lang = lang[0]
252 if lang in ('en',): # We will only consider English as the effective setting
253 return ['en']
254 try:
255 mlang = Language.from_name(lang.replace('_', '-'))
256 set_lang[0] = mlang.code.replace('-', '_')
257 except ValueError: # For the case of missing language, like Ko
258 set_lang[0] = 'en'
259 return ['en']
260 else:
261 return ['en'] # [set_lang[0]]
262 return [mlang.name]
264 def _get_extension(self): 1ab
265 if not self._format_chooser:
266 return 'robot'
267 return self._format_chooser.GetStringSelection().lower()
269 def on_path_changed(self, event): 1ab
270 if not hasattr(self, "_path_display"):
271 return
272 self._path_display.SetValue(self._get_path())
273 event.Skip()
276class _WithImmutableParent(object): 1ab
278 def _create_parent_chooser(self, sizer, default_dir): 1ab
279 _ = default_dir
280 return self._create_parent_display(sizer, self._path)
283class NewProjectDialog(_CreationDialog): 1ab
285 def __init__(self, project): 1ab
286 self._controller = project
287 self.dlg = _CreationDialog.__init__(self, project.default_dir, _("New Project"))
289 def _execute(self): 1ab
290 cmd = CreateNewDirectoryProject if self._is_dir_type()\
291 else CreateNewFileProject
292 self.language = self.selected_language()
293 cmd(self._get_path(), self._is_task_type(), self.language).execute(self._controller)
294 if self.dlg:
295 del self.dlg
298class NewResourceDialog(_WithImmutableParent, _CreationDialog): 1ab
300 def __init__(self, controller, settings): 1ab
301 self._path = controller.directory
302 _CreationDialog.__init__(self, controller.default_dir,
303 _("New Resource File"))
304 self._format_chooser.SetStringSelection(
305 settings.get(DEFAULT_FFORMAT, "robot"))
306 self._controller = controller
308 def _execute(self): 1ab
309 self._controller.execute(CreateNewResource(self._get_path()))
311 def _create_type_chooser(self, sizer): 1ab
312 return None
315class AddSuiteDialog(_WithImmutableParent, _CreationDialog): 1ab
317 NAME = _("Add Suite") 1ab
319 def __init__(self, controller, settings): 1ab
320 self._controller = controller
321 self._path = controller.directory
322 _CreationDialog.__init__(self, self._path, self.NAME)
323 self._format_chooser.SetStringSelection(
324 settings.get(DEFAULT_FFORMAT, "robot"))
326 def _create_name_editor(self, sizer): 1ab
327 name_editor = _CreationDialog._create_name_editor(self, sizer)
328 name_editor.SetBackgroundColour(Colour(self.color_secondary_background))
329 name_editor.SetForegroundColour(Colour(self.color_secondary_foreground))
330 name_editor.SetValidator(
331 SuiteFileNameValidator(_("Name"), self._is_dir_type))
332 return name_editor
334 def _execute(self): 1ab
335 cmd = AddTestDataDirectory if self._is_dir_type() else AddTestCaseFile
336 self._controller.execute(cmd(self._get_path()))
339class AddDirectoryDialog(AddSuiteDialog): 1ab
341 NAME = _("Add Directory") 1ab
343 def _create_type_chooser(self, sizer): 1ab
344 return None
346 def _is_dir_type(self): 1ab
347 return True
350class _FileFormatDialog(_CreationDialog): 1ab
352 def __init__(self, controller): 1ab
353 sizer = self._init_dialog(_("Set Data Format"))
354 self._controller = controller
355 self._create_help(sizer)
356 self._chooser = self._create_format_chooser(sizer, callback=False)
357 self._chooser.SetStringSelection(controller.get_format() or "TXT")
358 self._recursive = self._create_recursion_selector(sizer)
359 self._finalize_dialog(sizer)
361 def _create_help(self, sizer): 1ab
362 """ Just ignore it """
363 pass
365 def _create_recursion_selector(self, sizer): 1ab
366 return None
368 def _get_format(self): 1ab
369 return self._chooser.GetStringSelection()
372class ChangeFormatDialog(_FileFormatDialog): 1ab
374 def _create_recursion_selector(self, sizer): 1ab
375 if not self._controller.is_directory_suite():
376 return None
377 selector = wx.CheckBox(self, label=_("Change recursively"))
378 selector.SetBackgroundColour(Colour(self.color_secondary_background))
379 selector.SetForegroundColour(Colour(self.color_secondary_foreground))
380 selector.SetValue(True)
381 sizer.Add(selector, flag=wx.ALL, border=5)
382 return selector
384 def _execute(self): 1ab
385 cmd = SetFileFormat if not self._get_recursive() \
386 else SetFileFormatRecuresively
387 self._controller.execute(cmd(self._get_format()))
389 def _get_recursive(self): 1ab
390 return self._recursive and self._recursive.IsChecked()
393class InitFileFormatDialog(_FileFormatDialog): 1ab
395 def _create_help(self, sizer): 1ab
396 ihelp = _("Provide format for initialization file in directory\n\"%s\".") \
397 % self._controller.directory
398 sizer.Add(Label(self, label=ihelp), flag=wx.ALL, border=5)
400 def _execute(self): 1ab
401 self._controller.execute(SetFileFormat(self._get_format()))
404class RobotFilePathDialog(wx.FileDialog): 1ab
406 def __init__(self, window, controller, settings): 1ab
407 self._controller = controller
408 style = wx.FD_OPEN
409 wx.FileDialog.__init__(self, window, style=style, wildcard=self._get_wildcard(settings),
410 defaultDir=self._controller.default_dir, message=_("Open"))
412 @staticmethod 1ab
413 def _get_wildcard(settings): 1ab
414 filetypes = [
415 ("robot", _("Robot data (*.robot)|*.robot")),
416 ("txt", _("Robot data (*.txt)|*.txt")),
417 ("resource", _("Robot resource file (*.resource)|*.resource")),
418 ("tsv", _("Robot Tab Separated data (*.tsv)|*.tsv")),
419 # ("html", "Robot HTML data (pre 3.2.2) (*.html)|*.html"),
420 ("all", _("All files|*.*"))
421 ]
422 default_format = settings.get(DEFAULT_FFORMAT, "robot")
423 robottypes = settings.get('robot types', ['robot', 'resource', 'txt', 'tsv']) # , 'html'
424 if default_format not in robottypes:
425 default_format = "all"
426 first = [ft for ft in filetypes if ft[0] == default_format]
427 rest = [ft for ft in filetypes if ft[0] != default_format]
428 return '|'.join(ft[1] for ft in first + rest)
430 def execute(self): 1ab
431 if self.ShowModal() == wx.ID_OK:
432 path = self.GetPath()
433 self._controller.update_default_dir(path)
434 else:
435 path = None
436 self.Destroy()
437 return path