diff options
Diffstat (limited to 'ui/qt/widgets/filter_expression_toolbar.cpp')
-rw-r--r-- | ui/qt/widgets/filter_expression_toolbar.cpp | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/ui/qt/widgets/filter_expression_toolbar.cpp b/ui/qt/widgets/filter_expression_toolbar.cpp new file mode 100644 index 0000000..caf91cf --- /dev/null +++ b/ui/qt/widgets/filter_expression_toolbar.cpp @@ -0,0 +1,465 @@ +/* filter_expression_toolbar.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <ui/qt/widgets/filter_expression_toolbar.h> +#include <ui/qt/utils/color_utils.h> +#include <ui/qt/utils/qt_ui_utils.h> +#include <ui/qt/utils/wireshark_mime_data.h> +#include <ui/qt/models/uat_model.h> +#include <ui/qt/filter_action.h> +#include <ui/qt/main_application.h> + +#include <epan/filter_expressions.h> +#include <ui/preference_utils.h> + +#include <QApplication> +#include <QFrame> +#include <QMenu> +#include <QEvent> +#include <QContextMenuEvent> +#include <QToolButton> +#include <QToolTip> + +static const char *dfe_property_ = "display filter expression"; //TODO : Fix Translate +static const char *dfe_property_label_ = "display_filter_expression_label"; +static const char *dfe_property_expression_ = "display_filter_expression_expr"; +static const char *dfe_property_comment_ = "display_filter_expression_comment"; +static const char *dfe_menu_ = "filter_menu"; + +#define PARENT_SEPARATOR "//" + +struct filter_expression_data +{ + FilterExpressionToolBar* toolbar; + bool actions_added; +}; + +FilterExpressionToolBar::FilterExpressionToolBar(QWidget * parent) : + DragDropToolBar(parent) +{ + updateStyleSheet(); + + setContextMenuPolicy(Qt::CustomContextMenu); + /* Give minimum space to the bar, so that drops on an empty bar will work */ + setMinimumWidth(10); + + connect (this, &QWidget::customContextMenuRequested, this, &FilterExpressionToolBar::onCustomMenuHandler); + connect(this, &DragDropToolBar::actionMoved, this, &FilterExpressionToolBar::onActionMoved); + connect(this, &DragDropToolBar::newFilterDropped, this, &FilterExpressionToolBar::onFilterDropped); + + connect(mainApp, &MainApplication::appInitialized, + this, &FilterExpressionToolBar::filterExpressionsChanged); + connect(mainApp, &MainApplication::filterExpressionsChanged, + this, &FilterExpressionToolBar::filterExpressionsChanged); + +} + +bool FilterExpressionToolBar::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + updateStyleSheet(); + break; + default: + break; + + } + return DragDropToolBar::event(event); +} + +void FilterExpressionToolBar::onCustomMenuHandler(const QPoint& pos) +{ + QAction * filterAction = actionAt(pos); + if (! filterAction) + return; + + customMenu(this, filterAction, pos); +} + +void FilterExpressionToolBar::customMenu(FilterExpressionToolBar * target, QAction * filterAction, const QPoint& pos) +{ + QMenu * filterMenu = new QMenu(target); + filterMenu->setAttribute(Qt::WA_DeleteOnClose); + + /* Only display context menu for actual filter actions */ + QString filterText = filterAction->property(dfe_property_expression_).toString().trimmed(); + + if (!filterText.isEmpty()) + { + filterMenu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionApply, filterText, true, target)); + filterMenu->addMenu(FilterAction::createFilterMenu(FilterAction::ActionPrepare, filterText, true, target)); + filterMenu->addSeparator(); + filterMenu->addAction(FilterAction::copyFilterAction(filterText, target)); + filterMenu->addSeparator(); + QAction * actEdit = filterMenu->addAction(tr("Edit")); + connect(actEdit, &QAction::triggered, target, &FilterExpressionToolBar::editFilter); + actEdit->setProperty(dfe_property_label_, filterAction->property(dfe_property_label_)); + actEdit->setProperty(dfe_property_expression_, filterAction->property(dfe_property_expression_)); + actEdit->setData(filterAction->data()); + QAction * actDisable = filterMenu->addAction(tr("Disable")); + connect(actDisable, &QAction::triggered, target, &FilterExpressionToolBar::disableFilter); + actDisable->setProperty(dfe_property_label_, filterAction->property(dfe_property_label_)); + actDisable->setProperty(dfe_property_expression_, filterAction->property(dfe_property_expression_)); + actDisable->setData(filterAction->data()); + QAction * actRemove = filterMenu->addAction(tr("Remove")); + connect(actRemove, &QAction::triggered, target, &FilterExpressionToolBar::removeFilter); + actRemove->setProperty(dfe_property_label_, filterAction->property(dfe_property_label_)); + actRemove->setProperty(dfe_property_expression_, filterAction->property(dfe_property_expression_)); + actRemove->setData(filterAction->data()); + filterMenu->addSeparator(); + } + QAction *actFilter = filterMenu->addAction(tr("Filter Button Preferences...")); + connect(actFilter, &QAction::triggered, target, &FilterExpressionToolBar::toolBarShowPreferences); + + /* Forcing the menus to get closed, no matter which action has been triggered */ + connect(filterMenu, &QMenu::triggered, this, &FilterExpressionToolBar::closeMenu); + + filterMenu->popup(mapToGlobal(pos)); +} + +void FilterExpressionToolBar::filterExpressionsChanged() +{ + struct filter_expression_data data; + + data.toolbar = this; + data.actions_added = false; + + // Hiding and showing seems to be the only way to get the layout to + // work correctly in some cases. See bug 14121 for details. + clear(); + setUpdatesEnabled(false); + hide(); + + // XXX Add a context menu for removing and changing buttons. + filter_expression_iterate_expressions(filter_expression_add_action, &data); + + show(); + setUpdatesEnabled(true); +} + +void FilterExpressionToolBar::removeFilter() +{ + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QString label = ((QAction *)sender())->property(dfe_property_label_).toString(); + QString expr = ((QAction *)sender())->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + + QModelIndex rowIndex = uatModel->index(idx, 0); + if (rowIndex.isValid()) { + uatModel->removeRow(rowIndex.row()); + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + filterExpressionsChanged(); + } +} + +WiresharkMimeData * FilterExpressionToolBar::createMimeData(QString name, int position) +{ + ToolbarEntryMimeData * element = new ToolbarEntryMimeData(name, position); + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QModelIndex rowIndex; + for (int cnt = 0; cnt < uatModel->rowCount() && ! rowIndex.isValid(); cnt++) + { + if (uatModel->data(uatModel->index(cnt, 1), Qt::DisplayRole).toString().compare(name) == 0) + { + rowIndex = uatModel->index(cnt, 2); + element->setFilter(rowIndex.data().toString()); + } + } + + return element; +} + +void FilterExpressionToolBar::onActionMoved(QAction* action, int oldPos, int newPos) +{ + gchar* err = NULL; + if (oldPos == newPos) + return; + + QString label = action->property(dfe_property_label_).toString(); + QString expr = action->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + + if (idx > -1 && oldPos > -1 && newPos > -1) + { + uat_t * table = uat_get_table_by_name("Display expressions"); + uat_move_index(table, oldPos, newPos); + uat_save(table, &err); + + g_free(err); + } +} + +void FilterExpressionToolBar::disableFilter() +{ + QString label = ((QAction *)sender())->property(dfe_property_label_).toString(); + QString expr = ((QAction *)sender())->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QModelIndex rowIndex = uatModel->index(idx, 0); + if (rowIndex.isValid()) { + uatModel->setData(rowIndex, QVariant::fromValue(false)); + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + filterExpressionsChanged(); + } +} + +void FilterExpressionToolBar::editFilter() +{ + if (! sender()) + return; + + QString label = ((QAction *)sender())->property(dfe_property_label_).toString(); + QString expr = ((QAction *)sender())->property(dfe_property_expression_).toString(); + + int idx = uatRowIndexForFilter(label, expr); + + if (idx > -1) + emit filterEdit(idx); +} + +void FilterExpressionToolBar::onFilterDropped(QString description, QString filter) +{ + if (filter.length() == 0) + return; + + filter_expression_new(qUtf8Printable(description), + qUtf8Printable(filter), qUtf8Printable(description), TRUE); + + save_migrated_uat("Display expressions", &prefs.filter_expressions_old); + filterExpressionsChanged(); +} + +void FilterExpressionToolBar::toolBarShowPreferences() +{ + emit filterPreferences(); +} + +void FilterExpressionToolBar::updateStyleSheet() +{ + // Try to draw 1-pixel-wide separator lines from the button label + // ascent to its baseline. + setStyleSheet(QString( + "QToolBar { background: none; border: none; spacing: 1px; }" + "QFrame { background: none; min-width: 1px; max-width: 1px; }" + )); +} + +int FilterExpressionToolBar::uatRowIndexForFilter(QString label, QString expression) +{ + int result = -1; + + if (expression.length() == 0) + return result; + + UatModel * uatModel = new UatModel(this, "Display expressions"); + + QModelIndex rowIndex; + + if (label.length() > 0) + { + for (int cnt = 0; cnt < uatModel->rowCount() && ! rowIndex.isValid(); cnt++) + { + if (uatModel->data(uatModel->index(cnt, 1), Qt::DisplayRole).toString().compare(label) == 0 && + uatModel->data(uatModel->index(cnt, 2), Qt::DisplayRole).toString().compare(expression) == 0) + { + rowIndex = uatModel->index(cnt, 2); + } + } + } + else + { + rowIndex = uatModel->findRowForColumnContent(((QAction *)sender())->data(), 2); + } + + if (rowIndex.isValid()) + result = rowIndex.row(); + + delete uatModel; + + return result; +} + +bool FilterExpressionToolBar::eventFilter(QObject *obj, QEvent *event) +{ + QMenu * qm = qobject_cast<QMenu *>(obj); + + if (qm && qm->property(dfe_menu_).toBool()) + { + + if (event->type() == QEvent::ContextMenu) + { + QContextMenuEvent *ctx = static_cast<QContextMenuEvent *>(event); + QAction * filterAction = qm->actionAt(ctx->pos()); + + if (filterAction) + customMenu(this, filterAction, ctx->pos()); + return true; + } + else if (event->type() == QEvent::ToolTip) + { + QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); + QAction * filterAction = qm->actionAt(helpEvent->pos()); + if (filterAction) { + QToolTip::showText(helpEvent->globalPos(), filterAction->property(dfe_property_comment_).toString().trimmed()); + } else { + QToolTip::hideText(); + event->ignore(); + } + + return true; + } + } + + return QToolBar::eventFilter(obj, event); +} + +void FilterExpressionToolBar::closeMenu(QAction * /*sender*/) +{ + foreach(QAction * entry, actions()) + { + QWidget * widget = widgetForAction(entry); + QToolButton * tb = qobject_cast<QToolButton *>(widget); + if (tb && tb->menu()) + tb->menu()->close(); + } +} + +QMenu * FilterExpressionToolBar::findParentMenu(const QStringList tree, void *fed_data, QMenu *parent ) +{ + if (!fed_data) + return Q_NULLPTR; + + struct filter_expression_data* data = (filter_expression_data*)fed_data; + if (!data->toolbar) + return Q_NULLPTR; + + if (! tree.isEmpty()) + { + if (!parent) + { + /* Searching existing main menus */ + foreach(QAction * entry, data->toolbar->actions()) + { + QWidget * widget = data->toolbar->widgetForAction(entry); + QToolButton * tb = qobject_cast<QToolButton *>(widget); + if (tb && tb->menu() && tb->text().compare(tree.at(0).trimmed()) == 0) + return findParentMenu(tree.mid(1), fed_data, tb->menu()); + } + } + else if (parent) + { + QString menuName = tree.at(0).trimmed(); + /* Iterate to see, if we next have to jump into another submenu */ + foreach(QAction *entry, parent->actions()) + { + if (entry->menu() && entry->text().compare(menuName) == 0) + return findParentMenu(tree.mid(1), fed_data, entry->menu()); + } + + /* Submenu not found, creating */ + QMenu * subMenu = new QMenu(menuName); + subMenu->installEventFilter(data->toolbar); + subMenu->setProperty(dfe_menu_, QVariant::fromValue(true)); + parent->addMenu(subMenu); + return findParentMenu(tree.mid(1), fed_data, subMenu); + } + + /* No menu has been found, create one */ + QString parentName = tree.at(0).trimmed(); + QToolButton * menuButton = new QToolButton(); + menuButton->setText(parentName); + menuButton->setPopupMode(QToolButton::MenuButtonPopup); + QMenu * parentMenu = new QMenu(menuButton); + parentMenu->installEventFilter(data->toolbar); + parentMenu->setProperty(dfe_menu_, QVariant::fromValue(true)); + menuButton->setMenu(parentMenu); + // Required for QToolButton::MenuButtonPopup. + connect(menuButton, &QToolButton::pressed, menuButton, &QToolButton::showMenu); + data->toolbar->addWidget(menuButton); + + return findParentMenu(tree.mid(1), fed_data, parentMenu); + } + else if (parent) + return parent; + + return Q_NULLPTR; +} + +bool FilterExpressionToolBar::filter_expression_add_action(const void *key _U_, void *value, void *user_data) +{ + filter_expression_t* fe = (filter_expression_t*)value; + struct filter_expression_data* data = (filter_expression_data*)user_data; + + if (!fe->enabled) + return FALSE; + + QString label = QString(fe->label); + + /* Search for parent menu and create if not found */ + QStringList tree = label.split(PARENT_SEPARATOR); + if (!tree.isEmpty()) + tree.removeLast(); + QMenu * parentMenu = findParentMenu(tree, data); + if (parentMenu) + label = label.mid(label.lastIndexOf(PARENT_SEPARATOR) + QString(PARENT_SEPARATOR).length()).trimmed(); + + QAction *dfb_action = new QAction(label, data->toolbar); + if (strlen(fe->comment) > 0) + { + QString tooltip = QString("%1\n%2").arg(fe->comment).arg(fe->expression); + dfb_action->setToolTip(tooltip); + dfb_action->setProperty(dfe_property_comment_, tooltip); + } + else + { + dfb_action->setToolTip(fe->expression); + dfb_action->setProperty(dfe_property_comment_, QString(fe->expression)); + } + dfb_action->setData(QString::fromUtf8(fe->expression)); + dfb_action->setProperty(dfe_property_, true); + dfb_action->setProperty(dfe_property_label_, QString(fe->label)); + dfb_action->setProperty(dfe_property_expression_, QString(fe->expression)); + + if (data->actions_added) { + QFrame *sep = new QFrame(); + sep->setEnabled(false); + data->toolbar->addWidget(sep); + } + + if (parentMenu) + parentMenu->addAction(dfb_action); + else + data->toolbar->addAction(dfb_action); + + connect(dfb_action, &QAction::triggered, data->toolbar, &FilterExpressionToolBar::filterClicked); + data->actions_added = true; + return FALSE; +} + +void FilterExpressionToolBar::filterClicked() +{ + bool prepare = false; + QAction *dfb_action = qobject_cast<QAction*>(sender()); + + if (!dfb_action) + return; + + QString filterText = dfb_action->data().toString(); + prepare = (QApplication::keyboardModifiers() & Qt::ShiftModifier); + + emit filterSelected(filterText, prepare); +} |