First commit for the new hex editor tool.

Sat, 09 Jan 2016 19:04:34 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sat, 09 Jan 2016 19:04:34 +0100
changeset 4650
b1ca3bcde70b
parent 4649
bbc8b2de9173
child 4651
7f3f276d3bf3

First commit for the new hex editor tool.

HexEdit/HexEditChunks.py file | annotate | diff | comparison | revisions
HexEdit/HexEditMainWindow.py file | annotate | diff | comparison | revisions
HexEdit/HexEditUndoStack.py file | annotate | diff | comparison | revisions
HexEdit/HexEditWidget.py file | annotate | diff | comparison | revisions
HexEdit/__init__.py file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
QScintilla/KeySequenceTranslator.py file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
eric6_hexeditor.py file | annotate | diff | comparison | revisions
eric6_hexeditor.pyw file | annotate | diff | comparison | revisions
icons/default/hexEditor.png file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HexEdit/HexEditChunks.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,446 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the storage backend for the hex editor.
+"""
+
+from __future__ import unicode_literals
+
+import sys
+
+from PyQt5.QtCore import QBuffer, QIODevice, QByteArray
+
+
+class HexEditChunk(object):
+    """
+    Class implementing a container for the data chunks.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        self.data = bytearray()
+        self.dataChanged = bytearray()
+        self.absPos = 0
+
+
+class HexEditChunks(object):
+    """
+    Class implementing the storage backend for the hex editor.
+    
+    When HexEditWidget loads data, HexEditChunks access them using a QIODevice
+    interface. When the app uses a QByteArray or Python bytearray interface,
+    QBuffer is used to provide again a QIODevice like interface. No data will
+    be changed, therefore HexEditChunks opens the QIODevice in
+    QIODevice.ReadOnly mode. After every access HexEditChunks closes the
+    QIODevice. That's why external applications can overwrite files while
+    HexEditWidget shows them.
+
+    When the the user starts to edit the data, HexEditChunks creates a local
+    copy of a chunk of data (4 kilobytes) and notes all changes there. Parallel
+    to that chunk, there is a second chunk, which keeps track of which bytes
+    are changed and which are not.
+    """
+    BUFFER_SIZE = 0x10000
+    CHUNK_SIZE = 0x1000
+    READ_CHUNK_MASK = 0xfffffffffffff000
+    
+    def __init__(self, ioDevice=None):
+        """
+        Constructor
+        
+        @param ioDevice io device to get the data from
+        @type QIODevice
+        """
+        self.__ioDevice = None
+        self.__pos = 0
+        self.__size = 0
+        self.__chunks = []
+        
+        if ioDevice is None:
+            buf = QBuffer()
+            self.setIODevice(buf)
+        else:
+            self.setIODevice(ioDevice)
+    
+    def setIODevice(self, ioDevice):
+        """
+        Public method to set an io device to read the binary data from.
+        
+        @param ioDevice io device to get the data from
+        @type QIODevice
+        @return flag indicating successful operation
+        @rtype bool
+        """
+        self.__ioDevice = ioDevice
+        ok = self.__ioDevice.open(QIODevice.ReadOnly)
+        if ok:
+            # open successfully
+            self.__size = self.__ioDevice.size()
+            self.__ioDevice.close()
+        else:
+            # fallback is an empty buffer
+            self.__ioDevice = QBuffer()
+            self.__size = 0
+        
+        self.__chunks = []
+        self.__pos = 0
+        
+        return ok
+    
+    def data(self, pos=0, maxSize=-1, highlighted=None):
+        """
+        Public method to get data out of the chunks.
+        
+        @param pos position to get bytes from
+        @type int
+        @param maxSize maximum amount of bytes to get
+        @type int
+        @param highlighted reference to a byte array storing highlighting info
+        @byte bytearray
+        @return retrieved data
+        @rtype bytearray
+        """
+        ioDelta = 0
+        chunkIdx = 0
+        
+        chunk = HexEditChunk()
+        buffer = bytearray()
+        
+        if highlighted is not None:
+            del highlighted[:]
+        
+        if pos >= self.__size:
+            return buffer
+        
+        if maxSize < 0:
+            maxSize = self.__size
+        elif (pos + maxSize) > self.__size:
+            maxSize = self.__size - pos
+        
+        self.__ioDevice.open(QIODevice.ReadOnly)
+        
+        while maxSize > 0:
+            chunk.absPos = sys.maxsize
+            chunksLoopOngoing = True
+            while chunkIdx < len(self.__chunks) and chunksLoopOngoing:
+                # In this section, we track changes before our required data
+                # and we take the edited data, if availible. ioDelta is a
+                # difference counter to justify the read pointer to the
+                # original data, if data in between was deleted or inserted.
+                
+                chunk = self.__chunks[chunkIdx]
+                if chunk.absPos > pos:
+                    chunksLoopOngoing = False
+                else:
+                    chunkIdx += 1
+                    chunkOfs = pos - chunk.absPos
+                    if maxSize > (len(chunk.data) - chunkOfs):
+                        count = len(chunk.data) - chunkOfs
+                        ioDelta += self.CHUNK_SIZE - len(chunk.data)
+                    else:
+                        count = maxSize
+                    if count > 0:
+                        buffer += chunk.data[chunkOfs:chunkOfs + count]
+                        maxSize -= count
+                        pos += count
+                        if highlighted is not None:
+                            highlighted += \
+                                chunk.dataChanged[chunkOfs:chunkOfs + count]
+            
+            if maxSize > 0 and pos < chunk.absPos:
+                # In this section, we read data from the original source. This
+                # will only happen, when no copied data is available.
+                if chunk.absPos - pos > maxSize:
+                    byteCount = maxSize
+                else:
+                    byteCount = chunk.absPos - pos
+                
+                maxSize -= byteCount
+                self.__ioDevice.seek(pos + ioDelta)
+                readBuffer = bytearray(self.__ioDevice.read(byteCount))
+                buffer += readBuffer
+                if highlighted is not None:
+                    highlighted += bytearray(len(readBuffer))
+                pos += len(readBuffer)
+        
+        self.__ioDevice.close()
+        return buffer
+    
+    def write(self, ioDevice, pos=0, count=-1):
+        """
+        Public method to write data to an io device.
+        
+        @param ioDevice io device to write the data to
+        @type QIODevice
+        @param pos position to write bytes from
+        @type int
+        @param count amount of bytes to write
+        @type int
+        @return flag indicating success
+        @rtype bool
+        """
+        if count == -1:
+            # write all data
+            count = self.__size
+        
+        ok = ioDevice.open(QIODevice.WriteOnly)
+        if ok:
+            idx = pos
+            while idx < count:
+                data = self.data(idx, self.BUFFER_SIZE)
+                ioDevice.write(QByteArray(data))
+                
+                # increment loop variable
+                idx += self.BUFFER_SIZE
+            
+            ioDevice.close()
+        
+        return ok
+    
+    def setDataChanged(self, pos, dataChanged):
+        """
+        Public method to set highlighting info.
+        
+        @param pos position to set highlighting info for
+        @type int
+        @param dataChanged flag indicating changed data
+        @type bool
+        """
+        if pos < 0 or pos >= self.__size:
+            # position is out of range, do nothing
+            return
+        chunkIdx = self.__getChunkIndex(pos)
+        posInChunk = pos - self.__chunks[chunkIdx].absPos
+        self.__chunks[chunkIdx].dataChanged[posInChunk] = int(dataChanged)
+    
+    def dataChanged(self, pos):
+        """
+        Public method to test, if some data was changed.
+        
+        @param pos byte position to check
+        @type int
+        @return flag indicating the changed state
+        @rtype bool
+        """
+        highlighted = bytearray()
+        self.data(pos, 1, highlighted)
+        return highlighted and bool(highlighted[0])
+    
+    def indexOf(self, byteArray, start):
+        """
+        Public method to search the first occurrence of some data.
+        
+        @param byteArray data to search for
+        @type bytearray
+        @param start position to start the search at
+        @type int
+        @return position the data was found at or -1 if nothing could be found
+        @rtype int
+        """
+        ba = bytearray(byteArray)
+        
+        result = -1
+        pos = start
+        while pos < self.__size:
+            buffer = self.data(pos, self.BUFFER_SIZE + len(ba) - 1)
+            findPos = buffer.find(ba)
+            if findPos >= 0:
+                result = pos + findPos
+                break
+            
+            # increment loop variable
+            pos += self.BUFFER_SIZE
+        
+        return result
+    
+    def lastIndexOf(self, byteArray, start):
+        """
+        Public method to search the last occurrence of some data.
+        
+        @param byteArray data to search for
+        @type bytearray
+        @param start position to start the search at
+        @type int
+        @return position the data was found at or -1 if nothing could be found
+        @rtype int
+        """
+        ba = bytearray(byteArray)
+        
+        result = -1
+        pos = start
+        while pos > 0 and result < 0:
+            sPos = pos - self.BUFFER_SIZE - len(ba) + 1
+            if sPos < 0:
+                sPos = 0
+            
+            buffer = self.data(sPos, pos - sPos)
+            findPos = buffer.rfind(ba)
+            if findPos >= 0:
+                result = sPos + findPos
+                break
+            
+            # increment loop variable
+            pos -= self.BUFFER_SIZE
+        
+        return result
+    
+    def insert(self, pos, data):
+        """
+        Public method to insert a byte.
+        
+        @param pos position to insert at
+        @type int
+        @param data byte to insert
+        @type int (range 0 to 255)
+        @return flag indicating success
+        @rtype bool
+        """
+        if pos < 0 or pos > self.__size:
+            # position is out of range, do nothing
+            return False
+        
+        if pos == self.__size:
+            chunkIdx = self.__getChunkIndex(pos - 1)
+        else:
+            chunkIdx = self.__getChunkIndex(pos)
+        chunk = self.__chunks[chunkIdx]
+        posInChunk = pos - chunk.absPos
+        chunk.data.insert(posInChunk, data)
+        chunk.dataChanged.insert(posInChunk, 1)
+        for idx in range(chunkIdx + 1, len(self.__chunks)):
+            self.__chunks[idx].absPos += 1
+        self.__size += 1
+        self.__pos = pos
+        return True
+    
+    def overwrite(self, pos, data):
+        """
+        Public method to overwrite a byte.
+        
+        @param pos position to overwrite
+        @type int
+        @param data byte to overwrite with
+        @type int (range 0 to 255)
+        @return flag indicating success
+        @rtype bool
+        """
+        if pos < 0 or pos >= self.__size:
+            # position is out of range, do nothing
+            return False
+        
+        chunkIdx = self.__getChunkIndex(pos)
+        chunk = self.__chunks[chunkIdx]
+        posInChunk = pos - chunk.absPos
+        chunk.data[posInChunk] = data
+        chunk.dataChanged[posInChunk] = 1
+        self.__pos = pos
+        return True
+    
+    def removeAt(self, pos):
+        """
+        Public method to remove a byte.
+        
+        @param pos position to remove
+        @type int
+        @return flag indicating success
+        @rtype bool
+        """
+        if pos < 0 or pos >= self.__size:
+            # position is out of range, do nothing
+            return
+        
+        chunkIdx = self.__getChunkIndex(pos)
+        chunk = self.__chunks[chunkIdx]
+        posInChunk = pos - chunk.absPos
+        chunk.data.pop(posInChunk)
+        chunk.dataChanged.pop(posInChunk)
+        for idx in range(chunkIdx + 1, len(self.__chunks)):
+            self.__chunks[idx].absPos -= 1
+        self.__size -= 1
+        self.__pos = pos
+        return True
+    
+    def __getitem__(self, pos):
+        """
+        Special method to get a byte at a position.
+        
+        Note: This realizes the [] get operator.
+        
+        @param pos position of byte to get
+        @type int
+        @return requested byte
+        @rtype int (range 0 to 255)
+        """
+        if pos >= self.__size:
+            return 0
+##            raise IndexError
+        
+        return self.data(pos, 1)[0]
+    
+    def pos(self):
+        """
+        Public method to get the current position.
+        
+        @return current position
+        @rtype int
+        """
+        return self.__pos
+    
+    def size(self):
+        """
+        Public method to get the current data size.
+        
+        @return current data size
+        @rtype int
+        """
+        return self.__size
+    
+    def __getChunkIndex(self, absPos):
+        """
+        Private method to get the chunk index for a position.
+        
+        This method checks, if there is already a copied chunk available. If
+        there is one, it returns its index. If there is no copied chunk
+        available, original data will be copied into a new chunk.
+        
+        @param absPos absolute position of the data.
+        @type int
+        @return index of the chunk containing the position
+        @rtype int
+        """
+        foundIdx = -1
+        insertIdx = 0
+        ioDelta = 0
+        
+        for idx in range(len(self.__chunks)):
+            chunk = self.__chunks[idx]
+            if absPos >= chunk.absPos and \
+                    absPos < (chunk.absPos + len(chunk.data)):
+                foundIdx = idx
+                break
+            
+            if absPos < chunk.absPos:
+                insertIdx = idx
+                break
+            
+            ioDelta += len(chunk.data) - self.CHUNK_SIZE
+            insertIdx = idx + 1
+        
+        if foundIdx == -1:
+            newChunk = HexEditChunk()
+            readAbsPos = absPos - ioDelta
+            readPos = readAbsPos & self.READ_CHUNK_MASK
+            self.__ioDevice.open(QIODevice.ReadOnly)
+            self.__ioDevice.seek(readPos)
+            newChunk.data = bytearray(self.__ioDevice.read(self.CHUNK_SIZE))
+            self.__ioDevice.close()
+            newChunk.absPos = absPos - (readAbsPos - readPos)
+            newChunk.dataChanged = bytearray(len(newChunk.data))
+            self.__chunks.insert(insertIdx, newChunk)
+            foundIdx = insertIdx
+        
+        return foundIdx
--- /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()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HexEdit/HexEditUndoStack.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Undo stack for the hex edit widget.
+"""
+
+from __future__ import unicode_literals
+
+try:
+    from enum import Enum
+except ImportError:
+    from ThirdParty.enum import Enum
+
+from PyQt5.QtWidgets import QUndoStack, QUndoCommand
+
+
+class HexEditCommand(Enum):
+    """
+    Class implementing the edit comands.
+    """
+    Insert = 0
+    RemoveAt = 1
+    Overwrite = 2
+
+
+class HexEditUndoCommand(QUndoCommand):
+    """
+    Class implementing the Undo command.
+    """
+    def __init__(self, chunks, cmd, pos, newByte, parent=None):
+        """
+        Constructor
+        
+        @param chunks reference to the data container
+        @type HexEditChunks
+        @param cmd edit command
+        @type HexEditCommand
+        @param pos edit position
+        @type int
+        @param newByte new byte value
+        @type int (range 0 to 255)
+        @param parent reference to the parent command
+        @type QUndoCommand
+        """
+        super(HexEditUndoCommand, self).__init__(parent)
+        
+        self.__chunks = chunks
+        self._pos = pos
+        self._newByte = newByte
+        self._cmd = cmd
+        
+        self.__wasChanged = False
+        self.__oldByte = 0
+    
+    def undo(self):
+        """
+        Public method to undo the command.
+        """
+        if self._cmd == HexEditCommand.Insert:
+            self.__chunks.removeAt(self._pos)
+        elif self._cmd == HexEditCommand.Overwrite:
+            self.__chunks.overwrite(self._pos, self.__oldByte)
+            self.__chunks.setDataChanged(self._pos, self.__wasChanged)
+        elif self._cmd == HexEditCommand.RemoveAt:
+            self.__chunks.insert(self._pos, self.__oldByte)
+            self.__chunks.setDataChanged(self._pos, self.__wasChanged)
+    
+    def redo(self):
+        """
+        Public method to redo the command.
+        """
+        if self._cmd == HexEditCommand.Insert:
+            self.__chunks.insert(self._pos, self._newByte)
+        elif self._cmd == HexEditCommand.Overwrite:
+            self.__oldByte = self.__chunks[self._pos]
+            self.__wasChanged = self.__chunks.dataChanged(self._pos)
+            self.__chunks.overwrite(self._pos, self._newByte)
+        elif self._cmd == HexEditCommand.RemoveAt:
+            self.__oldByte = self.__chunks[self._pos]
+            self.__wasChanged = self.__chunks.dataChanged(self._pos)
+            self.__chunks.removeAt(self._pos)
+    
+    def mergeWith(self, command):
+        """
+        Public method to merge this command with another one.
+        
+        @param command reference to the command to merge with
+        @type QUndoCommand
+        @return flag indicating a successful merge
+        @rtype bool
+        """
+        result = False
+        
+        if self._cmd != HexEditCommand.RemoveAt:
+            if command._cmd == HexEditCommand.Overwrite:
+                if command._pos == self._pos:
+                    self._newByte = command._newByte
+                    result = True
+        
+        return result
+    
+    def id(self):
+        """
+        Public method to get the ID of this undo command class.
+        
+        @return ID of the undo command class
+        @rtype int
+        """
+        return 4242
+
+
+class HexEditUndoStack(QUndoStack):
+    """
+    Class implementing an Undo stack for the hex edit widget.
+    """
+    def __init__(self, chunks, parent=None):
+        """
+        Constructor
+        
+        @param chunks reference to the data container
+        @type HexEditChunks
+        @param parent reference to the parent object
+        @type QObject
+        """
+        super(HexEditUndoStack, self).__init__(parent)
+        
+        self.__chunks = chunks
+        self.__parent = parent
+    
+    def insert(self, pos, data):
+        """
+        Public method to insert a byte.
+        
+        @param pos position to insert at
+        @type int
+        @param data byte to be inserted
+        @type int (range 0 to 255)
+        """
+        if pos >= 0 and pos <= self.__chunks.size():
+            uc = HexEditUndoCommand(
+                self.__chunks, HexEditCommand.Insert, pos, data)
+            self.push(uc)
+    
+    def insertByteArray(self, pos, byteArray):
+        """
+        Public method to insert bytes.
+        
+        @param pos position to insert at
+        @type int
+        @param byteArray data to be inserted
+        @type byteArray or QByteArray
+        """
+        ba = bytearray(byteArray)
+        
+        if pos >= 0 and pos <= self.__chunks.size():
+            txt = self.tr("Inserting %n byte(s)", "", len(ba))
+            self.beginMacro(txt)
+            for idx in range(len(ba)):
+                uc = HexEditUndoCommand(
+                    self.__chunks, HexEditCommand.Insert, pos + idx, ba[idx])
+                self.push(uc)
+            self.endMacro()
+    
+    def removeAt(self, pos, length=1):
+        """
+        Public method to remove bytes.
+        
+        @param pos position to remove bytes from
+        @type int
+        @param length amount of bytes to remove
+        @type int
+        """
+        if pos >= 0 and pos <= self.__chunks.size():
+            if length == 1:
+                uc = HexEditUndoCommand(
+                    self.__chunks, HexEditCommand.RemoveAt, pos, 0)
+                self.push(uc)
+            else:
+                txt = self.tr("Deleting %n byte(s)", "", length)
+                self.beginMacro(txt)
+                for cnt in range(length):
+                    uc = HexEditUndoCommand(
+                        self.__chunks, HexEditCommand.RemoveAt, pos, 0)
+                    self.push(uc)
+                self.endMacro()
+    
+    def overwrite(self, pos, data):
+        """
+        Public method to replace a byte.
+        
+        @param pos position to replace the byte at
+        @type int
+        @param data byte to replace with
+        @type int (range 0 to 255)
+        """
+        if pos >= 0 and pos <= self.__chunks.size():
+            uc = HexEditUndoCommand(
+                self.__chunks, HexEditCommand.Overwrite, pos, data)
+            self.push(uc)
+    
+    def overwriteByteArray(self, pos, length, byteArray):
+        """
+        Public method to replace bytes.
+        
+        @param pos position to replace the bytes at
+        @type int
+        @param length amount of bytes to replace
+        @type int
+        @param byteArray bytes to replace with
+        @type bytearray or QByteArray
+        """
+        ba = bytearray(byteArray)
+        
+        if pos >= 0 and pos <= self.__chunks.size():
+            txt = self.tr("Inserting %n byte(s)", "", len(ba))
+            self.beginMacro(txt)
+            self.removeAt(pos, length)
+            self.insertByteArray(pos, ba)
+            self.endMacro()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HexEdit/HexEditWidget.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,1658 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing an editor for binary data.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QByteArray, QTimer, QRect, \
+    QBuffer, QIODevice
+from PyQt5.QtGui import QBrush, QPen, QColor, QFont, QPalette, QKeySequence, \
+    QPainter
+from PyQt5.QtWidgets import QAbstractScrollArea, QApplication
+
+from .HexEditChunks import HexEditChunks
+from .HexEditUndoStack import HexEditUndoStack
+
+import Globals
+
+
+# TODO: implement cursor in ASCII area
+# TODO: implement editing in ASCII area
+
+
+class HexEditWidget(QAbstractScrollArea):
+    """
+    Class implementing an editor for binary data.
+    
+    @signal currentAddressChanged(address) emitted to indicate the new
+        cursor position
+    @signal currentSizeChanged(size) emitted to indicate the new size of
+        the data
+    @signal dataChanged(modified) emitted to indicate a change of the data
+    @signal overwriteModeChanged(state) emitted to indicate a change of
+        the overwrite mode
+    @signal readOnlyChanged(state) emitted to indicate a change of the
+        read only state
+    @signal canRedoChanged(bool) emitted after the redo status has changed
+    @signal canUndoChanged(bool) emitted after the undo status has changed
+    @signal selectionAvailable(bool) emitted to signal a change of the
+        selection
+    """
+    currentAddressChanged = pyqtSignal(int)
+    currentSizeChanged = pyqtSignal(int)
+    dataChanged = pyqtSignal(bool)
+    overwriteModeChanged = pyqtSignal(bool)
+    readOnlyChanged = pyqtSignal(bool)
+    canRedoChanged = pyqtSignal(bool)
+    canUndoChanged = pyqtSignal(bool)
+    selectionAvailable = pyqtSignal(bool)
+    
+    HEXCHARS_PER_LINE = 47
+    BYTES_PER_LINE = 16
+    
+    def __init__(self, parent=None, embedded=False):
+        """
+        Constructor
+        
+        @param parent refernce to the parent widget
+        @type QWidget
+        @param embedded flag indicating an eric embedded hex editor
+        @type bool
+        """
+        super(HexEditWidget, self).__init__(parent)
+        
+        self.__embedded = embedded
+        
+        # Properties
+        self.__addressArea = True
+        # switch the address area on/off
+        self.__addressAreaColor = QColor()
+        # color of the address area
+        self.__addressOffset = 0
+        # offset into the shown address range
+        self.__addressWidth = 4
+        # address area width in characters
+        self.__asciiArea = True
+        # switch the ASCII area on/off
+        self.__data = bytearray()
+        # contents of the hex editor
+        self.__highlighting = True
+        # switch the highlighting feature on/off
+        self.__highlightingBrush = QBrush()
+        self.__highlightingPen = QPen()
+        # background and pen of highlighted text
+        self.__overwriteMode = True
+        # set overwrite mode on/off
+        self.__selectionBrush = QBrush()
+        self.__selectionPen = QPen()
+        # background and pen of selected text
+        self.__readOnly = False
+        # set read only mode on/off
+        self.__cursorPosition = 0
+        # absolute positioin of cursor, 1 Byte == 2 tics
+        
+        self.__addrDigits = 0
+        self.__blink = True
+        self.__bData = QBuffer()
+        self.__cursorRect = QRect()
+        self.__dataShown = bytearray()
+        self.__hexDataShown = bytearray()
+        self.__lastEventSize = 0
+        self.__markedShown = bytearray()
+        self.__modified = False
+        self.__rowsShown = 0
+        
+        # pixel related attributes (starting with __px)
+        self.__pxCharWidth = 0
+        self.__pxCharHeight = 0
+        self.__pxPosHexX = 0
+        self.__pxPosAdrX = 0
+        self.__pxPosAsciiX = 0
+        self.__pxGapAdr = 0
+        self.__pxGapAdrHex = 0
+        self.__pxGapHexAscii = 0
+        self.__pxSelectionSub = 0
+        self.__pxCursorWidth = 0
+        self.__pxCursorX = 0
+        self.__pxCursorY = 0
+        
+        # absolute byte position related attributes (starting with __b)
+        self.__bSelectionBegin = 0
+        self.__bSelectionEnd = 0
+        self.__bSelectionInit = 0
+        self.__bPosFirst = 0
+        self.__bPosLast = 0
+        self.__bPosCurrent = 0
+        
+        self.__chunks = HexEditChunks()
+        self.__undoStack = HexEditUndoStack(self.__chunks, self)
+        if Globals.isWindowsPlatform():
+            self.setFont(QFont("Courier", 10))
+        else:
+            self.setFont(QFont("Monospace", 10))
+        
+        self.setAddressAreaColor(self.palette().alternateBase().color())
+        self.setHighlightColor(QColor(0xff, 0xff, 0x99, 0xff))
+        self.setSelectionColor(self.palette().highlight().color())
+        
+        self.__cursorTimer = QTimer()
+        self.__cursorTimer.timeout.connect(self.__updateCursor)
+        
+        self.verticalScrollBar().valueChanged.connect(self.__adjust)
+        
+        self.__undoStack.indexChanged.connect(self.__dataChangedPrivate)
+        self.__undoStack.canRedoChanged.connect(self.__canRedoChanged)
+        self.__undoStack.canUndoChanged.connect(self.__canUndoChanged)
+        
+        self.readOnlyChanged.connect(self.__canRedoChanged)
+        self.readOnlyChanged.connect(self.__canUndoChanged)
+        
+        self.__cursorTimer.setInterval(500)
+        self.__cursorTimer.start()
+        
+        self.setAddressWidth(4)
+        self.setAddressArea(True)
+        self.setAsciiArea(True)
+        self.setOverwriteMode(True)
+        self.setHighlighting(True)
+        self.setReadOnly(False)
+        
+        self.__initialize()
+    
+    def undoStack(self):
+        """
+        Public method to get a reference to the undo stack.
+        
+        @return reference to the undo stack
+        @rtype HexEditUndoStack
+        """
+        return self.__undoStack
+    
+    @pyqtSlot()
+    def __canRedoChanged(self):
+        """
+        Private slot handling changes of the Redo state.
+        """
+        self.canRedoChanged.emit(
+            self.__undoStack.canRedo() and not self.__readOnly)
+    
+    @pyqtSlot()
+    def __canUndoChanged(self):
+        """
+        Private slot handling changes of the Undo state.
+        """
+        self.canUndoChanged.emit(
+            self.__undoStack.canUndo() and not self.__readOnly)
+    
+    def addressArea(self):
+        """
+        Public method to get the address area visibility.
+        
+        @return flag indicating the address area visibility
+        @rtype bool
+        """
+        return self.__addressArea
+    
+    def setAddressArea(self, on):
+        """
+        Public method to set the address area visibility.
+        
+        @param on flag indicating the address area visibility
+        @type bool
+        """
+        self.__addressArea = on
+        self.__adjust()
+        self.setCursorPosition(self.__cursorPosition)
+        self.viewport().update()
+    
+    def addressAreaColor(self):
+        """
+        Public method to get the address area color.
+        
+        @return address area color
+        @rtype QColor
+        """
+        return QColor(self.__addressAreaColor)
+    
+    def setAddressAreaColor(self, color):
+        """
+        Public method to set the address area color.
+        
+        @param color address area color
+        @type QColor
+        """
+        self.__addressAreaColor = QColor(color)
+        self.viewport().update()
+    
+    def addressOffset(self):
+        """
+        Public method to get the address offset.
+        
+        @return address offset
+        @rtype int
+        """
+        return self.__addressOffset
+    
+    def setAddressOffset(self, offset):
+        """
+        Public method to set the address offset.
+        
+        @param offset address offset
+        @type int
+        """
+        self.__addressOffset = offset
+        self.__adjust()
+        self.setCursorPosition(self.__cursorPosition)
+        self.viewport().update()
+    
+    def addressWidth(self):
+        """
+        Public method to get the minimum width of the address area in
+        characters.
+        
+        @return minimum width of the address area
+        @rtype int
+        """
+        size = self.__chunks.size()
+        n = 1
+        if size > 0x100000000:
+            n += 8
+            size //= 0x100000000
+        if size > 0x10000:
+            n += 4
+            size //= 0x10000
+        if size > 0x100:
+            n += 2
+            size //= 0x100
+        if size > 0x10:
+            n += 1
+            size //= 0x10
+        
+        if n > self.__addressWidth:
+            return n
+        else:
+            return self.__addressWidth
+    
+    def setAddressWidth(self, width):
+        """
+        Public method to set the width of the address area.
+        
+        @param width width of the address area in characters
+        @type int
+        """
+        self.__addressWidth = width
+        self.__adjust()
+        self.setCursorPosition(self.__cursorPosition)
+        self.viewport().update()
+    
+    def asciiArea(self):
+        """
+        Public method to get the visibility of the ASCII area.
+        
+        @return visibility of the ASCII area
+        @rtype bool
+        """
+        return self.__asciiArea
+    
+    def setAsciiArea(self, on):
+        """
+        Public method to set the visibility of the ASCII area.
+        
+        @param on flag indicating the visibility of the ASCII area
+        @type bool
+        """
+        self.__asciiArea = on
+        self.viewport().update()
+    
+    def cursorPosition(self):
+        """
+        Public method to get the cursor position.
+        
+        @return cursor position
+        @rtype int
+        """
+        return self.__cursorPosition
+    
+    def setCursorPosition(self, pos):
+        """
+        Public method to set the cursor position.
+        
+        @param pos cursor position
+        @type int
+        """
+        # step 1: delete old cursor
+        self.__blink = False
+        self.viewport().update(self.__cursorRect)
+        
+        # step 2: check, if cursor is in range
+        if self.__overwriteMode and pos > (self.__chunks.size() * 2 - 1):
+            pos = self.__chunks.size() * 2 - 1
+        if (not self.__overwriteMode) and pos > (self.__chunks.size() * 2):
+            pos = self.__chunks.size() * 2
+        if pos < 0:
+            pos = 0
+        
+        # step 3: calculate new position of cursor
+        self.__cursorPosition = pos
+        self.__bPosCurrent = pos // 2
+        self.__pxCursorY = (
+            ((pos // 2 - self.__bPosFirst) // self.BYTES_PER_LINE + 1) *
+            self.__pxCharHeight)
+        x = (pos % (2 * self.BYTES_PER_LINE))
+        self.__pxCursorX = (
+            (((x // 2) * 3) + (x % 2)) * self.__pxCharWidth + self.__pxPosHexX)
+        
+        if self.__overwriteMode:
+            self.__cursorRect = QRect(
+                self.__pxCursorX, self.__pxCursorY + self.__pxCursorWidth,
+                self.__pxCharWidth, self.__pxCursorWidth)
+        else:
+            self.__cursorRect = QRect(
+                self.__pxCursorX, self.__pxCursorY - self.__pxCharHeight + 4,
+                self.__pxCursorWidth, self.__pxCharHeight)
+        
+        # step 4: draw new cursor
+        self.__blink = True
+        self.viewport().update(self.__cursorRect)
+        self.currentAddressChanged.emit(self.__bPosCurrent)
+    
+    def data(self):
+        """
+        Public method to get the binary data.
+        
+        @return binary data
+        @rtype bytearray
+        """
+        return self.__chunks.data(0, -1)
+    
+    def setData(self, dataOrDevice):
+        """
+        Public method to set the data to show.
+        
+        @param dataOrDevice byte array or device containing the data
+        @type bytearray, QByteArray or QIODevice
+        @return flag indicating success
+        @rtype bool
+        @exception TypeError raised to indicate a wrong parameter type
+        """
+        if isinstance(dataOrDevice, (bytearray, QByteArray)):
+            self.__data = bytearray(dataOrDevice)
+            self.__bData.setData(self.__data)
+            return self.__setData(self.__bData)
+        elif isinstance(dataOrDevice, QIODevice):
+            return self.__setData(dataOrDevice)
+        else:
+            raise TypeError(
+                "setData: parameter must be bytearray, "
+                "QByteArray or QIODevice")
+    
+    def __setData(self, ioDevice):
+        """
+        Private method to set the data to show.
+        
+        @param ioDevice device containing the data
+        @type QIODevice
+        @return flag indicating success
+        @rtype bool
+        """
+        ok = self.__chunks.setIODevice(ioDevice)
+        self.__initialize()
+        self.__dataChangedPrivate()
+        return ok
+    
+    def highlighting(self):
+        """
+        Public method to get the highlighting state.
+        
+        @return highlighting state
+        @rtype bool
+        """
+        return self.__highlighting
+    
+    def setHighlighting(self, on):
+        """
+        Public method to set the highlighting state.
+        
+        @param on new highlighting state
+        @type bool
+        """
+        self.__highlighting = on
+        self.viewport().update()
+    
+    def highlightingColor(self):
+        """
+        Public method to get the highlighting color.
+        
+        @return highlighting color
+        @rtype QColor
+        """
+        return self.__highlightingBrush.color()
+    
+    def setHighlightColor(self, color):
+        """
+        Public method to set the highlight color.
+        
+        @param color new highlight color
+        @type QColor
+        """
+        self.__highlightingBrush = QBrush(color)
+        self.__highlightingPen = QPen(
+            self.viewport().palette().color(QPalette.WindowText))
+        self.viewport().update()
+    
+    def overwriteMode(self):
+        """
+        Public method to get the overwrite mode.
+        
+        @return overwrite mode
+        @rtype bool
+        """
+        return self.__overwriteMode
+    
+    def setOverwriteMode(self, on):
+        """
+        Public method to set the overwrite mode.
+        
+        @param on flag indicating the new overwrite mode
+        @type bool
+        """
+        self.__overwriteMode = on
+        self.overwriteModeChanged.emit(self.__overwriteMode)
+    
+    def selectionColor(self):
+        """
+        Public method to get the selection color.
+        
+        @return selection color
+        @rtype QColor
+        """
+        return self.__selectionBrush.color()
+    
+    def setSelectionColor(self, color):
+        """
+        Public method to set the selection color.
+        
+        @param color new selection color
+        @type QColor
+        """
+        self.__selectionBrush = QBrush(color)
+        self.__selectionPen = QPen(Qt.white)
+        self.viewport().update()
+    
+    def isReadOnly(self):
+        """
+        Public method to test the read only state.
+        
+        @return flag indicating the read only state
+        @rtype bool
+        """
+        return self.__readOnly
+    
+    def setReadOnly(self, on):
+        """
+        Public method to set the read only state.
+        
+        @param on new read only state
+        @type bool
+        """
+        self.__readOnly = on
+        self.readOnlyChanged.emit(self.__readOnly)
+    
+    def font(self):
+        """
+        Public method to get the font used to show the data.
+        
+        @return font used to show the data
+        @rtype QFont
+        """
+        return super(HexEditWidget, self).font()
+    
+    def setFont(self, font):
+        """
+        Public method to set the font used to show the data.
+        
+        @param font font used to show the data
+        @type QFont
+        """
+        super(HexEditWidget, self).setFont(font)
+        self.__pxCharWidth = self.fontMetrics().width("2")
+        self.__pxCharHeight = self.fontMetrics().height()
+        self.__pxGapAdr = self.__pxCharWidth // 2
+        self.__pxGapAdrHex = self.__pxCharWidth
+        self.__pxGapHexAscii = 2 * self.__pxCharWidth
+        self.__pxCursorWidth = self.__pxCharHeight // 7
+        self.__pxSelectionSub = self.__pxCharHeight // 5
+        self.viewport().update()
+    
+    def dataAt(self, pos, count=-1):
+        """
+        Public method to get data from a given position.
+        
+        @param pos position to get data from
+        @type int
+        @param count amount of bytes to get
+        @type int
+        @return requested data
+        @rtype bytearray
+        """
+        return bytearray(self.__chunks.data(pos, count))
+    
+    def write(self, device, pos=0, count=-1):
+        """
+        Public method to write data from a given position to a device.
+        
+        @param device device to write to
+        @type QIODevice
+        @param pos position to start the write at
+        @type int
+        @param count amount of bytes to write
+        @type int
+        @return flag indicating success
+        @rtype bool
+        """
+        return self.__chunks.write(device, pos, count)
+    
+    def insert(self, pos, ch):
+        """
+        Public method to insert a byte.
+        
+        @param pos position to insert the byte at
+        @type int
+        @param ch byte to insert
+        @type int in the range 0x00 to 0xff
+        """
+        assert ch in range(0, 256)
+        
+        self.__undoStack.insert(pos, ch)
+        self.__refresh()
+    
+    def remove(self, pos, length=1):
+        """
+        Public method to remove bytes.
+        
+        @param pos position to remove bytes from
+        @type int
+        @param length amount of bytes to remove
+        @type int
+        """
+        self.__undoStack.removeAt(pos, length)
+        self.__refresh()
+    
+    def replace(self, pos, ch):
+        """
+        Public method to replace a byte.
+        
+        @param pos position to replace the byte at
+        @type int
+        @param ch byte to replace with
+        @type int in the range 0x00 to 0xff
+        """
+        assert ch in range(0, 256)
+        
+        self.__undoStack.overwrite(pos, ch)
+        self.__refresh()
+    
+    def insertByteArray(self, pos, byteArray):
+        """
+        Public method to insert bytes.
+        
+        @param pos position to insert the bytes at
+        @type int
+        @param byteArray bytes to be insert
+        @type bytearray or QByteArray
+        """
+        self.__undoStack.insertByteArray(pos, bytearray(byteArray))
+        self.__refresh()
+    
+    def replaceByteArray(self, pos, len, byteArray):
+        """
+        Public method to replace bytes.
+        
+        @param pos position to replace the bytes at
+        @type int
+        @param len amount of bytes to replace
+        @type int
+        @param byteArray bytes to replace with
+        @type bytearray or QByteArray
+        """
+        self.__undoStack.overwriteByteArray(pos, len, bytearray(byteArray))
+        self.__refresh()
+    
+    def cursorPositionFromPoint(self, point):
+        """
+        Public method to calculate a cursor position from a graphics position.
+        
+        @param point graphics position
+        @type QPoint
+        @return cursor position
+        @rtype int
+        """
+        result = -1
+        if (point.x() >= self.__pxPosHexX) and (
+            point.x() < (self.__pxPosHexX + (1 + self.HEXCHARS_PER_LINE) *
+                         self.__pxCharWidth)):
+            x = (point.x() - self.__pxPosHexX - self.__pxCharWidth // 2) // \
+                self.__pxCharWidth
+            x = (x // 3) * 2 + x % 3
+            y = ((point.y() - 3) // self.__pxCharHeight) * 2 * \
+                self.BYTES_PER_LINE
+            result = self.__bPosFirst * 2 + x + y
+        return result
+    
+    def ensureVisible(self):
+        """
+        Public method to ensure, that the cursor is visible.
+        """
+        if self.__cursorPosition < 2 * self.__bPosFirst:
+            self.verticalScrollBar().setValue(
+                self.__cursorPosition // 2 // self.BYTES_PER_LINE)
+        if self.__cursorPosition > (
+            (self.__bPosFirst + (self.__rowsShown - 1) *
+             self.BYTES_PER_LINE) * 2):
+            self.verticalScrollBar().setValue(
+                self.__cursorPosition // 2 // self.BYTES_PER_LINE -
+                self.__rowsShown + 1)
+        self.viewport().update()
+    
+    def indexOf(self, byteArray, start):
+        """
+        Public method to find the first occurrence of a byte array in our data.
+        
+        @param byteArray data to search for
+        @type bytearray or QByteArray
+        @param start start position of the search
+        @type int
+        @return position of match (or -1 if not found)
+        @rtype int
+        """
+        byteArray = bytearray(byteArray)
+        pos = self.__chunks.indexOf(byteArray, start)
+        if pos > -1:
+            curPos = pos * 2
+            self.setCursorPosition(curPos + len(byteArray) * 2)
+            self.__resetSelection(curPos)
+            self.__setSelection(curPos + len(byteArray) * 2)
+            self.ensureVisible()
+        return pos
+    
+    def lastIndexOf(self, byteArray, start):
+        """
+        Public method to find the last occurrence of a byte array in our data.
+        
+        @param byteArray data to search for
+        @type bytearray or QByteArray
+        @param start start position of the search
+        @type int
+        @return position of match (or -1 if not found)
+        @rtype int
+        """
+        byteArray = bytearray(byteArray)
+        pos = self.__chunks.lastIndexOf(byteArray, start)
+        if pos > -1:
+            curPos = pos * 2
+            self.setCursorPosition(curPos - 1)
+            self.__resetSelection(curPos)
+            self.__setSelection(curPos + len(byteArray) * 2)
+            self.ensureVisible()
+        return pos
+    
+    def isModified(self):
+        """
+        Public method to check for any modification.
+        
+        @return flag indicating a modified state
+        @rtype bool
+        """
+        return self.__modified
+    
+    def setModified(self, modified, setCleanState=False):
+        """
+        Public slot to set the modified flag.
+        
+        @param modified flag indicating the new modification status
+        @type bool
+        @param setCleanState flag indicating to set the undo stack to clean
+        @type bool
+        """
+        self.__modified = modified
+        self.dataChanged.emit(modified)
+        
+        if not modified and setCleanState:
+            self.__undoStack.setClean()
+    
+    def selectionToReadableString(self):
+        """
+        Public method to get a formatted representation of the selection.
+        
+        @return formatted representation of the selection
+        @rtype str
+        """
+        byteArray = self.__chunks.data(self.__getSelectionBegin(),
+                                       self.__getSelectionLength())
+        return self.__toReadable(byteArray)
+    
+    def toReadableString(self):
+        """
+        Public method to get a formatted representation of our data.
+        
+        @return formatted representation of our data
+        @rtype str
+        """
+        byteArray = self.__chunks.data()
+        return self.__toReadable(byteArray)
+    
+    @pyqtSlot()
+    def redo(self):
+        """
+        Public slot to redo the last operation.
+        """
+        self.__undoStack.redo()
+        self.setCursorPosition(self.__chunks.pos() * 2)
+        self.__refresh()
+    
+    @pyqtSlot()
+    def undo(self):
+        """
+        Public slot to undo the last operation.
+        """
+        self.__undoStack.undo()
+        self.setCursorPosition(self.__chunks.pos() * 2)
+        self.__refresh()
+    
+    @pyqtSlot()
+    def revertToUnmodified(self):
+        """
+        Public slot to revert all changes.
+        """
+        cleanIndex = self.__undoStack.cleanIndex()
+        if cleanIndex >= 0:
+            self.__undoStack.setIndex(cleanIndex)
+        self.setCursorPosition(self.__chunks.pos() * 2)
+        self.__refresh()
+    
+    def editorCommand(self, cmd):
+        """
+        Public method to execute an editor command sent by the eric
+        view manager.
+        
+        @param cmd QScintilla command
+        @type int
+        """
+        if self.__embedded:
+            from PyQt5.Qsci import QsciScintilla
+            
+            # Cursor movements
+            if cmd == QsciScintilla.SCI_CHARLEFT:
+                self.moveCursorToPreviousChar()
+            elif cmd == QsciScintilla.SCI_CHARRIGHT:
+                self.moveCursorToNextChar()
+            elif cmd == QsciScintilla.SCI_LINEEND:
+                self.moveCursorToEndOfLine()
+            elif cmd == QsciScintilla.SCI_VCHOME:
+                self.moveCursorToStartOfLine()
+            elif cmd == QsciScintilla.SCI_LINEUP:
+                self.moveCursorToPreviousLine()
+            elif cmd == QsciScintilla.SCI_LINEDOWN:
+                self.moveCursorToNextLine()
+            elif cmd == QsciScintilla.SCI_PAGEDOWN:
+                self.moveCursorToNextPage()
+            elif cmd == QsciScintilla.SCI_PAGEUP:
+                self.moveCursorToPreviousPage()
+            elif cmd == QsciScintilla.SCI_DOCUMENTEND:
+                self.moveCursorToEndOfDocument()
+            elif cmd == QsciScintilla.SCI_DOCUMENTSTART:
+                self.moveCursorToStartOfDocument()
+            
+            # Selection commands
+            elif cmd == QsciScintilla.SCI_CHARRIGHTEXTEND:
+                self.selectNextChar()
+            elif cmd == QsciScintilla.SCI_CHARLEFTEXTEND:
+                self.selectPreviousChar()
+            elif cmd == QsciScintilla.SCI_LINEENDEXTEND:
+                self.selectToEndOfLine()
+            elif cmd == QsciScintilla.SCI_VCHOMEEXTEND:
+                self.selectToStartOfLine()
+            elif cmd == QsciScintilla.SCI_LINEUPEXTEND:
+                self.selectPreviousLine()
+            elif cmd == QsciScintilla.SCI_LINEDOWNEXTEND:
+                self.selectNextLine()
+            elif cmd == QsciScintilla.SCI_PAGEDOWNEXTEND:
+                self.selectNextPage()
+            elif cmd == QsciScintilla.SCI_PAGEUPEXTEND:
+                self.selectPreviousPage()
+            elif cmd == QsciScintilla.SCI_DOCUMENTENDEXTEND:
+                self.selectEndOfDocument()
+            elif cmd == QsciScintilla.SCI_DOCUMENTSTARTEXTEND:
+                self.selectStartOfDocument()
+            elif cmd == QsciScintilla.SCI_EDITTOGGLEOVERTYPE:
+                self.setOverwriteMode(not self.overwriteMode())
+                self.setCursorPosition(self.__cursorPosition)
+            
+            # Edit commands
+            if not self.__readOnly:
+                if cmd == QsciScintilla.SCI_CLEAR:
+                    self.deleteByte()
+                elif cmd == QsciScintilla.SCI_DELETEBACK:
+                    self.deleteByteBack()
+        
+            self.__refresh()
+    
+    ####################################################
+    ## Cursor movement commands
+    ####################################################
+    
+    def moveCursorToNextChar(self):
+        """
+        Public method to move the cursor to the next byte.
+        """
+        self.setCursorPosition(self.__cursorPosition + 1)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToPreviousChar(self):
+        """
+        Public method to move the cursor to the previous byte.
+        """
+        self.setCursorPosition(self.__cursorPosition - 1)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToEndOfLine(self):
+        """
+        Public method to move the cursor to the end of the current line.
+        """
+        self.setCursorPosition(self.__cursorPosition |
+                               (2 * self.BYTES_PER_LINE - 1))
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToStartOfLine(self):
+        """
+        Public method to move the cursor to the beginning of the current line.
+        """
+        self.setCursorPosition(
+            self.__cursorPosition -
+            (self.__cursorPosition % (2 * self.BYTES_PER_LINE)))
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToPreviousLine(self):
+        """
+        Public method to move the cursor to the previous line.
+        """
+        self.setCursorPosition(self.__cursorPosition - 2 * self.BYTES_PER_LINE)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToNextLine(self):
+        """
+        Public method to move the cursor to the next line.
+        """
+        self.setCursorPosition(self.__cursorPosition + 2 * self.BYTES_PER_LINE)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToNextPage(self):
+        """
+        Public method to move the cursor to the next page.
+        """
+        self.setCursorPosition(
+            self.__cursorPosition +
+            (self.__rowsShown - 1) * 2 * self.BYTES_PER_LINE)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToPreviousPage(self):
+        """
+        Public method to move the cursor to the previous page.
+        """
+        self.setCursorPosition(
+            self.__cursorPosition -
+            (self.__rowsShown - 1) * 2 * self.BYTES_PER_LINE)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToEndOfDocument(self):
+        """
+        Public method to move the cursor to the end of the data.
+        """
+        self.setCursorPosition(self.__chunks.size() * 2)
+        self.__resetSelection(self.__cursorPosition)
+    
+    def moveCursorToStartOfDocument(self):
+        """
+        Public method to move the cursor to the start of the data.
+        """
+        self.setCursorPosition(0)
+        self.__resetSelection(self.__cursorPosition)
+    
+    ####################################################
+    ## Selection commands
+    ####################################################
+    
+    def deselectAll(self):
+        """
+        Public method to deselect all data.
+        """
+        self.__resetSelection(0)
+        self.__refresh()
+    
+    def selectAll(self):
+        """
+        Public method to select all data.
+        """
+        self.__resetSelection(0)
+        self.__setSelection(2 * self.__chunks.size() + 1)
+        self.__refresh()
+    
+    def selectNextChar(self):
+        """
+        Public method to extend the selection by one byte right.
+        """
+        pos = self.__cursorPosition + 1
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectPreviousChar(self):
+        """
+        Public method to extend the selection by one byte left.
+        """
+        pos = self.__cursorPosition - 1
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectToEndOfLine(self):
+        """
+        Public method to extend the selection to the end of line.
+        """
+        pos = self.__cursorPosition - \
+            (self.__cursorPosition % (2 * self.BYTES_PER_LINE)) + \
+            2 * self.BYTES_PER_LINE
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectToStartOfLine(self):
+        """
+        Public method to extend the selection to the start of line.
+        """
+        pos = self.__cursorPosition - \
+            (self.__cursorPosition % (2 * self.BYTES_PER_LINE))
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectPreviousLine(self):
+        """
+        Public method to extend the selection one line up.
+        """
+        pos = self.__cursorPosition - 2 * self.BYTES_PER_LINE
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectNextLine(self):
+        """
+        Public method to extend the selection one line down.
+        """
+        pos = self.__cursorPosition + 2 * self.BYTES_PER_LINE
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectNextPage(self):
+        """
+        Public method to extend the selection one page down.
+        """
+        pos = self.__cursorPosition + \
+            ((self.viewport().height() // self.__pxCharHeight) - 1) * \
+            2 * self.BYTES_PER_LINE
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectPreviousPage(self):
+        """
+        Public method to extend the selection one page up.
+        """
+        pos = self.__cursorPosition - \
+            ((self.viewport().height() // self.__pxCharHeight) - 1) * \
+            2 * self.BYTES_PER_LINE
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectEndOfDocument(self):
+        """
+        Public method to extend the selection to the end of the data.
+        """
+        pos = self.__chunks.size() * 2
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    def selectStartOfDocument(self):
+        """
+        Public method to extend the selection to the start of the data.
+        """
+        pos = 0
+        self.setCursorPosition(pos)
+        self.__setSelection(pos)
+    
+    ####################################################
+    ## Edit commands
+    ####################################################
+    
+    def cut(self):
+        """
+        Public method to cut the selected bytes and move them to the clipboard.
+        """
+        if not self.__readOnly:
+            byteArray = self.__toHex(self.__chunks.data(
+                self.__getSelectionBegin(), self.__getSelectionLength()))
+            idx = 32
+            while idx < len(byteArray):
+                byteArray.insert(idx, "\n")
+                idx += 33
+            cb = QApplication.clipboard()
+            cb.setText(byteArray.decode(encoding="latin1"))
+            if self.__overwriteMode:
+                length = self.__getSelectionLength()
+                self.replaceByteArray(self.__getSelectionBegin(), length,
+                                      bytearray(length))
+            else:
+                self.remove(self.__getSelectionBegin(),
+                            self.__getSelectionLength())
+            self.setCursorPosition(2 * self.__getSelectionBegin())
+            self.__resetSelection(2 * self.__getSelectionBegin())
+    
+    def copy(self):
+        """
+        Public method to copy the selected bytes to the clipboard.
+        """
+        byteArray = self.__toHex(self.__chunks.data(
+            self.__getSelectionBegin(), self.__getSelectionLength()))
+        idx = 32
+        while idx < len(byteArray):
+            byteArray.insert(idx, "\n")
+            idx += 33
+        cb = QApplication.clipboard()
+        cb.setText(byteArray.decode(encoding="latin1"))
+    
+    def paste(self):
+        """
+        Public method to paste bytes from the clipboard.
+        """
+        if not self.__readOnly:
+            cb = QApplication.clipboard()
+            byteArray = self.__fromHex(cb.text().encode(encoding="latin1"))
+            if self.__overwriteMode:
+                self.replaceByteArray(self.__bPosCurrent, len(byteArray),
+                                      byteArray)
+            else:
+                self.insertByteArray(self.__bPosCurrent, byteArray)
+            self.setCursorPosition(
+                self.__cursorPosition + 2 * len(byteArray))
+            self.__resetSelection(2 * self.__getSelectionBegin())
+    
+    def deleteByte(self):
+        """
+        Public method to delete the current byte.
+        """
+        if not self.__readOnly:
+            if self.hasSelection():
+                self.__bPosCurrent = self.__getSelectionBegin()
+                if self.__overwriteMode:
+                    byteArray = bytearray(self.__getSelectionLength())
+                    self.replaceByteArray(self.__bPosCurrent, len(byteArray),
+                                          byteArray)
+                else:
+                    self.remove(self.__bPosCurrent,
+                                self.__getSelectionLength())
+            else:
+                if self.__overwriteMode:
+                    self.replace(self.__bPosCurrent, 0)
+                else:
+                    self.remove(self.__bPosCurrent, 1)
+            self.setCursorPosition(2 * self.__bPosCurrent)
+            self.__resetSelection(2 * self.__bPosCurrent)
+    
+    def deleteByteBack(self):
+        """
+        Public method to delete the previous byte.
+        """
+        if not self.__readOnly:
+            if self.hasSelection():
+                self.__bPosCurrent = self.__getSelectionBegin()
+                self.setCursorPosition(2 * self.__bPosCurrent)
+                if self.__overwriteMode:
+                    byteArray = bytearray(self.__getSelectionLength())
+                    self.replaceByteArray(self.__bPosCurrent, len(byteArray),
+                                          byteArray)
+                else:
+                    self.remove(self.__bPosCurrent,
+                                self.__getSelectionLength())
+            else:
+                self.__bPosCurrent -= 1
+                if self.__overwriteMode:
+                    self.replace(self.__bPosCurrent, 0)
+                else:
+                    self.remove(self.__bPosCurrent, 1)
+                self.setCursorPosition(2 * self.__bPosCurrent)
+            self.__resetSelection(2 * self.__bPosCurrent)
+    
+    ####################################################
+    ## Event handling methods
+    ####################################################
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected method to handle key press events.
+        
+        @param evt reference to the key event
+        @type QKeyEvent
+        """
+        if not self.__embedded:
+            # Cursor movements
+            if evt.matches(QKeySequence.MoveToNextChar):
+                self.moveCursorToNextChar()
+            elif evt.matches(QKeySequence.MoveToPreviousChar):
+                self.moveCursorToPreviousChar()
+            elif evt.matches(QKeySequence.MoveToEndOfLine):
+                self.moveCursorToEndOfLine()
+            elif evt.matches(QKeySequence.MoveToStartOfLine):
+                self.moveCursorToStartOfLine()
+            elif evt.matches(QKeySequence.MoveToPreviousLine):
+                self.moveCursorToPreviousLine()
+            elif evt.matches(QKeySequence.MoveToNextLine):
+                self.moveCursorToNextLine()
+            elif evt.matches(QKeySequence.MoveToNextPage):
+                self.moveCursorToNextPage()
+            elif evt.matches(QKeySequence.MoveToPreviousPage):
+                self.moveCursorToPreviousPage()
+            elif evt.matches(QKeySequence.MoveToEndOfDocument):
+                self.moveCursorToEndOfDocument()
+            elif evt.matches(QKeySequence.MoveToStartOfDocument):
+                self.moveCursorToStartOfDocument()
+            
+            # Selection commands
+            elif evt.matches(QKeySequence.SelectAll):
+                self.selectAll()
+            elif evt.matches(QKeySequence.SelectNextChar):
+                self.selectNextChar()
+            elif evt.matches(QKeySequence.SelectPreviousChar):
+                self.selectPreviousChar()
+            elif evt.matches(QKeySequence.SelectEndOfLine):
+                self.selectToEndOfLine()
+            elif evt.matches(QKeySequence.SelectStartOfLine):
+                self.selectToStartOfLine()
+            elif evt.matches(QKeySequence.SelectPreviousLine):
+                self.selectPreviousLine()
+            elif evt.matches(QKeySequence.SelectNextLine):
+                self.selectNextLine()
+            elif evt.matches(QKeySequence.SelectNextPage):
+                self.selectNextPage()
+            elif evt.matches(QKeySequence.SelectPreviousPage):
+                self.selectPreviousPage()
+            elif evt.matches(QKeySequence.SelectEndOfDocument):
+                self.selectEndOfDocument()
+            elif evt.matches(QKeySequence.SelectStartOfDocument):
+                self.selectStartOfDocument()
+            
+            # Edit commands
+            elif evt.matches(QKeySequence.Copy):
+                self.copy()
+            elif evt.key() == Qt.Key_Insert and \
+                    evt.modifiers() == Qt.NoModifier:
+                self.setOverwriteMode(not self.overwriteMode())
+                self.setCursorPosition(self.__cursorPosition)
+            
+            if not self.__readOnly:
+                if evt.matches(QKeySequence.Cut):
+                    self.cut()
+                elif evt.matches(QKeySequence.Paste):
+                    self.paste()
+                elif evt.matches(QKeySequence.Delete):
+                    self.deleteByte()
+                elif evt.key() == Qt.Key_Backspace and \
+                        evt.modifiers() == Qt.NoModifier:
+                    self.deleteByteBack()
+                elif evt.matches(QKeySequence.Undo):
+                    self.undo()
+                elif evt.matches(QKeySequence.Redo):
+                    self.redo()
+        
+        if not self.__readOnly and \
+            QApplication.keyboardModifiers() in [
+                Qt.NoModifier, Qt.KeypadModifier]:
+            # some hex input
+            key = evt.text()
+            if key and key in "0123456789abcdef":
+                if self.hasSelection():
+                    if self.__overwriteMode:
+                        length = self.__getSelectionLength()
+                        self.replaceByteArray(
+                            self.__getSelectionBegin(), length,
+                            bytearray(length))
+                    else:
+                        self.remove(self.__getSelectionBegin(),
+                                    self.__getSelectionLength())
+                        self.__bPosCurrent = self.__getSelectionBegin()
+                    self.setCursorPosition(2 * self.__bPosCurrent)
+                    self.__resetSelection(2 * self.__bPosCurrent)
+                
+                # if in insert mode, insert a byte
+                if not self.__overwriteMode:
+                    if (self.__cursorPosition % 2) == 0:
+                        self.insert(self.__bPosCurrent, 0)
+                
+                # change content
+                if self.__chunks.size() > 0:
+                    hexValue = self.__toHex(
+                        self.__chunks.data(self.__bPosCurrent, 1))
+                    if (self.__cursorPosition % 2) == 0:
+                        hexValue[0] = ord(key)
+                    else:
+                        hexValue[1] = ord(key)
+                    self.replace(self.__bPosCurrent,
+                                 self.__fromHex(hexValue)[0])
+                    
+                    self.setCursorPosition(self.__cursorPosition + 1)
+                    self.__resetSelection(self.__cursorPosition)
+        
+        self.__refresh()
+    
+    def mouseMoveEvent(self, evt):
+        """
+        Protected method to handle mouse moves.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        self.__blink = False
+        self.viewport().update()
+        actPos = self.cursorPositionFromPoint(evt.pos())
+        if actPos >= 0:
+            self.setCursorPosition(actPos)
+            self.__setSelection(actPos)
+    
+    def mousePressEvent(self, evt):
+        """
+        Protected method to handle mouse button presses.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        self.__blink = False
+        self.viewport().update()
+        cPos = self.cursorPositionFromPoint(evt.pos())
+        if cPos >= 0:
+            if evt.modifiers() == Qt.ShiftModifier:
+                self.__setSelection(cPos)
+            else:
+                self.__resetSelection(cPos)
+            self.setCursorPosition(cPos)
+    
+    def paintEvent(self, evt):
+        """
+        Protected method to handle paint events.
+        
+        @param evt reference to the paint event
+        @type QPaintEvent
+        """
+        painter = QPainter(self.viewport())
+        
+        if evt.rect() != self.__cursorRect:
+            pxOfsX = self.horizontalScrollBar().value()
+            pxPosStartY = self.__pxCharHeight
+            
+            # draw some patterns if needed
+            painter.fillRect(
+                evt.rect(), self.viewport().palette().color(QPalette.Base))
+            if self.__addressArea:
+                painter.fillRect(
+                    QRect(-pxOfsX, evt.rect().top(),
+                          self.__pxPosHexX - self.__pxGapAdrHex // 2 - pxOfsX,
+                          self.height()),
+                    self.__addressAreaColor)
+            if self.__asciiArea:
+                linePos = self.__pxPosAsciiX - (self.__pxGapHexAscii // 2)
+                painter.setPen(Qt.gray)
+                painter.drawLine(linePos - pxOfsX, evt.rect().top(),
+                                 linePos - pxOfsX, self.height())
+            
+            painter.setPen(
+                self.viewport().palette().color(QPalette.WindowText))
+            
+            # paint the address area
+            if self.__addressArea:
+                address = ""
+                row = 0
+                pxPosY = self.__pxCharHeight
+                while row <= len(self.__dataShown) // self.BYTES_PER_LINE:
+                    address = "{0:0{1}x}".format(
+                        self.__bPosFirst + row * self.BYTES_PER_LINE,
+                        self.__addrDigits)
+                    painter.drawText(self.__pxPosAdrX - pxOfsX, pxPosY,
+                                     address)
+                    # increment loop variables
+                    row += 1
+                    pxPosY += self.__pxCharHeight
+            
+            # paint hex and ascii area
+            colStandard = QPen(
+                self.viewport().palette().color(QPalette.WindowText))
+            
+            painter.setBackgroundMode(Qt.TransparentMode)
+            
+            row = 0
+            pxPosY = pxPosStartY
+            while row <= self.__rowsShown:
+                pxPosX = self.__pxPosHexX - pxOfsX
+                pxPosAsciiX2 = self.__pxPosAsciiX - pxOfsX
+                bPosLine = row * self.BYTES_PER_LINE
+                
+                colIdx = 0
+                while bPosLine + colIdx < len(self.__dataShown) and \
+                        colIdx < self.BYTES_PER_LINE:
+                    c = self.viewport().palette().color(QPalette.Base)
+                    painter.setPen(colStandard)
+                    
+                    posBa = self.__bPosFirst + bPosLine + colIdx
+                    if self.__getSelectionBegin() <= posBa and \
+                            self.__getSelectionEnd() > posBa:
+                        c = self.__selectionBrush.color()
+                        painter.setPen(self.__selectionPen)
+                    elif self.__highlighting:
+                        if self.__markedShown and self.__markedShown[
+                                posBa - self.__bPosFirst]:
+                            c = self.__highlightingBrush.color()
+                            painter.setPen(self.__highlightingPen)
+                    
+                    # render hex value
+                    r = QRect()
+                    if colIdx == 0:
+                        r.setRect(
+                            pxPosX,
+                            pxPosY - self.__pxCharHeight +
+                            self.__pxSelectionSub,
+                            2 * self.__pxCharWidth,
+                            self.__pxCharHeight)
+                    else:
+                        r.setRect(
+                            pxPosX - self.__pxCharWidth,
+                            pxPosY - self.__pxCharHeight +
+                            self.__pxSelectionSub,
+                            3 * self.__pxCharWidth,
+                            self.__pxCharHeight)
+                    painter.fillRect(r, c)
+                    hex = chr(self.__hexDataShown[(bPosLine + colIdx) * 2]) + \
+                        chr(self.__hexDataShown[(bPosLine + colIdx) * 2 + 1])
+                    painter.drawText(pxPosX, pxPosY, hex)
+                    pxPosX += 3 * self.__pxCharWidth
+                    
+                    # render ascii value
+                    if self.__asciiArea:
+                        by = self.__dataShown[bPosLine + colIdx]
+                        if by < 0x20 or by > 0x7e:
+                            ch = "."
+                        else:
+                            ch = chr(by)
+                        r.setRect(
+                            pxPosAsciiX2,
+                            pxPosY - self.__pxCharHeight +
+                            self.__pxSelectionSub,
+                            self.__pxCharWidth,
+                            self.__pxCharHeight)
+                        painter.fillRect(r, c)
+                        painter.drawText(pxPosAsciiX2, pxPosY, ch)
+                        pxPosAsciiX2 += self.__pxCharWidth
+                    
+                    # increment loop variable
+                    colIdx += 1
+                
+                # increment loop variables
+                row += 1
+                pxPosY += self.__pxCharHeight
+            
+            painter.setBackgroundMode(Qt.TransparentMode)
+            painter.setPen(
+                self.viewport().palette().color(QPalette.WindowText))
+            
+        # paint cursor
+        if self.__blink and not self.__readOnly and self.hasFocus():
+            painter.fillRect(
+                self.__cursorRect, self.palette().color(QPalette.WindowText))
+        else:
+            if self.__hexDataShown:
+                try:
+                    c = chr(self.__hexDataShown[
+                            self.__cursorPosition - self.__bPosFirst * 2])
+                except IndexError:
+                    c = ""
+            else:
+                c = ""
+            painter.drawText(self.__pxCursorX, self.__pxCursorY, c)
+        
+        # emit event, if size has changed
+        if self.__lastEventSize != self.__chunks.size():
+            self.__lastEventSize = self.__chunks.size()
+            self.currentSizeChanged.emit(self.__lastEventSize)
+    
+    def resizeEvent(self, evt):
+        """
+        Protected method to handle resize events.
+        
+        @param evt reference to the resize event
+        @type QResizeEvent
+        """
+        self.__adjust()
+    
+    def __resetSelection(self, pos=None):
+        """
+        Private method to reset the selection.
+        
+        @param pos position to set selection start and end to
+            (if this is None, selection end is set to selection start)
+        @type int or None
+        """
+        if pos is None:
+            self.__bSelectionBegin = self.__bSelectionInit
+            self.__bSelectionEnd = self.__bSelectionInit
+        else:
+            if pos < 0:
+                pos = 0
+            pos = pos // 2
+            self.__bSelectionInit = pos
+            self.__bSelectionBegin = pos
+            self.__bSelectionEnd = pos
+        
+        self.selectionAvailable.emit(False)
+    
+    def __setSelection(self, pos):
+        """
+        Private method to set the selection.
+        
+        @param pos position
+        @type int
+        """
+        if pos < 0:
+            pos = 0
+        pos = pos // 2
+        if pos >= self.__bSelectionInit:
+            self.__bSelectionEnd = pos
+            self.__bSelectionBegin = self.__bSelectionInit
+        else:
+            self.__bSelectionBegin = pos
+            self.__bSelectionEnd = self.__bSelectionInit
+        
+        self.selectionAvailable.emit(True)
+    
+    def __getSelectionBegin(self):
+        """
+        Private method to get the start of the selection.
+        
+        @return selection start
+        @rtype int
+        """
+        return self.__bSelectionBegin
+    
+    def __getSelectionEnd(self):
+        """
+        Private method to get the end of the selection.
+        
+        @return selection end
+        @rtype int
+        """
+        return self.__bSelectionEnd
+    
+    def __getSelectionLength(self):
+        """
+        Private method to get the length of the selection.
+        
+        @return selection length
+        @rtype int
+        """
+        return self.__bSelectionEnd - self.__bSelectionBegin
+    
+    def hasSelection(self):
+        """
+        Public method to test for a selection.
+        
+        @return flag indicating the presence of a selection
+        @rtype bool
+        """
+        return self.__bSelectionBegin != self.__bSelectionEnd
+    
+    def __initialize(self):
+        """
+        Private method to do some initialization.
+        """
+        self.__undoStack.clear()
+        self.setAddressOffset(0)
+        self.__resetSelection(0)
+        self.setCursorPosition(0)
+        self.verticalScrollBar().setValue(0)
+        self.__modified = False
+    
+    def __readBuffers(self):
+        """
+        Private method to read the buffers.
+        """
+        self.__dataShown = self.__chunks.data(
+            self.__bPosFirst,
+            self.__bPosLast - self.__bPosFirst + self.BYTES_PER_LINE + 1,
+            self.__markedShown
+        )
+        self.__hexDataShown = self.__toHex(self.__dataShown)
+    
+    def __toHex(self, byteArray):
+        """
+        Private method to convert the data of a Python bytearray to hex.
+        
+        @param byteArray byte array to be converted
+        @type bytearray
+        @return converted data
+        @rtype bytearray
+        """
+        return bytearray(QByteArray(byteArray).toHex())
+    
+    def __fromHex(self, byteArray):
+        """
+        Private method to convert data of a Python bytearray from hex.
+        
+        @param byteArray byte array to be converted
+        @type bytearray
+        @return converted data
+        @rtype bytearray
+        """
+        return bytearray(QByteArray.fromHex(byteArray))
+        
+    def __toReadable(self, byteArray):
+        """
+        Private method to convert some data into a readable format.
+        
+        @param byteArray data to be converted
+        @type bytearray or QByteArray
+        @return readable data
+        @rtype str
+        """
+        byteArray = bytearray(byteArray)
+        result = ""
+        for i in range(0, len(byteArray), 16):
+            addrStr = "{0:0{1}x}".format(self.__addressOffset + i,
+                                         self.addressWidth())
+            hexStr = ""
+            ascStr = ""
+            for j in range(16):
+                if (i + j) < len(byteArray):
+                    hexStr += " {0:02x}".format(byteArray[i + j])
+                    by = byteArray[i + j]
+                    if by < 0x20 or by > 0x7e:
+                        ch = "."
+                    else:
+                        ch = chr(by)
+                    ascStr += ch
+            result += "{0} {1:<48} {2:<17}\n".format(addrStr, hexStr, ascStr)
+        return result
+    
+    @pyqtSlot()
+    def __adjust(self):
+        """
+        Private slot to recalculate pixel positions.
+        """
+        # recalculate graphics
+        if self.__addressArea:
+            self.__addrDigits = self.addressWidth()
+            self.__pxPosHexX = self.__pxGapAdr + \
+                self.__addrDigits * self.__pxCharWidth + self.__pxGapAdrHex
+        else:
+            self.__pxPosHexX = self.__pxGapAdrHex
+        self.__pxPosAdrX = self.__pxGapAdr
+        self.__pxPosAsciiX = self.__pxPosHexX + \
+            self.HEXCHARS_PER_LINE * self.__pxCharWidth + self.__pxGapHexAscii
+        
+        # set horizontal scrollbar
+        pxWidth = self.__pxPosAsciiX
+        if self.__asciiArea:
+            pxWidth += self.BYTES_PER_LINE * self.__pxCharWidth
+        self.horizontalScrollBar().setRange(
+            0, pxWidth - self.viewport().width())
+        self.horizontalScrollBar().setPageStep(self.viewport().width())
+        
+        # set vertical scrollbar
+        self.__rowsShown = \
+            (self.viewport().height() - 4) // self.__pxCharHeight
+        lineCount = (self.__chunks.size() // self.BYTES_PER_LINE) + 1
+        self.verticalScrollBar().setRange(0, lineCount - self.__rowsShown)
+        self.verticalScrollBar().setPageStep(self.__rowsShown)
+        
+        # do the rest
+        value = self.verticalScrollBar().value()
+        self.__bPosFirst = value * self.BYTES_PER_LINE
+        self.__bPosLast = \
+            self.__bPosFirst + self.__rowsShown * self.BYTES_PER_LINE - 1
+        if self.__bPosLast >= self.__chunks.size():
+            self.__bPosLast = self.__chunks.size() - 1
+        self.__readBuffers()
+        self.setCursorPosition(self.__cursorPosition)
+    
+    @pyqtSlot(int)
+    def __dataChangedPrivate(self, idx=0):
+        """
+        Private slot to handle data changes.
+        
+        @param idx index
+        @type int
+        """
+        self.__modified = (
+            self.__undoStack.cleanIndex() == -1 or
+            self.__undoStack.index() != self.__undoStack.cleanIndex())
+        self.__adjust()
+        self.dataChanged.emit(self.__modified)
+    
+    @pyqtSlot()
+    def __refresh(self):
+        """
+        Private slot to refresh the display.
+        """
+        self.ensureVisible()
+        self.__readBuffers()
+    
+    @pyqtSlot()
+    def __updateCursor(self):
+        """
+        Private slot to update the blinking cursor.
+        """
+        self.__blink = not self.__blink
+        self.viewport().update(self.__cursorRect)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HexEdit/__init__.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing a hex editor.
+"""
+
+#
+# This is a Python only variant of QHexEdit2 created by Winfried Simon.
+# Winfried Simon <winfried.simon@gmail.com>
+#
--- a/Preferences/__init__.py	Sat Jan 09 19:01:17 2016 +0100
+++ b/Preferences/__init__.py	Sat Jan 09 19:04:34 2016 +0100
@@ -1128,6 +1128,7 @@
         "HelpViewerGeometry": QByteArray(),
         "HelpInspectorGeometry": QByteArray(),
         "IconEditorGeometry": QByteArray(),
+        "HexEditorGeometry": QByteArray(),
         "MainGeometry": QByteArray(),
         "MainMaximized": False,
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/QScintilla/KeySequenceTranslator.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing functions to map QScintilla keyboard commands to
+QKeySequence standard keys.
+"""
+
+from __future__ import unicode_literals
+
+from PyQt5.QtCore import qVersion
+from PyQt5.QtGui import QKeySequence
+from PyQt5.Qsci import QsciScintilla
+
+__all__ = ["s2qTranslate"]
+
+Scintilla2QKeySequence = {
+    QsciScintilla.SCI_CHARLEFT: QKeySequence.MoveToPreviousChar,
+    QsciScintilla.SCI_CHARRIGHT: QKeySequence.MoveToNextChar,
+    QsciScintilla.SCI_LINEUP: QKeySequence.MoveToPreviousLine,
+    QsciScintilla.SCI_LINEDOWN: QKeySequence.MoveToNextLine,
+    QsciScintilla.SCI_WORDPARTLEFT: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDPARTRIGHT: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDLEFT: QKeySequence.MoveToNextWord,
+    QsciScintilla.SCI_WORDRIGHT: QKeySequence.MoveToPreviousWord,
+    QsciScintilla.SCI_VCHOME: QKeySequence.MoveToStartOfLine,
+    QsciScintilla.SCI_HOMEDISPLAY: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEEND: QKeySequence.MoveToEndOfLine,
+    QsciScintilla.SCI_LINESCROLLDOWN: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINESCROLLUP: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_PARAUP: QKeySequence.MoveToStartOfBlock,
+    QsciScintilla.SCI_PARADOWN: QKeySequence.MoveToEndOfBlock,
+    QsciScintilla.SCI_PAGEUP: QKeySequence.MoveToPreviousPage,
+    QsciScintilla.SCI_PAGEDOWN: QKeySequence.MoveToNextPage,
+    QsciScintilla.SCI_DOCUMENTSTART: QKeySequence.MoveToStartOfDocument,
+    QsciScintilla.SCI_DOCUMENTEND: QKeySequence.MoveToEndOfDocument,
+    QsciScintilla.SCI_TAB: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_BACKTAB: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_CHARLEFTEXTEND: QKeySequence.SelectPreviousChar,
+    QsciScintilla.SCI_CHARRIGHTEXTEND: QKeySequence.SelectNextChar,
+    QsciScintilla.SCI_LINEUPEXTEND: QKeySequence.SelectPreviousLine,
+    QsciScintilla.SCI_LINEDOWNEXTEND: QKeySequence.SelectNextLine,
+    QsciScintilla.SCI_WORDPARTLEFTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDPARTRIGHTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDLEFTEXTEND: QKeySequence.SelectPreviousWord,
+    QsciScintilla.SCI_WORDRIGHTEXTEND: QKeySequence.SelectNextWord,
+    QsciScintilla.SCI_VCHOMEEXTEND: QKeySequence.SelectStartOfLine,
+    QsciScintilla.SCI_LINEENDEXTEND: QKeySequence.SelectEndOfLine,
+    QsciScintilla.SCI_PARAUPEXTEND: QKeySequence.SelectStartOfBlock,
+    QsciScintilla.SCI_PARADOWNEXTEND: QKeySequence.SelectEndOfBlock,
+    QsciScintilla.SCI_PAGEUPEXTEND: QKeySequence.SelectPreviousPage,
+    QsciScintilla.SCI_PAGEDOWNEXTEND: QKeySequence.SelectNextPage,
+    QsciScintilla.SCI_DOCUMENTSTARTEXTEND: QKeySequence.SelectStartOfDocument,
+    QsciScintilla.SCI_DOCUMENTENDEXTEND: QKeySequence.SelectEndOfDocument,
+    QsciScintilla.SCI_DELETEBACK: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_DELETEBACKNOTLINE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_CLEAR: QKeySequence.Delete,
+    QsciScintilla.SCI_DELWORDLEFT: QKeySequence.DeleteStartOfWord,
+    QsciScintilla.SCI_DELWORDRIGHT: QKeySequence.DeleteEndOfWord,
+    QsciScintilla.SCI_DELLINELEFT: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_DELLINERIGHT: QKeySequence.DeleteEndOfLine,
+    QsciScintilla.SCI_NEWLINE: QKeySequence.InsertLineSeparator,
+    QsciScintilla.SCI_LINEDELETE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEDUPLICATE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINETRANSPOSE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINECUT: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINECOPY: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_EDITTOGGLEOVERTYPE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEENDDISPLAY: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEENDDISPLAYEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_FORMFEED: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_CANCEL: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEDOWNRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEUPRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_CHARLEFTRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_CHARRIGHTRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_VCHOMERECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEENDRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_PAGEUPRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_PAGEDOWNRECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_SELECTIONDUPLICATE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_SCROLLTOSTART: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_SCROLLTOEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_VERTICALCENTRECARET: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDRIGHTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDRIGHTENDEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDLEFTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_WORDLEFTENDEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_HOME: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_HOMEEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_HOMERECTEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_HOMEDISPLAYEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_HOMEWRAP: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_HOMEWRAPEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_VCHOMEWRAP: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_VCHOMEWRAPEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEENDWRAP: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LINEENDWRAPEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_STUTTEREDPAGEUP: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_STUTTEREDPAGEUPEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_STUTTEREDPAGEDOWN: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_STUTTEREDPAGEDOWNEXTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_DELWORDRIGHTEND: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_MOVESELECTEDLINESUP: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_MOVESELECTEDLINESDOWN: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_LOWERCASE: QKeySequence.UnknownKey,
+    QsciScintilla.SCI_UPPERCASE: QKeySequence.UnknownKey,
+}
+if qVersion() >= "5.2.0":
+    Scintilla2QKeySequence[QsciScintilla.SCI_LINEDELETE] = \
+        QKeySequence.DeleteCompleteLine,
+if qVersion() >= "5.5.0":
+    Scintilla2QKeySequence[QsciScintilla.SCI_DELETEBACK] = \
+        QKeySequence.Backspace
+
+
+def s2qTranslate(scintillaCommand):
+    """
+    Function to translate a QScintilla command to a QKeySequence.
+    
+    @param scintillaCommand QScintilla command
+    @type int
+    @return Qt key sequence
+    @rtype QKeySequence.StandardKey
+    """
+    assert scintillaCommand in Scintilla2QKeySequence
+    return Scintilla2QKeySequence[scintillaCommand]
--- a/eric6.e4p	Sat Jan 09 19:01:17 2016 +0100
+++ b/eric6.e4p	Sat Jan 09 19:04:34 2016 +0100
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE Project SYSTEM "Project-5.1.dtd">
 <!-- eric project file for project eric6 -->
-<!-- Copyright (C) 2015 Detlev Offenbach, detlev@die-offenbachs.de -->
+<!-- Copyright (C) 2016 Detlev Offenbach, detlev@die-offenbachs.de -->
 <Project version="5.1">
   <Language>en_US</Language>
   <ProjectWordList>Dictionaries/words.dic</ProjectWordList>
@@ -466,6 +466,11 @@
     <Source>Helpviewer/data/html_rc.py</Source>
     <Source>Helpviewer/data/icons_rc.py</Source>
     <Source>Helpviewer/data/javascript_rc.py</Source>
+    <Source>HexEdit/HexEditChunks.py</Source>
+    <Source>HexEdit/HexEditMainWindow.py</Source>
+    <Source>HexEdit/HexEditUndoStack.py</Source>
+    <Source>HexEdit/HexEditWidget.py</Source>
+    <Source>HexEdit/__init__.py</Source>
     <Source>IconEditor/IconEditorGrid.py</Source>
     <Source>IconEditor/IconEditorPalette.py</Source>
     <Source>IconEditor/IconEditorWindow.py</Source>
@@ -866,6 +871,7 @@
     <Source>QScintilla/Exporters/ExporterTEX.py</Source>
     <Source>QScintilla/Exporters/__init__.py</Source>
     <Source>QScintilla/GotoDialog.py</Source>
+    <Source>QScintilla/KeySequenceTranslator.py</Source>
     <Source>QScintilla/Lexers/Lexer.py</Source>
     <Source>QScintilla/Lexers/LexerBash.py</Source>
     <Source>QScintilla/Lexers/LexerBatch.py</Source>
@@ -1248,6 +1254,8 @@
     <Source>eric6_doc.py</Source>
     <Source>eric6_editor.py</Source>
     <Source>eric6_editor.pyw</Source>
+    <Source>eric6_hexeditor.py</Source>
+    <Source>eric6_hexeditor.pyw</Source>
     <Source>eric6_iconeditor.py</Source>
     <Source>eric6_iconeditor.pyw</Source>
     <Source>eric6_plugininstall.py</Source>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6_hexeditor.py	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Eric6 Hex Editor.
+
+This is the main Python script that performs the necessary initialization
+of the hex editor and starts the Qt event loop. This is a standalone version
+of the integrated hex editor.
+"""
+
+from __future__ import unicode_literals
+
+import Toolbox.PyQt4ImportHook  # __IGNORE_WARNING__
+
+try:  # Only for Py2
+    import Globals.compatibility_fixes     # __IGNORE_WARNING__
+except (ImportError):
+    pass
+
+import sys
+import os
+
+for arg in sys.argv[:]:
+    if arg.startswith("--config="):
+        import Globals
+        configDir = arg.replace("--config=", "")
+        Globals.setConfigDir(configDir)
+        sys.argv.remove(arg)
+    elif arg.startswith("--settings="):
+        from PyQt5.QtCore import QSettings
+        settingsDir = os.path.expanduser(arg.replace("--settings=", ""))
+        if not os.path.isdir(settingsDir):
+            os.makedirs(settingsDir)
+        QSettings.setPath(QSettings.IniFormat, QSettings.UserScope,
+                          settingsDir)
+        sys.argv.remove(arg)
+
+from Globals import AppInfo
+
+from Toolbox import Startup
+
+
+def createMainWidget(argv):
+    """
+    Function to create the main widget.
+    
+    @param argv list of commandline parameters (list of strings)
+    @return reference to the main widget (QWidget)
+    """
+    from HexEdit.HexEditMainWindow import HexEditMainWindow
+    
+    try:
+        fileName = argv[1]
+    except IndexError:
+        fileName = ""
+    
+    editor = HexEditMainWindow(fileName, None)
+    return editor
+
+
+def main():
+    """
+    Main entry point into the application.
+    """
+    options = [
+        ("--config=configDir",
+         "use the given directory as the one containing the config files"),
+        ("--settings=settingsDir",
+         "use the given directory to store the settings files"),
+        ("", "name of file to edit")
+    ]
+    appinfo = AppInfo.makeAppInfo(sys.argv,
+                                  "Eric6 Hex Editor",
+                                  "",
+                                  "Little tool to edit binary files.",
+                                  options)
+    res = Startup.simpleAppStartup(sys.argv,
+                                   appinfo,
+                                   createMainWidget)
+    sys.exit(res)
+
+if __name__ == '__main__':
+    main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6_hexeditor.pyw	Sat Jan 09 19:04:34 2016 +0100
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the Windows entry point.
+"""
+
+from __future__ import unicode_literals
+
+from eric6_hexeditor import main
+
+main()
Binary file icons/default/hexEditor.png has changed

eric ide

mercurial