Coverage for src/robotide/editor/editors.py: 38%

313 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 wx 1ab

18 

19from abc import abstractmethod 1ab

20from wx import Colour 1ab

21from .settingeditors import ( 1ab

22 DocumentationEditor, SettingEditor, TagsEditor, 

23 ImportSettingListEditor, VariablesListEditor, MetadataListEditor) 

24from .. import robotapi, context 1ab

25from ..controller.settingcontrollers import ( 1ab

26 DocumentationController, VariableController, TagsController) 

27from ..publish import ( 1ab

28 RideItemSettingsChanged, RideInitFileRemoved, RideFileNameChanged) 

29from ..usages.UsageRunner import ResourceFileUsages 1ab

30from ..widgets import ( 1ab

31 ButtonWithHandler, Label, HeaderLabel, HorizontalSizer, HtmlWindow) 

32 

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

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

35 

36LIGHT_GREY = 'light grey' 1ab

37 

38 

39class WelcomePage(HtmlWindow): 1ab

40 undo = cut = copy = paste = delete = comment = uncomment = save \ 1ab

41 = show_content_assist = tree_item_selected = lambda *args: None 

42 

43 def __init__(self, parent): 1ab

44 HtmlWindow.__init__(self, parent, text=context.get_about_ride()) 1astuv

45 

46 def close(self): 1ab

47 self.Show(False) 

48 

49 def w_destroy(self): 1ab

50 self.close() 

51 self.Destroy() 

52 

53 

54class EditorPanel(wx.lib.scrolledpanel.ScrolledPanel): 1ab

55 """Base class for all editor panels""" 

56 # DEBUG: Move outside default editor package, document 

57 name = doc = '' 1ab

58 title = None 1ab

59 undo = cut = copy = paste = delete = comment = uncomment = save \ 1ab

60 = show_content_assist = lambda self: None 

61 

62 def __init__(self, plugin, parent, controller, tree): 1ab

63 wx.lib.scrolledpanel.ScrolledPanel.__init__(self, parent) 1aefcdg

64 from ..preferences import RideSettings 1aefcdg

65 _settings = RideSettings() 1aefcdg

66 self.general_settings = _settings['General'] 1aefcdg

67 self.color_background = self.general_settings.get('background', LIGHT_GREY) 1aefcdg

68 self.color_foreground = self.general_settings.get('foreground', 'black') 1aefcdg

69 self.color_secondary_background = self.general_settings.get('secondary background', LIGHT_GREY) 1aefcdg

70 self.color_secondary_foreground = self.general_settings.get('secondary foreground', 'black') 1aefcdg

71 self.color_background_help = self.general_settings.get('background help', (240, 242, 80)) 1aefcdg

72 self.color_foreground_text = self.general_settings.get('foreground text', (7, 0, 70)) 1aefcdg

73 self.font_face = self.general_settings.get('font face', '') 1aefcdg

74 self.font_size = self.general_settings.get('font size', 11) 1aefcdg

75 self.SetBackgroundColour(Colour(self.color_background)) 1aefcdg

76 self.SetForegroundColour(Colour(self.color_foreground)) 1aefcdg

77 self.plugin = plugin 1aefcdg

78 self.controller = controller 1aefcdg

79 try: 1aefcdg

80 self.language = controller.datafile_controller.language 1aefcdg

81 except AttributeError: 

82 self.language = ['en'] 

83 self._tree = tree 1aefcdg

84 

85 def tree_item_selected(self, item): 1ab

86 """ Maybe this needs to be published """ 

87 pass 

88 

89 

90class _RobotTableEditor(EditorPanel): 1ab

91 name = 'table' 1ab

92 doc = 'table editor' 1ab

93 kweditor = None 1ab

94 _settings_open_id = 'robot table settings open' 1ab

95 

96 def __init__(self, plugin, parent, controller, tree): 1ab

97 EditorPanel.__init__(self, plugin, parent, controller, tree) 1aefcdg

98 self.sizer = wx.BoxSizer(wx.VERTICAL) 1aefcdg

99 self.Bind(wx.EVT_IDLE, self.on_idle) 1aefcdg

100 self.SetSizer(self.sizer) 1aefcdg

101 if self.title: 101 ↛ 102line 101 didn't jump to line 102 because the condition on line 101 was never true1aefcdg

102 self.sizer.Add(self._create_header(self.title), 

103 0, wx.EXPAND | wx.ALL, 6) 

104 self._editors = [] 1aefcdg

105 self._last_shown_tooltip = None 1aefcdg

106 self._reset_last_show_tooltip() 1aefcdg

107 self._populate() 1aefcdg

108 self.plugin.subscribe(self._settings_changed, RideItemSettingsChanged) 1aefcdg

109 

110 @abstractmethod 1ab

111 def _populate(self): 1ab

112 pass 

113 

114 def _should_settings_be_open(self): 1ab

115 if self._settings_open_id not in self.plugin.global_settings: 

116 return False 

117 return self.plugin.global_settings[self._settings_open_id] 

118 

119 def _store_settings_open_status(self): 1ab

120 self.plugin.global_settings[self._settings_open_id] = \ 

121 self._settings.IsExpanded() 

122 

123 def _settings_changed(self, message): 1ab

124 if message.item == self.controller: 

125 for editor in self._editors: 

126 editor.update_value() 

127 

128 def on_idle(self, event): 1ab

129 __ = event 1ahijklmnopqr

130 if self._last_shown_tooltip and self._mouse_outside_tooltip(): 130 ↛ 131line 130 didn't jump to line 131 because the condition on line 130 was never true1ahijklmnopqr

131 self._last_shown_tooltip.hide() 

132 self._reset_last_show_tooltip() 

133 

134 def _mouse_outside_tooltip(self): 1ab

135 mx, my = wx.GetMousePosition() 

136 tx, ty = self._last_shown_tooltip.screen_position 

137 dx, dy = self._last_shown_tooltip.pw_size 

138 return (mx < tx or mx > tx+dx) or (my < ty or my > ty+dy) 

139 

140 def tooltip_allowed(self, tooltip): 1ab

141 if wx.GetMouseState().ControlDown() or \ 

142 self._last_shown_tooltip is tooltip: 

143 return False 

144 self._last_shown_tooltip = tooltip 

145 return True 

146 

147 def _reset_last_show_tooltip(self): 1ab

148 self._last_shown_tooltip = None 1aefcdg

149 

150 def close(self): 1ab

151 self.plugin.unsubscribe( 1cd

152 self._settings_changed, RideItemSettingsChanged) 

153 self.Unbind(wx.EVT_MOTION) 1cd

154 self.Show(False) 1cd

155 

156 def w_destroy(self): 1ab

157 self.close() 1cd

158 self.DestroyLater() 1cd

159 

160 def _create_header(self, text, readonly=False): 1ab

161 if readonly: 

162 text += _(' (READ ONLY)') 

163 self._title_display = HeaderLabel(self, text) 

164 return self._title_display 

165 

166 def _add_settings(self): 1ab

167 self._settings = self._create_settings() 

168 self._restore_settings_open_status() 

169 self._editors.append(self._settings) 

170 self.sizer.Add(self._settings, 0, wx.ALL | wx.EXPAND, 2) 

171 

172 def _create_settings(self): 1ab

173 settings = Settings(self) 

174 settings.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self._collabsible_changed) 

175 settings.build(self.controller.settings, self.plugin, self._tree) 

176 return settings 

177 

178 def _restore_settings_open_status(self): 1ab

179 if self._should_settings_be_open(): 

180 self._settings.Expand() 

181 wx.CallAfter(self._collabsible_changed) 

182 else: 

183 self._settings.Collapse() 

184 self.GetSizer().Layout() 

185 self.Refresh() 

186 self.Parent.GetSizer().Layout() 

187 self.Parent.Refresh() 

188 

189 def _collabsible_changed(self, event=None): 1ab

190 self._store_settings_open_status() 

191 self.GetSizer().Layout() 

192 self.Refresh() 

193 self.Parent.GetSizer().Layout() 

194 self.Parent.Refresh() 

195 if event: 

196 event.Skip() 

197 

198 def highlight_cell(self, obj, row, column): 1ab

199 """Highlight the given object at the given row and column""" 

200 if isinstance(obj, robotapi.Setting): 

201 setting_editor = self._get_settings_editor(obj) 

202 if setting_editor and hasattr(setting_editor, "highlight"): 

203 setting_editor.highlight(column) 

204 elif row >= 0 and column >= 0: 

205 self.kweditor.select(row, column) 

206 

207 def _get_settings_editor(self, setting): 1ab

208 """Return the settings editor for the given setting object""" 

209 for child in self.GetChildren(): 

210 if isinstance(child, SettingEditor) and child._item == setting: 

211 return child 

212 return None 

213 

214 def highlight(self, text, expand=True): 1ab

215 for editor in self._editors: 

216 editor.highlight(text, expand=expand) 

217 

218 

219class Settings(wx.CollapsiblePane): 1ab

220 BORDER = 2 1ab

221 

222 def __init__(self, parent): 1ab

223 wx.CollapsiblePane.__init__( 

224 self, parent, wx.ID_ANY, _('Settings'), 

225 style=wx.CP_DEFAULT_STYLE | wx.CP_NO_TLW_RESIZE) 

226 from ..preferences import RideSettings 

227 _settings = RideSettings() 

228 self.general_settings = _settings['General'] 

229 self.color_background = self.general_settings.get('background', LIGHT_GREY) 

230 self.color_foreground = self.general_settings.get('foreground', 'black') 

231 self.color_secondary_background = self.general_settings.get('secondary background', LIGHT_GREY) 

232 self.color_secondary_foreground = self.general_settings.get('secondary foreground', 'black') 

233 self.color_background_help = self.general_settings.get('background help', (240, 242, 80)) 

234 self.color_foreground_text = self.general_settings.get('foreground text', (7, 0, 70)) 

235 self.font_face = self.general_settings.get('font face', '') 

236 self.font_size = self.general_settings.get('font size', 11) 

237 self.SetBackgroundColour(Colour(self.color_background)) 

238 self.SetForegroundColour(Colour(self.color_foreground)) 

239 self._sizer = wx.BoxSizer(wx.VERTICAL) 

240 self._editors = [] 

241 self.Bind(wx.EVT_SIZE, self._recalc_size) 

242 

243 def Expand(self): 1ab

244 wx.CollapsiblePane.Expand(self) 

245 

246 def GetPane(self): 1ab

247 pane = wx.CollapsiblePane.GetPane(self) 

248 """ 

249 pane.SetBackgroundColour(Colour(200, 222, 40)) 

250 pane.SetOwnBackgroundColour(Colour(200, 222, 40)) 

251 pane.SetForegroundColour(Colour(7, 0, 70)) 

252 pane.SetOwnForegroundColour(Colour(7, 0, 70)) 

253 """ 

254 pane.tooltip_allowed = self.GetParent().tooltip_allowed 

255 return pane 

256 

257 def close(self): 1ab

258 for editor in self._editors: 

259 editor.close() 

260 

261 def update_value(self): 1ab

262 for editor in self._editors: 

263 editor.update_value() 

264 

265 def create_editor_for(self, controller, plugin, tree): 1ab

266 editor_cls = self._get_editor_class(controller) 

267 return editor_cls(self.GetPane(), controller, plugin, tree) 

268 

269 @staticmethod 1ab

270 def _get_editor_class(controller): 1ab

271 if isinstance(controller, DocumentationController): 

272 return DocumentationEditor 

273 if isinstance(controller, TagsController): 

274 return TagsEditor 

275 return SettingEditor 

276 

277 def build(self, settings, plugin, tree): 1ab

278 for setting in settings: 

279 # print(f"DEBUG: editors.py Settings build plugin={plugin.name} setting={setting.label}") 

280 editor = self.create_editor_for(setting, plugin, tree) 

281 self._sizer.Add(editor, 0, wx.ALL | wx.EXPAND, self.BORDER) 

282 self._editors.append(editor) 

283 editor.Refresh() 

284 self.GetPane().SetSizer(self._sizer) 

285 self._sizer.Layout() 

286 

287 def _recalc_size(self, event=None): 1ab

288 expand_button_height = 34 

289 total_height = 0 

290 if self.IsExpanded(): 

291 for editor in self._editors: 

292 total_height += editor.BestSize[1] 

293 total_height += 2 * self.BORDER + 1 

294 self.SetSizeHints(-1, total_height + expand_button_height) 

295 if event: 

296 event.Skip() 

297 

298 def highlight(self, text, expand=True): 1ab

299 match = False 

300 for editor in self._editors: 

301 if editor.contains(text): 

302 editor.highlight(text) 

303 match = True 

304 else: 

305 editor.clear_highlight() 

306 if match and expand: 

307 self.Expand() 

308 self.Parent.GetSizer().Layout() 

309 

310 

311class _FileEditor(_RobotTableEditor): 1ab

312 

313 def __init__(self, *args): 1ab

314 _RobotTableEditor.__init__(self, *args) 1efcdg

315 self.SetupScrolling() 1efcdg

316 self.plugin.subscribe( 1efcdg

317 self._update_source_and_name, RideFileNameChanged) 

318 

319 def _update_source(self, message=None): 1ab

320 _ = message 

321 self._source.SetValue(self.controller.data.source) 

322 

323 def _update_source_and_name(self, message): 1ab

324 _ = message 

325 self._title_display.SetLabel(self.controller.name) 

326 self._update_source() 

327 

328 def tree_item_selected(self, item): 1ab

329 if isinstance(item, VariableController): 

330 self._var_editor.select(item.name) 

331 

332 def _populate(self): 1ab

333 datafile = self.controller.data 

334 header = self._create_header( 

335 datafile.name, not self.controller.is_modifiable()) 

336 self.sizer.Add(header, 0, wx.EXPAND | wx.ALL, 5) 

337 self.sizer.Add(self._create_source_label(datafile.source), 0, wx.EXPAND | wx.ALL, 1) 

338 self.sizer.Add((0, 10)) 

339 self._add_settings() 

340 self._add_import_settings() 

341 self._add_variable_table() 

342 

343 def _create_source_label(self, source): 1ab

344 sizer = wx.BoxSizer(wx.HORIZONTAL) 

345 sizer.Add((5, 0)) 

346 sizer.Add(Label(self, label=_('Source'), size=(context.SETTING_LABEL_WIDTH, context.SETTING_ROW_HEIGHT))) 

347 self._source = wx.TextCtrl(self, style=wx.TE_READONLY | wx.NO_BORDER) 

348 self._source.SetBackgroundColour(Colour(self.color_background)) 

349 self._source.SetForegroundColour(Colour(self.color_foreground)) 

350 

351 self._source.SetValue(source) 

352 self._source.SetMaxSize(wx.Size(-1, context.SETTING_ROW_HEIGHT)) 

353 sizer.Add(self._source, 1, wx.EXPAND) 

354 return sizer 

355 

356 def _add_import_settings(self): 1ab

357 import_editor = ImportSettingListEditor(self, self._tree, self.controller.imports) 

358 self.sizer.Add(import_editor, 1, wx.EXPAND) 

359 self._editors.append(import_editor) 

360 

361 def _add_variable_table(self): 1ab

362 self._var_editor = VariablesListEditor(self, self._tree, self.controller.variables) 

363 self.sizer.Add(self._var_editor, 1, wx.EXPAND) 

364 self._editors.append(self._var_editor) 

365 

366 def close(self): 1ab

367 self.plugin.unsubscribe(self._update_source_and_name, RideFileNameChanged) 1cd

368 for editor in self._editors: 368 ↛ 369line 368 didn't jump to line 369 because the loop on line 368 never started1cd

369 editor.close() 

370 self._editors = [] 1cd

371 _RobotTableEditor.close(self) 1cd

372 

373 # Stubs so that ctrl+d ctrl+i don't throw exceptions 

374 delete_rows = insert_rows = lambda s: None 1ab

375 

376 

377class FindUsagesHeader(HorizontalSizer): 1ab

378 

379 def __init__(self, parent, header, usages_callback, color_foreground, color_background): 1ab

380 HorizontalSizer.__init__(self) 

381 self._header = HeaderLabel(parent, header) 

382 self.add_expanding(self._header) 

383 self.add_sizer(ButtonWithHandler(parent, _('Find Usages'), handler=usages_callback, 

384 color_secondary_foreground=color_foreground, 

385 color_secondary_background=color_background)) 

386 

387 def SetLabel(self, label): 1ab

388 self._header.SetLabel(label) 

389 

390 

391class ResourceFileEditor(_FileEditor): 1ab

392 _settings_open_id = 'resource file settings open' 1ab

393 

394 def _create_header(self, text, readonly=False): 1ab

395 if readonly: 

396 text += _(' (READ ONLY)') 

397 

398 def cb(event): 

399 __ = event 

400 ResourceFileUsages(self.controller, self._tree.highlight).show() 

401 self._title_display = FindUsagesHeader(self, text, cb, color_foreground=self.color_secondary_foreground, 

402 color_background=self.color_secondary_background) 

403 return self._title_display 

404 

405 

406class TestCaseFileEditor(_FileEditor): 1ab

407 __test__ = False 1ab

408 _settings_open_id = 'test case file settings open' 1ab

409 

410 def _populate(self): 1ab

411 _FileEditor._populate(self) 

412 self.sizer.Add((0, 10)) 

413 self._add_metadata() 

414 

415 def _add_metadata(self): 1ab

416 metadata_editor = MetadataListEditor(self, self._tree, self.controller.metadata) 

417 self.sizer.Add(metadata_editor, 1, wx.EXPAND) 

418 self._editors.append(metadata_editor) 

419 

420 

421class InitFileEditor(TestCaseFileEditor): 1ab

422 _settings_open_id = 'init file settings open' 1ab

423 

424 def _populate(self): 1ab

425 TestCaseFileEditor._populate(self) 

426 self.plugin.subscribe(self._init_file_removed, RideInitFileRemoved) 

427 

428 def _init_file_removed(self, message): 1ab

429 _ = message 

430 for setting, editor in zip(self.controller.settings, self._editors): 

431 editor.refresh_items(setting) 

432 

433 def close(self): 1ab

434 self.plugin.unsubscribe(self._init_file_removed, RideInitFileRemoved) 

435 super().close()