1 /* interface_tree_cache_model.cpp
2 * Model caching interface changes before sending them to global storage
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <ui/qt/models/interface_tree_cache_model.h>
28 #include "epan/prefs.h"
30 #include <ui/qt/utils/qt_ui_utils.h>
31 #include "ui/capture_globals.h"
32 #include "wsutil/utf8_entities.h"
34 #include "wiretap/wtap.h"
36 #include "wireshark_application.h"
38 #include <QIdentityProxyModel>
40 InterfaceTreeCacheModel::InterfaceTreeCacheModel(QObject *parent) :
41 QIdentityProxyModel(parent)
43 /* ATTENTION: This cache model is not intended to be used with anything
44 * else then InterfaceTreeModel, and will break with anything else
45 * leading to unintended results. */
46 sourceModel = new InterfaceTreeModel(parent);
48 QIdentityProxyModel::setSourceModel(sourceModel);
49 storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>();
51 checkableColumns << IFTREE_COL_HIDDEN << IFTREE_COL_PROMISCUOUSMODE;
52 #ifdef HAVE_PCAP_CREATE
53 checkableColumns << IFTREE_COL_MONITOR_MODE;
56 editableColumns << IFTREE_COL_INTERFACE_COMMENT << IFTREE_COL_SNAPLEN << IFTREE_COL_PIPE_PATH;
58 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
59 editableColumns << IFTREE_COL_BUFFERLEN;
63 InterfaceTreeCacheModel::~InterfaceTreeCacheModel()
66 /* This list should only exist, if the dialog is closed, without calling save first */
74 QVariant InterfaceTreeCacheModel::getColumnContent(int idx, int col, int role)
76 return InterfaceTreeCacheModel::data(index(idx, col), role);
80 void InterfaceTreeCacheModel::reset(int row)
85 storage = new QMap<int, QMap<InterfaceTreeColumns, QVariant> *>();
89 if ( storage->count() > row )
90 storage->remove(storage->keys().at(row));
94 void InterfaceTreeCacheModel::saveNewDevices()
96 QList<interface_t>::const_iterator it = newDevices.constBegin();
97 /* idx is used for iterating only over the indices of the new devices. As all new
98 * devices are stored with an index higher then sourceModel->rowCount(), we start
99 * only with those storage indices.
100 * it is just the iterator over the new devices. A new device must not necessarily
101 * have storage, which will lead to that device not being stored in global_capture_opts */
102 for (int idx = sourceModel->rowCount(); it != newDevices.constEnd(); ++it, idx++)
104 interface_t *device = const_cast<interface_t *>(&(*it));
105 bool useDevice = false;
107 QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0);
108 /* When devices are being added, they are added using generic values. So only devices
109 * whose data have been changed should be used from here on out. */
110 if ( dataField != 0 )
112 if ( device->if_info.type != IF_PIPE )
117 if ( device->if_info.type == IF_PIPE )
119 QVariant saveValue = dataField->value(IFTREE_COL_PIPE_PATH);
120 if ( saveValue.isValid() )
122 g_free(device->if_info.name);
123 device->if_info.name = qstring_strdup(saveValue.toString());
125 g_free(device->name);
126 device->name = qstring_strdup(saveValue.toString());
128 g_free(device->display_name);
129 device->display_name = qstring_strdup(saveValue.toString());
135 g_array_append_val(global_capture_opts.all_ifaces, *device);
139 /* All entries of this new devices have been considered */
140 storage->remove(idx);
147 void InterfaceTreeCacheModel::save()
149 if ( storage->count() == 0 )
152 QMap<char**, QStringList> prefStorage;
154 /* No devices are hidden until checking "Show" state */
155 prefStorage[&prefs.capture_devices_hide] = QStringList();
157 /* Storing new devices first including their changed values */
161 for(unsigned int idx = 0; idx < global_capture_opts.all_ifaces->len; idx++)
163 interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
168 /* Try to load a saved value row for this index */
169 QMap<InterfaceTreeColumns, QVariant> * dataField = storage->value(idx, 0);
171 /* Handle the storing of values for this device here */
174 QMap<InterfaceTreeColumns, QVariant>::const_iterator it = dataField->constBegin();
175 while ( it != dataField->constEnd() )
177 InterfaceTreeColumns col = it.key();
178 QVariant saveValue = it.value();
180 /* Setting the field values for each individual saved value cannot be generic, as the
181 * struct cannot be accessed in a generic way. Therefore below, each individually changed
182 * value has to be handled separately. Comments are stored only in the preference file
183 * and applied to the data name during loading. Therefore comments are not handled here */
185 if ( col == IFTREE_COL_HIDDEN )
187 device->hidden = saveValue.toBool();
189 else if ( device->if_info.type == IF_EXTCAP )
191 /* extcap interfaces do not have the following columns.
192 * ATTENTION: all generic columns must be added, BEFORE this
193 * if-clause, or they will be ignored for extcap interfaces */
195 else if ( col == IFTREE_COL_PROMISCUOUSMODE )
197 device->pmode = saveValue.toBool();
199 #ifdef HAVE_PCAP_CREATE
200 else if ( col == IFTREE_COL_MONITOR_MODE )
202 device->monitor_mode_enabled = saveValue.toBool();
205 else if ( col == IFTREE_COL_SNAPLEN )
207 int iVal = saveValue.toInt();
208 if ( iVal != WTAP_MAX_PACKET_SIZE_STANDARD )
210 device->has_snaplen = true;
211 device->snaplen = iVal;
215 device->has_snaplen = false;
216 device->snaplen = WTAP_MAX_PACKET_SIZE_STANDARD;
219 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
220 else if ( col == IFTREE_COL_BUFFERLEN )
222 device->buffer = saveValue.toInt();
229 QVariant content = getColumnContent(idx, IFTREE_COL_HIDDEN, Qt::CheckStateRole);
230 if ( content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Unchecked )
231 prefStorage[&prefs.capture_devices_hide] << QString(device->name);
233 content = getColumnContent(idx, IFTREE_COL_INTERFACE_COMMENT);
234 if ( content.isValid() && content.toString().size() > 0 )
235 prefStorage[&prefs.capture_devices_descr] << QString("%1(%2)").arg(device->name).arg(content.toString());
237 bool allowExtendedColumns = true;
239 if ( device->if_info.type == IF_EXTCAP )
240 allowExtendedColumns = false;
242 if ( allowExtendedColumns )
244 content = getColumnContent(idx, IFTREE_COL_PROMISCUOUSMODE, Qt::CheckStateRole);
245 if ( content.isValid() )
247 bool value = static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked;
248 prefStorage[&prefs.capture_devices_pmode] << QString("%1(%2)").arg(device->name).arg(value ? 1 : 0);
251 #ifdef HAVE_PCAP_CREATE
252 content = getColumnContent(idx, IFTREE_COL_MONITOR_MODE, Qt::CheckStateRole);
253 if ( content.isValid() && static_cast<Qt::CheckState>(content.toInt()) == Qt::Checked )
254 prefStorage[&prefs.capture_devices_monitor_mode] << QString(device->name);
257 content = getColumnContent(idx, IFTREE_COL_SNAPLEN);
258 if ( content.isValid() )
260 int value = content.toInt();
261 prefStorage[&prefs.capture_devices_snaplen] <<
262 QString("%1:%2(%3)").arg(device->name).
263 arg(device->has_snaplen ? 1 : 0).
264 arg(device->has_snaplen ? value : WTAP_MAX_PACKET_SIZE_STANDARD);
267 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
268 content = getColumnContent(idx, IFTREE_COL_BUFFERLEN);
269 if ( content.isValid() )
271 int value = content.toInt();
274 prefStorage[&prefs.capture_devices_buffersize] <<
275 QString("%1(%2)").arg(device->name).
283 QMap<char**, QStringList>::const_iterator it = prefStorage.constBegin();
284 while ( it != prefStorage.constEnd() )
286 char ** key = it.key();
289 *key = qstring_strdup(it.value().join(","));
294 wsApp->emitAppSignal(WiresharkApplication::LocalInterfacesChanged);
298 int InterfaceTreeCacheModel::rowCount(const QModelIndex & parent) const
300 int totalCount = sourceModel->rowCount(parent);
302 totalCount += newDevices.size();
307 bool InterfaceTreeCacheModel::changeIsAllowed(InterfaceTreeColumns col) const
309 if ( editableColumns.contains(col) || checkableColumns.contains(col) )
315 const interface_t * InterfaceTreeCacheModel::lookup(const QModelIndex &index) const
317 const interface_t * result = 0;
319 if ( ! index.isValid() )
321 if ( ! global_capture_opts.all_ifaces && newDevices.size() == 0 )
324 int idx = index.row();
326 if ( (unsigned int) idx >= global_capture_opts.all_ifaces->len )
328 idx = idx - global_capture_opts.all_ifaces->len;
329 if ( idx < newDevices.size() )
330 result = &newDevices[idx];
334 result = &g_array_index(global_capture_opts.all_ifaces, interface_t, idx);
341 /* This checks if the column can be edited for the given index. This differs from
342 * isAvailableField in such a way, that it is only used in flags and not any
344 bool InterfaceTreeCacheModel::isAllowedToBeEdited(const QModelIndex &index) const
349 const interface_t * device = lookup(index);
353 InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
354 if ( device->if_info.type == IF_EXTCAP )
356 /* extcap interfaces do not have those settings */
357 if ( col == IFTREE_COL_PROMISCUOUSMODE || col == IFTREE_COL_SNAPLEN )
359 #ifdef CAN_SET_CAPTURE_BUFFER_SIZE
360 if ( col == IFTREE_COL_BUFFERLEN )
368 // Whether this field is available for modification and display.
369 bool InterfaceTreeCacheModel::isAvailableField(const QModelIndex &index) const
374 const interface_t * device = lookup(index);
379 InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
380 if ( col == IFTREE_COL_HIDDEN )
382 // Do not allow default capture interface to be hidden.
383 if ( ! g_strcmp0(prefs.capture_device, device->display_name) )
391 Qt::ItemFlags InterfaceTreeCacheModel::flags(const QModelIndex &index) const
393 if ( ! index.isValid() )
396 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
398 InterfaceTreeColumns col = (InterfaceTreeColumns) index.column();
400 if ( changeIsAllowed(col) && isAvailableField(index) && isAllowedToBeEdited(index) )
402 if ( checkableColumns.contains(col) )
404 flags |= Qt::ItemIsUserCheckable;
408 flags |= Qt::ItemIsEditable;
415 bool InterfaceTreeCacheModel::setData(const QModelIndex &index, const QVariant &value, int role)
417 if ( ! index.isValid() )
420 if ( ! isAvailableField(index) )
423 int row = index.row();
424 InterfaceTreeColumns col = (InterfaceTreeColumns)index.column();
426 if ( role == Qt::CheckStateRole || role == Qt::EditRole )
428 if ( changeIsAllowed( col ) )
430 QVariant saveValue = value;
432 QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
433 /* obtain the list of already stored changes for this row. If none exist
434 * create a new storage row for this entry */
435 if ( ( dataField = storage->value(row, 0) ) == 0 )
437 dataField = new QMap<InterfaceTreeColumns, QVariant>();
438 storage->insert(row, dataField);
441 dataField->insert(col, saveValue);
450 QVariant InterfaceTreeCacheModel::data(const QModelIndex &index, int role) const
452 if ( ! index.isValid() )
455 int row = index.row();
457 InterfaceTreeColumns col = (InterfaceTreeColumns)index.column();
459 if ( isAvailableField(index) && isAllowedToBeEdited(index) )
461 if ( ( ( role == Qt::DisplayRole || role == Qt::EditRole ) && editableColumns.contains(col) ) ||
462 ( role == Qt::CheckStateRole && checkableColumns.contains(col) ) )
464 QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
465 if ( ( dataField = storage->value(row, 0) ) != 0 )
467 if ( dataField->contains(col) )
469 return dataField->value(col, QVariant());
476 if ( role == Qt::CheckStateRole )
478 else if ( role == Qt::DisplayRole )
479 return QString(UTF8_EM_DASH);
482 if ( row < sourceModel->rowCount() )
484 return sourceModel->data(index, role);
489 /* Handle all fields, which will have to be displayed for new devices. Only pipes
490 * are supported at the moment, so the information to be displayed is pretty limited.
491 * After saving, the devices are stored in global_capture_opts and no longer
492 * classify as new devices. */
493 const interface_t * device = lookup(index);
497 if ( role == Qt::DisplayRole || role == Qt::EditRole )
499 if ( col == IFTREE_COL_PIPE_PATH ||
500 col == IFTREE_COL_NAME ||
501 col == IFTREE_COL_INTERFACE_NAME )
504 QMap<InterfaceTreeColumns, QVariant> * dataField = 0;
505 if ( ( dataField = storage->value(row, 0) ) != 0 &&
506 dataField->contains(IFTREE_COL_PIPE_PATH) )
508 return dataField->value(IFTREE_COL_PIPE_PATH, QVariant());
511 return QString(device->name);
513 else if ( col == IFTREE_COL_TYPE )
515 return QVariant::fromValue((int)device->if_info.type);
518 else if ( role == Qt::CheckStateRole )
520 if ( col == IFTREE_COL_HIDDEN )
522 // Do not allow default capture interface to be hidden.
523 if ( ! g_strcmp0(prefs.capture_device, device->display_name) )
526 /* Hidden is a de-selection, therefore inverted logic here */
527 return device->hidden ? Qt::Unchecked : Qt::Checked;
538 QModelIndex InterfaceTreeCacheModel::index(int row, int column, const QModelIndex &parent) const
540 if ( row >= sourceModel->rowCount() && ( row - sourceModel->rowCount() ) < newDevices.count() )
542 return createIndex(row, column, (void *)0);
545 return sourceModel->index(row, column, parent);
548 void InterfaceTreeCacheModel::addDevice(const interface_t * newDevice)
550 emit beginInsertRows(QModelIndex(), rowCount(), rowCount());
551 newDevices << *newDevice;
552 emit endInsertRows();
555 void InterfaceTreeCacheModel::deleteDevice(const QModelIndex &index)
557 if ( ! index.isValid() )
560 emit beginRemoveRows(QModelIndex(), index.row(), index.row());
562 int row = index.row();
564 /* device is in newDevices */
565 if ( row >= sourceModel->rowCount() )
567 int newDeviceIdx = row - sourceModel->rowCount();
569 newDevices.removeAt(newDeviceIdx);
570 if ( storage->contains(index.row()) )
571 storage->remove(index.row());
573 /* The storage array has to be resorted, if the index, that was removed
574 * had been in the middle of the array. Can't start at index.row(), as
575 * it may not be contained in storage
576 * We must iterate using a list, not an iterator, otherwise the change
577 * will fold on itself. */
578 QList<int> storageKeys = storage->keys();
579 for ( int i = 0; i < storageKeys.size(); ++i )
581 int key = storageKeys.at(i);
582 if ( key > index.row() )
584 storage->insert(key - 1, storage->value(key));
585 storage->remove(key);
589 emit endRemoveRows();
593 interface_t *device = &g_array_index(global_capture_opts.all_ifaces, interface_t, row);
594 capture_opts_free_interface_t(device);
595 global_capture_opts.all_ifaces = g_array_remove_index(global_capture_opts.all_ifaces, row);
596 emit endRemoveRows();
597 wsApp->emitAppSignal(WiresharkApplication::LocalInterfacesChanged);
608 * indent-tabs-mode: nil
611 * ex: set shiftwidth=4 tabstop=8 expandtab:
612 * :indentSize=4:tabSize=8:noTabs=true: