summaryrefslogtreecommitdiffstats
path: root/ui/qt/widgets/display_filter_edit.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ui/qt/widgets/display_filter_edit.cpp846
1 files changed, 846 insertions, 0 deletions
diff --git a/ui/qt/widgets/display_filter_edit.cpp b/ui/qt/widgets/display_filter_edit.cpp
new file mode 100644
index 0000000..839b14f
--- /dev/null
+++ b/ui/qt/widgets/display_filter_edit.cpp
@@ -0,0 +1,846 @@
+/* display_filter_edit.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <epan/dfilter/dfilter.h>
+
+#include <ui/recent.h>
+
+#include <wsutil/utf8_entities.h>
+
+#include "main_application.h"
+
+#include <ui/qt/widgets/display_filter_edit.h>
+#include "filter_dialog.h"
+#include <ui/qt/widgets/stock_icon_tool_button.h>
+#include <ui/qt/widgets/syntax_line_edit.h>
+#include <ui/qt/utils/wireshark_mime_data.h>
+#include <ui/qt/utils/qt_ui_utils.h>
+#include <ui/qt/models/pref_models.h>
+#include <ui/qt/filter_action.h>
+#include <ui/qt/display_filter_expression_dialog.h>
+#include <ui/qt/main_window.h>
+
+#include <QAction>
+#include <QAbstractItemView>
+#include <QComboBox>
+#include <QCompleter>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPainter>
+#include <QStringListModel>
+#include <QWidget>
+#include <QObject>
+#include <QDrag>
+#include <QDropEvent>
+#include <QMimeData>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+// To do:
+// - Get rid of shortcuts and replace them with "n most recently applied filters"?
+// - We need simplified (button- and dropdown-free) versions for use in dialogs and field-only checking.
+// - Add a separator or otherwise distinguish between recent items and fields
+// in the completion dropdown.
+
+#ifdef __APPLE__
+#define DEFAULT_MODIFIER UTF8_PLACE_OF_INTEREST_SIGN
+#else
+#define DEFAULT_MODIFIER "Ctrl-"
+#endif
+
+// proto.c:fld_abbrev_chars
+static const QString fld_abbrev_chars_ = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
+
+DisplayFilterEdit::DisplayFilterEdit(QWidget *parent, DisplayFilterEditType type) :
+ SyntaxLineEdit(parent),
+ type_(type),
+ save_action_(NULL),
+ remove_action_(NULL),
+ actions_(Q_NULLPTR),
+ bookmark_button_(NULL),
+ clear_button_(NULL),
+ apply_button_(NULL),
+ leftAlignActions_(false),
+ last_applied_(QString()),
+ filter_word_preamble_(QString()),
+ autocomplete_accepts_field_(true)
+{
+ setAccessibleName(tr("Display filter entry"));
+
+ completion_model_ = new QStringListModel(this);
+ setCompleter(new QCompleter(completion_model_, this));
+ setCompletionTokenChars(fld_abbrev_chars_);
+
+ QString buttonStyle = QString(
+ "QToolButton {"
+ " border: none;"
+ " background: transparent;" // Disables platform style on Windows.
+ " padding: 0 0 0 0;"
+ "}"
+ "QToolButton::menu-indicator {"
+ " image: none;"
+ "}"
+ );
+
+ leftAlignActions_ = recent.gui_geometry_leftalign_actions;
+
+ if (type_ == DisplayFilterToApply) {
+ bookmark_button_ = new StockIconToolButton(this, "x-display-filter-bookmark");
+ bookmark_button_->setMenu(new QMenu(bookmark_button_));
+ bookmark_button_->setPopupMode(QToolButton::InstantPopup);
+ bookmark_button_->setToolTip(tr("Manage saved bookmarks."));
+ bookmark_button_->setIconSize(QSize(14, 14));
+ bookmark_button_->setStyleSheet(buttonStyle);
+ bookmark_button_->setVisible(false);
+
+ clear_button_ = new StockIconToolButton(this, "x-filter-clear");
+ clear_button_->setToolTip(tr("Clear display filter"));
+ clear_button_->setIconSize(QSize(14, 14));
+ clear_button_->setStyleSheet(buttonStyle);
+ clear_button_->setVisible(false);
+
+ apply_button_ = new StockIconToolButton(this, "x-filter-apply");
+ apply_button_->setEnabled(false);
+ apply_button_->setToolTip(tr("Apply display filter"));
+ apply_button_->setIconSize(QSize(24, 14));
+ apply_button_->setStyleSheet(buttonStyle);
+ apply_button_->setVisible(false);
+
+ connect(clear_button_, &StockIconToolButton::clicked, this, &DisplayFilterEdit::clearFilter);
+ connect(apply_button_, &StockIconToolButton::clicked, this, &DisplayFilterEdit::applyDisplayFilter);
+ connect(this, &DisplayFilterEdit::returnPressed, this, &DisplayFilterEdit::applyDisplayFilter);
+ }
+
+ connect(this, &DisplayFilterEdit::textChanged, this,
+ static_cast<void (DisplayFilterEdit::*)(const QString &)>(&DisplayFilterEdit::checkFilter));
+
+ connect(mainApp, &MainApplication::appInitialized, this, &DisplayFilterEdit::updateBookmarkMenu);
+ connect(mainApp, &MainApplication::displayFilterListChanged, this, &DisplayFilterEdit::updateBookmarkMenu);
+ connect(mainApp, &MainApplication::preferencesChanged, this, [=](){ checkFilter(); });
+
+ connect(mainApp, &MainApplication::appInitialized, this, &DisplayFilterEdit::connectToMainWindow);
+}
+
+void DisplayFilterEdit::connectToMainWindow()
+{
+ connect(this, &DisplayFilterEdit::filterPackets, qobject_cast<MainWindow *>(mainApp->mainWindow()),
+ &MainWindow::filterPackets);
+ connect(this, &DisplayFilterEdit::showPreferencesDialog,
+ qobject_cast<MainWindow *>(mainApp->mainWindow()), &MainWindow::showPreferencesDialog);
+ connect(qobject_cast<MainWindow *>(mainApp->mainWindow()), &MainWindow::displayFilterSuccess,
+ this, &DisplayFilterEdit::displayFilterSuccess);
+}
+
+void DisplayFilterEdit::contextMenuEvent(QContextMenuEvent *event) {
+ QMenu *menu = this->createStandardContextMenu();
+ menu->setAttribute(Qt::WA_DeleteOnClose);
+
+ if (menu->actions().count() <= 0) {
+ menu->deleteLater();
+ return;
+ }
+
+ QAction *first = menu->actions().at(0);
+
+ QAction *na = new QAction(tr("Display Filter Expression…"), this);
+ connect(na, &QAction::triggered, this, &DisplayFilterEdit::displayFilterExpression);
+ menu->insertAction(first, na);
+ menu->insertSeparator(first);
+
+ if (type_ == DisplayFilterToApply) {
+ na = new QAction(tr("Left align buttons"), this);
+ na->setCheckable(true);
+ na->setChecked(leftAlignActions_);
+ connect(na, &QAction::triggered, this, &DisplayFilterEdit::triggerAlignementAction);
+ menu->addSeparator();
+ menu->addAction(na);
+ }
+
+ menu->popup(event->globalPos());
+}
+
+void DisplayFilterEdit::triggerAlignementAction()
+{
+ leftAlignActions_ = ! leftAlignActions_;
+ if (qobject_cast<QAction *>(sender()))
+ qobject_cast<QAction *>(sender())->setChecked(leftAlignActions_);
+
+ recent.gui_geometry_leftalign_actions = leftAlignActions_;
+ write_recent();
+
+ alignActionButtons();
+}
+
+void DisplayFilterEdit::alignActionButtons()
+{
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize bksz, cbsz, apsz;
+ bksz = apsz = cbsz = QSize(0, 0);
+
+ if (type_ == DisplayFilterToApply) {
+ bookmark_button_->setMinimumHeight(contentsRect().height());
+ bookmark_button_->setMaximumHeight(contentsRect().height());
+ bksz = bookmark_button_->sizeHint();
+
+ apsz = apply_button_->sizeHint();
+ apply_button_->setMinimumHeight(contentsRect().height());
+ apply_button_->setMaximumHeight(contentsRect().height());
+
+ if (clear_button_->isVisible())
+ {
+ cbsz = clear_button_->sizeHint();
+ clear_button_->setMinimumHeight(contentsRect().height());
+ clear_button_->setMaximumHeight(contentsRect().height());
+ }
+ }
+
+ int leftPadding = frameWidth + 1;
+ int leftMargin = bksz.width();
+ int rightMargin = cbsz.width() + apsz.width() + frameWidth + 2;
+ if (leftAlignActions_)
+ {
+ leftMargin = rightMargin + bksz.width() - 2;
+ rightMargin = 0;
+ }
+
+ SyntaxLineEdit::setStyleSheet(style_sheet_ + QString(
+ "SyntaxLineEdit {"
+ " padding-left: %1px;"
+ " margin-left: %2px;"
+ " margin-right: %3px;"
+ "}"
+ )
+ .arg(leftPadding)
+ .arg(leftMargin)
+ .arg(rightMargin)
+ );
+
+ if (apply_button_) {
+ if (! leftAlignActions_)
+ {
+ apply_button_->move(contentsRect().right() - frameWidth - apsz.width(),
+ contentsRect().top());
+ } else {
+ apply_button_->move(contentsRect().left() + bookmark_button_->width(), contentsRect().top());
+ }
+ }
+
+ if (clear_button_ && apply_button_) {
+ if (! leftAlignActions_)
+ {
+ clear_button_->move(contentsRect().right() - frameWidth - cbsz.width() - apsz.width(),
+ contentsRect().top());
+ } else {
+ int width = bookmark_button_->width() + apply_button_->width();
+ clear_button_->move(contentsRect().left() + width, contentsRect().top());
+ }
+ }
+
+ update();
+}
+
+void DisplayFilterEdit::setDefaultPlaceholderText()
+{
+ switch (type_) {
+
+ case DisplayFilterToApply:
+ placeholder_text_ = QString(tr("Apply a display filter %1 <%2/>")).arg(UTF8_HORIZONTAL_ELLIPSIS)
+ .arg(DEFAULT_MODIFIER);
+ break;
+
+ case DisplayFilterToEnter:
+ placeholder_text_ = QString(tr("Enter a display filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS);
+ break;
+
+ case ReadFilterToApply:
+ placeholder_text_ = QString(tr("Apply a read filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS);
+ break;
+ }
+ setPlaceholderText(placeholder_text_);
+}
+
+void DisplayFilterEdit::paintEvent(QPaintEvent *evt) {
+ SyntaxLineEdit::paintEvent(evt);
+
+ if (bookmark_button_ && isEnabled()) {
+
+ if (! bookmark_button_->isVisible())
+ {
+ bookmark_button_->setVisible(true);
+ apply_button_->setVisible(true);
+ setDefaultPlaceholderText();
+ alignActionButtons();
+ return;
+ }
+
+ // Draw the borders by hand. We could try to do this in the
+ // style sheet but it's a pain.
+#ifdef Q_OS_MAC
+ QColor divider_color = Qt::gray;
+#else
+ QColor divider_color = palette().shadow().color();
+#endif
+ QPainter painter(this);
+ painter.setPen(divider_color);
+ QRect cr = contentsRect();
+ int left_xpos = 0;
+ int right_xpos = 0;
+
+ if (leftAlignActions_)
+ {
+ left_xpos = 1 + bookmark_button_->width();
+ if (clear_button_->isVisible())
+ left_xpos += clear_button_->width();
+ if (apply_button_->isVisible())
+ left_xpos += apply_button_->width();
+ right_xpos = cr.width() - 1;
+ }
+ else
+ {
+ left_xpos = bookmark_button_->width();
+ right_xpos = cr.width() - 4;
+ if (clear_button_->isVisible())
+ right_xpos -= clear_button_->width();
+ if (apply_button_->isVisible())
+ right_xpos -= apply_button_->width();
+ }
+
+ painter.drawLine(left_xpos, cr.top(), left_xpos, cr.bottom() + 1);
+ if (!text().isEmpty())
+ painter.drawLine(right_xpos, cr.top(), right_xpos, cr.bottom() + 1);
+ }
+}
+
+void DisplayFilterEdit::resizeEvent(QResizeEvent *)
+{
+ alignActionButtons();
+}
+
+void DisplayFilterEdit::focusOutEvent(QFocusEvent *event)
+{
+ if (syntaxState() == Valid) {
+ emit popFilterSyntaxStatus();
+ setToolTip(QString());
+ }
+ SyntaxLineEdit::focusOutEvent(event);
+}
+
+bool DisplayFilterEdit::checkFilter()
+{
+ checkFilter(text());
+
+ return syntaxState() != Invalid;
+}
+
+void DisplayFilterEdit::checkFilter(const QString& filter_text)
+{
+ if (text().length() == 0 && actions_ && actions_->checkedAction())
+ actions_->checkedAction()->setChecked(false);
+
+ if (clear_button_) {
+
+ if (filter_text.length() > 0)
+ clear_button_->setVisible(true);
+ else if (last_applied_.length() > 0)
+ setPlaceholderText(tr("Current filter: %1").arg(last_applied_));
+ else if (filter_text.length() <= 0 && last_applied_.length() <= 0)
+ clear_button_->setVisible(false);
+
+ alignActionButtons();
+ }
+
+ if ((filter_text.length() <= 0) && mainApp->mainWindow()->isActiveWindow())
+ mainApp->popStatus(MainApplication::FilterSyntax);
+
+ emit popFilterSyntaxStatus();
+ if (!checkDisplayFilter(filter_text))
+ return;
+
+ switch (syntaxState()) {
+ case Deprecated:
+ {
+ if (mainApp->mainWindow()->isActiveWindow())
+ mainApp->pushStatus(MainApplication::FilterSyntax, syntaxErrorMessage());
+ setToolTip(syntaxErrorMessage());
+ break;
+ }
+ case Invalid:
+ {
+ QString invalidMsg = tr("Invalid filter: ").append(syntaxErrorMessage());
+ if (mainApp->mainWindow()->isActiveWindow())
+ mainApp->pushStatus(MainApplication::FilterSyntax, syntaxErrorMessage());
+ setToolTip(invalidMsg);
+ break;
+ }
+ default:
+ setToolTip(QString());
+ break;
+ }
+
+ if (bookmark_button_) {
+
+ bookmark_button_->setStockIcon("x-display-filter-bookmark");
+ if (remove_action_ && save_action_)
+ {
+ remove_action_->setEnabled(false);
+ save_action_->setEnabled(false);
+ }
+
+ if (filter_text.length() > 0)
+ {
+ bool enable_save_action = false;
+ bool match = false;
+
+ FilterListModel model(FilterListModel::Display);
+ QModelIndex idx = model.findByExpression(filter_text);
+ if (idx.isValid()) {
+ match = true;
+
+ bookmark_button_->setStockIcon("x-filter-matching-bookmark");
+ if (remove_action_) {
+ remove_action_->setData(text());
+ remove_action_->setEnabled(true);
+ }
+ } else {
+ bookmark_button_->setStockIcon("x-display-filter-bookmark");
+ if (remove_action_) {
+ remove_action_->setEnabled(false);
+ }
+ }
+
+ if (!match && (syntaxState() == Valid || syntaxState() == Deprecated) && !filter_text.isEmpty()) {
+ enable_save_action = true;
+ }
+ if (save_action_) {
+ save_action_->setEnabled(enable_save_action);
+ }
+ }
+
+ apply_button_->setEnabled(syntaxState() != Invalid);
+ }
+}
+
+void DisplayFilterEdit::updateBookmarkMenu()
+{
+ if (!bookmark_button_)
+ return;
+
+ QMenu *bb_menu = bookmark_button_->menu();
+ bb_menu->clear();
+
+ save_action_ = bb_menu->addAction(tr("Save this filter"));
+ connect(save_action_, &QAction::triggered, this, &DisplayFilterEdit::saveFilter);
+ remove_action_ = bb_menu->addAction(tr("Remove this filter"));
+ connect(remove_action_, &QAction::triggered, this, &DisplayFilterEdit::removeFilter);
+ QAction *manage_action = bb_menu->addAction(tr("Manage Display Filters"));
+ connect(manage_action, &QAction::triggered, this, &DisplayFilterEdit::showFilters);
+ QAction *expr_action = bb_menu->addAction(tr("Filter Button Preferences..."));
+ connect(expr_action, &QAction::triggered, this, &DisplayFilterEdit::showExpressionPrefs);
+ bb_menu->addSeparator();
+
+ FilterListModel model(FilterListModel::Display);
+ QModelIndex idx = model.findByExpression(text());
+
+ int one_em = bb_menu->fontMetrics().height();
+
+ if (! actions_)
+ actions_ = new QActionGroup(this);
+
+ for (int row = 0; row < model.rowCount(); row++)
+ {
+ QModelIndex nameIdx = model.index(row, FilterListModel::ColumnName);
+ QString name = nameIdx.data().toString();
+ QString expr = model.index(row, FilterListModel::ColumnExpression).data().toString();
+ QString prep_text = QString("%1: %2").arg(name).arg(expr);
+
+ prep_text = bb_menu->fontMetrics().elidedText(prep_text, Qt::ElideRight, one_em * 40);
+ QAction * prep_action = bb_menu->addAction(prep_text);
+ prep_action->setCheckable(true);
+ if (nameIdx == idx)
+ prep_action->setChecked(true);
+
+ prep_action->setProperty("display_filter", expr);
+ actions_->addAction(prep_action);
+
+ connect(prep_action, &QAction::triggered, this, &DisplayFilterEdit::applyOrPrepareFilter);
+ }
+
+ checkFilter();
+}
+
+// GTK+ behavior:
+// - Operates on words (proto.c:fld_abbrev_chars).
+// - Popup appears when you enter or remove text.
+
+// Our behavior:
+// - Operates on words (fld_abbrev_chars_).
+// - Popup appears when you enter or remove text.
+// - Popup appears when you move the cursor.
+// - Popup does not appear when text is selected.
+// - Recent and saved display filters in popup when editing first word.
+
+// ui/gtk/filter_autocomplete.c:build_autocompletion_list
+void DisplayFilterEdit::buildCompletionList(const QString &field_word, const QString &preamble)
+{
+ // Push a hint about the current field.
+ if (syntaxState() == Valid) {
+ if (mainApp->mainWindow()->isActiveWindow())
+ mainApp->popStatus(MainApplication::FilterSyntax);
+
+ header_field_info *hfinfo = proto_registrar_get_byname(field_word.toUtf8().constData());
+ if (hfinfo) {
+ QString cursor_field_msg = QString("%1: %2")
+ .arg(hfinfo->name)
+ .arg(ftype_pretty_name(hfinfo->type));
+ if (mainApp->mainWindow()->isActiveWindow())
+ mainApp->pushStatus(MainApplication::FilterSyntax, cursor_field_msg);
+ }
+ }
+
+ if (field_word.length() < 1) {
+ completion_model_->setStringList(QStringList());
+ return;
+ }
+
+ // Grab matching display filters from our parent combo and from the
+ // saved display filters file. Skip ones that look like single fields
+ // and assume they will be added below.
+ QStringList complex_list;
+ QComboBox *df_combo = qobject_cast<QComboBox *>(parent());
+ if (df_combo) {
+ for (int i = 0; i < df_combo->count() ; i++) {
+ QString recent_filter = df_combo->itemText(i);
+
+ if (isComplexFilter(recent_filter)) {
+ complex_list << recent_filter;
+ }
+ }
+ }
+ FilterListModel model(FilterListModel::Display);
+ for (int row = 0; row < model.rowCount(); row++)
+ {
+ QString saved_filter = model.index(row, FilterListModel::ColumnExpression).data().toString();
+
+ if (isComplexFilter(saved_filter) && !complex_list.contains(saved_filter)) {
+ complex_list << saved_filter;
+ }
+ }
+
+ completion_model_->setStringList(complex_list);
+ completer()->setCompletionPrefix(field_word);
+
+ // Only add fields to completion if a field is valid at this position.
+ // Try to compile preamble and check error message.
+ if (preamble != filter_word_preamble_) {
+ df_error_t *df_err = NULL;
+ dfilter_t *test_df = NULL;
+ if (preamble.size() > 0) {
+ dfilter_compile_full(qUtf8Printable(preamble), &test_df, &df_err,
+ DF_EXPAND_MACROS, __func__);
+ }
+ if (test_df == NULL || (df_err != NULL && df_err->code == DF_ERROR_UNEXPECTED_END)) {
+ // Unexpected end of expression means "expected identifier (field) or literal".
+ autocomplete_accepts_field_ = true;
+ }
+ else {
+ autocomplete_accepts_field_ = false;
+ }
+ dfilter_free(test_df);
+ df_error_free(&df_err);
+ filter_word_preamble_ = preamble;
+ }
+
+ QStringList field_list;
+ if (autocomplete_accepts_field_) {
+ void *proto_cookie;
+
+ int field_dots = static_cast<int>(field_word.count('.')); // Some protocol names (_ws.expert) contain periods.
+ for (int proto_id = proto_get_first_protocol(&proto_cookie); proto_id != -1; proto_id = proto_get_next_protocol(&proto_cookie)) {
+ protocol_t *protocol = find_protocol_by_id(proto_id);
+ if (!proto_is_protocol_enabled(protocol)) continue;
+
+ const QString pfname = proto_get_protocol_filter_name(proto_id);
+ field_list << pfname;
+
+ // Add fields only if we're past the protocol name and only for the
+ // current protocol.
+ if (field_dots > pfname.count('.')) {
+ void *field_cookie;
+ const QByteArray fw_ba = field_word.toUtf8(); // or toLatin1 or toStdString?
+ const char *fw_utf8 = fw_ba.constData();
+ gsize fw_len = (gsize) strlen(fw_utf8);
+ for (header_field_info *hfinfo = proto_get_first_protocol_field(proto_id, &field_cookie); hfinfo; hfinfo = proto_get_next_protocol_field(proto_id, &field_cookie)) {
+ if (hfinfo->same_name_prev_id != -1) continue; // Ignore duplicate names.
+
+ if (!g_ascii_strncasecmp(fw_utf8, hfinfo->abbrev, fw_len)) {
+ if ((gsize) strlen(hfinfo->abbrev) != fw_len) field_list << hfinfo->abbrev;
+ }
+ }
+ }
+ }
+ field_list.sort();
+ }
+
+ completion_model_->setStringList(complex_list + field_list);
+ completer()->setCompletionPrefix(field_word);
+}
+
+void DisplayFilterEdit::setStyleSheet(const QString &style_sheet)
+{
+ style_sheet_ = style_sheet;
+ SyntaxLineEdit::setStyleSheet(style_sheet_);
+}
+
+void DisplayFilterEdit::clearFilter()
+{
+ clear();
+
+ last_applied_ = QString();
+ updateClearButton();
+
+ emit filterPackets(QString(), true);
+}
+
+void DisplayFilterEdit::applyDisplayFilter()
+{
+ if (completer()->popup()->currentIndex().isValid()) {
+ // If the popup (not the QCompleter itself) has a currently
+ // valid QModelIndex, that means that the popup's
+ // QAbstractItemView::activated() signal has not yet
+ // been handled, which means that text() has the old value,
+ // not the one from the completer.
+ return;
+ }
+
+ if (syntaxState() == Invalid)
+ return;
+
+ if (text().length() > 0)
+ last_applied_ = text();
+
+ updateClearButton();
+
+ emit filterPackets(text(), true);
+}
+
+void DisplayFilterEdit::updateClearButton()
+{
+ setDefaultPlaceholderText();
+ clear_button_->setVisible(!text().isEmpty());
+ alignActionButtons();
+}
+
+void DisplayFilterEdit::displayFilterSuccess(bool success)
+{
+ if (apply_button_)
+ apply_button_->setEnabled(!success);
+}
+
+void DisplayFilterEdit::changeEvent(QEvent* event)
+{
+ if (0 != event)
+ {
+ switch (event->type())
+ {
+ case QEvent::LanguageChange:
+ setDefaultPlaceholderText();
+ break;
+ default:
+ break;
+ }
+ }
+ SyntaxLineEdit::changeEvent(event);
+}
+
+void DisplayFilterEdit::saveFilter()
+{
+ FilterDialog *display_filter_dlg = new FilterDialog(window(), FilterDialog::DisplayFilter, text());
+ display_filter_dlg->setWindowModality(Qt::ApplicationModal);
+ display_filter_dlg->setAttribute(Qt::WA_DeleteOnClose);
+ display_filter_dlg->show();
+}
+
+void DisplayFilterEdit::removeFilter()
+{
+ if (! actions_ || ! actions_->checkedAction())
+ return;
+
+ QAction *ra = actions_->checkedAction();
+ if (ra->property("display_filter").toString().isEmpty())
+ return;
+
+ QString remove_filter = ra->property("display_filter").toString();
+
+ FilterListModel model(FilterListModel::Display);
+ QModelIndex idx = model.findByExpression(remove_filter);
+
+ if (idx.isValid())
+ {
+ model.removeFilter(idx);
+ model.saveList();
+ }
+
+ updateBookmarkMenu();
+}
+
+void DisplayFilterEdit::showFilters()
+{
+ FilterDialog *display_filter_dlg = new FilterDialog(window(), FilterDialog::DisplayFilter);
+ display_filter_dlg->setWindowModality(Qt::ApplicationModal);
+ display_filter_dlg->setAttribute(Qt::WA_DeleteOnClose);
+ display_filter_dlg->show();
+}
+
+void DisplayFilterEdit::showExpressionPrefs()
+{
+ emit showPreferencesDialog(PrefsModel::typeToString(PrefsModel::FilterButtons));
+}
+
+void DisplayFilterEdit::applyOrPrepareFilter()
+{
+ QAction *pa = qobject_cast<QAction*>(sender());
+ if (! pa || pa->property("display_filter").toString().isEmpty())
+ return;
+
+ QString filterText = pa->property("display_filter").toString();
+ last_applied_ = filterText;
+ setText(filterText);
+
+ // Holding down the Shift key will only prepare filter.
+ if (!(QApplication::keyboardModifiers() & Qt::ShiftModifier)) {
+ applyDisplayFilter();
+ }
+}
+
+void DisplayFilterEdit::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (! event || ! event->mimeData())
+ return;
+
+ if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()) ||
+ event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) {
+ if (event->source() != this)
+ {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void DisplayFilterEdit::dragMoveEvent(QDragMoveEvent *event)
+{
+ if (! event || ! event->mimeData())
+ return;
+
+ if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()) ||
+ event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType)) {
+ if (event->source() != this)
+ {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+ } else {
+ event->acceptProposedAction();
+ }
+ } else {
+ event->ignore();
+ }
+}
+
+void DisplayFilterEdit::dropEvent(QDropEvent *event)
+{
+ if (! event || ! event->mimeData())
+ return;
+
+ QString filterText = "";
+ if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType))
+ {
+ QByteArray jsonData = event->mimeData()->data(WiresharkMimeData::DisplayFilterMimeType);
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
+ if (! jsonDoc.isObject())
+ return;
+
+ QJsonObject data = jsonDoc.object();
+
+ if ((QApplication::keyboardModifiers() & Qt::AltModifier) && data.contains("field"))
+ filterText = data["field"].toString();
+ else if (data.contains("filter"))
+ filterText = data["filter"].toString();
+ }
+ else if (qobject_cast<const ToolbarEntryMimeData *>(event->mimeData()))
+ {
+ const ToolbarEntryMimeData * data = qobject_cast<const ToolbarEntryMimeData *>(event->mimeData());
+
+ filterText = data->filter();
+ }
+
+ /* Moving items around */
+ if (filterText.length() > 0) {
+ if (event->source() != this)
+ {
+ event->setDropAction(Qt::CopyAction);
+ event->accept();
+
+ bool prepare = QApplication::keyboardModifiers() & Qt::ShiftModifier;
+
+ if (text().length() > 0 || QApplication::keyboardModifiers() & Qt::MetaModifier)
+ {
+ createFilterTextDropMenu(event, prepare, filterText);
+ return;
+ }
+
+ last_applied_ = filterText;
+ setText(filterText);
+
+ // Holding down the Shift key will only prepare filter.
+ if (! prepare) {
+ applyDisplayFilter();
+ }
+
+ } else {
+ event->acceptProposedAction();
+ }
+
+ } else {
+ event->ignore();
+ }
+}
+
+void DisplayFilterEdit::createFilterTextDropMenu(QDropEvent *event, bool prepare, QString filterText)
+{
+ if (filterText.isEmpty())
+ return;
+
+ FilterAction::Action filterAct = prepare ? FilterAction::ActionPrepare : FilterAction::ActionApply;
+ QMenu * applyMenu = FilterAction::createFilterMenu(filterAct, filterText, true, this);
+ applyMenu->setAttribute(Qt::WA_DeleteOnClose);
+
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0)
+ applyMenu->popup(this->mapToGlobal(event->position().toPoint()));
+#else
+ applyMenu->popup(this->mapToGlobal(event->pos()));
+#endif
+}
+
+void DisplayFilterEdit::displayFilterExpression()
+{
+ DisplayFilterExpressionDialog *dfe_dialog = new DisplayFilterExpressionDialog(this);
+
+ connect(dfe_dialog, &DisplayFilterExpressionDialog::insertDisplayFilter,
+ this, &DisplayFilterEdit::insertFilter);
+
+ dfe_dialog->show();
+}