summaryrefslogtreecommitdiff
path: root/frontend/xsane-back-gtk.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/xsane-back-gtk.c')
-rw-r--r--frontend/xsane-back-gtk.c1426
1 files changed, 1426 insertions, 0 deletions
diff --git a/frontend/xsane-back-gtk.c b/frontend/xsane-back-gtk.c
new file mode 100644
index 0000000..8bde762
--- /dev/null
+++ b/frontend/xsane-back-gtk.c
@@ -0,0 +1,1426 @@
+/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
+
+ xsane-back-gtk.c
+
+ Oliver Rauch <Oliver.Rauch@Wolfsburg.DE>
+ Copyright (C) 1998-2000 Oliver Rauch
+ This file is part of the XSANE package.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+#include "xsane.h"
+#include "xsane-back-gtk.h"
+#include "xsane-preferences.h"
+#include "xsane-text.h"
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* extern declarations */
+extern void xsane_panel_build(GSGDialog *dialog);
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* forward declarations: */
+static void xsane_back_gtk_panel_rebuild(GSGDialog *dialog);
+void xsane_set_sensitivity(SANE_Int sensitivity);
+void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d);
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+const char *xsane_back_gtk_unit_string(SANE_Unit unit)
+{
+ double d;
+
+ switch (unit)
+ {
+ case SANE_UNIT_NONE: return "none";
+ case SANE_UNIT_PIXEL: return "pixel";
+ case SANE_UNIT_BIT: return "bit";
+ case SANE_UNIT_DPI: return "dpi";
+ case SANE_UNIT_PERCENT: return "%";
+ case SANE_UNIT_MM:
+ d = preferences.length_unit;
+ if (d > 9.9 && d < 10.1)
+ {
+ return "cm";
+ }
+ else if (d > 25.3 && d < 25.5)
+ {
+ return "in";
+ }
+ return "mm";
+ case SANE_UNIT_MICROSECOND: return "\265s";
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_tooltip(GtkTooltips *tooltips, GtkWidget *widget, const char *desc)
+{
+ if (desc && desc[0])
+ {
+ gtk_tooltips_set_tip(tooltips, widget, desc, 0);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+int xsane_back_gtk_make_path(size_t buf_size, char *buf,
+ const char *prog_name,
+ const char *dir_name,
+ const char *prefix, const char *dev_name,
+ const char *postfix,
+ int location)
+{
+ struct passwd *pw;
+ size_t len, extra;
+ int i;
+
+ if (location == XSANE_PATH_LOCAL_SANE) /* make path to local file */
+ {
+ pw = getpwuid(getuid()); /* get homedirectory */
+ if (!pw)
+ {
+ snprintf(buf, buf_size, "%s %s", ERR_HOME_DIR, strerror(errno));
+ xsane_back_gtk_error(buf, FALSE);
+ return -1;
+ }
+
+ snprintf(buf, buf_size, "%s/.sane", pw->pw_dir);
+ mkdir(buf, 0777); /* ensure ~/.sane directory exists */
+ }
+ else if (location == XSANE_PATH_SYSTEM) /* make path to system file */
+ {
+ snprintf(buf, buf_size, "%s", STRINGIFY(PATH_SANE_DATA_DIR));
+ }
+ else /* make path to temporary file */
+ {
+ snprintf(buf, buf_size, "%s", PATH_SANE_TMP);
+ }
+
+ len = strlen(buf);
+
+ if (prog_name)
+ {
+ extra = strlen(prog_name);
+ if (len + extra + 1 >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+ memcpy(buf + len, prog_name, extra);
+ len += extra;
+ buf[len] = '\0';
+ mkdir(buf, 0777); /* ensure ~/.sane/PROG_NAME directory exists */
+ }
+ if (len >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+
+
+ if (dir_name)
+ {
+ extra = strlen(dir_name);
+ if (len + extra + 1 >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+ memcpy(buf + len, dir_name, extra);
+ len += extra;
+ buf[len] = '\0';
+ mkdir(buf, 0777); /* ensure DIR_NAME directory exists */
+ }
+
+ if (len >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ buf[len++] = '/';
+
+
+ if (prefix)
+ {
+ extra = strlen(prefix);
+ if (len + extra >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ memcpy(buf + len, prefix, extra);
+ len += extra;
+ }
+
+ if (location == XSANE_PATH_TMP) /* system tmp dir, add uid */
+ {
+ char uid_prefix[256];
+ uid_t uid;
+
+ uid = getuid();
+ snprintf(uid_prefix, sizeof(uid_prefix), "%d-", uid);
+
+ extra = strlen(uid_prefix);
+ if (len + extra >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ memcpy(buf + len, uid_prefix, extra);
+ len += extra;
+ }
+
+ if (dev_name)
+ {
+ /* Turn devicename into valid filename by replacing slashes by "_", "_" gets "__", spaces are erased */
+
+ for (i = 0; dev_name[i]; ++i)
+ {
+ if (len + 2 >= buf_size)
+ {
+ goto filename_too_long;
+ }
+
+ switch (dev_name[i])
+ {
+ case '/': /* "/" -> "_" */
+ buf[len++] = '_';
+ break;
+
+ case ' ': /* erase " " */
+ break;
+
+ case '_': /* "_" -> "__" */
+ buf[len++] = '_';
+ /* fall through */
+ default:
+ buf[len++] = dev_name[i];
+ break;
+ }
+ }
+ }
+
+ if (postfix)
+ {
+ extra = strlen(postfix);
+ if (len + extra >= buf_size)
+ {
+ goto filename_too_long;
+ }
+ memcpy(buf + len, postfix, extra);
+ len += extra;
+ }
+ if (len >= buf_size)
+ goto filename_too_long;
+
+ buf[len++] = '\0';
+ return 0;
+
+filename_too_long:
+ xsane_back_gtk_error(ERR_FILENAME_TOO_LONG, FALSE);
+ errno = E2BIG;
+ return -1;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_option(GSGDialog * dialog, int opt_num, void *val, SANE_Action action)
+{
+ SANE_Status status;
+ SANE_Int info;
+ char buf[256];
+
+ status = sane_control_option(dialog->dev, opt_num, action, val, &info);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, sane_get_option_descriptor(dialog->dev, opt_num)->name,
+ XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, FALSE);
+ return;
+ }
+
+ if ((info & SANE_INFO_RELOAD_PARAMS) && dialog->param_change_callback)
+ {
+ (*dialog->param_change_callback) (dialog, dialog->param_change_arg);
+ }
+
+ if (info & SANE_INFO_RELOAD_OPTIONS)
+ {
+ xsane_back_gtk_panel_rebuild(dialog);
+ if (dialog->option_reload_callback)
+ {
+ (*dialog->option_reload_callback) (dialog, dialog->option_reload_arg);
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_close_dialog_callback(GtkWidget * widget, gpointer data)
+{
+ gtk_widget_destroy(data);
+ xsane_back_gtk_message_dialog_active = 0;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static gint decision_flag;
+static GtkWidget *decision_dialog;
+
+void xsane_back_gtk_decision_callback(GtkWidget * widget, gpointer data)
+{
+ gtk_widget_destroy(decision_dialog);
+ xsane_back_gtk_message_dialog_active = 0;
+ decision_flag = (long) data;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+gint xsane_back_gtk_decision(gchar *title, gchar **xpm_d, gchar *message, gchar *oktext, gchar *rejecttext, gint wait)
+{
+ GtkWidget *main_vbox, *hbox, *label, *button;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ GtkWidget *pixmapwidget;
+
+ if (xsane_back_gtk_message_dialog_active)
+ {
+ fprintf(stderr, "%s: %s\n", title, message);
+ return TRUE;
+ }
+ xsane_back_gtk_message_dialog_active = 1;
+ decision_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
+ gtk_window_set_position(GTK_WINDOW(decision_dialog), GTK_WIN_POS_MOUSE);
+ gtk_window_set_title(GTK_WINDOW(decision_dialog), title);
+ gtk_signal_connect(GTK_OBJECT(decision_dialog), "delete_event",
+ GTK_SIGNAL_FUNC(xsane_back_gtk_decision_callback), (void *) -1); /* -1 = cancel */
+
+ xsane_set_window_icon(decision_dialog, 0);
+
+ /* create the main vbox */
+ main_vbox = gtk_vbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 5);
+ gtk_widget_show(main_vbox);
+
+ gtk_container_add(GTK_CONTAINER(decision_dialog), main_vbox);
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+ gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
+
+ /* the info icon */
+ if (xpm_d)
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d(decision_dialog->window, &mask, xsane.bg_trans, xpm_d);
+ pixmapwidget = gtk_pixmap_new(pixmap, mask);
+ gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 10);
+ gtk_widget_show(pixmapwidget);
+ gdk_pixmap_unref(pixmap);
+ }
+
+ /* the message */
+ label = gtk_label_new(message);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ gtk_widget_show(hbox);
+
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
+ gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
+
+ /* the confirmation button */
+ button = gtk_button_new_with_label(oktext);
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_back_gtk_decision_callback, (void *) 1 /* confirm */);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_grab_default(button);
+ gtk_widget_show(button);
+
+
+ if (rejecttext) /* the rejection button */
+ {
+ button = gtk_button_new_with_label(rejecttext);
+ gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_back_gtk_decision_callback, (void *) -1 /* reject */);
+ gtk_container_add(GTK_CONTAINER(hbox), button);
+ gtk_widget_show(button);
+ }
+ gtk_widget_show(hbox);
+
+ gtk_widget_show(decision_dialog);
+
+ if (!wait)
+ {
+ return TRUE;
+ }
+
+ decision_flag = 0;
+
+ while (decision_flag == 0)
+ {
+ gtk_main_iteration();
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+
+ if (decision_flag == 1)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_message(gchar *title, gchar **icon_xpm, gchar *message, gint wait)
+{
+ xsane_back_gtk_decision(title, icon_xpm, message, ERR_BUTTON_OK, 0 /* no reject text */, wait);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_error(gchar *error, gint wait)
+{
+ if (wait)
+ {
+ SANE_Int old_sensitivity = xsane.sensitivity;
+
+ xsane_set_sensitivity(FALSE);
+ xsane_back_gtk_message(ERR_HEADER_ERROR, (gchar**) error_xpm, error, wait);
+ xsane_set_sensitivity(old_sensitivity);
+ }
+ else
+ {
+ xsane_back_gtk_message(ERR_HEADER_ERROR, (gchar **) error_xpm, error, wait);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_warning(gchar *warning, gint wait)
+{
+ if (wait)
+ {
+ SANE_Int old_sensitivity = xsane.sensitivity;
+
+ xsane_set_sensitivity(FALSE);
+ xsane_back_gtk_message(ERR_HEADER_WARNING, (gchar**) warning_xpm, warning, wait);
+ xsane_set_sensitivity(old_sensitivity);
+ }
+ else
+ {
+ xsane_back_gtk_message(ERR_HEADER_WARNING, (gchar**) warning_xpm, warning, wait);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_get_filename_button_clicked(GtkWidget *w, gpointer data)
+{
+ int *clicked = data;
+ *clicked = 1;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+int xsane_back_gtk_get_filename(const char *label, const char *default_name, size_t max_len, char *filename, int show_fileopts)
+{
+ int cancel = 0, ok = 0, destroy = 0;
+ GtkWidget *fileselection;
+
+ fileselection = gtk_file_selection_new((char *) label);
+
+ gtk_signal_connect(GTK_OBJECT(fileselection),
+ "destroy", GTK_SIGNAL_FUNC(xsane_back_gtk_get_filename_button_clicked), &destroy);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->cancel_button),
+ "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &cancel);
+ gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->ok_button),
+ "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &ok);
+ if (default_name)
+ {
+ gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), (char *) default_name);
+ }
+
+ if (show_fileopts)
+ {
+ gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(fileselection));
+ }
+ else
+ {
+ gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(fileselection));
+ }
+
+ gtk_widget_show(fileselection);
+
+ while (!cancel && !ok && !destroy)
+ {
+ if (!gtk_events_pending())
+ {
+ usleep(100000);
+ }
+ gtk_main_iteration();
+ }
+
+ if (ok)
+ {
+ size_t len, cwd_len;
+ char *cwd;
+
+ strncpy(filename, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection)), max_len - 1);
+ filename[max_len - 1] = '\0';
+
+ len = strlen(filename);
+ cwd = alloca(len + 2);
+ getcwd(cwd, len + 1);
+ cwd_len = strlen(cwd);
+ cwd[cwd_len++] = '/';
+ cwd[cwd_len] = '\0';
+ if (strncmp(filename, cwd, cwd_len) == 0)
+ {
+ memcpy(filename, filename + cwd_len, len - cwd_len + 1);
+ }
+ }
+
+ if (!destroy)
+ {
+ gtk_widget_destroy(fileselection);
+ }
+
+ return ok ? 0 : -1;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_back_gtk_autobutton_update(GtkWidget *widget, GSGDialogElement *elem)
+{
+ GSGDialog *dialog = elem->dialog;
+ int opt_num = elem - dialog->element;
+ const SANE_Option_Descriptor *opt;
+ SANE_Status status;
+ SANE_Word val;
+ char buf[256];
+
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ xsane_back_gtk_set_option(dialog, opt_num, 0, SANE_ACTION_SET_AUTO);
+ }
+ else
+ {
+ status = sane_control_option(dialog->dev, opt_num, SANE_ACTION_GET_VALUE, &val, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_GET_OPTION, opt->name, XSANE_STRSTATUS(status));
+ xsane_back_gtk_error(buf, FALSE);
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ }
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_autobutton_new(GtkWidget *parent, GSGDialogElement *elem,
+ GtkWidget *label, GtkTooltips *tooltips)
+{
+ GtkWidget *button, *alignment;
+
+ button = gtk_check_button_new();
+ gtk_container_set_border_width(GTK_CONTAINER(button), 0);
+ gtk_widget_set_usize(button, 20, 20);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_back_gtk_autobutton_update, elem);
+ xsane_back_gtk_set_tooltip(tooltips, button, "Turns on automatic mode.");
+
+ alignment = gtk_alignment_new(0.0, 1.0, 0.5, 0.5);
+ gtk_container_add(GTK_CONTAINER(alignment), button);
+
+ gtk_box_pack_end(GTK_BOX(parent), label, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(parent), alignment, FALSE, FALSE, 2);
+
+ gtk_widget_show(alignment);
+ gtk_widget_show(button);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static gint xsane_back_gtk_button_update(GtkWidget * widget, GSGDialogElement * elem)
+{
+ GSGDialog *dialog = elem->dialog;
+ int opt_num = elem - dialog->element;
+ const SANE_Option_Descriptor *opt;
+ SANE_Word val = SANE_FALSE;
+
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ if (GTK_TOGGLE_BUTTON(widget)->active)
+ {
+ val = SANE_TRUE;
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_button_new(GtkWidget * parent, const char *name, SANE_Word val,
+ GSGDialogElement * elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *button;
+
+ button = gtk_check_button_new_with_label((char *) name);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), val);
+ gtk_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_back_gtk_button_update, elem);
+ gtk_box_pack_start(GTK_BOX(parent), button, FALSE, TRUE, 0);
+ gtk_widget_show(button);
+ xsane_back_gtk_set_tooltip(tooltips, button, desc);
+
+ gtk_widget_set_sensitive(button, settable);
+
+ elem->widget = button;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_scale_update(GtkAdjustment * adj_data, GSGDialogElement * elem)
+{
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ SANE_Word val, new_val;
+ int opt_num;
+ double d;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ switch(opt->type)
+ {
+ case SANE_TYPE_INT:
+ val = adj_data->value + 0.5;
+ break;
+
+ case SANE_TYPE_FIXED:
+ d = adj_data->value;
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ d *= preferences.length_unit;
+ }
+ val = SANE_FIX(d);
+ break;
+
+ default:
+ fprintf(stderr, "xsane_back_gtk_scale_update: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ return;
+ }
+
+ xsane_back_gtk_set_option(dialog, opt_num, &val, SANE_ACTION_SET_VALUE);
+ sane_control_option(dialog->dev, opt_num, SANE_ACTION_GET_VALUE, &new_val, 0);
+ if (new_val != val)
+ {
+ val = new_val;
+ goto value_changed;
+ }
+ return; /* value didn't change */
+
+value_changed:
+ switch(opt->type)
+ {
+ case SANE_TYPE_INT:
+ adj_data->value = val;
+ break;
+
+ case SANE_TYPE_FIXED:
+ d = SANE_UNFIX(val);
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ d /= preferences.length_unit;
+ }
+ adj_data->value = d;
+ break;
+
+ default:
+ break;
+ }
+ /* Let widget know that value changed _again_. This must converge
+ quickly---otherwise things would get very slow very quickly (as
+ in "infinite recursion"): */
+ gtk_signal_emit_by_name(GTK_OBJECT(adj_data), "value_changed");
+ return;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_scale_new(GtkWidget * parent, const char *name, gfloat val,
+ gfloat min, gfloat max, gfloat quant, int automatic,
+ GSGDialogElement * elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *hbox, *label, *scale;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new((char *) name);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ elem->data = gtk_adjustment_new(val, min, max, quant, 1.0, 0.0);
+ scale = gtk_hscale_new(GTK_ADJUSTMENT(elem->data));
+ xsane_back_gtk_set_tooltip(tooltips, scale, desc);
+ gtk_widget_set_usize(scale, 150, 0);
+
+ if (automatic)
+ {
+ xsane_back_gtk_autobutton_new(hbox, elem, scale, tooltips);
+ }
+ else
+ {
+ gtk_box_pack_end(GTK_BOX(hbox), scale, FALSE, FALSE, 0); /* make scales fixed */
+/* gtk_box_pack_end(GTK_BOX(hbox), scale, TRUE, TRUE, 0); */ /* make scales sizeable */
+ }
+
+ gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS);
+ gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_TOP);
+ if (quant - (int) quant == 0.0)
+ {
+ gtk_scale_set_digits(GTK_SCALE(scale), 0);
+ }
+ else
+ {
+ /* one place behind decimal point */
+ gtk_scale_set_digits(GTK_SCALE(scale), 1);
+ }
+
+ gtk_signal_connect(elem->data, "value_changed", (GtkSignalFunc) xsane_back_gtk_scale_update, elem);
+
+ gtk_widget_show(label);
+ gtk_widget_show(scale);
+ gtk_widget_show(hbox);
+
+ gtk_widget_set_sensitive(scale, settable);
+
+ elem->widget = scale;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_push_button_callback(GtkWidget * widget, gpointer data)
+{
+ GSGDialogElement *elem = data;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+
+ opt_num = elem - dialog->element;
+ xsane_back_gtk_set_option(dialog, opt_num, 0, SANE_ACTION_SET_VALUE);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static int xsane_back_gtk_option_menu_lookup(GSGMenuItem menu_items[], const char *string)
+{
+ int i;
+
+ for (i = 0; strcmp(menu_items[i].label, string) != 0; ++i);
+
+ return i;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_option_menu_callback(GtkWidget * widget, gpointer data)
+{
+ GSGMenuItem *menu_item = data;
+ GSGDialogElement *elem = menu_item->elem;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ int opt_num;
+ double dval;
+ SANE_Word val;
+ void *valp = &val;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ switch(opt->type)
+ {
+ case SANE_TYPE_INT:
+ sscanf(menu_item->label, "%d", &val);
+ break;
+
+ case SANE_TYPE_FIXED:
+ sscanf(menu_item->label, "%lg", &dval);
+ val = SANE_FIX(dval);
+ break;
+
+ case SANE_TYPE_STRING:
+ valp = menu_item->label;
+ break;
+
+ default:
+ fprintf(stderr, "xsane_back_gtk_option_menu_callback: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
+ break;
+ }
+ xsane_back_gtk_set_option(dialog, opt_num, valp, SANE_ACTION_SET_VALUE);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_option_menu_new(GtkWidget *parent, const char *name, char *str_list[],
+ const char *val, GSGDialogElement * elem,
+ GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *hbox, *label, *option_menu, *menu, *item;
+ GSGMenuItem *menu_items;
+ int i, num_items;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new((char *) name);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ for (num_items = 0; str_list[num_items]; ++num_items);
+ menu_items = malloc(num_items * sizeof(menu_items[0]));
+
+ menu = gtk_menu_new();
+ for (i = 0; i < num_items; ++i)
+ {
+ item = gtk_menu_item_new_with_label(_BGT(str_list[i]));
+ gtk_container_add(GTK_CONTAINER(menu), item);
+ gtk_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_back_gtk_option_menu_callback, menu_items + i);
+
+ gtk_widget_show(item);
+
+ menu_items[i].label = str_list[i];
+ menu_items[i].elem = elem;
+ menu_items[i].index = i;
+ }
+
+ option_menu = gtk_option_menu_new();
+ gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 2);
+ gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
+ gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), xsane_back_gtk_option_menu_lookup(menu_items, val));
+ xsane_back_gtk_set_tooltip(tooltips, option_menu, desc);
+
+ gtk_widget_show(label);
+ gtk_widget_show(option_menu);
+ gtk_widget_show(hbox);
+
+ gtk_widget_set_sensitive(option_menu, settable);
+
+ elem->widget = option_menu;
+ elem->menu_size = num_items;
+ elem->menu = menu_items;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_text_entry_callback(GtkWidget *w, gpointer data)
+{
+ GSGDialogElement *elem = data;
+ const SANE_Option_Descriptor *opt;
+ GSGDialog *dialog = elem->dialog;
+ gchar *text;
+ int opt_num;
+ char *buf;
+
+ opt_num = elem - dialog->element;
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+
+ buf = alloca(opt->size);
+ buf[0] = '\0';
+
+ text = gtk_entry_get_text(GTK_ENTRY(elem->widget));
+ if (text)
+ {
+ strncpy(buf, text, opt->size);
+ }
+ buf[opt->size - 1] = '\0';
+
+ xsane_back_gtk_set_option(dialog, opt_num, buf, SANE_ACTION_SET_VALUE);
+
+ if (strcmp(buf, text) != 0) /* the backend modified the option value; update widget: */
+ {
+ gtk_entry_set_text(GTK_ENTRY(elem->widget), buf);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_text_entry_new(GtkWidget * parent, const char *name, const char *val, GSGDialogElement *elem,
+ GtkTooltips *tooltips, const char *desc, SANE_Int settable)
+{
+ GtkWidget *hbox, *text, *label;
+
+ hbox = gtk_hbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
+ gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
+
+ label = gtk_label_new((char *) name);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
+
+ text = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(text), (char *) val);
+/* gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, TRUE, 0); */ /* text entry fixed */
+ gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0); /* text entry sizeable */
+ gtk_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_back_gtk_text_entry_callback, elem);
+ xsane_back_gtk_set_tooltip(tooltips, text, desc);
+
+ gtk_widget_show(hbox);
+ gtk_widget_show(label);
+ gtk_widget_show(text);
+
+ gtk_widget_set_sensitive(text, settable);
+
+ elem->widget = text;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+GtkWidget *xsane_back_gtk_group_new(GtkWidget *parent, const char * title)
+{
+ GtkWidget * frame, * vbox;
+
+ frame = gtk_frame_new((char *) title);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
+ gtk_box_pack_start(GTK_BOX(parent), frame, FALSE, FALSE, 0);
+
+ vbox = gtk_vbox_new(FALSE, 4);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_widget_show(vbox);
+ return vbox;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+#if 0
+static GtkWidget* xsane_back_gtk_curve_new(GSGDialog *dialog, int optnum)
+{
+ const SANE_Option_Descriptor * opt;
+ gfloat fmin, fmax, val, *vector;
+ SANE_Word *optval, min, max;
+ GtkWidget *curve, *gamma;
+ SANE_Status status;
+ SANE_Handle dev;
+ int i, optlen;
+
+ gamma = gtk_gamma_curve_new();
+ curve = GTK_GAMMA_CURVE(gamma)->curve;
+ dev = dialog->dev;
+
+ opt = sane_get_option_descriptor(dev, optnum);
+ optlen = opt->size / sizeof(SANE_Word);
+ vector = alloca(optlen * (sizeof(vector[0]) + sizeof(optval[0])));
+ optval = (SANE_Word *) (vector + optlen);
+
+ min = max = 0;
+ switch(opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ min = opt->constraint.range->min;
+ max = opt->constraint.range->max;
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (opt->constraint.word_list[0] > 1)
+ {
+ min = max = opt->constraint.word_list[1];
+ for (i = 2; i < opt->constraint.word_list[0]; ++i)
+ {
+ if (opt->constraint.word_list[i] < min)
+ {
+ min = opt->constraint.word_list[i];
+ }
+
+ if (opt->constraint.word_list[i] > max)
+ {
+ max = opt->constraint.word_list[i];
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (min == max)
+ {
+ fprintf(stderr, "xsane_back_gtk_curve_new: %s: `%s'\n", WARN_NO_VALUE_CONSTRAINT, opt->name);
+ fmin = 0;
+ fmax = 255;
+ }
+ else if (opt->type == SANE_TYPE_FIXED)
+ {
+ fmin = SANE_UNFIX(min);
+ fmax = SANE_UNFIX(max);
+ }
+ else
+ {
+ fmin = min;
+ fmax = max;
+ }
+ gtk_curve_set_range(GTK_CURVE(curve), 0, optlen - 1, fmin, fmax);
+
+ status = sane_control_option(dev, optnum, SANE_ACTION_GET_VALUE, optval, 0);
+ if (status == SANE_STATUS_GOOD)
+ {
+ for (i = 0; i < optlen; ++i)
+ {
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ val = SANE_UNFIX(optval[i]);
+ }
+ else
+ {
+ val = optval[i];
+ }
+ vector[i] = val;
+ }
+ gtk_curve_set_vector(GTK_CURVE(curve), optlen, vector);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(gamma, FALSE);
+ }
+
+ return gamma;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_vector_new(GSGDialog * dialog, GtkWidget *vbox, int num_vopts, int *vopts)
+{
+ GtkWidget *notebook, *label, *curve;
+ const SANE_Option_Descriptor *opt;
+ int i;
+
+ notebook = gtk_notebook_new();
+ gtk_container_set_border_width(GTK_CONTAINER(notebook), 4);
+ gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
+
+ for (i = 0; i < num_vopts; ++i)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, vopts[i]);
+
+ label = gtk_label_new((char *) opt->title);
+ vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);
+ gtk_widget_show(vbox);
+ gtk_widget_show(label);
+
+ curve = xsane_back_gtk_curve_new(dialog, vopts[i]);
+ gtk_container_set_border_width(GTK_CONTAINER(curve), 4);
+ gtk_box_pack_start(GTK_BOX(vbox), curve, TRUE, TRUE, 0);
+ gtk_widget_show(curve);
+
+ dialog->element[vopts[i]].widget = curve;
+ }
+ gtk_widget_show(notebook);
+}
+#endif
+/* ----------------------------------------------------------------------------------------------------------------- */
+#if 0
+static void tooltips_destroy(GSGDialog * dialog)
+{
+ gtk_object_unref(GTK_OBJECT(dialog->tooltips));
+ dialog->tooltips = 0;
+}
+#endif
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+static void xsane_back_gtk_panel_destroy(GSGDialog * dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ GSGDialogElement *elem;
+ int i, j;
+
+ gtk_widget_destroy(dialog->xsane_hbox);
+ gtk_widget_destroy(dialog->standard_hbox);
+ gtk_widget_destroy(dialog->advanced_hbox);
+
+ /* free the menu labels of integer/fix-point word-lists: */
+ for (i = 0; i < dialog->num_elements; ++i)
+ {
+ if (dialog->element[i].menu)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, i);
+ elem = dialog->element + i;
+ if (opt->type != SANE_TYPE_STRING)
+ {
+ for (j = 0; j < elem->menu_size; ++j)
+ {
+ if (elem->menu[j].label)
+ {
+ free(elem->menu[j].label);
+ elem->menu[j].label = 0;
+ }
+ }
+ free(elem->menu);
+ elem->menu = 0;
+ }
+ }
+ }
+ memset(dialog->element, 0, dialog->num_elements * sizeof(dialog->element[0]));
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* When an setting an option changes the dialog, everything may
+ change: the option titles, the activity-status of the option, its
+ constraints or what not. Thus, rather than trying to be clever in
+ detecting what exactly changed, we use a brute-force method of
+ rebuilding the entire dialog. */
+
+static void xsane_back_gtk_panel_rebuild(GSGDialog * dialog)
+{
+ xsane_back_gtk_panel_destroy(dialog);
+ xsane_panel_build(dialog);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_refresh_dialog(GSGDialog *dialog)
+{
+ xsane_back_gtk_panel_rebuild(dialog);
+ if (dialog->param_change_callback)
+ {
+ (*dialog->param_change_callback) (dialog, dialog->param_change_arg);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_update_scan_window(GSGDialog *dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ double old_val, new_val;
+ GSGDialogElement *elem;
+ SANE_Status status;
+ SANE_Word word;
+ int i, optnum;
+ char str[64];
+
+ for (i = 0; i < 4; ++i)
+ {
+ if (dialog->well_known.coord[i] > 0)
+ {
+ optnum = dialog->well_known.coord[i];
+ elem = dialog->element + optnum;
+ opt = sane_get_option_descriptor(dialog->dev, optnum);
+
+ status = sane_control_option(dialog->dev, optnum, SANE_ACTION_GET_VALUE, &word, 0);
+ if (status != SANE_STATUS_GOOD)
+ {
+ continue; /* sliently ignore errors */
+ }
+
+ switch(opt->constraint_type)
+ {
+ case SANE_CONSTRAINT_RANGE:
+ if (opt->type == SANE_TYPE_INT)
+ {
+ old_val = GTK_ADJUSTMENT(elem->data)->value;
+ new_val = word;
+ GTK_ADJUSTMENT(elem->data)->value = new_val;
+ }
+ else
+ {
+ old_val = GTK_ADJUSTMENT(elem->data)->value;
+ new_val = SANE_UNFIX(word);
+ if (opt->unit == SANE_UNIT_MM)
+ {
+ new_val /= preferences.length_unit;
+ }
+ GTK_ADJUSTMENT(elem->data)->value = new_val;
+ }
+
+ if (old_val != new_val)
+ {
+ gtk_signal_emit_by_name(GTK_OBJECT(elem->data), "value_changed");
+ }
+ break;
+
+ case SANE_CONSTRAINT_WORD_LIST:
+ if (opt->type == SANE_TYPE_INT)
+ {
+ sprintf(str, "%d", word);
+ }
+ else
+ {
+ sprintf(str, "%g", SANE_UNFIX(word));
+ }
+ /* XXX maybe we should call this only when the value changes... */
+ gtk_option_menu_set_history(GTK_OPTION_MENU(elem->widget), xsane_back_gtk_option_menu_lookup(elem->menu, str));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+/* Ensure sure the device has up-to-date option values. Except for
+ vectors, all option values are kept current. Vectors are
+ downloaded into the device during this call. */
+void xsane_back_gtk_sync(GSGDialog *dialog)
+{
+ const SANE_Option_Descriptor *opt;
+ gfloat val, *vector;
+ SANE_Word *optval;
+ int i, j, optlen;
+ GtkWidget *curve;
+
+ for (i = 1; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, i);
+
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ continue;
+ }
+
+ if (opt->type != SANE_TYPE_INT && opt->type != SANE_TYPE_FIXED)
+ {
+ continue;
+ }
+
+ if (opt->size == sizeof(SANE_Word))
+ {
+ continue;
+ }
+
+ /* ok, we're dealing with an active vector */
+
+ optlen = opt->size / sizeof(SANE_Word);
+ optval = alloca(optlen * sizeof(optval[0]));
+ vector = alloca(optlen * sizeof(vector[0]));
+
+ curve = GTK_GAMMA_CURVE(dialog->element[i].widget)->curve;
+ gtk_curve_get_vector(GTK_CURVE(curve), optlen, vector);
+ for (j = 0; j < optlen; ++j)
+ {
+ val = vector[j];
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ optval[j] = SANE_FIX(val);
+ }
+ else
+ {
+ optval[j] = val + 0.5;
+ }
+ }
+
+ xsane_back_gtk_set_option(dialog, i, optval, SANE_ACTION_SET_VALUE);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_update_vector(GSGDialog *dialog, int opt_num, SANE_Int *vector)
+{
+ const SANE_Option_Descriptor *opt;
+ gfloat val;
+ SANE_Word *optval;
+ int j, optlen;
+
+ if (opt_num < 1)
+ return; /* not defined */
+
+ opt = sane_get_option_descriptor(dialog->dev, opt_num);
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap))
+ {
+ return; /* inactive */
+ }
+
+ if (opt->type != SANE_TYPE_INT && opt->type != SANE_TYPE_FIXED)
+ {
+ return;
+ }
+
+ if (opt->size == sizeof(SANE_Word))
+ {
+ return;
+ }
+
+ /* ok, we're dealing with an active vector */
+
+ optlen = opt->size / sizeof(SANE_Word);
+ optval = alloca(optlen * sizeof(optval[0]));
+ for (j = 0; j < optlen; ++j)
+ {
+ val = vector[j];
+ if (opt->type == SANE_TYPE_FIXED)
+ {
+ optval[j] = SANE_FIX(val);
+ }
+ else
+ {
+ optval[j] = val + 0.5;
+ }
+ }
+
+ xsane_back_gtk_set_option(dialog, opt_num, optval, SANE_ACTION_SET_VALUE);
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_tooltips(GSGDialog *dialog, int enable)
+{
+ if (!dialog->tooltips)
+ {
+ return;
+ }
+
+ if (enable)
+ {
+ gtk_tooltips_enable(dialog->tooltips);
+ }
+ else
+ {
+ gtk_tooltips_disable(dialog->tooltips);
+ }
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_set_sensitivity(GSGDialog *dialog, int sensitive)
+{
+ const SANE_Option_Descriptor *opt;
+ int i;
+
+ for (i = 0; i < dialog->num_elements; ++i)
+ {
+ opt = sane_get_option_descriptor(dialog->dev, i);
+
+ if (!SANE_OPTION_IS_ACTIVE(opt->cap) || !SANE_OPTION_IS_SETTABLE(opt->cap) ||
+ opt->type == SANE_TYPE_GROUP || !dialog->element[i].widget)
+ {
+ continue;
+ }
+
+ if (!(opt->cap & SANE_CAP_ALWAYS_SETTABLE))
+ {
+ gtk_widget_set_sensitive(dialog->element[i].widget, sensitive);
+ }
+ }
+
+ if (dialog)
+ {
+ if (dialog->xsanemode_widget)
+ {
+ gtk_widget_set_sensitive(dialog->xsanemode_widget, sensitive);
+ }
+ }
+
+ while (gtk_events_pending())
+ {
+ gtk_main_iteration();
+ }
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_set_sensitivity(SANE_Int sensitivity)
+{
+ if (xsane.shell)
+ {
+ gtk_widget_set_sensitive(xsane.shell, sensitivity);
+ gtk_widget_set_sensitive(xsane.standard_options_shell, sensitivity);
+ gtk_widget_set_sensitive(xsane.advanced_options_shell, sensitivity);
+ gtk_widget_set_sensitive(xsane.histogram_dialog, sensitivity);
+ }
+
+ if (xsane.preview)
+ {
+ gtk_widget_set_sensitive(xsane.preview->button_box, sensitivity); /* button box at top of window */
+#if 0
+ gtk_widget_set_sensitive(xsane.preview->viewport, sensitivity); /* Preview image selection */
+#endif
+ gtk_widget_set_sensitive(xsane.preview->start, sensitivity); /* Acquire preview button */
+ }
+
+ if (xsane.fax_dialog)
+ {
+ gtk_widget_set_sensitive(xsane.fax_dialog, sensitivity);
+ }
+
+ if (dialog)
+ {
+ xsane_back_gtk_set_sensitivity(dialog, sensitivity);
+ }
+
+ while (gtk_events_pending()) /* make sure set_sensitivity is displayed */
+ {
+ gtk_main_iteration();
+ }
+
+ xsane.sensitivity = sensitivity;
+}
+
+/* ----------------------------------------------------------------------------------------------------------------- */
+
+void xsane_back_gtk_destroy_dialog(GSGDialog * dialog)
+{
+ SANE_Handle dev = dialog->dev;
+
+ xsane_back_gtk_panel_destroy(dialog);
+ free((void *) dialog->dev_name);
+ free(dialog->element);
+ free(dialog);
+
+ sane_close(dev);
+}
+/* ---------------------------------------------------------------------------------------------------------------------- */
+
+void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d)
+{
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+
+ gtk_widget_realize(gtk_window);
+ if (xpm_d)
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d(gtk_window->window, &mask, xsane.bg_trans, xpm_d);
+ }
+ else
+ {
+ if (xsane.window_icon_pixmap)
+ {
+ pixmap = xsane.window_icon_pixmap;
+ mask = xsane.window_icon_mask;
+ }
+ else
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d(gtk_window->window, &mask, xsane.bg_trans, (gchar **) xsane_window_icon_xpm);
+ }
+ }
+
+ gdk_window_set_icon(gtk_window->window, 0, pixmap, mask);
+}
+
+/* ---------------------------------------------------------------------------------------------------------------------- */