Robot Framework Integrated Development Environment (RIDE)
treecontroller.py
Go to the documentation of this file.
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 
16 import wx
17 
18 from robotide import utils
19 from robotide.action.actioninfo import ActionInfoCollection, ActionInfo
20 from robotide.context import IS_WINDOWS, ctrl_or_cmd, bind_keys_to_evt_menu
21 from ..macrocontrollers import TestCaseController
22 from robotide.controller import ctrlcommands
23 from robotide.controller.tags import Tag, DefaultTag
24 from robotide.publish import RideTestSelectedForRunningChanged
25 
26 tree_actions = """
27 [Navigate]
28 !Go &Back | Go back to previous location in tree | Alt-%s | ART_GO_BACK
29 !Go &Forward | Go forward to next location in tree | Alt-%s | ART_GO_FORWARD
30 """ % (('Left', 'Right') if IS_WINDOWS else ('Z', 'X'))
31 # Left and right cannot be overridden in tree on non Windows OSses, issue 354
32 
33 
35 
36  def __init__(self, tree, action_registerer, settings, test_selection, history=None):
37  self._tree_tree = tree
38  self._action_registerer_action_registerer = action_registerer
39  self.settingssettings = settings
40  self._history_history = history or _History()
41  self._test_selection_test_selection = test_selection
42 
44  actions = ActionInfoCollection(tree_actions, self, self._tree_tree)
45  self._action_registerer_action_registerer.register_actions(actions)
46  self._action_registerer_action_registerer.register_action(ActionInfo(menu_name='Edit', name='Add Tag to selected',
47  action=self.OnAddTagToSelectedOnAddTagToSelected))
48  self._action_registerer_action_registerer.register_action(ActionInfo(menu_name='Edit', name='Clear Selected',
49  action=self.OnClearSelectedOnClearSelected))
50 
51  def OnGoBack(self, event):
52  node = self._history_history.back()
53  if node:
54  self._tree_tree.SelectItem(node)
55 
56  def OnAddTagToSelected(self, event):
57  if self._test_selection_test_selection.is_empty():
58  return
59  name = wx.GetTextFromUser(message='Enter Tag Name', caption='Add Tag To Selected')
60  if name:
61  self._test_selection_test_selection.add_tag(name)
62 
63  def OnClearSelected(self, event):
64  self._test_selection_test_selection.clear_all(message=None)
65 
66  def OnGoForward(self, event):
67  node = self._history_history.forward()
68  if node:
69  self._tree_tree.SelectItem(node)
70 
71  def add_to_history(self, node):
72  self._history_history.change(node)
73 
74  def clear_history(self):
75  self._history_history.clear()
76 
77  def mark_controller_dirty(self, controller):
78  if not controller.dirty:
79  return
80  node = self.find_node_by_controllerfind_node_by_controller(controller)
81  if node:
82  self.mark_node_dirtymark_node_dirty(node)
83 
84  def mark_node_dirty(self, node):
85  text = self._tree_tree.GetItemText(node)
86  if not text.startswith('*'):
87  self._tree_tree.SetItemText(node, '*' + text)
88 
89  def find_node_by_controller(self, controller):
90  def match_handler(n):
91  handler = self.get_handlerget_handler(n)
92  return handler and controller is handler.controller
93  return self._find_node_with_predicate_find_node_with_predicate(self._tree_tree._root, match_handler)
94 
95  def find_node_with_label(self, node, label):
96  matcher = lambda n: utils.eq(self._tree_tree.GetItemText(n), label)
97  return self._find_node_with_predicate_find_node_with_predicate(node, matcher)
98 
99  def _find_node_with_predicate(self, node, predicate):
100  if node != self._tree_tree._root and predicate(node):
101  return node
102  item, cookie = self._tree_tree.GetFirstChild(node)
103  while item:
104  if predicate(item):
105  return item
106  if self._tree_tree.ItemHasChildren(item):
107  result = self._find_node_with_predicate_find_node_with_predicate(item, predicate)
108  if result:
109  return result
110  item, cookie = self._tree_tree.GetNextChild(node, cookie)
111  return None
112 
113  def get_handler(self, node=None):
114  return self._tree_tree.GetItemData(node or self._tree_tree.GetSelection())
115 
116  def bind_keys(self):
117  bind_keys_to_evt_menu(self._tree_tree, self._get_bind_keys_get_bind_keys())
118 
119  def _get_bind_keys(self):
120  bindings = [
121  (ctrl_or_cmd(), wx.WXK_UP, self._tree_tree.OnMoveUp),
122  (ctrl_or_cmd(), wx.WXK_DOWN, self._tree_tree.OnMoveDown),
123  (wx.ACCEL_NORMAL, wx.WXK_F2, self._tree_tree.label_editor.OnLabelEdit),
124  (wx.ACCEL_NORMAL, wx.WXK_WINDOWS_MENU, self._tree_tree.OnRightClick),
125  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('d'), lambda event: self._expanded_handler_expanded_handler().OnSafeDelete(event)),
126  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('f'),
127  lambda event: self._expanded_handler_expanded_handler().OnNewSuite(event)),
128  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('k'),
129  lambda event: self._expanded_handler_expanded_handler().OnNewUserKeyword(event)),
130  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('t'),
131  lambda event: self._expanded_handler_expanded_handler().OnNewTestCase(event)),
132  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('v'),
133  lambda event: self._expanded_handler_expanded_handler().OnNewScalar(event)),
134  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('l'),
135  lambda event: self._expanded_handler_expanded_handler().OnNewListVariable(event)),
136  (ctrl_or_cmd() | wx.ACCEL_SHIFT, ord('c'),
137  lambda event: self._expanded_handler_expanded_handler().OnCopy(event))
138  ]
139  if not IS_WINDOWS:
140  bindings.append((wx.ACCEL_NORMAL, wx.WXK_LEFT, self._tree_tree.OnLeftArrow))
141  return bindings
142 
143  def _expanded_handler(self):
144  handler = self.get_handlerget_handler()
145  if not self._tree_tree.IsExpanded(handler.node):
146  self._tree_tree.Expand(handler.node)
147  return handler
148 
149 
150 class _History():
151 
152  def __init__(self):
153  self._back_back = []
154  self._forward_forward = []
155 
156  def change(self, state):
157  if not self._back_back or state != self._back_back[-1]:
158  self._back_back.append(state)
159  self._forward_forward = []
160 
161  def back(self):
162  if not self._back_back:
163  return None
164  if len(self._back_back) > 1:
165  self._forward_forward.append(self._back_back.pop())
166  return self._back_back[-1]
167 
168  def forward(self):
169  if not self._forward_forward:
170  return None
171  state = self._forward_forward.pop()
172  self._back_back.append(state)
173  return state
174 
175  def top(self):
176  return self._back_back and self._back_back[-1] or None
177 
178  def clear(self):
179  self._back_back.clear()
180  self._forward_forward.clear()
181 
182 
184 
185  def __init__(self):
186  self._tests_tests: {TestCaseController} = set()
187 
188  def is_empty(self):
189  return not bool(self._tests_tests)
190 
191  def is_test_selected(self, test):
192  return test in self._tests_tests
193 
194  def clear_all(self, message):
195  self._tests_tests = set()
196  self._send_selection_changed_message_send_selection_changed_message()
197 
198  def unselect_all(self, tests):
199  self.select_allselect_all(tests, selected=False)
200 
201  def select_all(self, tests, selected=True):
202  for test in tests:
203  self.selectselect(test, selected, notifySelection=False)
204  self._send_selection_changed_message_send_selection_changed_message()
205 
206  def select(self, test: TestCaseController, doSelect=True, notifySelection=True):
207  changed = False
208  if doSelect and not self.is_test_selectedis_test_selected(test):
209  self._tests_tests.add(test)
210  changed = True
211  elif not doSelect and self.is_test_selectedis_test_selected(test):
212  self._tests_tests.remove(test)
213  changed = True
214  if notifySelection and changed:
215  self._send_selection_changed_message_send_selection_changed_message()
216 
217  def remove_invalid_cases_selection(self, cases_file_controller):
218  invalid_cases = list()
219  for test in self._tests_tests:
220  if test.datafile_controller == cases_file_controller:
221  invalid_cases.append(test)
222  for _ in invalid_cases:
223  self._tests_tests.remove(_)
224  self._send_selection_changed_message_send_selection_changed_message()
225 
227  message = RideTestSelectedForRunningChanged(tests=self._tests_tests)
228  wx.CallAfter(message.publish)
229 
230  def add_tag(self, name):
231  for test in self._tests_tests:
232  self._add_tag_to_test_add_tag_to_test(name, test)
233 
234  def _add_tag_to_test(self, name, test):
235  if name not in [t.name for t in test.tags]:
236  self._move_default_tags_to_test_move_default_tags_to_test(test)
237  self._add_tag_add_tag(test, name)
238 
239  def _move_default_tags_to_test(self, test):
240  for tag in test.tags:
241  if isinstance(tag, DefaultTag):
242  self._add_tag_add_tag(test, tag.name)
243 
244  def _add_tag(self, test, name):
245  test.tags.execute(ctrlcommands.ChangeTag(Tag(None), name))
Used to create menu entries, keyboard shortcuts and/or toolbar buttons.
Definition: actioninfo.py:176
def select(self, TestCaseController test, doSelect=True, notifySelection=True)
def __init__(self, tree, action_registerer, settings, test_selection, history=None)
Sent whenever a test is selected or unselected from the tree.
Definition: messages.py:197
def ActionInfoCollection(data, event_handler, container=None)
Parses the data into a list of ActionInfo and SeparatorInfo objects.
Definition: actioninfo.py:106
def bind_keys_to_evt_menu(target, actions)
Definition: __init__.py:74
def ctrl_or_cmd()
Definition: __init__.py:68