6 """ |
6 """ |
7 Module implementing the hex editor main window. |
7 Module implementing the hex editor main window. |
8 """ |
8 """ |
9 |
9 |
10 import os |
10 import os |
|
11 import contextlib |
11 |
12 |
12 from PyQt5.QtCore import ( |
13 from PyQt5.QtCore import ( |
13 pyqtSignal, pyqtSlot, QFile, QFileInfo, QSize, QCoreApplication, QLocale |
14 pyqtSignal, pyqtSlot, QFile, QFileInfo, QSize, QCoreApplication, QLocale |
14 ) |
15 ) |
15 from PyQt5.QtGui import QKeySequence |
16 from PyQt5.QtGui import QKeySequence |
55 @param parent parent widget of this window (QWidget) |
56 @param parent parent widget of this window (QWidget) |
56 @param fromEric flag indicating whether it was called from within |
57 @param fromEric flag indicating whether it was called from within |
57 eric (boolean) |
58 eric (boolean) |
58 @param project reference to the project object (Project) |
59 @param project reference to the project object (Project) |
59 """ |
60 """ |
60 super(HexEditMainWindow, self).__init__(parent) |
61 super().__init__(parent) |
61 self.setObjectName("eric6_hex_editor") |
62 self.setObjectName("eric6_hex_editor") |
62 |
63 |
63 self.__srHistory = { |
64 self.__srHistory = { |
64 "search": [], |
65 "search": [], |
65 # list of recent searches (tuple of format type index and |
66 # list of recent searches (tuple of format type index and |
917 state = self.saveState() |
918 state = self.saveState() |
918 Preferences.setHexEditor("HexEditorState", state) |
919 Preferences.setHexEditor("HexEditorState", state) |
919 |
920 |
920 Preferences.setGeometry("HexEditorGeometry", self.saveGeometry()) |
921 Preferences.setGeometry("HexEditorGeometry", self.saveGeometry()) |
921 |
922 |
922 try: |
923 with contextlib.suppress(ValueError): |
923 if self.__fromEric or len(self.__class__.windows) > 1: |
924 if self.__fromEric or len(self.__class__.windows) > 1: |
924 del self.__class__.windows[ |
925 del self.__class__.windows[ |
925 self.__class__.windows.index(self)] |
926 self.__class__.windows.index(self)] |
926 except ValueError: |
|
927 pass |
|
928 |
927 |
929 if not self.__fromEric: |
928 if not self.__fromEric: |
930 Preferences.syncPreferences() |
929 Preferences.syncPreferences() |
931 |
930 |
932 self.__saveRecent() |
931 self.__saveRecent() |
938 |
937 |
939 def __openHexFileNewWindow(self): |
938 def __openHexFileNewWindow(self): |
940 """ |
939 """ |
941 Private slot called to open a binary file in new hex editor window. |
940 Private slot called to open a binary file in new hex editor window. |
942 """ |
941 """ |
943 if not self.__lastOpenPath: |
942 if ( |
944 if self.__project and self.__project.isOpen(): |
943 not self.__lastOpenPath and |
945 self.__lastOpenPath = self.__project.getProjectPath() |
944 self.__project is not None and |
|
945 self.__project.isOpen() |
|
946 ): |
|
947 self.__lastOpenPath = self.__project.getProjectPath() |
946 |
948 |
947 fileName = E5FileDialog.getOpenFileName( |
949 fileName = E5FileDialog.getOpenFileName( |
948 self, |
950 self, |
949 self.tr("Open binary file in new window"), |
951 self.tr("Open binary file in new window"), |
950 self.__lastOpenPath, |
952 self.__lastOpenPath, |
1010 def __openHexFile(self): |
1012 def __openHexFile(self): |
1011 """ |
1013 """ |
1012 Private slot to open a binary file. |
1014 Private slot to open a binary file. |
1013 """ |
1015 """ |
1014 if self.__maybeSave(): |
1016 if self.__maybeSave(): |
1015 if not self.__lastOpenPath: |
1017 if ( |
1016 if self.__project and self.__project.isOpen(): |
1018 not self.__lastOpenPath and |
1017 self.__lastOpenPath = self.__project.getProjectPath() |
1019 self.__project is not None and |
|
1020 self.__project.isOpen() |
|
1021 ): |
|
1022 self.__lastOpenPath = self.__project.getProjectPath() |
1018 |
1023 |
1019 fileName = E5FileDialog.getOpenFileName( |
1024 fileName = E5FileDialog.getOpenFileName( |
1020 self, |
1025 self, |
1021 self.tr("Open binary file"), |
1026 self.tr("Open binary file"), |
1022 self.__lastOpenPath, |
1027 self.__lastOpenPath, |
1038 Private method to save a binary file. |
1043 Private method to save a binary file. |
1039 |
1044 |
1040 @return flag indicating success |
1045 @return flag indicating success |
1041 @rtype bool |
1046 @rtype bool |
1042 """ |
1047 """ |
1043 if not self.__fileName: |
1048 ok = ( |
1044 ok = self.__saveHexFileAs() |
1049 self.__saveHexDataFile(self.__fileName) |
1045 else: |
1050 if self.__fileName else |
1046 ok = self.__saveHexDataFile(self.__fileName) |
1051 self.__saveHexFileAs() |
|
1052 ) |
1047 |
1053 |
1048 if ok: |
1054 if ok: |
1049 self.__editor.undoStack().setClean() |
1055 self.__editor.undoStack().setClean() |
1050 |
1056 |
1051 return ok |
1057 return ok |
1055 Private method to save the data to a new file. |
1061 Private method to save the data to a new file. |
1056 |
1062 |
1057 @return flag indicating success |
1063 @return flag indicating success |
1058 @rtype bool |
1064 @rtype bool |
1059 """ |
1065 """ |
1060 if not self.__lastSavePath: |
1066 if ( |
1061 if self.__project and self.__project.isOpen(): |
1067 not self.__lastSavePath and |
1062 self.__lastSavePath = self.__project.getProjectPath() |
1068 self.__project is not None and |
|
1069 self.__project.isOpen() |
|
1070 ): |
|
1071 self.__lastSavePath = self.__project.getProjectPath() |
1063 if not self.__lastSavePath and self.__lastOpenPath: |
1072 if not self.__lastSavePath and self.__lastOpenPath: |
1064 self.__lastSavePath = self.__lastOpenPath |
1073 self.__lastSavePath = self.__lastOpenPath |
1065 |
1074 |
1066 fileName = E5FileDialog.getSaveFileName( |
1075 fileName = E5FileDialog.getSaveFileName( |
1067 self, |
1076 self, |
1134 |
1143 |
1135 @param selectionOnly flag indicating to save the selection only |
1144 @param selectionOnly flag indicating to save the selection only |
1136 @type bool |
1145 @type bool |
1137 """ |
1146 """ |
1138 savePath = self.__lastSavePath |
1147 savePath = self.__lastSavePath |
1139 if not savePath: |
1148 if ( |
1140 if self.__project and self.__project.isOpen(): |
1149 not savePath and |
1141 savePath = self.__project.getProjectPath() |
1150 self.__project is not None and |
|
1151 self.__project.isOpen() |
|
1152 ): |
|
1153 savePath = self.__project.getProjectPath() |
1142 if not savePath and self.__lastOpenPath: |
1154 if not savePath and self.__lastOpenPath: |
1143 savePath = self.__lastOpenPath |
1155 savePath = self.__lastOpenPath |
1144 |
1156 |
1145 fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( |
1157 fileName, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( |
1146 self, |
1158 self, |
1173 self, self.tr("eric Hex Editor"), |
1185 self, self.tr("eric Hex Editor"), |
1174 self.tr("Cannot write file '{0}:\n{1}.") |
1186 self.tr("Cannot write file '{0}:\n{1}.") |
1175 .format(fileName, file.errorString())) |
1187 .format(fileName, file.errorString())) |
1176 return |
1188 return |
1177 |
1189 |
1178 if selectionOnly: |
1190 readableData = ( |
1179 readableData = self.__editor.selectionToReadableString() |
1191 self.__editor.selectionToReadableString() |
1180 else: |
1192 if selectionOnly else |
1181 readableData = self.__editor.toReadableString() |
1193 self.__editor.toReadableString() |
|
1194 ) |
1182 res = file.write(readableData.encode("latin1")) != -1 |
1195 res = file.write(readableData.encode("latin1")) != -1 |
1183 file.close() |
1196 file.close() |
1184 |
1197 |
1185 if not res: |
1198 if not res: |
1186 E5MessageBox.warning( |
1199 E5MessageBox.warning( |
1222 """ |
1235 """ |
1223 self.__fileName = fileName |
1236 self.__fileName = fileName |
1224 # insert filename into list of recently opened files |
1237 # insert filename into list of recently opened files |
1225 self.__addToRecentList(fileName) |
1238 self.__addToRecentList(fileName) |
1226 |
1239 |
1227 if not self.__fileName: |
1240 shownName = ( |
1228 shownName = self.tr("Untitled") |
1241 self.tr("Untitled") |
1229 else: |
1242 if not self.__fileName else |
1230 shownName = self.__strippedName(self.__fileName) |
1243 self.__strippedName(self.__fileName) |
|
1244 ) |
1231 |
1245 |
1232 self.setWindowTitle(self.tr("{0}[*] - {1}") |
1246 self.setWindowTitle(self.tr("{0}[*] - {1}") |
1233 .format(shownName, self.tr("Hex Editor"))) |
1247 .format(shownName, self.tr("Hex Editor"))) |
1234 |
1248 |
1235 self.setWindowModified(self.__editor.isModified()) |
1249 self.setWindowModified(self.__editor.isModified()) |
1307 """ |
1321 """ |
1308 Private method to handle the search action. |
1322 Private method to handle the search action. |
1309 """ |
1323 """ |
1310 self.__replaceWidget.hide() |
1324 self.__replaceWidget.hide() |
1311 self.__gotoWidget.hide() |
1325 self.__gotoWidget.hide() |
1312 if self.__editor.hasSelection(): |
1326 txt = ( |
1313 txt = self.__editor.selectionToHexString() |
1327 self.__editor.selectionToHexString() |
1314 else: |
1328 if self.__editor.hasSelection() else |
1315 txt = "" |
1329 "" |
|
1330 ) |
1316 self.__searchWidget.show(txt) |
1331 self.__searchWidget.show(txt) |
1317 |
1332 |
1318 def __replace(self): |
1333 def __replace(self): |
1319 """ |
1334 """ |
1320 Private method to handle the replace action. |
1335 Private method to handle the replace action. |
1321 """ |
1336 """ |
1322 self.__searchWidget.hide() |
1337 self.__searchWidget.hide() |
1323 self.__gotoWidget.hide() |
1338 self.__gotoWidget.hide() |
1324 if self.__editor.hasSelection(): |
1339 txt = ( |
1325 txt = self.__editor.selectionToHexString() |
1340 self.__editor.selectionToHexString() |
1326 else: |
1341 if self.__editor.hasSelection() else |
1327 txt = "" |
1342 "" |
|
1343 ) |
1328 self.__replaceWidget.show(txt) |
1344 self.__replaceWidget.show(txt) |
1329 |
1345 |
1330 def __goto(self): |
1346 def __goto(self): |
1331 """ |
1347 """ |
1332 Private method to handle the goto action. |
1348 Private method to handle the goto action. |
1363 |
1379 |
1364 def __showPreferences(self): |
1380 def __showPreferences(self): |
1365 """ |
1381 """ |
1366 Private slot to set the preferences. |
1382 Private slot to set the preferences. |
1367 """ |
1383 """ |
1368 from Preferences.ConfigurationDialog import ConfigurationDialog |
1384 from Preferences.ConfigurationDialog import ( |
|
1385 ConfigurationDialog, ConfigurationMode |
|
1386 ) |
1369 dlg = ConfigurationDialog( |
1387 dlg = ConfigurationDialog( |
1370 None, 'Configuration', True, fromEric=True, |
1388 None, 'Configuration', True, fromEric=True, |
1371 displayMode=ConfigurationDialog.HexEditorMode) |
1389 displayMode=ConfigurationMode.HEXEDITORMODE) |
1372 dlg.preferencesChanged.connect( |
1390 dlg.preferencesChanged.connect( |
1373 self.__preferencesChangedByLocalPreferencesDialog) |
1391 self.__preferencesChangedByLocalPreferencesDialog) |
1374 dlg.show() |
1392 dlg.show() |
1375 dlg.showConfigurationPageByName("hexEditorPage") |
1393 dlg.showConfigurationPageByName("hexEditorPage") |
1376 dlg.exec() |
1394 dlg.exec() |
1415 """ |
1433 """ |
1416 self.__loadRecent() |
1434 self.__loadRecent() |
1417 |
1435 |
1418 self.__recentMenu.clear() |
1436 self.__recentMenu.clear() |
1419 |
1437 |
1420 idx = 1 |
1438 for idx, rs in enumerate(self.__recent, start=1): |
1421 for rs in self.__recent: |
1439 formatStr = '&{0:d}. {1}' if idx < 10 else '{0:d}. {1}' |
1422 if idx < 10: |
|
1423 formatStr = '&{0:d}. {1}' |
|
1424 else: |
|
1425 formatStr = '{0:d}. {1}' |
|
1426 act = self.__recentMenu.addAction( |
1440 act = self.__recentMenu.addAction( |
1427 formatStr.format( |
1441 formatStr.format( |
1428 idx, |
1442 idx, |
1429 Utilities.compactPath( |
1443 Utilities.compactPath( |
1430 rs, HexEditMainWindow.maxMenuFilePathLen))) |
1444 rs, HexEditMainWindow.maxMenuFilePathLen))) |
1431 act.setData(rs) |
1445 act.setData(rs) |
1432 act.setEnabled(QFileInfo(rs).exists()) |
1446 act.setEnabled(QFileInfo(rs).exists()) |
1433 idx += 1 |
|
1434 |
1447 |
1435 self.__recentMenu.addSeparator() |
1448 self.__recentMenu.addSeparator() |
1436 self.__recentMenu.addAction(self.tr('&Clear'), self.__clearRecent) |
1449 self.__recentMenu.addAction(self.tr('&Clear'), self.__clearRecent) |
1437 |
1450 |
1438 @pyqtSlot(QAction) |
1451 @pyqtSlot(QAction) |