/*
 * nvidia-settings: A tool for configuring the NVIDIA X driver on Unix
 * and Linux systems.
 *
 * Copyright (C) 2010 NVIDIA Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of Version 2 of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Version 2
 * of the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the:
 *
 *           Free Software Foundation, Inc.
 *           59 Temple Place - Suite 330
 *           Boston, MA 02111-1307, USA
 *
 */

#include <gtk/gtk.h>

#include "NvCtrlAttributes.h"

#include "ctkbanner.h"
#include "ctk3dvisionpro.h"
#include "ctkconfig.h"
#include "ctkhelp.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/*
 * Icons.
 */
#include "svp_battery_0.h"
#include "svp_battery_25.h"
#include "svp_battery_50.h"
#include "svp_battery_75.h"
#include "svp_battery_100.h"
#include "svp_status_nosignal.h"
#include "svp_status_verylow.h"
#include "svp_status_low.h"
#include "svp_status_good.h"
#include "svp_status_verygood.h"
#include "svp_status_excellent.h"
#include "svp_autopick_next_channel.h"
#include "svp_add_glasses.h"

//-----------------------------------------------------------------------------
#define NUM_GLASSES_INFO_ATTRIBS (2)

#define HTU(idx)                 (ctk_3d_vision_pro->htu_info[(idx)])

enum {
    CHANGED,
    LAST_SIGNAL
};

typedef struct _AddGlassesDlg {
    GtkWidget *parent;
    GtkWidget *mnu_glasses_name;
    GtkWidget *dlg_add_glasses;
    int        glasses_selected_index;
} AddGlassesDlg;

typedef struct _RenameGlassesDlg {
    GtkWidget *parent;
    GtkWidget *mnu_glasses_name;
    GtkWidget *dlg_rename_glasses;
    int        glasses_selected_index;
} RenameGlassesDlg;

typedef struct _IdentifyGlassesDlg {
    GtkWidget *parent;
    GtkWidget *mnu_glasses_name;
    GtkWidget *dlg_identify_glasses;
    int        glasses_selected_index;
} IdentifyGlassesDlg;

typedef struct _ChannelRangeDlg {
    GtkWidget *parent;
    GtkWidget *dlg_channel_range;
} ChannelRangeDlg;

typedef struct _SelectChannelDlg {
    GtkWidget *parent;
    GtkWidget *dlg_select_channel;
} SelectChannelDlg;

typedef struct _RemoveGlassesDlg {
    GtkWidget *parent;
    GtkWidget *dlg_remove_glasses;
    GtkWidget *mnu_glasses_name;
    int        glasses_selected_index;
} RemoveGlassesDlg;

typedef void (*BUTTON_CLICK)(GtkButton *button, gpointer user_data);

typedef struct WidgetSizeRec {
    GtkWidget *widget;
    int        width;
} WidgetSize;

//-----------------------------------------------------------------------------

static guint __signals[LAST_SIGNAL] = { 0 };

const char *__mnu_glasses_name_tooltip = "Select glasses name";
const char *__goggle_info_tooltip      = "A list of glasses synced to the hub"
                                         " and their battery levels";
const char *__channel_range_tooltip    = "Choose hub range";
const char *__add_glasses_tooltip      = "Add more glasses to sync to the hub";
const char *__refresh_tooltip          = "Refresh Glasses Information";
const char *__identify_tooltip         = "Identify glasses";
const char *__rename_tooltip           = "Rename glasses";
const char *__remove_glasses_tooltip   = "Remove glasses synced to the hub";
const char *__autopick_tooltip         = "Auto pick next channel";
const char *__mnu_select_glasses_tooltip = "Select glasses avilable in the "
                                           "range to sync with the hub";

/******************************************************************************
 *
 * Various helper and Widget callback functions
 *
 ******************************************************************************/

static const char** get_battery_status_icon(int battery)
{
    if (battery == 0) {
        return (const char **)&svp_battery_0_xpm;
    } else if (battery < 50) {
        return (const char **)&svp_battery_25_xpm;
    } else if (battery < 75) {
        return (const char **)&svp_battery_50_xpm;
    } else if (battery < 100) {
        return (const char **)&svp_battery_75_xpm;
    } else if (battery == 100) {
        return (const char **)&svp_battery_100_xpm;
    }

    return NULL;
}

static const char** get_signal_strength_icon(int signal_strength)
{
    if (signal_strength == 0) {
        return (const char **)&svp_status_nosignal_xpm;
    } else if (signal_strength < 25) {
        return (const char **)&svp_status_verylow_xpm;
    } else if (signal_strength < 50) {
        return (const char **)&svp_status_low_xpm;
    } else if (signal_strength < 75) {
        return (const char **)&svp_status_good_xpm;
    } else if (signal_strength < 100) {
        return (const char **)&svp_status_verygood_xpm;
    } else if (signal_strength == 100) {
        return (const char **)&svp_status_excellent_xpm;
    }

    return NULL;
}

static void ctk_3d_vision_pro_class_init(Ctk3DVisionProClass
                                         *ctk_3d_vision_pro_class)
{
    GObjectClass *gobject_class;

    gobject_class = (GObjectClass *) ctk_3d_vision_pro_class;

    __signals[CHANGED] = g_signal_new("changed",
                       G_OBJECT_CLASS_TYPE(ctk_3d_vision_pro_class),
                       G_SIGNAL_RUN_LAST,
                       G_STRUCT_OFFSET(Ctk3DVisionProClass, changed),
                       NULL, NULL, g_cclosure_marshal_VOID__VOID,
                       G_TYPE_NONE, 0);
}

GType ctk_3d_vision_pro_get_type(void)
{
    static GType ctk_3d_vision_pro_type = 0;

    if (!ctk_3d_vision_pro_type) {
        static const GTypeInfo ctk_3d_vision_pro_info = {
            sizeof (Ctk3DVisionProClass),
            NULL, /* base_init */
            NULL, /* base_finalize */
            (GClassInitFunc) ctk_3d_vision_pro_class_init,
            NULL, /* class_finalize */
            NULL, /* class_data */
            sizeof (Ctk3DVisionPro),
            0,    /* n_preallocs */
            NULL, /* instance_init */
        };

        ctk_3d_vision_pro_type = g_type_register_static(GTK_TYPE_VBOX,
                                 "Ctk3DVisionPro", &ctk_3d_vision_pro_info, 0);
    }

    return ctk_3d_vision_pro_type;
}

static GtkWidget *add_button(char *label, BUTTON_CLICK handler,
                             Ctk3DVisionPro *ctk_3d_vision_pro,
                             GtkWidget *pack_in, const char *tooltip)
{
    GtkWidget *button;
    GtkWidget *hbox;
    GtkWidget *alignment;

    hbox = gtk_hbox_new(FALSE, 0);
    button = gtk_button_new_with_label(label);
    gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(handler),
                     (gpointer) ctk_3d_vision_pro);
    ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, button, tooltip);

    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_container_add(GTK_CONTAINER(alignment), hbox);
    gtk_box_pack_start(GTK_BOX(pack_in), alignment, TRUE, TRUE, 0);

    return button;
}

static void glasses_name_changed(GtkOptionMenu *option_menu,
                                 gpointer user_data)
{
    RemoveGlassesDlg *dlg = (RemoveGlassesDlg *)user_data;
    dlg->glasses_selected_index = gtk_option_menu_get_history(option_menu);
}

static GtkWidget *create_glasses_list_menu(Ctk3DVisionPro *ctk_3d_vision_pro,
                                           GlassesInfo **glasses_info,
                                           guint num_glasses, gpointer dlg)
{
    GtkWidget *menu;
    GtkWidget *menu_item;
    GtkWidget *mnu_glasses_name;
    int i;

    mnu_glasses_name = gtk_option_menu_new();
    g_signal_connect(G_OBJECT(mnu_glasses_name), "changed",
                     G_CALLBACK(glasses_name_changed),
                     (gpointer) dlg);
    menu = gtk_menu_new();

    ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, mnu_glasses_name,
                           __mnu_glasses_name_tooltip);

    for (i = 0; i < num_glasses; i++) {
        menu_item = gtk_menu_item_new_with_label(glasses_info[i]->name);
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
        gtk_widget_show(menu_item);
    }

    /* Setup the menu and select the glasses name */
    g_signal_handlers_block_by_func(G_OBJECT(mnu_glasses_name),
                                    G_CALLBACK(glasses_name_changed),
                                    (gpointer) dlg);

    gtk_option_menu_set_menu(GTK_OPTION_MENU(mnu_glasses_name), menu);
    gtk_option_menu_set_history(GTK_OPTION_MENU(mnu_glasses_name), 0);

    /* If dropdown has only one item, disable menu selection */
    if (num_glasses > 1) {
        gtk_widget_set_sensitive(mnu_glasses_name, True);
    } else {
        gtk_widget_set_sensitive(mnu_glasses_name, False);
    }

    g_signal_handlers_unblock_by_func
        (G_OBJECT(mnu_glasses_name),
         G_CALLBACK(glasses_name_changed), (gpointer) dlg);

    return mnu_glasses_name;
}

static const char *new_glasses_name_activate(GtkWidget *widget,
                                             gpointer user_data)
{
//    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    const gchar *str = gtk_entry_get_text(GTK_ENTRY(widget));

    // Store new glasses name in dialog box
    return str;
}

static gboolean new_glasses_name_focus_out(GtkWidget *widget, GdkEvent *event,
                                           gpointer user_data)
{
    new_glasses_name_activate(widget, user_data);

    return FALSE;

} /* display_panning_focus_out() */

static void dummy_button_signal(GtkWidget *widget,
                                gpointer user_data)
{
    /* This is a dummy function so tooltips are enabled
     * for the fbconfig table column titles
     */
}

static void update_glasses_info_data_table(Ctk3DVisionPro *ctk_3d_vision_pro,
                                           GtkWidget *data_table,
                                           WidgetSize *glasses_header_sizes)
{
    int i;
    GtkRequisition req;

    for (i = 0; i < HTU(0)->num_glasses; i++) {
        char str[NUM_GLASSES_INFO_ATTRIBS + 1][16];//MAX_ATTRIB_LENGTH
        int cell = 0;

        snprintf((char *)(&(str[cell++])), 16, "%s", HTU(0)->glasses_info[i]->name);
        snprintf((char *)(&(str[cell++])), 16, "%d", HTU(0)->glasses_info[i]->battery);

        for (cell = 0; cell < NUM_GLASSES_INFO_ATTRIBS; cell++) {
            GtkWidget *hbox;
            GtkWidget * label;
            GtkWidget *image;

            hbox = gtk_hbox_new(FALSE, 0);
            label = gtk_label_new(str[cell]);
            gtk_label_set_justify( GTK_LABEL(label), GTK_JUSTIFY_CENTER);
            gtk_table_attach(GTK_TABLE(data_table), hbox,
                             cell, cell+1,
                             i+1, i+2,
                             GTK_EXPAND, GTK_EXPAND, 0, 0);
            gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);

            if (cell == 1) {
                image = gtk_image_new_from_pixbuf(gdk_pixbuf_new_from_xpm_data(
                            get_battery_status_icon(HTU(0)->glasses_info[i]->battery)));
                gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
            }
            /* Make sure the table headers are the same width
             * as their table data column
             */
            gtk_widget_size_request(label, &req);

            if ( glasses_header_sizes[cell].width > req.width ) {
                gtk_widget_set_size_request(label,
                                            glasses_header_sizes[cell].width,
                                            -1);
            } else if ( glasses_header_sizes[cell].width < req.width ) {
                glasses_header_sizes[cell].width = req.width + 6;
                gtk_widget_set_size_request(glasses_header_sizes[cell].widget,
                                            glasses_header_sizes[cell].width,
                                            -1);
            }
        }
    }
}

static void create_glasses_info_table(Ctk3DVisionPro *ctk_3d_vision_pro,
                                      GtkWidget *pack_in)
{
    GtkWidget *hbox, *hbox1;
    GtkWidget *vbox, *vbox2;
    GtkWidget *label;
    GtkWidget *hseparator;
    GtkWidget *data_viewport, *full_viewport;
    GtkWidget *vscrollbar, *hscrollbar, *vpan;
    WidgetSize glasses_header_sizes[NUM_GLASSES_INFO_ATTRIBS];
    GtkWidget *data_table, *header_table;
    GtkRequisition req;
    GtkWidget *event;    /* For setting the background color to white */
    gchar *goggle_info_titles[NUM_GLASSES_INFO_ATTRIBS] =
           {"Glasses ID", "Battery Level (%)"};
    int i;

    /* Create clist in a scroll box */
    hbox1 = gtk_hbox_new(FALSE, 0);
    label = gtk_label_new("Glasses Information");
    hseparator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(hbox1), label, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbox1), hseparator, TRUE, TRUE, 5);

    hbox  = gtk_hbox_new(FALSE, 0);
    vbox  = gtk_vbox_new(FALSE, 5);
    vbox2 = gtk_vbox_new(FALSE, 5);
    vpan  = gtk_vpaned_new();

    data_viewport = gtk_viewport_new(NULL, NULL);
    gtk_widget_set_size_request(data_viewport, 250, 100);
    vscrollbar = gtk_vscrollbar_new(gtk_viewport_get_vadjustment
                                    (GTK_VIEWPORT(data_viewport)));

    full_viewport = gtk_viewport_new(NULL, NULL);
    gtk_widget_set_size_request(full_viewport, 300, 150);
    hscrollbar = gtk_hscrollbar_new(gtk_viewport_get_hadjustment
                                    (GTK_VIEWPORT(full_viewport)));

    /* Create the header table */
    header_table = gtk_table_new(HTU(0)->num_glasses, NUM_GLASSES_INFO_ATTRIBS, FALSE);
    for ( i = 0; i < NUM_GLASSES_INFO_ATTRIBS; i++ ) {
        GtkWidget * btn  = gtk_button_new_with_label(goggle_info_titles[i]);
        g_signal_connect(G_OBJECT(btn), "clicked",
                         G_CALLBACK(dummy_button_signal),
                         (gpointer) ctk_3d_vision_pro);
        ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, btn, __goggle_info_tooltip);
        gtk_table_attach(GTK_TABLE(header_table), btn, i, i+1, 0, 1,
                         GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);

        glasses_header_sizes[i].widget = btn;
        gtk_widget_size_request(btn, &req);
        glasses_header_sizes[i].width = req.width;
    }

    /* Create the data table */
    data_table = gtk_table_new(HTU(0)->num_glasses, NUM_GLASSES_INFO_ATTRIBS, FALSE);
    event = gtk_event_box_new();

    gtk_widget_modify_fg(data_table, GTK_STATE_NORMAL,
                         &(data_table->style->text[GTK_STATE_NORMAL]));
    gtk_widget_modify_bg(data_table, GTK_STATE_NORMAL,
                         &(data_table->style->base[GTK_STATE_NORMAL]));
    gtk_container_add (GTK_CONTAINER(event), data_table);
    gtk_widget_modify_fg(event, GTK_STATE_NORMAL,
                         &(event->style->text[GTK_STATE_NORMAL]));
    gtk_widget_modify_bg(event, GTK_STATE_NORMAL,
                         &(event->style->base[GTK_STATE_NORMAL]));
    gtk_container_add(GTK_CONTAINER(data_viewport), event);

    /* Pack the glasses info header and data tables */
    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), header_table, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), data_viewport, TRUE, TRUE, 0);
    gtk_container_add (GTK_CONTAINER(full_viewport), vbox);

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), full_viewport, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hscrollbar, FALSE, FALSE, 0);

    hbox = gtk_hbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vscrollbar, FALSE, FALSE, 0);

    vbox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hbox1, FALSE, FALSE, 5);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 0);

//    gtk_paned_pack1 (GTK_PANED (vpan), scrollWin, TRUE, FALSE);
    gtk_paned_pack2 (GTK_PANED (vpan), vbox, TRUE, FALSE);
    gtk_box_pack_start(GTK_BOX(pack_in), vpan, TRUE, TRUE, 0);

    /* Fill the data table */
    update_glasses_info_data_table(ctk_3d_vision_pro, data_table, glasses_header_sizes);
}

static void add_menu_item(GtkWidget *menu, const char *caption)
{
    GtkWidget *menu_item;
    GtkWidget *hbox;
    GtkWidget *label;

    menu_item = gtk_menu_item_new();
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);

    hbox = gtk_hbox_new(FALSE, 0);
    gtk_container_add(GTK_CONTAINER(menu_item), hbox);

    label = gtk_label_new(caption);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
}

/******************************************************************************
 *
 * Create dialog boxes and Button click callbacks
 *
 ******************************************************************************/

static void refresh_button_clicked(GtkButton *button, gpointer user_data)
{
    // Refresh the Glasses information list..
}

//=============================================================================

static AddGlassesDlg *create_add_glasses_dlg(Ctk3DVisionPro *ctk_3d_vision_pro)
{
    AddGlassesDlg *dlg;
    GtkWidget *label;
    GtkWidget *hbox;
    GtkWidget *image;
    GtkWidget *parent = GTK_WIDGET(ctk_3d_vision_pro);

    dlg = (AddGlassesDlg *)malloc(sizeof(AddGlassesDlg));
    if (dlg == NULL) {
        return NULL;
    }

    dlg->parent = parent;

    /* Create the dialog */
    dlg->dlg_add_glasses = gtk_dialog_new_with_buttons
        ("Add glasses",
         ctk_3d_vision_pro->parent_wnd,
         GTK_DIALOG_MODAL |  GTK_DIALOG_DESTROY_WITH_PARENT |
         GTK_DIALOG_NO_SEPARATOR,
         GTK_STOCK_SAVE,
         GTK_RESPONSE_ACCEPT,
         GTK_STOCK_CANCEL,
         GTK_RESPONSE_REJECT,
         NULL);

    gtk_dialog_set_default_response(GTK_DIALOG(dlg->dlg_add_glasses),
                                    GTK_RESPONSE_REJECT);

    label = gtk_label_new("1. Press button on the glasses\n"
                          "   to initiate the connection.");
    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);

    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_add_glasses)->vbox),
         hbox, TRUE, TRUE, 5);

    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_add_glasses)->vbox),
         hbox, TRUE, TRUE, 5);

    image = gtk_image_new_from_pixbuf(gdk_pixbuf_new_from_xpm_data(
                svp_add_glasses_xpm));
    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg->dlg_add_glasses)->vbox),
                       image, FALSE, FALSE, 0);

    label = gtk_label_new("2. List of glasses connected:");
    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_add_glasses)->vbox),
         hbox, TRUE, TRUE, 5);

    //generate menu option for glasses' name
    hbox = gtk_hbox_new(TRUE, 0);

    // XXX
    // Query glasses in the range using NV-Control
    // Form a list of glasses and add glasses as synced to the hub
    dlg->mnu_glasses_name = create_glasses_list_menu(ctk_3d_vision_pro,
                                HTU(0)->glasses_info, HTU(0)->num_glasses,
                                (gpointer)dlg);
    gtk_box_pack_start(GTK_BOX(hbox), dlg->mnu_glasses_name,
                       TRUE, TRUE, 0);

    ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, dlg->mnu_glasses_name,
                           __mnu_select_glasses_tooltip);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_add_glasses)->vbox),
         hbox, TRUE, TRUE, 5);
    gtk_dialog_set_has_separator(GTK_DIALOG(dlg->dlg_add_glasses), TRUE);

    gtk_widget_show_all(GTK_DIALOG(dlg->dlg_add_glasses)->vbox);

    return dlg;
}

static void add_glasses_button_clicked(GtkButton *button, gpointer user_data)
{
    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    AddGlassesDlg *dlg;
    gint result;

    dlg = create_add_glasses_dlg(ctk_3d_vision_pro);
    if (dlg == NULL) {
        return;
    }

    gtk_window_set_transient_for
        (GTK_WINDOW(dlg->dlg_add_glasses),
         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dlg->parent))));

    gtk_window_resize(GTK_WINDOW(dlg->dlg_add_glasses), 350, 1);
    gtk_window_set_resizable(GTK_WINDOW(dlg->dlg_add_glasses),
                             FALSE);

    gtk_widget_show(dlg->dlg_add_glasses);
    result = gtk_dialog_run(GTK_DIALOG(dlg->dlg_add_glasses));
    gtk_widget_hide(dlg->dlg_add_glasses);

    /* Handle user's response */
    switch (result) {
    case GTK_RESPONSE_OK:
        // XXX
        // Selected glasses is dlg->glasses_selected_index
        // Add emtry in the glasses information table
        // Add node into glasses_info list
        // Send/query any relevant NV-Control attributes
    default:
        /* do nothing. */
        break;
    }
    free(dlg);
}

//=============================================================================

static RemoveGlassesDlg *create_remove_glasses_dlg(Ctk3DVisionPro *ctk_3d_vision_pro)
{
    RemoveGlassesDlg *dlg;
    GtkWidget *label;
    GtkWidget *hbox;
    GtkWidget *parent = GTK_WIDGET(ctk_3d_vision_pro);

    dlg = (RemoveGlassesDlg *)malloc(sizeof(RemoveGlassesDlg));
    if (dlg == NULL) {
        return NULL;
    }

    dlg->parent = parent;

    /* Create the dialog */
    dlg->dlg_remove_glasses = gtk_dialog_new_with_buttons
        ("Remove glasses",
         ctk_3d_vision_pro->parent_wnd,
         GTK_DIALOG_MODAL |  GTK_DIALOG_DESTROY_WITH_PARENT |
         GTK_DIALOG_NO_SEPARATOR,
         GTK_STOCK_OK,
         GTK_RESPONSE_OK,
         GTK_STOCK_CANCEL,
         GTK_RESPONSE_REJECT,
         NULL);

    gtk_dialog_set_default_response(GTK_DIALOG(dlg->dlg_remove_glasses),
                                    GTK_RESPONSE_REJECT);

    label = gtk_label_new("Remove glasses synced to this hub:");
    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 20);

    //generate menu option for glasses' name
    dlg->mnu_glasses_name = create_glasses_list_menu(ctk_3d_vision_pro,
                                HTU(0)->glasses_info, HTU(0)->num_glasses,
                                (gpointer)dlg);
    gtk_box_pack_start(GTK_BOX(hbox), dlg->mnu_glasses_name,
                       TRUE, TRUE, 0);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_remove_glasses)->vbox),
         hbox, TRUE, TRUE, 20);

    gtk_dialog_set_has_separator(GTK_DIALOG(dlg->dlg_remove_glasses), TRUE);

    gtk_widget_show_all(GTK_DIALOG(dlg->dlg_remove_glasses)->vbox);

    return dlg;
}

static void remove_button_clicked(GtkButton *button, gpointer user_data)
{
    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    RemoveGlassesDlg *dlg;
    gint result;

    dlg = create_remove_glasses_dlg(ctk_3d_vision_pro);

    if (dlg == NULL) {
        return;
    }

    dlg->glasses_selected_index = -1;
    gtk_window_set_transient_for
        (GTK_WINDOW(dlg->dlg_remove_glasses),
         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dlg->parent))));

    gtk_window_resize(GTK_WINDOW(dlg->dlg_remove_glasses), 350, 1);
    gtk_window_set_resizable(GTK_WINDOW(dlg->dlg_remove_glasses),
                              FALSE);

    gtk_widget_show(dlg->dlg_remove_glasses);
    result = gtk_dialog_run(GTK_DIALOG(dlg->dlg_remove_glasses));
    gtk_widget_hide(dlg->dlg_remove_glasses);

    /* Handle user's response */
    switch (result) {
    case GTK_RESPONSE_OK:
        // XXX
        // dlg->glasses_selected_index;
        // Remove glasses entry from glasses_info
        // Send NV-Control command to unpair glasses
        // Refresh the Glasses information table
        break;
    default:
        /* do nothing. */
        break;
    }

    free(dlg);
}

//=============================================================================

static IdentifyGlassesDlg *create_identify_glasses_dlg(Ctk3DVisionPro *ctk_3d_vision_pro)
{
    IdentifyGlassesDlg *dlg;
    GtkWidget *label;
    GtkWidget *hbox;
    GtkWidget *parent = GTK_WIDGET(ctk_3d_vision_pro);

    dlg = (IdentifyGlassesDlg *)malloc(sizeof(IdentifyGlassesDlg));
    if (dlg == NULL) {
        return NULL;
    }

    dlg->parent = parent;

    /* Create the dialog */
    dlg->dlg_identify_glasses = gtk_dialog_new_with_buttons
        ("Identify glasses",
         ctk_3d_vision_pro->parent_wnd,
         GTK_DIALOG_MODAL |  GTK_DIALOG_DESTROY_WITH_PARENT |
         GTK_DIALOG_NO_SEPARATOR,
         GTK_STOCK_OK,
         GTK_RESPONSE_OK,
         GTK_STOCK_CANCEL,
         GTK_RESPONSE_REJECT,
         NULL);

    gtk_dialog_set_default_response(GTK_DIALOG(dlg->dlg_identify_glasses),
                                    GTK_RESPONSE_REJECT);

    label = gtk_label_new("Identify selected glasses:");
    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 20);

    /* Select glasses to remove */
    dlg->mnu_glasses_name = create_glasses_list_menu(ctk_3d_vision_pro,
                                HTU(0)->glasses_info, HTU(0)->num_glasses,
                                (gpointer)dlg);
    gtk_box_pack_start(GTK_BOX(hbox), dlg->mnu_glasses_name,
                       TRUE, TRUE, 0);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_identify_glasses)->vbox),
         hbox, TRUE, TRUE, 20);

    gtk_dialog_set_has_separator(GTK_DIALOG(dlg->dlg_identify_glasses), TRUE);

    gtk_widget_show_all(GTK_DIALOG(dlg->dlg_identify_glasses)->vbox);

    return dlg;
}

static void identify_button_clicked(GtkButton *button, gpointer user_data)
{
    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    IdentifyGlassesDlg *dlg;
    gint result;

    dlg = create_identify_glasses_dlg(ctk_3d_vision_pro);

    if (dlg == NULL) {
        return;
    }

    dlg->glasses_selected_index = -1;
    gtk_window_set_transient_for
        (GTK_WINDOW(dlg->dlg_identify_glasses),
         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dlg->parent))));

    gtk_window_resize(GTK_WINDOW(dlg->dlg_identify_glasses), 350, 1);
    gtk_window_set_resizable(GTK_WINDOW(dlg->dlg_identify_glasses),
                              FALSE);

    gtk_widget_show(dlg->dlg_identify_glasses);
    result = gtk_dialog_run(GTK_DIALOG(dlg->dlg_identify_glasses));
    gtk_widget_hide(dlg->dlg_identify_glasses);

    /* Handle user's response */
    switch (result) {
    case GTK_RESPONSE_OK:
        // XXX
        // dlg->glasses_selected_index;
        // Remove glasses entry from glasses_info
        // Send NV-Control command to unpair glasses
        // Refresh the Glasses information table
        break;
    default:
        /* do nothing. */
        break;
    }

    free(dlg);
}

//=============================================================================

static RenameGlassesDlg *create_rename_glasses_dlg(Ctk3DVisionPro *ctk_3d_vision_pro)
{
    RenameGlassesDlg *dlg;
    GtkWidget *label;
    GtkWidget *hbox;
    GtkWidget *new_glasses_name;
    const char *__new_glasses_name_tooltip = "Add new glasses name";

    GtkWidget *parent = GTK_WIDGET(ctk_3d_vision_pro);
    dlg = (RenameGlassesDlg *)malloc(sizeof(RenameGlassesDlg));
    if (dlg == NULL) {
        return NULL;
    }

    dlg->parent = parent;

    /* Create the dialog */
    dlg->dlg_rename_glasses = gtk_dialog_new_with_buttons
        ("Rename glasses",
         ctk_3d_vision_pro->parent_wnd,
         GTK_DIALOG_MODAL |  GTK_DIALOG_DESTROY_WITH_PARENT |
         GTK_DIALOG_NO_SEPARATOR,
         GTK_STOCK_SAVE,
         GTK_RESPONSE_ACCEPT,
         GTK_STOCK_CANCEL,
         GTK_RESPONSE_REJECT,
         NULL);

    gtk_dialog_set_default_response(GTK_DIALOG(dlg->dlg_rename_glasses),
                                    GTK_RESPONSE_REJECT);


    label = gtk_label_new("Name:");
    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 20);

    /* Select glasses to remove */
    dlg->mnu_glasses_name = create_glasses_list_menu(ctk_3d_vision_pro,
                                HTU(0)->glasses_info, HTU(0)->num_glasses,
                                (gpointer)dlg);
    gtk_box_pack_start(GTK_BOX(hbox), dlg->mnu_glasses_name,
                       TRUE, TRUE, 0);

    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_rename_glasses)->vbox),
         hbox, TRUE, TRUE, 20);

    new_glasses_name = gtk_entry_new();
    ctk_config_set_tooltip(ctk_3d_vision_pro->ctk_config, new_glasses_name,
                           __new_glasses_name_tooltip);
    g_signal_connect(G_OBJECT(new_glasses_name), "activate",
                     G_CALLBACK(new_glasses_name_activate),
                     (gpointer) ctk_3d_vision_pro);
    g_signal_connect(G_OBJECT(new_glasses_name), "focus-out-event",
                     G_CALLBACK(new_glasses_name_focus_out),
                     (gpointer) ctk_3d_vision_pro);

    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg->dlg_rename_glasses)->vbox),
                       new_glasses_name, TRUE, TRUE, 0);

    gtk_dialog_set_has_separator(GTK_DIALOG(dlg->dlg_rename_glasses), TRUE);

    gtk_widget_show_all(GTK_DIALOG(dlg->dlg_rename_glasses)->vbox);

    return dlg;
}

static void rename_button_clicked(GtkButton *button, gpointer user_data)
{
    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    RenameGlassesDlg *dlg;
    gint result;

    dlg = create_rename_glasses_dlg(ctk_3d_vision_pro);

    gtk_window_set_transient_for
        (GTK_WINDOW(dlg->dlg_rename_glasses),
         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dlg->parent))));

    gtk_window_resize(GTK_WINDOW(dlg->dlg_rename_glasses), 350, 1);
    gtk_window_set_resizable(GTK_WINDOW(dlg->dlg_rename_glasses),
                             FALSE);

    gtk_widget_show(dlg->dlg_rename_glasses);
    result = gtk_dialog_run(GTK_DIALOG(dlg->dlg_rename_glasses));
    gtk_widget_hide(dlg->dlg_rename_glasses);

    /* Handle user's response */
    switch (result) {
    case GTK_RESPONSE_OK:
        // XXX
        // Check if glasses name is unique
        // If unique update glasses_info link list
        // Update glasses information table
        // if not unique goto dialog box again and display error
        break;
    default:
        /* do nothing. */
        break;
    }
    free(dlg);
}

//=============================================================================

static SelectChannelDlg *create_select_channel_dlg(Ctk3DVisionPro *ctk_3d_vision_pro)
{
    SelectChannelDlg *dlg;
    GtkWidget *label;
    GtkWidget *hbox;
    GtkWidget *image;
    GtkWidget *parent = GTK_WIDGET(ctk_3d_vision_pro);

    dlg = (SelectChannelDlg *)malloc(sizeof(SelectChannelDlg));
    if (dlg == NULL) {
        return NULL;
    }

    dlg->parent = parent;

    /* Create the dialog */
    dlg->dlg_select_channel = gtk_dialog_new_with_buttons
        ("Auto pick next channel",
         ctk_3d_vision_pro->parent_wnd,
         GTK_DIALOG_MODAL |  GTK_DIALOG_DESTROY_WITH_PARENT |
         GTK_DIALOG_NO_SEPARATOR,
         GTK_STOCK_CANCEL,
         GTK_RESPONSE_REJECT,
         NULL);

    gtk_dialog_set_default_response(GTK_DIALOG(dlg->dlg_select_channel),
                                    GTK_RESPONSE_REJECT);

    label = gtk_label_new("Picking next channel...");
    hbox = gtk_hbox_new(TRUE, 0);
    image = gtk_image_new_from_pixbuf(gdk_pixbuf_new_from_xpm_data(
                svp_autopick_next_channel_xpm));
    gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 10);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 20);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_select_channel)->vbox),
         hbox, TRUE, TRUE, 20);

    gtk_dialog_set_has_separator(GTK_DIALOG(dlg->dlg_select_channel), TRUE);

    gtk_widget_show_all(GTK_DIALOG(dlg->dlg_select_channel)->vbox);

    return dlg;
}

static void select_next_channel_clicked(GtkButton *button, gpointer user_data)
{
    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    SelectChannelDlg *dlg;
    gint result;

    dlg = create_select_channel_dlg(ctk_3d_vision_pro);

    gtk_window_set_transient_for
        (GTK_WINDOW(dlg->dlg_select_channel),
         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dlg->parent))));

    gtk_window_resize(GTK_WINDOW(dlg->dlg_select_channel), 350, 1);
    gtk_window_set_resizable(GTK_WINDOW(dlg->dlg_select_channel),
                             FALSE);

    gtk_widget_show(dlg->dlg_select_channel);

    /* XXX Launch separate timer/thread to communicate with NV-Control
     * to pick new channel. Once channel is picked, send delete event
     * to the dialog. If cancel button is clicked, stop looking for
     * next channel and return to main page.
     */
    result = gtk_dialog_run(GTK_DIALOG(dlg->dlg_select_channel));
    gtk_widget_hide(dlg->dlg_select_channel);

    /* Handle user's response */
    switch (result) {
    case GTK_RESPONSE_CANCEL:
        break;
    case GTK_RESPONSE_DELETE_EVENT:
        break;
    default:
        /* do nothing. */
        break;
    }

    free(dlg);
}

//=============================================================================

static ChannelRangeDlg *create_channel_range_change_dlg(Ctk3DVisionPro *ctk_3d_vision_pro,
                                                        SVP_RANGE range)
{
    ChannelRangeDlg *dlg;
    GtkWidget *label = NULL;
    GtkWidget *hbox;
    GtkWidget *parent = GTK_WIDGET(ctk_3d_vision_pro);

    dlg = (ChannelRangeDlg *)malloc(sizeof(ChannelRangeDlg));
    if (dlg == NULL) {
        return NULL;
    }

    dlg->parent = parent;

    /* Create the dialog */
    dlg->dlg_channel_range = gtk_dialog_new_with_buttons
        ("Modify Transceiver Range",
         ctk_3d_vision_pro->parent_wnd,
         GTK_DIALOG_MODAL |  GTK_DIALOG_DESTROY_WITH_PARENT |
         GTK_DIALOG_NO_SEPARATOR,
         GTK_STOCK_YES,
         GTK_RESPONSE_ACCEPT,
         GTK_STOCK_NO,
         GTK_RESPONSE_REJECT,
         NULL);

    gtk_dialog_set_default_response(GTK_DIALOG(dlg->dlg_channel_range),
                                    GTK_RESPONSE_REJECT);

    switch (range) {
    case SVP_SHORT_RANGE:
        label = gtk_label_new("You have changed transceiver range to short range (less than 2m.).\n"
                              "Only glasses in this range will be available.\n\n"
                              "Do you want to apply changes?");
        break;
    case SVP_MEDIUM_RANGE:
        label = gtk_label_new("You have changed transceiver range to medium range (less than 10m.).\n"
                              "Only glasses in this range will be available.\n\n"
                              "Do you want to apply changes?");
        break;
    case SVP_LONG_RANGE:
        label = gtk_label_new("You have changed transceiver range to long range.\n"
                              "Only glasses in this range will be available.\n\n"
                              "Do you want to apply changes?");
        break;
    }

    hbox = gtk_hbox_new(TRUE, 0);
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 20);
    gtk_box_pack_start
        (GTK_BOX(GTK_DIALOG(dlg->dlg_channel_range)->vbox),
         hbox, TRUE, TRUE, 20);

    gtk_dialog_set_has_separator(GTK_DIALOG(dlg->dlg_channel_range), TRUE);

    gtk_widget_show_all(GTK_DIALOG(dlg->dlg_channel_range)->vbox);

    return dlg;
}

static void channel_range_changed(
    GtkOptionMenu *option_menu,
    gpointer user_data
)
{
    Ctk3DVisionPro *ctk_3d_vision_pro = CTK_3D_VISION_PRO(user_data);
    ChannelRangeDlg *dlg;
    gint result;
    SVP_RANGE range;

    range = gtk_option_menu_get_history(option_menu);

    if (HTU(0)->channel_range == range) {
        return;
    }

    dlg = create_channel_range_change_dlg(ctk_3d_vision_pro, range);

    gtk_window_set_transient_for
        (GTK_WINDOW(dlg->dlg_channel_range),
         GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(dlg->parent))));

    gtk_window_resize(GTK_WINDOW(dlg->dlg_channel_range), 350, 1);
    gtk_window_set_resizable(GTK_WINDOW(dlg->dlg_channel_range),
                              FALSE);

    gtk_widget_show(dlg->dlg_channel_range);
    result = gtk_dialog_run(GTK_DIALOG(dlg->dlg_channel_range));
    gtk_widget_hide(dlg->dlg_channel_range);

    /* Handle user's response */
    switch (result) {
    case GTK_RESPONSE_YES:
        HTU(0)->channel_range = range;
        /* Send NV-Control command */

        break;
    default:
        /* do nothing. */
        break;
    }
    free(dlg);
}

//*****************************************************************************

GtkWidget* ctk_3d_vision_pro_new(NvCtrlAttributeHandle *handle,
                                 CtkConfig *ctk_config, ParsedAttribute *p,
                                 CtkEvent *ctk_event)
{
    GObject *object;
    Ctk3DVisionPro *ctk_3d_vision_pro;
    GtkWidget *mainhbox;
    GtkWidget *leftvbox;
    GtkWidget *rightvbox;
    GtkWidget *banner;
    GtkWidget *alignment;
    GtkWidget *vbox;
    GtkWidget *frame_vbox;
    GtkWidget *label;
    GtkWidget *frame;
    GtkWidget *hbox;
    GtkWidget *hseparator;
    int i;
    GtkWidget *image;
    GtkWidget *menu;
    char temp[64]; //scrachpad memory used to construct labels.

    object = g_object_new(CTK_TYPE_3D_VISION_PRO, NULL);
    ctk_3d_vision_pro = CTK_3D_VISION_PRO(object);

    ctk_3d_vision_pro->handle = handle;
    ctk_3d_vision_pro->ctk_config = ctk_config;

    // XXX
    // populate ctk_3d_vision_pro...
    // query attributes and fill the data ..
    // what abt dynamic data/ changing data like battery status/signal strength
    // .... use timer to update?

    ctk_3d_vision_pro->num_htu = 1;
    ctk_3d_vision_pro->htu_info = (HtuInfo **) malloc(sizeof(HtuInfo *) *
                                  ctk_3d_vision_pro->num_htu);;
    for (i = 0; i < ctk_3d_vision_pro->num_htu; i++) {
        HtuInfo *htu = (HtuInfo*) malloc(sizeof(HtuInfo));
        HTU(i) = htu;
    }
    HTU(0)->signal_strength = 60;

    HTU(0)->num_glasses = 0;
    HTU(0)->glasses_info = (GlassesInfo **)malloc(sizeof(GlassesInfo *) *
                           HTU(0)->num_glasses);;
    for (i = 0; i < HTU(0)->num_glasses; i++) {
        GlassesInfo *glasses = (GlassesInfo *)malloc(sizeof(GlassesInfo));
        HTU(0)->glasses_info[i] = glasses;

        glasses->name = "";
        glasses->battery = 0;
    }

//-----------------------------------------------------------------------------
//  Construct and display NVIDIA 3D VisionPro page

    gtk_box_set_spacing(GTK_BOX(ctk_3d_vision_pro), 5);

    /*
     * Banner: TOP - LEFT -> RIGHT
     *
     * This image serves as a visual reference for basic color_box correction
     * purposes.
     */

    banner = ctk_banner_image_new(BANNER_ARTWORK_SVP);
    gtk_box_pack_start(GTK_BOX(ctk_3d_vision_pro), banner, FALSE, FALSE, 0);

    mainhbox = gtk_hbox_new(FALSE, 5);
    gtk_box_pack_start(GTK_BOX(ctk_3d_vision_pro), mainhbox,
                       FALSE, FALSE, 0);

//-----------------------------------------------------------------------------
//  left vertical box

    leftvbox = gtk_vbox_new(FALSE, 5);
    gtk_box_pack_start(GTK_BOX(mainhbox), leftvbox, FALSE, FALSE, 0);

    frame = gtk_frame_new("Glasses");
    gtk_box_pack_start(GTK_BOX(leftvbox), frame, FALSE, FALSE, 0);

    
    frame_vbox = gtk_vbox_new(FALSE, 5);
    gtk_container_set_border_width(GTK_CONTAINER(frame_vbox), 5);
    gtk_container_add(GTK_CONTAINER(frame), frame_vbox);
    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_box_pack_start(GTK_BOX(frame_vbox), alignment, TRUE, TRUE, 0);

    snprintf(temp, sizeof(temp), "Glasses Connected: %d", HTU(0)->num_glasses);
    label = gtk_label_new(temp);
    gtk_container_add(GTK_CONTAINER(alignment), label);


    hbox = gtk_hbox_new(FALSE, 5);
    gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_box_pack_start(GTK_BOX(frame_vbox), alignment, TRUE, TRUE, 0);
    gtk_container_add(GTK_CONTAINER(alignment), hbox);

    add_button("Add Glasses", add_glasses_button_clicked, ctk_3d_vision_pro,
               hbox, __add_glasses_tooltip);
    add_button("Refresh", refresh_button_clicked, ctk_3d_vision_pro,
               hbox, __refresh_tooltip);
    add_button("Identify", identify_button_clicked, ctk_3d_vision_pro,
               hbox, __identify_tooltip);
    add_button("Rename", rename_button_clicked, ctk_3d_vision_pro,
               hbox, __rename_tooltip);
    add_button("Remove", remove_button_clicked, ctk_3d_vision_pro,
               hbox, __remove_glasses_tooltip);

    create_glasses_info_table(ctk_3d_vision_pro, frame_vbox);

//-----------------------------------------------------------------------------
//  right vertical box

    rightvbox = gtk_vbox_new(FALSE, 10);
    gtk_box_pack_start(GTK_BOX(mainhbox), rightvbox, FALSE, FALSE, 0);

    frame = gtk_frame_new("RF Transceiver");
    gtk_box_pack_start(GTK_BOX(rightvbox), frame, FALSE, FALSE, 0);

    vbox = gtk_vbox_new(FALSE, 5);
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
    gtk_container_add(GTK_CONTAINER(frame), vbox);

    hbox = gtk_hbox_new(FALSE, 5);
    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), alignment, TRUE, TRUE, 0);
    snprintf(temp, sizeof(temp), "RF Transceivers Connected: %d",
             ctk_3d_vision_pro->num_htu);
    label = gtk_label_new(temp);
    gtk_container_add(GTK_CONTAINER(alignment), label);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    hbox = gtk_hbox_new(FALSE, 5);
    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_box_pack_start(GTK_BOX(hbox), alignment, TRUE, TRUE, 0);
    label = gtk_label_new("Signal Strength:");
    gtk_container_add(GTK_CONTAINER(alignment), label);

    image = gtk_image_new_from_pixbuf(gdk_pixbuf_new_from_xpm_data(
              get_signal_strength_icon(HTU(0)->signal_strength)));
    gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

    add_button("Autopick Next Channel", select_next_channel_clicked,
               ctk_3d_vision_pro, vbox, __autopick_tooltip);

    hseparator = gtk_hseparator_new();
    gtk_box_pack_start(GTK_BOX(vbox), hseparator, FALSE, FALSE, 0);

    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
    label = gtk_label_new("Transceiver Range:");
    gtk_container_add(GTK_CONTAINER(alignment), label);

    menu = gtk_menu_new();
    add_menu_item(menu, "Short Range (up to 2 meter)");
    add_menu_item(menu, "Medium Range (up to 10 meter)");
    add_menu_item(menu, "Long Range");
    ctk_3d_vision_pro->option_menu = gtk_option_menu_new ();
    gtk_option_menu_set_menu
        (GTK_OPTION_MENU(ctk_3d_vision_pro->option_menu), menu);

    alignment = gtk_alignment_new(0, 1, 0, 0);
    gtk_box_pack_start(GTK_BOX(vbox), alignment, TRUE, TRUE, 0);
    gtk_container_add(GTK_CONTAINER(alignment), ctk_3d_vision_pro->option_menu); 

    g_object_set_data(G_OBJECT(ctk_3d_vision_pro->option_menu),
                      "channel_range", GINT_TO_POINTER(0));

    g_signal_connect(G_OBJECT(ctk_3d_vision_pro->option_menu), "changed",
                     G_CALLBACK(channel_range_changed),
                     (gpointer) ctk_3d_vision_pro);

    ctk_config_set_tooltip(ctk_config, ctk_3d_vision_pro->option_menu,
                           __channel_range_tooltip);

    ctk_3d_vision_pro->parent_wnd = GTK_WINDOW(gtk_widget_get_parent(GTK_WIDGET(ctk_3d_vision_pro)));

    /* finally, show the widget */
    gtk_widget_show_all(GTK_WIDGET(object));
    
    return GTK_WIDGET(object);
}


GtkTextBuffer *ctk_3d_vision_pro_create_help(GtkTextTagTable *table)
{
    GtkTextIter i;
    GtkTextBuffer *b;

    b = gtk_text_buffer_new(table); 
    gtk_text_buffer_get_iter_at_offset(b, &i, 0);

    // XXX fill help text
    ctk_help_title(b, &i, "NVIDIA 3D VisioPro help");
    ctk_help_para(b, &i, "Use this page to configure the NVIDIA 3D VisionPro hub and glasses.");

    ctk_help_heading(b, &i, "Glasses section");
    ctk_help_para(b, &i, "Info about glasses");

    ctk_help_heading(b, &i, "Add glasses");
    ctk_help_para(b, &i, "Info about Add glasses");

    ctk_help_heading(b, &i, "Refresh Glasses' Information");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Identify glasses");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Rename glasses");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Remove glasses");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Glasses ID");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Battery Level");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "RF Transceiver section");
    ctk_help_para(b, &i, "Info about dongle");

    ctk_help_heading(b, &i, "Autopick next channel");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Signal strength");
    ctk_help_para(b, &i, "");

    ctk_help_heading(b, &i, "Transceiver Range");
    ctk_help_para(b, &i, "");

    ctk_help_finish(b);

    return b;
}

/* ctk_3d_vision_pro_select() *******************************************
 *
 * Callback function for when the 3D Vision Pro page is being displayed
 * in the control panel.
 *
 */
void ctk_3d_vision_pro_select(GtkWidget *w)
{

}

/* ctk_3d_vision_pro_unselect() *****************************************
 *
 * Callback function for when the 3D VisionPro page is no longer being
 * displayed by the control panel.  (User clicked on another page.)
 *
 */
void ctk_3d_vision_pro_unselect(GtkWidget *w)
{

}

/* ctk_3d_vision_pro_config_file_attributes() ****************************
 *
 * Add to the ParsedAttribute list any attributes that we want saved
 * in the config file.
 *
 */
void ctk_3d_vision_pro_config_file_attributes(GtkWidget *w,
                                              ParsedAttribute *head)
{
// XXX Write and read config data
}

