HexEdit/HexEditMainWindow.py

changeset 4650
b1ca3bcde70b
child 4651
7f3f276d3bf3
equal deleted inserted replaced
4649:bbc8b2de9173 4650:b1ca3bcde70b
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the hex editor main window.
8 """
9
10 from __future__ import unicode_literals
11
12 import os
13
14 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFile, QFileInfo, QSize
15 from PyQt5.QtGui import QKeySequence
16 from PyQt5.QtWidgets import QWhatsThis, QLabel
17
18 from E5Gui.E5Action import E5Action
19 from E5Gui.E5MainWindow import E5MainWindow
20 from E5Gui import E5FileDialog, E5MessageBox
21
22 from .HexEditWidget import HexEditWidget
23
24 import UI.PixmapCache
25 import UI.Config
26
27 import Preferences
28
29
30 # TODO: implement a configuration page for the hex editor
31 # TODO: implement an action in the eric IDE to open a hex editor
32 class HexEditMainWindow(E5MainWindow):
33 """
34 Class implementing the web browser main window.
35
36 @signal editorClosed() emitted after the window was requested to close down
37 """
38 editorClosed = pyqtSignal()
39
40 windows = []
41
42 def __init__(self, fileName="", parent=None, fromEric=False, project=None):
43 """
44 Constructor
45
46 @param fileName name of a file to load on startup (string)
47 @param parent parent widget of this window (QWidget)
48 @keyparam fromEric flag indicating whether it was called from within
49 eric6 (boolean)
50 @keyparam project reference to the project object (Project)
51 """
52 super(HexEditMainWindow, self).__init__(parent)
53 self.setObjectName("eric6_hex_editor")
54
55 self.fromEric = fromEric
56 self.setWindowIcon(UI.PixmapCache.getIcon("hexEditor.png"))
57
58 if not self.fromEric:
59 self.setStyle(Preferences.getUI("Style"),
60 Preferences.getUI("StyleSheet"))
61
62 self.__editor = HexEditWidget()
63 self.setCentralWidget(self.__editor)
64
65 g = Preferences.getGeometry("HexEditorGeometry")
66 if g.isEmpty():
67 s = QSize(600, 500)
68 self.resize(s)
69 else:
70 self.restoreGeometry(g)
71
72 self.__initActions()
73 self.__initMenus()
74 self.__initToolbars()
75 self.__createStatusBar()
76
77 self.__class__.windows.append(self)
78
79 self.__editor.currentAddressChanged.connect(self.__showAddress)
80 self.__editor.currentSizeChanged.connect(self.__showSize)
81 self.__editor.dataChanged.connect(self.__modificationChanged)
82 self.__editor.overwriteModeChanged.connect(self.__showEditMode)
83 self.__editor.readOnlyChanged.connect(self.__showReadOnlyMode)
84
85 self.__project = project
86 self.__lastOpenPath = ""
87 self.__lastSavePath = ""
88
89 self.__setCurrentFile("")
90 if fileName:
91 self.__loadHexFile(fileName)
92
93 self.__checkActions()
94
95 def __initActions(self):
96 """
97 Private method to define the user interface actions.
98 """
99 # list of all actions
100 self.__actions = []
101
102 self.__initFileActions()
103 self.__initEditActions()
104 ## self.__initViewActions()
105 ## self.__initToolsActions()
106 self.__initHelpActions()
107
108 def __initFileActions(self):
109 """
110 Private method to define the file related user interface actions.
111 """
112 self.newWindowAct = E5Action(
113 self.tr('New Window'),
114 UI.PixmapCache.getIcon("newWindow.png"),
115 self.tr('New &Window'),
116 0, 0, self, 'hexEditor_file_new_window')
117 self.newWindowAct.setStatusTip(self.tr(
118 'Open a binary file for editing in a new hex editor window'))
119 self.newWindowAct.setWhatsThis(self.tr(
120 """<b>New Window</b>"""
121 """<p>This opens a binary file for editing in a new hex editor"""
122 """ window.</p>"""
123 ))
124 self.newWindowAct.triggered.connect(self.__openHexFileNewWindow)
125 self.__actions.append(self.newWindowAct)
126
127 self.openAct = E5Action(
128 self.tr('Open'),
129 UI.PixmapCache.getIcon("open.png"),
130 self.tr('&Open...'),
131 QKeySequence(self.tr("Ctrl+O", "File|Open")),
132 0, self, 'hexEditor_file_open')
133 self.openAct.setStatusTip(self.tr('Open a binary file for editing'))
134 self.openAct.setWhatsThis(self.tr(
135 """<b>Open File</b>"""
136 """<p>This opens a binary file for editing."""
137 """ It pops up a file selection dialog.</p>"""
138 ))
139 self.openAct.triggered.connect(self.__openHexFile)
140 self.__actions.append(self.openAct)
141
142 self.openReadOnlyAct = E5Action(
143 self.tr('Open Read Only'),
144 self.tr('Open Read Only...'),
145 0, 0, self, 'hexEditor_file_open_read_only')
146 self.openReadOnlyAct.setStatusTip(self.tr(
147 'Open a binary file for viewing'))
148 self.openReadOnlyAct.setWhatsThis(self.tr(
149 """<b>Open Read Only</b>"""
150 """<p>This opens a binary file for viewing (i.e. in read only"""
151 """ mode). It pops up a file selection dialog.</p>"""
152 ))
153 self.openReadOnlyAct.triggered.connect(self.__openHexFileReadOnly)
154 self.__actions.append(self.openReadOnlyAct)
155
156 self.saveAct = E5Action(
157 self.tr('Save'),
158 UI.PixmapCache.getIcon("fileSave.png"),
159 self.tr('&Save'),
160 QKeySequence(self.tr("Ctrl+S", "File|Save")),
161 0, self, 'hexEditor_file_save')
162 self.saveAct.setStatusTip(self.tr('Save the current binary file'))
163 self.saveAct.setWhatsThis(self.tr(
164 """<b>Save File</b>"""
165 """<p>Save the contents of the hex editor window.</p>"""
166 ))
167 self.saveAct.triggered.connect(self.__saveHexFile)
168 self.__actions.append(self.saveAct)
169
170 self.saveAsAct = E5Action(
171 self.tr('Save As'),
172 UI.PixmapCache.getIcon("fileSaveAs.png"),
173 self.tr('Save &As...'),
174 QKeySequence(self.tr("Shift+Ctrl+S", "File|Save As")),
175 0, self, 'hexEditor_file_save_as')
176 self.saveAsAct.setStatusTip(
177 self.tr('Save the current binary data to a new file'))
178 self.saveAsAct.setWhatsThis(self.tr(
179 """<b>Save As...</b>"""
180 """<p>Saves the current binary data to a new file.</p>"""
181 ))
182 self.saveAsAct.triggered.connect(self.__saveHexFileAs)
183 self.__actions.append(self.saveAsAct)
184
185 self.saveReadableAct = E5Action(
186 self.tr('Save As Readable'),
187 self.tr('Save As &Readable...'),
188 0, 0, self, 'hexEditor_file_save_readable')
189 self.saveReadableAct.setStatusTip(
190 self.tr('Save the current binary data to a new file in a readable'
191 ' format'))
192 self.saveReadableAct.setWhatsThis(self.tr(
193 """<b>Save As Readable...</b>"""
194 """<p>Saves the current binary data to a new file in a readable"""
195 """ format.</p>"""
196 ))
197 self.saveReadableAct.triggered.connect(self.__saveHexFileReadable)
198 self.__actions.append(self.saveReadableAct)
199
200 self.closeAct = E5Action(
201 self.tr('Close'),
202 UI.PixmapCache.getIcon("close.png"),
203 self.tr('&Close'),
204 QKeySequence(self.tr("Ctrl+W", "File|Close")),
205 0, self, 'hexEditor_file_close')
206 self.closeAct.setStatusTip(self.tr(
207 'Close the current hex editor window'))
208 self.closeAct.setWhatsThis(self.tr(
209 """<b>Close</b>"""
210 """<p>Closes the current hex editor window.</p>"""
211 ))
212 self.closeAct.triggered.connect(self.close)
213 self.__actions.append(self.closeAct)
214
215 self.closeAllAct = E5Action(
216 self.tr('Close All'),
217 self.tr('Close &All'),
218 0, 0, self, 'hexEditor_file_close_all')
219 self.closeAllAct.setStatusTip(self.tr(
220 'Close all hex editor windows'))
221 self.closeAllAct.setWhatsThis(self.tr(
222 """<b>Close All</b>"""
223 """<p>Closes all hex editor windows.</p>"""
224 ))
225 self.closeAllAct.triggered.connect(self.__closeAll)
226 self.__actions.append(self.closeAllAct)
227
228 self.closeOthersAct = E5Action(
229 self.tr('Close Others'),
230 self.tr('Close Others'),
231 0, 0, self, 'hexEditor_file_close_others')
232 self.closeOthersAct.setStatusTip(self.tr(
233 'Close all hex other editor windows'))
234 self.closeOthersAct.setWhatsThis(self.tr(
235 """<b>Close Others</b>"""
236 """<p>Closes all other hex editor windows.</p>"""
237 ))
238 self.closeOthersAct.triggered.connect(self.__closeOthers)
239 self.__actions.append(self.closeOthersAct)
240
241 self.exitAct = E5Action(
242 self.tr('Quit'),
243 UI.PixmapCache.getIcon("exit.png"),
244 self.tr('&Quit'),
245 QKeySequence(self.tr("Ctrl+Q", "File|Quit")),
246 0, self, 'hexEditor_file_quit')
247 self.exitAct.setStatusTip(self.tr('Quit the hex editor'))
248 self.exitAct.setWhatsThis(self.tr(
249 """<b>Quit</b>"""
250 """<p>Quit the hex editor.</p>"""
251 ))
252 if not self.fromEric:
253 self.exitAct.triggered.connect(self.__closeAll)
254 self.__actions.append(self.exitAct)
255
256 def __initEditActions(self):
257 """
258 Private method to create the Edit actions.
259 """
260 self.undoAct = E5Action(
261 self.tr('Undo'),
262 UI.PixmapCache.getIcon("editUndo.png"),
263 self.tr('&Undo'),
264 QKeySequence(self.tr("Ctrl+Z", "Edit|Undo")),
265 QKeySequence(self.tr("Alt+Backspace", "Edit|Undo")),
266 self, 'hexEditor_edit_undo')
267 self.undoAct.setStatusTip(self.tr('Undo the last change'))
268 self.undoAct.setWhatsThis(self.tr(
269 """<b>Undo</b>"""
270 """<p>Undo the last change done.</p>"""
271 ))
272 self.undoAct.triggered.connect(self.__editor.undo)
273 self.__actions.append(self.undoAct)
274
275 self.redoAct = E5Action(
276 self.tr('Redo'),
277 UI.PixmapCache.getIcon("editRedo.png"),
278 self.tr('&Redo'),
279 QKeySequence(self.tr("Ctrl+Shift+Z", "Edit|Redo")),
280 0, self, 'hexEditor_edit_redo')
281 self.redoAct.setStatusTip(self.tr('Redo the last change'))
282 self.redoAct.setWhatsThis(self.tr(
283 """<b>Redo</b>"""
284 """<p>Redo the last change done.</p>"""
285 ))
286 self.redoAct.triggered.connect(self.__editor.redo)
287 self.__actions.append(self.redoAct)
288
289 self.revertAct = E5Action(
290 self.tr('Revert to last saved state'),
291 self.tr('Re&vert to last saved state'),
292 QKeySequence(self.tr("Ctrl+Y", "Edit|Revert")),
293 0,
294 self, 'hexEditor_edit_revert')
295 self.revertAct.setStatusTip(self.tr('Revert to last saved state'))
296 self.revertAct.setWhatsThis(self.tr(
297 """<b>Revert to last saved state</b>"""
298 """<p>Undo all changes up to the last saved state of the"""
299 """ editor.</p>"""
300 ))
301 self.revertAct.triggered.connect(self.__editor.revertToUnmodified)
302 self.__actions.append(self.revertAct)
303
304 self.cutAct = E5Action(
305 self.tr('Cut'),
306 UI.PixmapCache.getIcon("editCut.png"),
307 self.tr('Cu&t'),
308 QKeySequence(self.tr("Ctrl+X", "Edit|Cut")),
309 QKeySequence(self.tr("Shift+Del", "Edit|Cut")),
310 self, 'hexEditor_edit_cut')
311 self.cutAct.setStatusTip(self.tr('Cut the selection'))
312 self.cutAct.setWhatsThis(self.tr(
313 """<b>Cut</b>"""
314 """<p>Cut the selected binary area to the clipboard.</p>"""
315 ))
316 self.cutAct.triggered.connect(self.__editor.cut)
317 self.__actions.append(self.cutAct)
318
319 self.copyAct = E5Action(
320 self.tr('Copy'),
321 UI.PixmapCache.getIcon("editCopy.png"),
322 self.tr('&Copy'),
323 QKeySequence(self.tr("Ctrl+C", "Edit|Copy")),
324 QKeySequence(self.tr("Ctrl+Ins", "Edit|Copy")),
325 self, 'hexEditor_edit_copy')
326 self.copyAct.setStatusTip(self.tr('Copy the selection'))
327 self.copyAct.setWhatsThis(self.tr(
328 """<b>Copy</b>"""
329 """<p>Copy the selected binary area to the clipboard.</p>"""
330 ))
331 self.copyAct.triggered.connect(self.__editor.copy)
332 self.__actions.append(self.copyAct)
333
334 self.pasteAct = E5Action(
335 self.tr('Paste'),
336 UI.PixmapCache.getIcon("editPaste.png"),
337 self.tr('&Paste'),
338 QKeySequence(self.tr("Ctrl+V", "Edit|Paste")),
339 QKeySequence(self.tr("Shift+Ins", "Edit|Paste")),
340 self, 'hexEditor_edit_paste')
341 self.pasteAct.setStatusTip(self.tr('Paste the clipboard contents'))
342 self.pasteAct.setWhatsThis(self.tr(
343 """<b>Paste</b>"""
344 """<p>Paste the clipboard contents.</p>"""
345 ))
346 self.pasteAct.triggered.connect(self.__editor.paste)
347 self.__actions.append(self.pasteAct)
348
349 self.selectAllAct = E5Action(
350 self.tr('Select All'),
351 UI.PixmapCache.getIcon("editSelectAll.png"),
352 self.tr('&Select All'),
353 QKeySequence(self.tr("Ctrl+A", "Edit|Select All")),
354 0,
355 self, 'hexEditor_edit_select_all')
356 self.selectAllAct.setStatusTip(self.tr(
357 'Select the complete binary data'))
358 self.selectAllAct.setWhatsThis(self.tr(
359 """<b>Select All</b>"""
360 """<p>Selects the complete binary data.</p>"""
361 ))
362 self.selectAllAct.triggered.connect(self.__editor.selectAll)
363 self.__actions.append(self.selectAllAct)
364
365 self.deselectAllAct = E5Action(
366 self.tr('Deselect all'),
367 self.tr('&Deselect all'),
368 QKeySequence(self.tr("Alt+Ctrl+A", "Edit|Deselect all")),
369 0,
370 self, 'hexEditor_edit_deselect_all')
371 self.deselectAllAct.setStatusTip(self.tr('Deselect all binary data'))
372 self.deselectAllAct.setWhatsThis(self.tr(
373 """<b>Deselect All</b>"""
374 """<p>Deselect all all binary data.</p>"""
375 ))
376 self.deselectAllAct.triggered.connect(self.__editor.deselectAll)
377 self.__actions.append(self.deselectAllAct)
378
379 self.saveSelectionReadableAct = E5Action(
380 self.tr('Save Selection Readable'),
381 self.tr('Save Selection Readable...'),
382 0, 0, self, 'hexEditor_edit_selection_save_readable')
383 self.saveSelectionReadableAct.setStatusTip(
384 self.tr('Save the binary data of the current selection to a file'
385 ' in a readable format'))
386 self.saveSelectionReadableAct.setWhatsThis(self.tr(
387 """<b>Save Selection Readable...</b>"""
388 """<p>Saves the binary data of the current selection to a file"""
389 """ in a readable format.</p>"""
390 ))
391 self.saveSelectionReadableAct.triggered.connect(
392 self.__saveSelectionReadable)
393 self.__actions.append(self.saveSelectionReadableAct)
394
395 self.readonlyAct = E5Action(
396 self.tr('Set Read Only'),
397 self.tr('Set Read Only'),
398 0, 0, self, 'hexEditor_edit_readonly', True)
399 self.readonlyAct.setStatusTip(self.tr(
400 'Change the edit mode to read only'))
401 self.readonlyAct.setWhatsThis(self.tr(
402 """<b>Set Read Only</b>"""
403 """<p>This changes the edit mode to read only (i.e. to view"""
404 """ mode).</p>"""
405 ))
406 self.readonlyAct.setChecked(False)
407 self.readonlyAct.toggled[bool].connect(self.__editor.setReadOnly)
408 self.__actions.append(self.readonlyAct)
409
410 self.redoAct.setEnabled(False)
411 self.__editor.canRedoChanged.connect(self.redoAct.setEnabled)
412
413 self.undoAct.setEnabled(False)
414 self.__editor.canUndoChanged.connect(self.undoAct.setEnabled)
415
416 self.revertAct.setEnabled(False)
417 self.__editor.dataChanged.connect(self.revertAct.setEnabled)
418
419 self.cutAct.setEnabled(False)
420 self.copyAct.setEnabled(False)
421 self.saveSelectionReadableAct.setEnabled(False)
422 self.__editor.selectionAvailable.connect(self.__checkActions)
423 self.__editor.selectionAvailable.connect(self.copyAct.setEnabled)
424 self.__editor.selectionAvailable.connect(
425 self.saveSelectionReadableAct.setEnabled)
426
427 def __initHelpActions(self):
428 """
429 Private method to create the Help actions.
430 """
431 self.aboutAct = E5Action(
432 self.tr('About'),
433 self.tr('&About'),
434 0, 0, self, 'hexEditor_help_about')
435 self.aboutAct.setStatusTip(self.tr(
436 'Display information about this software'))
437 self.aboutAct.setWhatsThis(self.tr(
438 """<b>About</b>"""
439 """<p>Display some information about this software.</p>"""))
440 self.aboutAct.triggered.connect(self.__about)
441 self.__actions.append(self.aboutAct)
442
443 self.aboutQtAct = E5Action(
444 self.tr('About Qt'),
445 self.tr('About &Qt'),
446 0, 0, self, 'hexEditor_help_about_qt')
447 self.aboutQtAct.setStatusTip(
448 self.tr('Display information about the Qt toolkit'))
449 self.aboutQtAct.setWhatsThis(self.tr(
450 """<b>About Qt</b>"""
451 """<p>Display some information about the Qt toolkit.</p>"""
452 ))
453 self.aboutQtAct.triggered.connect(self.__aboutQt)
454 self.__actions.append(self.aboutQtAct)
455
456 self.whatsThisAct = E5Action(
457 self.tr('What\'s This?'),
458 UI.PixmapCache.getIcon("whatsThis.png"),
459 self.tr('&What\'s This?'),
460 QKeySequence(self.tr("Shift+F1", "Help|What's This?'")),
461 0, self, 'hexEditor_help_whats_this')
462 self.whatsThisAct.setStatusTip(self.tr('Context sensitive help'))
463 self.whatsThisAct.setWhatsThis(self.tr(
464 """<b>Display context sensitive help</b>"""
465 """<p>In What's This? mode, the mouse cursor shows an arrow"""
466 """ with a question mark, and you can click on the interface"""
467 """ elements to get a short description of what they do and"""
468 """ how to use them. In dialogs, this feature can be accessed"""
469 """ using the context help button in the titlebar.</p>"""
470 ))
471 self.whatsThisAct.triggered.connect(self.__whatsThis)
472 self.__actions.append(self.whatsThisAct)
473
474 def __initMenus(self):
475 """
476 Private method to create the menus.
477 """
478 mb = self.menuBar()
479
480 menu = mb.addMenu(self.tr('&File'))
481 menu.setTearOffEnabled(True)
482 menu.addAction(self.newWindowAct)
483 menu.addAction(self.openAct)
484 menu.addAction(self.openReadOnlyAct)
485 menu.addSeparator()
486 menu.addAction(self.saveAct)
487 menu.addAction(self.saveAsAct)
488 menu.addAction(self.saveReadableAct)
489 menu.addSeparator()
490 menu.addAction(self.closeAct)
491 menu.addAction(self.closeOthersAct)
492 if self.fromEric:
493 menu.addAction(self.closeAllAct)
494 else:
495 menu.addSeparator()
496 menu.addAction(self.exitAct)
497
498 menu = mb.addMenu(self.tr("&Edit"))
499 menu.setTearOffEnabled(True)
500 menu.addAction(self.undoAct)
501 menu.addAction(self.redoAct)
502 menu.addAction(self.revertAct)
503 menu.addSeparator()
504 menu.addAction(self.cutAct)
505 menu.addAction(self.copyAct)
506 menu.addAction(self.pasteAct)
507 menu.addSeparator()
508 menu.addAction(self.selectAllAct)
509 menu.addAction(self.deselectAllAct)
510 menu.addAction(self.saveSelectionReadableAct)
511 menu.addSeparator()
512 menu.addAction(self.readonlyAct)
513
514 mb.addSeparator()
515
516 menu = mb.addMenu(self.tr("&Help"))
517 menu.addAction(self.aboutAct)
518 menu.addAction(self.aboutQtAct)
519 menu.addSeparator()
520 menu.addAction(self.whatsThisAct)
521
522 def __initToolbars(self):
523 """
524 Private method to create the toolbars.
525 """
526 filetb = self.addToolBar(self.tr("File"))
527 filetb.setObjectName("FileToolBar")
528 filetb.setIconSize(UI.Config.ToolBarIconSize)
529 filetb.addAction(self.newWindowAct)
530 filetb.addAction(self.openAct)
531 filetb.addSeparator()
532 filetb.addAction(self.saveAct)
533 filetb.addAction(self.saveAsAct)
534 filetb.addSeparator()
535 filetb.addAction(self.closeAct)
536 if not self.fromEric:
537 filetb.addAction(self.exitAct)
538
539 edittb = self.addToolBar(self.tr("Edit"))
540 edittb.setObjectName("EditToolBar")
541 edittb.setIconSize(UI.Config.ToolBarIconSize)
542 edittb.addAction(self.undoAct)
543 edittb.addAction(self.redoAct)
544 edittb.addSeparator()
545 edittb.addAction(self.cutAct)
546 edittb.addAction(self.copyAct)
547 edittb.addAction(self.pasteAct)
548
549 helptb = self.addToolBar(self.tr("Help"))
550 helptb.setObjectName("HelpToolBar")
551 helptb.setIconSize(UI.Config.ToolBarIconSize)
552 helptb.addAction(self.whatsThisAct)
553
554 def __createStatusBar(self):
555 """
556 Private method to initialize the status bar.
557 """
558 self.__statusBar = self.statusBar()
559 self.__statusBar.setSizeGripEnabled(True)
560
561 self.__sbEditMode = QLabel(self.__statusBar)
562 self.__statusBar.addPermanentWidget(self.__sbEditMode)
563 self.__sbEditMode.setWhatsThis(self.tr(
564 """<p>This part of the status bar displays the edit mode.</p>"""
565 ))
566
567 self.__sbReadOnly = QLabel(self.__statusBar)
568 self.__statusBar.addPermanentWidget(self.__sbReadOnly)
569 self.__sbReadOnly.setWhatsThis(self.tr(
570 """<p>This part of the status bar displays the read"""
571 """ only mode.</p>"""
572 ))
573
574 self.__sbAddress = QLabel(self.__statusBar)
575 self.__statusBar.addPermanentWidget(self.__sbAddress)
576 self.__sbAddress.setWhatsThis(self.tr(
577 """<p>This part of the status bar displays the cursor"""
578 """ address.</p>"""
579 ))
580
581 self.__showEditMode(self.__editor.overwriteMode())
582 self.__showReadOnlyMode(self.__editor.isReadOnly())
583
584 self.__sbSize = QLabel(self.__statusBar)
585 self.__statusBar.addPermanentWidget(self.__sbSize)
586 self.__sbSize.setWhatsThis(self.tr(
587 """<p>This part of the status bar displays the size of the"""
588 """ binary data.</p>"""
589 ))
590
591 @pyqtSlot(int)
592 def __showAddress(self, address):
593 """
594 Private slot to show the address of the cursor position.
595
596 @param address address of the cursor
597 @type int
598 """
599 self.__sbAddress.setText(self.tr("Address: {0:#x}").format(address))
600
601 @pyqtSlot(bool)
602 def __showReadOnlyMode(self, on):
603 """
604 Private slot to show the read only mode.
605
606 @param on flag indicating the read only state
607 @type bool
608 """
609 self.__sbReadOnly.setText(self.tr("ro") if on else self.tr("rw"))
610
611 @pyqtSlot(bool)
612 def __showEditMode(self, overwrite):
613 """
614 Private slot to show the edit mode.
615
616 @param overwrite flag indicating overwrite mode
617 @type bool
618 """
619 self.__sbEditMode.setText(
620 self.tr("Overwrite") if overwrite else self.tr("Insert"))
621
622 @pyqtSlot(int)
623 def __showSize(self, size):
624 """
625 Private slot to show the binary data size.
626
627 @param size size of the binary data
628 @type int
629 """
630 self.__sbSize.setText(self.tr("Size: {0:n}").format(size))
631
632 def closeEvent(self, evt):
633 """
634 Protected event handler for the close event.
635
636 @param evt reference to the close event
637 <br />This event is simply accepted after the history has been
638 saved and all window references have been deleted.
639 @type QCloseEvent
640 """
641 if self.__maybeSave():
642 Preferences.setGeometry("HexEditorGeometry", self.saveGeometry())
643
644 try:
645 if self.fromEric or len(self.__class__.windows) > 1:
646 del self.__class__.windows[
647 self.__class__.windows.index(self)]
648 except ValueError:
649 pass
650
651 if not self.fromEric:
652 Preferences.syncPreferences()
653
654 evt.accept()
655 self.editorClosed.emit()
656 else:
657 evt.ignore()
658
659 def __openHexFileNewWindow(self):
660 """
661 Private slot called to open a binary file in new hex editor window.
662 """
663 if not self.__lastOpenPath:
664 if self.__project and self.__project.isOpen():
665 self.__lastOpenPath = self.__project.getProjectPath()
666
667 fileName = E5FileDialog.getOpenFileName(
668 self,
669 self.tr("Open binary file in new window"),
670 self.__lastOpenPath,
671 self.tr("All Files (*)"))
672 if fileName:
673 he = HexEditMainWindow(fileName=fileName,
674 parent=self.parent(),
675 fromEric=self.fromEric,
676 project=self.__project)
677 he.setRecentPaths("", self.__lastSavePath)
678 he.show()
679
680 def __maybeSave(self):
681 """
682 Private method to ask the user to save the file, if it was modified.
683
684 @return flag indicating, if it is ok to continue
685 @rtype bool
686 """
687 if self.__editor.isModified():
688 ret = E5MessageBox.okToClearData(
689 self,
690 self.tr("eric6 Hex Editor"),
691 self.tr("""The loaded file has unsaved changes."""),
692 self.__saveHexFile)
693 if not ret:
694 return False
695 return True
696
697 def __loadHexFile(self, fileName):
698 """
699 Private method to load a binary file.
700
701 @param fileName name of the binary file to load
702 @type str
703 """
704 file = QFile(fileName)
705 if not file.exists():
706 E5MessageBox.warning(
707 self, self.tr("eric6 Hex Editor"),
708 self.tr("The file '{0}' does not exist.")
709 .format(fileName))
710 return
711
712 if not file.open(QFile.ReadOnly):
713 E5MessageBox.warning(
714 self, self.tr("eric6 Hex Editor"),
715 self.tr("Cannot read file '{0}:\n{1}.")
716 .format(fileName, file.errorString()))
717 return
718
719 data = file.readAll()
720 file.close()
721
722 self.__lastOpenPath = os.path.dirname(fileName)
723 self.__editor.setData(data)
724 self.__setCurrentFile(fileName)
725
726 def __openHexFile(self):
727 """
728 Private slot to open a binary file.
729 """
730 if self.__maybeSave():
731 if not self.__lastOpenPath:
732 if self.__project and self.__project.isOpen():
733 self.__lastOpenPath = self.__project.getProjectPath()
734
735 fileName = E5FileDialog.getOpenFileName(
736 self,
737 self.tr("Open binary file"),
738 self.__lastOpenPath,
739 self.tr("All Files (*)"))
740 if fileName:
741 self.__loadHexFile(fileName)
742 self.__checkActions()
743
744 def __openHexFileReadOnly(self):
745 """
746 Private slot to open a binary file in read only mode.
747 """
748 self.__openHexFile()
749 self.__editor.setReadOnly(True)
750 self.__checkActions()
751
752 def __saveHexFile(self):
753 """
754 Private method to save a binary file.
755
756 @return flag indicating success
757 @rtype bool
758 """
759 if not self.__fileName:
760 ok = self.__saveHexFileAs()
761 else:
762 ok = self.__saveHexDataFile(self.__fileName)
763
764 if ok:
765 self.__editor.undoStack().setClean()
766
767 return ok
768
769 def __saveHexFileAs(self):
770 """
771 Private method to save the data to a new file.
772
773 @return flag indicating success
774 @rtype bool
775 """
776 if not self.__lastSavePath:
777 if self.__project and self.__project.isOpen():
778 self.__lastSavePath = self.__project.getProjectPath()
779 if not self.__lastSavePath and self.__lastOpenPath:
780 self.__lastSavePath = self.__lastOpenPath
781
782 fileName = E5FileDialog.getSaveFileName(
783 self,
784 self.tr("Save binary file"),
785 self.__lastSavePath,
786 self.tr("All Files (*)"),
787 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
788 if not fileName:
789 return False
790
791 if QFileInfo(fileName).exists():
792 res = E5MessageBox.yesNo(
793 self,
794 self.tr("Save binary file"),
795 self.tr("<p>The file <b>{0}</b> already exists."
796 " Overwrite it?</p>").format(fileName),
797 icon=E5MessageBox.Warning)
798 if not res:
799 return False
800
801 self.__lastSavePath = os.path.dirname(fileName)
802
803 return self.__saveHexDataFile(fileName)
804
805 def __saveHexDataFile(self, fileName):
806 """
807 Private method to save the binary data to a file.
808
809 @param fileName name of the file to write to
810 @type str
811 @return flag indicating success
812 @rtype bool
813 """
814 file = QFile(fileName)
815 if not file.open(QFile.WriteOnly):
816 E5MessageBox.warning(
817 self, self.tr("eric6 Hex Editor"),
818 self.tr("Cannot write file '{0}:\n{1}.")
819 .format(fileName, file.errorString()))
820
821 self.__checkActions()
822
823 return False
824
825 res = file.write(self.__editor.data()) != -1
826 file.close()
827
828 if not res:
829 E5MessageBox.warning(
830 self, self.tr("eric6 Hex Editor"),
831 self.tr("Cannot write file '{0}:\n{1}.")
832 .format(fileName, file.errorString()))
833
834 self.__checkActions()
835
836 return False
837
838 self.__editor.setModified(False, setCleanState=True)
839
840 self.__setCurrentFile(fileName)
841 self.__statusBar.showMessage(self.tr("File saved"), 2000)
842
843 self.__checkActions()
844
845 return True
846
847 def __saveHexFileReadable(self, selectionOnly=False):
848 """
849 Private method to save the binary data in readable format.
850
851 @param selectionOnly flag indicating to save the selection only
852 @type bool
853 """
854 savePath = self.__lastSavePath
855 if not savePath:
856 if self.__project and self.__project.isOpen():
857 savePath = self.__project.getProjectPath()
858 if not savePath and self.__lastOpenPath:
859 savePath = self.__lastOpenPath
860
861 fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
862 self,
863 self.tr("Save to readable file"),
864 savePath,
865 self.tr("Text Files (*.txt);;All Files (*)"),
866 self.tr("Text Files (*.txt)"),
867 E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
868 if not fileName:
869 return
870
871 ext = QFileInfo(fileName).suffix()
872 if not ext:
873 ex = selectedFilter.split("(*")[1].split(")")[0]
874 if ex:
875 fileName += ex
876 if QFileInfo(fileName).exists():
877 res = E5MessageBox.yesNo(
878 self,
879 self.tr("Save to readable file"),
880 self.tr("<p>The file <b>{0}</b> already exists."
881 " Overwrite it?</p>").format(fileName),
882 icon=E5MessageBox.Warning)
883 if not res:
884 return
885
886 file = QFile(fileName)
887 if not file.open(QFile.WriteOnly):
888 E5MessageBox.warning(
889 self, self.tr("eric6 Hex Editor"),
890 self.tr("Cannot write file '{0}:\n{1}.")
891 .format(fileName, file.errorString()))
892 return
893
894 if selectionOnly:
895 readableData = self.__editor.selectionToReadableString()
896 else:
897 readableData = self.__editor.toReadableString()
898 res = file.write(readableData.encode("latin1")) != -1
899 file.close()
900
901 if not res:
902 E5MessageBox.warning(
903 self, self.tr("eric6 Hex Editor"),
904 self.tr("Cannot write file '{0}:\n{1}.")
905 .format(fileName, file.errorString()))
906 return
907
908 self.__statusBar.showMessage(self.tr("File saved"), 2000)
909
910 def __saveSelectionReadable(self):
911 """
912 Private method to save the data of the current selection in readable
913 format.
914 """
915 self.__saveHexFileReadable(selectionOnly=True)
916
917 def __closeAll(self):
918 """
919 Private slot to close all windows.
920 """
921 self.__closeOthers()
922 self.close()
923
924 def __closeOthers(self):
925 """
926 Private slot to close all other windows.
927 """
928 for win in self.__class__.windows[:]:
929 if win != self:
930 win.close()
931
932 def __setCurrentFile(self, fileName):
933 """
934 Private method to register the file name of the current file.
935
936 @param fileName name of the file to register
937 @type str
938 """
939 self.__fileName = fileName
940
941 if not self.__fileName:
942 shownName = self.tr("Untitled")
943 else:
944 shownName = self.__strippedName(self.__fileName)
945
946 self.setWindowTitle(self.tr("{0}[*] - {1}")
947 .format(shownName, self.tr("Hex Editor")))
948
949 self.setWindowModified(self.__editor.isModified())
950
951 def __strippedName(self, fullFileName):
952 """
953 Private method to return the filename part of the given path.
954
955 @param fullFileName full pathname of the given file
956 @type str
957 @return filename part
958 @rtype str
959 """
960 return QFileInfo(fullFileName).fileName()
961
962 def setRecentPaths(self, openPath, savePath):
963 """
964 Public method to set the last open and save paths.
965
966 @param openPath least recently used open path
967 @type str
968 @param savePath least recently used save path
969 @type str
970 """
971 if openPath:
972 self.__lastOpenPath = openPath
973 if savePath:
974 self.__lastSavePath = savePath
975
976 @pyqtSlot()
977 def __checkActions(self):
978 """
979 Private slot to check some actions for their enable/disable status.
980 """
981 self.saveAct.setEnabled(self.__editor.isModified())
982
983 self.cutAct.setEnabled(not self.__editor.isReadOnly() and
984 self.__editor.hasSelection())
985 self.pasteAct.setEnabled(not self.__editor.isReadOnly())
986
987 @pyqtSlot(bool)
988 def __modificationChanged(self, m):
989 """
990 Private slot to handle the dataChanged signal.
991
992 @param m modification status
993 @type bool
994 """
995 self.setWindowModified(m)
996 self.__checkActions()
997
998 def __about(self):
999 """
1000 Private slot to show a little About message.
1001 """
1002 E5MessageBox.about(
1003 self, self.tr("About eric6 Hex Editor"),
1004 self.tr("The eric6 Hex Editor is a simple editor component"
1005 " to edit binary files."))
1006
1007 def __aboutQt(self):
1008 """
1009 Private slot to handle the About Qt dialog.
1010 """
1011 E5MessageBox.aboutQt(self, "eric6 Hex Editor")
1012
1013 def __whatsThis(self):
1014 """
1015 Private slot called in to enter Whats This mode.
1016 """
1017 QWhatsThis.enterWhatsThisMode()

eric ide

mercurial