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

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. 

15 

16import builtins 1ab

17import os 1ab

18 

19import wx 1ab

20from wx import Colour 1ab

21from wx.lib.filebrowsebutton import DirBrowseButton 1ab

22from multiprocessing import shared_memory 1ab

23 

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 

39 

40 

41_ = wx.GetTranslation # To keep linter/code analyser happy 1ab

42builtins.__dict__['_'] = wx.GetTranslation 1ab

43 

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

46 

47DEFAULT_FFORMAT = "default file format" 1ab

48DOC_LANGUAGE = 'doc language' 1ab

49 

50 

51class _CreationDialog(RIDEDialog): 1ab

52 

53 formats = ["ROBOT", "TXT", "TSV"] # Removed "HTML" 1ab

54 

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() 

77 

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) 

83 

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) 

89 

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 

104 

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) 

108 

109 def _create_type_chooser(self, sizer): 1ab

110 return self._create_radiobuttons(sizer, _("Type"), [_("File"), _("Directory")]) 

111 

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 

124 

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 

164 

165 def _create_format_chooser(self, sizer, callback=True): 1ab

166 from ..controller.filecontrollers import ResourceFileController 

167 

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) 

173 

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 

182 

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 

197 

198 def _create_parent_display(self, sizer, path): 1ab

199 return self._create_display(sizer, _("Parent Directory"), path) 

200 

201 def _create_path_display(self, sizer, path): 1ab

202 return self._create_display(sizer, _("Created Path"), path, 

203 NewSuitePathValidator()) 

204 

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 

218 

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() 

226 

227 def _is_dir_type(self): 1ab

228 if not self._type_chooser: 

229 return False 

230 return self._type_chooser.GetStringSelection() == _("Directory") 

231 

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() 

241 

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] 

263 

264 def _get_extension(self): 1ab

265 if not self._format_chooser: 

266 return 'robot' 

267 return self._format_chooser.GetStringSelection().lower() 

268 

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() 

274 

275 

276class _WithImmutableParent(object): 1ab

277 

278 def _create_parent_chooser(self, sizer, default_dir): 1ab

279 _ = default_dir 

280 return self._create_parent_display(sizer, self._path) 

281 

282 

283class NewProjectDialog(_CreationDialog): 1ab

284 

285 def __init__(self, project): 1ab

286 self._controller = project 

287 self.dlg = _CreationDialog.__init__(self, project.default_dir, _("New Project")) 

288 

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 

296 

297 

298class NewResourceDialog(_WithImmutableParent, _CreationDialog): 1ab

299 

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 

307 

308 def _execute(self): 1ab

309 self._controller.execute(CreateNewResource(self._get_path())) 

310 

311 def _create_type_chooser(self, sizer): 1ab

312 return None 

313 

314 

315class AddSuiteDialog(_WithImmutableParent, _CreationDialog): 1ab

316 

317 NAME = _("Add Suite") 1ab

318 

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")) 

325 

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 

333 

334 def _execute(self): 1ab

335 cmd = AddTestDataDirectory if self._is_dir_type() else AddTestCaseFile 

336 self._controller.execute(cmd(self._get_path())) 

337 

338 

339class AddDirectoryDialog(AddSuiteDialog): 1ab

340 

341 NAME = _("Add Directory") 1ab

342 

343 def _create_type_chooser(self, sizer): 1ab

344 return None 

345 

346 def _is_dir_type(self): 1ab

347 return True 

348 

349 

350class _FileFormatDialog(_CreationDialog): 1ab

351 

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) 

360 

361 def _create_help(self, sizer): 1ab

362 """ Just ignore it """ 

363 pass 

364 

365 def _create_recursion_selector(self, sizer): 1ab

366 return None 

367 

368 def _get_format(self): 1ab

369 return self._chooser.GetStringSelection() 

370 

371 

372class ChangeFormatDialog(_FileFormatDialog): 1ab

373 

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 

383 

384 def _execute(self): 1ab

385 cmd = SetFileFormat if not self._get_recursive() \ 

386 else SetFileFormatRecuresively 

387 self._controller.execute(cmd(self._get_format())) 

388 

389 def _get_recursive(self): 1ab

390 return self._recursive and self._recursive.IsChecked() 

391 

392 

393class InitFileFormatDialog(_FileFormatDialog): 1ab

394 

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) 

399 

400 def _execute(self): 1ab

401 self._controller.execute(SetFileFormat(self._get_format())) 

402 

403 

404class RobotFilePathDialog(wx.FileDialog): 1ab

405 

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")) 

411 

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) 

429 

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