summaryrefslogtreecommitdiff
path: root/ui/qt/interface_frame.cpp
diff options
context:
space:
mode:
authorRoland Knall <rknall@gmail.com>2016-10-01 08:54:57 +0200
committerRoland Knall <rknall@gmail.com>2016-10-01 13:18:51 +0000
commit99097dd3c65358a525e40767cc1501c4116c3a4d (patch)
tree1ad9637554aebded5ff4bf8a400a6c3fc2ed4b4e /ui/qt/interface_frame.cpp
parentb6ad91520fd602710f5afe4a4eb8787a6bca22d4 (diff)
downloadwireshark-99097dd3c65358a525e40767cc1501c4116c3a4d.tar.gz
Interface List: Change display to view/model
This changes the underlying model of the main interface tree. Because of that, we can resort to a view/model approach, enlisting the global interfaces list as only data source. The interface list works identical to the old list, but allows for filtering of the displayed interfaces by type. Only types, which are present and whose interfaces are not hidden, are being displayed for selection. Change-Id: If8475b227daa026dc0ad3d25bc7fe050d5bf2ac3 Reviewed-on: https://code.wireshark.org/review/17940 Reviewed-by: Roland Knall <rknall@gmail.com>
Diffstat (limited to 'ui/qt/interface_frame.cpp')
-rw-r--r--ui/qt/interface_frame.cpp395
1 files changed, 395 insertions, 0 deletions
diff --git a/ui/qt/interface_frame.cpp b/ui/qt/interface_frame.cpp
new file mode 100644
index 0000000000..1fdd1c4dca
--- /dev/null
+++ b/ui/qt/interface_frame.cpp
@@ -0,0 +1,395 @@
+/* interface_frame.cpp
+ * Display of interfaces, including their respective data, and the
+ * capability to filter interfaces by type
+ *
+ * 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 <ui_interface_frame.h>
+
+#include "ui/qt/interface_frame.h"
+#include "ui/qt/interface_tree_model.h"
+
+#include "sparkline_delegate.h"
+#include "wireshark_application.h"
+
+#ifdef HAVE_EXTCAP
+#include "extcap.h"
+#endif
+
+#include <QFrame>
+#include <QPushButton>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QItemSelection>
+
+#define BTN_IFTYPE_PROPERTY "ifType"
+
+#ifdef HAVE_LIBPCAP
+const int stat_update_interval_ = 1000; // ms
+#endif
+
+InterfaceFrame::InterfaceFrame(QWidget * parent)
+: AccordionFrame(parent),
+ ui(new Ui::InterfaceFrame)
+#ifdef HAVE_LIBPCAP
+ ,stat_timer_(NULL)
+#endif // HAVE_LIBPCAP
+{
+ ui->setupUi(this);
+
+ setStyleSheet(QString(
+ "QFrame {"
+ " border: 0;"
+ "}"
+ "QTreeView {"
+ " border: 0;"
+ "}"
+ ));
+
+#ifdef Q_OS_MAC
+ ui->interfaceTree->setAttribute(Qt::WA_MacShowFocusRect, false);
+#endif
+
+ /* TODO: There must be a better way to do this */
+ ifTypeDescription.insert(IF_WIRED, tr("Physical"));
+ ifTypeDescription.insert(IF_AIRPCAP, tr("AirPCAP"));
+ ifTypeDescription.insert(IF_PIPE, tr("Pipe"));
+ ifTypeDescription.insert(IF_STDIN, tr("STDIN"));
+ ifTypeDescription.insert(IF_BLUETOOTH, tr("Bluetooth"));
+ ifTypeDescription.insert(IF_WIRELESS, tr("Wireless"));
+ ifTypeDescription.insert(IF_DIALUP, tr("Dial-Up"));
+ ifTypeDescription.insert(IF_USB, tr("USB"));
+#ifdef HAVE_EXTCAP
+ ifTypeDescription.insert(IF_EXTCAP, tr("External Capture"));
+#endif
+ ifTypeDescription.insert(IF_VIRTUAL, tr ("Virtual"));
+
+ proxyModel = new InterfaceSortFilterModel(this);
+ sourceModel = new InterfaceTreeModel(this);
+ proxyModel->setSourceModel(sourceModel);
+ ui->interfaceTree->setModel(proxyModel);
+
+ ui->interfaceTree->setItemDelegateForColumn(IFTREE_COL_STATS, new SparkLineDelegate(this));
+
+ buttonLayout = new QHBoxLayout(ui->wdgButtons);
+
+ connect(wsApp, SIGNAL(appInitialized()), this, SLOT(interfaceListChanged()));
+ connect(wsApp, SIGNAL(localInterfaceListChanged()), this, SLOT(interfaceListChanged()));
+
+ connect(ui->interfaceTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ this, SLOT(interfaceTreeSelectionChanged(const QItemSelection &, const QItemSelection &)));
+
+ ui->wdgButtons->setLayout(buttonLayout);
+}
+
+InterfaceFrame::~InterfaceFrame()
+{
+ delete sourceModel;
+ delete proxyModel;
+ delete ui;
+}
+
+void InterfaceFrame::hideEvent(QHideEvent *) {
+#ifdef HAVE_LIBPCAP
+ if (stat_timer_)
+ stat_timer_->stop();
+ sourceModel->stopStatistic();
+#endif // HAVE_LIBPCAP
+}
+
+void InterfaceFrame::showEvent(QShowEvent *) {
+#ifdef HAVE_LIBPCAP
+ if (stat_timer_)
+ stat_timer_->start(stat_update_interval_);
+#endif // HAVE_LIBPCAP
+}
+
+void InterfaceFrame::actionButton_toggled(bool checked)
+{
+ QVariant ifType = sender()->property(BTN_IFTYPE_PROPERTY);
+ if ( ifType.isValid() )
+ {
+ proxyModel->setInterfaceTypeVisible(ifType.toInt(), checked);
+ }
+}
+
+QAbstractButton * InterfaceFrame::createButton(QString text, QString prop, QVariant content )
+{
+ QPushButton * button = new QPushButton(text);
+ button->setCheckable(true);
+ if (prop.size() > 0 && prop.compare(BTN_IFTYPE_PROPERTY) == 0)
+ {
+ button->setProperty(prop.toStdString().c_str(), content);
+ button->setChecked(proxyModel->isInterfaceTypeShown(content.toInt()));
+ }
+
+ connect(button, SIGNAL(toggled(bool)), this, SLOT(actionButton_toggled(bool)));
+
+ return (QAbstractButton *)button;
+}
+
+void InterfaceFrame::interfaceListChanged()
+{
+ if (sourceModel->rowCount() == 0)
+ {
+ ui->interfaceTree->setHidden(true);
+ ui->lblNoInterfaces->setHidden(false);
+
+ if ( global_capture_opts.ifaces_err != 0 )
+ {
+ ui->lblNoInterfaces->setText(tr(global_capture_opts.ifaces_err_info));
+ }
+ else
+ {
+ ui->lblNoInterfaces->setText(tr("No interfaces found"));
+ }
+ }
+ else
+ {
+ ui->interfaceTree->setHidden(false);
+ ui->lblNoInterfaces->setHidden(true);
+ }
+
+ resetInterfaceButtons();
+}
+
+void InterfaceFrame::resetInterfaceButtons()
+{
+ QAbstractButton * button = 0;
+
+ if ( ! global_capture_opts.all_ifaces )
+ return;
+
+ foreach (QWidget * w, ui->wdgButtons->findChildren<QWidget *>())
+ delete w;
+
+ foreach ( int ifType, proxyModel->typesDisplayed() )
+ {
+ QString text = ifTypeDescription[ifType];
+
+ button = createButton(text, BTN_IFTYPE_PROPERTY, ifType);
+
+ buttonLayout->addWidget(button);
+ }
+
+#ifdef HAVE_EXTCAP
+ /* This is a rude approach. Issue here is, that if WS starts with extcap interfaces disabled,
+ * the next command reduces the column to 0 size. If extcap interfaces get enabled afterwards,
+ * the icon for configuration is not shown. Doing this in the action for the buttons does not
+ * work, as it leads to a loop. Good approach would be to use hidden icon for all other types,
+ * or to determine size here and set it. 50 is bigger then the biggest icon, so for now, this
+ * is being set. */
+ ui->interfaceTree->setColumnWidth(IFTREE_COL_EXTCAP, 50);
+#endif
+ ui->interfaceTree->resizeColumnToContents(IFTREE_COL_NAME);
+ ui->interfaceTree->resizeColumnToContents(IFTREE_COL_STATS);
+
+#ifdef HAVE_LIBPCAP
+ if (!stat_timer_) {
+ updateStatistics();
+ stat_timer_ = new QTimer(this);
+ connect(stat_timer_, SIGNAL(timeout()), this, SLOT(updateStatistics()));
+ stat_timer_->start(stat_update_interval_);
+ }
+#endif
+}
+
+void InterfaceFrame::updateSelectedInterfaces()
+{
+ if ( ! global_capture_opts.all_ifaces )
+ return;
+
+ QItemSelection mySelection;
+
+ for(unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++)
+ {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+
+ if ( device.selected )
+ {
+ QModelIndex selectIndex = proxyModel->mapFromSource(sourceModel->index(idx, 0));
+ /* Proxy model has masked out the interface */
+ if ( !selectIndex.isValid() )
+ continue;
+
+ mySelection.merge(
+ QItemSelection( selectIndex, proxyModel->index(selectIndex.row(), sourceModel->columnCount() - 1) ),
+ QItemSelectionModel::SelectCurrent
+ );
+ }
+ }
+
+ ui->interfaceTree->selectionModel()->clearSelection();
+ ui->interfaceTree->selectionModel()->select(mySelection, QItemSelectionModel::SelectCurrent );
+}
+
+void InterfaceFrame::interfaceTreeSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected)
+{
+ if (selected.count() == 0 && deselected.count() == 0)
+ return;
+ if ( ! global_capture_opts.all_ifaces )
+ return;
+
+ QList<int> selectedIndices;
+
+ /* Take all selected interfaces, not just the newly ones */
+ QItemSelection allSelected = ui->interfaceTree->selectionModel()->selection();
+ QItemSelection sourceSelection = proxyModel->mapSelectionToSource(allSelected);
+
+ foreach(QItemSelectionRange selection, sourceSelection)
+ {
+ foreach(QModelIndex index, selection.indexes())
+ {
+ if ( ! selectedIndices.contains(index.row()) )
+ {
+ selectedIndices.append(index.row());
+ }
+ }
+ }
+
+ global_capture_opts.num_selected = 0;
+ bool selectionHasChanged = false;
+
+ for ( unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++ )
+ {
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
+ if ( !device.locked )
+ {
+ if ( selectedIndices.contains(idx) )
+ {
+ if (! device.selected )
+ selectionHasChanged = true;
+ device.selected = TRUE;
+ global_capture_opts.num_selected++;
+ } else {
+ if ( device.selected )
+ selectionHasChanged = true;
+ device.selected = FALSE;
+ }
+ device.locked = TRUE;
+ global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, idx);
+ g_array_insert_val(global_capture_opts.all_ifaces, idx, device);
+
+ device.locked = FALSE;
+ global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, idx);
+ g_array_insert_val(global_capture_opts.all_ifaces, idx, device);
+ }
+ }
+
+ if ( selectionHasChanged )
+ emit itemSelectionChanged();
+}
+
+void InterfaceFrame::on_interfaceTree_doubleClicked(const QModelIndex &index)
+{
+ QModelIndex realIndex = proxyModel->mapToSource(index);
+
+ if ( ! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) realIndex.row() )
+ return;
+
+#ifdef HAVE_EXTCAP
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, realIndex.row());
+
+ QString extcap_string = device.if_info.extcap;
+
+ /* We trust the string here. If this interface is really extcap, the string is
+ * being checked immediatly before the dialog is being generated */
+ if ( extcap_string.length() > 0 )
+ {
+ QString device_name = device.name;
+ /* this checks if configuration is required and not yet provided or saved via prefs */
+ if ( extcap_has_configuration((const char *)(device_name.toStdString().c_str()), TRUE) )
+ {
+ emit showExtcapOptions(device_name);
+ return;
+ }
+ }
+#endif
+ emit startCapture();
+}
+
+#ifdef HAVE_EXTCAP
+void InterfaceFrame::on_interfaceTree_clicked(const QModelIndex &index)
+{
+ if ( index.column() == 0 )
+ {
+ QModelIndex realIndex = proxyModel->mapToSource(index);
+
+ if ( ! global_capture_opts.all_ifaces || global_capture_opts.all_ifaces->len <= (guint) realIndex.row() )
+ return;
+
+ interface_t device = g_array_index(global_capture_opts.all_ifaces, interface_t, realIndex.row());
+
+ QString extcap_string = device.if_info.extcap;
+
+ /* We trust the string here. If this interface is really extcap, the string is
+ * being checked immediatly before the dialog is being generated */
+ if ( extcap_string.length() > 0 )
+ {
+ QString device_name = device.name;
+
+ /* this checks if configuration is required and not yet provided or saved via prefs */
+ if ( extcap_has_configuration((const char *)(device_name.toStdString().c_str()), FALSE) )
+ {
+ emit showExtcapOptions(device_name);
+ return;
+ }
+ }
+ }
+}
+#endif
+
+void InterfaceFrame::updateStatistics(void)
+{
+ if ( ! global_capture_opts.all_ifaces )
+ return;
+
+#ifdef HAVE_LIBPCAP
+
+ for( unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++ )
+ {
+ QModelIndex selectIndex = proxyModel->mapFromSource(sourceModel->index(idx, 0));
+
+ /* Proxy model has not masked out the interface */
+ if ( selectIndex.isValid() )
+ sourceModel->updateStatistic(idx);
+ }
+
+#endif
+}
+
+/* Proxy Method so we do not need to expose the source model */
+void InterfaceFrame::getPoints(int idx, PointList * pts)
+{
+ sourceModel->getPoints(idx, pts);
+}
+
+/*
+ * 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:
+ */