19 from itertools
import chain
27 from .
import settingcontrollers
31 from .
import validators
33 from ..namespace.embeddedargs
import EmbeddedArgsHandler
34 from ..namespace
import namespace
36 from ..publish.messages
import (RideSelectResource, RideFileNameChanged, RideSaving, RideSaved, RideSaveAll,
38 from ..utils
import overrides, variablematcher
50 if not isinstance(other, Occurrence):
53 self.
_in_steps_in_steps()
and other._in_steps())
59 return self.
_item_item
71 return self.
_item_item.datafile
78 return self.
_item_item.parent.parent
79 return self.
_item_item.parent
85 return self.
_item_item.parent.name
92 return "Variable Table"
94 return self.
_item_item.label
97 return 'Steps' if self.
countcount == 1
else 'Steps (%d usages)' % self.
countcount
103 from .
import tablecontrollers
107 from .macrocontrollers
import KeywordNameController
109 return isinstance(self.
_item_item, KeywordNameController)
115 from .macrocontrollers
import ForLoopStepController
117 return isinstance(self.
_item_item.parent, ForLoopStepController)
126 return self.
_value_value, new_name
127 return new_name, self.
_value_value
138 raise NotImplementedError(self.__class__)
141 return '%s(%s)' % (self.__class__.__name__, self.
_params_str_params_str())
148 if isinstance(param, str):
149 return '"%s"' % param
178 tags = [tag
for tag
in context
if tag.controller == context]
180 context.notify_value_changed()
183 if old_values == []
and self.
_tag_tag.is_empty():
185 return ' | '.join(value
for value
in
190 if self.
_tag_tag.is_empty():
191 return [v.name
for v
in old_values] + [self.
_value_value]
195 if v != self.
_tag_tag:
196 new_list.append(v.name)
197 if self.
_value_value
not in new_list:
198 new_list += [self.
_value_value]
206 tag.controller.notify_value_changed()
217 result = self._execute(context)
222 _get_undo_command = property
225 raise NotImplementedError(self.__class__.__name__)
231 if not context.is_undo_empty():
232 result = context.pop_from_undo()._execute_without_redo_clear(context)
233 redo_command = context.pop_from_undo()
234 context.push_to_redo(redo_command)
241 if not context.is_redo_empty():
242 return context.pop_from_redo()._execute_without_redo_clear(context)
255 self.
_destination_destination.add_test_or_keyword(context)
264 res = context.new_resource(self.
_path_path)
276 context.set_datafile(self.
_datafile_datafile)
283 context.notify_steps_changed()
289 raise NotImplementedError(self.__class__.__name__)
293 return context.steps[self._row]
300 return lambda *args:
''
304 notify = finish =
lambda x:
None
312 _gherkin_prefix = re.compile(
'^(Given|When|Then|And|But) ', re.IGNORECASE)
314 def __init__(self, original_name, new_name, observer, keyword_info=None):
325 was_gherkin, keyword_name = self.
_get_gherkin_get_gherkin(original_name)
326 is_gherkin, new_keyword_name = self.
_get_gherkin_get_gherkin(new_name)
327 if was_gherkin
and not is_gherkin:
328 keyword_name = original_name
329 if not was_gherkin
and is_gherkin:
332 new_keyword_name = new_name
333 if was_gherkin
and is_gherkin:
335 if original_name.split(
' ', 1)[0].
lower() != new_name.split(
337 new_keyword_name = new_name
338 keyword_name = original_name
339 return keyword_name, new_keyword_name
342 keyword_value = re.sub(self.
_gherkin_prefix_gherkin_prefix,
'', original_name)
343 value_is_gherkin = (keyword_value != original_name)
344 return value_is_gherkin, keyword_value
347 return (self._original_name, self.
_new_name_new_name,
359 context.update_namespace()
366 self._original_name, keyword_info=self.
_keyword_info_keyword_info)):
368 occurrences.append(occ)
373 for oc
in occurrences:
374 oc.replace_keyword(self.
_new_name_new_name)
378 for oc
in occurrences:
379 oc.notify_value_changed()
396 old_name = context.name
397 context.test_name.rename(self.
_new_name_new_name)
398 context.test_name._item.notify_name_changed(old_name)
411 validation_result = self.
_validator_validator.validate(context)
412 if validation_result:
413 old_filename = context.filename
414 context.set_basename(self.
_new_basename_new_basename.strip())
416 old_filename=old_filename).publish()
417 return validation_result
423 directory_controller = excluded_controller.remove_from_excludes()
425 new_controller=directory_controller).publish()
431 excluded_controller = directory_controller.exclude()
433 new_controller=excluded_controller).publish()
438 def __init__(self, new_basename, get_should_modify_imports):
445 if validation_result:
446 old_filename = context.filename
448 if modify_imports
is None:
451 context.set_basename_and_modify_imports(self.
_new_basename_new_basename)
455 old_filename=old_filename).publish()
456 return validation_result
460 index_difference =
None
463 index_difference = context.sort_tests()
471 index_difference =
None
474 index_difference = context.sort_keywords()
482 index_difference =
None
485 index_difference = context.sort_variables()
537 context.editable_value = self.
_item_item
543 context.move_up(self.
_item_item)
549 context.move_down(self.
_item_item)
555 context.delete(self.
_item_item)
567 context.remove_from_filesystem()
575 context.open_filemanager()
581 context.remove_readonly()
587 context.remove_folder_from_filesystem()
588 context.remove_from_model()
598 context.set_value(*self.
_values_values)
599 context.set_comment(self.
_comment_comment)
609 lib = context.add_library(*self.
_values_values)
610 lib.set_comment(self.
_comment_comment)
621 res = context.add_resource(*self.
_values_values)
622 res.set_comment(self.
_comment_comment)
633 var = context.add_variables(*self.
_values_values)
634 var.set_comment(self.
_comment_comment)
641 context.remove_static_imports_to_this()
642 DeleteFile.execute(self, context)
648 context.remove_static_imports_to_this()
649 DeleteFolder.execute(self, context)
654 def __init__(self, new_name, new_value, new_comment):
660 has_data = context.has_data()
664 context.notify_value_changed()
666 context.notify_variable_added()
682 def __init__(self, keyword_name, keyword_info=None):
683 if keyword_name.strip() ==
'':
684 raise ValueError(
'Keyword name can not be "%s"' % keyword_name)
690 if variablematcher.contains_scalar_variable(keyword_name)
and \
691 not variablematcher.is_variable(keyword_name):
694 kw.name = keyword_name
704 for df
in context.datafiles:
711 if datafile.filename
and \
712 os.path.basename(datafile.filename) == self.
_keyword_source_keyword_source:
717 for setting
in df.settings:
719 for test_items
in (self.
_items_from_test_items_from_test(test)
for test
in df.tests):
720 for item
in test_items:
723 for item
in kw_items:
727 return chain([kw.keyword_name]
if kw.source == self.
_keyword_source_keyword_source
728 else [], kw.steps, [kw.teardown]
if kw.teardown
else [])
732 return chain(test.settings, test.steps)
735 item_info = datafile_controller.keyword_info(self.
_keyword_name_keyword_name)
736 return item_info.source
if item_info
else None
744 return item.contains_keyword(
756 @overrides(FindOccurrences)
759 return item.contains_variable(self.
_keyword_name_keyword_name)
762 for itm
in FindOccurrences._items_from_datafile(self, df):
767 from .macrocontrollers
import TestCaseController
769 if isinstance(ctrl, TestCaseController):
775 return chain([kw.keyword_name], kw.steps, kw.settings)
783 for df
in context.datafiles:
791 return datafile
in [self.
_context_context.datafile_controller] + \
805 return name
in context.get_local_variables()
or \
806 any(step.contains_variable_assignment(name)
807 for step
in context.steps)
811 return context.datafile_controller.variables.contains_variable(name)
815 [
None, context.datafile_controller]
819 return name
in list(namespace._VariableStash.global_variables.keys())
823 if df.variables.contains_variable(name):
829 files = [context.datafile_controller]
831 files += [imp.get_imported_controller()
832 for imp
in f.imports
if imp.is_resource
and
833 imp.get_imported_controller()
not in files]
838 from .filecontrollers
import ResourceFileController
840 files = [context.datafile_controller]
842 if isinstance(f, ResourceFileController):
843 files += [imp.datafile_controller
844 for imp
in f.get_where_used()]
850 raise ValueError(
'Keyword can not be empty')
851 while cells[0] ==
'':
855 argstr =
' | '.join((
'${arg%s}' % (i + 1)
for i
in range(len(args))))
866 kw = context.create_keyword(self.
_kw_name_kw_name, self.
_args_args)
880 return context.create_test(self.
_test_name_test_name)
890 context.notify_suite_added(ctrl)
894 raise NotImplementedError(self.__class__.__name__)
900 return context.new_test_case_file(self.
_path_path)
906 return context.new_test_data_directory(self.
_path_path)
915 context.new_file_project(self.
_path_path)
924 context.new_directory_project(self.
_path_path)
933 context.save_with_new_format(self.
_format_format)
942 context.save_with_new_format_recursive(self.
_format_format)
950 var_controller.value,
951 var_controller.comment)
954 context.datafile_controller.\
969 var_controller = context.datafile_controller.\
972 return var_controller
978 return 'AddVariable("%s", "%s", "%s")' % \
1000 self.
_item_item.delete()
1008 def __init__(self, new_kw_name, new_kw_args, step_range):
1017 context.extract_keyword(self.
_name_name, self.
_args_args, self.
_rows_rows)
1018 context.notify_steps_changed()
1019 context.clear_undo()
1023 print(f
"DEBUG: ctrlcommands.py ExtractScalar name{name} value{value}, comment{comment}, cell{cell}")
1033 (row, col + 1), (row, col + len(cells) - 1)))
1045 steps = context.steps
1046 while len(steps) <= self.
_row_row:
1047 context.add_step(len(steps))
1048 steps = context.steps
1049 step = self.
_step_step(context)
1051 self.
_row_row, self.
_col_col, step.get_value(self.
_col_col), insert=
False)
1054 step.insert_value_before(self.
_col_col, self.
_value_value)
1059 self.
_step_step(context).remove_empty_columns_from_end(
not self.
_insert_insert)
1068 for newline
in (
'\r\n',
'\n',
'\r'):
1069 item = item.replace(newline,
'\\n')
1073 value = self.
_step_step(context).get_value(self.
_col_col).strip()
1074 should_be = self.
_value_value.strip()
1076 if value == should_be:
1078 return (value.replace(
' ',
'') ==
'FOR' and
1079 should_be.replace(
' ',
'') ==
'FOR')
or\
1080 (value.replace(
' ',
'') ==
'FOR' and
1081 should_be.replace(
' ',
'').upper() ==
':FOR')
or\
1082 (value.replace(
' ',
'') ==
'END' and
1083 should_be.replace(
' ',
'') ==
'END')
1089 return '%s(%s, %s, "%s")' % \
1090 (self.__class__.__name__, self.
_row_row, self.
_col_col, self.
_value_value)
1099 RideSaving(path=context.filename, datafile=context).publish()
1100 datafile_controller = context.datafile_controller
1102 for macro_controller
in chain(datafile_controller.tests, datafile_controller.keywords):
1103 macro_controller.execute(
Purify())
1104 datafile_controller.save()
1105 datafile_controller.unmark_dirty()
1106 RideSaved(path=context.filename).publish()
1115 for datafile_controller
in context._get_all_dirty_controllers():
1116 if datafile_controller.has_format():
1126 if len(context.steps) <= i:
1130 step = context.steps[i]
1131 step.remove_empty_columns_from_end()
1138 context.execute(
DeleteRows(context.get_empty_rows()))
1139 context.notify_steps_changed()
1149 return '%s, %s' % (self.
_row_row, self.
_col_col)
1152 self.
_step_step(context).shift_right(self.
_col_col, delete=delete)
1156 self.
_step_step(context)._recreate(context.steps[self.
_row_row].as_list())
1157 assert self.
_step_step(context).get_value(self.
_col_col) ==
'',
'Should have an empty value after insert'
1172 return self.
_row_row, self.
_col_col
1175 step = self.
_step_step(context)
1180 step.get_value(self.
_col_col), insert=
False))
1181 step.shift_left(self.
_col_col, delete=
True)
1198 if len(context.steps) <= self.
_row_row:
1200 self._change_value(context)
1204 return '%s(%s)' % (self.__class__.__name__, self.
_row_row)
1210 step = context.steps[self.
_row_row]
1214 context.remove_step(self.
_row_row)
1217 if hasattr(self,
'_undo_command'):
1225 row = self.
_row_row
if self.
_row_row != -1
else len(context.steps)
1226 context.add_step(row)
1228 assert not(any(i
for i
in self.
_step_step(context).as_list()
if i)), \
1229 'Should have an empty row after add instead %r' % \
1230 self.
_step_step(context).as_list()
1240 self.
_step_step(context).comment()
1250 self.
_step_step(context).uncomment()
1263 return [self.
_rows_rows]
1267 def non_empty_from_left(line):
1269 steps = context.steps[line].as_list()
1271 while index < len(steps)
and steps[index] ==
'':
1273 if index == len(steps):
1281 number_of_steps_before = len(context.steps)
1286 for row
in self.
_rows_rows:
1288 index = non_empty_from_left(row)
1289 prev_cell_row = row - 1
if row > 0
else 0
1290 prev_cell = non_empty_from_left(prev_cell_row)
1291 pre_prev_row = row - 2
if row > 1
else 0
1292 pre_prev_col = non_empty_from_left(pre_prev_row)
1293 next_cell_row = row + 1
if row + 1 < number_of_steps_before
else row
1294 next_row_col = non_empty_from_left(next_cell_row)
1295 add_indent = (pre_prev_col > index
and context.steps[row]._step.cells[index] !=
'END')
or (index < pre_prev_col
and context.steps[pre_prev_row]._step.cells[pre_prev_col] ==
'FOR')
1296 del_indent = (prev_cell > index)
and (context.steps[row]._step.cells[index] ==
'END')
1297 del_prev_indent = (prev_cell < index)
and (context.steps[prev_cell_row]._step.cells[prev_cell] ==
'FOR')
1299 if context.steps[row].as_list()[index] ==
'FOR':
1300 new_next_indent = (prev_cell == index
and (context.steps[prev_cell_row]._step.cells[prev_cell] !=
'END' or pre_prev_col > index)) \
1301 or (context.steps[next_cell_row]._step.cells[index] !=
'END' and next_row_col < index)
1303 new_next_indent = (pre_prev_col > next_row_col
and context.steps[row]._step.cells[index] !=
'END')
and \
1305 keep_indent = (context.steps[row]._step.cells[index] ==
'FOR'
1306 and context.steps[prev_cell_row]._step.cells[prev_cell] ==
'FOR' and next_row_col > index
1307 )
or (prev_cell > index
and context.steps[row]._step.cells[index] ==
'END' and context.steps[prev_cell_row]._step.cells[prev_cell] !=
'END')
1309 print(f"DEBUG: MoveRowsUp loop row({row}) index({index}) {context.steps[row].as_list()[index]} "
1310 f"prev_cell({prev_cell}) {context.steps[prev_cell_row].as_list()[prev_cell]}\n"
1311 f"next_row({next_row_col}) {context.steps[next_cell_row].as_list()[next_row_col]}\n"
1312 f"pre_prev_col({pre_prev_col}) {context.steps[pre_prev_row].as_list()[pre_prev_col]}\n"
1313 f"add_indent({add_indent}) new_next_indent({new_next_indent}) keep_indent({keep_indent})\n"
1314 f"del_indent({del_indent}) del_prev_indent({del_prev_indent})\n")
1315 print(f"DEBUG: MoveRowsUp before move: next_+1= {context.steps[next_cell_row].as_list()}")
1317 context.move_step_up(row)
1323 new_index = non_empty_from_left(prev_cell_row)
1324 if add_indent
and new_index > index + 1:
1326 context.steps[prev_cell_row].shift_left(0, delete=
True)
1329 prev_cell = non_empty_from_left(prev_cell_row)
1331 if keep_indent
and new_index > index:
1333 for _
in range(index, new_index):
1334 context.steps[prev_cell_row].shift_left(0, delete=
True)
1335 if add_indent
and (context.steps[prev_cell_row]._step.cells[prev_cell] !=
'FOR' and context.steps[row]._step.cells[index] !=
'END'):
1337 context.steps[row].shift_right(0)
1339 if new_next_indent
and not keep_indent
and not del_indent:
1342 context.steps[row].shift_right(0)
1343 if add_indent
and new_next_indent
and context.steps[prev_cell_row]._step.cells[pre_prev_col] !=
'FOR':
1345 context.steps[prev_cell_row].shift_right(0)
1346 if del_indent
or (keep_indent
and context.steps[prev_cell_row]._step.cells[prev_cell] ==
'END'):
1348 context.steps[row].shift_left(0, delete=
True)
1351 context.steps[prev_cell_row].shift_left(0, delete=
True)
1352 if keep_indent
and prev_cell > index
and len(context.steps[prev_cell_row]._step.cells) > prev_cell
and context.steps[prev_cell_row]._step.cells[prev_cell] ==
'END':
1354 context.steps[prev_cell_row].shift_left(0, delete=
True)
1358 assert len(context.steps) == number_of_steps_before
1362 _last_row = property
1365 return self.
_rows_rows[-1]
1368 _first_row = property
1371 return self.
_rows_rows[0]
1384 return [self.
_rows_rows]
1389 number_of_steps_before = len(context.steps)
1390 for row
in reversed(self.
_rows_rows):
1391 moving_start = context.steps[row]._first_non_empty_cell()
1392 existing_start = context.steps[row+1]._first_non_empty_cell()
1393 keep_indent = moving_start == existing_start
1394 decrease_indent = moving_start > existing_start
1396 increase_indent = moving_start < existing_start
or (context.steps[row].as_list()[moving_start] ==
'END' and context.steps[row+1].as_list()[existing_start] !=
'FOR')
1397 prev_decrease_indent = context.steps[row].as_list()[moving_start] ==
'FOR' and increase_indent
1398 if prev_decrease_indent:
1399 increase_indent =
False
1400 if context.steps[row+1].as_list()[existing_start] ==
'FOR' and context.steps[row].as_list()[moving_start] !=
'END' and keep_indent:
1401 increase_indent =
True
1403 if context.steps[row + 1].as_list()[existing_start] ==
'END' and not keep_indent\
1404 or (
not increase_indent
and context.steps[row].as_list()[moving_start] ==
'FOR'):
1405 decrease_indent =
True
1406 context.move_step_down(row)
1408 new_existing_start = context.steps[row]._first_non_empty_cell()
1411 context.steps[row+1].shift_left(moving_start, )
1412 if increase_indent
and not keep_indent:
1413 context.steps[row+1].shift_right(0)
1414 if increase_indent
and keep_indent:
1415 context.steps[row].shift_right(0)
1416 if prev_decrease_indent
and new_existing_start > moving_start:
1418 context.steps[row].shift_left(moving_start)
1419 assert len(context.steps) == number_of_steps_before
1423 _last_row = property
1426 return self.
_rows_rows[-1]
1439 undos = [undo
for _, undo
in executions]
1442 return [result
for result, _
in executions]
1451 return [(cmd._execute(context), cmd._get_undo_command())
1462 for changed
in CompositeCommand._execute(self, context))
1471 return [(cmd.change_steps(context), cmd._get_undo_command())
1477 for r
in reversed(sorted(rows))])
1482 first_row = sorted(rows)[0]
1495 row_s, col_s = top_left
1496 row_e, col_e = bottom_right
1499 for row
in range(row_s, row_e + 1)
1500 for col
in range(col_s, col_e + 1)])
1504 row_s, col_s = top_left
1507 for row
in range(len(content))
1508 for col
in range(len(content[row]))])
1514 AddRows([row+i
for i
in range(len(content))]),
1520 for row, col
in selection:
1528 for row, col
in selection:
1535 row_s, col_s = top_left
1536 row_e, col_e = bottom_right
1539 for row
in range(row_s, row_e + 1)
1540 for col
in range(col_s, col_e + 1)])
1544 row_s, col_s = top_left
1545 row_e, col_e = bottom_right
1548 for row
in range(row_s, row_e + 1)
1549 for _
in range(col_s, col_e + 1)])
def _execute(self, context)
def __init__(self, new_kw_name, args=None)
def __init__(self, values, comment)
def execute(self, context)
def execute(self, context)
def __init__(self, values, comment)
def _change_value(self, context)
def _add_data_file(self, context)
def execute(self, context)
def __init__(self, new_test_name)
def _add_data_file(self, context)
def _execute(self, context)
def __init__(self, name, value, comment)
def __init__(self, values, comment)
def execute(self, context)
def __init__(self, row, col, value, insert=False)
def change_steps(self, context)
Return True if steps changed, False otherwise.
def _escape_newlines(item)
def _validate_postcondition(self, context)
def _create_value_list(self, old_values)
def __init__(self, tag, value)
def _create_value(self, old_values)
def execute(self, context)
def execute(self, context)
def _create_undo_command(self, undos)
def __init__(self, *commands)
def _executions(self, context)
def _execute(self, context)
def execute(self, context)
def __init__(self, new_name)
def execute(self, context)
def execute(self, context)
def execute(self, context)
def change_steps(self, context)
Return True if steps changed, False otherwise.
def __init__(self, row, col)
def execute(self, context)
def execute(self, context)
def execute(self, context)
def execute(self, context)
def execute(self, context)
def _change_value(self, context)
def execute(self, directory_controller)
def execute(self, context)
def __init__(self, new_kw_name, new_kw_args, step_range)
def execute(self, context)
def _items_from_datafile_should_be_checked(self, datafile)
def _items_from_test(test)
def _yield_for_other_threads()
def _contains_item(self, item)
def _items_from(self, context)
def _items_from_datafile(self, df)
def _items_from_keyword(self, kw)
def _find_occurrences_in(self, items)
def _create_regexp(self, keyword_name)
def _find_keyword_source(self, datafile_controller)
def __init__(self, keyword_name, keyword_info=None)
def _is_local_variable(name, context)
def _is_imported_variable(self, name, context)
def _items_from_controller(self, ctrl)
def _items_from(self, context)
def _get_all_where_used(context)
def _items_from_datafile_should_be_checked(self, datafile)
def _is_file_variable(name, context)
def _contains_item(self, item)
def _get_source_of_imported_var(self, name, context)
def _get_all_imported(context)
def _is_builtin_variable(name)
def _items_from_keyword(self, kw)
def _items_from_datafile(self, df)
def execute(self, excluded_controller)
def __init__(self, row, col)
def change_steps(self, context, delete=False)
def execute(self, context)
def change_steps(self, context)
Return True if steps changed, False otherwise.
def change_steps(self, context)
Return True if steps changed, False otherwise.
def execute(self, context)
def __init__(self, destination)
def execute(self, context)
def __getattr__(self, name)
def replace_keyword(self, new_name)
def __init__(self, item, value)
def _get_replace_values(self, new_name)
def _in_variable_table(self)
def notify_value_changed(self)
def execute(self, context)
def execute(self, context)
def __init__(self, user_script)
def _execute(self, context)
def execute(self, context)
def _execute(self, context)
def execute(self, context)
def _execute(self, context)
def __init__(self, var_controller)
def execute(self, context)
def __init__(self, new_basename)
def __init__(self, original_name, new_name, observer, keyword_info=None)
def _get_gherkin(self, original_name)
def _execute(self, context)
def _replace_keywords_in(self, occurrences)
def _notify_values_changed(self, occurrences)
def _find_occurrences(self, context)
def _check_gherkin(self, new_name, original_name)
def __init__(self, new_basename, get_should_modify_imports)
def execute(self, context)
def __init__(self, new_name)
def _execute(self, context)
def __init__(self, index_difference)
def _execute(self, context)
def __init__(self, index_difference)
def _execute(self, context)
def __init__(self, index_difference)
def _execute(self, context)
def execute(self, context)
def __init__(self, reformat=False)
def __init__(self, reformat=False)
def execute(self, context)
def execute(self, context)
def __init__(self, datafile)
def execute(self, context)
def __init__(self, values, comment)
def _execute(self, context)
def _execute(self, context)
def _execute(self, context)
def _executions(self, context)
def change_steps(self, context)
Return True if steps changed, False otherwise.
def _create_undo_command(self, undos)
def __init__(self, *commands)
def execute(self, context)
def execute(self, context)
def __init__(self, new_name)
def execute(self, context)
def __init__(self, new_name, new_value, new_comment)
def execute(self, context)
def execute(self, context)
def _add_data_file(self, context)
def execute(self, context)
def execute(self, context)
def _execute_without_redo_clear(self, context)
def _get_undo_command(self)
def change_steps(self, context)
Return True if steps changed, False otherwise.
def __init__(self, row)
Command that will operate on a given logical row of test/user keyword.
def change_steps(self, context)
Return True if steps changed, False otherwise.
def _execute(self, context)
Sent after test suite or resource file is renamed.
Sent when user selects Save All from File menu or via shortcut.
Sent after the file has been actually saved to disk.
Sent when user selects Save from File menu or via shortcut.
Sent when a resource should be selected.
def ExtractScalar(name, value, comment, cell)
def InsertArea(top_left, content)
def ClearArea(top_left, bottom_right)
def PasteArea(top_left, content)
def _rows_from_selection(selection)
def AddKeywordFromCells(cells)
def InsertCells(top_left, bottom_right)
def ExtractList(name, value, comment, cells)
def DeleteCells(top_left, bottom_right)
def _cols_from_selection(selection)