Coverage for src/robotide/run/configmanagerui.py: 33%

123 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 

17import wx 

18from wx import Colour 

19from wx.lib.mixins.listctrl import TextEditMixin 

20 

21from ..editor.listeditor import AutoWidthColumnList, ListEditorBase 

22from ..widgets import RIDEDialog, HelpLabel, ButtonWithHandler 

23 

24_ = wx.GetTranslation # To keep linter/code analyser happy 

25builtins.__dict__['_'] = wx.GetTranslation 

26 

27_CONFIG_HELP = _("""The specified command string will be split from whitespaces into a command 

28and its arguments. If either the command or any of the arguments require 

29internal spaces, they must be written as '<SPACE>'.\n 

30The command will be executed in the system directly without opening a shell. 

31This means that shell commands and extensions are not available. For example, 

32in Windows batch files to execute must contain the '.bat' extension and 'dir' 

33command does not work.\n 

34Examples: 

35 robot.bat --include smoke C:\\my_tests 

36 svn update /home/robot 

37 C:\\Program<SPACE>Files\\App\\prg.exe argument<SPACE>with<SPACE>space, 

38Run configurations are stored in the RIDE settings file. 

39""") 

40 

41 

42class ConfigManagerDialog(RIDEDialog): 

43 

44 def __init__(self, configs, plugin): 

45 RIDEDialog.__init__(self, title=_('Manage Run Configurations')) 

46 # set Left to Right direction (while we don't have localization) 

47 

48 self.SetBackgroundColour(Colour(self.color_background)) 

49 # self.SetOwnBackgroundColour(Colour(self.color_background)) 

50 self.SetForegroundColour(Colour(self.color_foreground)) 

51 # self.SetOwnForegroundColour(Colour(self.color_foreground)) 

52 

53 self.SetLayoutDirection(wx.Layout_LeftToRight) 

54 self.plugin = plugin 

55 self._create_ui(configs) 

56 

57 def _create_ui(self, configs): 

58 self.SetSizer(wx.BoxSizer(wx.VERTICAL)) 

59 self._editor = self._create_editor(configs) 

60 self._create_help() 

61 self._create_line() 

62 self._create_buttons() 

63 self.SetSize((750, 500)) 

64 

65 def _create_editor(self, configs): 

66 editor = _ConfigListEditor(self, configs) 

67 self.Sizer.Add(editor, flag=wx.GROW, proportion=1) 

68 return editor 

69 

70 def _create_help(self): 

71 hhelp = HelpLabel(self, label=_CONFIG_HELP) 

72 hhelp.Wrap(700) 

73 self.Sizer.Add(hhelp, border=5, flag=wx.TOP) 

74 

75 def _create_line(self): 

76 line = wx.StaticLine(self, size=(20, -1), style=wx.LI_HORIZONTAL) 

77 if wx.VERSION < (4, 1, 0): 

78 self.Sizer.Add(line, border=5, flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP) 

79 else: 

80 self.Sizer.Add(line, border=5, flag=wx.GROW | wx.RIGHT | wx.TOP) 

81 

82 def _create_buttons(self, sizer=None): 

83 buttons = self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL) 

84 self.SetBackgroundColour(Colour(self.color_background)) 

85 self.SetForegroundColour(Colour(self.color_foreground)) 

86 for item in self.GetChildren(): 

87 if isinstance(item, (wx.Button, wx.BitmapButton, ButtonWithHandler)): 

88 item.SetBackgroundColour(Colour(self.color_secondary_background)) 

89 # item.SetOwnBackgroundColour(Colour(self.color_secondary_background)) 

90 item.SetForegroundColour(Colour(self.color_secondary_foreground)) 

91 # item.SetOwnForegroundColour(Colour(self.color_secondary_foreground)) 

92 self.Sizer.Add(buttons, flag=wx.ALIGN_CENTER | wx.ALL, border=5) 

93 

94 def get_data(self): 

95 return self._editor.get_data() 

96 

97 

98class _ConfigListEditor(ListEditorBase): 

99 _buttons = [_('New'), _('Remove')] 

100 _buttons_nt = ['New', 'Remove'] # Non-translated names 

101 _columns = [_('Name'), _('Command'), _('Documentation')] 

102 

103 def __init__(self, parent, configs): 

104 self._editor_open = False 

105 ListEditorBase.__init__(self, parent, self._columns, configs) 

106 

107 def _create_list(self, columns, data): 

108 return _TextEditListCtrl(self, columns, color_foreground=self.color_secondary_foreground, 

109 color_background=self.color_secondary_background, data=data) 

110 

111 @staticmethod 

112 def get_column_values(config): 

113 return config.name, config.command, config.doc 

114 

115 def get_data(self): 

116 return self._list.get_data() 

117 

118 def on_edit(self, event): 

119 self._list.open_editor(self._selection) 

120 

121 def on_new(self, event): 

122 __ = event 

123 self._list.new_item() 

124 

125 def on_remove(self, event): 

126 self.on_delete(event) 

127 

128 def new_config(self, data): 

129 self._controller.add(*data) 

130 

131 def edit_config(self, index, data): 

132 self._controller.edit(index, *data) 

133 

134 def on_delete(self, event): 

135 if not self._editor_open: 

136 ListEditorBase.on_delete(self, event) 

137 

138 def editor_open(self): 

139 self._editor_open = True 

140 

141 def editor_closed(self): 

142 self._editor_open = False 

143 

144 

145class _TextEditListCtrl(AutoWidthColumnList, TextEditMixin): 

146 last_index = property(lambda self: self.ItemCount - 1) 

147 

148 def __init__(self, parent, columns, color_foreground, color_background, data): 

149 AutoWidthColumnList.__init__(self, parent, columns, color_foreground=color_foreground, 

150 color_background=color_background, data=data) 

151 TextEditMixin.__init__(self) 

152 

153 self.SetBackgroundColour(Colour(color_background)) 

154 self.SetForegroundColour(Colour(color_foreground)) 

155 

156 self._set_command_column_width() 

157 self.col_locs = self._calculate_col_locs() 

158 self._new_item_creation = False 

159 

160 def _set_command_column_width(self): 

161 self.SetColumnWidth(1, 250) 

162 

163 def _calculate_col_locs(self): 

164 """Calculates and returns initial locations of colums. 

165 

166 This is needed so that TextEditMixin can work from context menu, 

167 without selecting the row first. 

168 """ 

169 locations = [0] 

170 loc = 0 

171 for n in range(self.GetColumnCount()): 

172 loc = loc + self.GetColumnWidth(n) 

173 locations.append(loc) 

174 return locations 

175 

176 def open_editor(self, row): 

177 self.OpenEditor(0, row) 

178 

179 def OpenEditor(self, col, row): 

180 self._parent.editor_open() 

181 TextEditMixin.OpenEditor(self, col, row) 

182 

183 def new_item(self): 

184 self._new_item_creation = True 

185 self.InsertItem(self.ItemCount, '') 

186 self.Select(self.ItemCount-1, True) 

187 self.open_editor(self.last_index) 

188 

189 def get_data(self): 

190 return [self._get_row(row) for row in range(self.ItemCount)] 

191 

192 def _get_row(self, row): 

193 return [self.GetItem(row, col).GetText() for col in range(3)] 

194 

195 def CloseEditor(self, event=None): 

196 TextEditMixin.CloseEditor(self, event) 

197 # It seems that this is called twice per editing action and in the 

198 # first time the value may be empty. 

199 # End new item creation only when there really is a value 

200 lastrow = self._get_row(self.last_index) 

201 if self._new_item_creation: 

202 if any(lastrow): 

203 self._new_item_creation = False 

204 self.Parent.new_config(lastrow) 

205 else: 

206 self.Parent.edit_config(self.curRow, self._get_row(self.curRow)) 

207 self.Select(self.curRow) 

208 self._parent.editor_closed()