MegaGlest/mk/linux/mojosetup/gui_gtkplus2.c

916 lines
30 KiB
C

/**
* MojoSetup; a portable, flexible installation application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
// Not only does GTK+ 2.x _look_ better than 1.x, it offers a lot of basic
// functionality, like message boxes, that you'd expect to exist in a GUI
// toolkit. In GTK+v1, you'd have to roll all that on your own!
//
// It's easier to implement in that regard, and produces a smaller DLL, but
// it has a million dependencies, so you might need to use a GTK+v1 plugin,
// too, in case they break backwards compatibility.
#if !SUPPORT_GUI_GTKPLUS2
#error Something is wrong in the build system.
#endif
#define BUILDING_EXTERNAL_PLUGIN 1
#include "gui.h"
MOJOGUI_PLUGIN(gtkplus2)
#if !GUI_STATIC_LINK_GTKPLUS2
CREATE_MOJOGUI_ENTRY_POINT(gtkplus2)
#endif
#undef format
#include <gtk/gtk.h>
#define format entry->format
typedef enum
{
PAGE_INTRO,
PAGE_README,
PAGE_OPTIONS,
PAGE_DEST,
PAGE_PRODUCTKEY,
PAGE_PROGRESS,
PAGE_FINAL
} WizardPages;
static WizardPages currentpage = PAGE_INTRO;
static gboolean canfwd = TRUE;
static GtkWidget *gtkwindow = NULL;
static GtkWidget *pagetitle = NULL;
static GtkWidget *notebook = NULL;
static GtkWidget *readme = NULL;
static GtkWidget *cancel = NULL;
static GtkWidget *back = NULL;
static GtkWidget *next = NULL;
static GtkWidget *finish = NULL;
static GtkWidget *msgbox = NULL;
static GtkWidget *destination = NULL;
static GtkWidget *productkey = NULL;
static GtkWidget *progressbar = NULL;
static GtkWidget *progresslabel = NULL;
static GtkWidget *finallabel = NULL;
static GtkWidget *browse = NULL;
static GtkWidget *splash = NULL;
static volatile enum
{
CLICK_BACK=-1,
CLICK_CANCEL,
CLICK_NEXT,
CLICK_NONE
} click_value = CLICK_NONE;
static void prepare_wizard(const char *name, WizardPages page,
boolean can_back, boolean can_fwd,
boolean can_cancel, boolean fwd_at_start)
{
char *markup = g_markup_printf_escaped(
"<span size='large' weight='bold'>%s</span>",
name);
currentpage = page;
canfwd = can_fwd;
gtk_label_set_markup(GTK_LABEL(pagetitle), markup);
g_free(markup);
gtk_widget_set_sensitive(back, can_back);
gtk_widget_set_sensitive(next, fwd_at_start);
gtk_widget_set_sensitive(cancel, can_cancel);
gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), (gint) page);
assert(click_value == CLICK_NONE);
assert(gtkwindow != NULL);
assert(msgbox == NULL);
} // prepare_wizard
static int wait_event(void)
{
int retval = 0;
// signals fired under gtk_main_iteration can change click_value.
gtk_main_iteration();
if (click_value == CLICK_CANCEL)
{
char *title = xstrdup(_("Cancel installation"));
char *text = xstrdup(_("Are you sure you want to cancel installation?"));
if (!MojoGui_gtkplus2_promptyn(title, text, false))
click_value = CLICK_NONE;
free(title);
free(text);
} // if
assert(click_value <= CLICK_NONE);
retval = (int) click_value;
click_value = CLICK_NONE;
return retval;
} // wait_event
static int pump_events(void)
{
int retval = (int) CLICK_NONE;
while ( (retval == ((int) CLICK_NONE)) && (gtk_events_pending()) )
retval = wait_event();
return retval;
} // pump_events
static int run_wizard(const char *name, WizardPages page,
boolean can_back, boolean can_fwd, boolean can_cancel,
boolean fwd_at_start)
{
int retval = CLICK_NONE;
prepare_wizard(name, page, can_back, can_fwd, can_cancel, fwd_at_start);
while (retval == ((int) CLICK_NONE))
retval = wait_event();
assert(retval < ((int) CLICK_NONE));
return retval;
} // run_wizard
static gboolean signal_delete(GtkWidget *w, GdkEvent *evt, gpointer data)
{
click_value = CLICK_CANCEL;
return TRUE; /* eat event: default handler destroys window! */
} // signal_delete
static void signal_clicked(GtkButton *_button, gpointer data)
{
GtkWidget *button = GTK_WIDGET(_button);
if (button == back)
click_value = CLICK_BACK;
else if (button == cancel)
click_value = CLICK_CANCEL;
else if ((button == next) || (button == finish))
click_value = CLICK_NEXT;
else
{
assert(0 && "Unknown click event.");
} // else
} // signal_clicked
static void signal_browse_clicked(GtkButton *_button, gpointer data)
{
GtkWidget *dialog = gtk_file_chooser_dialog_new (
_("Destination"),
NULL,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
gtk_combo_box_get_active_text(GTK_COMBO_BOX(destination)));
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
gchar *filename;
gchar *utfname;
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
utfname = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
gtk_combo_box_prepend_text(GTK_COMBO_BOX(destination), utfname);
gtk_combo_box_set_active(GTK_COMBO_BOX(destination), 0);
g_free(utfname);
g_free(filename);
}
// !!! FIXME: should append the package name to the directory they chose?
// !!! FIXME: This is annoying, they might have created the folder
// !!! FIXME: themselves in the dialog.
// !!! FIXME: Could warn when the target directory already contains files?
gtk_widget_destroy(dialog);
} // signal_browse_clicked
static void signal_dest_changed(GtkComboBox *combo, gpointer user_data)
{
// Disable the forward button when the destination entry is blank.
if ((currentpage == PAGE_DEST) && (canfwd))
{
gchar *str = gtk_combo_box_get_active_text(combo);
const gboolean filled_in = ((str != NULL) && (*str != '\0'));
g_free(str);
gtk_widget_set_sensitive(next, filled_in);
} // if
} // signal_dest_changed
static void signal_productkey_changed(GtkEditable *edit, gpointer user_data)
{
// Disable the forward button when the entry is blank.
if ((currentpage == PAGE_PRODUCTKEY) && (canfwd))
{
const char *fmt = (const char *) user_data;
char *key = (char *) gtk_editable_get_chars(edit, 0, -1);
const gboolean okay = isValidProductKey(fmt, key);
g_free(key);
gtk_widget_set_sensitive(next, okay);
} // if
} // signal_productkey_changed
static uint8 MojoGui_gtkplus2_priority(boolean istty)
{
// gnome-session exports this environment variable since 2002.
if (getenv("GNOME_DESKTOP_SESSION_ID") != NULL)
return MOJOGUI_PRIORITY_TRY_FIRST;
return MOJOGUI_PRIORITY_TRY_NORMAL;
} // MojoGui_gtkplus2_priority
static boolean MojoGui_gtkplus2_init(void)
{
int tmpargc = 0;
char *args[] = { NULL, NULL };
char **tmpargv = args;
if (!gtk_init_check(&tmpargc, &tmpargv))
{
logInfo("gtkplus2: gtk_init_check() failed, use another UI.");
return false;
} // if
return true;
} // MojoGui_gtkplus2_init
static void MojoGui_gtkplus2_deinit(void)
{
// !!! FIXME: GTK+ doesn't have a deinit function...it installs a crappy
// !!! FIXME: atexit() function!
} // MojoGui_gtkplus2_deinit
/**
*
* @param defbutton The response ID to use when enter is pressed, or 0
* to leave it up to GTK+.
*/
static gint do_msgbox(const char *title, const char *text,
GtkMessageType mtype, GtkButtonsType btype,
GtkResponseType defbutton,
void (*addButtonCallback)(GtkWidget *_msgbox))
{
gint retval = 0;
// Modal dialog, this will never be called recursively.
assert(msgbox == NULL);
msgbox = gtk_message_dialog_new(GTK_WINDOW(gtkwindow), GTK_DIALOG_MODAL,
mtype, btype, "%s", title);
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msgbox),
"%s", text);
if (defbutton)
gtk_dialog_set_default_response(GTK_DIALOG(msgbox), defbutton);
if (addButtonCallback != NULL)
addButtonCallback(msgbox);
retval = gtk_dialog_run(GTK_DIALOG(msgbox));
gtk_widget_destroy(msgbox);
msgbox = NULL;
return retval;
} // do_msgbox
static void MojoGui_gtkplus2_msgbox(const char *title, const char *text)
{
do_msgbox(title, text, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, 0, NULL);
} // MojoGui_gtkplus2_msgbox
static boolean MojoGui_gtkplus2_promptyn(const char *title, const char *text,
boolean defval)
{
gint rc = do_msgbox(title, text, GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
defval ? GTK_RESPONSE_YES : GTK_RESPONSE_NO,
NULL);
return (rc == GTK_RESPONSE_YES);
} // MojoGui_gtkplus2_promptyn
static void promptynanButtonCallback(GtkWidget *_msgbox)
{
char *always = xstrdup(_("_Always"));
char *never = xstrdup(_("N_ever"));
gtk_dialog_add_buttons(GTK_DIALOG(_msgbox),
GTK_STOCK_YES, GTK_RESPONSE_YES,
GTK_STOCK_NO, GTK_RESPONSE_NO,
always, 1,
never, 0,
NULL);
free(always);
free(never);
} // promptynanButtonCallback
static MojoGuiYNAN MojoGui_gtkplus2_promptynan(const char *title,
const char *text,
boolean defval)
{
const gint rc = do_msgbox(title, text, GTK_MESSAGE_QUESTION,
GTK_BUTTONS_NONE,
defval ? GTK_RESPONSE_YES : GTK_RESPONSE_NO,
promptynanButtonCallback);
switch (rc)
{
case GTK_RESPONSE_YES: return MOJOGUI_YES;
case GTK_RESPONSE_NO: return MOJOGUI_NO;
case 1: return MOJOGUI_ALWAYS;
case 0: return MOJOGUI_NEVER;
} // switch
assert(false && "BUG: unhandled case in switch statement");
return MOJOGUI_NO; // just in case.
} // MojoGui_gtkplus2_promptynan
static GtkWidget *create_button(GtkWidget *box, const char *iconname,
const char *text,
void (*signal_callback)
(GtkButton *button, gpointer data))
{
GtkWidget *button = gtk_button_new_with_mnemonic(text);
GtkWidget *image = gtk_image_new_from_stock(iconname, GTK_ICON_SIZE_BUTTON);
gtk_button_set_image (GTK_BUTTON(button), image);
gtk_widget_show(button);
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
gtk_signal_connect(GTK_OBJECT(button), "clicked",
GTK_SIGNAL_FUNC(signal_callback), NULL);
return button;
} // create_button
static void free_splash(guchar *pixels, gpointer data)
{
free(pixels);
} // free_splash
static GtkWidget *build_splash(const MojoGuiSplash *splash)
{
GtkWidget *retval = NULL;
GdkPixbuf *pixbuf = NULL;
guchar *rgba = NULL;
const uint32 splashlen = splash->w * splash->h * 4;
if (splash->position == MOJOGUI_SPLASH_NONE)
return NULL;
if ((splash->rgba == NULL) || (splashlen == 0))
return NULL;
rgba = (guchar *) xmalloc(splashlen);
memcpy(rgba, splash->rgba, splashlen);
pixbuf = gdk_pixbuf_new_from_data(rgba, GDK_COLORSPACE_RGB, TRUE, 8,
splash->w, splash->h, splash->w * 4,
free_splash, NULL);
if (pixbuf == NULL)
free(rgba);
else
{
retval = gtk_image_new_from_pixbuf(pixbuf);
g_object_unref(pixbuf); // retval adds a ref to pixbuf, so lose our's.
if (retval != NULL)
gtk_widget_show(retval);
} // else
return retval;
} // build_splash
static GtkWidget *create_gtkwindow(const char *title,
const MojoGuiSplash *_splash)
{
GtkWidget *splashbox = NULL;
GtkWidget *window;
GtkWidget *widget;
GtkWidget *box;
GtkWidget *alignment;
GtkWidget *hbox;
currentpage = PAGE_INTRO;
canfwd = TRUE;
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
gtk_window_set_title(GTK_WINDOW(window), title);
gtk_container_set_border_width(GTK_CONTAINER(window), 8);
GdkPixbuf *icon = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
"system-software-installer",
48, 0, NULL);
if (icon)
gtk_window_set_icon(GTK_WINDOW(window), icon);
assert(splash == NULL);
splash = build_splash(_splash);
if (splash != NULL)
{
// !!! FIXME: MOJOGUI_SPLASH_BACKGROUND?
const MojoGuiSplashPos pos = _splash->position;
if ((pos == MOJOGUI_SPLASH_LEFT) || (pos == MOJOGUI_SPLASH_RIGHT))
{
splashbox = gtk_hbox_new(FALSE, 6);
gtk_widget_show(splashbox);
gtk_container_add(GTK_CONTAINER(window), splashbox);
if (pos == MOJOGUI_SPLASH_LEFT)
gtk_box_pack_start(GTK_BOX(splashbox), splash, FALSE, FALSE, 6);
else
gtk_box_pack_end(GTK_BOX(splashbox), splash, FALSE, FALSE, 6);
} // if
else if ((pos == MOJOGUI_SPLASH_TOP) || (pos == MOJOGUI_SPLASH_BOTTOM))
{
splashbox = gtk_vbox_new(FALSE, 6);
gtk_widget_show(splashbox);
gtk_container_add(GTK_CONTAINER(window), splashbox);
if (pos == MOJOGUI_SPLASH_TOP)
gtk_box_pack_start(GTK_BOX(splashbox), splash, FALSE, FALSE, 6);
else
gtk_box_pack_end(GTK_BOX(splashbox), splash, FALSE, FALSE, 6);
} // else if
} // if
if (splashbox == NULL) // no splash, use the window for the top container.
splashbox = window;
box = gtk_vbox_new(FALSE, 6);
gtk_widget_show(box);
gtk_container_add(GTK_CONTAINER(splashbox), box);
pagetitle = gtk_label_new("");
gtk_widget_show(pagetitle);
gtk_box_pack_start(GTK_BOX(box), pagetitle, FALSE, TRUE, 16);
notebook = gtk_notebook_new();
gtk_widget_show(notebook);
gtk_container_set_border_width(GTK_CONTAINER(notebook), 0);
gtk_box_pack_start(GTK_BOX(box), notebook, TRUE, TRUE, 0);
gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook), FALSE);
gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE);
gtk_widget_set_size_request(notebook,
(gint) (((float) gdk_screen_width()) * 0.4),
(gint) (((float) gdk_screen_height()) * 0.4));
widget = gtk_hbutton_box_new();
gtk_widget_show(widget);
gtk_box_pack_start(GTK_BOX(box), widget, FALSE, FALSE, 0);
gtk_button_box_set_layout(GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_END);
gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (widget), 6, 0);
gtk_button_box_set_spacing(GTK_BUTTON_BOX (widget), 6);
box = widget;
cancel = create_button(box, "gtk-cancel", _("Cancel"), signal_clicked);
back = create_button(box, "gtk-go-back", _("Back"), signal_clicked);
next = create_button(box, "gtk-go-forward", _("Next"), signal_clicked);
finish = create_button(box, "gtk-goto-last", _("Finish"), signal_clicked);
gtk_widget_hide(finish);
// !!! FIXME: intro page.
widget = gtk_vbox_new(FALSE, 0);
gtk_widget_show(widget);
gtk_container_add(GTK_CONTAINER(notebook), widget);
// README/EULA page.
widget = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(widget),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_shadow_type(
GTK_SCROLLED_WINDOW(widget),
GTK_SHADOW_IN);
gtk_widget_show(widget);
gtk_container_add(GTK_CONTAINER(notebook), widget);
readme = gtk_text_view_new();
gtk_widget_show(readme);
gtk_container_add(GTK_CONTAINER(widget), readme);
gtk_text_view_set_editable(GTK_TEXT_VIEW(readme), FALSE);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(readme), GTK_WRAP_NONE);
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(readme), FALSE);
gtk_text_view_set_left_margin(GTK_TEXT_VIEW(readme), 4);
gtk_text_view_set_right_margin(GTK_TEXT_VIEW(readme), 4);
// !!! FIXME: options page.
box = gtk_vbox_new(FALSE, 0);
gtk_widget_show(box);
gtk_container_add(GTK_CONTAINER(notebook), box);
// Destination page
box = gtk_vbox_new(FALSE, 0);
gtk_widget_show(box);
hbox = gtk_hbox_new (FALSE, 6);
widget = gtk_label_new(_("Folder:"));
gtk_widget_show(widget);
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_CENTER);
gtk_label_set_line_wrap(GTK_LABEL(widget), FALSE);
alignment = gtk_alignment_new(0.5, 0.5, 1, 0);
destination = gtk_combo_box_entry_new_text();
gtk_signal_connect(GTK_OBJECT(destination), "changed",
GTK_SIGNAL_FUNC(signal_dest_changed), NULL);
gtk_container_add(GTK_CONTAINER(alignment), destination);
gtk_box_pack_start(GTK_BOX(hbox), alignment, TRUE, TRUE, 0);
browse = create_button(hbox, "gtk-open",
_("B_rowse..."), signal_browse_clicked);
gtk_widget_show_all (hbox);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0);
gtk_container_add(GTK_CONTAINER(notebook), box);
// Product key page
box = gtk_vbox_new(FALSE, 0);
gtk_widget_show(box);
widget = gtk_label_new(_("Please enter your product key"));
gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_CENTER);
gtk_label_set_line_wrap(GTK_LABEL(widget), TRUE);
gtk_widget_show(widget);
gtk_box_pack_start(GTK_BOX(box), widget, FALSE, TRUE, 0);
hbox = gtk_hbox_new(FALSE, 0);
gtk_widget_show(hbox);
productkey = gtk_entry_new();
gtk_entry_set_editable(GTK_ENTRY(productkey), TRUE);
gtk_entry_set_visibility(GTK_ENTRY(productkey), TRUE);
gtk_widget_show(productkey);
gtk_box_pack_start(GTK_BOX(hbox), productkey, TRUE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(notebook), box);
// Progress page
box = gtk_vbox_new(FALSE, 6);
gtk_widget_show(box);
alignment = gtk_alignment_new(0.5, 0.5, 1, 0);
gtk_widget_show(alignment);
progressbar = gtk_progress_bar_new();
gtk_widget_show(progressbar);
gtk_container_add(GTK_CONTAINER(alignment), progressbar);
gtk_box_pack_start(GTK_BOX(box), alignment, FALSE, TRUE, 0);
progresslabel = gtk_label_new("");
gtk_widget_show(progresslabel);
gtk_box_pack_start(GTK_BOX(box), progresslabel, FALSE, TRUE, 0);
gtk_label_set_justify(GTK_LABEL(progresslabel), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap(GTK_LABEL(progresslabel), FALSE);
gtk_container_add(GTK_CONTAINER(notebook), box);
// !!! FIXME: final page.
widget = gtk_vbox_new(FALSE, 0);
gtk_widget_show(widget);
gtk_container_add(GTK_CONTAINER(notebook), widget);
finallabel = gtk_label_new("");
gtk_widget_show(finallabel);
gtk_box_pack_start(GTK_BOX(widget), finallabel, FALSE, TRUE, 0);
gtk_label_set_justify(GTK_LABEL(finallabel), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap(GTK_LABEL(finallabel), TRUE);
gtk_signal_connect(GTK_OBJECT(window), "delete-event",
GTK_SIGNAL_FUNC(signal_delete), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_show(window);
gtk_widget_grab_focus(next);
return window;
} // create_gtkwindow
static boolean MojoGui_gtkplus2_start(const char *title,
const MojoGuiSplash *splash)
{
gtkwindow = create_gtkwindow(title, splash);
return (gtkwindow != NULL);
} // MojoGui_gtkplus2_start
static void MojoGui_gtkplus2_stop(void)
{
assert(msgbox == NULL);
if (gtkwindow != NULL)
gtk_widget_destroy(gtkwindow);
gtkwindow = NULL;
pagetitle = NULL;
finallabel = NULL;
progresslabel = NULL;
progressbar = NULL;
destination = NULL;
productkey = NULL;
notebook = NULL;
readme = NULL;
cancel = NULL;
back = NULL;
next = NULL;
finish = NULL;
splash = NULL;
} // MojoGui_gtkplus2_stop
static int MojoGui_gtkplus2_readme(const char *name, const uint8 *data,
size_t datalen, boolean can_back,
boolean can_fwd)
{
GtkTextBuffer *textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(readme));
gtk_text_buffer_set_text(textbuf, (const gchar *) data, datalen);
return run_wizard(name, PAGE_README, can_back, can_fwd, true, can_fwd);
} // MojoGui_gtkplus2_readme
static void set_option_tree_sensitivity(MojoGuiSetupOptions *opts, boolean val)
{
if (opts != NULL)
{
gtk_widget_set_sensitive((GtkWidget *) opts->guiopaque, val);
set_option_tree_sensitivity(opts->next_sibling, val);
set_option_tree_sensitivity(opts->child, val && opts->value);
} // if
} // set_option_tree_sensitivity
static void signal_option_toggled(GtkToggleButton *toggle, gpointer data)
{
MojoGuiSetupOptions *opts = (MojoGuiSetupOptions *) data;
const boolean enabled = gtk_toggle_button_get_active(toggle);
opts->value = enabled;
set_option_tree_sensitivity(opts->child, enabled);
} // signal_option_toggled
static GtkWidget *new_option_level(GtkWidget *box)
{
GtkWidget *alignment = gtk_alignment_new(0.0, 0.5, 0, 0);
GtkWidget *retval = gtk_vbox_new(FALSE, 0);
gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 15, 0);
gtk_widget_show(alignment);
gtk_widget_show(retval);
gtk_container_add(GTK_CONTAINER(alignment), retval);
gtk_box_pack_start(GTK_BOX(box), alignment, TRUE, TRUE, 0);
return retval;
} // new_option_level
// use this to generate a tooltip only as needed.
static GtkTooltips *get_tip(GtkTooltips **_tip)
{
if (*_tip == NULL)
{
GtkTooltips *tip = gtk_tooltips_new();
gtk_tooltips_enable(tip);
*_tip = tip;
} // if
return *_tip;
} // get_tip
static void build_options(MojoGuiSetupOptions *opts, GtkWidget *box,
gboolean sensitive)
{
if (opts != NULL)
{
GtkTooltips *tip = NULL;
GtkWidget *widget = NULL;
if (opts->is_group_parent)
{
MojoGuiSetupOptions *kids = opts->child;
GtkWidget *childbox = NULL;
GtkWidget *alignment = gtk_alignment_new(0.0, 0.5, 0, 0);
gtk_widget_show(alignment);
widget = gtk_label_new(opts->description);
opts->guiopaque = widget;
gtk_widget_set_sensitive(widget, sensitive);
gtk_widget_show(widget);
gtk_label_set_justify(GTK_LABEL(widget), GTK_JUSTIFY_LEFT);
gtk_label_set_line_wrap(GTK_LABEL(widget), FALSE);
gtk_container_add(GTK_CONTAINER(alignment), widget);
gtk_box_pack_start(GTK_BOX(box), alignment, FALSE, TRUE, 0);
if (opts->tooltip != NULL)
gtk_tooltips_set_tip(get_tip(&tip), widget, opts->tooltip, 0);
widget = NULL;
childbox = new_option_level(box);
while (kids)
{
widget = gtk_radio_button_new_with_label_from_widget(
GTK_RADIO_BUTTON(widget),
kids->description);
kids->guiopaque = widget;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
kids->value);
gtk_widget_set_sensitive(widget, sensitive);
gtk_widget_show(widget);
gtk_box_pack_start(GTK_BOX(childbox), widget, FALSE, TRUE, 0);
gtk_signal_connect(GTK_OBJECT(widget), "toggled",
GTK_SIGNAL_FUNC(signal_option_toggled), kids);
if (kids->tooltip != NULL)
gtk_tooltips_set_tip(get_tip(&tip),widget,kids->tooltip,0);
if (kids->child != NULL)
{
build_options(kids->child, new_option_level(childbox),
sensitive);
} // if
kids = kids->next_sibling;
} // while
} // if
else
{
widget = gtk_check_button_new_with_label(opts->description);
opts->guiopaque = widget;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),
opts->value);
gtk_widget_set_sensitive(widget, sensitive);
gtk_widget_show(widget);
gtk_box_pack_start(GTK_BOX(box), widget, FALSE, TRUE, 0);
gtk_signal_connect(GTK_OBJECT(widget), "toggled",
GTK_SIGNAL_FUNC(signal_option_toggled), opts);
if (opts->tooltip != NULL)
gtk_tooltips_set_tip(get_tip(&tip), widget, opts->tooltip, 0);
if (opts->child != NULL)
{
build_options(opts->child, new_option_level(box),
((sensitive) && (opts->value)) );
} // if
} // else
build_options(opts->next_sibling, box, sensitive);
} // if
} // build_options
static int MojoGui_gtkplus2_options(MojoGuiSetupOptions *opts,
boolean can_back, boolean can_fwd)
{
int retval;
GtkWidget *box;
GtkWidget *page; // this is a vbox.
page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), PAGE_OPTIONS);
box = gtk_vbox_new(FALSE, 0);
gtk_widget_show(box);
gtk_box_pack_start(GTK_BOX(page), box, FALSE, FALSE, 0);
build_options(opts, box, TRUE);
retval = run_wizard(_("Options"), PAGE_OPTIONS,
can_back, can_fwd, true, can_fwd);
gtk_widget_destroy(box);
return retval;
} // MojoGui_gtkplus2_options
static char *MojoGui_gtkplus2_destination(const char **recommends, int recnum,
int *command, boolean can_back,
boolean can_fwd)
{
GtkComboBox *combo = GTK_COMBO_BOX(destination);
const boolean fwd_at_start = ( (recnum > 0) && (*(recommends[0])) );
gchar *str = NULL;
char *retval = NULL;
int i;
for (i = 0; i < recnum; i++)
gtk_combo_box_append_text(combo, recommends[i]);
gtk_combo_box_set_active (combo, 0);
*command = run_wizard(_("Destination"), PAGE_DEST,
can_back, can_fwd, true, fwd_at_start);
str = gtk_combo_box_get_active_text(combo);
// shouldn't ever be empty ("next" should be disabled in that case).
assert( (*command <= 0) || ((str != NULL) && (*str != '\0')) );
retval = xstrdup(str);
g_free(str);
for (i = recnum-1; i >= 0; i--)
gtk_combo_box_remove_text(combo, i);
return retval;
} // MojoGui_gtkplus2_destination
static int MojoGui_gtkplus2_productkey(const char *desc, const char *fmt,
char *buf, const int buflen,
boolean can_back, boolean can_fwd)
{
gchar *str = NULL;
int retval = 0;
const boolean fwd_at_start = isValidProductKey(fmt, buf);
gtk_entry_set_max_length(GTK_ENTRY(productkey), buflen - 1);
gtk_entry_set_width_chars(GTK_ENTRY(productkey), buflen - 1);
gtk_entry_set_text(GTK_ENTRY(productkey), (gchar *) buf);
const guint connid = gtk_signal_connect(GTK_OBJECT(productkey), "changed",
GTK_SIGNAL_FUNC(signal_productkey_changed),
(void *) fmt);
retval = run_wizard(desc, PAGE_PRODUCTKEY,
can_back, can_fwd, true, fwd_at_start);
gtk_signal_disconnect(GTK_OBJECT(productkey), connid);
str = gtk_editable_get_chars(GTK_EDITABLE(productkey), 0, -1);
// should never be invalid ("next" should be disabled in that case).
assert( (retval <= 0) || ((str) && (isValidProductKey(fmt, str))) );
assert(strlen(str) < buflen);
strcpy(buf, (char *) str);
g_free(str);
gtk_entry_set_text(GTK_ENTRY(productkey), "");
return retval;
} // MojoGui_gtkplus2_productkey
static boolean MojoGui_gtkplus2_insertmedia(const char *medianame)
{
gint rc = 0;
// !!! FIXME: Use stock GTK icon for "media"?
// !!! FIXME: better text.
const char *title = xstrdup(_("Media change"));
// !!! FIXME: better text.
const char *fmt = xstrdup(_("Please insert '%0'"));
const char *text = format(fmt, medianame);
rc = do_msgbox(title, text, GTK_MESSAGE_WARNING,
GTK_BUTTONS_OK_CANCEL, GTK_RESPONSE_OK, NULL);
free((void *) text);
free((void *) fmt);
free((void *) title);
return (rc == GTK_RESPONSE_OK);
} // MojoGui_gtkplus2_insertmedia
static void MojoGui_gtkplus2_progressitem(void)
{
// no-op in this UI target.
} // MojoGui_gtkplus2_progressitem
static boolean MojoGui_gtkplus2_progress(const char *type, const char *component,
int percent, const char *item,
boolean can_cancel)
{
static uint32 lastTicks = 0;
const uint32 ticks = ticks();
int rc;
if ((ticks - lastTicks) > 200) // just not to spam this...
{
GtkProgressBar *progress = GTK_PROGRESS_BAR(progressbar);
if (percent < 0)
gtk_progress_bar_pulse(progress);
else
gtk_progress_bar_set_fraction(progress, ((gdouble) percent) / 100.0);
gtk_progress_bar_set_text(progress, component);
gtk_label_set_text(GTK_LABEL(progresslabel), item);
lastTicks = ticks;
} // if
prepare_wizard(type, PAGE_PROGRESS, false, false, can_cancel, false);
rc = pump_events();
assert( (rc == ((int) CLICK_CANCEL)) || (rc == ((int) CLICK_NONE)) );
return (rc != CLICK_CANCEL);
} // MojoGui_gtkplus2_progress
static void MojoGui_gtkplus2_final(const char *msg)
{
gtk_widget_hide(next);
gtk_widget_show(finish);
gtk_label_set_text(GTK_LABEL(finallabel), msg);
run_wizard(_("Finish"), PAGE_FINAL, false, true, false, true);
} // MojoGui_gtkplus2_final
// end of gui_gtkplus2.c ...