Coverage for src/robotide/recentfiles/recentfiles.py: 55%

108 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 os.path 

18 

19import wx 

20 

21from ..pluginapi import Plugin 

22from ..action import ActionInfo, SeparatorInfo 

23from ..publish import RideOpenSuite, RideFileNameChanged 

24from ..publish.messages import RideNewProject, RideSaved 

25 

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

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

28 

29 

30def normalize_path(path): 

31 if os.path.basename(path).startswith('__init__.'): 31 ↛ 32line 31 didn't jump to line 32 because the condition on line 31 was never true

32 return os.path.dirname(path) 

33 return os.path.abspath(path) 

34 

35 

36def remove_non_existing_paths(paths): 

37 return [f for f in paths if os.path.exists(f)] 

38 

39 

40class RecentFilesPlugin(Plugin): 

41 __doc__ = _("""Add recently opened files to the file menu.""") 

42 

43 def __init__(self, application=None): 

44 settings = {'recent_files': [], 'max_number_of_files': 4} 

45 Plugin.__init__(self, application, default_settings=settings) 

46 self.recent_files = remove_non_existing_paths(self.recent_files) 

47 self._new_project_path = None 

48 

49 def enable(self): 

50 self._save_currently_loaded_suite() 

51 self._add_recent_files_to_menu() 

52 self._new_project_path = None 

53 self.subscribe(self.on_suite_opened, RideOpenSuite) 

54 self.subscribe(self.on_file_name_changed, RideFileNameChanged) 

55 self.subscribe(self.on_new_project_opened, RideNewProject) 

56 self.subscribe(self.on_saved, RideSaved) 

57 

58 def disable(self): 

59 self.unregister_actions() 

60 self.unsubscribe_all() 

61 

62 def on_suite_opened(self, message): 

63 # Update menu with CallAfter to ensure ongoing menu selection 

64 # handling has finished before menu is changed 

65 wx.CallAfter(self._add_to_recent_files, message.path) 

66 self._new_project_path = None 

67 

68 def on_file_name_changed(self, message): 

69 self._new_project_path = None 

70 if not message.old_filename: 

71 return 

72 old_filename = normalize_path(message.old_filename) 

73 new_filename = normalize_path(message.datafile.filename) 

74 if old_filename not in self.recent_files: 

75 return 

76 index = self.recent_files.index(old_filename) 

77 self.recent_files[index] = new_filename 

78 self._save_settings_and_update_file_menu() 

79 

80 def on_new_project_opened(self, message): 

81 self._new_project_path = message.path 

82 

83 def on_saved(self, message): 

84 _ = message 

85 if self._new_project_path is not None: 

86 wx.CallAfter(self._add_to_recent_files, self._new_project_path) 

87 self._new_project_path = None 

88 

89 def _get_file_menu(self): 

90 menubar = self.get_menu_bar() 

91 pos = menubar.FindMenu('File') 

92 file_menu = menubar.GetMenu(pos) 

93 return file_menu 

94 

95 def _save_currently_loaded_suite(self): 

96 model = self.model 

97 if model and model.suite: 97 ↛ 98line 97 didn't jump to line 98 because the condition on line 97 was never true

98 self._add_to_recent_files(model.suite.source) 

99 

100 def _add_to_recent_files(self, path): 

101 if not path: 

102 return 

103 path = normalize_path(path) 

104 if path in self.recent_files: 

105 self.recent_files.remove(path) 

106 self.recent_files.insert(0, path) 

107 self._save_settings_and_update_file_menu() 

108 

109 def _save_settings_and_update_file_menu(self): 

110 self.recent_files = remove_non_existing_paths(self.recent_files) 

111 self.recent_files = self.recent_files[:self.max_number_of_files] 

112 self.save_setting('recent_files', self.recent_files) 

113 self.unregister_actions() 

114 self._add_recent_files_to_menu() 

115 

116 def _add_recent_files_to_menu(self): 

117 if not self.recent_files: 117 ↛ 118line 117 didn't jump to line 118 because the condition on line 117 was never true

118 action = ActionInfo(_('File'), _('No recent files')) 

119 action.set_menu_position(before=_('Exit')) 

120 self.register_action(action) 

121 else: 

122 for n, path in enumerate(self.recent_files): 

123 self._add_file_to_menu(path, n) 

124 sep = SeparatorInfo(_('File')) 

125 sep.set_menu_position(before=_('Exit')) 

126 self.register_action(sep) 

127 

128 def _add_file_to_menu(self, path, n): 

129 entry = RecentFileEntry(n+1, path, self) 

130 self.register_action(entry.get_action_info()) 

131 

132 

133class RecentFileEntry(object): 

134 

135 def __init__(self, index, file, plugin): 

136 self.file = file 

137 self.index = index 

138 self.path = normalize_path(self.file) 

139 self.filename = os.path.basename(file) 

140 self.plugin = plugin 

141 self.label = '&%s: %s' % (index, self.filename) 

142 self.doc = _('Open %s') % self.path 

143 

144 def on_open_recent(self, event): 

145 __ = event 

146 if not self.plugin.frame.check_unsaved_modifications(): 

147 return 

148 self.plugin.open_suite(self.path) 

149 

150 def get_action_info(self): 

151 action_info = ActionInfo(_('File'), self.label, self.on_open_recent, 

152 doc=self.doc) 

153 action_info.set_menu_position(before=_('Exit')) 

154 return action_info