Tue, 24 Jan 2023 10:52:27 +0100
Merged with branch 'pdf_viewer' to start next development phase.
scripts/install.py | file | annotate | diff | comparison | revisions |
--- a/eric7.epj Tue Jan 24 10:03:59 2023 +0100 +++ b/eric7.epj Tue Jan 24 10:52:27 2023 +0100 @@ -600,6 +600,7 @@ "src/eric7/Preferences/ConfigurationPages/MultiProjectPage.ui", "src/eric7/Preferences/ConfigurationPages/NetworkPage.ui", "src/eric7/Preferences/ConfigurationPages/NotificationsPage.ui", + "src/eric7/Preferences/ConfigurationPages/PdfViewerPage.ui", "src/eric7/Preferences/ConfigurationPages/PipPage.ui", "src/eric7/Preferences/ConfigurationPages/PluginManagerPage.ui", "src/eric7/Preferences/ConfigurationPages/PrinterPage.ui", @@ -1197,6 +1198,7 @@ "src/eric7/EricWidgets/EricSingleApplication.py", "src/eric7/EricWidgets/EricSpellCheckedTextEdit.py", "src/eric7/EricWidgets/EricSqueezeLabels.py", + "src/eric7/EricWidgets/EricStretchableSpacer.py", "src/eric7/EricWidgets/EricStringListEditWidget.py", "src/eric7/EricWidgets/EricTabWidget.py", "src/eric7/EricWidgets/EricTableView.py", @@ -1314,6 +1316,15 @@ "src/eric7/Network/IRC/IrcWidget.py", "src/eric7/Network/IRC/__init__.py", "src/eric7/Network/__init__.py", + "src/eric7/PdfViewer/PdfGoToDialog.py", + "src/eric7/PdfViewer/PdfInfoWidget.py", + "src/eric7/PdfViewer/PdfPageSelector.py", + "src/eric7/PdfViewer/PdfSearchWidget.py", + "src/eric7/PdfViewer/PdfToCWidget.py", + "src/eric7/PdfViewer/PdfView.py", + "src/eric7/PdfViewer/PdfViewerWindow.py", + "src/eric7/PdfViewer/PdfZoomSelector.py", + "src/eric7/PdfViewer/__init__.py", "src/eric7/PipInterface/Pip.py", "src/eric7/PipInterface/PipDialog.py", "src/eric7/PipInterface/PipFileSelectionDialog.py", @@ -1803,6 +1814,7 @@ "src/eric7/Preferences/ConfigurationPages/MultiProjectPage.py", "src/eric7/Preferences/ConfigurationPages/NetworkPage.py", "src/eric7/Preferences/ConfigurationPages/NotificationsPage.py", + "src/eric7/Preferences/ConfigurationPages/PdfViewerPage.py", "src/eric7/Preferences/ConfigurationPages/PipPage.py", "src/eric7/Preferences/ConfigurationPages/PluginManagerPage.py", "src/eric7/Preferences/ConfigurationPages/PrinterPage.py", @@ -2362,6 +2374,8 @@ "src/eric7/eric7_iconeditor.pyw", "src/eric7/eric7_ide.py", "src/eric7/eric7_ide.pyw", + "src/eric7/eric7_pdf.py", + "src/eric7/eric7_pdf.pyw", "src/eric7/eric7_plugininstall.py", "src/eric7/eric7_plugininstall.pyw", "src/eric7/eric7_pluginrepository.py",
--- a/pyproject.toml Tue Jan 24 10:03:59 2023 +0100 +++ b/pyproject.toml Tue Jan 24 10:52:27 2023 +0100 @@ -107,6 +107,7 @@ eric7_hexeditor = "eric7.eric7_hexeditor:main" eric7_iconeditor = "eric7.eric7_iconeditor:main" eric7_ide = "eric7.eric7_ide:main" +eric7_pdf = "eric7.eric7_pdf:main" eric7_plugininstall = "eric7.eric7_plugininstall:main" eric7_pluginrepository = "eric7.eric7_pluginrepository:main" eric7_pluginuninstall = "eric7.eric7_pluginuninstall:main"
--- a/scripts/install.py Tue Jan 24 10:03:59 2023 +0100 +++ b/scripts/install.py Tue Jan 24 10:52:27 2023 +0100 @@ -513,6 +513,7 @@ "eric7_hexeditor", "eric7_iconeditor", "eric7_ide", + "eric7_pdf", "eric7_plugininstall", "eric7_pluginrepository", "eric7_pluginuninstall", @@ -751,6 +752,7 @@ "eric7_hexeditor", "eric7_iconeditor", "eric7_ide", + "eric7_pdf", "eric7_plugininstall", "eric7_pluginrepository", "eric7_pluginuninstall",
--- a/scripts/uninstall.py Tue Jan 24 10:03:59 2023 +0100 +++ b/scripts/uninstall.py Tue Jan 24 10:52:27 2023 +0100 @@ -133,6 +133,7 @@ "eric7_hexeditor", "eric7_iconeditor", "eric7_ide", + "eric7_pdf", "eric7_plugininstall", "eric7_pluginrepository", "eric7_pluginuninstall",
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/EricWidgets/EricStretchableSpacer.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a stretchable spacer widget. +""" + +from PyQt6.QtWidgets import QHBoxLayout, QWidget + + +class EricStretchableSpacer(QWidget): + """ + Class implementing a stretchable spacer widget. + """ + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__layout = QHBoxLayout() + self.__layout.setContentsMargins(0, 0, 0, 0) + self.__layout.addStretch() + + self.setLayout(self.__layout)
--- a/src/eric7/Globals/__init__.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/Globals/__init__.py Tue Jan 24 10:52:27 2023 +0100 @@ -55,13 +55,14 @@ settingsNameRecent = "eric7recent" # key names of the various recent entries -recentNameMultiProject = "MultiProjects" -recentNameProject = "Projects" +recentNameBreakpointConditions = "BreakPointConditions" +recentNameBreakpointFiles = "BreakPointFiles" recentNameFiles = "Files" recentNameHexFiles = "HexFiles" recentNameHosts = "Hosts" -recentNameBreakpointFiles = "BreakPointFiles" -recentNameBreakpointConditions = "BreakPointConditions" +recentNameMultiProject = "MultiProjects" +recentNamePdfFiles = "PdfFiles" +recentNameProject = "Projects" recentNameTestDiscoverHistory = "UTDiscoverHistory" recentNameTestFileHistory = "UTFileHistory" recentNameTestNameHistory = "UTTestnameHistory"
--- a/src/eric7/HexEdit/HexEditMainWindow.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/HexEdit/HexEditMainWindow.py Tue Jan 24 10:52:27 2023 +0100 @@ -39,9 +39,9 @@ class HexEditMainWindow(EricMainWindow): """ - Class implementing the web browser main window. + Class implementing the hex editor main window. - @signal editorClosed() emitted after the window was requested to close down + @signal editorClosed() emitted after the window was requested to close """ editorClosed = pyqtSignal() @@ -108,7 +108,7 @@ self.__initToolbars() self.__createStatusBar() - self.__class__.windows.append(self) + HexEditMainWindow.windows.append(self) state = Preferences.getHexEditor("HexEditorState") self.restoreState(state) @@ -1028,8 +1028,8 @@ Preferences.setGeometry("HexEditorGeometry", self.saveGeometry()) with contextlib.suppress(ValueError): - if self.__fromEric or len(self.__class__.windows) > 1: - del self.__class__.windows[self.__class__.windows.index(self)] + if self.__fromEric or len(HexEditMainWindow.windows) > 1: + HexEditMainWindow.windows.remove(self) if not self.__fromEric: Preferences.syncPreferences() @@ -1323,7 +1323,7 @@ """ Private slot to close all other windows. """ - for win in self.__class__.windows[:]: + for win in HexEditMainWindow.windows[:]: if win != self: win.close()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfGoToDialog.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to enter a PDF page number to jump to. +""" + +from PyQt6.QtCore import Qt +from PyQt6.QtWidgets import ( + QDialog, + QDialogButtonBox, + QHBoxLayout, + QSlider, + QSpinBox, + QVBoxLayout, +) + + +class PdfGoToDialog(QDialog): + """ + Class implementing a dialog to enter a PDF page number to jump to. + """ + + def __init__(self, curPage, pageCount, parent=None): + """ + Constructor + + @param curPage current page number (0 based) + @type int + @param pageCount number of pages in the document + @type int + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__layout = QVBoxLayout() + self.setLayout(self.__layout) + + self.__hLayout = QHBoxLayout() + self.__valueSlider = QSlider(Qt.Orientation.Horizontal) + self.__valueSlider.setTickPosition(QSlider.TickPosition.TicksBothSides) + self.__valueSlider.setSingleStep(1) + self.__valueSlider.setPageStep(10) + self.__valueSlider.setMinimum(1) + self.__valueSlider.setMaximum(pageCount) + self.__valueSlider.setValue(curPage + 1) + self.__hLayout.addWidget(self.__valueSlider) + self.__valueSpinBox = QSpinBox() + self.__valueSpinBox.setMinimum(1) + self.__valueSpinBox.setMaximum(pageCount) + self.__valueSpinBox.setValue(curPage + 1) + self.__valueSpinBox.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.__hLayout.addWidget(self.__valueSpinBox) + self.__layout.addLayout(self.__hLayout) + + self.__buttonBox = QDialogButtonBox( + QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel, + Qt.Orientation.Horizontal, + ) + self.__layout.addWidget(self.__buttonBox) + + self.__valueSpinBox.setFocus(Qt.FocusReason.OtherFocusReason) + + # connect signals and slots + self.__buttonBox.accepted.connect(self.accept) + self.__buttonBox.rejected.connect(self.reject) + self.__valueSlider.valueChanged.connect(self.__valueSpinBox.setValue) + self.__valueSpinBox.valueChanged.connect(self.__valueSlider.setValue) + + def getPage(self): + """ + Public method to get the selected page. + + @return selected page (0 based) + @rtype int + """ + return self.__valueSpinBox.value() - 1
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfInfoWidget.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing an info widget showing data of a PDF document. +""" + +from PyQt6.QtCore import QFileInfo, Qt, pyqtSlot +from PyQt6.QtPdf import QPdfDocument +from PyQt6.QtWidgets import QFormLayout, QLabel, QWidget + +from eric7.Globals import dataString + + +class PdfInfoWidget(QWidget): + """ + Class implementing an info widget showing data of a PDF document. + """ + + def __init__(self, document, parent=None): + """ + Constructor + + @param document reference to the PDF document object + @type QPdfDocument + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__document = None + + self.__layout = QFormLayout(self) + self.__layout.setRowWrapPolicy(QFormLayout.RowWrapPolicy.WrapLongRows) + self.__layout.setFieldGrowthPolicy( + QFormLayout.FieldGrowthPolicy.AllNonFixedFieldsGrow + ) + self.__layout.setFormAlignment( + Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop + ) + self.__layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight) + + self.__infoLabels = { + "filePath": QLabel(), + "fileSize": QLabel(), + "title": QLabel(), + "subject": QLabel(), + "author": QLabel(), + "creator": QLabel(), + "producer": QLabel(), + "pages": QLabel(), + "creationDate": QLabel(), + "modificationDate": QLabel(), + "keywords": QLabel(), + "security": QLabel(), + } + for label in self.__infoLabels.values(): + label.setWordWrap(True) + self.__layout.addRow(self.tr("File Path:"), self.__infoLabels["filePath"]) + self.__layout.addRow(self.tr("File Size:"), self.__infoLabels["fileSize"]) + self.__layout.addRow(self.tr("Title:"), self.__infoLabels["title"]) + self.__layout.addRow(self.tr("Subject:"), self.__infoLabels["subject"]) + self.__layout.addRow(self.tr("Author:"), self.__infoLabels["author"]) + self.__layout.addRow(self.tr("Created with:"), self.__infoLabels["creator"]) + self.__layout.addRow(self.tr("Creator:"), self.__infoLabels["producer"]) + self.__layout.addRow(self.tr("Pages:"), self.__infoLabels["pages"]) + self.__layout.addRow(self.tr("Created at:"), self.__infoLabels["creationDate"]) + self.__layout.addRow( + self.tr("Last Modified at:"), self.__infoLabels["modificationDate"] + ) + self.__layout.addRow(self.tr("Keywords:"), self.__infoLabels["keywords"]) + self.__layout.addRow(self.tr("Security:"), self.__infoLabels["security"]) + + self.setLayout(self.__layout) + + self.setDocument(document) + + def setDocument(self, document): + """ + Public method to set the reference to the PDF document. + + @param document reference to the document + @type QPdfDocument + """ + if self.__document is not None: + self.__document.statusChanged.disconnect(self.__populateInfoLabels) + self.__document.pageCountChanged.disconnect(self.__handlePageCountChanged) + self.__document.passwordChanged.disconnect(self.__handlePasswordChanged) + + self.__document = document + + if document is not None: + self.__document.statusChanged.connect(self.__populateInfoLabels) + self.__document.pageCountChanged.connect(self.__handlePageCountChanged) + self.__document.passwordChanged.connect(self.__handlePasswordChanged) + + @pyqtSlot(QPdfDocument.Status) + def __populateInfoLabels(self, status): + """ + Private slot to populate the info labels upon a change of the document status. + + @param status document status + @type QPdfDocument.Status + """ + ready = status == QPdfDocument.Status.Ready + + self.__infoLabels["title"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.Title) if ready else "" + ) + self.__infoLabels["subject"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.Subject) + if ready + else "" + ) + self.__infoLabels["author"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.Author) if ready else "" + ) + self.__infoLabels["creator"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.Creator) + if ready + else "" + ) + self.__infoLabels["producer"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.Producer) + if ready + else "" + ) + self.__infoLabels["pages"].setText( + str(self.__document.pageCount()) if ready else "" + ) + self.__infoLabels["creationDate"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.CreationDate).toString( + "yyyy-MM-dd hh:mm:ss t" + ) + if ready + else "" + ) + self.__infoLabels["modificationDate"].setText( + self.__document.metaData( + QPdfDocument.MetaDataField.ModificationDate + ).toString("yyyy-MM-dd hh:mm:ss t") + if ready + else "" + ) + self.__infoLabels["keywords"].setText( + self.__document.metaData(QPdfDocument.MetaDataField.Keywords) + if ready + else "" + ) + + if ready: + self.__handlePasswordChanged() + else: + self.__infoLabels["security"].setText("") + + @pyqtSlot(int) + def __handlePageCountChanged(self, pageCount): + """ + Private slot to handle a change of the page count. + + @param pageCount changed page count + @type int + """ + self.__infoLabels["pages"].setText(str(pageCount)) + + @pyqtSlot() + def __handlePasswordChanged(self): + """ + Private slot to handle a change of the password. + """ + self.__infoLabels["security"].setText( + self.tr("Encrypted") + if self.__document.password() + else self.tr("Not Encrypted") + ) + + def setFileName(self, filename): + """ + Public method to set the file name info. + + @param filename DESCRIPTION + @type TYPE + """ + self.__infoLabels["filePath"].setText(filename) + if filename: + fi = QFileInfo(filename) + fileSize = fi.size() + self.__infoLabels["fileSize"].setText(dataString(fileSize)) + else: + self.__infoLabels["fileSize"].setText("")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfPageSelector.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a widget to select a PDF page to be shown. +""" + +import contextlib + +from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot +from PyQt6.QtGui import QIntValidator +from PyQt6.QtPdf import QPdfDocument +from PyQt6.QtWidgets import ( + QHBoxLayout, + QLabel, + QLineEdit, + QSizePolicy, + QToolButton, + QWidget, +) + +from eric7.EricGui import EricPixmapCache + + +class PdfPageSelector(QWidget): + """ + Class implementing a widget to select a PDF page to be shown. + + @signal valueChanged(int) emitted to signal the new value of the selector + @signal gotoPage() emitted to indicate the want to enter a page number via the + Go To dialog + """ + + valueChanged = pyqtSignal(int) + gotoPage = pyqtSignal() + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__document = None + + self.__prevButton = QToolButton(self) + self.__prevButton.setIcon(EricPixmapCache.getIcon("1uparrow")) + + self.__nextButton = QToolButton(self) + self.__nextButton.setIcon(EricPixmapCache.getIcon("1downarrow")) + + self.__pageButton = QToolButton() + self.__pageButton.setToolButtonStyle(Qt.ToolButtonStyle.ToolButtonTextOnly) + + self.__pageEntry = QLineEdit() + self.__pageEntry.setMaximumWidth(50) + self.__pageEntry.setMaxLength(10) + self.__pageEntry.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.__pageEntry.setSizePolicy( + QSizePolicy.Policy.Minimum, QSizePolicy.Policy.Fixed + ) + self.__pageLabel = QLabel() + + self.__layout = QHBoxLayout() + self.__layout.addWidget(self.__prevButton) + self.__layout.addWidget(self.__pageEntry) + self.__layout.addWidget(self.__pageLabel) + self.__layout.addWidget(QLabel(self.tr("of"))) + self.__layout.addWidget(self.__pageButton) + self.__layout.addWidget(self.__nextButton) + + self.setLayout(self.__layout) + + # Setup signal/slot connections + self.__prevButton.clicked.connect(self.__decrement) + self.__nextButton.clicked.connect(self.__increment) + self.__pageButton.clicked.connect(self.__pageButtonTriggered) + self.__pageEntry.editingFinished.connect(self.__pageEntered) + + self.__initialize() + + def __initialize(self): + """ + Private method to initialize some internal state. + """ + self.__value = -1 + self.__minimum = 0 + self.__maximum = 0 + + self.__prevButton.setEnabled(False) + self.__nextButton.setEnabled(False) + self.__pageEntry.clear() + self.__pageLabel.clear() + self.__pageButton.setText(" ") + + self.setEnabled(False) + + def setDocument(self, document): + """ + Public method to set a reference to the associated PDF document. + + @param document reference to the associated PDF document + @type QPdfDocument + """ + self.__document = document + self.__document.statusChanged.connect(self.__documentStatusChanged) + + @pyqtSlot(int) + def setValue(self, value): + """ + Public slot to set the value. + + Note: value is 0 based. + + @param value value to be set + @type int + """ + if value != self.__value: + with contextlib.suppress(RuntimeError): + self.__pageEntry.setText(self.__document.pageLabel(value)) + self.__pageLabel.setText(str(value + 1)) + + self.__value = value + + self.__prevButton.setEnabled(value > self.__minimum) + self.__nextButton.setEnabled(value < self.__maximum) + + self.valueChanged.emit(value) + + def value(self): + """ + Public method to get the current value. + + @return current value + @rtype int + """ + return self.__value + + def setMaximum(self, maximum): + """ + Public method to set the maximum value. + + Note: maximum is 0 based. + + @param maximum maximum value to be set + @type int + """ + self.__maximum = maximum + self.__nextButton.setEnabled(self.__value < self.__maximum) + self.__pageButton.setText(str(maximum + 1)) + + @pyqtSlot() + def __pageEntered(self): + """ + Private slot to handle the entering of a page value. + """ + model = self.__document.pageModel() + start = model.index(0) + indices = model.match( + start, QPdfDocument.PageModelRole.Label.value, self.__pageEntry.text() + ) + if indices: + self.setValue(indices[0].row()) + else: + # reset + blocked = self.__pageEntry.blockSignals(True) + self.__pageEntry.setText(self.__document.pageLabel(self.__value)) + self.__pageEntry.blockSignals(blocked) + + @pyqtSlot() + def __decrement(self): + """ + Private slot to decrement the current value. + """ + if self.__value > self.__minimum: + self.setValue(self.__value - 1) + + @pyqtSlot() + def __increment(self): + """ + Private slot to increment the current value. + """ + if self.__value < self.__maximum: + self.setValue(self.__value + 1) + + @pyqtSlot() + def __pageButtonTriggered(self): + """ + Private slot to handle the page button trigger. + """ + self.gotoPage.emit() + + @pyqtSlot(QPdfDocument.Status) + def __documentStatusChanged(self, status): + """ + Private slot to handle a change of the document status. + + @param status current document status + @type QPdfDocument.Status + """ + self.setEnabled(status == QPdfDocument.Status.Ready) + if status == QPdfDocument.Status.Ready: + numericalEntry = True + # test the first page + try: + _ = int(self.__document.pageLabel(0)) + except ValueError: + numericalEntry = False + # test the last page + try: + _ = int(self.__document.pageLabel(self.__document.pageCount() - 1)) + except ValueError: + numericalEntry = False + self.__pageEntry.setValidator( + QIntValidator(1, 99999) if numericalEntry else None + ) + self.__pageLabel.setVisible(not numericalEntry) + elif status == QPdfDocument.Status.Null: + self.__initialize()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfSearchWidget.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,441 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a Search widget. +""" + +from PyQt6.QtCore import QModelIndex, Qt, pyqtSignal, pyqtSlot +from PyQt6.QtPdf import QPdfDocument, QPdfLink, QPdfSearchModel +from PyQt6.QtWidgets import ( + QAbstractItemView, + QHBoxLayout, + QLabel, + QLineEdit, + QToolButton, + QTreeWidget, + QTreeWidgetItem, + QVBoxLayout, + QWidget, +) + +from eric7 import Preferences +from eric7.EricGui import EricPixmapCache + + +class PdfSearchResultsWidget(QTreeWidget): + """ + Class implementing a widget to show the search results. + + @signal rowCountChanged() emitted to indicate a change of the number + of items + @signal searchNextAvailable(bool) emitted to indicate the availability of + search results after the current one + @signal searchPrevAvailable(bool) emitted to indicate the availability of + search results before the current one + @signal searchResult(QPdfLink) emitted to send the link of a search result + @signal searchCleared() emitted to indicate that the search results have been + cleared + """ + + rowCountChanged = pyqtSignal() + searchNextAvailable = pyqtSignal(bool) + searchPrevAvailable = pyqtSignal(bool) + searchResult = pyqtSignal(QPdfLink) + searchCleared = pyqtSignal() + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.setColumnCount(2) + self.setHeaderHidden(True) + self.setAlternatingRowColors(True) + self.setSortingEnabled(False) + self.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) + self.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows) + + self.__searchModel = QPdfSearchModel(self) + self.__searchModel.modelReset.connect(self.__clear) + self.__searchModel.rowsInserted.connect(self.__rowsInserted) + + self.currentItemChanged.connect(self.__handleCurrentItemChanged) + + def setSearchString(self, searchString): + """ + Public method to set the search string. + + @param searchString search string + @type str + """ + self.__searchModel.setSearchString(searchString) + + def searchString(self): + """ + Public method to get the current search string. + + @return search string + @rtype str + """ + return self.__searchModel.searchString() + + def setDocument(self, document): + """ + Public method to set the PDF document object to be searched. + + @param document reference to the PDF document object + @type QPdfDocument + """ + self.__searchModel.setDocument(document) + + def document(self): + """ + Public method to get the reference to the PDF document object. + + @return reference to the PDF document object + @rtype QPdfDocument + """ + return self.__searchModel.document() + + @pyqtSlot() + def __clear(self): + """ + Private slot to clear the list of search results. + """ + self.clear() + + self.searchCleared.emit() + self.rowCountChanged.emit() + self.searchNextAvailable.emit(False) + self.searchPrevAvailable.emit(False) + + @pyqtSlot(QModelIndex, int, int) + def __rowsInserted(self, parent, first, last): + """ + Private slot to handle the insertion of rows of the search model. + + @param parent reference to the parent index + @type QModelIndex + @param first first row inserted + @type int + @param last last row inserted + @type int + """ + contextLength = Preferences.getPdfViewer("PdfSearchContextLength") + + for row in range(first, last + 1): + index = self.__searchModel.index(row, 0) + itm = QTreeWidgetItem( + self, + [ + self.tr("Page {0}").format( + self.__searchModel.document().pageLabel( + self.__searchModel.data( + index, QPdfSearchModel.Role.Page.value + ) + ) + ), + "", + ], + ) + contextBefore = self.__searchModel.data( + index, QPdfSearchModel.Role.ContextBefore.value + ) + if len(contextBefore) > contextLength: + contextBefore = "... {0}".format(contextBefore[-contextLength:]) + contextAfter = self.__searchModel.data( + index, QPdfSearchModel.Role.ContextAfter.value + ) + if len(contextAfter) > contextLength: + contextAfter = "{0} ...".format(contextAfter[:contextLength]) + resultLabel = QLabel( + self.tr( + "{0}<b>{1}</b>{2}", "context before, search string, context after" + ).format(contextBefore, self.searchString(), contextAfter) + ) + self.setItemWidget(itm, 1, resultLabel) + + if Preferences.getPdfViewer("PdfSearchHighlightAll"): + self.searchResult.emit(self.__searchModel.resultAtIndex(row)) + + for column in range(self.columnCount()): + self.resizeColumnToContents(column) + + self.rowCountChanged.emit() + self.searchNextAvailable.emit(True) + + def rowCount(self): + """ + Public method to get the number of rows. + + @return number of rows + @rtype int + """ + return self.topLevelItemCount() + + def currentRow(self): + """ + Public method to get the current row. + + @return current row + @rtype int + """ + curItem = self.currentItem() + if curItem is None: + return -1 + else: + return self.indexOfTopLevelItem(curItem) + + def setCurrentRow(self, row): + """ + Public method to set the current row. + + @param row row number to make the current row + @type int + """ + if 0 <= row < self.topLevelItemCount(): + self.setCurrentItem(self.topLevelItem(row)) + + def searchResultData(self, item, role): + """ + Public method to get data of a search result item. + + @param item reference to the search result item + @type QTreeWidgetItem + @param role item data role + @type QPdfSearchModel.Role or Qt.ItemDataRole + @return requested data + @rtype Any + """ + row = self.indexOfTopLevelItem(item) + index = self.__searchModel.index(row, 0) + return self.__searchModel.data(index, role) + + def getPdfLink(self, item): + """ + Public method to get the PDF link associated with a search result item. + + @param item reference to the search result item + @type QTreeWidgetItem + @return associated PDF link + @rtype QPdfLink + """ + row = self.indexOfTopLevelItem(item) + return self.__searchModel.resultAtIndex(row) + + @pyqtSlot() + def __handleCurrentItemChanged(self): + """ + Private slot to handle a change of the current item. + """ + hasSearchResults = bool(self.topLevelItemCount()) + currentRow = self.currentRow() + self.searchPrevAvailable.emit(hasSearchResults and currentRow > 0) + self.searchNextAvailable.emit( + hasSearchResults and currentRow < self.topLevelItemCount() - 1 + ) + + +class PdfSearchWidget(QWidget): + """ + Class implementing a Search widget. + + @signal searchResultActivated(QPdfLink) emitted to send the activated search + result link + @signal searchNextAvailable(bool) emitted to indicate the availability of + search results after the current one + @signal searchPrevAvailable(bool) emitted to indicate the availability of + search results before the current one + @signal searchResult(QPdfLink) emitted to send the link of a search result + @signal searchCleared() emitted to indicate that the search results have been + cleared + """ + + searchResultActivated = pyqtSignal(QPdfLink) + searchNextAvailable = pyqtSignal(bool) + searchPrevAvailable = pyqtSignal(bool) + searchResult = pyqtSignal(QPdfLink) + searchCleared = pyqtSignal() + + def __init__(self, document, parent=None): + """ + Constructor + + @param document reference to the PDF document object + @type QPdfDocument + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__layout = QVBoxLayout(self) + + # Line 1: a header label + self.__header = QLabel("<h2>{0}</h2>".format(self.tr("Search"))) + self.__header.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.__layout.addWidget(self.__header) + + # Line 2: search entry and navigation buttons + self.__searchLineLayout = QHBoxLayout() + + self.__searchEdit = QLineEdit(self) + self.__searchEdit.setPlaceholderText(self.tr("Search ...")) + self.__searchEdit.setClearButtonEnabled(True) + self.__searchLineLayout.addWidget(self.__searchEdit) + + # layout for the navigation buttons + self.__buttonsLayout = QHBoxLayout() + self.__buttonsLayout.setSpacing(0) + + self.__findPrevButton = QToolButton(self) + self.__findPrevButton.setToolTip( + self.tr("Press to move to the previous occurrence") + ) + self.__findPrevButton.setIcon(EricPixmapCache.getIcon("1leftarrow")) + self.__buttonsLayout.addWidget(self.__findPrevButton) + + self.__findNextButton = QToolButton(self) + self.__findNextButton.setToolTip( + self.tr("Press to move to the next occurrence") + ) + self.__findNextButton.setIcon(EricPixmapCache.getIcon("1rightarrow")) + self.__buttonsLayout.addWidget(self.__findNextButton) + + self.__searchLineLayout.addLayout(self.__buttonsLayout) + self.__layout.addLayout(self.__searchLineLayout) + + self.__resultsWidget = PdfSearchResultsWidget(self) + self.__resultsWidget.setDocument(document) + self.__layout.addWidget(self.__resultsWidget) + + self.__infoLabel = QLabel(self) + self.__infoLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.__layout.addWidget(self.__infoLabel) + + self.setLayout(self.__layout) + + self.__searchEdit.setEnabled(False) + self.__resultsWidget.setEnabled(False) + self.__findPrevButton.setEnabled(False) + self.__findNextButton.setEnabled(False) + + self.__resultsWidget.itemActivated.connect(self.__entrySelected) + document.statusChanged.connect(self.__handleDocumentStatus) + self.__searchEdit.returnPressed.connect(self.__search) + self.__searchEdit.textChanged.connect(self.__searchTextChanged) + self.__resultsWidget.searchNextAvailable.connect(self.searchNextAvailable) + self.__resultsWidget.searchPrevAvailable.connect(self.searchPrevAvailable) + self.__resultsWidget.searchNextAvailable.connect( + self.__findNextButton.setEnabled + ) + self.__resultsWidget.searchPrevAvailable.connect( + self.__findPrevButton.setEnabled + ) + self.__findNextButton.clicked.connect(self.nextResult) + self.__findPrevButton.clicked.connect(self.previousResult) + self.__resultsWidget.searchCleared.connect(self.searchCleared) + self.__resultsWidget.searchResult.connect(self.searchResult) + self.__resultsWidget.rowCountChanged.connect(self.__updateInfoLabel) + self.__resultsWidget.currentItemChanged.connect(self.__updateInfoLabel) + + self.__updateInfoLabel() + + @pyqtSlot(QPdfDocument.Status) + def __handleDocumentStatus(self, status): + """ + Private slot to handle a change of the document status. + + @param status document status + @type QPdfDocument.Status + """ + ready = status == QPdfDocument.Status.Ready + + self.__searchEdit.setEnabled(ready) + self.__resultsWidget.setEnabled(ready) + + if not ready: + self.__searchEdit.clear() + + @pyqtSlot(str) + def __searchTextChanged(self, text): + """ + Private slot to handle a change of the search string. + + @param text search string + @type str + """ + if not text: + self.__resultsWidget.setSearchString("") + + @pyqtSlot() + def __search(self): + """ + Private slot to initiate a new search. + """ + searchString = self.__searchEdit.text() + self.__resultsWidget.setSearchString(searchString) + + @pyqtSlot(QTreeWidgetItem) + def __entrySelected(self, item): + """ + Private slot to handle the selection of a search result entry. + + @param item reference to the selected item + @type QTreeWidgetItem + """ + link = self.__resultsWidget.getPdfLink(item) + self.searchResultActivated.emit(link) + + @pyqtSlot() + def nextResult(self): + """ + Public slot to activate the next result. + """ + row = self.__resultsWidget.currentRow() + if row < self.__resultsWidget.rowCount() - 1: + nextItem = self.__resultsWidget.topLevelItem(row + 1) + self.__resultsWidget.setCurrentItem(nextItem) + self.__entrySelected(nextItem) + + @pyqtSlot() + def previousResult(self): + """ + Public slot to activate the previous result. + """ + row = self.__resultsWidget.currentRow() + if row > 0: + prevItem = self.__resultsWidget.topLevelItem(row - 1) + self.__resultsWidget.setCurrentItem(prevItem) + self.__entrySelected(prevItem) + + @pyqtSlot() + def activateSearch(self): + """ + Public slot to 'activate' a search. + """ + self.__searchEdit.setFocus(Qt.FocusReason.OtherFocusReason) + self.__searchEdit.selectAll() + + @pyqtSlot() + def __updateInfoLabel(self): + """ + Private slot to update the data of the info label. + """ + rowCount = self.__resultsWidget.rowCount() + if rowCount: + currentRow = self.__resultsWidget.currentRow() + if currentRow == -1: # no result selected yet + self.__infoLabel.setText(self.tr("%n Result(s)", "", rowCount)) + else: + self.__infoLabel.setText( + self.tr("{0} of %n Results", "", rowCount).format(currentRow + 1) + ) + else: + self.__infoLabel.setText(self.tr("No results"))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfToCWidget.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a Table of Contents viewer widget. +""" + +from PyQt6.QtCore import QModelIndex, QSortFilterProxyModel, Qt, pyqtSignal, pyqtSlot +from PyQt6.QtPdf import QPdfBookmarkModel, QPdfDocument +from PyQt6.QtWidgets import QLabel, QLineEdit, QTreeView, QVBoxLayout, QWidget + + +class PdfToCModel(QPdfBookmarkModel): + """ + Class implementing a TOC model with page numbers. + """ + + def __init__(self, parent): + """ + Constructor + + @param parent DESCRIPTION + @type TYPE + """ + super().__init__(parent) + + def columnCount(self, index): + """ + Public method to define the number of columns to be shown. + + @param index index of the element + @type QModelIndex + @return column count (always 2) + @rtype int + """ + return 2 + + def data(self, index, role): + """ + Public method to return the requested data. + + @param index index of the element + @type QModelIndex + @param role data role + @type Qt.ItemDataRole + @return requested data + @rtype Any + """ + if not index.isValid(): + return None + + if index.column() == 1: + if role == Qt.ItemDataRole.DisplayRole: + page = index.data(QPdfBookmarkModel.Role.Page.value) + return self.document().pageLabel(page) + elif role == Qt.ItemDataRole.TextAlignmentRole: + return Qt.AlignmentFlag.AlignRight + + return super().data(index, role) + + +class PdfToCWidget(QWidget): + """ + Class implementing a Table of Contents viewer widget. + + @signal topicActivated(page, zoomFactor) emitted to navigate to the selected topic + """ + + topicActivated = pyqtSignal(int, float) + + def __init__(self, document, parent=None): + """ + Constructor + + @param document reference to the PDF document object + @type QPdfDocument + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.__layout = QVBoxLayout(self) + + self.__header = QLabel("<h2>{0}</h2>".format(self.tr("Contents"))) + self.__header.setAlignment(Qt.AlignmentFlag.AlignCenter) + self.__layout.addWidget(self.__header) + + self.__searchEdit = QLineEdit(self) + self.__searchEdit.setPlaceholderText(self.tr("Search ...")) + self.__searchEdit.setClearButtonEnabled(True) + self.__layout.addWidget(self.__searchEdit) + + self.__tocWidget = QTreeView(self) + self.__tocWidget.setHeaderHidden(True) + self.__tocWidget.setExpandsOnDoubleClick(False) + self.__tocModel = PdfToCModel(self) + self.__tocModel.setDocument(document) + self.__tocFilterModel = QSortFilterProxyModel(self) + self.__tocFilterModel.setRecursiveFilteringEnabled(True) + self.__tocFilterModel.setFilterCaseSensitivity( + Qt.CaseSensitivity.CaseInsensitive + ) + self.__tocFilterModel.setSourceModel(self.__tocModel) + self.__tocWidget.setModel(self.__tocFilterModel) + self.__layout.addWidget(self.__tocWidget) + + self.setLayout(self.__layout) + + self.__searchEdit.setEnabled(False) + self.__tocWidget.setEnabled(False) + + self.__tocWidget.activated.connect(self.__topicSelected) + document.statusChanged.connect(self.__handleDocumentStatus) + self.__searchEdit.textEdited.connect(self.__searchTextChanged) + + @pyqtSlot(QModelIndex) + def __topicSelected(self, index): + """ + Private slot to handle the selection of a ToC entry. + + @param index index of the activated entry + @type QModelIndex + """ + if not index.isValid(): + return + + page = index.data(QPdfBookmarkModel.Role.Page.value) + zoomFactor = index.data(QPdfBookmarkModel.Role.Zoom.value) + + self.topicActivated.emit(page, zoomFactor) + + @pyqtSlot(QPdfDocument.Status) + def __handleDocumentStatus(self, status): + """ + Private slot to handle a change of the document status. + + @param status document status + @type QPdfDocument.Status + """ + ready = status == QPdfDocument.Status.Ready + if ready: + self.__tocWidget.expandAll() + for column in range(self.__tocModel.columnCount(QModelIndex())): + self.__tocWidget.resizeColumnToContents(column) + + self.__searchEdit.setEnabled(ready) + self.__tocWidget.setEnabled(ready) + + @pyqtSlot(str) + def __searchTextChanged(self, text): + """ + Private slot to handle a change of the search text. + + @param text search text + @type str + """ + self.__tocFilterModel.setFilterWildcard("*{0}*".format(text)) + self.__tocWidget.expandAll()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfView.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,678 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a specialized PDF view class. +""" + +import collections +import enum + +from dataclasses import dataclass + +from PyQt6.QtCore import ( + QEvent, + QPoint, + QPointF, + QRect, + QRectF, + QSize, + QSizeF, + Qt, + pyqtSignal, + pyqtSlot, +) +from PyQt6.QtGui import QColor, QGuiApplication, QPainter, QPen +from PyQt6.QtPdf import QPdfDocument, QPdfLink +from PyQt6.QtPdfWidgets import QPdfView +from PyQt6.QtWidgets import QRubberBand + +from .PdfZoomSelector import PdfZoomSelector + + +class PdfMarkerType(enum.Enum): + """ + Class defining the various marker types. + """ + + SEARCHRESULT = 0 + SELECTION = 1 + + +@dataclass +class PdfMarker: + """ + Class defining the data structure for markers. + """ + + rectangle: QRectF + markerType: PdfMarkerType + + +@dataclass +class PdfMarkerGeometry: + """ + Class defining the data structure for marker geometries. + """ + + rectangle: QRect + markerType: PdfMarkerType + + +class PdfView(QPdfView): + """ + Class implementing a specialized PDF view. + + @signal selectionAvailable(bool) emitted to indicate the availability of a selection + """ + + MarkerColors = { + # merker type: (pen color, brush color) + PdfMarkerType.SEARCHRESULT: (QColor(255, 200, 0, 255), QColor(255, 200, 0, 64)), + PdfMarkerType.SELECTION: (QColor(0, 0, 255, 255), QColor(0, 0, 255, 64)), + } + + selectionAvailable = pyqtSignal(bool) + + def __init__(self, parent): + """ + Constructor + + @param parent reference to the parent widget + @type QWidget + """ + super().__init__(parent) + + self.__screenResolution = ( + QGuiApplication.primaryScreen().logicalDotsPerInch() / 72.0 + ) + + self.__documentViewport = QRect() + self.__documentSize = QSize() + self.__pageGeometries = {} + self.__markers = collections.defaultdict(list) + self.__markerGeometries = collections.defaultdict(list) + self.__rubberBand = None + + self.pageModeChanged.connect(self.__calculateDocumentLayout) + self.zoomModeChanged.connect(self.__calculateDocumentLayout) + self.zoomFactorChanged.connect(self.__calculateDocumentLayout) + self.pageSpacingChanged.connect(self.__calculateDocumentLayout) + self.documentMarginsChanged.connect(self.__calculateDocumentLayout) + + self.pageNavigator().currentPageChanged.connect(self.__currentPageChanged) + + self.grabGesture(Qt.GestureType.PinchGesture) + + def setDocument(self, document): + """ + Public method to set the PDF document. + + @param document reference to the PDF document object + @type QPdfDocument + """ + super().setDocument(document) + + document.statusChanged.connect(self.__calculateDocumentLayout) + + def __zoomInOut(self, zoomIn): + """ + Private method to zoom into or out of the view. + + @param zoomIn flag indicating to zoom into the view + @type bool + """ + zoomFactor = self.__zoomFactorForMode(self.zoomMode()) + + factors = list(PdfZoomSelector.ZoomValues) + factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView)) + factors.append(self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth)) + if zoomIn: + factors.sort() + if zoomFactor >= factors[-1]: + return + newIndex = next(x for x, val in enumerate(factors) if val > zoomFactor) + else: + factors.sort(reverse=True) + if zoomFactor <= factors[-1]: + return + newIndex = next(x for x, val in enumerate(factors) if val < zoomFactor) + newFactor = factors[newIndex] + if newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitInView): + self.setZoomMode(QPdfView.ZoomMode.FitInView) + self.zoomModeChanged.emit(QPdfView.ZoomMode.FitInView) + elif newFactor == self.__zoomFactorForMode(QPdfView.ZoomMode.FitToWidth): + self.setZoomMode(QPdfView.ZoomMode.FitToWidth) + self.zoomModeChanged.emit(QPdfView.ZoomMode.FitToWidth) + else: + self.setZoomFactor(newFactor) + self.zoomFactorChanged.emit(newFactor) + self.setZoomMode(QPdfView.ZoomMode.Custom) + self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) + + def __zoomFactorForMode(self, zoomMode): + """ + Private method to calculate the zoom factor iaw. the current zoom mode. + + @param zoomMode zoom mode to get the zoom factor for + @type QPdfView.ZoomMode + @return zoom factor + @rtype float + """ + self.__calculateDocumentViewport() + + if zoomMode == QPdfView.ZoomMode.Custom: + return self.zoomFactor() + else: + curPage = self.pageNavigator().currentPage() + margins = self.documentMargins() + if zoomMode == QPdfView.ZoomMode.FitToWidth: + pageSize = ( + self.document().pagePointSize(curPage) * self.__screenResolution + ).toSize() + factor = ( + self.__documentViewport.width() - margins.left() - margins.right() + ) / pageSize.width() + pageSize *= factor + else: + # QPdfView.ZoomMode.FitInView + viewportSize = self.__documentViewport.size() + QSize( + -margins.left() - margins.right(), -self.pageSpacing() + ) + pageSize = ( + self.document().pagePointSize(curPage) * self.__screenResolution + ).toSize() + pageSize = pageSize.scaled( + viewportSize, Qt.AspectRatioMode.KeepAspectRatio + ) + zoomFactor = ( + pageSize.width() + / ( + self.document().pagePointSize(curPage) * self.__screenResolution + ).width() + ) + return zoomFactor + + @pyqtSlot() + def zoomIn(self): + """ + Public slot to zoom into the view. + """ + self.__zoomInOut(True) + + @pyqtSlot() + def zoomOut(self): + """ + Public slot to zoom out of the view. + """ + self.__zoomInOut(False) + + @pyqtSlot() + def zoomReset(self): + """ + Public slot to reset the zoom factor of the view. + """ + if self.zoomMode() != QPdfView.ZoomMode.Custom or self.zoomFactor() != 1.0: + self.setZoomFactor(1.0) + self.zoomFactorChanged.emit(1.0) + self.setZoomMode(QPdfView.ZoomMode.Custom) + self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) + + def wheelEvent(self, evt): + """ + Protected method to handle wheel events. + + @param evt reference to the wheel event + @type QWheelEvent + """ + delta = evt.angleDelta().y() + if evt.modifiers() & Qt.KeyboardModifier.ControlModifier: + if delta < 0: + self.zoomOut() + elif delta > 0: + self.zoomIn() + evt.accept() + return + + elif evt.modifiers() & Qt.KeyboardModifier.ShiftModifier: + if delta < 0: + self.pageNavigator().back() + elif delta > 0: + self.pageNavigator().forward() + evt.accept() + return + + super().wheelEvent(evt) + + def keyPressEvent(self, evt): + """ + Protected method handling key press events. + + @param evt reference to the key event + @type QKeyEvent + """ + if evt.key() == Qt.Key.Key_Escape: + self.clearSelection() + + def mousePressEvent(self, evt): + """ + Protected method to handle mouse press events. + + @param evt reference to the mouse event + @type QMouseEvent + """ + if evt.button() == Qt.MouseButton.LeftButton: + self.clearMarkers(PdfMarkerType.SELECTION) + self.selectionAvailable.emit(False) + + self.__rubberBandOrigin = evt.pos() + if self.__rubberBand is None: + self.__rubberBand = QRubberBand( + QRubberBand.Shape.Rectangle, self.viewport() + ) + self.__rubberBand.setGeometry(QRect(self.__rubberBandOrigin, QSize())) + self.__rubberBand.show() + + super().mousePressEvent(evt) + + def mouseMoveEvent(self, evt): + """ + Protected method to handle mouse move events. + + @param evt reference to the mouse event + @type QMouseEvent + """ + if evt.buttons() & Qt.MouseButton.LeftButton: + self.__rubberBand.setGeometry( + QRect(self.__rubberBandOrigin, evt.pos()).normalized() + ) + + super().mousePressEvent(evt) + + def mouseReleaseEvent(self, evt): + """ + Protected method to handle mouse release events. + + @param evt reference to the mouse event + @type QMouseEvent + """ + if evt.button() == Qt.MouseButton.LeftButton: + self.__rubberBand.hide() + translatedRubber = self.__rubberBand.geometry().translated( + self.__documentViewport.topLeft() + ) + for page in self.__pageGeometries: + if self.__pageGeometries[page].intersects(translatedRubber): + translatedRubber = translatedRubber.translated( + -self.__pageGeometries[page].topLeft() + ) + factor = self.__zoomFactorForMode(self.zoomMode()) + selectionSize = ( + QSizeF(translatedRubber.size()) + / factor + / self.__screenResolution + ) + selectionTopLeft = ( + QPointF(translatedRubber.topLeft()) + / factor + / self.__screenResolution + ) + selectionRect = QRectF(selectionTopLeft, selectionSize) + selection = self.document().getSelection( + page, selectionRect.topLeft(), selectionRect.bottomRight() + ) + if selection.isValid(): + for bound in selection.bounds(): + self.addMarker( + page, bound.boundingRect(), PdfMarkerType.SELECTION + ) + self.selectionAvailable.emit(True) + + super().mousePressEvent(evt) + + def event(self, evt): + """ + Public method handling events. + + @param evt reference to the event + @type QEvent + @return flag indicating, if the event was handled + @rtype bool + """ + if evt.type() == QEvent.Type.Gesture: + self.gestureEvent(evt) + return True + + return super().event(evt) + + def gestureEvent(self, evt): + """ + Protected method handling gesture events. + + @param evt reference to the gesture event + @type QGestureEvent + """ + pinch = evt.gesture(Qt.GestureType.PinchGesture) + if pinch: + if pinch.state() == Qt.GestureState.GestureStarted: + pinch.setTotalScaleFactor(self.__zoomFactorForMode(self.zoomMode())) + elif pinch.state() == Qt.GestureState.GestureUpdated: + if self.zoomMode() != QPdfView.ZoomMode.Custom: + self.setZoomMode(QPdfView.ZoomMode.Custom) + self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) + zoomFactor = pinch.totalScaleFactor() + self.setZoomFactor(zoomFactor) + self.zoomFactorChanged.emit(zoomFactor) + evt.accept() + + def resizeEvent(self, evt): + """ + Protected method to handle a widget resize. + + @param evt reference to the resize event + @type QResizeEvent + """ + super().resizeEvent(evt) + + self.__calculateDocumentViewport() + + def paintEvent(self, evt): + """ + Protected method to paint the view. + + This event handler calls the original paint event handler of the super class + and paints the markers on top of the result. + + @param evt reference to the paint event + @type QPaintEvent + """ + super().paintEvent(evt) + + painter = QPainter(self.viewport()) + painter.translate(-self.__documentViewport.x(), -self.__documentViewport.y()) + for page in self.__markerGeometries: + for markerGeom in self.__markerGeometries[page]: + if markerGeom.rectangle.intersects(self.__documentViewport): + painter.setPen( + QPen(PdfView.MarkerColors[markerGeom.markerType][0], 2) + ) + painter.setBrush(PdfView.MarkerColors[markerGeom.markerType][1]) + painter.drawRect(markerGeom.rectangle) + painter.end() + + def __calculateDocumentViewport(self): + """ + Private method to calculate the document viewport. + + This is a PyQt implementation of the code found in the QPdfView class + because it is calculated in a private part and not accessible. + """ + x = self.horizontalScrollBar().value() + y = self.verticalScrollBar().value() + width = self.viewport().width() + height = self.viewport().height() + + docViewport = QRect(x, y, width, height) + if self.__documentViewport == docViewport: + return + + oldSize = self.__documentViewport.size() + + self.__documentViewport = docViewport + + if oldSize != self.__documentViewport.size(): + self.__calculateDocumentLayout() + + @pyqtSlot() + def __calculateDocumentLayout(self): + """ + Private slot to calculate the document layout data. + + This is a PyQt implementation of the code found in the QPdfView class + because it is calculated in a private part and not accessible. + """ + self.__documentSize = QSize() + self.__pageGeometries.clear() + self.__markerGeometries.clear() + + document = self.document() + margins = self.documentMargins() + + if document is None or document.status() != QPdfDocument.Status.Ready: + return + + pageCount = document.pageCount() + + totalWidth = 0 + + startPage = ( + self.pageNavigator().currentPage() + if self.pageMode() == QPdfView.PageMode.SinglePage + else 0 + ) + endPage = ( + self.pageNavigator().currentPage() + 1 + if self.pageMode() == QPdfView.PageMode.SinglePage + else pageCount + ) + + # calculate pageSizes + for page in range(startPage, endPage): + if self.zoomMode() == QPdfView.ZoomMode.Custom: + pageSize = QSizeF( + document.pagePointSize(page) + * self.__screenResolution + * self.zoomFactor() + ).toSize() + elif self.zoomMode() == QPdfView.ZoomMode.FitToWidth: + pageSize = QSizeF( + document.pagePointSize(page) * self.__screenResolution + ).toSize() + factor = ( + self.__documentViewport.width() - margins.left() - margins.right() + ) / pageSize.width() + pageSize *= factor + elif self.zoomMode() == QPdfView.ZoomMode.FitInView: + viewportSize = self.__documentViewport.size() + QSize( + -margins.left() - margins.right(), -self.pageSpacing() + ) + pageSize = QSizeF( + document.pagePointSize(page) * self.__screenResolution + ).toSize() + pageSize = pageSize.scaled( + viewportSize, Qt.AspectRatioMode.KeepAspectRatio + ) + + totalWidth = max(totalWidth, pageSize.width()) + + self.__pageGeometries[page] = QRect(QPoint(0, 0), pageSize) + + totalWidth += margins.left() + margins.right() + + pageY = margins.top() + + # calculate page positions + for page in range(startPage, endPage): + pageSize = self.__pageGeometries[page].size() + + # center horizontally inside the viewport + pageX = ( + max(totalWidth, self.__documentViewport.width()) - pageSize.width() + ) // 2 + self.__pageGeometries[page].moveTopLeft(QPoint(pageX, pageY)) + + self.__calculateMarkerGeometries(page, QPoint(pageX, pageY)) + + pageY += pageSize.height() + self.pageSpacing() + + pageY += margins.bottom() + + self.__documentSize = QSize(totalWidth, pageY) + + @pyqtSlot() + def __currentPageChanged(self): + """ + Private slot to handle a change of the current page. + """ + if self.pageMode() == QPdfView.PageMode.SinglePage: + self.__calculateDocumentLayout() + self.update() + + def __calculateMarkerGeometries(self, page, offset): + """ + Private method to calculate the marker geometries. + + @param page page number + @type int + @param offset page offset + @type QPoint or QPointF + """ + # calculate search marker sizes + if page in self.__markers: + factor = self.__zoomFactorForMode(self.zoomMode()) + for marker in self.__markers[page]: + markerSize = ( + QSizeF(marker.rectangle.size()) * factor * self.__screenResolution + ).toSize() + markerTopLeft = ( + QPointF(marker.rectangle.topLeft()) + * factor + * self.__screenResolution + ).toPoint() + + markerGeometry = QRect(markerTopLeft, markerSize) + self.__markerGeometries[page].append( + PdfMarkerGeometry( + rectangle=markerGeometry.translated(offset), + markerType=marker.markerType, + ) + ) + + def scrollContentsBy(self, dx, dy): + """ + Public method called when the scrollbars are moved. + + @param dx change of the horizontal scroll bar + @type int + @param dy change of the vertical scroll bar + @type int + """ + super().scrollContentsBy(dx, dy) + + self.__calculateDocumentViewport() + + def __updateView(self): + """ + Private method to update the view. + """ + self.__calculateDocumentLayout() + self.update() + + @pyqtSlot(int, QRectF, PdfMarkerType) + @pyqtSlot(int, QRect, PdfMarkerType) + def addMarker(self, page, rect, markerType): + """ + Public slot to add a marker. + + @param page page number for the marker + @type int + @param rect marker rectangle + @type QRect or QRectF + @param markerType type of the marker + @type PdfMarkerType + """ + marker = PdfMarker(rectangle=QRectF(rect), markerType=markerType) + if marker not in self.__markers[page]: + self.__markers[page].append(marker) + self.__updateView() + + @pyqtSlot(PdfMarkerType) + def clearMarkers(self, markerType): + """ + Public slot to clear the markers of a specific type. + + @param markerType type of the marker + @type PdfMarkerType + """ + markers = collections.defaultdict(list) + for page in self.__markers: + markersList = [ + m for m in self.__markers[page] if m.markerType != markerType + ] + if markersList: + markers[page] = markersList + + self.__markers = markers + self.__updateView() + + @pyqtSlot() + def clearAllMarkers(self): + """ + Public slot to clear all markers. + """ + self.__markers.clear() + self.__updateView() + + @pyqtSlot(QPdfLink) + def addSearchMarker(self, link): + """ + Public slot to add a search marker given a PDF link. + + @param link reference to the PDF link object + @type QPdfLink + """ + for rect in link.rectangles(): + self.addMarker(link.page(), rect, PdfMarkerType.SEARCHRESULT) + + @pyqtSlot() + def clearSearchMarkers(self): + """ + Public slot to clear the search markers. + """ + self.clearMarkers(PdfMarkerType.SEARCHRESULT) + + def hasSelection(self): + """ + Public method to check the presence of a selection. + + @return flag indicating the presence of a selection + @rtype bool + """ + return any( + m.markerType == PdfMarkerType.SELECTION + for p in self.__markers + for m in self.__markers[p] + ) + + def getSelection(self): + """ + Public method to get a PDF selection object. + + @return reference to the PDF selection object + @rtype QPdfSelection + """ + for page in self.__markers: + markersList = [ + m + for m in self.__markers[page] + if m.markerType == PdfMarkerType.SELECTION + ] + if markersList: + selection = self.document().getSelection( + page, + markersList[0].rectangle.topLeft(), + markersList[-1].rectangle.bottomRight(), + ) + if selection.isValid(): + return selection + + return None + + @pyqtSlot() + def clearSelection(self): + """ + Public slot to clear the current selection. + """ + self.clearMarkers(PdfMarkerType.SELECTION)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfViewerWindow.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,1489 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the PDF viewer main window. +""" + +import contextlib +import os +import pathlib + +from PyQt6.QtCore import QPointF, QSize, Qt, pyqtSignal, pyqtSlot +from PyQt6.QtGui import QAction, QActionGroup, QClipboard, QGuiApplication, QKeySequence +from PyQt6.QtPdf import QPdfDocument, QPdfLink +from PyQt6.QtPdfWidgets import QPdfView +from PyQt6.QtWidgets import ( + QDialog, + QInputDialog, + QLineEdit, + QMenu, + QSplitter, + QTabWidget, + QToolBar, + QWhatsThis, +) + +from eric7 import Preferences +from eric7.EricGui import EricPixmapCache +from eric7.EricGui.EricAction import EricAction +from eric7.EricWidgets import EricFileDialog, EricMessageBox +from eric7.EricWidgets.EricMainWindow import EricMainWindow +from eric7.EricWidgets.EricStretchableSpacer import EricStretchableSpacer +from eric7.Globals import recentNamePdfFiles +from eric7.SystemUtilities import FileSystemUtilities, OSUtilities + +from .PdfInfoWidget import PdfInfoWidget +from .PdfPageSelector import PdfPageSelector +from .PdfSearchWidget import PdfSearchWidget +from .PdfToCWidget import PdfToCWidget +from .PdfView import PdfView +from .PdfZoomSelector import PdfZoomSelector + + +class PdfViewerWindow(EricMainWindow): + """ + Class implementing the PDF viewer main window. + + @signal viewerClosed() emitted after the window was requested to close + """ + + viewerClosed = pyqtSignal() + + windows = [] + + maxMenuFilePathLen = 75 + + def __init__(self, fileName="", parent=None, fromEric=False, project=None): + """ + Constructor + + @param fileName name of a file to load on startup + @type str + @param parent parent widget of this window + @type QWidget + @param fromEric flag indicating whether it was called from within + eric + @type bool + @param project reference to the project object + @type Project + """ + super().__init__(parent) + self.setObjectName("eric7_pdf_viewer") + + self.__fromEric = fromEric + self.setWindowIcon(EricPixmapCache.getIcon("ericPdf")) + + if not self.__fromEric: + self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) + + self.__pdfDocument = QPdfDocument(self) + + self.__cw = QSplitter(Qt.Orientation.Horizontal, self) + self.__cw.setChildrenCollapsible(False) + self.__info = QTabWidget(self) + self.__cw.addWidget(self.__info) + self.__view = PdfView(self) + self.__view.setDocument(self.__pdfDocument) + self.__cw.addWidget(self.__view) + self.setCentralWidget(self.__cw) + + # create the various info widgets + self.__documentInfoWidget = PdfInfoWidget(self.__pdfDocument, self) + index = self.__info.addTab( + self.__documentInfoWidget, EricPixmapCache.getIcon("documentProperties"), "" + ) + self.__info.setTabToolTip(index, self.tr("Document Properties")) + + self.__searchWidget = PdfSearchWidget(self.__pdfDocument, self) + index = self.__info.addTab( + self.__searchWidget, EricPixmapCache.getIcon("find"), "" + ) + self.__info.setTabToolTip(index, self.tr("Search")) + + self.__tocWidget = PdfToCWidget(self.__pdfDocument, self) + index = self.__info.addTab( + self.__tocWidget, EricPixmapCache.getIcon("listSelection"), "" + ) + self.__info.setTabToolTip(index, self.tr("Table of Contents")) + + self.__info.setCurrentWidget(self.__tocWidget) + + # create a few widgets needed in the toolbars + self.__pageSelector = PdfPageSelector(self) + self.__pageSelector.setDocument(self.__pdfDocument) + self.__view.pageNavigator().currentPageChanged.connect( + self.__pageSelector.setValue + ) + self.__pageSelector.valueChanged.connect(self.__pageSelected) + self.__pageSelector.gotoPage.connect(self.__gotoPage) + + self.__zoomSelector = PdfZoomSelector(self) + self.__zoomSelector.reset() + + g = Preferences.getGeometry("PdfViewerGeometry") + if g.isEmpty(): + s = QSize(1000, 1000) + self.resize(s) + self.__cw.setSizes([300, 700]) + else: + self.restoreGeometry(g) + + self.__initActions() + self.__initMenus() + self.__initToolbars() + self.__createStatusBar() + + self.__setDisplayMode() # needs to be done after actions have been created + + self.__view.pageNavigator().backAvailableChanged.connect( + self.backwardAct.setEnabled + ) + self.__view.pageNavigator().forwardAvailableChanged.connect( + self.forwardAct.setEnabled + ) + + self.__zoomSelector.zoomModeChanged.connect(self.__view.setZoomMode) + self.__zoomSelector.zoomModeChanged.connect(self.__zoomModeChanged) + self.__zoomSelector.zoomFactorChanged.connect(self.__view.setZoomFactor) + self.__view.zoomFactorChanged.connect(self.__zoomSelector.setZoomFactor) + self.__view.zoomModeChanged.connect(self.__zoomSelector.setZoomMode) + self.__view.selectionAvailable.connect(self.copyAct.setEnabled) + + self.__tocWidget.topicActivated.connect(self.__tocActivated) + + self.__searchWidget.searchResultActivated.connect(self.__handleSearchResult) + self.__searchWidget.searchNextAvailable.connect(self.searchNextAct.setEnabled) + self.__searchWidget.searchPrevAvailable.connect(self.searchPrevAct.setEnabled) + self.__searchWidget.searchCleared.connect(self.__view.clearSearchMarkers) + self.__searchWidget.searchResult.connect(self.__view.addSearchMarker) + + PdfViewerWindow.windows.append(self) + + self.__restoreViewerState() + + self.__checkActions() + + self.__project = project + self.__lastOpenPath = "" + + self.__recent = [] + self.__loadRecent() + + self.__setCurrentFile("") + self.__setViewerTitle("") + if fileName: + self.__loadPdfFile(fileName) + + def __initActions(self): + """ + Private method to define the user interface actions. + """ + # list of all actions + self.__actions = [] + + self.__initFileActions() + self.__initGotoActions() + self.__initViewActions() + self.__initEditActions() + self.__initSettingsActions() + self.__initHelpActions() + + def __initFileActions(self): + """ + Private method to define the file related user interface actions. + """ + self.newWindowAct = EricAction( + self.tr("New Window"), + EricPixmapCache.getIcon("newWindow"), + self.tr("New &Window"), + QKeySequence(self.tr("Ctrl+Shift+N", "File|New Window")), + 0, + self, + "pdfviewer_file_new_window", + ) + self.newWindowAct.setStatusTip( + self.tr("Open a PDF file in a new PDF Viewer window") + ) + self.newWindowAct.setWhatsThis( + self.tr( + """<b>New Window</b>""" + """<p>This opens a PDF file in a new PDF Viewer window. It pops up""" + """ a file selection dialog.</p>""" + ) + ) + self.newWindowAct.triggered.connect(self.__openPdfFileNewWindow) + self.__actions.append(self.newWindowAct) + + self.openAct = EricAction( + self.tr("Open"), + EricPixmapCache.getIcon("open"), + self.tr("&Open..."), + QKeySequence(self.tr("Ctrl+O", "File|Open")), + 0, + self, + "pdfviewer_file_open", + ) + self.openAct.setStatusTip(self.tr("Open a PDF file for viewing")) + self.openAct.setWhatsThis( + self.tr( + """<b>Open</b>""" + """<p>This opens a PDF file for viewing. It pops up a file""" + """ selection dialog.</p>""" + ) + ) + self.openAct.triggered.connect(self.__openPdfFile) + self.__actions.append(self.openAct) + + self.reloadAct = EricAction( + self.tr("Reload"), + EricPixmapCache.getIcon("reload"), + self.tr("&Reload"), + QKeySequence("F5"), + 0, + self, + "pdfviewer_file_reload", + ) + self.reloadAct.setStatusTip(self.tr("Reload the current PDF document")) + self.reloadAct.triggered.connect(self.__reload) + self.__actions.append(self.reloadAct) + + self.propertiesAct = EricAction( + self.tr("Properties"), + EricPixmapCache.getIcon("documentProperties"), + self.tr("&Properties..."), + QKeySequence(self.tr("Alt+Return")), + 0, + self, + "pdfviewer_file_properties", + ) + self.propertiesAct.setStatusTip(self.tr("Show the document properties")) + self.propertiesAct.setWhatsThis( + self.tr( + """<b>Properties</b><p>Opens a dialog showing the document""" + """ properties.</p>""" + ) + ) + self.propertiesAct.triggered.connect(self.__showDocumentProperties) + self.__actions.append(self.propertiesAct) + + self.closeAct = EricAction( + self.tr("Close"), + EricPixmapCache.getIcon("close"), + self.tr("&Close"), + QKeySequence(self.tr("Ctrl+W", "File|Close")), + 0, + self, + "pdfviewer_file_close", + ) + self.closeAct.setStatusTip(self.tr("Close the current PDF Viewer window")) + self.closeAct.triggered.connect(self.close) + self.__actions.append(self.closeAct) + + self.closeAllAct = EricAction( + self.tr("Close All"), + self.tr("Close &All"), + 0, + 0, + self, + "pdfviewer_file_close_all", + ) + self.closeAllAct.setStatusTip(self.tr("Close all PDF Viewer windows")) + self.closeAllAct.triggered.connect(self.__closeAll) + self.__actions.append(self.closeAllAct) + + self.closeOthersAct = EricAction( + self.tr("Close Others"), + self.tr("Close Others"), + 0, + 0, + self, + "pdfviewer_file_close_others", + ) + self.closeOthersAct.setStatusTip(self.tr("Close all other PDF Viewer windows")) + self.closeOthersAct.triggered.connect(self.__closeOthers) + self.__actions.append(self.closeOthersAct) + + self.exitAct = EricAction( + self.tr("Quit"), + EricPixmapCache.getIcon("exit"), + self.tr("&Quit"), + QKeySequence(self.tr("Ctrl+Q", "File|Quit")), + 0, + self, + "pdfviewer_file_quit", + ) + self.exitAct.setStatusTip(self.tr("Quit the PDF Viewer")) + if not self.__fromEric: + self.exitAct.triggered.connect(self.__closeAll) + self.__actions.append(self.exitAct) + + def __initGotoActions(self): + """ + Private method to define the navigation related user interface actions. + """ + self.previousPageAct = EricAction( + self.tr("Previous Page"), + EricPixmapCache.getIcon("1leftarrow"), + self.tr("&Previous Page"), + 0, + 0, + self, + "pdfviewer_goto_previous", + ) + self.previousPageAct.setStatusTip(self.tr("Go to the previous page")) + self.previousPageAct.triggered.connect(self.__previousPage) + self.__actions.append(self.previousPageAct) + + self.nextPageAct = EricAction( + self.tr("Next Page"), + EricPixmapCache.getIcon("1rightarrow"), + self.tr("&Next Page"), + 0, + 0, + self, + "pdfviewer_goto_next", + ) + self.nextPageAct.setStatusTip(self.tr("Go to the next page")) + self.nextPageAct.triggered.connect(self.__nextPage) + self.__actions.append(self.nextPageAct) + + self.startDocumentAct = EricAction( + self.tr("Start of Document"), + EricPixmapCache.getIcon("gotoFirst"), + self.tr("&Start of Document"), + QKeySequence(self.tr("Ctrl+Home", "Goto|Start")), + 0, + self, + "pdfviewer_goto_start", + ) + self.startDocumentAct.setStatusTip( + self.tr("Go to the first page of the document") + ) + self.startDocumentAct.triggered.connect(self.__startDocument) + self.__actions.append(self.startDocumentAct) + + self.endDocumentAct = EricAction( + self.tr("End of Document"), + EricPixmapCache.getIcon("gotoLast"), + self.tr("&End of Document"), + QKeySequence(self.tr("Ctrl+End", "Goto|End")), + 0, + self, + "pdfviewer_goto_end", + ) + self.endDocumentAct.setStatusTip(self.tr("Go to the last page of the document")) + self.endDocumentAct.triggered.connect(self.__endDocument) + self.__actions.append(self.endDocumentAct) + + self.backwardAct = EricAction( + self.tr("Back"), + EricPixmapCache.getIcon("back"), + self.tr("&Back"), + QKeySequence(self.tr("Alt+Shift+Left", "Goto|Back")), + 0, + self, + "pdfviewer_goto_back", + ) + self.backwardAct.setStatusTip(self.tr("Go back in the view history")) + self.backwardAct.triggered.connect(self.__backInHistory) + self.__actions.append(self.backwardAct) + + self.forwardAct = EricAction( + self.tr("Forward"), + EricPixmapCache.getIcon("forward"), + self.tr("&Forward"), + QKeySequence(self.tr("Alt+Shift+Right", "Goto|Forward")), + 0, + self, + "pdfviewer_goto_forward", + ) + self.forwardAct.setStatusTip(self.tr("Go forward in the view history")) + self.forwardAct.triggered.connect(self.__forwardInHistory) + self.__actions.append(self.forwardAct) + + self.gotoAct = EricAction( + self.tr("Go to Page"), + EricPixmapCache.getIcon("gotoJump"), + self.tr("&Go to Page..."), + QKeySequence(self.tr("Ctrl+G", "Goto|Go to Page")), + 0, + self, + "pdfviewer_goto_gotopage", + ) + self.gotoAct.setStatusTip(self.tr("Jump to a page selected via a dialog")) + self.gotoAct.triggered.connect(self.__gotoPage) + self.__actions.append(self.gotoAct) + + def __initViewActions(self): + """ + Private method to define the view related user interface actions. + """ + self.fullScreenAct = EricAction( + self.tr("Full Screen"), + EricPixmapCache.getIcon("windowFullscreen"), + self.tr("&Full Screen"), + 0, + 0, + self, + "pdfviewer_view_full_screen", + ) + if OSUtilities.isMacPlatform(): + self.fullScreenAct.setShortcut(QKeySequence(self.tr("Meta+Ctrl+F"))) + else: + self.fullScreenAct.setShortcut(QKeySequence(self.tr("F11"))) + self.fullScreenAct.setCheckable(True) + self.fullScreenAct.triggered.connect(self.__toggleFullScreen) + self.__actions.append(self.fullScreenAct) + + self.zoomInAct = EricAction( + self.tr("Zoom in"), + EricPixmapCache.getIcon("zoomIn"), + self.tr("Zoom &in"), + QKeySequence(self.tr("Ctrl++", "View|Zoom in")), + 0, + self, + "pdfviewer_view_zoomin", + ) + self.zoomInAct.triggered.connect(self.__zoomIn) + self.__actions.append(self.zoomInAct) + + self.zoomOutAct = EricAction( + self.tr("Zoom out"), + EricPixmapCache.getIcon("zoomOut"), + self.tr("Zoom &out"), + QKeySequence(self.tr("Ctrl+-", "View|Zoom out")), + 0, + self, + "pdfviewer_view_zoomout", + ) + self.zoomOutAct.triggered.connect(self.__zoomOut) + self.__actions.append(self.zoomOutAct) + + self.zoomResetAct = EricAction( + self.tr("Zoom to 100%"), + EricPixmapCache.getIcon("zoomReset"), + self.tr("Zoom to &100%"), + QKeySequence(self.tr("Ctrl+0", "View|Zoom reset")), + 0, + self, + "pdfviewer_view_zoomreset", + ) + self.zoomResetAct.triggered.connect(self.__zoomReset) + self.__actions.append(self.zoomResetAct) + + self.zoomPageWidthAct = EricAction( + self.tr("Page Width"), + EricPixmapCache.getIcon("zoomFitWidth"), + self.tr("Page &Width"), + 0, + 0, + self, + "pdfviewer_view_zoompagewidth", + ) + self.zoomPageWidthAct.triggered.connect(self.__zoomPageWidth) + self.zoomPageWidthAct.setCheckable(True) + self.__actions.append(self.zoomPageWidthAct) + + self.zoomWholePageAct = EricAction( + self.tr("Whole Page"), + EricPixmapCache.getIcon("zoomFitPage"), + self.tr("Whole &Page"), + 0, + 0, + self, + "pdfviewer_view_zoomwholePage", + ) + self.zoomWholePageAct.triggered.connect(self.__zoomWholePage) + self.zoomWholePageAct.setCheckable(True) + self.__actions.append(self.zoomWholePageAct) + + def __initEditActions(self): + """ + Private method to create the Edit actions. + """ + self.copyAct = EricAction( + self.tr("Copy"), + EricPixmapCache.getIcon("editCopy"), + self.tr("&Copy"), + QKeySequence(self.tr("Ctrl+C", "Edit|Copy")), + 0, + self, + "pdfviewer_edit_copy", + ) + self.copyAct.triggered.connect(self.__copyText) + self.__actions.append(self.copyAct) + + self.copyAllAct = EricAction( + self.tr("Copy All Text"), + self.tr("Copy &All Text"), + QKeySequence(self.tr("Alt+Ctrl+C", "Edit|Copy All Text")), + 0, + self, + "pdfviewer_edit_copyall", + ) + self.copyAllAct.triggered.connect(self.__copyAllText) + self.__actions.append(self.copyAllAct) + + self.copyAllPageAct = EricAction( + self.tr("Copy All Page Text"), + self.tr("Copy All Page &Text"), + QKeySequence(self.tr("Shift+Ctrl+C", "Edit|Copy All Page Text")), + 0, + self, + "pdfviewer_edit_copyallpage", + ) + self.copyAllPageAct.triggered.connect(self.__copyAllTextOfPage) + self.__actions.append(self.copyAllPageAct) + + self.searchAct = EricAction( + self.tr("Search"), + EricPixmapCache.getIcon("find"), + self.tr("&Search..."), + QKeySequence(self.tr("Ctrl+F", "Edit|Search")), + 0, + self, + "pdfviewer_edit_search", + ) + self.searchAct.triggered.connect(self.__search) + self.__actions.append(self.searchAct) + + self.searchNextAct = EricAction( + self.tr("Search Next"), + EricPixmapCache.getIcon("findNext"), + self.tr("Search &Next"), + QKeySequence(self.tr("F3", "Edit|Search Next")), + 0, + self, + "pdfviewer_edit_searchnext", + ) + self.searchNextAct.triggered.connect(self.__searchWidget.nextResult) + self.__actions.append(self.searchNextAct) + + self.searchPrevAct = EricAction( + self.tr("Search Previous"), + EricPixmapCache.getIcon("findPrev"), + self.tr("Search &Previous"), + QKeySequence(self.tr("Shift+F3", "Edit|Search Previous")), + 0, + self, + "pdfviewer_edit_searchprevious", + ) + self.searchPrevAct.triggered.connect(self.__searchWidget.previousResult) + self.__actions.append(self.searchPrevAct) + + self.copyAct.setEnabled(False) + self.searchNextAct.setEnabled(False) + self.searchPrevAct.setEnabled(False) + + def __initSettingsActions(self): + """ + Private method to create the Settings actions. + """ + self.prefAct = EricAction( + self.tr("Preferences"), + EricPixmapCache.getIcon("configure"), + self.tr("&Preferences..."), + 0, + 0, + self, + "pdfviewer_settings_preferences", + ) + self.prefAct.setStatusTip(self.tr("Set the prefered configuration")) + self.prefAct.setWhatsThis( + self.tr( + """<b>Preferences</b>""" + """<p>Set the configuration items of the application""" + """ with your prefered values.</p>""" + ) + ) + self.prefAct.triggered.connect(self.__showPreferences) + self.prefAct.setMenuRole(QAction.MenuRole.PreferencesRole) + self.__actions.append(self.prefAct) + + self.sidebarAct = EricAction( + self.tr("Show Sidebar"), + EricPixmapCache.getIcon("sidebarExpandLeft"), + self.tr("Show &Sidebar"), + 0, + 0, + self, + "pdfviewer_settings_sidebar", + ) + self.sidebarAct.triggered.connect(self.__toggleSideBar) + self.sidebarAct.setCheckable(True) + self.__actions.append(self.sidebarAct) + + self.openRecentNewAct = EricAction( + self.tr("Open Recent File in New Window"), + self.tr("Open Recent File in New Window"), + 0, + 0, + self, + "pdfviewer_settings_openrecent new", + ) + self.openRecentNewAct.triggered.connect(self.__toggleOpenRecentNew) + self.openRecentNewAct.setCheckable(True) + self.__actions.append(self.sidebarAct) + + def __initHelpActions(self): + """ + Private method to create the Help actions. + """ + self.aboutAct = EricAction( + self.tr("About"), self.tr("&About"), 0, 0, self, "pdfviewer_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 = EricAction( + self.tr("About Qt"), + self.tr("About &Qt"), + 0, + 0, + self, + "pdfviewer_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 = EricAction( + self.tr("What's This?"), + EricPixmapCache.getIcon("whatsThis"), + self.tr("&What's This?"), + QKeySequence(self.tr("Shift+F1", "Help|What's This?'")), + 0, + self, + "pdfviewer_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) + + @pyqtSlot() + def __checkActions(self): + """ + Private slot to check some actions for their enable/disable status. + """ + ready = self.__pdfDocument.status() == QPdfDocument.Status.Ready + + self.reloadAct.setEnabled(ready) + self.propertiesAct.setEnabled(ready) + + curPage = self.__view.pageNavigator().currentPage() + self.previousPageAct.setEnabled(curPage > 0 and ready) + self.nextPageAct.setEnabled( + curPage < self.__pdfDocument.pageCount() - 1 and ready + ) + self.startDocumentAct.setEnabled(curPage != 0 and ready) + self.endDocumentAct.setEnabled( + curPage != self.__pdfDocument.pageCount() - 1 and ready + ) + self.gotoAct.setEnabled(ready) + + self.backwardAct.setEnabled(self.__view.pageNavigator().backAvailable()) + self.forwardAct.setEnabled(self.__view.pageNavigator().forwardAvailable()) + + self.zoomInAct.setEnabled(ready) + self.zoomOutAct.setEnabled(ready) + self.zoomResetAct.setEnabled(ready) + self.__zoomSelector.setEnabled(ready) + + self.copyAllAct.setEnabled(ready) + self.copyAllPageAct.setEnabled(ready) + self.searchAct.setEnabled(ready) + + def __initMenus(self): + """ + Private method to create the menus. + """ + mb = self.menuBar() + + menu = mb.addMenu(self.tr("&File")) + menu.setTearOffEnabled(True) + self.__recentMenu = QMenu(self.tr("Open &Recent Files"), menu) + menu.addAction(self.newWindowAct) + menu.addAction(self.openAct) + self.__menuRecentAct = menu.addMenu(self.__recentMenu) + menu.addSeparator() + menu.addAction(self.reloadAct) + menu.addSeparator() + menu.addAction(self.propertiesAct) + 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.aboutToShow.connect(self.__showFileMenu) + self.__recentMenu.aboutToShow.connect(self.__showRecentMenu) + self.__recentMenu.triggered.connect(self.__openRecentPdfFile) + + menu = mb.addMenu(self.tr("&View")) + menu.setTearOffEnabled(True) + menu.addAction(self.fullScreenAct) + menu.addSeparator() + menu.addAction(self.zoomInAct) + menu.addAction(self.zoomOutAct) + menu.addAction(self.zoomResetAct) + menu.addAction(self.zoomPageWidthAct) + menu.addAction(self.zoomWholePageAct) + menu.addSeparator() + modeMenu = menu.addMenu(self.tr("Display Mode")) + self.__singlePageAct = modeMenu.addAction(self.tr("Single Page")) + self.__singlePageAct.setCheckable(True) + self.__continuousPageAct = modeMenu.addAction(self.tr("Continuous")) + self.__continuousPageAct.setCheckable(True) + self.__displayModeActGrp = QActionGroup(self) + self.__displayModeActGrp.addAction(self.__singlePageAct) + self.__displayModeActGrp.addAction(self.__continuousPageAct) + modeMenu.triggered.connect(self.__displayModeSelected) + + menu = mb.addMenu(self.tr("&Edit")) + menu.setTearOffEnabled(True) + menu.addAction(self.copyAct) + menu.addSeparator() + menu.addAction(self.copyAllAct) + menu.addAction(self.copyAllPageAct) + menu.addSeparator() + menu.addAction(self.searchAct) + menu.addAction(self.searchNextAct) + menu.addAction(self.searchPrevAct) + + menu = mb.addMenu(self.tr("&Go To")) + menu.setTearOffEnabled(True) + menu.addAction(self.previousPageAct) + menu.addAction(self.nextPageAct) + menu.addSeparator() + menu.addAction(self.startDocumentAct) + menu.addAction(self.endDocumentAct) + menu.addSeparator() + menu.addAction(self.backwardAct) + menu.addAction(self.forwardAct) + menu.addSeparator() + menu.addAction(self.gotoAct) + + menu = mb.addMenu(self.tr("Se&ttings")) + menu.setTearOffEnabled(True) + menu.addAction(self.prefAct) + menu.addSeparator() + menu.addAction(self.sidebarAct) + menu.addSeparator() + menu.addAction(self.openRecentNewAct) + + 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. + """ + mainToolBar = QToolBar() + mainToolBar.setObjectName("main_toolbar") + mainToolBar.setMovable(False) + mainToolBar.setFloatable(False) + + # 0. Sidebar action + mainToolBar.addAction(self.sidebarAct) + mainToolBar.addSeparator() + + # 1. File actions + mainToolBar.addAction(self.newWindowAct) + mainToolBar.addAction(self.openAct) + mainToolBar.addSeparator() + mainToolBar.addAction(self.closeAct) + if not self.__fromEric: + mainToolBar.addAction(self.exitAct) + mainToolBar.addSeparator() + + # 2. Go to actions + mainToolBar.addWidget(EricStretchableSpacer()) + mainToolBar.addAction(self.startDocumentAct) + mainToolBar.addWidget(self.__pageSelector) + mainToolBar.addAction(self.endDocumentAct) + mainToolBar.addWidget(EricStretchableSpacer()) + mainToolBar.addSeparator() + + # 3. View actions + mainToolBar.addAction(self.zoomOutAct) + mainToolBar.addWidget(self.__zoomSelector) + mainToolBar.addAction(self.zoomInAct) + mainToolBar.addAction(self.zoomResetAct) + mainToolBar.addAction(self.zoomPageWidthAct) + mainToolBar.addAction(self.zoomWholePageAct) + + self.addToolBar(mainToolBar) + self.addToolBarBreak() + + def __createStatusBar(self): + """ + Private method to initialize the status bar. + """ + self.__statusBar = self.statusBar() + self.__statusBar.setSizeGripEnabled(True) + + # not yet implemented + + def closeEvent(self, evt): + """ + Protected method handling the close event. + + @param evt reference to the close event + @type QCloseEvent + """ + Preferences.setGeometry("PdfViewerGeometry", self.saveGeometry()) + + self.__saveViewerState() + + with contextlib.suppress(ValueError): + if self.__fromEric or len(PdfViewerWindow.windows) > 1: + PdfViewerWindow.windows.remove(self) + + self.__saveRecent() + + self.__documentInfoWidget.setDocument(None) + + evt.accept() + self.viewerClosed.emit() + + def __saveViewerState(self): + """ + Private method to save the PDF Viewer state data. + """ + state = self.saveState() + Preferences.setPdfViewer("PdfViewerState", state) + splitterState = self.__cw.saveState() + Preferences.setPdfViewer("PdfViewerSplitterState", splitterState) + Preferences.setPdfViewer("PdfViewerSidebarVisible", self.sidebarAct.isChecked()) + Preferences.setPdfViewer("PdfViewerZoomFactor", self.__view.zoomFactor()) + Preferences.setPdfViewer("PdfViewerZoomMode", self.__view.zoomMode()) + Preferences.setPdfViewer( + "PdfViewerOpenRecentInNewWindow", self.openRecentNewAct.isChecked() + ) + + if not self.__fromEric: + Preferences.syncPreferences() + + def __restoreViewerState(self): + """ + Private method to restore the PDF Viewer state data. + """ + state = Preferences.getPdfViewer("PdfViewerState") + self.restoreState(state) + splitterState = Preferences.getPdfViewer("PdfViewerSplitterState") + self.__cw.restoreState(splitterState) + self.__toggleSideBar(Preferences.getPdfViewer("PdfViewerSidebarVisible")) + self.__view.setZoomFactor(Preferences.getPdfViewer("PdfViewerZoomFactor")) + self.__view.setZoomMode(Preferences.getPdfViewer("PdfViewerZoomMode")) + self.openRecentNewAct.setChecked( + Preferences.getPdfViewer("PdfViewerOpenRecentInNewWindow") + ) + + def __setViewerTitle(self, title): + """ + Private method to set the viewer title. + + @param title title to be set + @type str + """ + if title: + self.setWindowTitle(self.tr("{0} - PDF Viewer").format(title)) + else: + self.setWindowTitle(self.tr("PDF Viewer")) + + def __getErrorString(self, err): + """ + Private method to get an error string for the given error. + + @param err error type + @type QPdfDocument.Error + @return string for the given error type + @rtype str + """ + if err == QPdfDocument.Error.None_: + reason = "" + elif err == QPdfDocument.Error.DataNotYetAvailable: + reason = self.tr("The document is still loading.") + elif err == QPdfDocument.Error.FileNotFound: + reason = self.tr("The file does not exist.") + elif err == QPdfDocument.Error.InvalidFileFormat: + reason = self.tr("The file is not a valid PDF file.") + elif err == QPdfDocument.Error.IncorrectPassword: + reason = self.tr("The password is not correct for this file.") + elif err == QPdfDocument.Error.UnsupportedSecurityScheme: + reason = self.tr("This kind of PDF file cannot be unlocked.") + else: + reason = self.tr("Unknown type of error.") + + return reason + + def __loadPdfFile(self, fileName): + """ + Private method to load a PDF file. + + @param fileName path of the PDF file to load + @type str + """ + canceled = False + err = QPdfDocument.Error.IncorrectPassword + while not canceled and err == QPdfDocument.Error.IncorrectPassword: + err = self.__pdfDocument.load(fileName) + if err == QPdfDocument.Error.IncorrectPassword: + password, ok = QInputDialog.getText( + self, + self.tr("Load PDF File"), + self.tr("Enter password to read the document:"), + QLineEdit.EchoMode.Password, + ) + if ok: + self.__pdfDocument.setPassword(password) + else: + canceled = True + if err != QPdfDocument.Error.None_: + EricMessageBox.critical( + self, + self.tr("Load PDF File"), + self.tr( + """<p>The PDF file <b>{0}</b> could not be loaded.</p>""" + """<p>Reason: {1}</p>""" + ).format(fileName, self.__getErrorString(err)), + ) + self.__documentInfoWidget.setFileName("") + return + + self.__lastOpenPath = os.path.dirname(fileName) + self.__setCurrentFile(fileName) + + documentTitle = self.__pdfDocument.metaData(QPdfDocument.MetaDataField.Title) + self.__setViewerTitle(documentTitle) + + self.__pageSelected(0) + self.__pageSelector.setMaximum(self.__pdfDocument.pageCount() - 1) + self.__pageSelector.setValue(0) + + self.__documentInfoWidget.setFileName(fileName) + + self.__info.setCurrentWidget(self.__tocWidget) + + @pyqtSlot() + def __reload(self): + """ + Private slot to reload the current PDF document. + """ + self.__loadPdfFile(self.__fileName) + + @pyqtSlot() + def __openPdfFile(self): + """ + Private slot to open a PDF file. + """ + if ( + not self.__lastOpenPath + and self.__project is not None + and self.__project.isOpen() + ): + self.__lastOpenPath = self.__project.getProjectPath() + + fileName = EricFileDialog.getOpenFileName( + self, + self.tr("Open PDF File"), + self.__lastOpenPath, + self.tr("PDF Files (*.pdf);;All Files (*)"), + ) + if fileName: + self.__loadPdfFile(fileName) + + @pyqtSlot() + def __openPdfFileNewWindow(self, fileName=""): + """ + Private slot called to open a PDF file in new viewer window. + + @param fileName name of the file to open (defaults to "") + @type str (optional) + """ + if ( + not self.__lastOpenPath + and self.__project is not None + and self.__project.isOpen() + ): + self.__lastOpenPath = self.__project.getProjectPath() + + if not fileName: + fileName = EricFileDialog.getOpenFileName( + self, + self.tr("Open PDF File"), + self.__lastOpenPath, + self.tr("PDF Files (*.pdf);;All Files (*)"), + ) + if fileName: + viewer = PdfViewerWindow( + fileName=fileName, + parent=self.parent(), + fromEric=self.__fromEric, + project=self.__project, + ) + viewer.show() + + @pyqtSlot() + def __closeAll(self): + """ + Private slot to close all windows. + """ + self.__closeOthers() + self.close() + + @pyqtSlot() + def __closeOthers(self): + """ + Private slot to close all other windows. + """ + for win in PdfViewerWindow.windows[:]: + if win != self: + win.close() + + @pyqtSlot(int) + def __pageSelected(self, page): + """ + Private slot to navigate to the given page. + + @param page index of the page to be shown + @type int + """ + nav = self.__view.pageNavigator() + nav.jump(page, QPointF(), nav.currentZoom()) + + self.__checkActions() + + @pyqtSlot(int, float) + def __tocActivated(self, page, zoomFactor): + """ + Private slot to handle the selection of a ToC topic. + + @param page page number + @type int + @param zoomFactor zoom factor + @type float + """ + nav = self.__view.pageNavigator() + nav.jump(page, QPointF(), zoomFactor) + + @pyqtSlot(QPdfLink) + def __handleSearchResult(self, link): + """ + Private slot to handle the selection of a search result. + + @param link PDF link to navigate to + @type QPdfLink + """ + self.__view.pageNavigator().jump(link) + self.__view.addSearchMarker(link) + + @pyqtSlot() + def __search(self): + """ + Private slot to initiate a search. + """ + self.__info.setCurrentWidget(self.__searchWidget) + self.__searchWidget.activateSearch() + + 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 + # insert filename into list of recently opened files + self.__addToRecentList(fileName) + + @pyqtSlot() + def __about(self): + """ + Private slot to show a little About message. + """ + EricMessageBox.about( + self, + self.tr("About eric PDF Viewer"), + self.tr("The eric PDF Viewer is a simple component for viewing PDF files."), + ) + + @pyqtSlot() + def __aboutQt(self): + """ + Private slot to handle the About Qt dialog. + """ + EricMessageBox.aboutQt(self, "eric PDF Viewer") + + @pyqtSlot() + def __whatsThis(self): + """ + Private slot called in to enter Whats This mode. + """ + QWhatsThis.enterWhatsThisMode() + + @pyqtSlot() + def __showPreferences(self): + """ + Private slot to set the preferences. + """ + from eric7.Preferences.ConfigurationDialog import ( + ConfigurationDialog, + ConfigurationMode, + ) + + dlg = ConfigurationDialog( + None, + "Configuration", + True, + fromEric=True, + displayMode=ConfigurationMode.PDFVIEWERMODE, + ) + dlg.show() + dlg.showConfigurationPageByName("pdfViewerPage") + dlg.exec() + if dlg.result() == QDialog.DialogCode.Accepted: + dlg.setPreferences() + Preferences.syncPreferences() + + @pyqtSlot() + def __showFileMenu(self): + """ + Private slot to modify the file menu before being shown. + """ + self.__menuRecentAct.setEnabled(len(self.__recent) > 0) + + @pyqtSlot() + def __showRecentMenu(self): + """ + Private slot to set up the recent files menu. + """ + self.__loadRecent() + + self.__recentMenu.clear() + + for idx, rs in enumerate(self.__recent, start=1): + formatStr = "&{0:d}. {1}" if idx < 10 else "{0:d}. {1}" + act = self.__recentMenu.addAction( + formatStr.format( + idx, + FileSystemUtilities.compactPath( + rs, PdfViewerWindow.maxMenuFilePathLen + ), + ) + ) + act.setData(rs) + act.setEnabled(pathlib.Path(rs).exists()) + + self.__recentMenu.addSeparator() + self.__recentMenu.addAction(self.tr("&Clear"), self.__clearRecent) + + @pyqtSlot(QAction) + def __openRecentPdfFile(self, act): + """ + Private method to open a file from the list of recently opened files. + + @param act reference to the action that triggered + @type QAction + """ + fileName = act.data() + if fileName is not None: + if Preferences.getPdfViewer("PdfViewerOpenRecentInNewWindow"): + self.__openPdfFileNewWindow(fileName) + else: + self.__loadPdfFile(fileName) + + @pyqtSlot() + def __clearRecent(self): + """ + Private method to clear the list of recently opened files. + """ + self.__recent = [] + + def __loadRecent(self): + """ + Private method to load the list of recently opened files. + """ + self.__recent = [] + Preferences.Prefs.rsettings.sync() + rs = Preferences.Prefs.rsettings.value(recentNamePdfFiles) + if rs is not None: + for f in Preferences.toList(rs): + if pathlib.Path(f).exists(): + self.__recent.append(f) + + def __saveRecent(self): + """ + Private method to save the list of recently opened files. + """ + Preferences.Prefs.rsettings.setValue(recentNamePdfFiles, self.__recent) + Preferences.Prefs.rsettings.sync() + + def __addToRecentList(self, fileName): + """ + Private method to add a file name to the list of recently opened files. + + @param fileName name of the file to be added + """ + if fileName: + for recent in self.__recent[:]: + if FileSystemUtilities.samepath(fileName, recent): + self.__recent.remove(recent) + self.__recent.insert(0, fileName) + maxRecent = Preferences.getPdfViewer("RecentNumber") + if len(self.__recent) > maxRecent: + self.__recent = self.__recent[:maxRecent] + self.__saveRecent() + + @pyqtSlot() + def __showDocumentProperties(self): + """ + Private slot to open a dialog showing the document properties. + """ + self.__toggleSideBar(True) + self.__info.setCurrentWidget(self.__documentInfoWidget) + + @pyqtSlot() + def __gotoPage(self): + """ + Private slot to show a dialog to select a page to jump to. + """ + from .PdfGoToDialog import PdfGoToDialog + + dlg = PdfGoToDialog( + self.__view.pageNavigator().currentPage(), + self.__pdfDocument.pageCount(), + self, + ) + if dlg.exec() == QDialog.DialogCode.Accepted: + page = dlg.getPage() + self.__pageSelected(page) + + @pyqtSlot() + def __previousPage(self): + """ + Private slot to go to the previous page. + """ + curPage = self.__view.pageNavigator().currentPage() + if curPage > 0: + self.__pageSelected(curPage - 1) + + @pyqtSlot() + def __nextPage(self): + """ + Private slot to go to the next page. + """ + curPage = self.__view.pageNavigator().currentPage() + if curPage < self.__pdfDocument.pageCount() - 1: + self.__pageSelected(curPage + 1) + + @pyqtSlot() + def __startDocument(self): + """ + Private slot to go to the first page of the document. + """ + self.__pageSelected(0) + + @pyqtSlot() + def __endDocument(self): + """ + Private slot to go to the last page of the document. + """ + self.__pageSelected(self.__pdfDocument.pageCount() - 1) + + @pyqtSlot() + def __backInHistory(self): + """ + Private slot to go back in the view history. + """ + self.__view.pageNavigator().back() + + @pyqtSlot() + def __forwardInHistory(self): + """ + Private slot to go forward in the view history. + """ + self.__view.pageNavigator().forward() + + @pyqtSlot(bool) + def __toggleFullScreen(self, on): + """ + Private slot to toggle the full screen mode. + + @param on flag indicating to activate full screen mode + @type bool + """ + if on: + self.showFullScreen() + else: + self.showNormal() + + @pyqtSlot() + def __zoomIn(self): + """ + Private slot to zoom into the view. + """ + self.__view.zoomIn() + + @pyqtSlot() + def __zoomOut(self): + """ + Private slot to zoom out of the view. + """ + self.__view.zoomOut() + + @pyqtSlot() + def __zoomReset(self): + """ + Private slot to reset the zoom factor of the view. + """ + self.__view.zoomReset() + + @pyqtSlot(bool) + def __zoomPageWidth(self, checked): + """ + Private slot to fit the page width. + + @param checked flag indicating the check state + @type bool + """ + if checked: + self.__view.setZoomMode(QPdfView.ZoomMode.FitToWidth) + self.zoomWholePageAct.setChecked(False) + + @pyqtSlot(bool) + def __zoomWholePage(self, checked): + """ + Private slot to fit the page width. + + @param checked flag indicating the check state + @type bool + """ + if checked: + self.__view.setZoomMode(QPdfView.ZoomMode.FitInView) + self.zoomPageWidthAct.setChecked(False) + + @pyqtSlot(QPdfView.ZoomMode) + def __zoomModeChanged(self, zoomMode): + """ + Private slot to handle a change of the zoom mode. + + @param zoomMode new zoom mode + @type QPdfView.ZoomMode + """ + self.zoomWholePageAct.setChecked(zoomMode == QPdfView.ZoomMode.FitInView) + self.zoomPageWidthAct.setChecked(zoomMode == QPdfView.ZoomMode.FitToWidth) + + @pyqtSlot(QAction) + def __displayModeSelected(self, act): + """ + Private slot to handle the selection of a display mode. + + @param act reference to the triggering action + @type QAction + """ + if act is self.__singlePageAct: + Preferences.setPdfViewer("PdfViewerDisplayMode", "single") + else: + Preferences.setPdfViewer("PdfViewerDisplayMode", "continuous") + self.__setDisplayMode() + + def __setDisplayMode(self): + """ + Private method to set the display mode iaw. configuration. + """ + if Preferences.getPdfViewer("PdfViewerDisplayMode") == "single": + self.__view.setPageMode(QPdfView.PageMode.SinglePage) + self.__singlePageAct.setChecked(True) + else: + self.__view.setPageMode(QPdfView.PageMode.MultiPage) + self.__continuousPageAct.setChecked(True) + return + + @pyqtSlot(bool) + def __toggleSideBar(self, visible): + """ + Private slot to togle the sidebar (info) widget. + + @param visible desired state of the sidebar + @type bool + """ + self.sidebarAct.setChecked(visible) + self.__info.setVisible(visible) + Preferences.setPdfViewer("PdfViewerSidebarVisible", visible) + + @pyqtSlot(bool) + def __toggleOpenRecentNew(self, on): + """ + Private slot to toggle the 'Open Recent File in New Window' action. + + @param on desired state of the action + @type bool + """ + Preferences.setPdfViewer("PdfViewerOpenRecentInNewWindow", on) + + @pyqtSlot() + def __copyAllTextOfPage(self): + """ + Private slot to copy all text of the current page to the system clipboard. + """ + selection = self.__pdfDocument.getAllText( + self.__view.pageNavigator().currentPage() + ) + selection.copyToClipboard() + + @pyqtSlot() + def __copyAllText(self): + """ + Private slot to copy all text of the document to the system clipboard. + """ + textPages = [] + for page in range(self.__pdfDocument.pageCount()): + textPages.append(self.__pdfDocument.getAllText(page).text()) + QGuiApplication.clipboard().setText( + "\r\n".join(textPages), QClipboard.Mode.Clipboard + ) + + @pyqtSlot() + def __copyText(self): + """ + Private slot to copy the selected text to the system clipboard. + """ + selection = self.__view.getSelection() + if selection is not None: + selection.copyToClipboard()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/PdfZoomSelector.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a widget to select a PDF zoom factor. +""" + +from PyQt6.QtCore import Qt, pyqtSignal, pyqtSlot +from PyQt6.QtPdfWidgets import QPdfView +from PyQt6.QtWidgets import QComboBox + + +class PdfZoomSelector(QComboBox): + """ + Class implementing a widget to select a PDF zoom factor. + + @signal zoomFactorChanged(factor) emitted to indicate the selected zoom factor + @signal zoomModeChanged(zoomMode) emitted to indicate the selected zoom mode + """ + + zoomFactorChanged = pyqtSignal(float) + zoomModeChanged = pyqtSignal(QPdfView.ZoomMode) + + ZoomValues = ( + 0.12, + 0.25, + 0.33, + 0.5, + 0.66, + 0.75, + 1.0, + 1.25, + 1.50, + 2.0, + 4.0, + 8.0, + 16.0, + 25.0, + 50.0, + ) + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (defaults to None) + @type QWidget (optional) + """ + super().__init__(parent) + + self.setEditable(True) + self.setInsertPolicy(QComboBox.InsertPolicy.NoInsert) + + self.__pageWidthLabel = self.tr("Page Width") + self.__wholePageLabel = self.tr("Whole Page") + + self.addItem(self.__pageWidthLabel) + self.addItem(self.__wholePageLabel) + for val in PdfZoomSelector.ZoomValues: + self.addItem(self.tr("{0}%").format(int(val * 100))) + + self.lineEdit().setAlignment(Qt.AlignmentFlag.AlignCenter) + + self.currentIndexChanged.connect(self.__editingFinished) + self.lineEdit().editingFinished.connect(self.__editingFinished) + + @pyqtSlot() + def __editingFinished(self): + """ + Private slot handling the end of entering a zoom factor. + """ + self.__processText(self.lineEdit().text()) + + @pyqtSlot(str) + def __processText(self, text): + """ + Private slot to handle the change of the entered zoom factor. + + @param text text to be handled + @type str + """ + if text == self.__pageWidthLabel: + self.zoomModeChanged.emit(QPdfView.ZoomMode.FitToWidth) + elif text == self.__wholePageLabel: + self.zoomModeChanged.emit(QPdfView.ZoomMode.FitInView) + else: + withoutPercent = text.replace("%", "").replace("&", "") + try: + zoomLevel = int(withoutPercent) + factor = zoomLevel / 100.0 + except ValueError: + factor = 1.0 + + self.zoomModeChanged.emit(QPdfView.ZoomMode.Custom) + self.zoomFactorChanged.emit(factor) + + @pyqtSlot() + def reset(self): + """ + Public slot to reset the zoom factor to 100%. + """ + self.setCurrentIndex(8) # index 8 is 100% + self.__editingFinished() + + @pyqtSlot(float) + def setZoomFactor(self, zoomFactor): + """ + Public slot to set the current zoom factor. + + @param zoomFactor current zoom factor + @type float + """ + self.setCurrentText(self.tr("{0}%").format(round(zoomFactor * 100))) + self.__editingFinished() + + @pyqtSlot(QPdfView.ZoomMode) + def setZoomMode(self, zoomMode): + """ + Public slot to set the zoom value iaw. the zoom mode. + + @param zoomMode current zoom mode + @type QPdfView.ZoomMode + """ + if zoomMode == QPdfView.ZoomMode.FitToWidth: + self.setCurrentIndex(0) # index 0 is 'Page Width' + elif zoomMode == QPdfView.ZoomMode.FitInView: + self.setCurrentIndex(1) # index 1 is 'Whole Page' + else: + # ignore Custom mode here + return + self.__editingFinished()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/PdfViewer/__init__.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing a simple PDF viewer tool. +"""
--- a/src/eric7/Preferences/ConfigurationDialog.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/Preferences/ConfigurationDialog.py Tue Jan 24 10:52:27 2023 +0100 @@ -83,6 +83,7 @@ HEXEDITORMODE = 2 WEBBROWSERMODE = 3 EDITORMODE = 4 + PDFVIEWERMODE = 5 class ConfigurationWidget(QWidget): @@ -247,6 +248,13 @@ None, None, ], + "pdfViewerPage": [ + self.tr("PDF Viewer"), + "ericPdf", + "PdfViewerPage", + None, + None, + ], "pipPage": [ self.tr("Python Package Management"), "pypi", @@ -854,6 +862,37 @@ ], } + elif displayMode == ConfigurationMode.PDFVIEWERMODE: + self.configItems = { + # key : [display string, pixmap name, dialog module name or + # page creation function, parent key, + # reference to configuration page (must always be last)] + # The dialog module must have the module function 'create' to + # create the configuration page. This must have the method + # 'save' to save the settings. + "iconsPage": [ + self.tr("Icons"), + "preferences-icons", + "IconsPage", + None, + None, + ], + "interfacePage": [ + self.tr("Interface"), + "preferences-interface", + "InterfaceLightPage", + None, + None, + ], + "pdfViewerPage": [ + self.tr("PDF Viewer"), + "ericPdf", + "PdfViewerPage", + None, + None, + ], + } + else: # display mode for generic use self.configItems = {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/Preferences/ConfigurationPages/PdfViewerPage.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the PDF Viewer configuration page. +""" + +from eric7 import Preferences + +from .ConfigurationPageBase import ConfigurationPageBase +from .Ui_PdfViewerPage import Ui_PdfViewerPage + + +class PdfViewerPage(ConfigurationPageBase, Ui_PdfViewerPage): + """ + Class implementing the PDF Viewer configuration page. + """ + + def __init__(self): + """ + Constructor + """ + super().__init__() + self.setupUi(self) + self.setObjectName("PdfViewerPage") + + # set initial values + self.contextLengthSpinBox.setValue( + Preferences.getPdfViewer("PdfSearchContextLength") + ) + self.highlightCheckBox.setChecked( + Preferences.getPdfViewer("PdfSearchHighlightAll") + ) + self.recentFilesSpinBox.setValue(Preferences.getPdfViewer("RecentNumber")) + + def save(self): + """ + Public slot to save the IRC configuration. + """ + Preferences.setPdfViewer( + "PdfSearchContextLength", self.contextLengthSpinBox.value() + ) + Preferences.setPdfViewer( + "PdfSearchHighlightAll", self.highlightCheckBox.isChecked() + ) + Preferences.setPdfViewer("RecentNumber", self.recentFilesSpinBox.value()) + + +def create(dlg): + """ + Module function to create the configuration page. + + @param dlg reference to the configuration dialog + @return reference to the instantiated page + @rtype ConfigurationPageBase + """ + page = PdfViewerPage() + return page
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/Preferences/ConfigurationPages/PdfViewerPage.ui Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,156 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PdfViewerPage</class> + <widget class="QWidget" name="PdfViewerPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>372</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="headerLabel"> + <property name="text"> + <string><b>Configure PDF Viewer</b></string> + </property> + </widget> + </item> + <item> + <widget class="Line" name="line14"> + <property name="frameShape"> + <enum>QFrame::HLine</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Sunken</enum> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Search</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Context Length:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="contextLengthSpinBox"> + <property name="toolTip"> + <string>Enter the amount of characters to show before and after the search string.</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="minimum"> + <number>10</number> + </property> + <property name="maximum"> + <number>60</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>203</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0" colspan="3"> + <widget class="QCheckBox" name="highlightCheckBox"> + <property name="toolTip"> + <string>Select to highlight all search results in the document.</string> + </property> + <property name="text"> + <string>Highlight all search results</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_7"> + <property name="title"> + <string>Recent Files</string> + </property> + <layout class="QHBoxLayout" name="_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Number of recent files:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="recentFilesSpinBox"> + <property name="toolTip"> + <string>Enter the number of recent files to remember</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="minimum"> + <number>5</number> + </property> + <property name="maximum"> + <number>50</number> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>132</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>contextLengthSpinBox</tabstop> + <tabstop>highlightCheckBox</tabstop> + <tabstop>recentFilesSpinBox</tabstop> + </tabstops> + <resources/> + <connections/> +</ui>
--- a/src/eric7/Preferences/__init__.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/Preferences/__init__.py Tue Jan 24 10:52:27 2023 +0100 @@ -39,6 +39,7 @@ QUrl, ) from PyQt6.QtGui import QColor, QFont, QPalette +from PyQt6.QtPdfWidgets import QPdfView from PyQt6.QtWidgets import QApplication try: @@ -1451,13 +1452,12 @@ # defaults for geometry geometryDefaults = { - "HelpViewerGeometry": QByteArray(), - "HelpInspectorGeometry": QByteArray(), - "WebBrowserGeometry": QByteArray(), - "IconEditorGeometry": QByteArray(), - "HexEditorGeometry": QByteArray(), "MainGeometry": QByteArray(), "MainMaximized": False, + "HexEditorGeometry": QByteArray(), + "IconEditorGeometry": QByteArray(), + "PdfViewerGeometry": QByteArray(), + "WebBrowserGeometry": QByteArray(), "WebInspectorGeometry": QByteArray(), } @@ -1631,6 +1631,20 @@ "MouseClickGotoButton": Qt.MouseButton.LeftButton, } + # defaults for Hex Editor + pdfViewerDefaults = { + "PdfViewerState": QByteArray(), + "PdfViewerSplitterState": QByteArray(), + "RecentNumber": 9, + "PdfViewerDisplayMode": "single", # single or continuous + "PdfViewerSidebarVisible": True, + "PdfViewerZoomMode": QPdfView.ZoomMode.Custom.value, + "PdfViewerZoomFactor": 1.0, + "PdfViewerOpenRecentInNewWindow": False, + "PdfSearchContextLength": 30, + "PdfSearchHighlightAll": True, + } + def readToolGroups(): """ @@ -3838,6 +3852,53 @@ Prefs.settings.setValue("AssistantJedi/" + key, value) +def getPdfViewer(key): + """ + Module function to retrieve the Pdf Viewer related settings. + + @param key the key of the value to get + @type str + @return the requested user setting + @rtype Any + """ + if key in ("RecentNumber", "PdfSearchContextLength"): + return int( + Prefs.settings.value("PdfViewer/" + key, Prefs.pdfViewerDefaults[key]) + ) + elif key in ( + "PdfViewerSidebarVisible", + "PdfViewerOpenRecentInNewWindow", + "PdfSearchHighlightAll", + ): + return toBool( + Prefs.settings.value("PdfViewer/" + key, Prefs.pdfViewerDefaults[key]) + ) + elif key in ("PdfViewerZoomFactor",): + return float( + Prefs.settings.value("PdfViewer/" + key, Prefs.pdfViewerDefaults[key]) + ) + elif key == "PdfViewerZoomMode": + return QPdfView.ZoomMode( + int(Prefs.settings.value("PdfViewer/" + key, Prefs.pdfViewerDefaults[key])) + ) + else: + return Prefs.settings.value("PdfViewer/" + key, Prefs.pdfViewerDefaults[key]) + + +def setPdfViewer(key, value): + """ + Module function to store the Pdf Viewer related settings. + + @param key the key of the setting to be set + @type str + @param value the value to be set + @type Any + """ + if key == "PdfViewerZoomMode": + value = value.value + Prefs.settings.setValue("PdfViewer/" + key, value) + + def getGeometry(key): """ Module function to retrieve the display geometry.
--- a/src/eric7/Project/ProjectBrowser.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/Project/ProjectBrowser.py Tue Jan 24 10:52:27 2023 +0100 @@ -50,6 +50,7 @@ @signal designerFile(filename) emitted to open a Qt-Designer file (str) @signal linguistFile(filename) emitted to open a Qt-Linguist (*.ts) file (str) + @signal pdfFile(filename) emitted to open a PDF file (str) @signal pixmapEditFile(filename) emitted to edit a pixmap file (str) @signal pixmapFile(filename) emitted to open a pixmap file (str) @signal preferencesChanged() emitted when the preferences have been changed @@ -80,6 +81,7 @@ closeSourceWindow = pyqtSignal(str) designerFile = pyqtSignal(str) linguistFile = pyqtSignal(str) + pdfFile = pyqtSignal(str) pixmapEditFile = pyqtSignal(str) pixmapFile = pyqtSignal(str) preferencesChanged = pyqtSignal()
--- a/src/eric7/Project/ProjectOthersBrowser.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/Project/ProjectOthersBrowser.py Tue Jan 24 10:52:27 2023 +0100 @@ -114,6 +114,7 @@ self.svgFile.connect(projectBrowser.svgFile) self.umlFile.connect(projectBrowser.umlFile) self.binaryFile.connect(projectBrowser.binaryFile) + self.pdfFile.connect(projectBrowser.pdfFile) def getIcon(self): """ @@ -137,6 +138,9 @@ self.openInEditorAct = self.menu.addAction( self.tr("Open in Editor"), self._openFileInEditor ) + self.openInPdfViewerAct = self.menu.addAction( + self.tr("Open in PDF Viewer"), self._openPdfViewer + ) self.menu.addSeparator() self.mimeTypeAct = self.menu.addAction( self.tr("Show Mime-Type"), self.__showMimeType @@ -260,6 +264,7 @@ if isinstance(itm, ProjectBrowserFileItem): self.editPixmapAct.setVisible(itm.isPixmapFile()) self.openInEditorAct.setVisible(itm.isSvgFile()) + self.openInPdfViewerAct.setVisible(itm.isPdfFile()) self.mimeTypeAct.setVisible(True) self.menu.popup(self.mapToGlobal(coord)) elif isinstance(itm, ProjectBrowserDirectoryItem): @@ -347,6 +352,16 @@ if isinstance(itm, ProjectBrowserFileItem): self.binaryFile.emit(itm.fileName()) + def _openPdfViewer(self): + """ + Protected slot to handle the open in PDF viewer popup menu entry. + """ + itmList = self.getSelectedItems() + + for itm in itmList: + if isinstance(itm, ProjectBrowserFileItem) and itm.isPdfFile(): + self.pdfFile.emit(itm.fileName()) + def _openItem(self): """ Protected slot to handle the open popup menu entry. @@ -355,7 +370,9 @@ for itm in itmList: if isinstance(itm, ProjectBrowserFileItem): - if itm.isSvgFile(): + if itm.isPdfFile(): + self.pdfFile.emit(itm.fileName()) + elif itm.isSvgFile(): self.svgFile.emit(itm.fileName()) elif itm.isPixmapFile(): self.pixmapFile.emit(itm.fileName())
--- a/src/eric7/Tools/TrayStarter.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/Tools/TrayStarter.py Tue Jan 24 10:52:27 2023 +0100 @@ -139,6 +139,11 @@ self.tr("Icon Editor"), self.__startIconEditor, ) + self.__menu.addAction( + EricPixmapCache.getIcon("ericPdf"), + self.tr("PDF Viewer"), + self.__startPdfViewer, + ) self.__menu.addSeparator() self.__menu.addAction( @@ -461,6 +466,12 @@ """ self.__startProc("eric7_shell.py") + def __startPdfViewer(self): + """ + Private slot to start the eric PDF Viewer window. + """ + self.__startProc("eric7_pdf.py") + def __showRecentProjectsMenu(self): """ Private method to set up the recent projects menu.
--- a/src/eric7/UI/Browser.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/UI/Browser.py Tue Jan 24 10:52:27 2023 +0100 @@ -89,6 +89,7 @@ @signal binaryFile(filename) emitted to open a file as binary (str) @signal testFile(filename) emitted to open a Python file for a unit test (str) + @signal pdfFile(filename) emitted to open a PDF file (str) """ sourceFile = pyqtSignal((str,), (str, int), (str, list), (str, int, str)) @@ -102,6 +103,7 @@ svgFile = pyqtSignal(str) umlFile = pyqtSignal(str) binaryFile = pyqtSignal(str) + pdfFile = pyqtSignal(str) testFile = pyqtSignal(str) def __init__(self, parent=None): @@ -294,6 +296,10 @@ QCoreApplication.translate("Browser", "Open in Editor"), self._openFileInEditor, ) + self.openInPdfViewerAct = self.menu.addAction( + QCoreApplication.translate("Browser", "Open in PDF Viewer"), + self._openPdfViewer, + ) self.menu.addSeparator() self.mimeTypeAct = self.menu.addAction( QCoreApplication.translate("Browser", "Show Mime-Type"), self.__showMimeType @@ -447,11 +453,13 @@ else: self.editPixmapAct.setVisible(itm.isPixmapFile()) self.openInEditorAct.setVisible(itm.isSvgFile()) + self.openInPdfViewerAct.setVisible(itm.isPdfFile()) self.menu.popup(coord) elif isinstance( itm, (BrowserClassItem, BrowserMethodItem, BrowserImportItem) ): self.editPixmapAct.setVisible(False) + self.openInPdfViewerAct.setVisible(False) self.menu.popup(coord) elif isinstance(itm, BrowserClassAttributeItem): self.attributeMenu.popup(coord) @@ -651,6 +659,16 @@ if isinstance(itm, BrowserFileItem): self.binaryFile.emit(itm.fileName()) + def _openPdfViewer(self): + """ + Protected slot to handle the open in PDF viewer popup menu entry. + """ + itmList = self.getSelectedItems([BrowserFileItem]) + + for itm in itmList: + if isinstance(itm, BrowserFileItem): + self.pdfFile.emit(itm.fileName()) + def _openFileInEditor(self): """ Protected slot to handle the Open in Editor menu action.
--- a/src/eric7/UI/BrowserModel.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/UI/BrowserModel.py Tue Jan 24 10:52:27 2023 +0100 @@ -1394,6 +1394,15 @@ """ return self.fileext == ".svg" + def isPdfFile(self): + """ + Public method to check, if this file is a PDF file. + + @return flag indicating a PDF file + @rtype bool + """ + return self.fileext == ".pdf" + def isDFile(self): """ Public method to check, if this file is a D file.
--- a/src/eric7/UI/UserInterface.py Tue Jan 24 10:03:59 2023 +0100 +++ b/src/eric7/UI/UserInterface.py Tue Jan 24 10:52:27 2023 +0100 @@ -433,6 +433,7 @@ self.projectBrowser.svgFile.connect(self.__showSvg) self.projectBrowser.umlFile.connect(self.__showUml) self.projectBrowser.binaryFile.connect(self.__openHexEditor) + self.projectBrowser.pdfFile.connect(self.__openPdfViewer) self.project.sourceFile.connect(self.viewmanager.openSourceFile) self.project.designerFile.connect(self.__designer) @@ -517,6 +518,7 @@ self.browser.svgFile.connect(self.__showSvg) self.browser.umlFile.connect(self.__showUml) self.browser.binaryFile.connect(self.__openHexEditor) + self.browser.pdfFile.connect(self.__openPdfViewer) self.browser.testFile.connect(self.__startTestScript) self.browser.trpreview.connect(self.__TRPreviewer) @@ -3141,6 +3143,25 @@ self.snapshotAct.triggered.connect(self.__snapshot) self.actions.append(self.snapshotAct) + self.pdfViewerAct = EricAction( + self.tr("eric PDF Viewer"), + EricPixmapCache.getIcon("ericPdf"), + self.tr("eric PDF &Viewer..."), + 0, + 0, + self, + "pdf_viewer", + ) + self.pdfViewerAct.setStatusTip(self.tr("Start the eric PDF Viewer")) + self.pdfViewerAct.setWhatsThis( + self.tr( + """<b>eric PDF Viewer</b>""" + """<p>Starts the eric PDF Viewer for viewing PDF files.</p>""" + ) + ) + self.pdfViewerAct.triggered.connect(self.__openPdfViewer) + self.actions.append(self.pdfViewerAct) + self.prefAct = EricAction( self.tr("Preferences"), EricPixmapCache.getIcon("configure"), @@ -4142,6 +4163,7 @@ toolstb.addAction(self.hexEditorAct) toolstb.addAction(self.iconEditorAct) toolstb.addAction(self.snapshotAct) + toolstb.addAction(self.pdfViewerAct) if self.webBrowserAct: toolstb.addSeparator() toolstb.addAction(self.webBrowserAct) @@ -5085,6 +5107,7 @@ btMenu.addAction(self.hexEditorAct) btMenu.addAction(self.iconEditorAct) btMenu.addAction(self.snapshotAct) + btMenu.addAction(self.pdfViewerAct) if self.webBrowserAct: btMenu.addAction(self.webBrowserAct) @@ -6323,7 +6346,8 @@ """ Private slot to open the hex editor window. - @param fn filename of the file to show (string) + @param fn path of the file to show (defaults to "") + @type str (optional) """ from eric7.HexEdit.HexEditMainWindow import HexEditMainWindow @@ -6332,11 +6356,26 @@ @pyqtSlot() @pyqtSlot(str) + def __openPdfViewer(self, fn=""): + """ + Private slot to open the PDF viewer window. + + @param fn path of the file to show (defaults to "") + @type str (optional) + """ + from eric7.PdfViewer.PdfViewerWindow import PdfViewerWindow + + dlg = PdfViewerWindow(fn, self, fromEric=True, project=self.project) + dlg.show() + + @pyqtSlot() + @pyqtSlot(str) def __editPixmap(self, fn=""): """ Private slot to show a pixmap in a dialog. - @param fn filename of the file to show (string) + @param fn path of the file to show (defaults to "") + @type str (optional) """ from eric7.IconEditor.IconEditorWindow import IconEditorWindow @@ -6349,7 +6388,8 @@ """ Private slot to show a pixmap in a dialog. - @param fn filename of the file to show (string) + @param fn path of the file to show (defaults to "") + @type str (optional) """ from eric7.Graphics.PixmapDiagram import PixmapDiagram
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/eric7_pdf.py Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +eric PDF Viewer. + +This is the main Python script that performs the necessary initialization +of the PDF viewer and starts the Qt event loop. This is a standalone version +of the integrated PDF viewer. +""" + +import os +import sys + +from PyQt6.QtGui import QGuiApplication + +for arg in sys.argv[:]: + if arg.startswith("--config="): + from eric7 import Globals + + configDir = arg.replace("--config=", "") + Globals.setConfigDir(configDir) + sys.argv.remove(arg) + elif arg.startswith("--settings="): + from PyQt6.QtCore import QSettings + + settingsDir = os.path.expanduser(arg.replace("--settings=", "")) + if not os.path.isdir(settingsDir): + os.makedirs(settingsDir) + QSettings.setPath( + QSettings.Format.IniFormat, QSettings.Scope.UserScope, settingsDir + ) + sys.argv.remove(arg) + +from eric7.Globals import AppInfo +from eric7.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 eric7.PdfViewer.PdfViewerWindow import PdfViewerWindow + + try: + fileName = argv[1] + except IndexError: + fileName = "" + + editor = PdfViewerWindow(fileName, None) + return editor + + +def main(): + """ + Main entry point into the application. + """ + QGuiApplication.setDesktopFileName("eric7_pdf.desktop") + + 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, "eric PDF Viewer", "", "Little tool to view PDF 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/src/eric7/eric7_pdf.pyw Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2023 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Windows entry point. +""" + +if __name__ == "__main__": + from eric7_pdf import main + + main()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/documentProperties.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="documentProperties.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 1,1 V 21 H 2.25 21 V 19.75 1 H 19.75 2.25 1 m 1.25,5 h 17.5 V 19.75 H 2.25 V 6 M 3.5,7.25 V 18.5 H 9.75 V 7.25 H 3.5 M 11,8.5 v 1.25 h 7.5 V 8.5 H 11 m 0,3.75 v 1.25 h 5 V 12.25 H 11 M 11,16 v 1.25 h 2.5 V 16 H 11" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/ericPdf.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,285 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="22" + height="22" + version="1.1" + viewBox="0 0 5.8208 5.8208" + id="svg37" + sodipodi:docname="ericPdf.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview39" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg37" /> + <defs + id="defs19"> + <linearGradient + id="f" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#54d883" + offset="0" + id="stop2" /> + <stop + stop-color="#abf9c7" + offset="1" + id="stop4" /> + </linearGradient> + <linearGradient + id="a" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#292c2f" + offset="0" + id="stop7" /> + <stop + stop-opacity="0" + offset="1" + id="stop9" /> + </linearGradient> + <linearGradient + id="g" + x1="9" + x2="38" + y1="9" + y2="38" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="h" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#197cf1" + offset="0" + id="stop13" /> + <stop + stop-color="#20bcfa" + offset="1" + id="stop15" /> + </linearGradient> + <linearGradient + id="e" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="a-3" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.07298809,0,0,0.07298809,0.59308977,-23.158892)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-6" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop4-7" /> + </linearGradient> + <linearGradient + id="linearGradient860" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-14-332.36)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop856" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop858" /> + </linearGradient> + <linearGradient + id="a-5" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-14,-332.36)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-3" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop4-5" /> + </linearGradient> + <linearGradient + id="linearGradient916" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-14-332.36)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop912" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop914" /> + </linearGradient> + <linearGradient + id="a-6" + y1="392.35999" + y2="365.20001" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(365.57,152.44)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-2" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".2" + id="stop4-9" /> + </linearGradient> + </defs> + <g + id="g918"> + <path + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + id="use26" + color-interpolation="sRGB" + color="#000000" + d="M 10,3 V 61 H 54 V 3 H 40 Z" + style="fill:url(#a-5)" /> + </g> + <g + id="g921" /> + <g + id="g952" + transform="translate(-0.26458001,-0.26000243)"> + <image + x="0.26458001" + y="0.26000243" + width="5.2916999" + height="5.2916999" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,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 " + id="image21" /> + </g> + <g + transform="matrix(0.09621154,0,0,0.09449347,-34.041566,-46.018318)" + id="g27"> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g15"> + <path + fill="#da2c2c" + d="m 389.57,517.8 v 28 h 22 v -28 h -14 z" + id="path9" /> + <rect + opacity="0.25" + x="389.57001" + y="544.79999" + width="22" + height="1" + id="rect11" /> + <rect + opacity="0.5" + x="389.57001" + y="517.79999" + width="22" + fill="#ffffff" + height="1" + id="rect13" /> + </g> + <rect + width="1" + x="392.57001" + y="518.79999" + fill="#ffffff" + height="26" + fill-opacity="0.252" + id="rect17" /> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g25"> + <rect + x="393.57001" + y="518.79999" + fill-opacity="0.083" + width="1" + height="26" + id="rect19" /> + <path + opacity="0.8" + fill="#ffffff" + d="m 398.43,525.8 c -0.162,0.01 -0.336,0 -0.527,0.132 -1.705,0.734 -0.561,2.747 1.612,4.279 0.013,-0.289 0.133,-0.917 0.13,-1.194 -1.854,-1.271 -2.939,-2.509 -1.757,-2.999 1.246,-0.517 1.633,0.895 1.655,2.944 0.116,0.12 0.92,0.654 1.042,0.739 -0.045,-1.869 -1.039,-3.777 -1.993,-3.885 -0.051,-0.01 -0.107,-0.01 -0.161,-0.01 m 1.736,3.252 -0.388,-0.151 -0.322,0.217 -0.065,0.682 0.406,0.312 0.169,0.226 c 1.251,0.83 2.317,1.469 3.718,1.997 l 0.874,0.13 h 0.575 c 0.53,-0.313 0.214,-0.433 2.141,-0.673 l -2.171,0.168 c -1.675,-0.649 -3.385,-1.582 -4.94,-2.6 m 3.822,2.72 c 0.251,0 2.211,-0.17 2.99,-0.242 0.772,-0.12 1.719,-0.156 1.54,0.746 0.204,-0.481 -0.261,-1.298 -0.922,-1.442 -1.023,-0.229 -2.646,0.398 -3.229,0.591 m 0.94,0.25 c -0.304,0.12 -0.999,0.207 -1.327,0.316 0.465,0.241 2.802,1.103 3.766,0.923 0.154,0 0.805,-0.468 0.821,-0.805 -0.05,0.613 -2.906,0.156 -3.26,-0.445 m 0.635,0.011 c -0.25,-0.12 -1.422,-0.114 -1.681,-0.211 -1.468,0.513 -3.442,1.719 -4.855,2.532 l -0.409,0.12 c -0.079,0.561 -0.425,0.755 -0.74,1.118 l 0.478,0.481 0.374,-0.613 c 1.654,-0.998 4.924,-2.611 6.832,-3.386 m -6.832,3.386 c -0.202,0.12 -0.395,0.241 -0.578,0.361 -0.424,1.204 -0.919,2.01 -1.391,1.98 0.729,0.349 2.301,-1.37 2.83,-2.986 m -2.83,2.986 c -1.113,-0.661 1.243,-1.867 1.77,-2.217 0.119,-0.337 -0.206,-0.895 -0.1,-1.292 -1.661,1.034 -2.643,2.531 -2.086,3.24 0.134,0.168 0.276,0.253 0.417,0.265 m 1.617,-2.52 c 0.077,-0.31 1.103,-1.015 1.289,-1.123 0.248,-1.954 0.26,-2.147 0.471,-3.666 -0.126,-0.12 -0.902,-0.725 -1.022,-0.809 -0.149,1.624 -0.429,3.824 -0.826,5.293" + id="path21" /> + <path + fill="url(#a)" + d="m 389.57,517.8 v 28 h 22 v -28 h -17 z" + id="path23" + style="fill:url(#a-6)" /> + </g> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/ericPdf48.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,206 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="48" + height="48" + version="1.1" + viewBox="0 0 12.7 12.7" + id="svg37" + sodipodi:docname="ericPdf48.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + inkscape:export-filename="/home/detlev/Development/Python/Eric/eric7_pdf/src/eric7/icons/oxygen/ericPdf48.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview39" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="13.416667" + inkscape:cx="23.999999" + inkscape:cy="23.999999" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg37" /> + <defs + id="defs19"> + <linearGradient + id="f" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#54d883" + offset="0" + id="stop2" /> + <stop + stop-color="#abf9c7" + offset="1" + id="stop4" /> + </linearGradient> + <linearGradient + id="a" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#292c2f" + offset="0" + id="stop7" /> + <stop + stop-opacity="0" + offset="1" + id="stop9" /> + </linearGradient> + <linearGradient + id="g" + x1="9" + x2="38" + y1="9" + y2="38" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="h" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#197cf1" + offset="0" + id="stop13" /> + <stop + stop-color="#20bcfa" + offset="1" + id="stop15" /> + </linearGradient> + <linearGradient + id="e" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="a-3" + y1="392.35999" + y2="365.20001" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(365.57,152.44)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-6" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".2" + id="stop4-7" /> + </linearGradient> + </defs> + <g + id="g952"> + <g + id="g956"> + <image + y="-1.2207031e-05" + width="12.7" + height="12.7" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,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 " + id="image21" + x="0" /> + </g> + </g> + <g + transform="matrix(0.20445069,0,0,0.20788681,-71.974939,-101.29379)" + id="g27"> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g15"> + <path + fill="#da2c2c" + d="m 389.57,517.8 v 28 h 22 v -28 h -14 z" + id="path9" /> + <rect + opacity="0.25" + x="389.57001" + y="544.79999" + width="22" + height="1" + id="rect11" /> + <rect + opacity="0.5" + x="389.57001" + y="517.79999" + width="22" + fill="#ffffff" + height="1" + id="rect13" /> + </g> + <rect + width="1" + x="392.57001" + y="518.79999" + fill="#ffffff" + height="26" + fill-opacity="0.252" + id="rect17" /> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g25"> + <rect + x="393.57001" + y="518.79999" + fill-opacity="0.083" + width="1" + height="26" + id="rect19" /> + <path + opacity="0.8" + fill="#ffffff" + d="m 398.43,525.8 c -0.162,0.01 -0.336,0 -0.527,0.132 -1.705,0.734 -0.561,2.747 1.612,4.279 0.013,-0.289 0.133,-0.917 0.13,-1.194 -1.854,-1.271 -2.939,-2.509 -1.757,-2.999 1.246,-0.517 1.633,0.895 1.655,2.944 0.116,0.12 0.92,0.654 1.042,0.739 -0.045,-1.869 -1.039,-3.777 -1.993,-3.885 -0.051,-0.01 -0.107,-0.01 -0.161,-0.01 m 1.736,3.252 -0.388,-0.151 -0.322,0.217 -0.065,0.682 0.406,0.312 0.169,0.226 c 1.251,0.83 2.317,1.469 3.718,1.997 l 0.874,0.13 h 0.575 c 0.53,-0.313 0.214,-0.433 2.141,-0.673 l -2.171,0.168 c -1.675,-0.649 -3.385,-1.582 -4.94,-2.6 m 3.822,2.72 c 0.251,0 2.211,-0.17 2.99,-0.242 0.772,-0.12 1.719,-0.156 1.54,0.746 0.204,-0.481 -0.261,-1.298 -0.922,-1.442 -1.023,-0.229 -2.646,0.398 -3.229,0.591 m 0.94,0.25 c -0.304,0.12 -0.999,0.207 -1.327,0.316 0.465,0.241 2.802,1.103 3.766,0.923 0.154,0 0.805,-0.468 0.821,-0.805 -0.05,0.613 -2.906,0.156 -3.26,-0.445 m 0.635,0.011 c -0.25,-0.12 -1.422,-0.114 -1.681,-0.211 -1.468,0.513 -3.442,1.719 -4.855,2.532 l -0.409,0.12 c -0.079,0.561 -0.425,0.755 -0.74,1.118 l 0.478,0.481 0.374,-0.613 c 1.654,-0.998 4.924,-2.611 6.832,-3.386 m -6.832,3.386 c -0.202,0.12 -0.395,0.241 -0.578,0.361 -0.424,1.204 -0.919,2.01 -1.391,1.98 0.729,0.349 2.301,-1.37 2.83,-2.986 m -2.83,2.986 c -1.113,-0.661 1.243,-1.867 1.77,-2.217 0.119,-0.337 -0.206,-0.895 -0.1,-1.292 -1.661,1.034 -2.643,2.531 -2.086,3.24 0.134,0.168 0.276,0.253 0.417,0.265 m 1.617,-2.52 c 0.077,-0.31 1.103,-1.015 1.289,-1.123 0.248,-1.954 0.26,-2.147 0.471,-3.666 -0.126,-0.12 -0.902,-0.725 -1.022,-0.809 -0.149,1.624 -0.429,3.824 -0.826,5.293" + id="path21" /> + <path + fill="url(#a)" + d="m 389.57,517.8 v 28 h 22 v -28 h -17 z" + id="path23" + style="fill:url(#a-3)" /> + </g> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/gotoFirst.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoFirst.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-2.3000001,-2.75)"> + <path + d="M 3,19 H 4 V 3 H 3 Z m 6.293,-8 8,8 L 18,18.293 10.707,11 18,3.707 17.293,3 Z" + id="path3" /> + <path + d="m 4.293,11 8,8 L 13,18.293 5.707,11 13,3.707 12.293,3 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/gotoJump.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="gotoJump.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 4.383789,1 3.5,1.883789 11.15625,9.540039 12.616211,11 11.15625,12.459961 3.5,20.116211 4.383789,21 12.040039,13.34375 14.383789,11 12.040039,8.65625 Z M 17.25,9.75 C 16.5575,9.75 16,10.3075 16,11 c 0,0.6925 0.5575,1.25 1.25,1.25 0.6925,0 1.25,-0.5575 1.25,-1.25 0,-0.6925 -0.5575,-1.25 -1.25,-1.25 z" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/gotoLast.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoLast.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-3.5666668,-2.75)"> + <path + d="m 19,3 h -1 v 16 h 1 z m -6.293,8 -8,-8 L 4,3.707 11.293,11 4,18.293 4.707,19 Z" + id="path3" /> + <path + d="M 17.707,11 9.707,3 9,3.707 16.293,11 9,18.293 9.707,19 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/pdfviewer.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="22" + version="1.1" + height="22" + id="svg6" + sodipodi:docname="pdfviewer.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="layer1" /> + <defs + id="defs3871" /> + <metadata + id="metadata3874" /> + <g + inkscape:label="Capa 1" + inkscape:groupmode="layer" + id="layer1" + transform="matrix(1 0 0 1 -326 -534.3622)"> + <path + inkscape:label="Capa 1" + inkscape:connector-curvature="0" + style="fill:#cf000f;stroke-width:1.66883" + id="path26" + d="m 330.10511,535.3622 c -0.27018,0.0167 -0.56038,0 -0.87894,0.22043 -2.84194,1.22285 -0.93576,4.57763 2.68673,7.13137 0.0217,-0.48109 0.22193,-1.52877 0.21693,-1.98983 -3.09046,-2.11874 -4.89836,-4.18137 -2.92868,-4.99911 2.07642,-0.86199 2.72089,1.49137 2.75758,4.90693 0.19346,0.2004 1.53367,1.0906 1.73713,1.23105 -0.075,-3.115 -1.73115,-6.29445 -3.32223,-6.47479 -0.085,-0.0167 -0.17847,-0.0167 -0.26853,-0.0167 m 2.8932,5.41942 -0.64665,-0.25182 -0.53614,0.36119 -0.108,1.13669 0.6772,0.51933 0.28105,0.37706 c 2.08477,1.38316 3.86088,2.44854 6.19747,3.32857 l 1.4572,0.21643 h 0.95898 c 0.88393,-0.52118 0.35692,-0.72173 3.56912,-1.12251 l -3.61916,0.28055 c -2.79191,-1.08242 -5.64098,-2.63691 -8.23276,-4.33284 m 6.37082,4.53405 c 0.41863,0 3.68487,-0.28337 4.98408,-0.40361 1.28589,-0.20055 2.8653,-0.26066 2.56676,1.24273 0.34024,-0.80172 -0.43501,-2.16282 -1.53744,-2.40329 -1.7045,-0.3809 -4.4093,0.66361 -5.38163,0.9844 m 1.56707,0.41663 c -0.50701,0.20054 -1.66531,0.3455 -2.21236,0.52584 0.77553,0.40095 4.66929,1.83871 6.27705,1.53796 0.25685,0 1.34235,-0.78083 1.36904,-1.34224 -0.0834,1.02229 -4.84331,0.26066 -5.43373,-0.7416 m 1.05906,0.0184 c -0.41694,-0.2004 -2.36933,-0.19054 -2.8013,-0.35086 -2.44604,0.85565 -5.7362,2.86453 -8.09116,4.21946 l -0.68213,0.20056 c -0.13154,0.93579 -0.70815,1.25808 -1.23418,1.86408 l 0.79721,0.80188 0.62377,-1.0223 c 2.75689,-1.66371 8.20729,-4.35171 11.38612,-5.6427 m -11.38612,5.6427 c -0.3369,0.20039 -0.65879,0.40093 -0.964,0.60132 -0.70715,2.00652 -1.53105,3.35746 -2.31825,3.29935 1.21582,0.58129 3.8346,-2.28323 4.71687,-4.97725 m -4.71687,4.97725 c -1.85462,-1.10246 2.07107,-3.1115 2.95001,-3.69478 0.19847,-0.56125 -0.34407,-1.49121 -0.1673,-2.15281 -2.76856,1.72398 -4.4055,4.21794 -3.47653,5.40056 0.22349,0.2807 0.46032,0.42097 0.69548,0.44102 m 2.69438,-4.19993 c 0.12892,-0.51665 1.8391,-1.69175 2.14764,-1.87226 0.41376,-3.25709 0.43347,-3.57872 0.78492,-6.11075 -0.21015,-0.20039 -1.50359,-1.20867 -1.70373,-1.34892 -0.24836,2.70602 -0.71546,6.37308 -1.37591,8.82246" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/sidebarExpandLeft.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="sidebarExpandLeft.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.25,0,0,1.25,-2.75,-2.75)"> + <path + d="M 3,3 V 19 H 19 V 3 Z M 8,4 H 18 V 18 H 8 Z" + stroke-linecap="square" + stroke-linejoin="round" + id="path3" /> + <path + d="M 11.353516,6.6464844 15.707031,11 11.353516,15.353516 10.646484,14.646484 14.292969,11 10.646484,7.3535156 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/zoomFitPage.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="zoomFitPage.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#eff0f1; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 1,1 V 2.25 6 H 2.25 V 2.25 H 6 V 1 H 2.25 Z M 11,1 8.5,3.5 h 5 z m 5,0 v 1.25 h 3.75 V 6 H 21 V 2.25 1 H 19.75 Z M 4.75,4.75 v 12.5 h 12.5 V 4.75 Z M 6,6 H 16 V 16 H 6 Z M 3.5,8.5 1,11 3.5,13.5 Z m 15,0 v 5 L 21,11 Z M 1,16 V 19.75 21 H 6 V 19.75 H 2.25 V 16 Z m 18.75,0 v 3.75 H 16 V 21 h 5 V 19.75 16 Z M 8.5,18.5 11,21 13.5,18.5 Z" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-dark/zoomFitWidth.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + viewBox="0 0 22 22" + id="svg7" + sodipodi:docname="zoomFitWidth.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview9" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg7" /> + <defs + id="defs3"> + <style + id="current-color-scheme" + type="text/css">.ColorScheme-Text { + color:#eff0f1; + }</style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + class="ColorScheme-Text" + d="M 1,1 V 6 H 2.25 V 2.25 H 6 V 1 H 2.25 Z m 15,0 v 1.25 h 3.75 V 6 H 21 V 1 H 19.75 Z M 4.75,4.75 v 12.5 h 12.5 V 4.75 Z M 6,6 H 16 V 16 H 6 Z M 3.5,8.5 1,11 3.5,13.5 Z m 15,0 v 5 L 21,11 Z M 1,16 v 5 H 6 V 19.75 H 2.25 V 16 Z m 18.75,0 v 3.75 H 16 V 21 h 5 v -5 z" + id="path5" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/documentProperties.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="documentProperties.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 1,1 V 21 H 2.25 21 V 19.75 1 H 19.75 2.25 1 m 1.25,5 h 17.5 V 19.75 H 2.25 V 6 M 3.5,7.25 V 18.5 H 9.75 V 7.25 H 3.5 M 11,8.5 v 1.25 h 7.5 V 8.5 H 11 m 0,3.75 v 1.25 h 5 V 12.25 H 11 M 11,16 v 1.25 h 2.5 V 16 H 11" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/ericPdf.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,285 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="22" + height="22" + version="1.1" + viewBox="0 0 5.8208 5.8208" + id="svg37" + sodipodi:docname="ericPdf.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview39" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg37" /> + <defs + id="defs19"> + <linearGradient + id="f" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#54d883" + offset="0" + id="stop2" /> + <stop + stop-color="#abf9c7" + offset="1" + id="stop4" /> + </linearGradient> + <linearGradient + id="a" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#292c2f" + offset="0" + id="stop7" /> + <stop + stop-opacity="0" + offset="1" + id="stop9" /> + </linearGradient> + <linearGradient + id="g" + x1="9" + x2="38" + y1="9" + y2="38" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="h" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#197cf1" + offset="0" + id="stop13" /> + <stop + stop-color="#20bcfa" + offset="1" + id="stop15" /> + </linearGradient> + <linearGradient + id="e" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientTransform="matrix(0.066146,0,0,0.066142,2.4253,293.6)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="a-3" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.07298809,0,0,0.07298809,0.59308977,-23.158892)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-6" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop4-7" /> + </linearGradient> + <linearGradient + id="linearGradient860" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-14-332.36)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop856" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop858" /> + </linearGradient> + <linearGradient + id="a-5" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-14,-332.36)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-3" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop4-5" /> + </linearGradient> + <linearGradient + id="linearGradient916" + y1="392.36" + y2="336.36" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(-14-332.36)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop912" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".15" + id="stop914" /> + </linearGradient> + <linearGradient + id="a-6" + y1="392.35999" + y2="365.20001" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(365.57,152.44)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-2" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".2" + id="stop4-9" /> + </linearGradient> + </defs> + <g + id="g918"> + <path + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + id="use26" + color-interpolation="sRGB" + color="#000000" + d="M 10,3 V 61 H 54 V 3 H 40 Z" + style="fill:url(#a-5)" /> + </g> + <g + id="g921" /> + <g + id="g952" + transform="translate(-0.26458001,-0.26000243)"> + <image + x="0.26458001" + y="0.26000243" + width="5.2916999" + height="5.2916999" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,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 " + id="image21" /> + </g> + <g + transform="matrix(0.09621154,0,0,0.09449347,-34.041566,-46.018318)" + id="g27"> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g15"> + <path + fill="#da2c2c" + d="m 389.57,517.8 v 28 h 22 v -28 h -14 z" + id="path9" /> + <rect + opacity="0.25" + x="389.57001" + y="544.79999" + width="22" + height="1" + id="rect11" /> + <rect + opacity="0.5" + x="389.57001" + y="517.79999" + width="22" + fill="#ffffff" + height="1" + id="rect13" /> + </g> + <rect + width="1" + x="392.57001" + y="518.79999" + fill="#ffffff" + height="26" + fill-opacity="0.252" + id="rect17" /> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g25"> + <rect + x="393.57001" + y="518.79999" + fill-opacity="0.083" + width="1" + height="26" + id="rect19" /> + <path + opacity="0.8" + fill="#ffffff" + d="m 398.43,525.8 c -0.162,0.01 -0.336,0 -0.527,0.132 -1.705,0.734 -0.561,2.747 1.612,4.279 0.013,-0.289 0.133,-0.917 0.13,-1.194 -1.854,-1.271 -2.939,-2.509 -1.757,-2.999 1.246,-0.517 1.633,0.895 1.655,2.944 0.116,0.12 0.92,0.654 1.042,0.739 -0.045,-1.869 -1.039,-3.777 -1.993,-3.885 -0.051,-0.01 -0.107,-0.01 -0.161,-0.01 m 1.736,3.252 -0.388,-0.151 -0.322,0.217 -0.065,0.682 0.406,0.312 0.169,0.226 c 1.251,0.83 2.317,1.469 3.718,1.997 l 0.874,0.13 h 0.575 c 0.53,-0.313 0.214,-0.433 2.141,-0.673 l -2.171,0.168 c -1.675,-0.649 -3.385,-1.582 -4.94,-2.6 m 3.822,2.72 c 0.251,0 2.211,-0.17 2.99,-0.242 0.772,-0.12 1.719,-0.156 1.54,0.746 0.204,-0.481 -0.261,-1.298 -0.922,-1.442 -1.023,-0.229 -2.646,0.398 -3.229,0.591 m 0.94,0.25 c -0.304,0.12 -0.999,0.207 -1.327,0.316 0.465,0.241 2.802,1.103 3.766,0.923 0.154,0 0.805,-0.468 0.821,-0.805 -0.05,0.613 -2.906,0.156 -3.26,-0.445 m 0.635,0.011 c -0.25,-0.12 -1.422,-0.114 -1.681,-0.211 -1.468,0.513 -3.442,1.719 -4.855,2.532 l -0.409,0.12 c -0.079,0.561 -0.425,0.755 -0.74,1.118 l 0.478,0.481 0.374,-0.613 c 1.654,-0.998 4.924,-2.611 6.832,-3.386 m -6.832,3.386 c -0.202,0.12 -0.395,0.241 -0.578,0.361 -0.424,1.204 -0.919,2.01 -1.391,1.98 0.729,0.349 2.301,-1.37 2.83,-2.986 m -2.83,2.986 c -1.113,-0.661 1.243,-1.867 1.77,-2.217 0.119,-0.337 -0.206,-0.895 -0.1,-1.292 -1.661,1.034 -2.643,2.531 -2.086,3.24 0.134,0.168 0.276,0.253 0.417,0.265 m 1.617,-2.52 c 0.077,-0.31 1.103,-1.015 1.289,-1.123 0.248,-1.954 0.26,-2.147 0.471,-3.666 -0.126,-0.12 -0.902,-0.725 -1.022,-0.809 -0.149,1.624 -0.429,3.824 -0.826,5.293" + id="path21" /> + <path + fill="url(#a)" + d="m 389.57,517.8 v 28 h 22 v -28 h -17 z" + id="path23" + style="fill:url(#a-6)" /> + </g> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/ericPdf48.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,203 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + width="48" + height="48" + version="1.1" + viewBox="0 0 12.7 12.7" + id="svg37" + sodipodi:docname="ericPdf48.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview39" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="13.416667" + inkscape:cx="23.999999" + inkscape:cy="23.999999" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg37" /> + <defs + id="defs19"> + <linearGradient + id="f" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#54d883" + offset="0" + id="stop2" /> + <stop + stop-color="#abf9c7" + offset="1" + id="stop4" /> + </linearGradient> + <linearGradient + id="a" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#292c2f" + offset="0" + id="stop7" /> + <stop + stop-opacity="0" + offset="1" + id="stop9" /> + </linearGradient> + <linearGradient + id="g" + x1="9" + x2="38" + y1="9" + y2="38" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="h" + x1="4" + x2="4" + y1="44" + y2="4" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse"> + <stop + stop-color="#197cf1" + offset="0" + id="stop13" /> + <stop + stop-color="#20bcfa" + offset="1" + id="stop15" /> + </linearGradient> + <linearGradient + id="e" + x1="34" + x2="44" + y1="19.008" + y2="29.008" + gradientTransform="matrix(0.15875,0,0,0.15874,5.1858,289.49)" + gradientUnits="userSpaceOnUse" + xlink:href="#a" /> + <linearGradient + id="a-3" + y1="392.35999" + y2="365.20001" + x2="0" + gradientUnits="userSpaceOnUse" + gradientTransform="translate(365.57,152.44)"> + <stop + stop-color="#ffffff" + stop-opacity="0" + id="stop2-6" /> + <stop + offset="1" + stop-color="#ffffff" + stop-opacity=".2" + id="stop4-7" /> + </linearGradient> + </defs> + <g + id="g952"> + <g + id="g956"> + <image + y="-1.2207031e-05" + width="12.7" + height="12.7" + preserveAspectRatio="none" + xlink:href="data:image/png;base64,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 " + id="image21" + x="0" /> + </g> + </g> + <g + transform="matrix(0.20445069,0,0,0.20788681,-71.974939,-101.29379)" + id="g27"> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g15"> + <path + fill="#da2c2c" + d="m 389.57,517.8 v 28 h 22 v -28 h -14 z" + id="path9" /> + <rect + opacity="0.25" + x="389.57001" + y="544.79999" + width="22" + height="1" + id="rect11" /> + <rect + opacity="0.5" + x="389.57001" + y="517.79999" + width="22" + fill="#ffffff" + height="1" + id="rect13" /> + </g> + <rect + width="1" + x="392.57001" + y="518.79999" + fill="#ffffff" + height="26" + fill-opacity="0.252" + id="rect17" /> + <g + color-rendering="auto" + color-interpolation-filters="linearRGB" + shape-rendering="auto" + image-rendering="auto" + text-rendering="auto" + color-interpolation="sRGB" + color="#000000" + id="g25"> + <rect + x="393.57001" + y="518.79999" + fill-opacity="0.083" + width="1" + height="26" + id="rect19" /> + <path + opacity="0.8" + fill="#ffffff" + d="m 398.43,525.8 c -0.162,0.01 -0.336,0 -0.527,0.132 -1.705,0.734 -0.561,2.747 1.612,4.279 0.013,-0.289 0.133,-0.917 0.13,-1.194 -1.854,-1.271 -2.939,-2.509 -1.757,-2.999 1.246,-0.517 1.633,0.895 1.655,2.944 0.116,0.12 0.92,0.654 1.042,0.739 -0.045,-1.869 -1.039,-3.777 -1.993,-3.885 -0.051,-0.01 -0.107,-0.01 -0.161,-0.01 m 1.736,3.252 -0.388,-0.151 -0.322,0.217 -0.065,0.682 0.406,0.312 0.169,0.226 c 1.251,0.83 2.317,1.469 3.718,1.997 l 0.874,0.13 h 0.575 c 0.53,-0.313 0.214,-0.433 2.141,-0.673 l -2.171,0.168 c -1.675,-0.649 -3.385,-1.582 -4.94,-2.6 m 3.822,2.72 c 0.251,0 2.211,-0.17 2.99,-0.242 0.772,-0.12 1.719,-0.156 1.54,0.746 0.204,-0.481 -0.261,-1.298 -0.922,-1.442 -1.023,-0.229 -2.646,0.398 -3.229,0.591 m 0.94,0.25 c -0.304,0.12 -0.999,0.207 -1.327,0.316 0.465,0.241 2.802,1.103 3.766,0.923 0.154,0 0.805,-0.468 0.821,-0.805 -0.05,0.613 -2.906,0.156 -3.26,-0.445 m 0.635,0.011 c -0.25,-0.12 -1.422,-0.114 -1.681,-0.211 -1.468,0.513 -3.442,1.719 -4.855,2.532 l -0.409,0.12 c -0.079,0.561 -0.425,0.755 -0.74,1.118 l 0.478,0.481 0.374,-0.613 c 1.654,-0.998 4.924,-2.611 6.832,-3.386 m -6.832,3.386 c -0.202,0.12 -0.395,0.241 -0.578,0.361 -0.424,1.204 -0.919,2.01 -1.391,1.98 0.729,0.349 2.301,-1.37 2.83,-2.986 m -2.83,2.986 c -1.113,-0.661 1.243,-1.867 1.77,-2.217 0.119,-0.337 -0.206,-0.895 -0.1,-1.292 -1.661,1.034 -2.643,2.531 -2.086,3.24 0.134,0.168 0.276,0.253 0.417,0.265 m 1.617,-2.52 c 0.077,-0.31 1.103,-1.015 1.289,-1.123 0.248,-1.954 0.26,-2.147 0.471,-3.666 -0.126,-0.12 -0.902,-0.725 -1.022,-0.809 -0.149,1.624 -0.429,3.824 -0.826,5.293" + id="path21" /> + <path + fill="url(#a)" + d="m 389.57,517.8 v 28 h 22 v -28 h -17 z" + id="path23" + style="fill:url(#a-3)" /> + </g> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/gotoFirst.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoFirst.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-2.3000001,-2.75)"> + <path + d="M 3,19 H 4 V 3 H 3 Z m 6.293,-8 8,8 L 18,18.293 10.707,11 18,3.707 17.293,3 Z" + id="path3" /> + <path + d="m 4.293,11 8,8 L 13,18.293 5.707,11 13,3.707 12.293,3 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/gotoJump.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="gotoJump.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 4.383789,1 3.5,1.883789 11.15625,9.540039 12.616211,11 11.15625,12.459961 3.5,20.116211 4.383789,21 12.040039,13.34375 14.383789,11 12.040039,8.65625 Z M 17.25,9.75 C 16.5575,9.75 16,10.3075 16,11 c 0,0.6925 0.5575,1.25 1.25,1.25 0.6925,0 1.25,-0.5575 1.25,-1.25 0,-0.6925 -0.5575,-1.25 -1.25,-1.25 z" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/gotoLast.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="gotoLast.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.2666667,0,0,1.25,-3.5666667,-2.75)"> + <path + d="m 19,3 h -1 v 16 h 1 z m -6.293,8 -8,-8 L 4,3.707 11.293,11 4,18.293 4.707,19 Z" + id="path3" /> + <path + d="M 17.707,11 9.707,3 9,3.707 16.293,11 9,18.293 9.707,19 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/pdfviewer.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + width="22" + version="1.1" + height="22" + id="svg6" + sodipodi:docname="pdfviewer.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="layer1" /> + <defs + id="defs3871" /> + <metadata + id="metadata3874" /> + <g + inkscape:label="Capa 1" + inkscape:groupmode="layer" + id="layer1" + transform="matrix(1 0 0 1 -326 -534.3622)"> + <path + inkscape:label="Capa 1" + inkscape:connector-curvature="0" + style="fill:#cf000f;stroke-width:1.66883" + id="path26" + d="m 330.10511,535.3622 c -0.27018,0.0167 -0.56038,0 -0.87894,0.22043 -2.84194,1.22285 -0.93576,4.57763 2.68673,7.13137 0.0217,-0.48109 0.22193,-1.52877 0.21693,-1.98983 -3.09046,-2.11874 -4.89836,-4.18137 -2.92868,-4.99911 2.07642,-0.86199 2.72089,1.49137 2.75758,4.90693 0.19346,0.2004 1.53367,1.0906 1.73713,1.23105 -0.075,-3.115 -1.73115,-6.29445 -3.32223,-6.47479 -0.085,-0.0167 -0.17847,-0.0167 -0.26853,-0.0167 m 2.8932,5.41942 -0.64665,-0.25182 -0.53614,0.36119 -0.108,1.13669 0.6772,0.51933 0.28105,0.37706 c 2.08477,1.38316 3.86088,2.44854 6.19747,3.32857 l 1.4572,0.21643 h 0.95898 c 0.88393,-0.52118 0.35692,-0.72173 3.56912,-1.12251 l -3.61916,0.28055 c -2.79191,-1.08242 -5.64098,-2.63691 -8.23276,-4.33284 m 6.37082,4.53405 c 0.41863,0 3.68487,-0.28337 4.98408,-0.40361 1.28589,-0.20055 2.8653,-0.26066 2.56676,1.24273 0.34024,-0.80172 -0.43501,-2.16282 -1.53744,-2.40329 -1.7045,-0.3809 -4.4093,0.66361 -5.38163,0.9844 m 1.56707,0.41663 c -0.50701,0.20054 -1.66531,0.3455 -2.21236,0.52584 0.77553,0.40095 4.66929,1.83871 6.27705,1.53796 0.25685,0 1.34235,-0.78083 1.36904,-1.34224 -0.0834,1.02229 -4.84331,0.26066 -5.43373,-0.7416 m 1.05906,0.0184 c -0.41694,-0.2004 -2.36933,-0.19054 -2.8013,-0.35086 -2.44604,0.85565 -5.7362,2.86453 -8.09116,4.21946 l -0.68213,0.20056 c -0.13154,0.93579 -0.70815,1.25808 -1.23418,1.86408 l 0.79721,0.80188 0.62377,-1.0223 c 2.75689,-1.66371 8.20729,-4.35171 11.38612,-5.6427 m -11.38612,5.6427 c -0.3369,0.20039 -0.65879,0.40093 -0.964,0.60132 -0.70715,2.00652 -1.53105,3.35746 -2.31825,3.29935 1.21582,0.58129 3.8346,-2.28323 4.71687,-4.97725 m -4.71687,4.97725 c -1.85462,-1.10246 2.07107,-3.1115 2.95001,-3.69478 0.19847,-0.56125 -0.34407,-1.49121 -0.1673,-2.15281 -2.76856,1.72398 -4.4055,4.21794 -3.47653,5.40056 0.22349,0.2807 0.46032,0.42097 0.69548,0.44102 m 2.69438,-4.19993 c 0.12892,-0.51665 1.8391,-1.69175 2.14764,-1.87226 0.41376,-3.25709 0.43347,-3.57872 0.78492,-6.11075 -0.21015,-0.20039 -1.50359,-1.20867 -1.70373,-1.34892 -0.24836,2.70602 -0.71546,6.37308 -1.37591,8.82246" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/sidebarExpandLeft.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg9" + sodipodi:docname="sidebarExpandLeft.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <defs + id="defs13" /> + <sodipodi:namedview + id="namedview11" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg9" /> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + <g + class="ColorScheme-Text" + fill="currentColor" + id="g7" + transform="matrix(1.25,0,0,1.25,-2.75,-2.75)"> + <path + d="M 3,3 V 19 H 19 V 3 Z M 8,4 H 18 V 18 H 8 Z" + stroke-linecap="square" + stroke-linejoin="round" + id="path3" /> + <path + d="M 11.353516,6.6464844 15.707031,11 11.353516,15.353516 10.646484,14.646484 14.292969,11 10.646484,7.3535156 Z" + id="path5" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/zoomFitPage.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + viewBox="0 0 22 22" + version="1.1" + id="svg6" + sodipodi:docname="zoomFitPage.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview8" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg6" /> + <defs + id="defs3051"> + <style + type="text/css" + id="current-color-scheme"> + .ColorScheme-Text { + color:#232629; + } + </style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + d="M 1,1 V 2.25 6 H 2.25 V 2.25 H 6 V 1 H 2.25 Z M 11,1 8.5,3.5 h 5 z m 5,0 v 1.25 h 3.75 V 6 H 21 V 2.25 1 H 19.75 Z M 4.75,4.75 v 12.5 h 12.5 V 4.75 Z M 6,6 H 16 V 16 H 6 Z M 3.5,8.5 1,11 3.5,13.5 Z m 15,0 v 5 L 21,11 Z M 1,16 V 19.75 21 H 6 V 19.75 H 2.25 V 16 Z m 18.75,0 v 3.75 H 16 V 21 h 5 V 19.75 16 Z M 8.5,18.5 11,21 13.5,18.5 Z" + class="ColorScheme-Text" + id="path4" /> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/eric7/icons/breeze-light/zoomFitWidth.svg Tue Jan 24 10:52:27 2023 +0100 @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + viewBox="0 0 22 22" + id="svg7" + sodipodi:docname="zoomFitWidth.svg" + inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg"> + <sodipodi:namedview + id="namedview9" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + inkscape:pagecheckerboard="0" + showgrid="false" + inkscape:zoom="29.272727" + inkscape:cx="11" + inkscape:cy="11" + inkscape:window-width="2580" + inkscape:window-height="1080" + inkscape:window-x="426" + inkscape:window-y="146" + inkscape:window-maximized="0" + inkscape:current-layer="svg7" /> + <defs + id="defs3"> + <style + id="current-color-scheme" + type="text/css">.ColorScheme-Text { + color:#232629; + }</style> + </defs> + <path + style="fill:currentColor;fill-opacity:1;stroke:none;stroke-width:1.25" + class="ColorScheme-Text" + d="M 1,1 V 6 H 2.25 V 2.25 H 6 V 1 H 2.25 Z m 15,0 v 1.25 h 3.75 V 6 H 21 V 1 H 19.75 Z M 4.75,4.75 v 12.5 h 12.5 V 4.75 Z M 6,6 H 16 V 16 H 6 Z M 3.5,8.5 1,11 3.5,13.5 Z m 15,0 v 5 L 21,11 Z M 1,16 v 5 H 6 V 19.75 H 2.25 V 16 Z m 18.75,0 v 3.75 H 16 V 21 h 5 v -5 z" + id="path5" /> +</svg>