3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include <wsutil/filter_files.h>
13 #include <wsutil/filesystem.h>
15 #include "filter_dialog.h"
16 #include <ui_filter_dialog.h>
18 #include <QMessageBox>
21 #include <QSortFilterProxyModel>
23 #include <ui/qt/utils/qt_ui_utils.h>
24 #include <ui/qt/widgets/capture_filter_edit.h>
25 #include <ui/qt/widgets/display_filter_edit.h>
26 #include "main_application.h"
28 FilterDialog::FilterDialog(QWidget
*parent
, FilterType filter_type
, QString new_filter_
) :
29 GeometryStateDialog(parent
),
30 ui(new Ui::FilterDialog
),
31 filter_type_(filter_type
),
32 filter_tree_delegate_(new FilterTreeDelegate(this, filter_type
))
36 if (parent
) loadGeometry(parent
->width() * 2 / 3, parent
->height() * 2 / 3);
37 setWindowIcon(mainApp
->normalIcon());
39 ui
->newToolButton
->setStockIcon("list-add");
40 ui
->deleteToolButton
->setStockIcon("list-remove");
41 ui
->copyToolButton
->setStockIcon("list-copy");
44 ui
->newToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
45 ui
->deleteToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
46 ui
->copyToolButton
->setAttribute(Qt::WA_MacSmallSize
, true);
47 ui
->pathLabel
->setAttribute(Qt::WA_MacSmallSize
, true);
51 ui
->filterTreeWidget
->setDragEnabled(true);
52 ui
->filterTreeWidget
->viewport()->setAcceptDrops(true);
53 ui
->filterTreeWidget
->setDropIndicatorShown(true);
54 ui
->filterTreeWidget
->setDragDropMode(QAbstractItemView::InternalMove
);
56 ui
->filterTreeView
->setDragEnabled(true);
57 ui
->filterTreeView
->setAcceptDrops(true);
58 ui
->filterTreeView
->setDropIndicatorShown(true);
60 const char * filename
= NULL
;
61 QString newFilterText
;
62 switch (filter_type
) {
64 setWindowTitle(mainApp
->windowTitleString(tr("Capture Filters")));
65 filename
= CFILTER_FILE_NAME
;
66 newFilterText
= tr("New capture filter");
67 model_
= new FilterListModel(FilterListModel::Capture
, this);
70 setWindowTitle(mainApp
->windowTitleString(tr("Display Filters")));
71 filename
= DFILTER_FILE_NAME
;
72 newFilterText
= tr("New display filter");
73 model_
= new FilterListModel(FilterListModel::Display
, this);
76 setWindowTitle(mainApp
->windowTitleString(tr("Display Filter Macros")));
77 filename
= DMACROS_FILE_NAME
;
78 newFilterText
= tr("New macro");
79 model_
= new FilterListModel(FilterListModel::DisplayMacro
, this);
82 ws_assert_not_reached();
85 ui
->filterTreeView
->setModel(model_
);
87 ui
->filterTreeView
->setItemDelegate(new FilterTreeDelegate(this, filter_type
));
89 if (new_filter_
.length() > 0)
90 addFilter(newFilterText
, new_filter_
, true);
92 ui
->filterTreeView
->resizeColumnToContents(FilterListModel::ColumnName
);
94 connect(ui
->filterTreeView
->selectionModel(), &QItemSelectionModel::selectionChanged
, this, &FilterDialog::selectionChanged
);
96 QString abs_path
= gchar_free_to_qstring(get_persconffile_path(filename
, true));
97 if (file_exists(abs_path
.toUtf8().constData())) {
98 ui
->pathLabel
->setText(abs_path
);
99 ui
->pathLabel
->setUrl(QUrl::fromLocalFile(abs_path
).toString());
100 ui
->pathLabel
->setToolTip(tr("Open ") + filename
);
101 ui
->pathLabel
->setEnabled(true);
105 FilterDialog::~FilterDialog()
110 void FilterDialog::addFilter(QString name
, QString filter
, bool start_editing
)
114 QModelIndex idx
= model_
->addFilter(name
, filter
);
115 ui
->filterTreeView
->scrollTo(idx
);
117 ui
->filterTreeView
->edit(idx
);
119 ui
->filterTreeView
->selectionModel()->select(idx
,QItemSelectionModel::ClearAndSelect
|QItemSelectionModel::Rows
);
123 void FilterDialog::updateWidgets()
125 if (! ui
->filterTreeView
->selectionModel())
128 qsizetype num_selected
= ui
->filterTreeView
->selectionModel()->selectedRows().count();
130 ui
->copyToolButton
->setEnabled(num_selected
== 1);
131 ui
->deleteToolButton
->setEnabled(num_selected
> 0);
134 void FilterDialog::selectionChanged(const QItemSelection
&/*selected*/, const QItemSelection
&/*deselected*/)
139 void FilterDialog::on_newToolButton_clicked()
144 switch (filter_type_
) {
146 //: This text is automatically filled in when a new filter is created
147 name
= tr("New capture filter");
148 filter
= "ip host host.example.com";
151 //: This text is automatically filled in when a new filter is created
152 name
= tr("New display filter");
153 filter
= "ip.host == host.example.com";
156 //: This text is automatically filled in when a new filter is created
157 name
= "eq_example_com";
158 filter
= "$1 == host.example.com";
161 ws_assert_not_reached();
164 addFilter(name
, filter
, true);
167 void FilterDialog::on_deleteToolButton_clicked()
169 QModelIndexList selected
= ui
->filterTreeView
->selectionModel()->selectedRows();
171 foreach (QModelIndex idx
, selected
)
173 if (idx
.isValid() && ! rows
.contains(idx
.row()))
176 model_
->removeFilter(idx
);
181 void FilterDialog::on_copyToolButton_clicked()
183 QModelIndexList selected
= ui
->filterTreeView
->selectionModel()->selectedRows();
184 if (selected
.count() <= 0)
187 int rowNr
= selected
.at(0).row();
188 QModelIndex row
= selected
.at(0).sibling(rowNr
, FilterListModel::ColumnName
);
190 addFilter(row
.data().toString(), row
.sibling(rowNr
, FilterListModel::ColumnExpression
).data().toString(), true);
193 void FilterDialog::on_buttonBox_accepted()
197 switch (filter_type_
) {
199 mainApp
->emitAppSignal(MainApplication::CaptureFilterListChanged
);
202 mainApp
->emitAppSignal(MainApplication::DisplayFilterListChanged
);
205 mainApp
->reloadDisplayFilterMacros();
206 // The function above emits MainApplication::FieldsChanged, which
207 // takes care of invalidating the current display filter text if
208 // it no longer compiles.
209 // XXX - What if the current display filter means something
210 // different now? Should we force a refilter (not redissection,
211 // the dissection shouldn't have changed) with the current display
212 // filter, or wait for the user to refilter?
213 // The UAT based display macro system did not refilter.
216 ws_assert_not_reached();
220 void FilterDialog::on_buttonBox_helpRequested()
222 switch (filter_type_
) {
224 mainApp
->helpTopicAction(HELP_CAPTURE_FILTERS_DIALOG
);
227 mainApp
->helpTopicAction(HELP_DISPLAY_FILTERS_DIALOG
);
230 mainApp
->helpTopicAction(HELP_DISPLAY_MACRO_DIALOG
);
233 ws_assert_not_reached();
238 // FilterTreeDelegate
239 // Delegate for editing capture and display filters.
242 FilterTreeDelegate::FilterTreeDelegate(QObject
*parent
, FilterDialog::FilterType filter_type
) :
243 QStyledItemDelegate(parent
),
244 filter_type_(filter_type
)
247 QWidget
*FilterTreeDelegate::createEditor(QWidget
*parent
, const QStyleOptionViewItem
&option
, const QModelIndex
&index
) const
249 QWidget
* w
= Q_NULLPTR
;
250 if (index
.column() != FilterListModel::ColumnExpression
) {
251 w
= QStyledItemDelegate::createEditor(parent
, option
, index
);
253 else if (filter_type_
== FilterDialog::CaptureFilter
) {
254 w
= new CaptureFilterEdit(parent
, true);
256 else if (filter_type_
== FilterDialog::DisplayFilter
) {
257 w
= new DisplayFilterEdit(parent
, DisplayFilterToEnter
);
260 w
= QStyledItemDelegate::createEditor(parent
, option
, index
);
263 if (qobject_cast
<QLineEdit
*>(w
)) {
264 if (index
.column() == FilterListModel::ColumnName
) {
265 if (filter_type_
== FilterDialog::DisplayMacro
) {
266 qobject_cast
<QLineEdit
*>(w
)->setValidator(new MacroNameValidator());
269 qobject_cast
<QLineEdit
*>(w
)->setValidator(new FilterValidator());
277 void FilterTreeDelegate::setEditorData(QWidget
*editor
, const QModelIndex
&index
) const
279 if (! editor
|| ! index
.isValid())
282 QStyledItemDelegate::setEditorData(editor
, index
);
284 if (qobject_cast
<QLineEdit
*>(editor
))
285 qobject_cast
<QLineEdit
*>(editor
)->setText(index
.data().toString());
288 QValidator::State
FilterValidator::validate(QString
& input
, int & /*pos*/) const
290 /* Making this a list to be able to easily add additional values in the future */
291 QStringList invalidKeys
= QStringList() << "\"";
293 if (input
.length() <= 0)
294 return QValidator::Intermediate
;
296 foreach (QString key
, invalidKeys
)
297 if (input
.indexOf(key
) >= 0)
298 return QValidator::Invalid
;
300 return QValidator::Acceptable
;
303 QValidator::State
MacroNameValidator::validate(QString
&input
, int & /*pos*/) const
305 if (input
.length() <= 0)
306 return QValidator::Intermediate
;
308 for (QChar ch
: input
) {
309 if (!ch
.isLetterOrNumber() && ch
!= '_') {
310 return QValidator::Invalid
;
314 return QValidator::Acceptable
;