1 /* interface_tree_model.cpp
2 * Model for the interface data for display in the interface frame
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include <ui/qt/models/interface_tree_model.h>
16 #include "ui/capture.h"
17 #include "capture/capture-pcap-util.h"
18 #include "capture_opts.h"
19 #include "ui/capture_ui_utils.h"
20 #include "ui/capture_globals.h"
23 #include "wsutil/filesystem.h"
25 #include <ui/qt/utils/qt_ui_utils.h>
26 #include <ui/qt/utils/stock_icon.h>
27 #include "main_application.h"
29 /* Needed for the meta type declaration of QList<int>* */
30 #include <ui/qt/models/sparkline_delegate.h>
34 const QString
InterfaceTreeModel::DefaultNumericValue
= QObject::tr("default");
37 * This is the data model for interface trees. It implies, that the index within
38 * global_capture_opts.all_ifaces is identical to the row. This is always the case, even
39 * when interfaces are hidden by the proxy model. But for this to work, every access
40 * to the index from within the view, has to be filtered through the proxy model.
42 InterfaceTreeModel::InterfaceTreeModel(QObject
*parent
) :
43 QAbstractTableModel(parent
)
48 connect(mainApp
, &MainApplication::appInitialized
, this, &InterfaceTreeModel::interfaceListChanged
);
49 connect(mainApp
, &MainApplication::localInterfaceListChanged
, this, &InterfaceTreeModel::interfaceListChanged
);
52 InterfaceTreeModel::~InterfaceTreeModel(void)
56 capture_stat_stop(stat_cache_
);
59 #endif // HAVE_LIBPCAP
62 QString
InterfaceTreeModel::interfaceError()
66 // First, see if there was an error fetching the interfaces.
69 if (global_capture_opts
.ifaces_err
!= 0)
71 return tr(global_capture_opts
.ifaces_err_info
);
75 // Otherwise, if there are no rows, there were no interfaces
80 return tr("No interfaces found.");
84 // No error. Return an empty string.
89 // We were built without pcap support, so we have no notion of
92 return tr("This version of Wireshark was built without packet capture support.");
96 int InterfaceTreeModel::rowCount(const QModelIndex
&) const
99 return (global_capture_opts
.all_ifaces
? global_capture_opts
.all_ifaces
->len
: 0);
101 /* Currently no interfaces available for libpcap-less builds */
106 int InterfaceTreeModel::columnCount(const QModelIndex
&) const
108 /* IFTREE_COL_MAX is not being displayed, it is the definition for the maximum numbers of columns */
109 return ((int) IFTREE_COL_MAX
);
112 QVariant
InterfaceTreeModel::data(const QModelIndex
&index
, int role
) const
115 bool interfacesLoaded
= true;
116 if (! global_capture_opts
.all_ifaces
|| global_capture_opts
.all_ifaces
->len
== 0)
117 interfacesLoaded
= false;
119 if (!index
.isValid())
122 int row
= index
.row();
123 InterfaceTreeColumns col
= (InterfaceTreeColumns
) index
.column();
125 if (interfacesLoaded
)
127 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, row
);
129 /* Data for display in cell */
130 if (role
== Qt::DisplayRole
)
132 /* Only the name is being displayed */
133 if (col
== IFTREE_COL_NAME
)
135 return QString(device
->name
);
137 else if (col
== IFTREE_COL_DESCRIPTION
)
139 return QString(device
->if_info
.friendly_name
);
141 else if (col
== IFTREE_COL_DISPLAY_NAME
)
143 return QString(device
->display_name
);
145 else if (col
== IFTREE_COL_PIPE_PATH
)
147 return QString(device
->if_info
.name
);
149 else if (col
== IFTREE_COL_CAPTURE_FILTER
)
151 if (device
->cfilter
&& strlen(device
->cfilter
) > 0)
152 return html_escape(QString(device
->cfilter
));
154 else if (col
== IFTREE_COL_EXTCAP_PATH
)
156 return QString(device
->if_info
.extcap
);
158 else if (col
== IFTREE_COL_SNAPLEN
)
160 return device
->has_snaplen
? QString::number(device
->snaplen
) : DefaultNumericValue
;
162 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
163 else if (col
== IFTREE_COL_BUFFERLEN
)
165 return QString::number(device
->buffer
);
168 else if (col
== IFTREE_COL_TYPE
)
170 return QVariant::fromValue((int)device
->if_info
.type
);
172 else if (col
== IFTREE_COL_COMMENT
)
174 QString comment
= gchar_free_to_qstring(capture_dev_user_descr_find(device
->name
));
175 if (comment
.length() > 0)
178 return QString(device
->if_info
.vendor_description
);
180 else if (col
== IFTREE_COL_DLT
)
182 // XXX - this is duplicated in
183 // InterfaceTreeWidgetItem::updateInterfaceColumns;
184 // it should be done in common code somewhere.
186 if (device
->active_dlt
== -1)
187 linkname
= "Unknown";
189 linkname
= QObject::tr("DLT %1").arg(device
->active_dlt
);
190 for (GList
*list
= device
->links
; list
!= Q_NULLPTR
; list
= gxx_list_next(list
)) {
191 link_row
*linkr
= gxx_list_data(link_row
*, list
);
192 if (linkr
->dlt
== device
->active_dlt
) {
193 linkname
= linkr
->name
;
203 /* Return empty string for every other DisplayRole */
207 else if (role
== Qt::CheckStateRole
)
209 if (col
== IFTREE_COL_HIDDEN
)
211 /* Hidden is a de-selection, therefore inverted logic here */
212 return device
->hidden
? Qt::Unchecked
: Qt::Checked
;
214 else if (col
== IFTREE_COL_PROMISCUOUSMODE
)
216 return device
->pmode
? Qt::Checked
: Qt::Unchecked
;
218 #ifdef HAVE_PCAP_CREATE
219 else if (col
== IFTREE_COL_MONITOR_MODE
)
221 return device
->monitor_mode_enabled
? Qt::Checked
: Qt::Unchecked
;
225 /* Used by SparkLineDelegate for loading the data for the statistics line */
226 else if (role
== Qt::UserRole
)
228 if (col
== IFTREE_COL_STATS
)
230 if ((active
.contains(device
->name
) && active
[device
->name
]) && points
.contains(device
->name
))
231 return QVariant::fromValue(points
[device
->name
]);
233 else if (col
== IFTREE_COL_ACTIVE
)
235 if (active
.contains(device
->name
))
236 return QVariant::fromValue(active
[device
->name
]);
238 else if (col
== IFTREE_COL_HIDDEN
)
240 return QVariant::fromValue((bool)device
->hidden
);
243 /* Displays the configuration icon for extcap interfaces */
244 else if (role
== Qt::DecorationRole
)
246 if (col
== IFTREE_COL_EXTCAP
)
248 if (device
->if_info
.type
== IF_EXTCAP
)
249 return QIcon(StockIcon("x-capture-options"));
252 else if (role
== Qt::TextAlignmentRole
)
254 if (col
== IFTREE_COL_EXTCAP
)
256 return Qt::AlignRight
;
259 /* Displays the tooltip for each row */
260 else if (role
== Qt::ToolTipRole
)
262 return toolTipForInterface(row
);
273 QVariant
InterfaceTreeModel::headerData(int section
, Qt::Orientation orientation
, int role
) const
275 if (orientation
== Qt::Horizontal
)
277 if (role
== Qt::DisplayRole
)
279 if (section
== IFTREE_COL_HIDDEN
)
283 else if (section
== IFTREE_COL_NAME
)
285 return tr("Interface Name");
287 else if (section
== IFTREE_COL_DESCRIPTION
)
289 return tr("Friendly Name");
291 else if (section
== IFTREE_COL_DISPLAY_NAME
)
293 return tr("Friendly Name");
295 else if (section
== IFTREE_COL_PIPE_PATH
)
297 return tr("Local Pipe Path");
299 else if (section
== IFTREE_COL_COMMENT
)
301 return tr("Comment");
303 else if (section
== IFTREE_COL_DLT
)
305 return tr("Link-Layer Header");
307 else if (section
== IFTREE_COL_PROMISCUOUSMODE
)
309 return tr("Promiscuous");
311 else if (section
== IFTREE_COL_SNAPLEN
)
313 return tr("Snaplen (B)");
315 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
316 else if (section
== IFTREE_COL_BUFFERLEN
)
318 return tr("Buffer (MB)");
321 #ifdef HAVE_PCAP_CREATE
322 else if (section
== IFTREE_COL_MONITOR_MODE
)
324 return tr("Monitor Mode");
327 else if (section
== IFTREE_COL_CAPTURE_FILTER
)
329 return tr("Capture Filter");
337 QVariant
InterfaceTreeModel::getColumnContent(int idx
, int col
, int role
)
339 return InterfaceTreeModel::data(index(idx
, col
), role
);
342 #ifdef HAVE_PCAP_REMOTE
343 bool InterfaceTreeModel::isRemote(int idx
)
345 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
346 if (device
->remote_opts
.src_type
== CAPTURE_IFREMOTE
)
353 * The interface list has changed. global_capture_opts.all_ifaces may have been reloaded
354 * or changed with current data. beginResetModel() and endResetModel() will signalize the
355 * proxy model and the view, that the data has changed and the view has to reload
357 void InterfaceTreeModel::interfaceListChanged()
368 * Displays the tooltip code for the given device index.
370 QVariant
InterfaceTreeModel::toolTipForInterface(int idx
) const
373 if (! global_capture_opts
.all_ifaces
|| global_capture_opts
.all_ifaces
->len
<= (unsigned) idx
)
376 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
378 QString tt_str
= "<p>";
379 if (device
->no_addresses
> 0)
381 tt_str
+= QStringLiteral("%1: %2")
382 .arg(device
->no_addresses
> 1 ? tr("Addresses") : tr("Address"))
383 .arg(html_escape(device
->addresses
))
384 .replace('\n', ", ");
386 else if (device
->if_info
.type
== IF_EXTCAP
)
388 tt_str
= tr("Extcap interface: %1").arg(get_basename(device
->if_info
.extcap
));
392 tt_str
= tr("No addresses");
396 QString cfilter
= device
->cfilter
;
397 if (cfilter
.isEmpty())
399 tt_str
+= tr("No capture filter");
403 tt_str
+= QStringLiteral("%1: %2")
404 .arg(tr("Capture filter"))
405 .arg(html_escape(cfilter
));
418 void InterfaceTreeModel::setCache(if_stat_cache_t
*stat_cache
)
421 stat_cache_
= stat_cache
;
424 void InterfaceTreeModel::stopStatistic()
428 capture_stat_stop(stat_cache_
);
434 void InterfaceTreeModel::updateStatistic(unsigned int idx
)
437 if (! global_capture_opts
.all_ifaces
|| global_capture_opts
.all_ifaces
->len
<= (unsigned) idx
)
440 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
442 if (device
->if_info
.type
== IF_PIPE
|| device
->if_info
.type
== IF_EXTCAP
)
447 // Start gathering statistics using dumpcap
449 // The stat cache will only properly configure if it has the list
450 // of interfaces in global_capture_opts->all_ifaces.
451 // We crash if we try to do this from InterfaceFrame::showEvent,
452 // because main.cpp calls mainw->show() before capture_opts_init().
453 stat_cache_
= capture_stat_start(&global_capture_opts
);
456 struct pcap_stat stats
;
458 bool isActive
= false;
460 if (capture_stats(stat_cache_
, device
->name
, &stats
))
462 if ( (int) stats
.ps_recv
> 0 )
465 if ((int)(stats
.ps_recv
- device
->last_packets
) >= 0)
467 diff
= stats
.ps_recv
- device
->last_packets
;
468 device
->packet_diff
= diff
;
470 device
->last_packets
= stats
.ps_recv
;
473 points
[device
->name
].append(diff
);
475 if (active
[device
->name
] != isActive
)
477 emit
layoutAboutToBeChanged();
478 active
[device
->name
] = isActive
;
479 emit
layoutChanged();
482 emit
dataChanged(index(idx
, IFTREE_COL_STATS
), index(idx
, IFTREE_COL_STATS
));
489 QItemSelection
InterfaceTreeModel::selectedDevices()
491 QItemSelection mySelection
;
493 for (int idx
= 0; idx
< rowCount(); idx
++)
495 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
497 if (device
->selected
)
499 QModelIndex selectIndex
= index(idx
, 0);
501 QItemSelection(selectIndex
, index(selectIndex
.row(), columnCount() - 1)),
502 QItemSelectionModel::SelectCurrent
510 bool InterfaceTreeModel::updateSelectedDevices(QItemSelection sourceSelection
)
512 bool selectionHasChanged
= false;
514 QList
<int> selectedIndices
;
516 QItemSelection::const_iterator it
= sourceSelection
.constBegin();
517 while (it
!= sourceSelection
.constEnd())
519 QModelIndexList indeces
= ((QItemSelectionRange
) (*it
)).indexes();
521 QModelIndexList::const_iterator cit
= indeces
.constBegin();
522 while (cit
!= indeces
.constEnd())
524 QModelIndex index
= (QModelIndex
) (*cit
);
525 if (! selectedIndices
.contains(index
.row()))
527 selectedIndices
.append(index
.row());
534 global_capture_opts
.num_selected
= 0;
536 for (unsigned int idx
= 0; idx
< global_capture_opts
.all_ifaces
->len
; idx
++)
538 interface_t
*device
= &g_array_index(global_capture_opts
.all_ifaces
, interface_t
, idx
);
539 if (selectedIndices
.contains(idx
))
541 if (! device
->selected
)
542 selectionHasChanged
= true;
543 device
->selected
= true;
544 global_capture_opts
.num_selected
++;
546 if (device
->selected
)
547 selectionHasChanged
= true;
548 device
->selected
= false;
552 Q_UNUSED(sourceSelection
)
554 return selectionHasChanged
;