/* capture_filter_edit.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * 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 #include #include "capture_opts.h" #include #include #include #include "capture_filter_edit.h" #include "capture_filter_syntax_worker.h" #include "stock_icon_tool_button.h" #include "wireshark_application.h" #include #include #include #include #include #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), 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_); placeholder_text_ = QString(tr("Enter a capture filter %1")).arg(UTF8_HORIZONTAL_ELLIPSIS); #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) setPlaceholderText(placeholder_text_); #endif // These are fully implemented in DisplayFilterEdit but not here. if (!plain_) { bookmark_button_ = new StockIconToolButton(this, "x-filter-bookmark"); bookmark_button_->setCursor(Qt::ArrowCursor); 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(clear())); } connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(checkFilter(const QString&))); 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())); } 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) ); QThread *syntax_thread = new QThread; syntax_worker_ = new CaptureFilterSyntaxWorker; syntax_worker_->moveToThread(syntax_thread); connect(wsApp, SIGNAL(appInitialized()), this, SLOT(initCaptureFilter())); 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(); checkFilter(); } #if QT_VERSION < QT_VERSION_CHECK(4, 7, 0) void CaptureFilterEdit::paintEvent(QPaintEvent *evt) { SyntaxLineEdit::paintEvent(evt); // http://wiki.forum.nokia.com/index.php/Custom_QLineEdit if (text().isEmpty() && ! this->hasFocus()) { QPainter p(this); QFont f = font(); f.setItalic(true); p.setFont(f); QColor color(palette().color(foregroundRole())); color.setAlphaF(0.5); p.setPen(color); QStyleOptionFrame opt; initStyleOption(&opt); QRect cr = style()->subElementRect(QStyle::SE_LineEditContents, &opt, this); cr.setLeft(cr.left() + 2); cr.setRight(cr.right() - 2); p.drawText(cr, Qt::AlignLeft|Qt::AlignVCenter, placeholder_text_); } // else check filter syntax and set the background accordingly // XXX - Should we add little warning/error icons as well? } #endif // QT < 4.7 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::checkFilter(const QString& filter) { setSyntaxState(Busy); popFilterSyntaxStatus(); bool empty = filter.isEmpty(); if (bookmark_button_) { bookmark_button_->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::initCaptureFilter() { #ifdef HAVE_LIBPCAP setText(global_capture_opts.default_options.cfilter); #endif // HAVE_LIBPCAP } 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 (bookmark_button_) { bookmark_button_->setEnabled(true); } if (apply_button_) { apply_button_->setEnabled(true); } } emit captureFilterSyntaxChanged(valid); } void CaptureFilterEdit::bookmarkClicked() { emit addBookmark(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(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(); } /* * 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: */