forked from osmocom/wireshark
Qt: Handle UAT editor dialogs explicitly.
QAbstractItemView assumes that editors are widgets, not windows. QAbstractItemView::edit calls QAbstractItemViewPrivate::openEditor, which simply calls show() and setFocus() on the editor widget. If that widget happens to be a native dialog, its event loop might not be processed. This is the case on macOS at least. Create widgets derived from QLineEdit that edit the value directly along with a button that can open the associated modal dialog. Install event filters so that we keep the correct tab behavior between fields of the UAT. Bug: 13958 Ping-Bug: 14031 Bug: 7761 Change-Id: Ie5f0a5cbde33bb9add8217029c2063a0bbfd804a Reviewed-on: https://code.wireshark.org/review/23015 Reviewed-by: Gerald Combs <gerald@wireshark.org> Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot Reviewed-by: Roland Knall <rknall@gmail.com>
This commit is contained in:
parent
9a5217bdd4
commit
f789736521
|
@ -23,6 +23,9 @@
|
||||||
#include <ui/qt/widgets/editor_file_dialog.h>
|
#include <ui/qt/widgets/editor_file_dialog.h>
|
||||||
#include <ui/qt/widgets/editor_color_dialog.h>
|
#include <ui/qt/widgets/editor_color_dialog.h>
|
||||||
|
|
||||||
|
// The Qt docs suggest overriding updateEditorGeometry, but the
|
||||||
|
// defaults seem sane.
|
||||||
|
|
||||||
UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
UatDelegate::UatDelegate(QObject *parent) : QStyledItemDelegate(parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -42,12 +45,10 @@ QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &
|
||||||
case PT_TXTMOD_DIRECTORYNAME:
|
case PT_TXTMOD_DIRECTORYNAME:
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
QString filename_old = index.model()->data(index, Qt::EditRole).toString();
|
QString filename_old = index.model()->data(index, Qt::EditRole).toString();
|
||||||
EditorFileDialog* fileDialog = new EditorFileDialog(index, parent, QString(field->title), filename_old);
|
EditorFileDialog* fileDialog = new EditorFileDialog(index, EditorFileDialog::Directory, parent, QString(field->title), filename_old);
|
||||||
|
|
||||||
fileDialog->setFileMode(QFileDialog::DirectoryOnly);
|
|
||||||
|
|
||||||
//Use signals to accept data from cell
|
//Use signals to accept data from cell
|
||||||
connect(fileDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyDirectory(const QModelIndex&)));
|
connect(fileDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyFilename(const QModelIndex&)));
|
||||||
return fileDialog;
|
return fileDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,9 +58,8 @@ QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &
|
||||||
case PT_TXTMOD_FILENAME:
|
case PT_TXTMOD_FILENAME:
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
QString filename_old = index.model()->data(index, Qt::EditRole).toString();
|
QString filename_old = index.model()->data(index, Qt::EditRole).toString();
|
||||||
EditorFileDialog* fileDialog = new EditorFileDialog(index, parent, QString(field->title), filename_old);
|
EditorFileDialog* fileDialog = new EditorFileDialog(index, EditorFileDialog::ExistingFile, parent, QString(field->title), filename_old);
|
||||||
|
|
||||||
fileDialog->setFileMode(QFileDialog::ExistingFile);
|
|
||||||
fileDialog->setOption(QFileDialog::DontConfirmOverwrite);
|
fileDialog->setOption(QFileDialog::DontConfirmOverwrite);
|
||||||
|
|
||||||
//Use signals to accept data from cell
|
//Use signals to accept data from cell
|
||||||
|
@ -73,9 +73,7 @@ QWidget *UatDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &
|
||||||
case PT_TXTMOD_COLOR:
|
case PT_TXTMOD_COLOR:
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
QColor color(index.model()->data(index, Qt::DecorationRole).toString());
|
QColor color(index.model()->data(index, Qt::DecorationRole).toString());
|
||||||
EditorColorDialog *colorDialog = new EditorColorDialog(index, color, new QWidget(parent));
|
EditorColorDialog *colorDialog = new EditorColorDialog(index, color, parent);
|
||||||
|
|
||||||
colorDialog->setWindowFlags(Qt::Window);
|
|
||||||
|
|
||||||
//Use signals to accept data from cell
|
//Use signals to accept data from cell
|
||||||
connect(colorDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyColor(const QModelIndex &)));
|
connect(colorDialog, SIGNAL(acceptEdit(const QModelIndex &)), this, SLOT(applyColor(const QModelIndex &)));
|
||||||
|
@ -179,8 +177,6 @@ void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||||
model->setData(index, data, Qt::EditRole);
|
model->setData(index, data, Qt::EditRole);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PT_TXTMOD_DIRECTORYNAME:
|
|
||||||
case PT_TXTMOD_FILENAME:
|
|
||||||
case PT_TXTMOD_COLOR:
|
case PT_TXTMOD_COLOR:
|
||||||
//do nothing, dialog signals will update table
|
//do nothing, dialog signals will update table
|
||||||
break;
|
break;
|
||||||
|
@ -190,58 +186,18 @@ void UatDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UatDelegate::updateEditorGeometry(QWidget *editor,
|
|
||||||
const QStyleOptionViewItem &option, const QModelIndex &index) const
|
|
||||||
{
|
|
||||||
uat_field_t *field = indexToField(index);
|
|
||||||
|
|
||||||
switch (field->mode) {
|
|
||||||
case PT_TXTMOD_DIRECTORYNAME:
|
|
||||||
{
|
|
||||||
QRect rect = option.rect;
|
|
||||||
rect.setBottom(rect.width());
|
|
||||||
editor->setGeometry(rect);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PT_TXTMOD_FILENAME:
|
|
||||||
{
|
|
||||||
QRect rect = option.rect;
|
|
||||||
rect.setWidth(600);
|
|
||||||
rect.setHeight(600);
|
|
||||||
editor->setGeometry(rect);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
//the defaults for other editors seem sane.
|
|
||||||
QStyledItemDelegate::updateEditorGeometry(editor, option, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UatDelegate::applyFilename(const QModelIndex& index)
|
void UatDelegate::applyFilename(const QModelIndex& index)
|
||||||
{
|
{
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
EditorFileDialog* fileDialog = static_cast<EditorFileDialog*>(sender());
|
EditorFileDialog* fileDialog = static_cast<EditorFileDialog*>(sender());
|
||||||
|
((QAbstractItemModel *)index.model())->setData(index, fileDialog->text(), Qt::EditRole);
|
||||||
QStringList files = fileDialog->selectedFiles();
|
|
||||||
if (files.size() > 0) {
|
|
||||||
((QAbstractItemModel *)index.model())->setData(index, files[0], Qt::EditRole);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UatDelegate::applyDirectory(const QModelIndex& index)
|
|
||||||
{
|
|
||||||
if (index.isValid()) {
|
|
||||||
EditorFileDialog* fileDialog = static_cast<EditorFileDialog*>(sender());
|
|
||||||
const QString &data = fileDialog->directory().absolutePath();
|
|
||||||
((QAbstractItemModel *)index.model())->setData(index, data, Qt::EditRole);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void UatDelegate::applyColor(const QModelIndex& index)
|
void UatDelegate::applyColor(const QModelIndex& index)
|
||||||
{
|
{
|
||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
QColorDialog *colorDialog = static_cast<QColorDialog*>(sender());
|
EditorColorDialog *colorDialog = static_cast<EditorColorDialog*>(sender());
|
||||||
QColor newColor = colorDialog->currentColor();
|
QColor newColor = colorDialog->currentColor();
|
||||||
((QAbstractItemModel *)index.model())->setData(index, newColor.name(), Qt::EditRole);
|
((QAbstractItemModel *)index.model())->setData(index, newColor.name(), Qt::EditRole);
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,7 @@ public:
|
||||||
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||||
const QModelIndex &index) const;
|
const QModelIndex &index) const;
|
||||||
|
|
||||||
void updateEditorGeometry(QWidget *editor,
|
|
||||||
const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void applyDirectory(const QModelIndex& index);
|
|
||||||
void applyFilename(const QModelIndex& index);
|
void applyFilename(const QModelIndex& index);
|
||||||
void applyColor(const QModelIndex& index);
|
void applyColor(const QModelIndex& index);
|
||||||
|
|
||||||
|
|
|
@ -10,24 +10,67 @@
|
||||||
|
|
||||||
#include <ui/qt/widgets/editor_color_dialog.h>
|
#include <ui/qt/widgets/editor_color_dialog.h>
|
||||||
|
|
||||||
EditorColorDialog::EditorColorDialog(const QModelIndex& index, QWidget* parent)
|
#include <QColorDialog>
|
||||||
: QColorDialog(parent)
|
#include <QKeyEvent>
|
||||||
, index_(index)
|
#include <QStyle>
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorColorDialog::EditorColorDialog(const QModelIndex& index, const QColor& initial, QWidget* parent)
|
EditorColorDialog::EditorColorDialog(const QModelIndex& index, const QColor& initial, QWidget* parent)
|
||||||
: QColorDialog(initial, parent)
|
: QLineEdit(parent)
|
||||||
|
, color_button_(new QPushButton(this))
|
||||||
, index_(index)
|
, index_(index)
|
||||||
|
, current_(initial)
|
||||||
{
|
{
|
||||||
|
connect(color_button_, SIGNAL(clicked()), this, SLOT(applyColor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorColorDialog::accept()
|
// QAbstractItemView installs QAbstractItemDelegate's event filter after
|
||||||
|
// we've been created. We need to install our own event filter after that
|
||||||
|
// happens so that we can steal tab keypresses.
|
||||||
|
void EditorColorDialog::focusInEvent(QFocusEvent *event)
|
||||||
{
|
{
|
||||||
emit acceptEdit(index_);
|
installEventFilter(this);
|
||||||
QColorDialog::accept();
|
QLineEdit::focusInEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorColorDialog::focusOutEvent(QFocusEvent *event)
|
||||||
|
{
|
||||||
|
removeEventFilter(this);
|
||||||
|
QLineEdit::focusOutEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorColorDialog::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::KeyPress) {
|
||||||
|
QKeyEvent* key = static_cast<QKeyEvent*>(event);
|
||||||
|
if ( (key->key() == Qt::Key_Tab) && !color_button_->hasFocus()) {
|
||||||
|
color_button_->setFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QLineEdit::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorColorDialog::resizeEvent(QResizeEvent *)
|
||||||
|
{
|
||||||
|
// Move the button to the end of the line edit and set its height.
|
||||||
|
QSize sz = color_button_->sizeHint();
|
||||||
|
int frame_width = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||||
|
color_button_->move(rect().right() - frame_width - sz.width(),
|
||||||
|
contentsRect().top());
|
||||||
|
color_button_->setMinimumHeight(contentsRect().height());
|
||||||
|
color_button_->setMaximumHeight(contentsRect().height());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorColorDialog::applyColor()
|
||||||
|
{
|
||||||
|
QColorDialog color_dlg;
|
||||||
|
|
||||||
|
color_dlg.setCurrentColor(current_);
|
||||||
|
if (color_dlg.exec() == QDialog::Accepted) {
|
||||||
|
current_ = color_dlg.currentColor();
|
||||||
|
emit acceptEdit(index_);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -11,23 +11,33 @@
|
||||||
#ifndef EDITOR_COLOR_DIALOG_H_
|
#ifndef EDITOR_COLOR_DIALOG_H_
|
||||||
#define EDITOR_COLOR_DIALOG_H_
|
#define EDITOR_COLOR_DIALOG_H_
|
||||||
|
|
||||||
#include <QColorDialog>
|
#include <QLineEdit>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QModelIndex>
|
#include <QModelIndex>
|
||||||
|
|
||||||
class EditorColorDialog : public QColorDialog
|
class EditorColorDialog : public QLineEdit
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
EditorColorDialog(const QModelIndex& index, QWidget* parent = 0);
|
|
||||||
EditorColorDialog(const QModelIndex& index, const QColor& initial, QWidget* parent = 0);
|
EditorColorDialog(const QModelIndex& index, const QColor& initial, QWidget* parent = 0);
|
||||||
|
|
||||||
void accept();
|
QColor currentColor() { return current_; }
|
||||||
|
virtual void focusInEvent(QFocusEvent *event);
|
||||||
|
virtual void focusOutEvent(QFocusEvent *event);
|
||||||
|
virtual bool eventFilter(QObject *obj, QEvent *event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void acceptEdit(const QModelIndex& index);
|
void acceptEdit(const QModelIndex& index);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void applyColor();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
|
||||||
|
QPushButton* color_button_;
|
||||||
const QModelIndex index_; //saved index of table cell
|
const QModelIndex index_; //saved index of table cell
|
||||||
|
QColor current_; //initial color in edit
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* EDITOR_COLOR_DIALOG_H_ */
|
#endif /* EDITOR_COLOR_DIALOG_H_ */
|
||||||
|
|
|
@ -10,22 +10,100 @@
|
||||||
|
|
||||||
#include <ui/qt/widgets/editor_file_dialog.h>
|
#include <ui/qt/widgets/editor_file_dialog.h>
|
||||||
|
|
||||||
EditorFileDialog::EditorFileDialog(const QModelIndex& index, QWidget* parent, Qt::WindowFlags flags)
|
#include <QKeyEvent>
|
||||||
: QFileDialog(parent, flags)
|
#include <QStyle>
|
||||||
|
|
||||||
|
#include "wsutil/utf8_entities.h"
|
||||||
|
|
||||||
|
EditorFileDialog::EditorFileDialog(const QModelIndex& index, enum FileMode mode, QWidget* parent, const QString& caption, const QString& directory, const QString& filter)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
, file_dialog_button_(new QPushButton(this))
|
||||||
, index_(index)
|
, index_(index)
|
||||||
|
, mode_(mode)
|
||||||
|
, caption_(caption)
|
||||||
|
, directory_(directory)
|
||||||
|
, filter_(filter)
|
||||||
|
, options_(0)
|
||||||
{
|
{
|
||||||
|
if (mode_ == Directory)
|
||||||
|
options_ = QFileDialog::ShowDirsOnly;
|
||||||
|
|
||||||
|
if (!directory.isEmpty())
|
||||||
|
setText(directory);
|
||||||
|
|
||||||
|
file_dialog_button_->setText(UTF8_HORIZONTAL_ELLIPSIS);
|
||||||
|
connect(file_dialog_button_, SIGNAL(clicked()), this, SLOT(applyFilename()));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorFileDialog::EditorFileDialog(const QModelIndex& index, QWidget* parent, const QString& caption, const QString& directory, const QString& filter)
|
void EditorFileDialog::setOption(QFileDialog::Option option, bool on)
|
||||||
: QFileDialog(parent, caption, directory, filter)
|
|
||||||
, index_(index)
|
|
||||||
{
|
{
|
||||||
|
if (on)
|
||||||
|
{
|
||||||
|
options_ |= option;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
options_ &= (~option);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorFileDialog::accept()
|
// QAbstractItemView installs QAbstractItemDelegate's event filter after
|
||||||
|
// we've been created. We need to install our own event filter after that
|
||||||
|
// happens so that we can steal tab keypresses.
|
||||||
|
void EditorFileDialog::focusInEvent(QFocusEvent *event)
|
||||||
{
|
{
|
||||||
emit acceptEdit(index_);
|
installEventFilter(this);
|
||||||
QFileDialog::accept();
|
QLineEdit::focusInEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorFileDialog::focusOutEvent(QFocusEvent *event)
|
||||||
|
{
|
||||||
|
removeEventFilter(this);
|
||||||
|
QLineEdit::focusOutEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditorFileDialog::eventFilter(QObject *obj, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::KeyPress) {
|
||||||
|
QKeyEvent* key = static_cast<QKeyEvent*>(event);
|
||||||
|
if ( (key->key() == Qt::Key_Tab) && !file_dialog_button_->hasFocus()) {
|
||||||
|
file_dialog_button_->setFocus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QLineEdit::eventFilter(obj, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorFileDialog::resizeEvent(QResizeEvent *)
|
||||||
|
{
|
||||||
|
// Move the button to the end of the line edit and set its height.
|
||||||
|
QSize sz = file_dialog_button_->sizeHint();
|
||||||
|
int frame_width = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||||
|
file_dialog_button_->move(rect().right() - frame_width - sz.width(),
|
||||||
|
contentsRect().top());
|
||||||
|
file_dialog_button_->setMinimumHeight(contentsRect().height());
|
||||||
|
file_dialog_button_->setMaximumHeight(contentsRect().height());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditorFileDialog::applyFilename()
|
||||||
|
{
|
||||||
|
QString file;
|
||||||
|
|
||||||
|
if (mode_ == Directory)
|
||||||
|
{
|
||||||
|
file = QFileDialog::getExistingDirectory(this, caption_, directory_, options_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = QFileDialog::getOpenFileName(this, caption_, directory_, filter_, NULL, options_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.isEmpty())
|
||||||
|
{
|
||||||
|
setText(file);
|
||||||
|
emit acceptEdit(index_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -11,23 +11,39 @@
|
||||||
#ifndef EDITOR_FILE_DIALOG_H_
|
#ifndef EDITOR_FILE_DIALOG_H_
|
||||||
#define EDITOR_FILE_DIALOG_H_
|
#define EDITOR_FILE_DIALOG_H_
|
||||||
|
|
||||||
#include <QFileDialog>
|
|
||||||
#include <QModelIndex>
|
#include <QModelIndex>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QPushButton>
|
||||||
|
|
||||||
class EditorFileDialog : public QFileDialog
|
class EditorFileDialog : public QLineEdit
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit EditorFileDialog(const QModelIndex& index, QWidget* parent, Qt::WindowFlags flags);
|
enum FileMode { ExistingFile, Directory };
|
||||||
explicit EditorFileDialog(const QModelIndex& index, QWidget* parent = 0, const QString & caption = QString(), const QString & directory = QString(), const QString & filter = QString());
|
|
||||||
|
|
||||||
void accept();
|
explicit EditorFileDialog(const QModelIndex& index, enum FileMode mode, QWidget* parent = 0, const QString & caption = QString(), const QString & directory = QString(), const QString & filter = QString());
|
||||||
|
|
||||||
|
void setOption(QFileDialog::Option option, bool on = true);
|
||||||
|
virtual void focusInEvent(QFocusEvent *event);
|
||||||
|
virtual void focusOutEvent(QFocusEvent *event);
|
||||||
|
virtual bool eventFilter(QObject *obj, QEvent *event);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void acceptEdit(const QModelIndex& index);
|
void acceptEdit(const QModelIndex& index);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void applyFilename();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
QPushButton* file_dialog_button_;
|
||||||
const QModelIndex index_; //saved index of table cell
|
const QModelIndex index_; //saved index of table cell
|
||||||
|
enum FileMode mode_;
|
||||||
|
QString caption_;
|
||||||
|
QString directory_;
|
||||||
|
QString filter_;
|
||||||
|
QFileDialog::Options options_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* EDITOR_FILE_DIALOG_H_ */
|
#endif /* EDITOR_FILE_DIALOG_H_ */
|
||||||
|
|
Loading…
Reference in New Issue