eric7/HexEdit/HexEditMainWindow.py

branch
eric7
changeset 8312
800c432b34c8
parent 8265
0090cfa83159
child 8314
e3642a6a1e71
equal deleted inserted replaced
8311:4e8b98454baa 8312:800c432b34c8
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2021 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the hex editor main window.
8 """
9
10 import os
11 import contextlib
12
13 from PyQt5.QtCore import (
14 pyqtSignal, pyqtSlot, QFile, QFileInfo, QSize, QCoreApplication, QLocale
15 )
16 from PyQt5.QtGui import QKeySequence
17 from PyQt5.QtWidgets import (
18 QWhatsThis, QLabel, QWidget, QVBoxLayout, QDialog, QAction, QFrame, QMenu
19 )
20
21 from E5Gui.E5Action import E5Action
22 from E5Gui.E5MainWindow import E5MainWindow
23 from E5Gui import E5FileDialog, E5MessageBox
24 from E5Gui.E5ClickableLabel import E5ClickableLabel
25
26 from Globals import strGroup, recentNameHexFiles
27
28 from .HexEditWidget import HexEditWidget
29 from .HexEditSearchReplaceWidget import HexEditSearchReplaceWidget
30 from .HexEditGotoWidget import HexEditGotoWidget
31
32 import UI.PixmapCache
33 import UI.Config
34
35 import Preferences
36 import Utilities
37
38
39 class HexEditMainWindow(E5MainWindow):
40 """
41 Class implementing the web browser main window.
42
43 @signal editorClosed() emitted after the window was requested to close down
44 """
45 editorClosed = pyqtSignal()
46
47 windows = []
48
49 maxMenuFilePathLen = 75
50
51 def __init__(self, fileName="", parent=None, fromEric=False, project=None):
52 """
53 Constructor
54
55 @param fileName name of a file to load on startup (string)
56 @param parent parent widget of this window (QWidget)
57 @param fromEric flag indicating whether it was called from within
58 eric (boolean)
59 @param project reference to the project object (Project)
60 """
61 super().__init__(parent)
62 self.setObjectName("eric6_hex_editor")
63
64 self.__srHistory = {
65 "search": [],
66 # list of recent searches (tuple of format type index and
67 # search term)
68 "replace": [],
69 # list of recent replaces (tuple of format type index and
70 # replace term
71 }
72
73 self.__fromEric = fromEric
74 self.setWindowIcon(UI.PixmapCache.getIcon("hexEditor"))
75
76 if not self.__fromEric:
77 self.setStyle(Preferences.getUI("Style"),
78 Preferences.getUI("StyleSheet"))
79
80 self.__editor = HexEditWidget()
81 self.__searchWidget = HexEditSearchReplaceWidget(
82 self.__editor, self, False)
83 self.__replaceWidget = HexEditSearchReplaceWidget(
84 self.__editor, self, True)
85 self.__gotoWidget = HexEditGotoWidget(self.__editor)
86 cw = QWidget()
87 layout = QVBoxLayout(cw)
88 layout.setContentsMargins(1, 1, 1, 1)
89 layout.setSpacing(1)
90 layout.addWidget(self.__editor)
91 layout.addWidget(self.__searchWidget)
92 layout.addWidget(self.__gotoWidget)
93 cw.setLayout(layout)
94 layout.addWidget(self.__replaceWidget)
95 self.__searchWidget.hide()
96 self.__replaceWidget.hide()
97 self.__gotoWidget.hide()
98 self.setCentralWidget(cw)
99
100 g = Preferences.getGeometry("HexEditorGeometry")
101 if g.isEmpty():
102 s = QSize(600, 500)
103 self.resize(s)
104 else:
105 self.restoreGeometry(g)
106
107 self.__initActions()
108 self.__initMenus()
109 self.__initToolbars()
110 self.__createStatusBar()
111
112 self.__class__.windows.append(self)
113
114 state = Preferences.getHexEditor("HexEditorState")
115 self.restoreState(state)
116
117 self.__editor.currentAddressChanged.connect(self.__showAddress)
118 self.__editor.selectionAvailable.connect(self.__showSelectionInfo)
119 self.__editor.currentSizeChanged.connect(self.__showSize)
120 self.__editor.dataChanged.connect(self.__modificationChanged)
121 self.__editor.overwriteModeChanged.connect(self.__showEditMode)
122 self.__editor.readOnlyChanged.connect(self.__showReadOnlyMode)
123 self.__editor.readOnlyChanged.connect(self.__checkActions)
124
125 self.preferencesChanged()
126 self.__editor.setOverwriteMode(
127 Preferences.getHexEditor("OpenInOverwriteMode"))
128
129 self.__project = project
130 self.__lastOpenPath = ""
131 self.__lastSavePath = ""
132
133 self.__recent = []
134 self.__loadRecent()
135
136 self.__setCurrentFile("")
137 if fileName:
138 self.__loadHexFile(fileName)
139
140 self.__checkActions()
141
142 def __initActions(self):
143 """
144 Private method to define the user interface actions.
145 """
146 # list of all actions
147 self.__actions = []
148
149 self.__initFileActions()
150 self.__initEditActions()
151 self.__initHelpActions()
152 if not self.__fromEric:
153 self.__initConfigActions()
154
155 def __initFileActions(self):
156 """
157 Private method to define the file related user interface actions.
158 """
159 self.newWindowAct = E5Action(
160 self.tr('New Window'),
161 UI.PixmapCache.getIcon("newWindow"),
162 self.tr('New &Window'),
163 0, 0, self, 'hexEditor_file_new_window')
164 self.newWindowAct.setStatusTip(self.tr(
165 'Open a binary file for editing in a new hex editor window'))
166 self.newWindowAct.setWhatsThis(self.tr(
167 """<b>New Window</b>"""
168 """<p>This opens a binary file for editing in a new hex editor"""
169 """ window.</p>"""
170 ))
171 self.newWindowAct.triggered.connect(self.__openHexFileNewWindow)
172 self.__actions.append(self.newWindowAct)
173
174 # correct texts will be set later
175 self.openAct = E5Action(
176 self.tr('Open'),
177 UI.PixmapCache.getIcon("open"),
178 self.tr('&Open...'),
179 QKeySequence(self.tr("Ctrl+O", "File|Open")),
180 0, self, 'hexEditor_file_open')
181 self.openAct.triggered.connect(self.__openHexFile)
182 self.__actions.append(self.openAct)
183
184 # correct texts will be set later
185 self.openReadOnlyAct = E5Action(
186 "", "",
187 0, 0, self, 'hexEditor_file_open_read_only')
188 self.openReadOnlyAct.triggered.connect(self.__openHexFileReadOnly)
189 self.__actions.append(self.openReadOnlyAct)
190
191 self.saveAct = E5Action(
192 self.tr('Save'),
193 UI.PixmapCache.getIcon("fileSave"),
194 self.tr('&Save'),
195 QKeySequence(self.tr("Ctrl+S", "File|Save")),
196 0, self, 'hexEditor_file_save')
197 self.saveAct.setStatusTip(self.tr('Save the current binary file'))
198 self.saveAct.setWhatsThis(self.tr(
199 """<b>Save File</b>"""
200 """<p>Save the contents of the hex editor window.</p>"""
201 ))
202 self.saveAct.triggered.connect(self.__saveHexFile)
203 self.__actions.append(self.saveAct)
204
205 self.saveAsAct = E5Action(
206 self.tr('Save As'),
207 UI.PixmapCache.getIcon("fileSaveAs"),
208 self.tr('Save &As...'),
209 QKeySequence(self.tr("Shift+Ctrl+S", "File|Save As")),
210 0, self, 'hexEditor_file_save_as')
211 self.saveAsAct.setStatusTip(
212 self.tr('Save the current binary data to a new file'))
213 self.saveAsAct.setWhatsThis(self.tr(
214 """<b>Save As...</b>"""
215 """<p>Saves the current binary data to a new file.</p>"""
216 ))
217 self.saveAsAct.triggered.connect(self.__saveHexFileAs)
218 self.__actions.append(self.saveAsAct)
219
220 self.saveReadableAct = E5Action(
221 self.tr('Save As Readable'),
222 self.tr('Save As &Readable...'),
223 0, 0, self, 'hexEditor_file_save_readable')
224 self.saveReadableAct.setStatusTip(
225 self.tr('Save the current binary data to a new file in a readable'
226 ' format'))
227 self.saveReadableAct.setWhatsThis(self.tr(
228 """<b>Save As Readable...</b>"""
229 """<p>Saves the current binary data to a new file in a readable"""
230 """ format.</p>"""
231 ))
232 self.saveReadableAct.triggered.connect(self.__saveHexFileReadable)
233 self.__actions.append(self.saveReadableAct)
234
235 self.closeAct = E5Action(
236 self.tr('Close'),
237 UI.PixmapCache.getIcon("close"),
238 self.tr('&Close'),
239 QKeySequence(self.tr("Ctrl+W", "File|Close")),
240 0, self, 'hexEditor_file_close')
241 self.closeAct.setStatusTip(self.tr(
242 'Close the current hex editor window'))
243 self.closeAct.setWhatsThis(self.tr(
244 """<b>Close</b>"""
245 """<p>Closes the current hex editor window.</p>"""
246 ))
247 self.closeAct.triggered.connect(self.close)
248 self.__actions.append(self.closeAct)
249
250 self.closeAllAct = E5Action(
251 self.tr('Close All'),
252 self.tr('Close &All'),
253 0, 0, self, 'hexEditor_file_close_all')
254 self.closeAllAct.setStatusTip(self.tr(
255 'Close all hex editor windows'))
256 self.closeAllAct.setWhatsThis(self.tr(
257 """<b>Close All</b>"""
258 """<p>Closes all hex editor windows.</p>"""
259 ))
260 self.closeAllAct.triggered.connect(self.__closeAll)
261 self.__actions.append(self.closeAllAct)
262
263 self.closeOthersAct = E5Action(
264 self.tr('Close Others'),
265 self.tr('Close Others'),
266 0, 0, self, 'hexEditor_file_close_others')
267 self.closeOthersAct.setStatusTip(self.tr(
268 'Close all other hex editor windows'))
269 self.closeOthersAct.setWhatsThis(self.tr(
270 """<b>Close Others</b>"""
271 """<p>Closes all other hex editor windows.</p>"""
272 ))
273 self.closeOthersAct.triggered.connect(self.__closeOthers)
274 self.__actions.append(self.closeOthersAct)
275
276 self.exitAct = E5Action(
277 self.tr('Quit'),
278 UI.PixmapCache.getIcon("exit"),
279 self.tr('&Quit'),
280 QKeySequence(self.tr("Ctrl+Q", "File|Quit")),
281 0, self, 'hexEditor_file_quit')
282 self.exitAct.setStatusTip(self.tr('Quit the hex editor'))
283 self.exitAct.setWhatsThis(self.tr(
284 """<b>Quit</b>"""
285 """<p>Quit the hex editor.</p>"""
286 ))
287 if not self.__fromEric:
288 self.exitAct.triggered.connect(self.__closeAll)
289 self.__actions.append(self.exitAct)
290
291 def __initEditActions(self):
292 """
293 Private method to create the Edit actions.
294 """
295 self.undoAct = E5Action(
296 self.tr('Undo'),
297 UI.PixmapCache.getIcon("editUndo"),
298 self.tr('&Undo'),
299 QKeySequence(self.tr("Ctrl+Z", "Edit|Undo")),
300 QKeySequence(self.tr("Alt+Backspace", "Edit|Undo")),
301 self, 'hexEditor_edit_undo')
302 self.undoAct.setStatusTip(self.tr('Undo the last change'))
303 self.undoAct.setWhatsThis(self.tr(
304 """<b>Undo</b>"""
305 """<p>Undo the last change done.</p>"""
306 ))
307 self.undoAct.triggered.connect(self.__editor.undo)
308 self.__actions.append(self.undoAct)
309
310 self.redoAct = E5Action(
311 self.tr('Redo'),
312 UI.PixmapCache.getIcon("editRedo"),
313 self.tr('&Redo'),
314 QKeySequence(self.tr("Ctrl+Shift+Z", "Edit|Redo")),
315 0, self, 'hexEditor_edit_redo')
316 self.redoAct.setStatusTip(self.tr('Redo the last change'))
317 self.redoAct.setWhatsThis(self.tr(
318 """<b>Redo</b>"""
319 """<p>Redo the last change done.</p>"""
320 ))
321 self.redoAct.triggered.connect(self.__editor.redo)
322 self.__actions.append(self.redoAct)
323
324 self.revertAct = E5Action(
325 self.tr('Revert to last saved state'),
326 self.tr('Re&vert to last saved state'),
327 QKeySequence(self.tr("Ctrl+Y", "Edit|Revert")),
328 0,
329 self, 'hexEditor_edit_revert')
330 self.revertAct.setStatusTip(self.tr('Revert to last saved state'))
331 self.revertAct.setWhatsThis(self.tr(
332 """<b>Revert to last saved state</b>"""
333 """<p>Undo all changes up to the last saved state of the"""
334 """ editor.</p>"""
335 ))
336 self.revertAct.triggered.connect(self.__editor.revertToUnmodified)
337 self.__actions.append(self.revertAct)
338
339 self.cutAct = E5Action(
340 self.tr('Cut'),
341 UI.PixmapCache.getIcon("editCut"),
342 self.tr('Cu&t'),
343 QKeySequence(self.tr("Ctrl+X", "Edit|Cut")),
344 QKeySequence(self.tr("Shift+Del", "Edit|Cut")),
345 self, 'hexEditor_edit_cut')
346 self.cutAct.setStatusTip(self.tr('Cut the selection'))
347 self.cutAct.setWhatsThis(self.tr(
348 """<b>Cut</b>"""
349 """<p>Cut the selected binary area to the clipboard.</p>"""
350 ))
351 self.cutAct.triggered.connect(self.__editor.cut)
352 self.__actions.append(self.cutAct)
353
354 self.copyAct = E5Action(
355 self.tr('Copy'),
356 UI.PixmapCache.getIcon("editCopy"),
357 self.tr('&Copy'),
358 QKeySequence(self.tr("Ctrl+C", "Edit|Copy")),
359 QKeySequence(self.tr("Ctrl+Ins", "Edit|Copy")),
360 self, 'hexEditor_edit_copy')
361 self.copyAct.setStatusTip(self.tr('Copy the selection'))
362 self.copyAct.setWhatsThis(self.tr(
363 """<b>Copy</b>"""
364 """<p>Copy the selected binary area to the clipboard.</p>"""
365 ))
366 self.copyAct.triggered.connect(self.__editor.copy)
367 self.__actions.append(self.copyAct)
368
369 self.pasteAct = E5Action(
370 self.tr('Paste'),
371 UI.PixmapCache.getIcon("editPaste"),
372 self.tr('&Paste'),
373 QKeySequence(self.tr("Ctrl+V", "Edit|Paste")),
374 QKeySequence(self.tr("Shift+Ins", "Edit|Paste")),
375 self, 'hexEditor_edit_paste')
376 self.pasteAct.setStatusTip(self.tr('Paste the clipboard contents'))
377 self.pasteAct.setWhatsThis(self.tr(
378 """<b>Paste</b>"""
379 """<p>Paste the clipboard contents.</p>"""
380 ))
381 self.pasteAct.triggered.connect(self.__editor.paste)
382 self.__actions.append(self.pasteAct)
383
384 self.selectAllAct = E5Action(
385 self.tr('Select All'),
386 UI.PixmapCache.getIcon("editSelectAll"),
387 self.tr('&Select All'),
388 QKeySequence(self.tr("Ctrl+A", "Edit|Select All")),
389 0,
390 self, 'hexEditor_edit_select_all')
391 self.selectAllAct.setStatusTip(self.tr(
392 'Select the complete binary data'))
393 self.selectAllAct.setWhatsThis(self.tr(
394 """<b>Select All</b>"""
395 """<p>Selects the complete binary data.</p>"""
396 ))
397 self.selectAllAct.triggered.connect(self.__editor.selectAll)
398 self.__actions.append(self.selectAllAct)
399
400 self.deselectAllAct = E5Action(
401 self.tr('Deselect all'),
402 self.tr('&Deselect all'),
403 QKeySequence(self.tr("Alt+Ctrl+A", "Edit|Deselect all")),
404 0,
405 self, 'hexEditor_edit_deselect_all')
406 self.deselectAllAct.setStatusTip(self.tr('Deselect all binary data'))
407 self.deselectAllAct.setWhatsThis(self.tr(
408 """<b>Deselect All</b>"""
409 """<p>Deselect all all binary data.</p>"""
410 ))
411 self.deselectAllAct.triggered.connect(self.__editor.deselectAll)
412 self.__actions.append(self.deselectAllAct)
413
414 self.saveSelectionReadableAct = E5Action(
415 self.tr('Save Selection Readable'),
416 self.tr('Save Selection Readable...'),
417 0, 0, self, 'hexEditor_edit_selection_save_readable')
418 self.saveSelectionReadableAct.setStatusTip(
419 self.tr('Save the binary data of the current selection to a file'
420 ' in a readable format'))
421 self.saveSelectionReadableAct.setWhatsThis(self.tr(
422 """<b>Save Selection Readable...</b>"""
423 """<p>Saves the binary data of the current selection to a file"""
424 """ in a readable format.</p>"""
425 ))
426 self.saveSelectionReadableAct.triggered.connect(
427 self.__saveSelectionReadable)
428 self.__actions.append(self.saveSelectionReadableAct)
429
430 self.readonlyAct = E5Action(
431 self.tr('Set Read Only'),
432 self.tr('Set Read Only'),
433 0, 0, self, 'hexEditor_edit_readonly', True)
434 self.readonlyAct.setStatusTip(self.tr(
435 'Change the edit mode to read only'))
436 self.readonlyAct.setWhatsThis(self.tr(
437 """<b>Set Read Only</b>"""
438 """<p>This changes the edit mode to read only (i.e. to view"""
439 """ mode).</p>"""
440 ))
441 self.readonlyAct.setChecked(False)
442 self.readonlyAct.toggled[bool].connect(self.__editor.setReadOnly)
443 self.__editor.readOnlyChanged.connect(self.readonlyAct.setChecked)
444 self.__actions.append(self.readonlyAct)
445
446 self.searchAct = E5Action(
447 self.tr('Search'),
448 UI.PixmapCache.getIcon("find"),
449 self.tr('&Search...'),
450 QKeySequence(self.tr("Ctrl+F", "Search|Search")),
451 0,
452 self, 'hexEditor_edit_search')
453 self.searchAct.setStatusTip(self.tr('Search for data'))
454 self.searchAct.setWhatsThis(self.tr(
455 """<b>Search</b>"""
456 """<p>Search for some data. A dialog is shown to enter the"""
457 """ data to search for in various formats.</p>"""
458 ))
459 self.searchAct.triggered.connect(self.__search)
460 self.__actions.append(self.searchAct)
461
462 self.searchNextAct = E5Action(
463 self.tr('Search next'),
464 UI.PixmapCache.getIcon("findNext"),
465 self.tr('Search &next'),
466 QKeySequence(self.tr("F3", "Search|Search next")),
467 0,
468 self, 'hexEditor_edit_search_next')
469 self.searchNextAct.setStatusTip(self.tr(
470 'Search next occurrence'))
471 self.searchNextAct.setWhatsThis(self.tr(
472 """<b>Search next</b>"""
473 """<p>Search the next occurrence of some data. The previously"""
474 """ entered search data are reused.</p>"""
475 ))
476 self.searchNextAct.triggered.connect(self.__searchWidget.findPrevNext)
477 self.__actions.append(self.searchNextAct)
478
479 self.searchPrevAct = E5Action(
480 self.tr('Search previous'),
481 UI.PixmapCache.getIcon("findPrev"),
482 self.tr('Search &previous'),
483 QKeySequence(self.tr("Shift+F3", "Search|Search previous")),
484 0,
485 self, 'hexEditor_edit_search_previous')
486 self.searchPrevAct.setStatusTip(self.tr(
487 'Search previous occurrence'))
488 self.searchPrevAct.setWhatsThis(self.tr(
489 """<b>Search previous</b>"""
490 """<p>Search the previous occurrence of some data. The"""
491 """ previously entered search data are reused.</p>"""
492 ))
493 self.searchPrevAct.triggered.connect(
494 lambda: self.__searchWidget.findPrevNext(True))
495 self.__actions.append(self.searchPrevAct)
496
497 self.replaceAct = E5Action(
498 self.tr('Replace'),
499 self.tr('&Replace...'),
500 QKeySequence(self.tr("Ctrl+R", "Search|Replace")),
501 0,
502 self, 'hexEditor_edit_search_replace')
503 self.replaceAct.setStatusTip(self.tr('Replace data'))
504 self.replaceAct.setWhatsThis(self.tr(
505 """<b>Replace</b>"""
506 """<p>Search for some data and replace it."""
507 """ A dialog is shown to enter the data to search for and the"""
508 """ replacement data in various formats.</p>"""
509 ))
510 self.replaceAct.triggered.connect(self.__replace)
511 self.__actions.append(self.replaceAct)
512
513 self.gotoAct = E5Action(
514 self.tr('Goto Offset'),
515 UI.PixmapCache.getIcon("goto"),
516 self.tr('&Goto Offset...'),
517 QKeySequence(QCoreApplication.translate(
518 'ViewManager', "Ctrl+G", "Search|Goto Offset")),
519 0,
520 self, 'hexEditor_edit_goto')
521 self.gotoAct.setStatusTip(self.tr('Goto Offset'))
522 self.gotoAct.setWhatsThis(self.tr(
523 """<b>Goto Offset</b>"""
524 """<p>Go to a specific address. A dialog is shown to enter"""
525 """ the movement data.</p>"""
526 ))
527 self.gotoAct.triggered.connect(self.__goto)
528 self.__actions.append(self.gotoAct)
529
530 self.redoAct.setEnabled(False)
531 self.__editor.canRedoChanged.connect(self.redoAct.setEnabled)
532
533 self.undoAct.setEnabled(False)
534 self.__editor.canUndoChanged.connect(self.undoAct.setEnabled)
535
536 self.revertAct.setEnabled(False)
537 self.__editor.dataChanged.connect(self.revertAct.setEnabled)
538
539 self.cutAct.setEnabled(False)
540 self.copyAct.setEnabled(False)
541 self.saveSelectionReadableAct.setEnabled(False)
542 self.__editor.selectionAvailable.connect(self.__checkActions)
543 self.__editor.selectionAvailable.connect(self.copyAct.setEnabled)
544 self.__editor.selectionAvailable.connect(
545 self.saveSelectionReadableAct.setEnabled)
546
547 def __initHelpActions(self):
548 """
549 Private method to create the Help actions.
550 """
551 self.aboutAct = E5Action(
552 self.tr('About'),
553 self.tr('&About'),
554 0, 0, self, 'hexEditor_help_about')
555 self.aboutAct.setStatusTip(self.tr(
556 'Display information about this software'))
557 self.aboutAct.setWhatsThis(self.tr(
558 """<b>About</b>"""
559 """<p>Display some information about this software.</p>"""))
560 self.aboutAct.triggered.connect(self.__about)
561 self.__actions.append(self.aboutAct)
562
563 self.aboutQtAct = E5Action(
564 self.tr('About Qt'),
565 self.tr('About &Qt'),
566 0, 0, self, 'hexEditor_help_about_qt')
567 self.aboutQtAct.setStatusTip(
568 self.tr('Display information about the Qt toolkit'))
569 self.aboutQtAct.setWhatsThis(self.tr(
570 """<b>About Qt</b>"""
571 """<p>Display some information about the Qt toolkit.</p>"""
572 ))
573 self.aboutQtAct.triggered.connect(self.__aboutQt)
574 self.__actions.append(self.aboutQtAct)
575
576 self.whatsThisAct = E5Action(
577 self.tr('What\'s This?'),
578 UI.PixmapCache.getIcon("whatsThis"),
579 self.tr('&What\'s This?'),
580 QKeySequence(self.tr("Shift+F1", "Help|What's This?'")),
581 0, self, 'hexEditor_help_whats_this')
582 self.whatsThisAct.setStatusTip(self.tr('Context sensitive help'))
583 self.whatsThisAct.setWhatsThis(self.tr(
584 """<b>Display context sensitive help</b>"""
585 """<p>In What's This? mode, the mouse cursor shows an arrow"""
586 """ with a question mark, and you can click on the interface"""
587 """ elements to get a short description of what they do and"""
588 """ how to use them. In dialogs, this feature can be accessed"""
589 """ using the context help button in the titlebar.</p>"""
590 ))
591 self.whatsThisAct.triggered.connect(self.__whatsThis)
592 self.__actions.append(self.whatsThisAct)
593
594 def __initConfigActions(self):
595 """
596 Private method to create the Settings actions.
597 """
598 self.prefAct = E5Action(
599 self.tr('Preferences'),
600 UI.PixmapCache.getIcon("configure"),
601 self.tr('&Preferences...'),
602 0, 0, self, 'hexEditor_settings_preferences')
603 self.prefAct.setStatusTip(self.tr(
604 'Set the prefered configuration'))
605 self.prefAct.setWhatsThis(self.tr(
606 """<b>Preferences</b>"""
607 """<p>Set the configuration items of the application"""
608 """ with your prefered values.</p>"""
609 ))
610 self.prefAct.triggered.connect(self.__showPreferences)
611 self.prefAct.setMenuRole(QAction.MenuRole.PreferencesRole)
612 self.__actions.append(self.prefAct)
613
614 def __setReadOnlyActionTexts(self):
615 """
616 Private method to switch the 'Open Read Only' action between
617 'read only' and 'read write'.
618 """
619 if Preferences.getHexEditor("OpenReadOnly"):
620 self.openAct.setStatusTip(self.tr(
621 'Open a binary file for viewing'))
622 self.openAct.setWhatsThis(self.tr(
623 """<b>Open File</b>"""
624 """<p>This opens a binary file for viewing (i.e. in read"""
625 """ only mode). It pops up a file selection dialog.</p>"""
626 ))
627
628 self.openReadOnlyAct.setText(self.tr('Open for Editing...'))
629 self.openReadOnlyAct.setIconText(self.tr('Open for Editing'))
630 self.openReadOnlyAct.setStatusTip(self.tr(
631 'Open a binary file for editing'))
632 self.openReadOnlyAct.setWhatsThis(self.tr(
633 """<b>Open for Editing</b>"""
634 """<p>This opens a binary file for editing."""
635 """ It pops up a file selection dialog.</p>"""
636 ))
637 else:
638 self.openAct.setStatusTip(self.tr(
639 'Open a binary file for editing'))
640 self.openAct.setWhatsThis(self.tr(
641 """<b>Open File</b>"""
642 """<p>This opens a binary file for editing."""
643 """ It pops up a file selection dialog.</p>"""
644 ))
645
646 self.openReadOnlyAct.setText(self.tr('Open Read Only...'))
647 self.openReadOnlyAct.setIconText(self.tr('Open Read Only'))
648 self.openReadOnlyAct.setStatusTip(self.tr(
649 'Open a binary file for viewing'))
650 self.openReadOnlyAct.setWhatsThis(self.tr(
651 """<b>Open Read Only</b>"""
652 """<p>This opens a binary file for viewing (i.e. in read"""
653 """ only mode). It pops up a file selection dialog.</p>"""
654 ))
655
656 def __initMenus(self):
657 """
658 Private method to create the menus.
659 """
660 mb = self.menuBar()
661
662 menu = mb.addMenu(self.tr('&File'))
663 menu.setTearOffEnabled(True)
664 self.__recentMenu = QMenu(self.tr('Open &Recent Files'), menu)
665 menu.addAction(self.newWindowAct)
666 menu.addAction(self.openAct)
667 menu.addAction(self.openReadOnlyAct)
668 self.__menuRecentAct = menu.addMenu(self.__recentMenu)
669 menu.addSeparator()
670 menu.addAction(self.saveAct)
671 menu.addAction(self.saveAsAct)
672 menu.addAction(self.saveReadableAct)
673 menu.addSeparator()
674 menu.addAction(self.closeAct)
675 menu.addAction(self.closeOthersAct)
676 if self.__fromEric:
677 menu.addAction(self.closeAllAct)
678 else:
679 menu.addSeparator()
680 menu.addAction(self.exitAct)
681 menu.aboutToShow.connect(self.__showFileMenu)
682 self.__recentMenu.aboutToShow.connect(self.__showRecentMenu)
683 self.__recentMenu.triggered.connect(self.__openRecentHexFile)
684
685 menu = mb.addMenu(self.tr("&Edit"))
686 menu.setTearOffEnabled(True)
687 menu.addAction(self.undoAct)
688 menu.addAction(self.redoAct)
689 menu.addAction(self.revertAct)
690 menu.addSeparator()
691 menu.addAction(self.cutAct)
692 menu.addAction(self.copyAct)
693 menu.addAction(self.pasteAct)
694 menu.addSeparator()
695 menu.addAction(self.selectAllAct)
696 menu.addAction(self.deselectAllAct)
697 menu.addAction(self.saveSelectionReadableAct)
698 menu.addSeparator()
699 menu.addAction(self.searchAct)
700 menu.addAction(self.searchNextAct)
701 menu.addAction(self.searchPrevAct)
702 menu.addAction(self.replaceAct)
703 menu.addSeparator()
704 menu.addAction(self.gotoAct)
705 menu.addSeparator()
706 menu.addAction(self.readonlyAct)
707
708 if not self.__fromEric:
709 menu = mb.addMenu(self.tr("Se&ttings"))
710 menu.setTearOffEnabled(True)
711 menu.addAction(self.prefAct)
712
713 mb.addSeparator()
714
715 menu = mb.addMenu(self.tr("&Help"))
716 menu.addAction(self.aboutAct)
717 menu.addAction(self.aboutQtAct)
718 menu.addSeparator()
719 menu.addAction(self.whatsThisAct)
720
721 def __initToolbars(self):
722 """
723 Private method to create the toolbars.
724 """
725 filetb = self.addToolBar(self.tr("File"))
726 filetb.setObjectName("FileToolBar")
727 filetb.setIconSize(UI.Config.ToolBarIconSize)
728 filetb.addAction(self.newWindowAct)
729 filetb.addAction(self.openAct)
730 filetb.addSeparator()
731 filetb.addAction(self.saveAct)
732 filetb.addAction(self.saveAsAct)
733 filetb.addSeparator()
734 filetb.addAction(self.closeAct)
735 if not self.__fromEric:
736 filetb.addAction(self.exitAct)
737
738 edittb = self.addToolBar(self.tr("Edit"))
739 edittb.setObjectName("EditToolBar")
740 edittb.setIconSize(UI.Config.ToolBarIconSize)
741 edittb.addAction(self.undoAct)
742 edittb.addAction(self.redoAct)
743 edittb.addSeparator()
744 edittb.addAction(self.cutAct)
745 edittb.addAction(self.copyAct)
746 edittb.addAction(self.pasteAct)
747
748 searchtb = self.addToolBar(self.tr("Find"))
749 searchtb.setObjectName("SearchToolBar")
750 searchtb.setIconSize(UI.Config.ToolBarIconSize)
751 searchtb.addAction(self.searchAct)
752 searchtb.addAction(self.searchNextAct)
753 searchtb.addAction(self.searchPrevAct)
754
755 if not self.__fromEric:
756 settingstb = self.addToolBar(self.tr("Settings"))
757 settingstb.setObjectName("SettingsToolBar")
758 settingstb.setIconSize(UI.Config.ToolBarIconSize)
759 settingstb.addAction(self.prefAct)
760
761 helptb = self.addToolBar(self.tr("Help"))
762 helptb.setObjectName("HelpToolBar")
763 helptb.setIconSize(UI.Config.ToolBarIconSize)
764 helptb.addAction(self.whatsThisAct)
765
766 def __createStatusBar(self):
767 """
768 Private method to initialize the status bar.
769 """
770 self.__statusBar = self.statusBar()
771 self.__statusBar.setSizeGripEnabled(True)
772
773 self.__sbAddress = QLabel(self.__statusBar)
774 self.__statusBar.addPermanentWidget(self.__sbAddress)
775 self.__sbAddress.setWhatsThis(self.tr(
776 """<p>This part of the status bar displays the cursor"""
777 """ address.</p>"""
778 ))
779 self.__sbAddress.setFrameStyle(
780 QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
781
782 self.__sbSelection = QLabel(self.__statusBar)
783 self.__statusBar.addPermanentWidget(self.__sbSelection)
784 self.__sbSelection.setWhatsThis(self.tr(
785 """<p>This part of the status bar displays some selection"""
786 """ information.</p>"""
787 ))
788 self.__sbSelection.setFrameStyle(
789 QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
790
791 self.__sbSize = QLabel(self.__statusBar)
792 self.__statusBar.addPermanentWidget(self.__sbSize)
793 self.__sbSize.setWhatsThis(self.tr(
794 """<p>This part of the status bar displays the size of the"""
795 """ binary data.</p>"""
796 ))
797 self.__sbSize.setFrameStyle(
798 QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
799
800 self.__sbEditMode = E5ClickableLabel(self.__statusBar)
801 self.__statusBar.addPermanentWidget(self.__sbEditMode)
802 self.__sbEditMode.setWhatsThis(self.tr(
803 """<p>This part of the status bar displays the edit mode.</p>"""
804 ))
805 self.__sbEditMode.setFrameStyle(
806 QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
807 self.__sbEditMode.clicked.connect(self.__toggleEditMode)
808
809 self.__sbReadOnly = E5ClickableLabel(self.__statusBar)
810 self.__statusBar.addPermanentWidget(self.__sbReadOnly)
811 self.__sbReadOnly.setWhatsThis(self.tr(
812 """<p>This part of the status bar displays the read"""
813 """ only mode.</p>"""
814 ))
815 self.__sbReadOnly.setFrameStyle(
816 QFrame.Shape.StyledPanel | QFrame.Shadow.Plain)
817 self.__sbReadOnly.clicked.connect(self.__toggleReadOnlyMode)
818
819 self.__showEditMode(self.__editor.overwriteMode())
820 self.__showReadOnlyMode(self.__editor.isReadOnly())
821
822 @pyqtSlot(int)
823 def __showAddress(self, address):
824 """
825 Private slot to show the address of the cursor position.
826
827 @param address address of the cursor
828 @type int
829 """
830 txt = "{0:0{1}x}".format(address, self.__editor.addressWidth())
831 txt = strGroup(txt, ":", 4)
832 self.__sbAddress.setText(self.tr("Address: {0}").format(txt))
833
834 @pyqtSlot(bool)
835 def __showSelectionInfo(self, avail):
836 """
837 Private slot to show selection information.
838
839 @param avail flag indicating the availability of a selection.
840 @type bool
841 """
842 if avail:
843 addrWidth = self.__editor.addressWidth()
844 start = "{0:0{1}x}".format(self.__editor.getSelectionBegin(),
845 addrWidth)
846 start = strGroup(start, ":", 4)
847 end = "{0:0{1}x}".format(self.__editor.getSelectionEnd(),
848 addrWidth)
849 end = strGroup(end, ":", 4)
850 slen = self.__editor.getSelectionLength()
851 self.__sbSelection.setText(
852 self.tr("Selection: {0} - {1} ({2} Bytes)",
853 "0: start, 1: end, 2: selection length")
854 .format(start, end, QLocale().toString(slen))
855 )
856 else:
857 self.__sbSelection.setText(
858 self.tr("Selection: -", "no selection available"))
859
860 @pyqtSlot(bool)
861 def __showReadOnlyMode(self, on):
862 """
863 Private slot to show the read only mode.
864
865 @param on flag indicating the read only state
866 @type bool
867 """
868 self.__sbReadOnly.setText(self.tr("ro") if on else self.tr("rw"))
869
870 @pyqtSlot()
871 def __toggleReadOnlyMode(self):
872 """
873 Private slot to toggle the read only mode upon a click on the status
874 bar label.
875 """
876 self.__editor.setReadOnly(not self.__editor.isReadOnly())
877
878 @pyqtSlot(bool)
879 def __showEditMode(self, overwrite):
880 """
881 Private slot to show the edit mode.
882
883 @param overwrite flag indicating overwrite mode
884 @type bool
885 """
886 self.__sbEditMode.setText(
887 self.tr("Overwrite") if overwrite else self.tr("Insert"))
888
889 @pyqtSlot()
890 def __toggleEditMode(self):
891 """
892 Private slot to toggle the edit mode upon a click on the status bar
893 label.
894 """
895 self.__editor.setOverwriteMode(not self.__editor.overwriteMode())
896
897 @pyqtSlot(int)
898 def __showSize(self, size):
899 """
900 Private slot to show the binary data size.
901
902 @param size size of the binary data
903 @type int
904 """
905 self.__sbSize.setText(
906 self.tr("Size: {0}").format(QLocale().toString(size)))
907
908 def closeEvent(self, evt):
909 """
910 Protected event handler for the close event.
911
912 @param evt reference to the close event
913 <br />This event is simply accepted after the history has been
914 saved and all window references have been deleted.
915 @type QCloseEvent
916 """
917 if self.__maybeSave():
918 state = self.saveState()
919 Preferences.setHexEditor("HexEditorState", state)
920
921 Preferences.setGeometry("HexEditorGeometry", self.saveGeometry())
922
923 with contextlib.suppress(ValueError):
924 if self.__fromEric or len(self.__class__.windows) > 1:
925 del self.__class__.windows[
926 self.__class__.windows.index(self)]
927
928 if not self.__fromEric:
929 Preferences.syncPreferences()
930
931 self.__saveRecent()
932
933 evt.accept()
934 self.editorClosed.emit()
935 else:
936 evt.ignore()
937
938 def __openHexFileNewWindow(self):
939 """
940 Private slot called to open a binary file in new hex editor window.
941 """
942 if (
943 not self.__lastOpenPath and
944 self.__project is not None and
945 self.__project.isOpen()
946 ):
947 self.__lastOpenPath = self.__project.getProjectPath()
948
949 fileName = E5FileDialog.getOpenFileName(
950 self,
951 self.tr("Open binary file in new window"),
952 self.__lastOpenPath,
953 self.tr("All Files (*)"))
954 if fileName:
955 he = HexEditMainWindow(fileName=fileName,
956 parent=self.parent(),
957 fromEric=self.__fromEric,
958 project=self.__project)
959 he.setRecentPaths("", self.__lastSavePath)
960 he.show()
961
962 def __maybeSave(self):
963 """
964 Private method to ask the user to save the file, if it was modified.
965
966 @return flag indicating, if it is ok to continue
967 @rtype bool
968 """
969 if self.__editor.isModified():
970 ret = E5MessageBox.okToClearData(
971 self,
972 self.tr("eric Hex Editor"),
973 self.tr("""The loaded file has unsaved changes."""),
974 self.__saveHexFile)
975 if not ret:
976 return False
977 return True
978
979 def __loadHexFile(self, fileName):
980 """
981 Private method to load a binary file.
982
983 @param fileName name of the binary file to load
984 @type str
985 """
986 file = QFile(fileName)
987 if not file.exists():
988 E5MessageBox.warning(
989 self, self.tr("eric Hex Editor"),
990 self.tr("The file '{0}' does not exist.")
991 .format(fileName))
992 return
993
994 if not file.open(QFile.ReadOnly):
995 E5MessageBox.warning(
996 self, self.tr("eric Hex Editor"),
997 self.tr("Cannot read file '{0}:\n{1}.")
998 .format(fileName, file.errorString()))
999 return
1000
1001 data = file.readAll()
1002 file.close()
1003
1004 self.__lastOpenPath = os.path.dirname(fileName)
1005 self.__editor.setData(data)
1006 self.__setCurrentFile(fileName)
1007
1008 self.__editor.setReadOnly(Preferences.getHexEditor("OpenReadOnly"))
1009
1010 self.__gotoWidget.reset()
1011
1012 def __openHexFile(self):
1013 """
1014 Private slot to open a binary file.
1015 """
1016 if self.__maybeSave():
1017 if (
1018 not self.__lastOpenPath and
1019 self.__project is not None and
1020 self.__project.isOpen()
1021 ):
1022 self.__lastOpenPath = self.__project.getProjectPath()
1023
1024 fileName = E5FileDialog.getOpenFileName(
1025 self,
1026 self.tr("Open binary file"),
1027 self.__lastOpenPath,
1028 self.tr("All Files (*)"))
1029 if fileName:
1030 self.__loadHexFile(fileName)
1031 self.__checkActions()
1032
1033 def __openHexFileReadOnly(self):
1034 """
1035 Private slot to open a binary file in read only mode.
1036 """
1037 self.__openHexFile()
1038 self.__editor.setReadOnly(not Preferences.getHexEditor("OpenReadOnly"))
1039 self.__checkActions()
1040
1041 def __saveHexFile(self):
1042 """
1043 Private method to save a binary file.
1044
1045 @return flag indicating success
1046 @rtype bool
1047 """
1048 ok = (
1049 self.__saveHexDataFile(self.__fileName)
1050 if self.__fileName else
1051 self.__saveHexFileAs()
1052 )
1053
1054 if ok:
1055 self.__editor.undoStack().setClean()
1056
1057 return ok
1058
1059 def __saveHexFileAs(self):
1060 """
1061 Private method to save the data to a new file.
1062
1063 @return flag indicating success
1064 @rtype bool
1065 """
1066 if (
1067 not self.__lastSavePath and
1068 self.__project is not None and
1069 self.__project.isOpen()
1070 ):
1071 self.__lastSavePath = self.__project.getProjectPath()
1072 if not self.__lastSavePath and self.__lastOpenPath:
1073 self.__lastSavePath = self.__lastOpenPath
1074
1075 fileName = E5FileDialog.getSaveFileName(
1076 self,
1077 self.tr("Save binary file"),
1078 self.__lastSavePath,
1079 self.tr("All Files (*)"),
1080 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
1081 if not fileName:
1082 return False
1083
1084 if QFileInfo(fileName).exists():
1085 res = E5MessageBox.yesNo(
1086 self,
1087 self.tr("Save binary file"),
1088 self.tr("<p>The file <b>{0}</b> already exists."
1089 " Overwrite it?</p>").format(fileName),
1090 icon=E5MessageBox.Warning)
1091 if not res:
1092 return False
1093
1094 self.__lastSavePath = os.path.dirname(fileName)
1095
1096 return self.__saveHexDataFile(fileName)
1097
1098 def __saveHexDataFile(self, fileName):
1099 """
1100 Private method to save the binary data to a file.
1101
1102 @param fileName name of the file to write to
1103 @type str
1104 @return flag indicating success
1105 @rtype bool
1106 """
1107 file = QFile(fileName)
1108 if not file.open(QFile.WriteOnly):
1109 E5MessageBox.warning(
1110 self, self.tr("eric Hex Editor"),
1111 self.tr("Cannot write file '{0}:\n{1}.")
1112 .format(fileName, file.errorString()))
1113
1114 self.__checkActions()
1115
1116 return False
1117
1118 res = file.write(self.__editor.data()) != -1
1119 file.close()
1120
1121 if not res:
1122 E5MessageBox.warning(
1123 self, self.tr("eric Hex Editor"),
1124 self.tr("Cannot write file '{0}:\n{1}.")
1125 .format(fileName, file.errorString()))
1126
1127 self.__checkActions()
1128
1129 return False
1130
1131 self.__editor.setModified(False, setCleanState=True)
1132
1133 self.__setCurrentFile(fileName)
1134 self.__statusBar.showMessage(self.tr("File saved"), 2000)
1135
1136 self.__checkActions()
1137
1138 return True
1139
1140 def __saveHexFileReadable(self, selectionOnly=False):
1141 """
1142 Private method to save the binary data in readable format.
1143
1144 @param selectionOnly flag indicating to save the selection only
1145 @type bool
1146 """
1147 savePath = self.__lastSavePath
1148 if (
1149 not savePath and
1150 self.__project is not None and
1151 self.__project.isOpen()
1152 ):
1153 savePath = self.__project.getProjectPath()
1154 if not savePath and self.__lastOpenPath:
1155 savePath = self.__lastOpenPath
1156
1157 fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
1158 self,
1159 self.tr("Save to readable file"),
1160 savePath,
1161 self.tr("Text Files (*.txt);;All Files (*)"),
1162 self.tr("Text Files (*.txt)"),
1163 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
1164 if not fileName:
1165 return
1166
1167 ext = QFileInfo(fileName).suffix()
1168 if not ext:
1169 ex = selectedFilter.split("(*")[1].split(")")[0]
1170 if ex:
1171 fileName += ex
1172 if QFileInfo(fileName).exists():
1173 res = E5MessageBox.yesNo(
1174 self,
1175 self.tr("Save to readable file"),
1176 self.tr("<p>The file <b>{0}</b> already exists."
1177 " Overwrite it?</p>").format(fileName),
1178 icon=E5MessageBox.Warning)
1179 if not res:
1180 return
1181
1182 file = QFile(fileName)
1183 if not file.open(QFile.WriteOnly):
1184 E5MessageBox.warning(
1185 self, self.tr("eric Hex Editor"),
1186 self.tr("Cannot write file '{0}:\n{1}.")
1187 .format(fileName, file.errorString()))
1188 return
1189
1190 readableData = (
1191 self.__editor.selectionToReadableString()
1192 if selectionOnly else
1193 self.__editor.toReadableString()
1194 )
1195 res = file.write(readableData.encode("latin1")) != -1
1196 file.close()
1197
1198 if not res:
1199 E5MessageBox.warning(
1200 self, self.tr("eric Hex Editor"),
1201 self.tr("Cannot write file '{0}:\n{1}.")
1202 .format(fileName, file.errorString()))
1203 return
1204
1205 self.__statusBar.showMessage(self.tr("File saved"), 2000)
1206
1207 def __saveSelectionReadable(self):
1208 """
1209 Private method to save the data of the current selection in readable
1210 format.
1211 """
1212 self.__saveHexFileReadable(selectionOnly=True)
1213
1214 def __closeAll(self):
1215 """
1216 Private slot to close all windows.
1217 """
1218 self.__closeOthers()
1219 self.close()
1220
1221 def __closeOthers(self):
1222 """
1223 Private slot to close all other windows.
1224 """
1225 for win in self.__class__.windows[:]:
1226 if win != self:
1227 win.close()
1228
1229 def __setCurrentFile(self, fileName):
1230 """
1231 Private method to register the file name of the current file.
1232
1233 @param fileName name of the file to register
1234 @type str
1235 """
1236 self.__fileName = fileName
1237 # insert filename into list of recently opened files
1238 self.__addToRecentList(fileName)
1239
1240 shownName = (
1241 self.tr("Untitled")
1242 if not self.__fileName else
1243 self.__strippedName(self.__fileName)
1244 )
1245
1246 self.setWindowTitle(self.tr("{0}[*] - {1}")
1247 .format(shownName, self.tr("Hex Editor")))
1248
1249 self.setWindowModified(self.__editor.isModified())
1250
1251 def __strippedName(self, fullFileName):
1252 """
1253 Private method to return the filename part of the given path.
1254
1255 @param fullFileName full pathname of the given file
1256 @type str
1257 @return filename part
1258 @rtype str
1259 """
1260 return QFileInfo(fullFileName).fileName()
1261
1262 def setRecentPaths(self, openPath, savePath):
1263 """
1264 Public method to set the last open and save paths.
1265
1266 @param openPath least recently used open path
1267 @type str
1268 @param savePath least recently used save path
1269 @type str
1270 """
1271 if openPath:
1272 self.__lastOpenPath = openPath
1273 if savePath:
1274 self.__lastSavePath = savePath
1275
1276 @pyqtSlot()
1277 def __checkActions(self):
1278 """
1279 Private slot to check some actions for their enable/disable status.
1280 """
1281 self.saveAct.setEnabled(self.__editor.isModified())
1282
1283 self.cutAct.setEnabled(not self.__editor.isReadOnly() and
1284 self.__editor.hasSelection())
1285 self.pasteAct.setEnabled(not self.__editor.isReadOnly())
1286 self.replaceAct.setEnabled(not self.__editor.isReadOnly())
1287
1288 @pyqtSlot(bool)
1289 def __modificationChanged(self, m):
1290 """
1291 Private slot to handle the dataChanged signal.
1292
1293 @param m modification status
1294 @type bool
1295 """
1296 self.setWindowModified(m)
1297 self.__checkActions()
1298
1299 def __about(self):
1300 """
1301 Private slot to show a little About message.
1302 """
1303 E5MessageBox.about(
1304 self, self.tr("About eric Hex Editor"),
1305 self.tr("The eric Hex Editor is a simple editor component"
1306 " to edit binary files."))
1307
1308 def __aboutQt(self):
1309 """
1310 Private slot to handle the About Qt dialog.
1311 """
1312 E5MessageBox.aboutQt(self, "eric Hex Editor")
1313
1314 def __whatsThis(self):
1315 """
1316 Private slot called in to enter Whats This mode.
1317 """
1318 QWhatsThis.enterWhatsThisMode()
1319
1320 def __search(self):
1321 """
1322 Private method to handle the search action.
1323 """
1324 self.__replaceWidget.hide()
1325 self.__gotoWidget.hide()
1326 txt = (
1327 self.__editor.selectionToHexString()
1328 if self.__editor.hasSelection() else
1329 ""
1330 )
1331 self.__searchWidget.show(txt)
1332
1333 def __replace(self):
1334 """
1335 Private method to handle the replace action.
1336 """
1337 self.__searchWidget.hide()
1338 self.__gotoWidget.hide()
1339 txt = (
1340 self.__editor.selectionToHexString()
1341 if self.__editor.hasSelection() else
1342 ""
1343 )
1344 self.__replaceWidget.show(txt)
1345
1346 def __goto(self):
1347 """
1348 Private method to handle the goto action.
1349 """
1350 self.__searchWidget.hide()
1351 self.__replaceWidget.hide()
1352 self.__gotoWidget.show()
1353
1354 def preferencesChanged(self):
1355 """
1356 Public method to (re-)read the various settings.
1357 """
1358 self.__editor.setAddressWidth(
1359 Preferences.getHexEditor("AddressAreaWidth"))
1360 self.__editor.setAddressArea(
1361 Preferences.getHexEditor("ShowAddressArea"))
1362 self.__editor.setAsciiArea(
1363 Preferences.getHexEditor("ShowAsciiArea"))
1364 self.__editor.setHighlighting(
1365 Preferences.getHexEditor("HighlightChanges"))
1366 self.__editor.setHighlightColors(
1367 Preferences.getHexEditor("HighlightingForeGround"),
1368 Preferences.getHexEditor("HighlightingBackGround"))
1369 self.__editor.setSelectionColors(
1370 Preferences.getHexEditor("SelectionForeGround"),
1371 Preferences.getHexEditor("SelectionBackGround"))
1372 self.__editor.setAddressAreaColors(
1373 Preferences.getHexEditor("AddressAreaForeGround"),
1374 Preferences.getHexEditor("AddressAreaBackGround"))
1375 self.__editor.setFont(
1376 Preferences.getHexEditor("Font"))
1377
1378 self.__setReadOnlyActionTexts()
1379
1380 def __showPreferences(self):
1381 """
1382 Private slot to set the preferences.
1383 """
1384 from Preferences.ConfigurationDialog import (
1385 ConfigurationDialog, ConfigurationMode
1386 )
1387 dlg = ConfigurationDialog(
1388 None, 'Configuration', True, fromEric=True,
1389 displayMode=ConfigurationMode.HEXEDITORMODE)
1390 dlg.preferencesChanged.connect(
1391 self.__preferencesChangedByLocalPreferencesDialog)
1392 dlg.show()
1393 dlg.showConfigurationPageByName("hexEditorPage")
1394 dlg.exec()
1395 QCoreApplication.processEvents()
1396 if dlg.result() == QDialog.DialogCode.Accepted:
1397 dlg.setPreferences()
1398 Preferences.syncPreferences()
1399 self.__preferencesChangedByLocalPreferencesDialog()
1400
1401 def __preferencesChangedByLocalPreferencesDialog(self):
1402 """
1403 Private slot to handle preferences changes by our local dialog.
1404 """
1405 for hexEditor in HexEditMainWindow.windows:
1406 hexEditor.preferencesChanged()
1407
1408 def getSRHistory(self, key):
1409 """
1410 Public method to get the search or replace history list.
1411
1412 @param key name of list to return
1413 @type str (must be 'search' or 'replace')
1414 @return the requested history list
1415 @rtype list of tuples of (int, str)
1416 """
1417 if key in ['search', 'replace']:
1418 return self.__srHistory[key]
1419
1420 return []
1421
1422 @pyqtSlot()
1423 def __showFileMenu(self):
1424 """
1425 Private slot to modify the file menu before being shown.
1426 """
1427 self.__menuRecentAct.setEnabled(len(self.__recent) > 0)
1428
1429 @pyqtSlot()
1430 def __showRecentMenu(self):
1431 """
1432 Private slot to set up the recent files menu.
1433 """
1434 self.__loadRecent()
1435
1436 self.__recentMenu.clear()
1437
1438 for idx, rs in enumerate(self.__recent, start=1):
1439 formatStr = '&{0:d}. {1}' if idx < 10 else '{0:d}. {1}'
1440 act = self.__recentMenu.addAction(
1441 formatStr.format(
1442 idx,
1443 Utilities.compactPath(
1444 rs, HexEditMainWindow.maxMenuFilePathLen)))
1445 act.setData(rs)
1446 act.setEnabled(QFileInfo(rs).exists())
1447
1448 self.__recentMenu.addSeparator()
1449 self.__recentMenu.addAction(self.tr('&Clear'), self.__clearRecent)
1450
1451 @pyqtSlot(QAction)
1452 def __openRecentHexFile(self, act):
1453 """
1454 Private method to open a file from the list of recently opened files.
1455
1456 @param act reference to the action that triggered (QAction)
1457 """
1458 fileName = act.data()
1459 if fileName and self.__maybeSave():
1460 self.__loadHexFile(fileName)
1461 self.__editor.setReadOnly(Preferences.getHexEditor("OpenReadOnly"))
1462 self.__checkActions()
1463
1464 @pyqtSlot()
1465 def __clearRecent(self):
1466 """
1467 Private method to clear the list of recently opened files.
1468 """
1469 self.__recent = []
1470
1471 def __loadRecent(self):
1472 """
1473 Private method to load the list of recently opened files.
1474 """
1475 self.__recent = []
1476 Preferences.Prefs.rsettings.sync()
1477 rs = Preferences.Prefs.rsettings.value(recentNameHexFiles)
1478 if rs is not None:
1479 for f in Preferences.toList(rs):
1480 if QFileInfo(f).exists():
1481 self.__recent.append(f)
1482
1483 def __saveRecent(self):
1484 """
1485 Private method to save the list of recently opened files.
1486 """
1487 Preferences.Prefs.rsettings.setValue(recentNameHexFiles, self.__recent)
1488 Preferences.Prefs.rsettings.sync()
1489
1490 def __addToRecentList(self, fileName):
1491 """
1492 Private method to add a file name to the list of recently opened files.
1493
1494 @param fileName name of the file to be added
1495 """
1496 if fileName:
1497 for recent in self.__recent[:]:
1498 if Utilities.samepath(fileName, recent):
1499 self.__recent.remove(recent)
1500 self.__recent.insert(0, fileName)
1501 maxRecent = Preferences.getHexEditor("RecentNumber")
1502 if len(self.__recent) > maxRecent:
1503 self.__recent = self.__recent[:maxRecent]
1504 self.__saveRecent()

eric ide

mercurial