wireshark/ui/qt/widgets/packet_list_header.cpp
Tomasz Moń 1d20a875e4 Qt: Fix bug causing hidden columns to be displayed
Set capture file for model and header before columnsChanged() is called
to prevent skipping model reset in PacketListModel::resetColumns() due
to cap_file_ being NULL.

Do not strech last section in packet list header. This prevents
QHeaderViewPrivate::resizeSections() from messing up the column sizes
via resize events. For some reason (unknown to me) underlying
QHeaderView implementation has different idea about the number of
visible columns than our code.

Ping-Bug: 16063
Change-Id: I482c1080adb418b7922ee99d357d4962dc086026
Reviewed-on: https://code.wireshark.org/review/36120
Petri-Dish: Tomasz Moń <desowin@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Roland Knall <rknall@gmail.com>
2020-03-15 12:07:06 +00:00

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(false);
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" UTF8_HORIZONTAL_ELLIPSIS));
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" UTF8_HORIZONTAL_ELLIPSIS));
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:
*/