41 |
41 |
42 class ToolGenerateHashPlugin(QObject): |
42 class ToolGenerateHashPlugin(QObject): |
43 """ |
43 """ |
44 Class implementing the 'Generate Hash' tool plug-in. |
44 Class implementing the 'Generate Hash' tool plug-in. |
45 """ |
45 """ |
|
46 |
46 Hashes = [ |
47 Hashes = [ |
47 h for h in ("md5", "sha1", "sha224", "sha256", "sha384", "sha512", |
48 h |
48 "sha3_224", "sha3_256", "sha3_384", "sha3_512") |
49 for h in ( |
|
50 "md5", |
|
51 "sha1", |
|
52 "sha224", |
|
53 "sha256", |
|
54 "sha384", |
|
55 "sha512", |
|
56 "sha3_224", |
|
57 "sha3_256", |
|
58 "sha3_384", |
|
59 "sha3_512", |
|
60 ) |
49 if h in hashlib.algorithms_guaranteed |
61 if h in hashlib.algorithms_guaranteed |
50 ] |
62 ] |
51 |
63 |
52 def __init__(self, ui): |
64 def __init__(self, ui): |
53 """ |
65 """ |
54 Constructor |
66 Constructor |
55 |
67 |
56 @param ui reference to the user interface object (UI.UserInterface) |
68 @param ui reference to the user interface object (UI.UserInterface) |
57 """ |
69 """ |
58 super().__init__(ui) |
70 super().__init__(ui) |
59 self.__ui = ui |
71 self.__ui = ui |
60 |
72 |
61 self.__translator = None |
73 self.__translator = None |
62 self.__loadTranslator() |
74 self.__loadTranslator() |
63 |
75 |
64 self.__initMenus() |
76 self.__initMenus() |
65 |
77 |
66 self.__editors = {} |
78 self.__editors = {} |
67 self.__mainActions = [] |
79 self.__mainActions = [] |
68 |
80 |
69 def activate(self): |
81 def activate(self): |
70 """ |
82 """ |
71 Public method to activate this plugin. |
83 Public method to activate this plugin. |
72 |
84 |
73 @return tuple of None and activation status |
85 @return tuple of None and activation status |
74 @rtype bool |
86 @rtype bool |
75 """ |
87 """ |
76 global error |
88 global error |
77 error = "" # clear previous error |
89 error = "" # clear previous error |
78 |
90 |
79 self.__ui.showMenu.connect(self.__populateMenu) |
91 self.__ui.showMenu.connect(self.__populateMenu) |
80 |
92 |
81 menu = self.__ui.getMenu("plugin_tools") |
93 menu = self.__ui.getMenu("plugin_tools") |
82 if menu is not None: |
94 if menu is not None: |
83 if not menu.isEmpty(): |
95 if not menu.isEmpty(): |
84 act = menu.addSeparator() |
96 act = menu.addSeparator() |
85 self.__mainActions.append(act) |
97 self.__mainActions.append(act) |
86 act = menu.addMenu(self.__fileMenu) |
98 act = menu.addMenu(self.__fileMenu) |
87 self.__mainActions.append(act) |
99 self.__mainActions.append(act) |
88 act = menu.addMenu(self.__dirMenu) |
100 act = menu.addMenu(self.__dirMenu) |
89 self.__mainActions.append(act) |
101 self.__mainActions.append(act) |
90 |
102 |
91 ericApp().getObject("ViewManager").editorOpenedEd.connect( |
103 ericApp().getObject("ViewManager").editorOpenedEd.connect(self.__editorOpened) |
92 self.__editorOpened) |
104 ericApp().getObject("ViewManager").editorClosedEd.connect(self.__editorClosed) |
93 ericApp().getObject("ViewManager").editorClosedEd.connect( |
105 |
94 self.__editorClosed) |
|
95 |
|
96 for editor in ericApp().getObject("ViewManager").getOpenEditors(): |
106 for editor in ericApp().getObject("ViewManager").getOpenEditors(): |
97 self.__editorOpened(editor) |
107 self.__editorOpened(editor) |
98 |
108 |
99 return None, True |
109 return None, True |
100 |
110 |
101 def deactivate(self): |
111 def deactivate(self): |
102 """ |
112 """ |
103 Public method to deactivate this plugin. |
113 Public method to deactivate this plugin. |
104 """ |
114 """ |
105 self.__ui.showMenu.disconnect(self.__populateMenu) |
115 self.__ui.showMenu.disconnect(self.__populateMenu) |
106 |
116 |
107 menu = self.__ui.getMenu("plugin_tools") |
117 menu = self.__ui.getMenu("plugin_tools") |
108 if menu is not None: |
118 if menu is not None: |
109 for act in self.__mainActions: |
119 for act in self.__mainActions: |
110 menu.removeAction(act) |
120 menu.removeAction(act) |
111 self.__mainActions = [] |
121 self.__mainActions = [] |
112 |
122 |
113 ericApp().getObject("ViewManager").editorOpenedEd.disconnect( |
123 ericApp().getObject("ViewManager").editorOpenedEd.disconnect( |
114 self.__editorOpened) |
124 self.__editorOpened |
|
125 ) |
115 ericApp().getObject("ViewManager").editorClosedEd.disconnect( |
126 ericApp().getObject("ViewManager").editorClosedEd.disconnect( |
116 self.__editorClosed) |
127 self.__editorClosed |
117 |
128 ) |
|
129 |
118 for editor, acts in self.__editors.items(): |
130 for editor, acts in self.__editors.items(): |
119 editor.showMenu.disconnect(self.__editorShowMenu) |
131 editor.showMenu.disconnect(self.__editorShowMenu) |
120 menu = editor.getMenu("Tools") |
132 menu = editor.getMenu("Tools") |
121 if menu is not None: |
133 if menu is not None: |
122 for act in acts: |
134 for act in acts: |
123 menu.removeAction(act) |
135 menu.removeAction(act) |
124 self.__editors = {} |
136 self.__editors = {} |
125 |
137 |
126 def __loadTranslator(self): |
138 def __loadTranslator(self): |
127 """ |
139 """ |
128 Private method to load the translation file. |
140 Private method to load the translation file. |
129 """ |
141 """ |
130 if self.__ui is not None: |
142 if self.__ui is not None: |
131 loc = self.__ui.getLocale() |
143 loc = self.__ui.getLocale() |
132 if loc and loc != "C": |
144 if loc and loc != "C": |
133 locale_dir = os.path.join( |
145 locale_dir = os.path.join( |
134 os.path.dirname(__file__), "ToolGenerateHash", "i18n") |
146 os.path.dirname(__file__), "ToolGenerateHash", "i18n" |
|
147 ) |
135 translation = "generatehash_{0}".format(loc) |
148 translation = "generatehash_{0}".format(loc) |
136 translator = QTranslator(None) |
149 translator = QTranslator(None) |
137 loaded = translator.load(translation, locale_dir) |
150 loaded = translator.load(translation, locale_dir) |
138 if loaded: |
151 if loaded: |
139 self.__translator = translator |
152 self.__translator = translator |
140 ericApp().installTranslator(self.__translator) |
153 ericApp().installTranslator(self.__translator) |
141 else: |
154 else: |
142 print("Warning: translation file '{0}' could not be" |
155 print( |
143 " loaded.".format(translation)) |
156 "Warning: translation file '{0}' could not be" |
|
157 " loaded.".format(translation) |
|
158 ) |
144 print("Using default.") |
159 print("Using default.") |
145 |
160 |
146 def __initMenus(self): |
161 def __initMenus(self): |
147 """ |
162 """ |
148 Private method to initialize the hash generation menus. |
163 Private method to initialize the hash generation menus. |
149 """ |
164 """ |
150 self.__fileMenu = QMenu(self.tr("Generate File Hash")) |
165 self.__fileMenu = QMenu(self.tr("Generate File Hash")) |
151 for hashName in self.Hashes: |
166 for hashName in self.Hashes: |
152 self.__fileMenu.addAction( |
167 self.__fileMenu.addAction(hashName.upper().replace("_", ":")).setData( |
153 hashName.upper().replace("_", ":")).setData(hashName) |
168 hashName |
|
169 ) |
154 self.__fileMenu.setEnabled(False) |
170 self.__fileMenu.setEnabled(False) |
155 self.__fileMenu.triggered.connect(self.__hashFile) |
171 self.__fileMenu.triggered.connect(self.__hashFile) |
156 |
172 |
157 self.__dirMenu = QMenu(self.tr("Generate Directory Hash")) |
173 self.__dirMenu = QMenu(self.tr("Generate Directory Hash")) |
158 for hashName in self.Hashes: |
174 for hashName in self.Hashes: |
159 self.__dirMenu.addAction( |
175 self.__dirMenu.addAction(hashName.upper().replace("_", ":")).setData( |
160 hashName.upper().replace("_", ":")).setData(hashName) |
176 hashName |
|
177 ) |
161 self.__dirMenu.setEnabled(False) |
178 self.__dirMenu.setEnabled(False) |
162 self.__dirMenu.triggered.connect(self.__hashDirectory) |
179 self.__dirMenu.triggered.connect(self.__hashDirectory) |
163 |
180 |
164 def __populateMenu(self, name, menu): |
181 def __populateMenu(self, name, menu): |
165 """ |
182 """ |
166 Private slot to populate the tools menu with our entries. |
183 Private slot to populate the tools menu with our entries. |
167 |
184 |
168 @param name name of the menu |
185 @param name name of the menu |
169 @type str |
186 @type str |
170 @param menu reference to the menu to be populated |
187 @param menu reference to the menu to be populated |
171 @type QMenu |
188 @type QMenu |
172 """ |
189 """ |
173 if name not in ["Tools", "PluginTools"]: |
190 if name not in ["Tools", "PluginTools"]: |
174 return |
191 return |
175 |
192 |
176 editor = ericApp().getObject("ViewManager").activeWindow() |
193 editor = ericApp().getObject("ViewManager").activeWindow() |
177 |
194 |
178 if name == "Tools": |
195 if name == "Tools": |
179 if not menu.isEmpty(): |
196 if not menu.isEmpty(): |
180 menu.addSeparator() |
197 menu.addSeparator() |
181 |
198 |
182 act = menu.addMenu(self.__fileMenu) |
199 act = menu.addMenu(self.__fileMenu) |
183 act.setEnabled(editor is not None) |
200 act.setEnabled(editor is not None) |
184 act = menu.addMenu(self.__dirMenu) |
201 act = menu.addMenu(self.__dirMenu) |
185 act.setEnabled(editor is not None) |
202 act.setEnabled(editor is not None) |
186 elif name == "PluginTools" and self.__mainActions: |
203 elif name == "PluginTools" and self.__mainActions: |
187 self.__mainActions[-2].setEnabled(editor is not None) |
204 self.__mainActions[-2].setEnabled(editor is not None) |
188 self.__mainActions[-1].setEnabled(editor is not None) |
205 self.__mainActions[-1].setEnabled(editor is not None) |
189 |
206 |
190 def __editorOpened(self, editor): |
207 def __editorOpened(self, editor): |
191 """ |
208 """ |
192 Private slot called, when a new editor was opened. |
209 Private slot called, when a new editor was opened. |
193 |
210 |
194 @param editor reference to the new editor |
211 @param editor reference to the new editor |
195 @type Editor |
212 @type Editor |
196 """ |
213 """ |
197 menu = editor.getMenu("Tools") |
214 menu = editor.getMenu("Tools") |
198 if menu is not None: |
215 if menu is not None: |
203 act = menu.addMenu(self.__fileMenu) |
220 act = menu.addMenu(self.__fileMenu) |
204 self.__editors[editor].append(act) |
221 self.__editors[editor].append(act) |
205 act = menu.addMenu(self.__dirMenu) |
222 act = menu.addMenu(self.__dirMenu) |
206 self.__editors[editor].append(act) |
223 self.__editors[editor].append(act) |
207 editor.showMenu.connect(self.__editorShowMenu) |
224 editor.showMenu.connect(self.__editorShowMenu) |
208 |
225 |
209 self.__fileMenu.setEnabled(True) |
226 self.__fileMenu.setEnabled(True) |
210 self.__dirMenu.setEnabled(True) |
227 self.__dirMenu.setEnabled(True) |
211 |
228 |
212 def __editorClosed(self, editor): |
229 def __editorClosed(self, editor): |
213 """ |
230 """ |
214 Private slot called, when an editor was closed. |
231 Private slot called, when an editor was closed. |
215 |
232 |
216 @param editor reference to the editor |
233 @param editor reference to the editor |
217 @type Editor |
234 @type Editor |
218 """ |
235 """ |
219 with contextlib.suppress(KeyError): |
236 with contextlib.suppress(KeyError): |
220 del self.__editors[editor] |
237 del self.__editors[editor] |
221 if not self.__editors: |
238 if not self.__editors: |
222 self.__fileMenu.setEnabled(False) |
239 self.__fileMenu.setEnabled(False) |
223 self.__dirMenu.setEnabled(False) |
240 self.__dirMenu.setEnabled(False) |
224 |
241 |
225 def __editorShowMenu(self, menuName, menu, editor): |
242 def __editorShowMenu(self, menuName, menu, editor): |
226 """ |
243 """ |
227 Private slot called, when the the editor context menu or a submenu is |
244 Private slot called, when the the editor context menu or a submenu is |
228 about to be shown. |
245 about to be shown. |
229 |
246 |
230 @param menuName name of the menu to be shown |
247 @param menuName name of the menu to be shown |
231 @type str |
248 @type str |
232 @param menu reference to the menu |
249 @param menu reference to the menu |
233 @type QMenu |
250 @type QMenu |
234 @param editor reference to the editor |
251 @param editor reference to the editor |
235 @type Editor |
252 @type Editor |
236 """ |
253 """ |
237 if ( |
254 if menuName == "Tools" and self.__fileMenu.menuAction() not in menu.actions(): |
238 menuName == "Tools" and |
|
239 self.__fileMenu.menuAction() not in menu.actions() |
|
240 ): |
|
241 # Re-add our menu |
255 # Re-add our menu |
242 self.__editors[editor] = [] |
256 self.__editors[editor] = [] |
243 if not menu.isEmpty(): |
257 if not menu.isEmpty(): |
244 act = menu.addSeparator() |
258 act = menu.addSeparator() |
245 self.__editors[editor].append(act) |
259 self.__editors[editor].append(act) |
246 act = menu.addMenu(self.__fileMenu) |
260 act = menu.addMenu(self.__fileMenu) |
247 self.__editors[editor].append(act) |
261 self.__editors[editor].append(act) |
248 act = menu.addMenu(self.__dirMenu) |
262 act = menu.addMenu(self.__dirMenu) |
249 self.__editors[editor].append(act) |
263 self.__editors[editor].append(act) |
250 |
264 |
251 self.__fileMenu.setEnabled(True) |
265 self.__fileMenu.setEnabled(True) |
252 self.__dirMenu.setEnabled(True) |
266 self.__dirMenu.setEnabled(True) |
253 |
267 |
254 def __insertHash(self, hashStr): |
268 def __insertHash(self, hashStr): |
255 """ |
269 """ |
256 Private method to insert the generated hash string. |
270 Private method to insert the generated hash string. |
257 |
271 |
258 @param hashStr hash string |
272 @param hashStr hash string |
259 @type str |
273 @type str |
260 """ |
274 """ |
261 if hashStr: |
275 if hashStr: |
262 editor = ericApp().getObject("ViewManager").activeWindow() |
276 editor = ericApp().getObject("ViewManager").activeWindow() |
263 line, index = editor.getCursorPosition() |
277 line, index = editor.getCursorPosition() |
264 # It should be done this way to allow undo |
278 # It should be done this way to allow undo |
265 editor.beginUndoAction() |
279 editor.beginUndoAction() |
266 editor.insertAt(hashStr, line, index) |
280 editor.insertAt(hashStr, line, index) |
267 editor.endUndoAction() |
281 editor.endUndoAction() |
268 |
282 |
269 @pyqtSlot(QAction) |
283 @pyqtSlot(QAction) |
270 def __hashFile(self, act): |
284 def __hashFile(self, act): |
271 """ |
285 """ |
272 Private slot to generate the hash for a file. |
286 Private slot to generate the hash for a file. |
273 |
287 |
274 @param act reference to the action that was triggered |
288 @param act reference to the action that was triggered |
275 @type QAction |
289 @type QAction |
276 """ |
290 """ |
277 if act is None: |
291 if act is None: |
278 return |
292 return |
279 |
293 |
280 name = EricFileDialog.getOpenFileName( |
294 name = EricFileDialog.getOpenFileName(self.__ui, self.tr("Generate File Hash")) |
281 self.__ui, |
|
282 self.tr("Generate File Hash")) |
|
283 if name: |
295 if name: |
284 try: |
296 try: |
285 with open(name, "rb") as f: |
297 with open(name, "rb") as f: |
286 hashStr = hashlib.new(act.data(), f.read()).hexdigest() |
298 hashStr = hashlib.new(act.data(), f.read()).hexdigest() |
287 except OSError as err: |
299 except OSError as err: |
288 EricMessageBox.critical( |
300 EricMessageBox.critical( |
289 self.__ui, |
301 self.__ui, |
290 self.tr("Generate File Hash"), |
302 self.tr("Generate File Hash"), |
291 self.tr("""<p>The hash for <b>{0}</b> could not""" |
303 self.tr( |
292 """ be generated.</p><p>Reason: {1}</p>""") |
304 """<p>The hash for <b>{0}</b> could not""" |
293 .format(name, str(err)) |
305 """ be generated.</p><p>Reason: {1}</p>""" |
|
306 ).format(name, str(err)), |
294 ) |
307 ) |
295 return |
308 return |
296 |
309 |
297 self.__insertHash(hashStr) |
310 self.__insertHash(hashStr) |
298 |
311 |
299 @pyqtSlot(QAction) |
312 @pyqtSlot(QAction) |
300 def __hashDirectory(self, act): |
313 def __hashDirectory(self, act): |
301 """ |
314 """ |
302 Private slot to generate the hash for a directory. |
315 Private slot to generate the hash for a directory. |
303 |
316 |
304 @param act reference to the action that was triggered |
317 @param act reference to the action that was triggered |
305 @type QAction |
318 @type QAction |
306 """ |
319 """ |
307 if act is None: |
320 if act is None: |
308 return |
321 return |
309 |
322 |
310 folder = EricFileDialog.getExistingDirectory( |
323 folder = EricFileDialog.getExistingDirectory( |
311 self.__ui, |
324 self.__ui, self.tr("Generate Directory Hash"), "", EricFileDialog.Option(0) |
312 self.tr("Generate Directory Hash"), |
325 ) |
313 "", |
|
314 EricFileDialog.Option(0)) |
|
315 if folder and os.path.isdir(folder): |
326 if folder and os.path.isdir(folder): |
316 fails = 0 |
327 fails = 0 |
317 hashes = [] |
328 hashes = [] |
318 for name in os.listdir(folder): |
329 for name in os.listdir(folder): |
319 if ( |
330 if not name.startswith(".") and os.path.isfile( |
320 not name.startswith(".") and |
331 os.path.join(folder, name) |
321 os.path.isfile(os.path.join(folder, name)) |
|
322 ): |
332 ): |
323 try: |
333 try: |
324 with open(os.path.join(folder, name), "rb") as f: |
334 with open(os.path.join(folder, name), "rb") as f: |
325 hashStr = hashlib.new( |
335 hashStr = hashlib.new(act.data(), f.read()).hexdigest() |
326 act.data(), f.read()).hexdigest() |
|
327 hashes.append((name, hashStr)) |
336 hashes.append((name, hashStr)) |
328 except OSError: |
337 except OSError: |
329 fails += 1 |
338 fails += 1 |
330 if fails: |
339 if fails: |
331 EricMessageBox.critical( |
340 EricMessageBox.critical( |
332 self.__ui, |
341 self.__ui, |
333 self.tr("Generate Directory Hash"), |
342 self.tr("Generate Directory Hash"), |
334 self.tr("""<p>The hash for some files could not""" |
343 self.tr( |
335 """ be generated.</p>""") |
344 """<p>The hash for some files could not""" |
|
345 """ be generated.</p>""" |
|
346 ), |
336 ) |
347 ) |
337 else: |
348 else: |
338 editor = ericApp().getObject("ViewManager").activeWindow() |
349 editor = ericApp().getObject("ViewManager").activeWindow() |
339 line, index = editor.getCursorPosition() |
350 line, index = editor.getCursorPosition() |
340 indLevel = (editor.indentation(line) // |
351 indLevel = editor.indentation(line) // editor.indentationWidth() |
341 editor.indentationWidth()) |
|
342 if editor.indentationsUseTabs(): |
352 if editor.indentationsUseTabs(): |
343 indString = '\t' |
353 indString = "\t" |
344 else: |
354 else: |
345 indString = editor.indentationWidth() * ' ' |
355 indString = editor.indentationWidth() * " " |
346 indent = (indLevel + 1) * indString |
356 indent = (indLevel + 1) * indString |
347 code = ["["] |
357 code = ["["] |
348 for name, hashStr in hashes: |
358 for name, hashStr in hashes: |
349 code.append("{0}('{1}', '{2}'),".format( |
359 code.append("{0}('{1}', '{2}'),".format(indent, name, hashStr)) |
350 indent, name, hashStr)) |
|
351 code.append("{0}]".format(indLevel * indString)) |
360 code.append("{0}]".format(indLevel * indString)) |
352 |
361 |
353 self.__insertHash(os.linesep.join(code)) |
362 self.__insertHash(os.linesep.join(code)) |
|
363 |
354 |
364 |
355 # |
365 # |
356 # eflag: noqa = M801 |
366 # eflag: noqa = M801 |