aboutsummaryrefslogtreecommitdiffstats
path: root/ui/qt/widgets/capture_filter_edit.cpp
diff options
context:
space:
mode:
authorRoland Knall <roland.knall@br-automation.com>2017-07-05 16:56:45 +0200
committerMichael Mann <mmann78@netscape.net>2017-07-11 21:30:29 +0000
commitee699eb7200aa4470622abe4d1b8a80c461dae0c (patch)
treeff6fbc4f0769a2b5af107ee9485943836b97e64d /ui/qt/widgets/capture_filter_edit.cpp
parent66cc2ed39ddd28fc2d8b22ce2d07783a35a9f10d (diff)
Qt: Move all utility widgets to widgets subdirectory
Move all utility widgets to the widgets subdirectory and add separate source_group for their files Correct some alphabetization in ui/qt/CMakeLists.txt noticed during compare. Change-Id: I2d664edc2b32f126438fb673ea53a5ae94cd43d1 Reviewed-on: https://code.wireshark.org/review/22531 Petri-Dish: Michael Mann <mmann78@netscape.net> Reviewed-by: Roland Knall <rknall@gmail.com> Reviewed-by: Michael Mann <mmann78@netscape.net>
Diffstat (limited to 'ui/qt/widgets/capture_filter_edit.cpp')
-rw-r--r--ui/qt/widgets/capture_filter_edit.cpp541
1 files changed, 541 insertions, 0 deletions
diff --git a/ui/qt/widgets/capture_filter_edit.cpp b/ui/qt/widgets/capture_filter_edit.cpp
new file mode 100644
index 0000000000..41bc695865
--- /dev/null
+++ b/ui/qt/widgets/capture_filter_edit.cpp
@@ -0,0 +1,541 @@
+/* capture_filter_edit.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include <epan/proto.h>
+
+#include "capture_opts.h"
+
+#include <ui/capture_globals.h>
+#include <ui/filter_files.h>
+#include <wsutil/utf8_entities.h>
+
+#include "capture_filter_edit.h"
+#include "capture_filter_syntax_worker.h"
+#include "filter_dialog.h"
+#include "stock_icon_tool_button.h"
+#include "wireshark_application.h"
+
+#include <QComboBox>
+#include <QCompleter>
+#include <QMenu>
+#include <QMessageBox>
+#include <QPainter>
+#include <QStringListModel>
+#include <QStyleOptionFrame>
+
+#include "qt_ui_utils.h"
+
+// To do:
+// - This duplicates some DisplayFilterEdit code.
+// - We need simplified (button- and dropdown-free) versions for use in dialogs and field-only checking.
+
+
+#if defined(Q_OS_MAC) && 0
+// http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSImage_Class/Reference/Reference.html
+// http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Frontends/VirtualBox/src/platform/darwin/UICocoaSpecialControls.mm
+
+class UIMiniCancelButton: public QAbstractButton
+{
+ Q_OBJECT;
+
+public:
+ UIMiniCancelButton(QWidget *pParent = 0);
+
+ void setText(const QString &strText) { m_pButton->setText(strText); }
+ void setToolTip(const QString &strTip) { m_pButton->setToolTip(strTip); }
+ void removeBorder() {}
+
+protected:
+ void paintEvent(QPaintEvent * /* pEvent */) {}
+ void resizeEvent(QResizeEvent *pEvent);
+
+private:
+ UICocoaButton *m_pButton;
+};
+
+UIMiniCancelButton::UIMiniCancelButton(QWidget *pParent /* = 0 */)
+ : QAbstractButton(pParent)
+{
+ setShortcut(QKeySequence(Qt::Key_Escape));
+ m_pButton = new UICocoaButton(UICocoaButton::CancelButton, this);
+ connect(m_pButton, SIGNAL(clicked()),
+ this, SIGNAL(clicked()));
+ setFixedSize(m_pButton->size());
+}
+
+#endif
+
+static const QString libpcap_primitive_chars_ = "-0123456789abcdefghijklmnopqrstuvwxyz";
+
+// grep '^[a-z].*return [A-Z].*;$' scanner.l | awk '{gsub(/\|/, "\n") ; print " << \"" $1 "\""}' | sort
+// Remove "and" and "or".
+static const QStringList libpcap_primitives_ = QStringList()
+ << "aarp" << "action" << "address1" << "address2" << "address3" << "address4"
+ << "ah" << "arp" << "atalk" << "bcc" << "broadcast" << "byte" << "carp"
+ << "clnp" << "connectmsg" << "csnp" << "decnet" << "direction" << "dpc"
+ << "dst" << "es-is" << "esis" << "esp" << "fddi" << "fisu" << "gateway"
+ << "greater" << "hdpc" << "hfisu" << "hlssu" << "hmsu" << "hopc" << "host"
+ << "hsio" << "hsls" << "icmp" << "icmp6" << "igmp" << "igrp" << "iih" << "ilmic"
+ << "inbound" << "ip" << "ip6" << "ipx" << "is-is" << "isis" << "iso" << "l1"
+ << "l2" << "lane" << "lat" << "len" << "less" << "link" << "llc" << "lsp"
+ << "lssu" << "lsu" << "mask" << "metac" << "metaconnect" << "mopdl" << "moprc"
+ << "mpls" << "msu" << "multicast" << "net" << "netbeui" << "oam" << "oamf4"
+ << "oamf4ec" << "oamf4sc" << "on" << "opc" << "outbound" << "pim"
+ << "port" << "portrange" << "pppoed" << "pppoes" << "proto" << "psnp" << "ra"
+ << "radio" << "rarp" << "reason" << "rnr" << "rset" << "sc" << "sca" << "sctp"
+ << "sio" << "sls" << "snp" << "src" << "srnr" << "stp" << "subtype" << "ta"
+ << "tcp" << "type" << "udp" << "vci" << "vlan" << "vpi" << "vrrp"
+ ;
+
+CaptureFilterEdit::CaptureFilterEdit(QWidget *parent, bool plain) :
+ SyntaxLineEdit(parent),
+ plain_(plain),
+ field_name_only_(false),
+ enable_save_action_(false),
+ save_action_(NULL),
+ remove_action_(NULL),
+ bookmark_button_(NULL),
+ clear_button_(NULL),
+ apply_button_(NULL)
+{
+ setAccessibleName(tr("Capture filter entry"));
+
+ completion_model_ = new QStringListModel(this);
+ setCompleter(new QCompleter(completion_model_, this));
+ setCompletionTokenChars(libpcap_primitive_chars_);
+
+ setConflict(false);
+
+ if (!plain_) {
+ bookmark_button_ = new StockIconToolButton(this, "x-capture-filter-bookmark");
+ bookmark_button_->setCursor(Qt::ArrowCursor);
+ 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(
+ "QToolButton {"
+ " border: none;"
+ " background: transparent;" // Disables platform style on Windows.
+ " padding: 0 0 0 0;"
+ "}"
+ "QToolButton::menu-indicator { image: none; }"
+ );
+ connect(bookmark_button_, SIGNAL(clicked()), this, SLOT(bookmarkClicked()));
+ }
+
+ if (!plain_) {
+ clear_button_ = new StockIconToolButton(this, "x-filter-clear");
+ clear_button_->setCursor(Qt::ArrowCursor);
+ clear_button_->setToolTip(QString());
+ clear_button_->setIconSize(QSize(14, 14));
+ clear_button_->setStyleSheet(
+ "QToolButton {"
+ " border: none;"
+ " background: transparent;" // Disables platform style on Windows.
+ " padding: 0 0 0 0;"
+ " margin-left: 1px;"
+ "}"
+ );
+ connect(clear_button_, SIGNAL(clicked()), this, SLOT(clearFilter()));
+ }
+
+ connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(checkFilter(const QString&)));
+
+#if 0
+ // Disable the apply button for now
+ if (!plain_) {
+ apply_button_ = new StockIconToolButton(this, "x-filter-apply");
+ apply_button_->setCursor(Qt::ArrowCursor);
+ apply_button_->setEnabled(false);
+ apply_button_->setToolTip(tr("Apply this filter string to the display."));
+ apply_button_->setIconSize(QSize(24, 14));
+ apply_button_->setStyleSheet(
+ "QToolButton {"
+ " border: none;"
+ " background: transparent;" // Disables platform style on Windows.
+ " padding: 0 0 0 0;"
+ "}"
+ );
+ connect(apply_button_, SIGNAL(clicked()), this, SLOT(applyCaptureFilter()));
+ }
+#endif
+ connect(this, SIGNAL(returnPressed()), this, SLOT(applyCaptureFilter()));
+
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize bksz;
+ if (bookmark_button_) bksz = bookmark_button_->sizeHint();
+ QSize cbsz;
+ if (clear_button_) cbsz = clear_button_->sizeHint();
+ QSize apsz;
+ if (apply_button_) apsz = apply_button_->sizeHint();
+
+ setStyleSheet(QString(
+ "CaptureFilterEdit {"
+ " padding-left: %1px;"
+ " margin-left: %2px;"
+ " margin-right: %3px;"
+ "}"
+ )
+ .arg(frameWidth + 1)
+ .arg(bksz.width())
+ .arg(cbsz.width() + apsz.width() + frameWidth + 1)
+ );
+
+ QComboBox *cf_combo = qobject_cast<QComboBox *>(parent);
+ if (cf_combo) {
+ connect(cf_combo, SIGNAL(activated(QString)), this, SIGNAL(textEdited(QString)));
+ }
+
+ QThread *syntax_thread = new QThread;
+ syntax_worker_ = new CaptureFilterSyntaxWorker;
+ syntax_worker_->moveToThread(syntax_thread);
+ connect(wsApp, SIGNAL(appInitialized()), this, SLOT(updateBookmarkMenu()));
+ connect(wsApp, SIGNAL(captureFilterListChanged()), this, SLOT(updateBookmarkMenu()));
+ connect(syntax_thread, SIGNAL(started()), syntax_worker_, SLOT(start()));
+ connect(syntax_thread, SIGNAL(started()), this, SLOT(checkFilter()));
+ connect(syntax_worker_, SIGNAL(syntaxResult(QString,int,QString)),
+ this, SLOT(setFilterSyntaxState(QString,int,QString)));
+ connect(syntax_thread, SIGNAL(finished()), syntax_worker_, SLOT(deleteLater()));
+ syntax_thread->start();
+ updateBookmarkMenu();
+}
+
+void CaptureFilterEdit::paintEvent(QPaintEvent *evt) {
+ SyntaxLineEdit::paintEvent(evt);
+
+ if (bookmark_button_) {
+ // Draw the right border 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();
+ QSize bksz = bookmark_button_->size();
+ painter.drawLine(bksz.width(), cr.top(), bksz.width(), cr.bottom());
+ }
+}
+
+void CaptureFilterEdit::resizeEvent(QResizeEvent *)
+{
+ QSize cbsz;
+ if (clear_button_) cbsz = clear_button_->sizeHint();
+ QSize apsz;
+ if (apply_button_) apsz = apply_button_->sizeHint();
+
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ if (clear_button_) {
+ clear_button_->move(contentsRect().right() - frameWidth - cbsz.width() - apsz.width(),
+ contentsRect().top());
+ clear_button_->setMinimumHeight(contentsRect().height());
+ clear_button_->setMaximumHeight(contentsRect().height());
+ }
+ if (apply_button_) {
+ apply_button_->move(contentsRect().right() - frameWidth - apsz.width(),
+ contentsRect().top());
+ apply_button_->setMinimumHeight(contentsRect().height());
+ apply_button_->setMaximumHeight(contentsRect().height());
+ }
+ if (bookmark_button_) {
+ bookmark_button_->setMinimumHeight(contentsRect().height());
+ bookmark_button_->setMaximumHeight(contentsRect().height());
+ }
+}
+
+void CaptureFilterEdit::setConflict(bool conflict)
+{
+ if (conflict) {
+ //: This is a very long concept that needs to fit into a short space.
+ placeholder_text_ = tr("Multiple filters selected. Override them here or leave this blank to preserve them.");
+ setToolTip(tr("<p>The interfaces you have selected have different capture filters."
+ " Typing a filter here will override them. Doing nothing will"
+ " preserve them.</p>"));
+ } else {
+ placeholder_text_ = QString(tr("Enter a capture filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS);
+ setToolTip(QString());
+ }
+ setPlaceholderText(placeholder_text_);
+}
+
+// XXX Make this private along with setConflict.
+QPair<const QString, bool> CaptureFilterEdit::getSelectedFilter()
+{
+ QString user_filter;
+ bool filter_conflict = false;
+#ifdef HAVE_LIBPCAP
+ int selected_devices = 0;
+
+ for (guint i = 0; i < global_capture_opts.all_ifaces->len; i++) {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, i);
+ if (device.selected) {
+ selected_devices++;
+ if (selected_devices == 1) {
+ user_filter = device.cfilter;
+ } else {
+ if (user_filter.compare(device.cfilter)) {
+ filter_conflict = true;
+ }
+ }
+ }
+ }
+#endif // HAVE_LIBPCAP
+ return QPair<const QString, bool>(user_filter, filter_conflict);
+}
+
+void CaptureFilterEdit::checkFilter(const QString& filter)
+{
+ setSyntaxState(Busy);
+ popFilterSyntaxStatus();
+ bool empty = filter.isEmpty();
+
+ setConflict(false);
+ if (bookmark_button_) {
+ bool match = false;
+
+ for (GList *cf_item = get_filter_list_first(CFILTER_LIST); cf_item; cf_item = g_list_next(cf_item)) {
+ if (!cf_item->data) continue;
+ filter_def *cf_def = (filter_def *) cf_item->data;
+ if (!cf_def->name || !cf_def->strval) continue;
+
+ if (filter.compare(cf_def->strval) == 0) {
+ match = true;
+ }
+ }
+
+ if (match) {
+ bookmark_button_->setStockIcon("x-filter-matching-bookmark");
+ if (remove_action_) {
+ remove_action_->setData(text());
+ remove_action_->setVisible(true);
+ }
+ } else {
+ bookmark_button_->setStockIcon("x-capture-filter-bookmark");
+ if (remove_action_) {
+ remove_action_->setVisible(false);
+ }
+ }
+
+ enable_save_action_ = (!match && !filter.isEmpty());
+ if (save_action_) {
+ save_action_->setEnabled(false);
+ }
+ }
+
+ if (apply_button_) {
+ apply_button_->setEnabled(false);
+ }
+
+ if (clear_button_) {
+ clear_button_->setVisible(!empty);
+ }
+
+ if (empty) {
+ setFilterSyntaxState(filter, Empty, QString());
+ } else {
+ syntax_worker_->checkFilter(filter);
+ }
+}
+
+void CaptureFilterEdit::checkFilter()
+{
+ checkFilter(text());
+}
+
+void CaptureFilterEdit::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_, SIGNAL(triggered(bool)), this, SLOT(saveFilter()));
+ remove_action_ = bb_menu->addAction(tr("Remove this filter"));
+ connect(remove_action_, SIGNAL(triggered(bool)), this, SLOT(removeFilter()));
+ QAction *manage_action = bb_menu->addAction(tr("Manage Capture Filters"));
+ connect(manage_action, SIGNAL(triggered(bool)), this, SLOT(showFilters()));
+ bb_menu->addSeparator();
+
+ for (GList *cf_item = get_filter_list_first(CFILTER_LIST); cf_item; cf_item = g_list_next(cf_item)) {
+ if (!cf_item->data) continue;
+ filter_def *cf_def = (filter_def *) cf_item->data;
+ if (!cf_def->name || !cf_def->strval) continue;
+
+ int one_em = bb_menu->fontMetrics().height();
+ QString prep_text = QString("%1: %2").arg(cf_def->name).arg(cf_def->strval);
+ prep_text = bb_menu->fontMetrics().elidedText(prep_text, Qt::ElideRight, one_em * 40);
+
+ QAction *prep_action = bb_menu->addAction(prep_text);
+ prep_action->setData(cf_def->strval);
+ connect(prep_action, SIGNAL(triggered(bool)), this, SLOT(prepareFilter()));
+ }
+
+ checkFilter();
+}
+
+void CaptureFilterEdit::setFilterSyntaxState(QString filter, int state, QString err_msg)
+{
+ if (filter.compare(text()) == 0) { // The user hasn't changed the filter
+ setSyntaxState((SyntaxState)state);
+ if (!err_msg.isEmpty()) {
+ emit pushFilterSyntaxStatus(err_msg);
+ }
+ }
+
+ bool valid = (state != Invalid);
+
+ if (valid) {
+ if (save_action_) {
+ save_action_->setEnabled(enable_save_action_);
+ }
+ if (apply_button_) {
+ apply_button_->setEnabled(true);
+ }
+ }
+
+ emit captureFilterSyntaxChanged(valid);
+}
+
+void CaptureFilterEdit::bookmarkClicked()
+{
+ emit addBookmark(text());
+}
+
+void CaptureFilterEdit::clearFilter()
+{
+ clear();
+ emit textEdited(text());
+}
+
+void CaptureFilterEdit::buildCompletionList(const QString &primitive_word)
+{
+ if (primitive_word.length() < 1) {
+ completion_model_->setStringList(QStringList());
+ return;
+ }
+
+ // Grab matching capture filters from our parent combo and from the
+ // saved capture filters file. Skip ones that look like single fields
+ // and assume they will be added below.
+ QStringList complex_list;
+ QComboBox *cf_combo = qobject_cast<QComboBox *>(parent());
+ if (cf_combo) {
+ for (int i = 0; i < cf_combo->count() ; i++) {
+ QString recent_filter = cf_combo->itemText(i);
+
+ if (isComplexFilter(recent_filter)) {
+ complex_list << recent_filter;
+ }
+ }
+ }
+ for (const GList *cf_item = get_filter_list_first(CFILTER_LIST); cf_item; cf_item = g_list_next(cf_item)) {
+ const filter_def *cf_def = (filter_def *) cf_item->data;
+ if (!cf_def || !cf_def->strval) continue;
+ QString saved_filter = cf_def->strval;
+
+ if (isComplexFilter(saved_filter) && !complex_list.contains(saved_filter)) {
+ complex_list << saved_filter;
+ }
+ }
+
+ // libpcap has a small number of primitives so we just add the whole list
+ // sans the current word.
+ QStringList primitive_list = libpcap_primitives_;
+ primitive_list.removeAll(primitive_word);
+
+ completion_model_->setStringList(complex_list + primitive_list);
+ completer()->setCompletionPrefix(primitive_word);
+}
+
+void CaptureFilterEdit::applyCaptureFilter()
+{
+ if (syntaxState() == Invalid) {
+ return;
+ }
+
+ emit startCapture();
+}
+
+void CaptureFilterEdit::saveFilter()
+{
+ FilterDialog capture_filter_dlg(window(), FilterDialog::CaptureFilter, text());
+ capture_filter_dlg.exec();
+}
+
+void CaptureFilterEdit::removeFilter()
+{
+ QAction *ra = qobject_cast<QAction*>(sender());
+ if (!ra || ra->data().toString().isEmpty()) return;
+
+ QString remove_filter = ra->data().toString();
+
+ for (GList *cf_item = get_filter_list_first(CFILTER_LIST); cf_item; cf_item = g_list_next(cf_item)) {
+ if (!cf_item->data) continue;
+ filter_def *cf_def = (filter_def *) cf_item->data;
+ if (!cf_def->name || !cf_def->strval) continue;
+
+ if (remove_filter.compare(cf_def->strval) == 0) {
+ remove_from_filter_list(CFILTER_LIST, cf_item);
+ }
+ }
+
+ save_filter_list(CFILTER_LIST);
+
+ updateBookmarkMenu();
+}
+
+void CaptureFilterEdit::showFilters()
+{
+ FilterDialog capture_filter_dlg(window(), FilterDialog::CaptureFilter);
+ capture_filter_dlg.exec();
+}
+
+void CaptureFilterEdit::prepareFilter()
+{
+ QAction *pa = qobject_cast<QAction*>(sender());
+ if (!pa || pa->data().toString().isEmpty()) return;
+
+ QString filter(pa->data().toString());
+ setText(filter);
+ emit textEdited(filter);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */