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
« 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
17import wx
18from wx import Colour
19from wx.lib.mixins.listctrl import TextEditMixin
21from ..editor.listeditor import AutoWidthColumnList, ListEditorBase
22from ..widgets import RIDEDialog, HelpLabel, ButtonWithHandler
24_ = wx.GetTranslation # To keep linter/code analyser happy
25builtins.__dict__['_'] = wx.GetTranslation
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""")
42class ConfigManagerDialog(RIDEDialog):
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)
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))
53 self.SetLayoutDirection(wx.Layout_LeftToRight)
54 self.plugin = plugin
55 self._create_ui(configs)
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))
65 def _create_editor(self, configs):
66 editor = _ConfigListEditor(self, configs)
67 self.Sizer.Add(editor, flag=wx.GROW, proportion=1)
68 return editor
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)
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)
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)
94 def get_data(self):
95 return self._editor.get_data()
98class _ConfigListEditor(ListEditorBase):
99 _buttons = [_('New'), _('Remove')]
100 _buttons_nt = ['New', 'Remove'] # Non-translated names
101 _columns = [_('Name'), _('Command'), _('Documentation')]
103 def __init__(self, parent, configs):
104 self._editor_open = False
105 ListEditorBase.__init__(self, parent, self._columns, configs)
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)
111 @staticmethod
112 def get_column_values(config):
113 return config.name, config.command, config.doc
115 def get_data(self):
116 return self._list.get_data()
118 def on_edit(self, event):
119 self._list.open_editor(self._selection)
121 def on_new(self, event):
122 __ = event
123 self._list.new_item()
125 def on_remove(self, event):
126 self.on_delete(event)
128 def new_config(self, data):
129 self._controller.add(*data)
131 def edit_config(self, index, data):
132 self._controller.edit(index, *data)
134 def on_delete(self, event):
135 if not self._editor_open:
136 ListEditorBase.on_delete(self, event)
138 def editor_open(self):
139 self._editor_open = True
141 def editor_closed(self):
142 self._editor_open = False
145class _TextEditListCtrl(AutoWidthColumnList, TextEditMixin):
146 last_index = property(lambda self: self.ItemCount - 1)
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)
153 self.SetBackgroundColour(Colour(color_background))
154 self.SetForegroundColour(Colour(color_foreground))
156 self._set_command_column_width()
157 self.col_locs = self._calculate_col_locs()
158 self._new_item_creation = False
160 def _set_command_column_width(self):
161 self.SetColumnWidth(1, 250)
163 def _calculate_col_locs(self):
164 """Calculates and returns initial locations of colums.
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
176 def open_editor(self, row):
177 self.OpenEditor(0, row)
179 def OpenEditor(self, col, row):
180 self._parent.editor_open()
181 TextEditMixin.OpenEditor(self, col, row)
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)
189 def get_data(self):
190 return [self._get_row(row) for row in range(self.ItemCount)]
192 def _get_row(self, row):
193 return [self.GetItem(row, col).GetText() for col in range(3)]
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()