Allow editing via the advanced preferences tree. Double-clicking an

item's name, status, or type resets it to its default value.
Double-clicking the item's value lets you edit it. Implement the
advanced search field. (Clicking OK and Cancel still doesn't yet do
anything.)

Note that we could probably use a
prefs_register_{uint16|port}_preference routine for 16-bit values. Make
reset_pref public. Update some names and descriptions.

svn path=/trunk/; revision=46986
This commit is contained in:
Gerald Combs 2013-01-07 19:13:03 +00:00
parent b671f8dbd6
commit 0fa6a4c421
6 changed files with 398 additions and 83 deletions

View File

@ -947,6 +947,7 @@ report_open_failure
report_read_failure
report_write_failure
req_resp_hdrs_do_reassembly
reset_pref
reset_tap_listeners
reset_tcp_reassembly
rose_ctx_clean_data

View File

@ -840,6 +840,11 @@ prefs_register_uint_preference(module_t *module, const char *name,
preference->info.base = base;
}
/*
* XXX Add a prefs_register_{uint16|port}_preference which sets max_value?
*/
/*
* Register a "custom" preference with a unsigned integral value.
* XXX - This should be temporary until we can find a better way
@ -1241,11 +1246,12 @@ static prefs_set_pref_e console_log_level_set_cb(pref_t* pref, const gchar* valu
}
static const char * console_log_level_type_name_cb(void) {
return "Console log level (for debugging)";
return "Log level";
}
static char * console_log_level_type_description_cb(void) {
return g_strdup_printf(
"Console log level (for debugging)\n"
"A bitmask of log levels:\n"
"ERROR = 4\n"
"CRITICAL = 8\n"
@ -1693,12 +1699,12 @@ static prefs_set_pref_e capture_column_set_cb(pref_t* pref, const gchar* value,
static const char * capture_column_type_name_cb(void) {
return "Capture options dialog column list";
return "Column list";
}
static char * capture_column_type_description_cb(void) {
return g_strdup_printf(
"List of columns to be displayed.\n"
"List of columns to be displayed in the capture options dialog.\n"
"Possible values: INTERFACE,LINK,PMODE,SNAPLEN,MONITOR,BUFFER,FILTER\n");
}
@ -2695,10 +2701,10 @@ pre_init_prefs(void)
/*
* Reset a single dissector preference.
*/
static void
reset_pref(gpointer data, gpointer user_data _U_)
void
reset_pref(pref_t *pref)
{
pref_t *pref = data;
if (!pref) return;
switch (pref->type) {
@ -2756,6 +2762,13 @@ reset_pref(gpointer data, gpointer user_data _U_)
}
}
static void
reset_pref_cb(gpointer data, gpointer user_data _U_)
{
pref_t *pref = (pref_t *) data;
reset_pref(pref);
}
typedef struct {
module_t *module;
} reset_pref_arg_t;
@ -2769,7 +2782,7 @@ reset_module_prefs(void *value, void *data _U_)
reset_pref_arg_t arg;
arg.module = value;
g_list_foreach(arg.module->prefs, reset_pref, &arg);
g_list_foreach(arg.module->prefs, reset_pref_cb, &arg);
return FALSE;
}
@ -3926,7 +3939,7 @@ prefs_pref_type_name(pref_t *pref)
break;
case PREF_ENUM:
type_name = "Enumeration";
type_name = "Choice";
break;
case PREF_STRING:
@ -4057,7 +4070,7 @@ prefs_pref_type_description(pref_t *pref)
default:
break;
}
return g_strdup_printf("%s.", type_desc);
return g_strdup(type_desc);
}
gboolean

View File

@ -514,6 +514,12 @@ extern e_prefs *read_prefs(int *, int *, char **, int *, int *, char **);
into "*pf_path_return", and return the errno. */
extern int write_prefs(char **);
/** Set a preference to its default value
*
* @param pref A preference.
*/
extern void reset_pref(pref_t *pref);
/*
* Given a string of the form "<pref name>:<pref value>", as might appear
* as an argument to a "-o" option, parse it and set the preference in

View File

@ -21,22 +21,26 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <glib.h>
#include "color.h"
#include "packet-range.h"
#include <epan/prefs.h>
#include <epan/prefs-int.h>
#include "preferences_dialog.h"
#include "ui_preferences_dialog.h"
#include "wireshark_application.h"
#include <epan/prefs-int.h>
#include "syntax_line_edit.h"
#include <QTreeWidgetItemIterator>
#include <QFrame>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QLineEdit>
#include <QComboBox>
#include <QFileDialog>
#include <QColorDialog>
#include <QMessageBox>
#include <QDebug>
Q_DECLARE_METATYPE(pref_t *)
extern "C" {
// Callbacks prefs routines
@ -49,15 +53,11 @@ fill_advanced_prefs(module_t *module, gpointer root_ptr)
if (module->numprefs < 1 && !prefs_module_has_submodules(module)) return 0;
QString module_title;
// if (module->parent == NULL)
module_title = module->title;
// else
// module_title = QString(module->parent->title) +": "+ module->title;
QString module_title = module->title;
QTreeWidgetItem *tl_item = new QTreeWidgetItem(root_item);
tl_item->setText(0, module_title);
tl_item->setToolTip(0, module->description);
tl_item->setToolTip(0, QString("<span>%1</span>").arg(module->description));
tl_item->setFirstColumnSpanned(true);
QList<QTreeWidgetItem *>tl_children;
@ -70,43 +70,19 @@ fill_advanced_prefs(module_t *module, gpointer root_ptr)
QTreeWidgetItem *item = new QTreeWidgetItem();
QString full_name = QString(module->name ? module->name : module->parent->name) + "." + pref->name;
QFont font = item->font(0);
char *type_desc = prefs_pref_type_description(pref);
char *cur_value = prefs_pref_to_str(pref, false);
char *default_value = prefs_pref_to_str(pref, true);
bool is_default = false;
bool is_editable = true;
if (pref->type == PREF_UAT) {
is_editable = false;
} else {
if (prefs_pref_is_default(pref)) is_default = true;
}
char * default_value = prefs_pref_to_str(pref, true);
item->setData(0, Qt::UserRole, qVariantFromValue(pref));
item->setText(0, full_name);
item->setToolTip(0, pref->description);
item->setText(1, is_default ? "Default" : "Changed");
item->setToolTip(1, "Has this value been changed?");
item->setToolTip(0, QString("<span>%1</span>").arg(pref->description));
item->setText(2, type_name);
item->setToolTip(2, type_desc);
item->setText(3, QString(cur_value).replace(QRegExp("\n\t"), " "));
item->setToolTip(3, QString("Default: ") + default_value);
item->setToolTip(2, QString("<span>%1</span>").arg(type_desc));
item->setToolTip(3, QString("<span>%1</span>").arg(strlen(default_value) < 1 ? "Default value is empty" : default_value));
g_free(type_desc);
g_free(cur_value);
g_free(default_value);
font.setBold(!is_default);
if (!is_editable) {
item->setFlags(item->flags() ^ Qt::ItemIsEnabled);
}
font.setItalic(!is_editable);
item->setFont(0, font);
item->setFont(0, font);
item->setFont(1, font);
item->setFont(2, font);
item->setFont(3, font);
tl_children << item;
}
tl_item->addChildren(tl_children);
@ -130,25 +106,24 @@ PreferencesDialog::PreferencesDialog(QWidget *parent) :
pd_ui_(new Ui::PreferencesDialog)
{
pd_ui_->setupUi(this);
QTreeWidgetItem tmp_item;
QTreeWidgetItem tmp_item; // Adding pre-populated top-level items is much faster
pd_ui_->advancedTree->setUpdatesEnabled(false);
prefs_modules_foreach_submodules(NULL, fill_advanced_prefs, (gpointer) &tmp_item);
pd_ui_->advancedTree->invisibleRootItem()->addChildren(tmp_item.takeChildren());
pd_ui_->advancedTree->expandAll();
pd_ui_->advancedTree->setSortingEnabled(true);
pd_ui_->advancedTree->sortByColumn(0, Qt::AscendingOrder);
pd_ui_->advancedTree->setColumnWidth(0, pd_ui_->advancedTree->width() * 2 / 5);
pd_ui_->advancedTree->resizeColumnToContents(1);
pd_ui_->advancedTree->resizeColumnToContents(2);
pd_ui_->advancedTree->setColumnWidth(3, pd_ui_->advancedTree->width() * 3 / 5);
pd_ui_->advancedTree->setUpdatesEnabled(true);
QTreeWidgetItemIterator pref_it(pd_ui_->advancedTree, QTreeWidgetItemIterator::NoChildren);
while (*pref_it) {
// pref_t *pref = (*pref_it)->data(0, Qt::UserRole).value<pref_t *>();
updateItem(*(*pref_it));
// if (pref) pref_item_hash_[pref] = (*pref_it);
++pref_it;
}
pd_ui_->splitter->setStretchFactor(0, 1);
pd_ui_->splitter->setStretchFactor(1, 5);
pd_ui_->prefsTree->invisibleRootItem()->child(appearance_item_)->setExpanded(true);
pd_ui_->prefsTree->invisibleRootItem()->child(advanced_item_)->setSelected(true);
pd_ui_->prefsTree->setCurrentItem(pd_ui_->prefsTree->invisibleRootItem()->child(advanced_item_));
}
PreferencesDialog::~PreferencesDialog()
@ -169,6 +144,307 @@ void PreferencesDialog::showEvent(QShowEvent *evt)
sizes[0] = new_prefs_tree_width;
pd_ui_->splitter->setSizes(sizes);
pd_ui_->splitter->setStretchFactor(0, 0);
pd_ui_->advancedTree->expandAll();
pd_ui_->advancedTree->setSortingEnabled(true);
pd_ui_->advancedTree->sortByColumn(0, Qt::AscendingOrder);
pd_ui_->advancedTree->setColumnWidth(0, pd_ui_->stackedWidget->width() / 2); // Don't let long items widen things too much
pd_ui_->advancedTree->resizeColumnToContents(1);
pd_ui_->advancedTree->resizeColumnToContents(2);
pd_ui_->advancedTree->resizeColumnToContents(3);
}
void PreferencesDialog::updateItem(QTreeWidgetItem &item)
{
pref_t *pref = item.data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
char *cur_value = prefs_pref_to_str(pref, false);
bool is_changed = false;
QFont font = item.font(0);
if (pref->type == PREF_UAT) {
item.setText(1, "Unknown");
} else if (prefs_pref_is_default(pref)) {
item.setText(1, "Default");
} else {
item.setText(1, "Changed");
is_changed = true;
}
font.setBold(is_changed);
item.setFont(0, font);
item.setFont(0, font);
item.setFont(1, font);
item.setFont(2, font);
item.setFont(3, font);
item.setToolTip(1, "Has this value been changed?");
item.setText(3, QString(cur_value).remove(QRegExp("\n\t")));
g_free(cur_value);
}
void PreferencesDialog::on_prefsTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
Q_UNUSED(previous)
QString frame_name = current->text(0).remove(" ").toLower().append("Frame");
QFrame *frame = pd_ui_->stackedWidget->findChild<QFrame *>(frame_name);
if (frame) {
pd_ui_->stackedWidget->setCurrentWidget(frame);
}
}
void PreferencesDialog::on_advancedSearchLineEdit_textEdited(const QString &search_str)
{
// Hide or show each branch
QTreeWidgetItemIterator branch_it(pd_ui_->advancedTree);
while (*branch_it) {
if ((*branch_it)->data(0, Qt::UserRole).value<pref_t *>() == NULL) {
(*branch_it)->setHidden(!search_str.isEmpty());
}
++branch_it;
}
// Hide or show each item, showing its parents if needed
QTreeWidgetItemIterator pref_it(pd_ui_->advancedTree);
while (*pref_it) {
bool hidden = true;
if ((*pref_it)->data(0, Qt::UserRole).value<pref_t *>()) {
QTreeWidgetItem *parent = (*pref_it)->parent();
if (search_str.isEmpty() ||
(*pref_it)->text(0).contains(search_str, Qt::CaseInsensitive) ||
(*pref_it)->toolTip(0).contains(search_str, Qt::CaseInsensitive)) {
hidden = false;
}
(*pref_it)->setHidden(hidden);
if (!hidden) {
while (parent) {
parent->setHidden(false);
parent = parent->parent();
}
}
}
++pref_it;
}
}
void PreferencesDialog::on_advancedTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
{
Q_UNUSED(current);
if (previous && pd_ui_->advancedTree->itemWidget(previous, 3)) {
pd_ui_->advancedTree->removeItemWidget(previous, 3);
}
}
void PreferencesDialog::on_advancedTree_itemActivated(QTreeWidgetItem *item, int column)
{
pref_t *pref = item->data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
if (column < 3) {
reset_pref(pref);
updateItem(*item);
} else {
QWidget *editor = NULL;
switch (pref->type) {
case PREF_UINT:
{
QLineEdit *line_edit = new QLineEdit();
line_edit->setInputMask("0000000009;");
line_edit->setText(*pref->varp.string);
connect(line_edit, SIGNAL(editingFinished()), this, SLOT(uintPrefEditingFinished()));
editor = line_edit;
break;
}
case PREF_BOOL:
*pref->varp.boolp = !*pref->varp.boolp;
updateItem(*item);
break;
case PREF_ENUM:
{
QComboBox *combo = new QComboBox();
const enum_val_t *ev;
for (ev = pref->info.enum_info.enumvals; ev && ev->description; ev++) {
combo->addItem(ev->description, QVariant(ev->value));
if (*pref->varp.enump == ev->value)
combo->setCurrentIndex(combo->count() - 1);
}
connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(enumPrefCurrentIndexChanged(int)));
editor = combo;
break;
}
case PREF_STRING:
{
QLineEdit *line_edit = new QLineEdit();
line_edit->setText(*pref->varp.string);
connect(line_edit, SIGNAL(editingFinished()), this, SLOT(stringPrefEditingFinished()));
editor = line_edit;
break;
}
case PREF_FILENAME:
{
QString filename = QFileDialog::getSaveFileName(this,
QString("Wireshark: ") + pref->description,
*pref->varp.string);
if (!filename.isEmpty()) {
g_free((void *)*pref->varp.string);
*pref->varp.string = g_strdup(filename.toUtf8().constData());
updateItem(*item);
}
break;
}
case PREF_RANGE:
{
SyntaxLineEdit *syntax_edit = new SyntaxLineEdit();
char *cur_val = prefs_pref_to_str(pref, FALSE);
connect(syntax_edit, SIGNAL(textChanged(QString)),
this, SLOT(rangePrefTextChanged(QString)));
connect(syntax_edit, SIGNAL(editingFinished()), this, SLOT(rangePrefEditingFinished()));
syntax_edit->setText(cur_val);
g_free(cur_val);
editor = syntax_edit;
break;
}
case PREF_COLOR:
{
QColorDialog color_dlg;
color_dlg.setCurrentColor(QColor(
pref->varp.color->red >> 8,
pref->varp.color->green >> 8,
pref->varp.color->blue >> 8
));
if (color_dlg.exec() == QDialog::Accepted) {
QColor cc = color_dlg.currentColor();
pref->varp.color->red = cc.red() << 8 | cc.red();
pref->varp.color->green = cc.green() << 8 | cc.green();
pref->varp.color->blue = cc.blue() << 8 | cc.blue();
updateItem(*item);
}
break;
}
case PREF_UAT:
qDebug() << "FIX open uat dialog" << item->text(column);
break;
default:
break;
}
if (editor) {
QFrame *edit_frame = new QFrame();
QHBoxLayout *hb = new QHBoxLayout();
QSpacerItem *spacer = new QSpacerItem(5, 10);
hb->addWidget(editor, 0);
hb->addSpacerItem(spacer);
hb->setStretch(1, 1);
hb->setContentsMargins(0, 0, 0, 0);
edit_frame->setLineWidth(0);
edit_frame->setFrameStyle(QFrame::NoFrame);
// The documentation suggests setting autoFillbackground. That looks silly
// so we clear the item text instead.
item->setText(3, "");
edit_frame->setLayout(hb);
pd_ui_->advancedTree->setItemWidget(item, 3, edit_frame);
editor->setFocus();
}
}
}
void PreferencesDialog::uintPrefEditingFinished()
{
QLineEdit *line_edit = qobject_cast<QLineEdit *>(QObject::sender());
QTreeWidgetItem *item = pd_ui_->advancedTree->currentItem();
if (!line_edit || !item) return;
pref_t *pref = item->data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
*pref->varp.uint = line_edit->text().toUInt();
pd_ui_->advancedTree->removeItemWidget(item, 3);
updateItem(*item);
}
void PreferencesDialog::enumPrefCurrentIndexChanged(int index)
{
QComboBox *combo_box = qobject_cast<QComboBox *>(QObject::sender());
QTreeWidgetItem *item = pd_ui_->advancedTree->currentItem();
if (!combo_box || !item || index < 0) return;
pref_t *pref = item->data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
*pref->varp.enump = combo_box->itemData(index, Qt::UserRole).toInt();
// pd_ui_->advancedTree->removeItemWidget(item, 3); // Crashes
updateItem(*item);
}
void PreferencesDialog::stringPrefEditingFinished()
{
QLineEdit *line_edit = qobject_cast<QLineEdit *>(QObject::sender());
QTreeWidgetItem *item = pd_ui_->advancedTree->currentItem();
if (!line_edit || !item) return;
pref_t *pref = item->data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
g_free((void *)*pref->varp.string);
*pref->varp.string = g_strdup(line_edit->text().toUtf8().constData());
pd_ui_->advancedTree->removeItemWidget(item, 3);
updateItem(*item);
}
void PreferencesDialog::rangePrefTextChanged(const QString &text)
{
SyntaxLineEdit *syntax_edit = qobject_cast<SyntaxLineEdit *>(QObject::sender());
QTreeWidgetItem *item = pd_ui_->advancedTree->currentItem();
if (!syntax_edit || !item) return;
pref_t *pref = item->data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
if (text.isEmpty()) {
syntax_edit->setSyntaxState(SyntaxLineEdit::Empty);
} else {
range_t *newrange;
convert_ret_t ret = range_convert_str(&newrange, text.toUtf8().constData(), pref->info.max_value);
if (ret == CVT_NO_ERROR) {
syntax_edit->setSyntaxState(SyntaxLineEdit::Valid);
g_free(newrange);
} else {
syntax_edit->setSyntaxState(SyntaxLineEdit::Invalid);
}
}
}
void PreferencesDialog::rangePrefEditingFinished()
{
SyntaxLineEdit *syntax_edit = qobject_cast<SyntaxLineEdit *>(QObject::sender());
QTreeWidgetItem *item = pd_ui_->advancedTree->currentItem();
if (!syntax_edit || !item) return;
pref_t *pref = item->data(0, Qt::UserRole).value<pref_t *>();
if (!pref) return;
range_t *newrange;
convert_ret_t ret = range_convert_str(&newrange, syntax_edit->text().toUtf8().constData(), pref->info.max_value);
if (ret == CVT_NO_ERROR) {
g_free(*pref->varp.range);
*pref->varp.range = newrange;
}
pd_ui_->advancedTree->removeItemWidget(item, 3);
updateItem(*item);
}
void PreferencesDialog::on_buttonBox_helpRequested()
{
wsApp->helpTopicAction(HELP_PREFERENCES_DIALOG);
}
/*

View File

@ -24,7 +24,17 @@
#ifndef PREFERENCES_DIALOG_H
#define PREFERENCES_DIALOG_H
#include "config.h"
#include <glib.h>
#include "color.h"
#include "packet-range.h"
#include <epan/prefs.h>
#include <QDialog>
#include <QTreeWidgetItem>
namespace Ui {
class PreferencesDialog;
@ -42,7 +52,22 @@ protected:
void showEvent(QShowEvent *evt);
private:
void updateItem(QTreeWidgetItem &item);
Ui::PreferencesDialog *pd_ui_;
// QHash<pref_t *, QTreeWidgetItem *> pref_item_hash_;
private slots:
void on_prefsTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
void on_advancedSearchLineEdit_textEdited(const QString &search_str);
void on_advancedTree_itemActivated(QTreeWidgetItem *item, int column);
void uintPrefEditingFinished();
void enumPrefCurrentIndexChanged(int index);
void stringPrefEditingFinished();
void rangePrefTextChanged(const QString & text);
void rangePrefEditingFinished();
void on_buttonBox_helpRequested();
void on_advancedTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
};
#endif // PREFERENCES_DIALOG_H

View File

@ -35,32 +35,29 @@
<property name="text">
<string>Appearance</string>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
<item>
<property name="text">
<string>Layout</string>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
</item>
<item>
<property name="text">
<string>Columns</string>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
</item>
<item>
<property name="text">
<string>Font and Colors</string>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
<property name="flags">
<set>ItemIsDragEnabled|ItemIsUserCheckable</set>
</property>
</item>
</item>
<item>
@ -116,10 +113,10 @@
<widget class="QFrame" name="appearanceFrame"/>
<widget class="QFrame" name="layoutFrame"/>
<widget class="QFrame" name="columnFrame"/>
<widget class="QFrame" name="fontColorFrame"/>
<widget class="QFrame" name="fontandcolorFrame"/>
<widget class="QFrame" name="captureFrame"/>
<widget class="QFrame" name="filterFrame"/>
<widget class="QFrame" name="nameResolutionFrame"/>
<widget class="QFrame" name="nameresolutionFrame"/>
<widget class="QFrame" name="protocolsFrame"/>
<widget class="QFrame" name="statisticsFrame"/>
<widget class="QFrame" name="advancedFrame">
@ -153,9 +150,6 @@
</item>
<item>
<widget class="QTreeWidget" name="advancedTree">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>