/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend xsane-back-gtk.c Oliver Rauch Copyright (C) 1998-2002 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-front-gtk.h" #include "xsane-preferences.h" #include "xsane-gamma.h" /* ----------------------------------------------------------------------------------------------------------------- */ /* extern declarations */ extern void xsane_panel_build(void); /* ----------------------------------------------------------------------------------------------------------------- */ /* forward declarations: */ SANE_Status xsane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int *info); const SANE_Option_Descriptor *xsane_get_option_descriptor(SANE_Handle handle, SANE_Int option); const char *xsane_back_gtk_unit_string(SANE_Unit unit); void xsane_back_gtk_set_tooltip(GtkTooltips *tooltips, GtkWidget *widget, const gchar *desc); 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); void xsane_back_gtk_set_option(int opt_num, void *val, SANE_Action action); static void xsane_back_gtk_panel_rebuild(void); void xsane_set_sensitivity(SANE_Int sensitivity); void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d); /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_bound_int(int *value, int min, int max) { DBG(DBG_proc3, "xsane_bound_int\n"); if (min > max) { int help = min; min = max; max = help; } if (*value < min) { *value = min; } if (*value > max) { *value = max; } } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_bound_float(float *value, float min, float max) { DBG(DBG_proc3, "xsane_bound_float\n"); if (min > max) { double help = min; min = max; max = help; } if (*value < min) { *value = min; } if (*value > max) { *value = max; } } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_bound_double(double *value, double min, double max) { DBG(DBG_proc3, "xsane_bound_double\n"); if (min > max) { double help = min; min = max; max = help; } if (*value < min) { *value = min; } if (*value > max) { *value = max; } } /* ----------------------------------------------------------------------------------------------------------------- */ /* returns 1 if value is in bounds */ int xsane_check_bound_double(double value, double min, double max) { int in_bounds = 1; DBG(DBG_proc3, "xsane_check_bound_double\n"); if (min > max) { double help = min; min = max; max = help; } if (value < min) { in_bounds = 0; } if (value > max) { in_bounds = 0; } return (in_bounds); } /* ----------------------------------------------------------------------------------------------------------------- */ const SANE_Option_Descriptor *xsane_get_option_descriptor(SANE_Handle handle, SANE_Int option) { DBG(DBG_optdesc, "xsane_get_option_descriptor(%d)\n", option); if (option >= 0) { return sane_get_option_descriptor(handle, option); } return NULL; } /* ----------------------------------------------------------------------------------------------------------------- */ SANE_Status xsane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int *info) { DBG(DBG_proc, "xsane_control_option(option = %d, action = %d)\n", option, action); if (option >= 0) { SANE_Status status; status = sane_control_option(handle, option, action, val, info); if (status) { DBG(DBG_error, "ERROR: xsane_control_option(option = %d, action = %d) failed\n", option, action); } return status; } return SANE_STATUS_INVAL; } /* ----------------------------------------------------------------------------------------------------------------- */ const char *xsane_back_gtk_unit_string(SANE_Unit unit) { DBG(DBG_proc, "xsane_back_gtk_unit_string\n"); switch (unit) { case SANE_UNIT_NONE: return "none"; case SANE_UNIT_PIXEL: return "px"; case SANE_UNIT_BIT: return "bit"; case SANE_UNIT_DPI: return "dpi"; case SANE_UNIT_PERCENT: return "%"; case SANE_UNIT_MM: if (preferences.length_unit > 9.9 && preferences.length_unit < 10.1) { return "cm"; } else if (preferences.length_unit > 25.3 && preferences.length_unit < 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 gchar *desc) { DBG(DBG_proc, "xsane_back_gtk_set_tooltip\n"); 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) { size_t len, extra; int i; DBG(DBG_proc, "xsane_back_gtk_make_path\n"); if (location == XSANE_PATH_LOCAL_SANE) /* make path to local file */ { if (getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)) != NULL) { snprintf(buf, buf_size-2, "%s%c.sane", getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)), SLASH); } else { snprintf(buf, buf_size-2, "%s", STRINGIFY(XSANE_FIXED_HOME_PATH)); } mkdir(buf, 0777); /* ensure ~/.sane directory exists */ } else if (location == XSANE_PATH_SYSTEM) /* make path to system file */ { snprintf(buf, buf_size-2, "%s", STRINGIFY(PATH_SANE_DATA_DIR)); } else /* make path to temporary file */ { snprintf(buf, buf_size-2, "%s", preferences.tmp_path); } len = strlen(buf); buf[len++] = SLASH; if (prog_name) { extra = strlen(prog_name); if (len + extra + 2 >= buf_size) { goto filename_too_long; } memcpy(buf + len, prog_name, extra); len += extra; buf[len] = '\0'; mkdir(buf, 0777); /* ensure ~/.sane/PROG_NAME directory exists */ buf[len++] = SLASH; /* OS/2 does not like slash at end of mktemp-path */ } if (len >= buf_size) { goto filename_too_long; } if (dir_name) { extra = strlen(dir_name); if (len + extra + 2 >= buf_size) { goto filename_too_long; } memcpy(buf + len, dir_name, extra); len += extra; buf[len++] = SLASH; buf[len] = '\0'; mkdir(buf, 0777); /* ensure DIR_NAME directory exists */ } if (len >= buf_size) { goto filename_too_long; } if (prefix) { extra = strlen(prefix); if (len + extra >= buf_size) { goto filename_too_long; } memcpy(buf + len, 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 '/': /* "/" -> "_" */ buf[len++] = '_'; break; #ifdef _WIN32 case ':': /* ":" -> "_" */ buf[len++] = '_'; break; #endif #ifdef HAVE_OS2_H case ':': /* ":" -> "_" */ buf[len++] = '_'; break; #endif 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; } if (location == XSANE_PATH_TMP) /* tmp dir, add uid */ { char tmpbuf[256]; uid_t uid; int fd; uid = getuid(); snprintf(tmpbuf, sizeof(tmpbuf), "-%d-", uid); extra = strlen(tmpbuf); if (len + extra >= buf_size) { goto filename_too_long; } memcpy(buf + len, tmpbuf, extra); len += extra; if (len + 7 >= buf_size) { goto filename_too_long; } memcpy(buf + len, "XXXXXX", 6); /* create unique filename */ len += 6; buf[len] = '\0'; #if 1 fd = mkstemp(buf); /* create unique filename and opens/creates the file */ #else mktemp(buf); /* not safe */ umask(0177); fd = open(buf, O_WRONLY | O_EXCL | O_CREAT, 0600); umask(XSANE_DEFAULT_UMASK); /* define new file permissions */ #endif if (fd == -1) { xsane_back_gtk_error(ERR_CREATE_TEMP_FILE, FALSE); return -1; } close(fd); /* will be opened again later */ } else { buf[len++] = '\0'; } DBG(DBG_proc, "path = \"%s\"\n", buf); return 0; filename_too_long: xsane_back_gtk_error(ERR_FILENAME_TOO_LONG, FALSE); errno = E2BIG; return -1; } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_set_option(int opt_num, void *val, SANE_Action action) { SANE_Status status; SANE_Int info; char buf[256]; int old_colors = xsane.xsane_colors; int update_gamma = FALSE; DBG(DBG_proc, "xsane_back_gtk_set_option\n"); status = xsane_control_option(xsane.dev, opt_num, action, val, &info); if (status != SANE_STATUS_GOOD) { snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, xsane_get_option_descriptor(xsane.dev, opt_num)->name, XSANE_STRSTATUS(status)); xsane_back_gtk_error(buf, FALSE); return; } if (info & SANE_INFO_RELOAD_PARAMS) { xsane_update_param(0); } if (info & SANE_INFO_RELOAD_OPTIONS) { xsane_back_gtk_panel_rebuild(); if (xsane.preview) { preview_update_surface(xsane.preview, 0); } update_gamma = TRUE; /* scanner gamma correction may have changed, medium may need update */ } if (xsane.xsane_colors != old_colors) { /* we have to update gamma tables and histogram because medium settings */ /* may have changed */ update_gamma = TRUE; } if (update_gamma) { xsane_update_gamma_curve(TRUE); } } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_close_dialog_callback(GtkWidget * widget, gpointer data) { DBG(DBG_proc, "xsane_back_gtk_close_dialog_callback\n"); 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) { DBG(DBG_proc, "xsane_back_gtk_decision_callback\n"); 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, int wait) { GtkWidget *main_vbox, *hbox, *label, *button; GdkPixmap *pixmap; GdkBitmap *mask; GtkWidget *pixmapwidget; DBG(DBG_proc, "xsane_back_gtk_decision\n"); if (xsane.back_gtk_message_dialog_active) { DBG(DBG_error0, "%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_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); 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_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 5); gtk_widget_show(button); } gtk_widget_show(hbox); gtk_widget_show(decision_dialog); while (gtk_events_pending()) { gtk_main_iteration(); } 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, int wait) { DBG(DBG_proc, "xsane_back_gtk_message\n"); xsane_back_gtk_decision(title, icon_xpm, message, BUTTON_OK, 0 /* no reject text */, wait); } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_error(gchar *error, int wait) { DBG(DBG_proc, "xsane_back_gtk_error: %s\n", error); 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, int wait) { DBG(DBG_proc, "xsane_back_gtk_warning: %s\n", warning); 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); } } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_info(gchar *info, int wait) { DBG(DBG_proc, "xsane_back_gtk_info: %s\n", info); if (wait) { SANE_Int old_sensitivity = xsane.sensitivity; xsane_set_sensitivity(FALSE); xsane_back_gtk_message(ERR_HEADER_INFO, (gchar**) info_xpm, info, wait); xsane_set_sensitivity(old_sensitivity); } else { xsane_back_gtk_message(ERR_HEADER_INFO, (gchar**) info_xpm, info, wait); } } /* ----------------------------------------------------------------------------------------------------------------- */ static void xsane_back_gtk_get_filename_button_clicked(GtkWidget *w, gpointer data) { int *clicked = data; DBG(DBG_proc, "xsane_back_gtk_get_filename_button_clicked\n"); *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 shorten_path, int hide_file_list) { int cancel = 0, ok = 0, destroy = 0; GtkWidget *fileselection; GtkAccelGroup *accelerator_group; DBG(DBG_proc, "xsane_back_gtk_get_filename\n"); fileselection = gtk_file_selection_new((char *) label); accelerator_group = gtk_accel_group_new(); gtk_accel_group_attach(accelerator_group, GTK_OBJECT(fileselection)); 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_widget_add_accelerator(GTK_FILE_SELECTION(fileselection)->cancel_button, "clicked", accelerator_group, GDK_Escape, 0, GTK_ACCEL_LOCKED); gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->ok_button), "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &ok); if (default_name) { DBG(DBG_info, "xsane_back_gtk_get_filename: default_name =%s\n", default_name); gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), (char *) default_name); } if (hide_file_list) { DBG(DBG_info, "xsane_back_gtk_get_filename: hiding file-list and delete-file-widget\n"); gtk_widget_hide(GTK_FILE_SELECTION(fileselection)->file_list->parent); gtk_widget_hide(GTK_FILE_SELECTION(fileselection)->fileop_del_file); } if (show_fileopts) { DBG(DBG_info, "xsane_back_gtk_get_filename: showing file-options\n"); gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(fileselection)); } else { DBG(DBG_info, "xsane_back_gtk_get_filename: hiding file-options\n"); gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(fileselection)); } gtk_widget_show(fileselection); DBG(DBG_info, "xsane_back_gtk_get_filename: waiting for user action\n"); while (!cancel && !ok && !destroy) { if (gtk_events_pending()) { gtk_main_iteration(); } } if (ok) { size_t len, cwd_len; char *cwd; DBG(DBG_info, "ok button pressed\n"); 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); /* alloca => memory is freed on return */ getcwd(cwd, len + 1); cwd_len = strlen(cwd); cwd[cwd_len++] = '/'; cwd[cwd_len] = '\0'; DBG(DBG_info, "xsane_back_gtk_get_filename: full path filename = %s\n", filename); if (shorten_path && (strncmp(filename, cwd, cwd_len) == 0)) { memcpy(filename, filename + cwd_len, len - cwd_len + 1); DBG(DBG_info, "xsane_back_gtk_get_filename: short path filename = %s\n", filename); } } if (!destroy) { gtk_widget_destroy(fileselection); } return ok ? 0 : -1; } /* ----------------------------------------------------------------------------------------------------------------- */ static gint xsane_back_gtk_autobutton_update(GtkWidget *widget, GSGDialogElement *elem) { int opt_num = elem - xsane.element; const SANE_Option_Descriptor *opt; SANE_Status status; SANE_Word val; char buf[256]; DBG(DBG_proc, "xsane_back_gtk_autobutton_update\n"); opt = xsane_get_option_descriptor(xsane.dev, opt_num); if (GTK_TOGGLE_BUTTON(widget)->active) { xsane_back_gtk_set_option(opt_num, 0, SANE_ACTION_SET_AUTO); } else { status = xsane_control_option(xsane.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(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; DBG(DBG_proc, "xsane_back_gtk_autobutton_new\n"); 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) { int opt_num = elem - xsane.element; const SANE_Option_Descriptor *opt; SANE_Word val = SANE_FALSE; DBG(DBG_proc, "xsane_back_gtk_button_update\n"); opt = xsane_get_option_descriptor(xsane.dev, opt_num); if (GTK_TOGGLE_BUTTON(widget)->active) { val = SANE_TRUE; } xsane_back_gtk_set_option(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; DBG(DBG_proc, "xsane_back_gtk_button_new\n"); 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; SANE_Word val, new_val; int opt_num; double d; DBG(DBG_proc, "xsane_back_gtk_scale_update\n"); opt_num = elem - xsane.element; opt = xsane_get_option_descriptor(xsane.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: DBG(DBG_error, "xsane_back_gtk_scale_update: %s %d\n", ERR_UNKNOWN_TYPE, opt->type); return; } xsane_back_gtk_set_option(opt_num, &val, SANE_ACTION_SET_VALUE); xsane_control_option(xsane.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; DBG(DBG_proc, "xsane_back_gtk_scale_new(%s)\n", name); 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, quant*10, 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 { /* set number of digits in dependacne of quantization */ gtk_scale_set_digits(GTK_SCALE(scale), (int) log10(1/quant)+0.8); } 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; int opt_num; DBG(DBG_proc, "xsane_back_gtk_push_button_callback\n"); opt_num = elem - xsane.element; xsane_back_gtk_set_option(opt_num, 0, SANE_ACTION_SET_VALUE); } /* ----------------------------------------------------------------------------------------------------------------- */ static int xsane_back_gtk_option_menu_lookup(GSGMenuItem menu_items[], const char *string) { int i; DBG(DBG_proc, "xsane_back_gtk_option_menu_lookup\n"); 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; int opt_num; double dval; SANE_Word val; void *valp = &val; DBG(DBG_proc, "xsane_back_gtk_option_menu_callback\n"); opt_num = elem - xsane.element; opt = xsane_get_option_descriptor(xsane.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: DBG(DBG_error, "xsane_back_gtk_option_menu_callback: %s %d\n", ERR_UNKNOWN_TYPE, opt->type); break; } xsane_back_gtk_set_option(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; DBG(DBG_proc, "xsane_back_gtk_option_menu_new(%s)\n", name); 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; gchar *text; int opt_num; char *buf; DBG(DBG_proc, "xsane_back_gtk_text_entry_callback\n"); opt_num = elem - xsane.element; opt = xsane_get_option_descriptor(xsane.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(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; DBG(DBG_proc, "xsane_back_gtk_text_entry_new\n"); 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; DBG(DBG_proc, "xsane_back_gtk_group_new(%s)\n", title); 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 void tooltips_destroy(void) { DBG(DBG_proc, "tooltips_destroy\n"); gtk_object_unref(GTK_OBJECT(xsane.tooltips)); xsane.tooltips = 0; } #endif /* ----------------------------------------------------------------------------------------------------------------- */ static void xsane_back_gtk_panel_destroy(void) { const SANE_Option_Descriptor *opt; GSGDialogElement *elem; int i, j; DBG(DBG_proc, "xsane_back_gtk_panel_destroy\n"); gtk_widget_destroy(xsane.xsane_hbox); gtk_widget_destroy(xsane.standard_hbox); gtk_widget_destroy(xsane.advanced_hbox); /* free the menu labels of integer/fix-point word-lists: */ for (i = 0; i < xsane.num_elements; ++i) { if (xsane.element[i].menu) { opt = xsane_get_option_descriptor(xsane.dev, i); elem = xsane.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(xsane.element, 0, xsane.num_elements * sizeof(xsane.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(void) { DBG(DBG_proc, "xsane_back_gtk_panel_rebuild\n"); xsane_back_gtk_panel_destroy(); xsane_panel_build(); } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_refresh_dialog(void) { DBG(DBG_proc, "xsane_back_gtk_refresh_dialog\n"); xsane_back_gtk_panel_rebuild(); xsane_update_param(0); } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_update_scan_window(void) { const SANE_Option_Descriptor *opt; double old_val, new_val; GSGDialogElement *elem; SANE_Status status; SANE_Word word; int i, optnum; char str[64]; DBG(DBG_proc, "xsane_back_gtk_update_scan_window\n"); for (i = 0; i < 4; ++i) { if (xsane.well_known.coord[i] > 0) { optnum = xsane.well_known.coord[i]; elem = xsane.element + optnum; opt = xsane_get_option_descriptor(xsane.dev, optnum); status = xsane_control_option(xsane.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(void) { const SANE_Option_Descriptor *opt; gfloat val, *vector; SANE_Word *optval; int i, j, optlen; GtkWidget *curve; DBG(DBG_proc, "xsane_back_gtk_sync\n"); for (i = 1; i < xsane.num_elements; ++i) { opt = xsane_get_option_descriptor(xsane.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(xsane.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(i, optval, SANE_ACTION_SET_VALUE); } } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_update_vector(int opt_num, SANE_Int *vector) { const SANE_Option_Descriptor *opt; gfloat val; SANE_Word *optval; int j, optlen; DBG(DBG_proc, "xsane_back_gtk_update_vector\n"); if (opt_num < 1) return; /* not defined */ opt = xsane_get_option_descriptor(xsane.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(opt_num, optval, SANE_ACTION_SET_VALUE); } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_set_tooltips(int enable) { DBG(DBG_proc, "xsane_back_gtk_set_tooltips\n"); if (!xsane.tooltips) { return; } if (enable) { gtk_tooltips_enable(xsane.tooltips); } else { gtk_tooltips_disable(xsane.tooltips); } } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_set_sensitivity(int sensitive) { const SANE_Option_Descriptor *opt; int i; DBG(DBG_proc, "xsane_back_gtk_set_sensitivity\n"); for (i = 0; i < xsane.num_elements; ++i) { opt = xsane_get_option_descriptor(xsane.dev, i); if (!SANE_OPTION_IS_ACTIVE(opt->cap) || !SANE_OPTION_IS_SETTABLE(opt->cap) || opt->type == SANE_TYPE_GROUP || !xsane.element[i].widget) { continue; } if (!(opt->cap & SANE_CAP_ALWAYS_SETTABLE)) { gtk_widget_set_sensitive(xsane.element[i].widget, sensitive); } } if (xsane.xsanemode_widget) { gtk_widget_set_sensitive(xsane.xsanemode_widget, sensitive); } while (gtk_events_pending()) { gtk_main_iteration(); } } /* ---------------------------------------------------------------------------------------------------------------------- */ void xsane_set_sensitivity(SANE_Int sensitivity) { DBG(DBG_proc, "xsane_set_sensitivity\n"); if (xsane.shell) { gtk_widget_set_sensitive(xsane.menubar, sensitivity); gtk_widget_set_sensitive(xsane.xsane_window, sensitivity); gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), 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); #ifdef HAVE_WORKING_GTK_GAMMACURVE gtk_widget_set_sensitive(xsane.gamma_dialog, sensitivity); #endif } if (xsane.preview) { gtk_widget_set_sensitive(xsane.preview->button_box, sensitivity); /* button box at top of window */ #if 1 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 0 xsane_back_gtk_set_sensitivity(sensitivity); #endif while (gtk_events_pending()) /* make sure set_sensitivity is displayed */ { gtk_main_iteration(); } xsane.sensitivity = sensitivity; } /* ----------------------------------------------------------------------------------------------------------------- */ void xsane_back_gtk_destroy_dialog(void) { SANE_Handle dev = xsane.dev; DBG(DBG_proc, "xsane_back_gtk_destroy_dialog\n"); xsane_back_gtk_panel_destroy(); free((void *) xsane.dev_name); free(xsane.element); sane_close(dev); } /* ---------------------------------------------------------------------------------------------------------------------- */ void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d) { GdkPixmap *pixmap; GdkBitmap *mask; DBG(DBG_proc, "xsane_set_window_icon\n"); 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); } /* ---------------------------------------------------------------------------------------------------------------------- */