diff options
Diffstat (limited to 'frontend/xsane-back-gtk.c')
-rw-r--r-- | frontend/xsane-back-gtk.c | 1426 |
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); +} + +/* ---------------------------------------------------------------------------------------------------------------------- */ |