guint
fill_advanced_prefs(module_t *module, gpointer root_ptr)
{
    QTreeWidgetItem *root_item = static_cast<QTreeWidgetItem *>(root_ptr);

    if (!module || !root_item) return 1;

    if (module->numprefs < 1 && !prefs_module_has_submodules(module)) return 0;

    QString module_title = module->title;

    QTreeWidgetItem *tl_item = new QTreeWidgetItem(root_item);
    tl_item->setText(0, module_title);
    tl_item->setToolTip(0, QString("<span>%1</span>").arg(module->description));
    tl_item->setFirstColumnSpanned(true);

    QList<QTreeWidgetItem *>tl_children;
    for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) {
        pref_t *pref = (pref_t *) pref_l->data;

        if (pref->type == PREF_OBSOLETE || pref->type == PREF_STATIC_TEXT) continue;

        const char *type_name = prefs_pref_type_name(pref);
        if (!type_name) continue;

        pref_stash(pref, NULL);

        QTreeWidgetItem *item = new QTreeWidgetItem();
        QString full_name = QString(module->name ? module->name : module->parent->name) + "." + pref->name;
        QString type_desc = gchar_free_to_qstring(prefs_pref_type_description(pref));
        QString default_value = gchar_free_to_qstring(prefs_pref_to_str(pref, pref_stashed));

        item->setData(0, Qt::UserRole, qVariantFromValue(pref));
        item->setText(0, full_name);
        item->setToolTip(0, QString("<span>%1</span>").arg(pref->description));
        item->setToolTip(1, QObject::tr("Has this preference been changed?"));
        item->setText(2, type_name);
        item->setToolTip(2, QString("<span>%1</span>").arg(type_desc));
        item->setToolTip(3, QString("<span>%1</span>").arg(
                             default_value.isEmpty() ? default_value : QObject::tr("Default value is empty")));
        tl_children << item;

        // .uat is a void * so it wins the "useful key value" prize.
        if (pref->varp.uat) {
            pref_ptr_to_pref_[pref->varp.uat] = pref;
        }
    }
    tl_item->addChildren(tl_children);

    if(prefs_module_has_submodules(module))
        return prefs_modules_foreach_submodules(module, fill_advanced_prefs, tl_item);

    return 0;
}
static guint
module_prefs_unstash(module_t *module, gpointer data)
{
    gboolean *must_redissect_p = (gboolean *)data;

    module->prefs_changed = FALSE;        /* assume none of them changed */
    for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) {
        pref_t *pref = (pref_t *) pref_l->data;

        if (pref->type == PREF_OBSOLETE || pref->type == PREF_STATIC_TEXT) continue;

        pref_unstash(pref, &module->prefs_changed);
    }

    /* If any of them changed, indicate that we must redissect and refilter
       the current capture (if we have one), as the preference change
       could cause packets to be dissected differently. */
    if (module->prefs_changed)
        *must_redissect_p = TRUE;

    if(prefs_module_has_submodules(module))
        return prefs_modules_foreach_submodules(module, module_prefs_unstash, data);

    return 0;     /* Keep unstashing. */
}
static guint
module_prefs_clean_stash(module_t *module, gpointer)
{
    for (GList *pref_l = module->prefs; pref_l && pref_l->data; pref_l = g_list_next(pref_l)) {
        pref_t *pref = (pref_t *) pref_l->data;

        if (prefs_get_type(pref) == PREF_OBSOLETE || prefs_get_type(pref) == PREF_STATIC_TEXT) continue;

        pref_clean_stash(pref, NULL);
    }

    if(prefs_module_has_submodules(module))
        return prefs_modules_foreach_submodules(module, module_prefs_clean_stash, NULL);

    return 0;     /* Keep cleaning modules */
}
static guint
module_prefs_show(module_t *module, gpointer ti_ptr)
{
    QTreeWidgetItem *item = static_cast<QTreeWidgetItem *>(ti_ptr);

    if (!item) return 0;

    QStackedWidget *stacked_widget = item->data(0, Qt::UserRole).value<QStackedWidget *>();

    if (!stacked_widget) return 0;

    if (!module->use_gui) {
        /* This module uses its own GUI interface to modify its
         * preferences, so ignore it
         */
        return 0;
    }

    /*
     * Is this module an interior node, with modules underneath it?
     */
    if (!prefs_module_has_submodules(module)) {
        /*
         * No.
         * Does it have any preferences (other than possibly obsolete ones)?
         */
        if (prefs_pref_foreach(module, pref_exists, NULL) == 0) {
            /*
             * No.  Don't put the module into the preferences window,
             * as there's nothing to show.
             *
             * XXX - we should do the same for interior ndes; if the module
             * has no non-obsolete preferences *and* nothing under it has
             * non-obsolete preferences, don't put it into the window.
             */
            return 0;
        }
    }

    /*
     * Add this module to the tree.
     */
    QTreeWidgetItem *new_item = new QTreeWidgetItem(item);
    new_item->setText(0, module->title);
    new_item->setData(0, Qt::UserRole, item->data(0, Qt::UserRole));

    /*
     * Is this an interior node?
     */
    if (prefs_module_has_submodules(module)) {
        /*
         * Yes. Walk the subtree and attach stuff to it.
         */
        prefs_modules_foreach_submodules(module, module_prefs_show, (gpointer) new_item);
    }

    /*
     * We create pages for interior nodes even if they don't have
     * preferences, so that we at least have something to show
     * if the user clicks on them, even if it's empty.
     */

    /* Scrolled window */
    ModulePreferencesScrollArea *mpsa = new ModulePreferencesScrollArea(module);

//    /* Associate this module with the page's frame. */
//    g_object_set_data(G_OBJECT(frame), E_PAGE_MODULE_KEY, module);

    /* Add the page to the notebook */
    stacked_widget->addWidget(mpsa);

    /* Attach the page to the tree item */
    new_item->setData(0, Qt::UserRole, qVariantFromValue(qobject_cast<QWidget *>(mpsa)));

    return 0;
}