2020-07-28 14:33:28 +00:00
|
|
|
# This software is licensed under the "GPLv3" License as described in the "LICENSE" file,
|
|
|
|
# which should be included with this package. The terms are also available at
|
|
|
|
# http://www.gnu.org/licenses/gpl-3.0.html
|
|
|
|
|
2020-09-01 21:02:58 +00:00
|
|
|
import re
|
2020-07-28 14:33:28 +00:00
|
|
|
from PyQt5.QtCore import Qt, pyqtSlot
|
|
|
|
from PyQt5.QtWidgets import (
|
2021-08-15 09:10:18 +00:00
|
|
|
QPushButton,
|
|
|
|
QLineEdit,
|
|
|
|
QVBoxLayout,
|
|
|
|
QGridLayout,
|
|
|
|
QDialog,
|
|
|
|
QTableView,
|
|
|
|
QAbstractItemView,
|
|
|
|
QSpacerItem,
|
|
|
|
QSizePolicy,
|
|
|
|
QHeaderView,
|
2020-07-28 14:33:28 +00:00
|
|
|
)
|
2022-05-09 06:40:08 +00:00
|
|
|
from qt.exclude_list_table import ExcludeListTable
|
2020-07-28 14:33:28 +00:00
|
|
|
|
2020-08-17 02:13:20 +00:00
|
|
|
from core.exclude import AlreadyThereException
|
2020-07-28 14:33:28 +00:00
|
|
|
from hscommon.trans import trget
|
2021-08-15 09:10:18 +00:00
|
|
|
|
2020-07-28 14:33:28 +00:00
|
|
|
tr = trget("ui")
|
|
|
|
|
|
|
|
|
|
|
|
class ExcludeListDialog(QDialog):
|
|
|
|
def __init__(self, app, parent, model, **kwargs):
|
|
|
|
flags = Qt.CustomizeWindowHint | Qt.WindowTitleHint | Qt.WindowSystemMenuHint
|
|
|
|
super().__init__(parent, flags, **kwargs)
|
2020-08-17 02:13:20 +00:00
|
|
|
self.app = app
|
2020-07-28 14:33:28 +00:00
|
|
|
self.specific_actions = frozenset()
|
|
|
|
self._setupUI()
|
|
|
|
self.model = model # ExcludeListDialogCore
|
|
|
|
self.model.view = self
|
2020-08-17 02:13:20 +00:00
|
|
|
self.table = ExcludeListTable(app, view=self.tableView) # Qt ExcludeListTable
|
2020-09-01 21:02:58 +00:00
|
|
|
self._row_matched = False # test if at least one row matched our test string
|
|
|
|
self._input_styled = False
|
2020-07-28 14:33:28 +00:00
|
|
|
|
2020-08-17 02:13:20 +00:00
|
|
|
self.buttonAdd.clicked.connect(self.addStringFromLineEdit)
|
|
|
|
self.buttonRemove.clicked.connect(self.removeSelected)
|
2020-07-28 14:33:28 +00:00
|
|
|
self.buttonRestore.clicked.connect(self.restoreDefaults)
|
|
|
|
self.buttonClose.clicked.connect(self.accept)
|
2020-08-17 02:13:20 +00:00
|
|
|
self.buttonHelp.clicked.connect(self.display_help_message)
|
2020-09-01 21:02:58 +00:00
|
|
|
self.buttonTestString.clicked.connect(self.onTestStringButtonClicked)
|
|
|
|
self.inputLine.textEdited.connect(self.reset_input_style)
|
|
|
|
self.testLine.textEdited.connect(self.reset_input_style)
|
|
|
|
self.testLine.textEdited.connect(self.reset_table_style)
|
2020-07-28 14:33:28 +00:00
|
|
|
|
|
|
|
def _setupUI(self):
|
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
gridlayout = QGridLayout()
|
|
|
|
self.buttonAdd = QPushButton(tr("Add"))
|
|
|
|
self.buttonRemove = QPushButton(tr("Remove Selected"))
|
|
|
|
self.buttonRestore = QPushButton(tr("Restore defaults"))
|
2020-09-01 21:02:58 +00:00
|
|
|
self.buttonTestString = QPushButton(tr("Test string"))
|
2020-07-28 14:33:28 +00:00
|
|
|
self.buttonClose = QPushButton(tr("Close"))
|
2020-08-17 02:13:20 +00:00
|
|
|
self.buttonHelp = QPushButton(tr("Help"))
|
2020-09-01 21:02:58 +00:00
|
|
|
self.inputLine = QLineEdit()
|
|
|
|
self.testLine = QLineEdit()
|
|
|
|
self.tableView = QTableView()
|
2020-07-28 14:33:28 +00:00
|
|
|
triggers = (
|
2021-08-15 09:10:18 +00:00
|
|
|
QAbstractItemView.DoubleClicked | QAbstractItemView.EditKeyPressed | QAbstractItemView.SelectedClicked
|
2020-07-28 14:33:28 +00:00
|
|
|
)
|
|
|
|
self.tableView.setEditTriggers(triggers)
|
|
|
|
self.tableView.setSelectionMode(QTableView.ExtendedSelection)
|
|
|
|
self.tableView.setSelectionBehavior(QTableView.SelectRows)
|
2020-08-17 02:13:20 +00:00
|
|
|
self.tableView.setShowGrid(False)
|
|
|
|
vheader = self.tableView.verticalHeader()
|
|
|
|
vheader.setSectionsMovable(True)
|
|
|
|
vheader.setVisible(False)
|
2020-07-28 14:33:28 +00:00
|
|
|
hheader = self.tableView.horizontalHeader()
|
|
|
|
hheader.setSectionsMovable(False)
|
|
|
|
hheader.setSectionResizeMode(QHeaderView.Fixed)
|
|
|
|
hheader.setStretchLastSection(True)
|
|
|
|
hheader.setHighlightSections(False)
|
2020-08-17 02:13:20 +00:00
|
|
|
hheader.setVisible(True)
|
2020-09-01 21:02:58 +00:00
|
|
|
gridlayout.addWidget(self.inputLine, 0, 0)
|
2020-07-28 14:33:28 +00:00
|
|
|
gridlayout.addWidget(self.buttonAdd, 0, 1, Qt.AlignLeft)
|
|
|
|
gridlayout.addWidget(self.buttonRemove, 1, 1, Qt.AlignLeft)
|
|
|
|
gridlayout.addWidget(self.buttonRestore, 2, 1, Qt.AlignLeft)
|
2020-08-17 02:13:20 +00:00
|
|
|
gridlayout.addWidget(self.buttonHelp, 3, 1, Qt.AlignLeft)
|
2020-09-01 21:02:58 +00:00
|
|
|
gridlayout.addWidget(self.buttonClose, 4, 1)
|
|
|
|
gridlayout.addWidget(self.tableView, 1, 0, 6, 1)
|
2020-08-17 02:13:20 +00:00
|
|
|
gridlayout.addItem(QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.Expanding), 4, 1)
|
2020-09-01 21:02:58 +00:00
|
|
|
gridlayout.addWidget(self.buttonTestString, 6, 1)
|
|
|
|
gridlayout.addWidget(self.testLine, 6, 0)
|
|
|
|
|
2020-07-28 14:33:28 +00:00
|
|
|
layout.addLayout(gridlayout)
|
2020-12-29 17:52:22 +00:00
|
|
|
self.inputLine.setPlaceholderText(tr("Type a python regular expression here..."))
|
2020-09-01 21:02:58 +00:00
|
|
|
self.inputLine.setFocus()
|
2020-12-29 17:52:22 +00:00
|
|
|
self.testLine.setPlaceholderText(tr("Type a file system path or filename here..."))
|
2020-09-01 21:02:58 +00:00
|
|
|
self.testLine.setClearButtonEnabled(True)
|
2020-07-28 14:33:28 +00:00
|
|
|
|
|
|
|
# --- model --> view
|
|
|
|
def show(self):
|
|
|
|
super().show()
|
2020-09-01 21:02:58 +00:00
|
|
|
self.inputLine.setFocus()
|
2020-07-28 14:33:28 +00:00
|
|
|
|
|
|
|
@pyqtSlot()
|
2020-08-17 02:13:20 +00:00
|
|
|
def addStringFromLineEdit(self):
|
2020-09-01 21:02:58 +00:00
|
|
|
text = self.inputLine.text()
|
2020-07-28 14:33:28 +00:00
|
|
|
if not text:
|
|
|
|
return
|
2020-08-17 02:13:20 +00:00
|
|
|
try:
|
|
|
|
self.model.add(text)
|
|
|
|
except AlreadyThereException:
|
|
|
|
self.app.show_message("Expression already in the list.")
|
|
|
|
return
|
|
|
|
except Exception as e:
|
|
|
|
self.app.show_message(f"Expression is invalid: {e}")
|
|
|
|
return
|
2020-09-01 21:02:58 +00:00
|
|
|
self.inputLine.clear()
|
2020-07-28 14:33:28 +00:00
|
|
|
|
2020-08-17 02:13:20 +00:00
|
|
|
def removeSelected(self):
|
2020-07-28 14:33:28 +00:00
|
|
|
self.model.remove_selected()
|
|
|
|
|
|
|
|
def restoreDefaults(self):
|
|
|
|
self.model.restore_defaults()
|
2020-08-17 02:13:20 +00:00
|
|
|
|
2020-09-01 21:02:58 +00:00
|
|
|
def onTestStringButtonClicked(self):
|
|
|
|
input_text = self.testLine.text()
|
|
|
|
if not input_text:
|
|
|
|
self.reset_input_style()
|
|
|
|
return
|
2021-06-18 23:52:31 +00:00
|
|
|
# If at least one row matched, we know whether table is highlighted or not
|
2020-09-01 21:02:58 +00:00
|
|
|
self._row_matched = self.model.test_string(input_text)
|
2020-12-30 22:18:42 +00:00
|
|
|
self.table.refresh()
|
2020-09-01 21:02:58 +00:00
|
|
|
|
2021-06-18 23:52:31 +00:00
|
|
|
# Test the string currently in the input text box as well
|
2020-09-01 21:02:58 +00:00
|
|
|
input_regex = self.inputLine.text()
|
|
|
|
if not input_regex:
|
|
|
|
self.reset_input_style()
|
|
|
|
return
|
2021-06-18 23:52:31 +00:00
|
|
|
compiled = None
|
2020-09-01 21:02:58 +00:00
|
|
|
try:
|
|
|
|
compiled = re.compile(input_regex)
|
|
|
|
except re.error:
|
|
|
|
self.reset_input_style()
|
|
|
|
return
|
2021-06-18 23:52:31 +00:00
|
|
|
if self.model.is_match(input_text, compiled):
|
2020-12-29 15:41:34 +00:00
|
|
|
self.inputLine.setStyleSheet("background-color: rgb(10, 200, 10);")
|
2021-06-18 23:52:31 +00:00
|
|
|
self._input_styled = True
|
2020-09-01 21:02:58 +00:00
|
|
|
else:
|
|
|
|
self.reset_input_style()
|
|
|
|
|
|
|
|
def reset_input_style(self):
|
|
|
|
"""Reset regex input line background"""
|
|
|
|
if self._input_styled:
|
|
|
|
self.inputLine.setStyleSheet(self.styleSheet())
|
2021-06-18 23:52:31 +00:00
|
|
|
self._input_styled = False
|
2020-09-01 21:02:58 +00:00
|
|
|
|
|
|
|
def reset_table_style(self):
|
|
|
|
if self._row_matched:
|
|
|
|
self._row_matched = False
|
|
|
|
self.model.reset_rows_highlight()
|
2020-12-30 22:18:42 +00:00
|
|
|
self.table.refresh()
|
2020-09-01 21:02:58 +00:00
|
|
|
|
2020-08-17 02:13:20 +00:00
|
|
|
def display_help_message(self):
|
2021-08-15 09:10:18 +00:00
|
|
|
self.app.show_message(
|
|
|
|
tr(
|
|
|
|
"""\
|
2020-09-01 21:02:58 +00:00
|
|
|
These (case sensitive) python regular expressions will filter out files during scans.<br>\
|
|
|
|
Directores will also have their <strong>default state</strong> set to Excluded \
|
2021-01-29 16:54:54 +00:00
|
|
|
in the Directories tab if their name happens to match one of the selected regular expressions.<br>\
|
2021-01-29 17:56:29 +00:00
|
|
|
For each file collected, two tests are performed to determine whether or not to completely ignore it:<br>\
|
2020-09-01 21:02:58 +00:00
|
|
|
<li>1. Regular expressions with no path separator in them will be compared to the file name only.</li>
|
2023-01-10 04:35:12 +00:00
|
|
|
<li>2. Regular expressions with at least one path separator in them will be compared to the full path to the file.</li>\
|
|
|
|
<br>Example: if you want to filter out .PNG files from the "My Pictures" directory only:<br>\
|
2020-09-01 21:02:58 +00:00
|
|
|
<code>.*My\\sPictures\\\\.*\\.png</code><br><br>\
|
2021-01-29 16:54:54 +00:00
|
|
|
You can test the regular expression with the "test string" button after pasting a fake path in the test field:<br>\
|
2020-09-01 21:02:58 +00:00
|
|
|
<code>C:\\\\User\\My Pictures\\test.png</code><br><br>
|
2020-12-29 17:28:30 +00:00
|
|
|
Matching regular expressions will be highlighted.<br>\
|
2021-01-29 16:54:54 +00:00
|
|
|
If there is at least one highlight, the path or filename tested will be ignored during scans.<br><br>\
|
2021-08-15 09:10:18 +00:00
|
|
|
Directories and files starting with a period '.' are filtered out by default.<br><br>"""
|
|
|
|
)
|
|
|
|
)
|