forked from osmocom/wireshark
383 lines
11 KiB
C++
383 lines
11 KiB
C++
/* packet_list_header.cpp
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include <QDropEvent>
|
|
#include <QMimeData>
|
|
#include <QToolTip>
|
|
#include <QAction>
|
|
#include <QInputDialog>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
|
|
#include <packet_list.h>
|
|
|
|
#include <wireshark_application.h>
|
|
#include <epan/column.h>
|
|
#include <ui/recent.h>
|
|
#include <ui/preference_utils.h>
|
|
#include <ui/packet_list_utils.h>
|
|
#include <ui/qt/main_window.h>
|
|
|
|
#include <models/packet_list_model.h>
|
|
#include <ui/qt/utils/wireshark_mime_data.h>
|
|
#include <ui/qt/widgets/packet_list_header.h>
|
|
|
|
PacketListHeader::PacketListHeader(Qt::Orientation orientation, capture_file * cap_file, QWidget *parent) :
|
|
QHeaderView(orientation, parent),
|
|
cap_file_(cap_file),
|
|
sectionIdx(-1),
|
|
lastSize(-1)
|
|
{
|
|
setAcceptDrops(true);
|
|
setSectionsMovable(true);
|
|
setStretchLastSection(true);
|
|
setDefaultAlignment(Qt::AlignLeft|Qt::AlignVCenter);
|
|
}
|
|
|
|
void PacketListHeader::dragEnterEvent(QDragEnterEvent *event)
|
|
{
|
|
if (! event || ! event->mimeData())
|
|
return;
|
|
|
|
if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType) && event->source() != this->parent())
|
|
{
|
|
if (event->source() != this)
|
|
{
|
|
event->setDropAction(Qt::CopyAction);
|
|
event->accept();
|
|
} else {
|
|
event->acceptProposedAction();
|
|
}
|
|
}
|
|
else
|
|
QHeaderView::dragEnterEvent(event);
|
|
}
|
|
|
|
void PacketListHeader::dragMoveEvent(QDragMoveEvent *event)
|
|
{
|
|
if (! event || ! event->mimeData())
|
|
return;
|
|
|
|
if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType))
|
|
{
|
|
if (event->source() != this)
|
|
{
|
|
event->setDropAction(Qt::CopyAction);
|
|
event->accept();
|
|
} else {
|
|
event->acceptProposedAction();
|
|
}
|
|
}
|
|
else
|
|
QHeaderView::dragMoveEvent(event);
|
|
}
|
|
|
|
void PacketListHeader::dropEvent(QDropEvent *event)
|
|
{
|
|
if (! event || ! event->mimeData())
|
|
return;
|
|
|
|
/* Moving items around */
|
|
if (event->mimeData()->hasFormat(WiresharkMimeData::DisplayFilterMimeType))
|
|
{
|
|
QByteArray jsonData = event->mimeData()->data(WiresharkMimeData::DisplayFilterMimeType);
|
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData);
|
|
if (! jsonDoc.isObject())
|
|
return;
|
|
|
|
QJsonObject data = jsonDoc.object();
|
|
|
|
if ( event->source() != this && data.contains("description") && data.contains("name") )
|
|
{
|
|
event->setDropAction(Qt::CopyAction);
|
|
event->accept();
|
|
|
|
MainWindow * mw = qobject_cast<MainWindow *>(wsApp->mainWindow());
|
|
if (mw)
|
|
{
|
|
int idx = logicalIndexAt(event->pos());
|
|
mw->insertColumn(data["description"].toString(), data["name"].toString(), idx);
|
|
}
|
|
|
|
} else {
|
|
event->acceptProposedAction();
|
|
}
|
|
}
|
|
else
|
|
QHeaderView::dropEvent(event);
|
|
}
|
|
|
|
void PacketListHeader::mousePressEvent(QMouseEvent *e)
|
|
{
|
|
if (e->button() == Qt::LeftButton && sectionIdx < 0)
|
|
{
|
|
/* No move happening yet */
|
|
int sectIdx = logicalIndexAt(e->localPos().x() - 4, e->localPos().y());
|
|
|
|
QString headerName = model()->headerData(sectIdx, orientation()).toString();
|
|
lastSize = sectionSize(sectIdx);
|
|
QToolTip::showText(e->globalPos(), QString("Width: %1").arg(sectionSize(sectIdx)));
|
|
}
|
|
QHeaderView::mousePressEvent(e);
|
|
}
|
|
|
|
void PacketListHeader::mouseMoveEvent(QMouseEvent *e)
|
|
{
|
|
if (e->button() == Qt::NoButton || ! (e->buttons() & Qt::LeftButton))
|
|
{
|
|
/* no move is happening */
|
|
sectionIdx = -1;
|
|
lastSize = -1;
|
|
}
|
|
else if (e->buttons() & Qt::LeftButton)
|
|
{
|
|
/* section being moved */
|
|
int triggeredSection = logicalIndexAt(e->localPos().x() - 4, e->localPos().y());
|
|
|
|
if (sectionIdx < 0)
|
|
sectionIdx = triggeredSection;
|
|
else if (sectionIdx == triggeredSection)
|
|
{
|
|
/* Only run for the current moving section after a change */
|
|
QString headerName = model()->headerData(sectionIdx, orientation()).toString();
|
|
lastSize = sectionSize(sectionIdx);
|
|
QToolTip::showText(e->globalPos(), QString("Width: %1").arg(lastSize));
|
|
}
|
|
}
|
|
QHeaderView::mouseMoveEvent(e);
|
|
}
|
|
|
|
void PacketListHeader::setCaptureFile(capture_file *cap_file)
|
|
{
|
|
this->cap_file_ = cap_file;
|
|
}
|
|
|
|
void PacketListHeader::contextMenuEvent(QContextMenuEvent *event)
|
|
{
|
|
int sectionIdx = logicalIndexAt(event->pos());
|
|
char xalign = recent_get_column_xalign(sectionIdx);
|
|
QAction * action = Q_NULLPTR;
|
|
QMenu * contextMenu = new QMenu(this);
|
|
contextMenu->setProperty("column", QVariant::fromValue(sectionIdx));
|
|
|
|
QActionGroup * alignmentActions = new QActionGroup(contextMenu);
|
|
alignmentActions->setExclusive(false);
|
|
alignmentActions->setProperty("column", QVariant::fromValue(sectionIdx));
|
|
action = alignmentActions->addAction(tr("Align Left"));
|
|
action->setCheckable(true);
|
|
action->setChecked(xalign == COLUMN_XALIGN_LEFT ? true : false);
|
|
action->setData(QVariant::fromValue(COLUMN_XALIGN_LEFT));
|
|
action = alignmentActions->addAction(tr("Align Center"));
|
|
action->setCheckable(true);
|
|
action->setChecked(xalign == COLUMN_XALIGN_CENTER ? true : false);
|
|
action->setData(QVariant::fromValue(COLUMN_XALIGN_CENTER));
|
|
action = alignmentActions->addAction(tr("Align Right"));
|
|
action->setCheckable(true);
|
|
action->setChecked(xalign == COLUMN_XALIGN_RIGHT ? true : false);
|
|
action->setData(QVariant::fromValue(COLUMN_XALIGN_RIGHT));
|
|
connect(alignmentActions, &QActionGroup::triggered, this, &PacketListHeader::setAlignment);
|
|
|
|
contextMenu->addActions(alignmentActions->actions());
|
|
contextMenu->addSeparator();
|
|
|
|
action = contextMenu->addAction(tr("Column Preferences…"));
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::showColumnPrefs);
|
|
action = contextMenu->addAction(tr("Edit Column"));
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::doEditColumn);
|
|
action = contextMenu->addAction(tr("Resize to Contents"));
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::resizeToContent);
|
|
action = contextMenu->addAction(tr("Resize Column to Width…"));
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::resizeToWidth);
|
|
|
|
action = contextMenu->addAction(tr("Resolve Names"));
|
|
bool canResolve = resolve_column(sectionIdx, cap_file_);
|
|
action->setEnabled(canResolve);
|
|
action->setChecked(canResolve && get_column_resolved(sectionIdx));
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::doResolveNames);
|
|
|
|
contextMenu->addSeparator();
|
|
|
|
for (int cnt = 0; cnt < prefs.num_cols; cnt++) {
|
|
QString title(get_column_title(cnt));
|
|
QString detail;
|
|
if (get_column_format(cnt) == COL_CUSTOM) {
|
|
detail = get_column_custom_fields(cnt);
|
|
} else {
|
|
detail = col_format_desc(get_column_format(cnt));
|
|
}
|
|
|
|
if (prefs.gui_qt_packet_header_column_definition)
|
|
title.append(QString("\t%1").arg(detail));
|
|
|
|
QAction *action = new QAction(title, this);
|
|
action->setToolTip(detail);
|
|
action->setCheckable(true);
|
|
action->setChecked(get_column_visible(cnt));
|
|
action->setData(QVariant::fromValue(cnt));
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::columnVisibilityTriggered);
|
|
contextMenu->addAction(action);
|
|
}
|
|
contextMenu->setToolTipsVisible(true);
|
|
|
|
contextMenu->addSeparator();
|
|
|
|
action = contextMenu->addAction(tr("Remove this Column"));
|
|
action->setEnabled(sectionIdx >= 0 && count() > 2);
|
|
connect(action, &QAction::triggered, this, &PacketListHeader::removeColumn);
|
|
|
|
contextMenu->popup(viewport()->mapToGlobal(event->pos()));
|
|
}
|
|
|
|
void PacketListHeader::setSectionVisibility()
|
|
{
|
|
for (int cnt = 0; cnt < prefs.num_cols; cnt++)
|
|
setSectionHidden(cnt, get_column_visible(cnt) ? false : true);
|
|
}
|
|
|
|
void PacketListHeader::columnVisibilityTriggered()
|
|
{
|
|
QAction *ha = qobject_cast<QAction*>(sender());
|
|
if (!ha) return;
|
|
|
|
int col = ha->data().toInt();
|
|
set_column_visible(col, ha->isChecked());
|
|
setSectionVisibility();
|
|
if (ha->isChecked())
|
|
emit resetColumnWidth(col);
|
|
|
|
prefs_main_write();
|
|
}
|
|
|
|
void PacketListHeader::setAlignment(QAction *action)
|
|
{
|
|
if (!action)
|
|
return;
|
|
|
|
QActionGroup * group = action->actionGroup();
|
|
if (! group)
|
|
return;
|
|
|
|
int section = group->property("column").toInt();
|
|
if (section >= 0)
|
|
{
|
|
QChar data = action->data().toChar();
|
|
recent_set_column_xalign(section, action->isChecked() ? data.toLatin1() : COLUMN_XALIGN_DEFAULT);
|
|
emit updatePackets(false);
|
|
}
|
|
}
|
|
|
|
void PacketListHeader::showColumnPrefs()
|
|
{
|
|
emit showColumnPreferences(PrefsModel::typeToString(PrefsModel::Columns));
|
|
}
|
|
|
|
void PacketListHeader::doEditColumn()
|
|
{
|
|
QAction * action = qobject_cast<QAction *>(sender());
|
|
if (!action)
|
|
return;
|
|
|
|
QMenu * menu = qobject_cast<QMenu *>(action->parent());
|
|
if (! menu)
|
|
return;
|
|
|
|
int section = menu->property("column").toInt();
|
|
emit editColumn(section);
|
|
}
|
|
|
|
void PacketListHeader::doResolveNames()
|
|
{
|
|
QAction * action = qobject_cast<QAction *>(sender());
|
|
if (!action)
|
|
return;
|
|
|
|
QMenu * menu = qobject_cast<QMenu *>(action->parent());
|
|
if (!menu)
|
|
return;
|
|
|
|
PacketListModel * plmModel = qobject_cast<PacketListModel *>(model());
|
|
if (!plmModel)
|
|
return;
|
|
|
|
int section = menu->property("column").toInt();
|
|
|
|
set_column_resolved(section, action->isChecked());
|
|
plmModel->resetColumns();
|
|
prefs_main_write();
|
|
emit updatePackets(true);
|
|
}
|
|
|
|
void PacketListHeader::resizeToContent()
|
|
{
|
|
QAction * action = qobject_cast<QAction *>(sender());
|
|
if (!action)
|
|
return;
|
|
|
|
QMenu * menu = qobject_cast<QMenu *>(action->parent());
|
|
if (!menu)
|
|
return;
|
|
|
|
int section = menu->property("column").toInt();
|
|
PacketList * packetList = qobject_cast<PacketList *>(parent());
|
|
if (packetList)
|
|
packetList->resizeColumnToContents(section);
|
|
}
|
|
|
|
void PacketListHeader::removeColumn()
|
|
{
|
|
QAction * action = qobject_cast<QAction *>(sender());
|
|
if (!action)
|
|
return;
|
|
|
|
QMenu * menu = qobject_cast<QMenu *>(action->parent());
|
|
if (!menu)
|
|
return;
|
|
|
|
int section = menu->property("column").toInt();
|
|
|
|
if (count() > 2) {
|
|
column_prefs_remove_nth(section);
|
|
emit columnsChanged();
|
|
prefs_main_write();
|
|
}
|
|
}
|
|
|
|
void PacketListHeader::resizeToWidth()
|
|
{
|
|
QAction * action = qobject_cast<QAction *>(sender());
|
|
if (!action)
|
|
return;
|
|
|
|
QMenu * menu = qobject_cast<QMenu *>(action->parent());
|
|
if (!menu)
|
|
return;
|
|
|
|
bool ok = false;
|
|
int width = -1;
|
|
int section = menu->property("column").toInt();
|
|
QString headerName = model()->headerData(section, orientation()).toString();
|
|
width = QInputDialog::getInt(this, tr("Column %1").arg(headerName), tr("Width:"),
|
|
sectionSize(section), 0, 1000, 1, &ok);
|
|
if (ok)
|
|
resizeSection(section, width);
|
|
}
|
|
|
|
/*
|
|
* 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:
|
|
*/
|