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
12 #include "manuf_dialog.h"
13 #include <ui_manuf_dialog.h>
18 #include <QStandardItemModel>
19 #include <QPushButton>
20 #include <QRegularExpression>
23 #include <QButtonGroup>
26 #include "main_application.h"
27 #include <epan/manuf.h>
28 #include <epan/strutil.h>
29 #include <wsutil/regex.h>
30 #include <utils/qt_ui_utils.h>
32 #define PLACEHOLDER_SEARCH_ADDR "Search address"
33 #define PLACEHOLDER_SEARCH_NAME "Search name"
35 ManufDialog::ManufDialog(QWidget
&parent
, CaptureFile
&cf
) :
36 WiresharkDialog(parent
, cf
),
37 ui(new Ui::ManufDialog
)
42 model_
= new ManufTableModel(this);
43 proxy_model_
= new ManufSortFilterProxyModel(this);
44 proxy_model_
->setSourceModel(model_
);
46 ui
->manufTableView
->setModel(proxy_model_
);
47 ui
->manufTableView
->setContextMenuPolicy(Qt::ActionsContextMenu
);
48 ui
->manufTableView
->setColumnHidden(ManufTableModel::COL_SHORT_NAME
, true);
50 QAction
*select_action
= new QAction(tr("Select all"), ui
->manufTableView
);
51 ui
->manufTableView
->addAction(select_action
);
52 connect(select_action
, &QAction::triggered
, ui
->manufTableView
, &QTableView::selectAll
);
54 QAction
*copy_action
= new QAction(tr("Copy"), ui
->manufTableView
);
55 ui
->manufTableView
->addAction(copy_action
);
56 connect(copy_action
, &QAction::triggered
, this, &ManufDialog::copyToClipboard
);
58 QPushButton
*find_button
= ui
->buttonBox
->addButton(tr("Find"), QDialogButtonBox::ActionRole
);
59 find_button
->setDefault(true);
60 connect(find_button
, &QPushButton::clicked
, this, &ManufDialog::on_editingFinished
);
62 QPushButton
*clear_button
= ui
->buttonBox
->addButton(tr("Clear"), QDialogButtonBox::ActionRole
);
63 connect(clear_button
, &QPushButton::clicked
, this, &ManufDialog::clearFilter
);
65 QPushButton
*copy_button
= ui
->buttonBox
->addButton(tr("Copy"), QDialogButtonBox::ApplyRole
);
66 connect(copy_button
, &QPushButton::clicked
, this, &ManufDialog::copyToClipboard
);
68 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
69 connect(ui
->radioButtonGroup
, &QButtonGroup::buttonClicked
, this, &ManufDialog::on_searchToggled
);
70 connect(ui
->radioButtonGroup
, &QButtonGroup::buttonClicked
, this, &ManufDialog::on_editingFinished
);
72 connect(ui
->radioButtonGroup
, QOverload
<QAbstractButton
*>::of(&QButtonGroup::buttonClicked
), this, &ManufDialog::on_searchToggled
);
73 connect(ui
->radioButtonGroup
, QOverload
<QAbstractButton
*>::of(&QButtonGroup::buttonClicked
), this, &ManufDialog::on_editingFinished
);
75 #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
76 connect(ui
->checkShortNameButton
, &QCheckBox::checkStateChanged
, this, &ManufDialog::shortNameStateChanged
);
78 connect(ui
->checkShortNameButton
, &QCheckBox::stateChanged
, this, &ManufDialog::shortNameStateChanged
);
81 ui
->manufLineEdit
->setPlaceholderText(tr(PLACEHOLDER_SEARCH_ADDR
));
83 ui
->hintLabel
->clear();
86 ManufDialog::~ManufDialog()
91 void ManufDialog::searchVendor(QString
&text
)
93 QRegularExpression name_re
;
95 name_re
= QRegularExpression(text
, QRegularExpression::CaseInsensitiveOption
);
96 if (!name_re
.isValid()) {
97 ui
->hintLabel
->setText(QStringLiteral("<small><i>Invalid regular expression: %1</i></small>").arg(name_re
.errorString()));
101 proxy_model_
->setFilterName(name_re
);
102 ui
->hintLabel
->setText(QStringLiteral("<small><i>Found %1 matches for \"%2\"</i></small>").arg(proxy_model_
->rowCount()).arg(text
));
105 static QByteArray
convertMacAddressToByteArray(const QString
&bytesString
)
107 GByteArray
*bytes
= g_byte_array_new();
109 if (!hex_str_to_bytes(qUtf8Printable(bytesString
), bytes
, false)
110 || bytes
->len
== 0 || bytes
->len
> 6) {
111 g_byte_array_free(bytes
, true);
115 /* Mask out multicast/locally administered flags. */
116 bytes
->data
[0] &= 0xFC;
118 return gbytearray_free_to_qbytearray(bytes
);
121 QString
convertToMacAddress(const QByteArray
& byteArray
) {
123 for (int i
= 0; i
< byteArray
.size(); ++i
) {
124 macAddress
+= QStringLiteral("%1").arg(static_cast<quint8
>(byteArray
[i
]), 2, 16, QChar('0'));
125 if (i
!= byteArray
.size() - 1) {
129 return macAddress
.toUpper();
132 void ManufDialog::searchPrefix(QString
&text
)
136 addr
= convertMacAddressToByteArray(text
);
137 if (addr
.isEmpty()) {
138 ui
->hintLabel
->setText(QStringLiteral("<small><i>\"%1\" is not a valid MAC address</i></small>").arg(text
));
142 proxy_model_
->setFilterAddress(addr
);
143 ui
->hintLabel
->setText(QStringLiteral("<small><i>Found %1 matches for \"%2\"</i></small>").arg(proxy_model_
->rowCount()).arg(convertToMacAddress(addr
)));
146 void ManufDialog::on_searchToggled(void)
148 if (ui
->ouiRadioButton
->isChecked())
149 ui
->manufLineEdit
->setPlaceholderText(tr(PLACEHOLDER_SEARCH_ADDR
));
150 else if (ui
->vendorRadioButton
->isChecked())
151 ui
->manufLineEdit
->setPlaceholderText(tr(PLACEHOLDER_SEARCH_NAME
));
153 ws_assert_not_reached();
156 void ManufDialog::on_editingFinished(void)
158 QString text
= ui
->manufLineEdit
->text();
163 if (ui
->ouiRadioButton
->isChecked())
165 else if (ui
->vendorRadioButton
->isChecked())
168 ws_assert_not_reached();
171 #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0)
172 void ManufDialog::shortNameStateChanged(Qt::CheckState state
)
174 void ManufDialog::shortNameStateChanged(int state
)
177 ui
->manufTableView
->setColumnHidden(ManufTableModel::COL_SHORT_NAME
, state
== Qt::Checked
? false : true);
180 void ManufDialog::clearFilter()
182 proxy_model_
->clearFilter();
183 ui
->manufLineEdit
->clear();
184 ui
->hintLabel
->clear();
187 void ManufDialog::copyToClipboard() {
188 QModelIndexList selectedIndexes
= ui
->manufTableView
->selectionModel()->selectedIndexes();
190 std::sort(selectedIndexes
.begin(), selectedIndexes
.end(), [](const QModelIndex
&a
, const QModelIndex
&b
) {
191 return a
.row() < b
.row() || (a
.row() == b
.row() && a
.column() < b
.column());
194 QAbstractItemModel
*model
= ui
->manufTableView
->model();
197 int previousRow
= -1;
199 for (const QModelIndex
& selectedIndex
: selectedIndexes
) {
200 // If the row changed, add a newline character
201 if (selectedIndex
.row() != previousRow
) {
202 if (!copiedData
.isEmpty()) {
205 previousRow
= selectedIndex
.row();
208 // If not the first column in the row, add a tab character
212 // Add the cell data to the string
213 copiedData
+= model
->data(selectedIndex
).toString();
216 QClipboard
*clipboard
= QApplication::clipboard();
217 clipboard
->setText(copiedData
);