HexEdit/HexEditMainWindow.py

changeset 4650
b1ca3bcde70b
child 4651
7f3f276d3bf3
diff -r bbc8b2de9173 -r b1ca3bcde70b HexEdit/HexEditMainWindow.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HexEdit/HexEditMainWindow.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,1017 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the hex editor main window.
+"""
+
+from __future__ import unicode_literals
+
+import os
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, QFile, QFileInfo, QSize
+from PyQt5.QtGui import QKeySequence
+from PyQt5.QtWidgets import QWhatsThis, QLabel
+
+from E5Gui.E5Action import E5Action
+from E5Gui.E5MainWindow import E5MainWindow
+from E5Gui import E5FileDialog, E5MessageBox
+
+from .HexEditWidget import HexEditWidget
+
+import UI.PixmapCache
+import UI.Config
+
+import Preferences
+
+
+# TODO: implement a configuration page for the hex editor
+# TODO: implement an action in the eric IDE to open a hex editor
+class HexEditMainWindow(E5MainWindow):
+    """
+    Class implementing the web browser main window.
+    
+    @signal editorClosed() emitted after the window was requested to close down
+    """
+    editorClosed = pyqtSignal()
+    
+    windows = []
+    
+    def __init__(self, fileName="", parent=None, fromEric=False, project=None):
+        """
+        Constructor
+        
+        @param fileName name of a file to load on startup (string)
+        @param parent parent widget of this window (QWidget)
+        @keyparam fromEric flag indicating whether it was called from within
+            eric6 (boolean)
+        @keyparam project reference to the project object (Project)
+        """
+        super(HexEditMainWindow, self).__init__(parent)
+        self.setObjectName("eric6_hex_editor")
+        
+        self.fromEric = fromEric
+        self.setWindowIcon(UI.PixmapCache.getIcon("hexEditor.png"))
+        
+        if not self.fromEric:
+            self.setStyle(Preferences.getUI("Style"),
+                          Preferences.getUI("StyleSheet"))
+        
+        self.__editor = HexEditWidget()
+        self.setCentralWidget(self.__editor)
+        
+        g = Preferences.getGeometry("HexEditorGeometry")
+        if g.isEmpty():
+            s = QSize(600, 500)
+            self.resize(s)
+        else:
+            self.restoreGeometry(g)
+        
+        self.__initActions()
+        self.__initMenus()
+        self.__initToolbars()
+        self.__createStatusBar()
+        
+        self.__class__.windows.append(self)
+        
+        self.__editor.currentAddressChanged.connect(self.__showAddress)
+        self.__editor.currentSizeChanged.connect(self.__showSize)
+        self.__editor.dataChanged.connect(self.__modificationChanged)
+        self.__editor.overwriteModeChanged.connect(self.__showEditMode)
+        self.__editor.readOnlyChanged.connect(self.__showReadOnlyMode)
+        
+        self.__project = project
+        self.__lastOpenPath = ""
+        self.__lastSavePath = ""
+        
+        self.__setCurrentFile("")
+        if fileName:
+            self.__loadHexFile(fileName)
+        
+        self.__checkActions()
+    
+    def __initActions(self):
+        """
+        Private method to define the user interface actions.
+        """
+        # list of all actions
+        self.__actions = []
+        
+        self.__initFileActions()
+        self.__initEditActions()
+##        self.__initViewActions()
+##        self.__initToolsActions()
+        self.__initHelpActions()
+        
+    def __initFileActions(self):
+        """
+        Private method to define the file related user interface actions.
+        """
+        self.newWindowAct = E5Action(
+            self.tr('New Window'),
+            UI.PixmapCache.getIcon("newWindow.png"),
+            self.tr('New &Window'),
+            0, 0, self, 'hexEditor_file_new_window')
+        self.newWindowAct.setStatusTip(self.tr(
+            'Open a binary file for editing in a new hex editor window'))
+        self.newWindowAct.setWhatsThis(self.tr(
+            """<b>New Window</b>"""
+            """<p>This opens a binary file for editing in a new hex editor"""
+            """ window.</p>"""
+        ))
+        self.newWindowAct.triggered.connect(self.__openHexFileNewWindow)
+        self.__actions.append(self.newWindowAct)
+        
+        self.openAct = E5Action(
+            self.tr('Open'),
+            UI.PixmapCache.getIcon("open.png"),
+            self.tr('&Open...'),
+            QKeySequence(self.tr("Ctrl+O", "File|Open")),
+            0, self, 'hexEditor_file_open')
+        self.openAct.setStatusTip(self.tr('Open a binary file for editing'))
+        self.openAct.setWhatsThis(self.tr(
+            """<b>Open File</b>"""
+            """<p>This opens a binary file for editing."""
+            """ It pops up a file selection dialog.</p>"""
+        ))
+        self.openAct.triggered.connect(self.__openHexFile)
+        self.__actions.append(self.openAct)
+        
+        self.openReadOnlyAct = E5Action(
+            self.tr('Open Read Only'),
+            self.tr('Open Read Only...'),
+            0, 0, self, 'hexEditor_file_open_read_only')
+        self.openReadOnlyAct.setStatusTip(self.tr(
+            'Open a binary file for viewing'))
+        self.openReadOnlyAct.setWhatsThis(self.tr(
+            """<b>Open Read Only</b>"""
+            """<p>This opens a binary file for viewing (i.e. in read only"""
+            """ mode). It pops up a file selection dialog.</p>"""
+        ))
+        self.openReadOnlyAct.triggered.connect(self.__openHexFileReadOnly)
+        self.__actions.append(self.openReadOnlyAct)
+        
+        self.saveAct = E5Action(
+            self.tr('Save'),
+            UI.PixmapCache.getIcon("fileSave.png"),
+            self.tr('&Save'),
+            QKeySequence(self.tr("Ctrl+S", "File|Save")),
+            0, self, 'hexEditor_file_save')
+        self.saveAct.setStatusTip(self.tr('Save the current binary file'))
+        self.saveAct.setWhatsThis(self.tr(
+            """<b>Save File</b>"""
+            """<p>Save the contents of the hex editor window.</p>"""
+        ))
+        self.saveAct.triggered.connect(self.__saveHexFile)
+        self.__actions.append(self.saveAct)
+        
+        self.saveAsAct = E5Action(
+            self.tr('Save As'),
+            UI.PixmapCache.getIcon("fileSaveAs.png"),
+            self.tr('Save &As...'),
+            QKeySequence(self.tr("Shift+Ctrl+S", "File|Save As")),
+            0, self, 'hexEditor_file_save_as')
+        self.saveAsAct.setStatusTip(
+            self.tr('Save the current binary data to a new file'))
+        self.saveAsAct.setWhatsThis(self.tr(
+            """<b>Save As...</b>"""
+            """<p>Saves the current binary data to a new file.</p>"""
+        ))
+        self.saveAsAct.triggered.connect(self.__saveHexFileAs)
+        self.__actions.append(self.saveAsAct)
+        
+        self.saveReadableAct = E5Action(
+            self.tr('Save As Readable'),
+            self.tr('Save As &Readable...'),
+            0, 0, self, 'hexEditor_file_save_readable')
+        self.saveReadableAct.setStatusTip(
+            self.tr('Save the current binary data to a new file in a readable'
+                    ' format'))
+        self.saveReadableAct.setWhatsThis(self.tr(
+            """<b>Save As Readable...</b>"""
+            """<p>Saves the current binary data to a new file in a readable"""
+            """ format.</p>"""
+        ))
+        self.saveReadableAct.triggered.connect(self.__saveHexFileReadable)
+        self.__actions.append(self.saveReadableAct)
+        
+        self.closeAct = E5Action(
+            self.tr('Close'),
+            UI.PixmapCache.getIcon("close.png"),
+            self.tr('&Close'),
+            QKeySequence(self.tr("Ctrl+W", "File|Close")),
+            0, self, 'hexEditor_file_close')
+        self.closeAct.setStatusTip(self.tr(
+            'Close the current hex editor window'))
+        self.closeAct.setWhatsThis(self.tr(
+            """<b>Close</b>"""
+            """<p>Closes the current hex editor window.</p>"""
+        ))
+        self.closeAct.triggered.connect(self.close)
+        self.__actions.append(self.closeAct)
+        
+        self.closeAllAct = E5Action(
+            self.tr('Close All'),
+            self.tr('Close &All'),
+            0, 0, self, 'hexEditor_file_close_all')
+        self.closeAllAct.setStatusTip(self.tr(
+            'Close all hex editor windows'))
+        self.closeAllAct.setWhatsThis(self.tr(
+            """<b>Close All</b>"""
+            """<p>Closes all hex editor windows.</p>"""
+        ))
+        self.closeAllAct.triggered.connect(self.__closeAll)
+        self.__actions.append(self.closeAllAct)
+        
+        self.closeOthersAct = E5Action(
+            self.tr('Close Others'),
+            self.tr('Close Others'),
+            0, 0, self, 'hexEditor_file_close_others')
+        self.closeOthersAct.setStatusTip(self.tr(
+            'Close all hex other editor windows'))
+        self.closeOthersAct.setWhatsThis(self.tr(
+            """<b>Close Others</b>"""
+            """<p>Closes all other hex editor windows.</p>"""
+        ))
+        self.closeOthersAct.triggered.connect(self.__closeOthers)
+        self.__actions.append(self.closeOthersAct)
+        
+        self.exitAct = E5Action(
+            self.tr('Quit'),
+            UI.PixmapCache.getIcon("exit.png"),
+            self.tr('&Quit'),
+            QKeySequence(self.tr("Ctrl+Q", "File|Quit")),
+            0, self, 'hexEditor_file_quit')
+        self.exitAct.setStatusTip(self.tr('Quit the hex editor'))
+        self.exitAct.setWhatsThis(self.tr(
+            """<b>Quit</b>"""
+            """<p>Quit the hex editor.</p>"""
+        ))
+        if not self.fromEric:
+            self.exitAct.triggered.connect(self.__closeAll)
+        self.__actions.append(self.exitAct)
+    
+    def __initEditActions(self):
+        """
+        Private method to create the Edit actions.
+        """
+        self.undoAct = E5Action(
+            self.tr('Undo'),
+            UI.PixmapCache.getIcon("editUndo.png"),
+            self.tr('&Undo'),
+            QKeySequence(self.tr("Ctrl+Z", "Edit|Undo")),
+            QKeySequence(self.tr("Alt+Backspace", "Edit|Undo")),
+            self, 'hexEditor_edit_undo')
+        self.undoAct.setStatusTip(self.tr('Undo the last change'))
+        self.undoAct.setWhatsThis(self.tr(
+            """<b>Undo</b>"""
+            """<p>Undo the last change done.</p>"""
+        ))
+        self.undoAct.triggered.connect(self.__editor.undo)
+        self.__actions.append(self.undoAct)
+        
+        self.redoAct = E5Action(
+            self.tr('Redo'),
+            UI.PixmapCache.getIcon("editRedo.png"),
+            self.tr('&Redo'),
+            QKeySequence(self.tr("Ctrl+Shift+Z", "Edit|Redo")),
+            0, self, 'hexEditor_edit_redo')
+        self.redoAct.setStatusTip(self.tr('Redo the last change'))
+        self.redoAct.setWhatsThis(self.tr(
+            """<b>Redo</b>"""
+            """<p>Redo the last change done.</p>"""
+        ))
+        self.redoAct.triggered.connect(self.__editor.redo)
+        self.__actions.append(self.redoAct)
+        
+        self.revertAct = E5Action(
+            self.tr('Revert to last saved state'),
+            self.tr('Re&vert to last saved state'),
+            QKeySequence(self.tr("Ctrl+Y", "Edit|Revert")),
+            0,
+            self, 'hexEditor_edit_revert')
+        self.revertAct.setStatusTip(self.tr('Revert to last saved state'))
+        self.revertAct.setWhatsThis(self.tr(
+            """<b>Revert to last saved state</b>"""
+            """<p>Undo all changes up to the last saved state of the"""
+            """ editor.</p>"""
+        ))
+        self.revertAct.triggered.connect(self.__editor.revertToUnmodified)
+        self.__actions.append(self.revertAct)
+        
+        self.cutAct = E5Action(
+            self.tr('Cut'),
+            UI.PixmapCache.getIcon("editCut.png"),
+            self.tr('Cu&t'),
+            QKeySequence(self.tr("Ctrl+X", "Edit|Cut")),
+            QKeySequence(self.tr("Shift+Del", "Edit|Cut")),
+            self, 'hexEditor_edit_cut')
+        self.cutAct.setStatusTip(self.tr('Cut the selection'))
+        self.cutAct.setWhatsThis(self.tr(
+            """<b>Cut</b>"""
+            """<p>Cut the selected binary area to the clipboard.</p>"""
+        ))
+        self.cutAct.triggered.connect(self.__editor.cut)
+        self.__actions.append(self.cutAct)
+        
+        self.copyAct = E5Action(
+            self.tr('Copy'),
+            UI.PixmapCache.getIcon("editCopy.png"),
+            self.tr('&Copy'),
+            QKeySequence(self.tr("Ctrl+C", "Edit|Copy")),
+            QKeySequence(self.tr("Ctrl+Ins", "Edit|Copy")),
+            self, 'hexEditor_edit_copy')
+        self.copyAct.setStatusTip(self.tr('Copy the selection'))
+        self.copyAct.setWhatsThis(self.tr(
+            """<b>Copy</b>"""
+            """<p>Copy the selected binary area to the clipboard.</p>"""
+        ))
+        self.copyAct.triggered.connect(self.__editor.copy)
+        self.__actions.append(self.copyAct)
+        
+        self.pasteAct = E5Action(
+            self.tr('Paste'),
+            UI.PixmapCache.getIcon("editPaste.png"),
+            self.tr('&Paste'),
+            QKeySequence(self.tr("Ctrl+V", "Edit|Paste")),
+            QKeySequence(self.tr("Shift+Ins", "Edit|Paste")),
+            self, 'hexEditor_edit_paste')
+        self.pasteAct.setStatusTip(self.tr('Paste the clipboard contents'))
+        self.pasteAct.setWhatsThis(self.tr(
+            """<b>Paste</b>"""
+            """<p>Paste the clipboard contents.</p>"""
+        ))
+        self.pasteAct.triggered.connect(self.__editor.paste)
+        self.__actions.append(self.pasteAct)
+        
+        self.selectAllAct = E5Action(
+            self.tr('Select All'),
+            UI.PixmapCache.getIcon("editSelectAll.png"),
+            self.tr('&Select All'),
+            QKeySequence(self.tr("Ctrl+A", "Edit|Select All")),
+            0,
+            self, 'hexEditor_edit_select_all')
+        self.selectAllAct.setStatusTip(self.tr(
+            'Select the complete binary data'))
+        self.selectAllAct.setWhatsThis(self.tr(
+            """<b>Select All</b>"""
+            """<p>Selects the complete binary data.</p>"""
+        ))
+        self.selectAllAct.triggered.connect(self.__editor.selectAll)
+        self.__actions.append(self.selectAllAct)
+        
+        self.deselectAllAct = E5Action(
+            self.tr('Deselect all'),
+            self.tr('&Deselect all'),
+            QKeySequence(self.tr("Alt+Ctrl+A", "Edit|Deselect all")),
+            0,
+            self, 'hexEditor_edit_deselect_all')
+        self.deselectAllAct.setStatusTip(self.tr('Deselect all binary data'))
+        self.deselectAllAct.setWhatsThis(self.tr(
+            """<b>Deselect All</b>"""
+            """<p>Deselect all all binary data.</p>"""
+        ))
+        self.deselectAllAct.triggered.connect(self.__editor.deselectAll)
+        self.__actions.append(self.deselectAllAct)
+        
+        self.saveSelectionReadableAct = E5Action(
+            self.tr('Save Selection Readable'),
+            self.tr('Save Selection Readable...'),
+            0, 0, self, 'hexEditor_edit_selection_save_readable')
+        self.saveSelectionReadableAct.setStatusTip(
+            self.tr('Save the binary data of the current selection to a file'
+                    ' in a readable format'))
+        self.saveSelectionReadableAct.setWhatsThis(self.tr(
+            """<b>Save Selection Readable...</b>"""
+            """<p>Saves the binary data of the current selection to a file"""
+            """ in a readable format.</p>"""
+        ))
+        self.saveSelectionReadableAct.triggered.connect(
+            self.__saveSelectionReadable)
+        self.__actions.append(self.saveSelectionReadableAct)
+        
+        self.readonlyAct = E5Action(
+            self.tr('Set Read Only'),
+            self.tr('Set Read Only'),
+            0, 0, self, 'hexEditor_edit_readonly', True)
+        self.readonlyAct.setStatusTip(self.tr(
+            'Change the edit mode to read only'))
+        self.readonlyAct.setWhatsThis(self.tr(
+            """<b>Set Read Only</b>"""
+            """<p>This changes the edit mode to read only (i.e. to view"""
+            """ mode).</p>"""
+        ))
+        self.readonlyAct.setChecked(False)
+        self.readonlyAct.toggled[bool].connect(self.__editor.setReadOnly)
+        self.__actions.append(self.readonlyAct)
+        
+        self.redoAct.setEnabled(False)
+        self.__editor.canRedoChanged.connect(self.redoAct.setEnabled)
+        
+        self.undoAct.setEnabled(False)
+        self.__editor.canUndoChanged.connect(self.undoAct.setEnabled)
+        
+        self.revertAct.setEnabled(False)
+        self.__editor.dataChanged.connect(self.revertAct.setEnabled)
+        
+        self.cutAct.setEnabled(False)
+        self.copyAct.setEnabled(False)
+        self.saveSelectionReadableAct.setEnabled(False)
+        self.__editor.selectionAvailable.connect(self.__checkActions)
+        self.__editor.selectionAvailable.connect(self.copyAct.setEnabled)
+        self.__editor.selectionAvailable.connect(
+            self.saveSelectionReadableAct.setEnabled)
+    
+    def __initHelpActions(self):
+        """
+        Private method to create the Help actions.
+        """
+        self.aboutAct = E5Action(
+            self.tr('About'),
+            self.tr('&About'),
+            0, 0, self, 'hexEditor_help_about')
+        self.aboutAct.setStatusTip(self.tr(
+            'Display information about this software'))
+        self.aboutAct.setWhatsThis(self.tr(
+            """<b>About</b>"""
+            """<p>Display some information about this software.</p>"""))
+        self.aboutAct.triggered.connect(self.__about)
+        self.__actions.append(self.aboutAct)
+        
+        self.aboutQtAct = E5Action(
+            self.tr('About Qt'),
+            self.tr('About &Qt'),
+            0, 0, self, 'hexEditor_help_about_qt')
+        self.aboutQtAct.setStatusTip(
+            self.tr('Display information about the Qt toolkit'))
+        self.aboutQtAct.setWhatsThis(self.tr(
+            """<b>About Qt</b>"""
+            """<p>Display some information about the Qt toolkit.</p>"""
+        ))
+        self.aboutQtAct.triggered.connect(self.__aboutQt)
+        self.__actions.append(self.aboutQtAct)
+        
+        self.whatsThisAct = E5Action(
+            self.tr('What\'s This?'),
+            UI.PixmapCache.getIcon("whatsThis.png"),
+            self.tr('&What\'s This?'),
+            QKeySequence(self.tr("Shift+F1", "Help|What's This?'")),
+            0, self, 'hexEditor_help_whats_this')
+        self.whatsThisAct.setStatusTip(self.tr('Context sensitive help'))
+        self.whatsThisAct.setWhatsThis(self.tr(
+            """<b>Display context sensitive help</b>"""
+            """<p>In What's This? mode, the mouse cursor shows an arrow"""
+            """ with a question mark, and you can click on the interface"""
+            """ elements to get a short description of what they do and"""
+            """ how to use them. In dialogs, this feature can be accessed"""
+            """ using the context help button in the titlebar.</p>"""
+        ))
+        self.whatsThisAct.triggered.connect(self.__whatsThis)
+        self.__actions.append(self.whatsThisAct)
+    
+    def __initMenus(self):
+        """
+        Private method to create the menus.
+        """
+        mb = self.menuBar()
+        
+        menu = mb.addMenu(self.tr('&File'))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.newWindowAct)
+        menu.addAction(self.openAct)
+        menu.addAction(self.openReadOnlyAct)
+        menu.addSeparator()
+        menu.addAction(self.saveAct)
+        menu.addAction(self.saveAsAct)
+        menu.addAction(self.saveReadableAct)
+        menu.addSeparator()
+        menu.addAction(self.closeAct)
+        menu.addAction(self.closeOthersAct)
+        if self.fromEric:
+            menu.addAction(self.closeAllAct)
+        else:
+            menu.addSeparator()
+            menu.addAction(self.exitAct)
+        
+        menu = mb.addMenu(self.tr("&Edit"))
+        menu.setTearOffEnabled(True)
+        menu.addAction(self.undoAct)
+        menu.addAction(self.redoAct)
+        menu.addAction(self.revertAct)
+        menu.addSeparator()
+        menu.addAction(self.cutAct)
+        menu.addAction(self.copyAct)
+        menu.addAction(self.pasteAct)
+        menu.addSeparator()
+        menu.addAction(self.selectAllAct)
+        menu.addAction(self.deselectAllAct)
+        menu.addAction(self.saveSelectionReadableAct)
+        menu.addSeparator()
+        menu.addAction(self.readonlyAct)
+        
+        mb.addSeparator()
+        
+        menu = mb.addMenu(self.tr("&Help"))
+        menu.addAction(self.aboutAct)
+        menu.addAction(self.aboutQtAct)
+        menu.addSeparator()
+        menu.addAction(self.whatsThisAct)
+    
+    def __initToolbars(self):
+        """
+        Private method to create the toolbars.
+        """
+        filetb = self.addToolBar(self.tr("File"))
+        filetb.setObjectName("FileToolBar")
+        filetb.setIconSize(UI.Config.ToolBarIconSize)
+        filetb.addAction(self.newWindowAct)
+        filetb.addAction(self.openAct)
+        filetb.addSeparator()
+        filetb.addAction(self.saveAct)
+        filetb.addAction(self.saveAsAct)
+        filetb.addSeparator()
+        filetb.addAction(self.closeAct)
+        if not self.fromEric:
+            filetb.addAction(self.exitAct)
+        
+        edittb = self.addToolBar(self.tr("Edit"))
+        edittb.setObjectName("EditToolBar")
+        edittb.setIconSize(UI.Config.ToolBarIconSize)
+        edittb.addAction(self.undoAct)
+        edittb.addAction(self.redoAct)
+        edittb.addSeparator()
+        edittb.addAction(self.cutAct)
+        edittb.addAction(self.copyAct)
+        edittb.addAction(self.pasteAct)
+        
+        helptb = self.addToolBar(self.tr("Help"))
+        helptb.setObjectName("HelpToolBar")
+        helptb.setIconSize(UI.Config.ToolBarIconSize)
+        helptb.addAction(self.whatsThisAct)
+    
+    def __createStatusBar(self):
+        """
+        Private method to initialize the status bar.
+        """
+        self.__statusBar = self.statusBar()
+        self.__statusBar.setSizeGripEnabled(True)
+        
+        self.__sbEditMode = QLabel(self.__statusBar)
+        self.__statusBar.addPermanentWidget(self.__sbEditMode)
+        self.__sbEditMode.setWhatsThis(self.tr(
+            """<p>This part of the status bar displays the edit mode.</p>"""
+        ))
+        
+        self.__sbReadOnly = QLabel(self.__statusBar)
+        self.__statusBar.addPermanentWidget(self.__sbReadOnly)
+        self.__sbReadOnly.setWhatsThis(self.tr(
+            """<p>This part of the status bar displays the read"""
+            """ only mode.</p>"""
+        ))
+
+        self.__sbAddress = QLabel(self.__statusBar)
+        self.__statusBar.addPermanentWidget(self.__sbAddress)
+        self.__sbAddress.setWhatsThis(self.tr(
+            """<p>This part of the status bar displays the cursor"""
+            """ address.</p>"""
+        ))
+        
+        self.__showEditMode(self.__editor.overwriteMode())
+        self.__showReadOnlyMode(self.__editor.isReadOnly())
+        
+        self.__sbSize = QLabel(self.__statusBar)
+        self.__statusBar.addPermanentWidget(self.__sbSize)
+        self.__sbSize.setWhatsThis(self.tr(
+            """<p>This part of the status bar displays the size of the"""
+            """ binary data.</p>"""
+        ))
+    
+    @pyqtSlot(int)
+    def __showAddress(self, address):
+        """
+        Private slot to show the address of the cursor position.
+        
+        @param address address of the cursor
+        @type int
+        """
+        self.__sbAddress.setText(self.tr("Address: {0:#x}").format(address))
+    
+    @pyqtSlot(bool)
+    def __showReadOnlyMode(self, on):
+        """
+        Private slot to show the read only mode.
+        
+        @param on flag indicating the read only state
+        @type bool
+        """
+        self.__sbReadOnly.setText(self.tr("ro") if on else self.tr("rw"))
+    
+    @pyqtSlot(bool)
+    def __showEditMode(self, overwrite):
+        """
+        Private slot to show the edit mode.
+        
+        @param overwrite flag indicating overwrite mode
+        @type bool
+        """
+        self.__sbEditMode.setText(
+            self.tr("Overwrite") if overwrite else self.tr("Insert"))
+    
+    @pyqtSlot(int)
+    def __showSize(self, size):
+        """
+        Private slot to show the binary data size.
+        
+        @param size size of the binary data
+        @type int
+        """
+        self.__sbSize.setText(self.tr("Size: {0:n}").format(size))
+    
+    def closeEvent(self, evt):
+        """
+        Protected event handler for the close event.
+        
+        @param evt reference to the close event
+            <br />This event is simply accepted after the history has been
+            saved and all window references have been deleted.
+        @type QCloseEvent
+        """
+        if self.__maybeSave():
+            Preferences.setGeometry("HexEditorGeometry", self.saveGeometry())
+            
+            try:
+                if self.fromEric or len(self.__class__.windows) > 1:
+                    del self.__class__.windows[
+                        self.__class__.windows.index(self)]
+            except ValueError:
+                pass
+            
+            if not self.fromEric:
+                Preferences.syncPreferences()
+            
+            evt.accept()
+            self.editorClosed.emit()
+        else:
+            evt.ignore()
+    
+    def __openHexFileNewWindow(self):
+        """
+        Private slot called to open a binary file in new hex editor window.
+        """
+        if not self.__lastOpenPath:
+            if self.__project and self.__project.isOpen():
+                self.__lastOpenPath = self.__project.getProjectPath()
+        
+        fileName = E5FileDialog.getOpenFileName(
+            self,
+            self.tr("Open binary file in new window"),
+            self.__lastOpenPath,
+            self.tr("All Files (*)"))
+        if fileName:
+            he = HexEditMainWindow(fileName=fileName,
+                                   parent=self.parent(),
+                                   fromEric=self.fromEric,
+                                   project=self.__project)
+            he.setRecentPaths("", self.__lastSavePath)
+            he.show()
+    
+    def __maybeSave(self):
+        """
+        Private method to ask the user to save the file, if it was modified.
+        
+        @return flag indicating, if it is ok to continue
+        @rtype bool
+        """
+        if self.__editor.isModified():
+            ret = E5MessageBox.okToClearData(
+                self,
+                self.tr("eric6 Hex Editor"),
+                self.tr("""The loaded file has unsaved changes."""),
+                self.__saveHexFile)
+            if not ret:
+                return False
+        return True
+    
+    def __loadHexFile(self, fileName):
+        """
+        Private method to load a binary file.
+        
+        @param fileName name of the binary file to load
+        @type str
+        """
+        file = QFile(fileName)
+        if not file.exists():
+            E5MessageBox.warning(
+                self, self.tr("eric6 Hex Editor"),
+                self.tr("The file '{0}' does not exist.")
+                .format(fileName))
+            return
+        
+        if not file.open(QFile.ReadOnly):
+            E5MessageBox.warning(
+                self, self.tr("eric6 Hex Editor"),
+                self.tr("Cannot read file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+            return
+        
+        data = file.readAll()
+        file.close()
+        
+        self.__lastOpenPath = os.path.dirname(fileName)
+        self.__editor.setData(data)
+        self.__setCurrentFile(fileName)
+    
+    def __openHexFile(self):
+        """
+        Private slot to open a binary file.
+        """
+        if self.__maybeSave():
+            if not self.__lastOpenPath:
+                if self.__project and self.__project.isOpen():
+                    self.__lastOpenPath = self.__project.getProjectPath()
+            
+            fileName = E5FileDialog.getOpenFileName(
+                self,
+                self.tr("Open binary file"),
+                self.__lastOpenPath,
+                self.tr("All Files (*)"))
+            if fileName:
+                self.__loadHexFile(fileName)
+        self.__checkActions()
+    
+    def __openHexFileReadOnly(self):
+        """
+        Private slot to open a binary file in read only mode.
+        """
+        self.__openHexFile()
+        self.__editor.setReadOnly(True)
+        self.__checkActions()
+    
+    def __saveHexFile(self):
+        """
+        Private method to save a binary file.
+        
+        @return flag indicating success
+        @rtype bool
+        """
+        if not self.__fileName:
+            ok = self.__saveHexFileAs()
+        else:
+            ok = self.__saveHexDataFile(self.__fileName)
+        
+        if ok:
+            self.__editor.undoStack().setClean()
+        
+        return ok
+    
+    def __saveHexFileAs(self):
+        """
+        Private method to save the data to a new file.
+        
+        @return flag indicating success
+        @rtype bool
+        """
+        if not self.__lastSavePath:
+            if self.__project and self.__project.isOpen():
+                self.__lastSavePath = self.__project.getProjectPath()
+        if not self.__lastSavePath and self.__lastOpenPath:
+            self.__lastSavePath = self.__lastOpenPath
+        
+        fileName = E5FileDialog.getSaveFileName(
+            self,
+            self.tr("Save binary file"),
+            self.__lastSavePath,
+            self.tr("All Files (*)"),
+            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
+        if not fileName:
+            return False
+        
+        if QFileInfo(fileName).exists():
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr("Save binary file"),
+                self.tr("<p>The file <b>{0}</b> already exists."
+                        " Overwrite it?</p>").format(fileName),
+                icon=E5MessageBox.Warning)
+            if not res:
+                return False
+        
+        self.__lastSavePath = os.path.dirname(fileName)
+        
+        return self.__saveHexDataFile(fileName)
+    
+    def __saveHexDataFile(self, fileName):
+        """
+        Private method to save the binary data to a file.
+        
+        @param fileName name of the file to write to
+        @type str
+        @return flag indicating success
+        @rtype bool
+        """
+        file = QFile(fileName)
+        if not file.open(QFile.WriteOnly):
+            E5MessageBox.warning(
+                self, self.tr("eric6 Hex Editor"),
+                self.tr("Cannot write file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+        
+            self.__checkActions()
+            
+            return False
+        
+        res = file.write(self.__editor.data()) != -1
+        file.close()
+        
+        if not res:
+            E5MessageBox.warning(
+                self, self.tr("eric6 Hex Editor"),
+                self.tr("Cannot write file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+        
+            self.__checkActions()
+            
+            return False
+        
+        self.__editor.setModified(False, setCleanState=True)
+        
+        self.__setCurrentFile(fileName)
+        self.__statusBar.showMessage(self.tr("File saved"), 2000)
+        
+        self.__checkActions()
+        
+        return True
+    
+    def __saveHexFileReadable(self, selectionOnly=False):
+        """
+        Private method to save the binary data in readable format.
+        
+        @param selectionOnly flag indicating to save the selection only
+        @type bool
+        """
+        savePath = self.__lastSavePath
+        if not savePath:
+            if self.__project and self.__project.isOpen():
+                savePath = self.__project.getProjectPath()
+        if not savePath and self.__lastOpenPath:
+            savePath = self.__lastOpenPath
+        
+        fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter(
+            self,
+            self.tr("Save to readable file"),
+            savePath,
+            self.tr("Text Files (*.txt);;All Files (*)"),
+            self.tr("Text Files (*.txt)"),
+            E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
+        if not fileName:
+            return
+        
+        ext = QFileInfo(fileName).suffix()
+        if not ext:
+            ex = selectedFilter.split("(*")[1].split(")")[0]
+            if ex:
+                fileName += ex
+        if QFileInfo(fileName).exists():
+            res = E5MessageBox.yesNo(
+                self,
+                self.tr("Save to readable file"),
+                self.tr("<p>The file <b>{0}</b> already exists."
+                        " Overwrite it?</p>").format(fileName),
+                icon=E5MessageBox.Warning)
+            if not res:
+                return
+        
+        file = QFile(fileName)
+        if not file.open(QFile.WriteOnly):
+            E5MessageBox.warning(
+                self, self.tr("eric6 Hex Editor"),
+                self.tr("Cannot write file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+            return
+        
+        if selectionOnly:
+            readableData = self.__editor.selectionToReadableString()
+        else:
+            readableData = self.__editor.toReadableString()
+        res = file.write(readableData.encode("latin1")) != -1
+        file.close()
+        
+        if not res:
+            E5MessageBox.warning(
+                self, self.tr("eric6 Hex Editor"),
+                self.tr("Cannot write file '{0}:\n{1}.")
+                .format(fileName, file.errorString()))
+            return
+        
+        self.__statusBar.showMessage(self.tr("File saved"), 2000)
+    
+    def __saveSelectionReadable(self):
+        """
+        Private method to save the data of the current selection in readable
+        format.
+        """
+        self.__saveHexFileReadable(selectionOnly=True)
+    
+    def __closeAll(self):
+        """
+        Private slot to close all windows.
+        """
+        self.__closeOthers()
+        self.close()
+    
+    def __closeOthers(self):
+        """
+        Private slot to close all other windows.
+        """
+        for win in self.__class__.windows[:]:
+            if win != self:
+                win.close()
+
+    def __setCurrentFile(self, fileName):
+        """
+        Private method to register the file name of the current file.
+        
+        @param fileName name of the file to register
+        @type str
+        """
+        self.__fileName = fileName
+        
+        if not self.__fileName:
+            shownName = self.tr("Untitled")
+        else:
+            shownName = self.__strippedName(self.__fileName)
+        
+        self.setWindowTitle(self.tr("{0}[*] - {1}")
+                            .format(shownName, self.tr("Hex Editor")))
+        
+        self.setWindowModified(self.__editor.isModified())
+    
+    def __strippedName(self, fullFileName):
+        """
+        Private method to return the filename part of the given path.
+        
+        @param fullFileName full pathname of the given file
+        @type str
+        @return filename part
+        @rtype str
+        """
+        return QFileInfo(fullFileName).fileName()
+    
+    def setRecentPaths(self, openPath, savePath):
+        """
+        Public method to set the last open and save paths.
+        
+        @param openPath least recently used open path
+        @type str
+        @param savePath least recently used save path
+        @type str
+        """
+        if openPath:
+            self.__lastOpenPath = openPath
+        if savePath:
+            self.__lastSavePath = savePath
+    
+    @pyqtSlot()
+    def __checkActions(self):
+        """
+        Private slot to check some actions for their enable/disable status.
+        """
+        self.saveAct.setEnabled(self.__editor.isModified())
+        
+        self.cutAct.setEnabled(not self.__editor.isReadOnly() and
+                               self.__editor.hasSelection())
+        self.pasteAct.setEnabled(not self.__editor.isReadOnly())
+    
+    @pyqtSlot(bool)
+    def __modificationChanged(self, m):
+        """
+        Private slot to handle the dataChanged signal.
+        
+        @param m modification status
+        @type bool
+        """
+        self.setWindowModified(m)
+        self.__checkActions()
+    
+    def __about(self):
+        """
+        Private slot to show a little About message.
+        """
+        E5MessageBox.about(
+            self, self.tr("About eric6 Hex Editor"),
+            self.tr("The eric6 Hex Editor is a simple editor component"
+                    " to edit binary files."))
+    
+    def __aboutQt(self):
+        """
+        Private slot to handle the About Qt dialog.
+        """
+        E5MessageBox.aboutQt(self, "eric6 Hex Editor")
+    
+    def __whatsThis(self):
+        """
+        Private slot called in to enter Whats This mode.
+        """
+        QWhatsThis.enterWhatsThisMode()

eric ide

mercurial