1 /* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend
2 
3    xsane-back-gtk.c
4 
5    Oliver Rauch <Oliver.Rauch@rauch-domain.de>
6    Copyright (C) 1998-2010 Oliver Rauch
7    This file is part of the XSANE package.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
22 
23 /* ----------------------------------------------------------------------------------------------------------------- */
24 
25 #include "xsane.h"
26 #include "xsane-back-gtk.h"
27 #include "xsane-front-gtk.h"
28 #include "xsane-preferences.h"
29 #include "xsane-gamma.h"
30 
31 /* ----------------------------------------------------------------------------------------------------------------- */
32 
33 /* extern declarations */
34 extern void xsane_panel_build(void);
35 
36 /* ----------------------------------------------------------------------------------------------------------------- */
37 
38 /* forward declarations: */
39 SANE_Status xsane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int *info);
40 const SANE_Option_Descriptor *xsane_get_option_descriptor(SANE_Handle handle, SANE_Int option);
41 const char *xsane_back_gtk_unit_string(SANE_Unit unit);
42 void xsane_back_gtk_set_tooltip(GtkTooltips *tooltips, GtkWidget *widget, const gchar *desc);
43 int xsane_back_gtk_make_path(size_t buf_size, char *buf, const char *prog_name, const char *dir_name,
44                              const char *prefix, const char *dev_name, const char *postfix, int location);
45 void xsane_back_gtk_set_option(int opt_num, void *val, SANE_Action action);
46 
47 static void xsane_back_gtk_panel_rebuild(void);
48 void xsane_set_sensitivity(SANE_Int sensitivity);
49 void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d);
50 
51 /* ----------------------------------------------------------------------------------------------------------------- */
52 
xsane_bound_int(int * value,int min,int max)53 void xsane_bound_int(int *value, int min, int max)
54 {
55   DBG(DBG_proc3, "xsane_bound_int\n");
56 
57   if (min > max)
58   {
59     int help = min;
60     min = max;
61     max = help;
62   }
63 
64   if (*value < min)
65   {
66     *value = min;
67   }
68 
69   if (*value > max)
70   {
71     *value = max;
72   }
73 }
74 
75 /* ----------------------------------------------------------------------------------------------------------------- */
76 
xsane_bound_float(float * value,float min,float max)77 void xsane_bound_float(float *value, float min, float max)
78 {
79   DBG(DBG_proc3, "xsane_bound_float\n");
80 
81   if (min > max)
82   {
83     double help = min;
84     min = max;
85     max = help;
86   }
87 
88   if (*value < min)
89   {
90     *value = min;
91   }
92 
93   if (*value > max)
94   {
95     *value = max;
96   }
97 }
98 
99 /* ----------------------------------------------------------------------------------------------------------------- */
100 
xsane_bound_double(double * value,double min,double max)101 void xsane_bound_double(double *value, double min, double max)
102 {
103   DBG(DBG_proc3, "xsane_bound_double\n");
104 
105   if (min > max)
106   {
107     double help = min;
108     min = max;
109     max = help;
110   }
111 
112   if (*value < min)
113   {
114     *value = min;
115   }
116 
117   if (*value > max)
118   {
119     *value = max;
120   }
121 }
122 
123 /* ----------------------------------------------------------------------------------------------------------------- */
124 
125 /* returns 1 if value is in bounds */
xsane_check_bound_double(double value,double min,double max)126 int xsane_check_bound_double(double value, double min, double max)
127 {
128  int in_bounds = 1;
129 
130   DBG(DBG_proc3, "xsane_check_bound_double\n");
131 
132   if (min > max)
133   {
134     double help = min;
135     min = max;
136     max = help;
137   }
138 
139   if (value < min)
140   {
141     in_bounds = 0;
142   }
143 
144   if (value > max)
145   {
146     in_bounds = 0;
147   }
148 
149  return (in_bounds);
150 }
151 
152 /* ----------------------------------------------------------------------------------------------------------------- */
153 
xsane_get_option_descriptor(SANE_Handle handle,SANE_Int option)154 const SANE_Option_Descriptor *xsane_get_option_descriptor(SANE_Handle handle, SANE_Int option)
155 {
156   DBG(DBG_optdesc, "xsane_get_option_descriptor(%d)\n", option);
157 
158   if (option >= 0)
159   {
160     return sane_get_option_descriptor(handle, option);
161   }
162  return NULL;
163 }
164 
165 /* ----------------------------------------------------------------------------------------------------------------- */
166 
xsane_control_option(SANE_Handle handle,SANE_Int option,SANE_Action action,void * val,SANE_Int * info)167 SANE_Status xsane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void *val, SANE_Int *info)
168 {
169   DBG(DBG_proc, "xsane_control_option(option = %d, action = %d)\n", option, action);
170 
171   if (option >= 0)
172   {
173    SANE_Status status;
174 
175 #if 1
176     /* I am not sure about a correct and intelligent way to handle an option that has not defined SANE_CAP_SOFT_DETECT */
177     /* the test backend creates an option without SANE_CAP_SOFT_DETECT that causes an error message when I do not do the following */
178     if (action == SANE_ACTION_GET_VALUE)
179     {
180      const SANE_Option_Descriptor *opt;
181 
182       opt = xsane_get_option_descriptor(xsane.dev, option);
183       if ((opt) && (!(opt->cap & SANE_CAP_SOFT_DETECT)))
184       {
185         DBG(DBG_warning, "WARNING: xsane_control_option(option = %d, action = %d): SANE_CAP_SOFT_DETECT is not set\n", option, action);
186         if (option > 0) /* continue for option == 0, otherwise we can not read this option */
187         {
188           return SANE_STATUS_GOOD;
189         }
190       }
191     }
192 #endif
193 
194     status = sane_control_option(handle, option, action, val, info);
195     if (status)
196     {
197       DBG(DBG_error, "ERROR: xsane_control_option(option = %d, action = %d) failed\n", option, action);
198     }
199 
200     return status;
201   }
202 
203  return SANE_STATUS_INVAL;
204 }
205 
206 /* ----------------------------------------------------------------------------------------------------------------- */
207 
xsane_back_gtk_unit_string(SANE_Unit unit)208 const char *xsane_back_gtk_unit_string(SANE_Unit unit)
209 {
210   DBG(DBG_proc, "xsane_back_gtk_unit_string\n");
211 
212   switch (unit)
213   {
214     case SANE_UNIT_NONE:	return "none";
215     case SANE_UNIT_PIXEL:	return "px";
216     case SANE_UNIT_BIT:		return "bit";
217     case SANE_UNIT_DPI:		return "dpi";
218     case SANE_UNIT_PERCENT:	return "%";
219     case SANE_UNIT_MM:
220       if (preferences.length_unit > 9.9 && preferences.length_unit < 10.1)
221       {
222 	return "cm";
223       }
224       else if (preferences.length_unit > 25.3 && preferences.length_unit < 25.5)
225       {
226 	return "in";
227       }
228       return "mm";
229     case SANE_UNIT_MICROSECOND:	return "\302\265s"; /* UTF8 µs */
230   }
231   return 0;
232 }
233 
234 /* ----------------------------------------------------------------------------------------------------------------- */
235 
xsane_back_gtk_set_tooltip(GtkTooltips * tooltips,GtkWidget * widget,const gchar * desc)236 void xsane_back_gtk_set_tooltip(GtkTooltips *tooltips, GtkWidget *widget, const gchar *desc)
237 {
238   DBG(DBG_proc, "xsane_back_gtk_set_tooltip\n");
239 
240   if (desc && desc[0])
241   {
242     gtk_tooltips_set_tip(tooltips, widget, desc, 0);
243   }
244 }
245 
246 /* ----------------------------------------------------------------------------------------------------------------- */
247 
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)248 int xsane_back_gtk_make_path(size_t buf_size, char *buf, const char *prog_name, const char *dir_name,
249                              const char *prefix, const char *dev_name, const char *postfix, int location)
250 {
251  size_t len, extra;
252  int i;
253 
254   DBG(DBG_proc, "xsane_back_gtk_make_path\n");
255 
256   if (location == XSANE_PATH_LOCAL_SANE) /* make path to local file */
257   {
258     if (getenv(STRINGIFY(ENVIRONMENT_APPDATA_DIR_NAME)) != NULL)
259     {
260       snprintf(buf, buf_size-2, "%s%c.sane", getenv(STRINGIFY(ENVIRONMENT_APPDATA_DIR_NAME)), SLASH);
261     }
262     else
263     {
264       snprintf(buf, buf_size-2, "%s", STRINGIFY(XSANE_FIXED_APPDATA_DIR));
265     }
266     mkdir(buf, 0777);	/* ensure ~/.sane directory exists */
267   }
268   else if (location == XSANE_PATH_SYSTEM) /* make path to system file */
269   {
270     snprintf(buf, buf_size-2, "%s", STRINGIFY(PATH_SANE_DATA_DIR));
271   }
272   else /* make path to temporary file XSANE_PATH_TMP */
273   {
274     snprintf(buf, buf_size-2, "%s", preferences.tmp_path);
275   }
276 
277   len = strlen(buf);
278 
279   buf[len++] = SLASH;
280 
281   if (prog_name)
282   {
283     extra = strlen(prog_name);
284     if (len + extra + 2 >= buf_size)
285     {
286       goto filename_too_long;
287     }
288 
289     memcpy(buf + len, prog_name, extra);
290     len += extra;
291 
292     buf[len] = '\0';
293     mkdir(buf, 0777);	/* ensure ~/.sane/PROG_NAME directory exists */
294 
295     buf[len++] = SLASH; /* OS/2 does not like slash at end of mktemp-path */
296   }
297 
298   if (len >= buf_size)
299   {
300     goto filename_too_long;
301   }
302 
303   if (dir_name)
304   {
305     extra = strlen(dir_name);
306     if (len + extra + 2 >= buf_size)
307     {
308       goto filename_too_long;
309     }
310 
311     memcpy(buf + len, dir_name, extra);
312     len += extra;
313 
314     buf[len++] = SLASH;
315 
316     buf[len] = '\0';
317     mkdir(buf, 0777);	/* ensure DIR_NAME directory exists */
318   }
319 
320   if (len >= buf_size)
321   {
322     goto filename_too_long;
323   }
324 
325 
326   if (prefix)
327   {
328     extra = strlen(prefix);
329     if (len + extra >= buf_size)
330     {
331       goto filename_too_long;
332     }
333 
334     memcpy(buf + len, prefix, extra);
335     len += extra;
336   }
337 
338   if (dev_name)
339   {
340       /* Turn devicename into valid filename by replacing slashes and other forbidden characters by "_", "_" gets "__", spaces are erased  */
341 
342     for (i = 0; dev_name[i]; ++i)
343     {
344       if (len + 2 >= buf_size)
345       {
346         goto filename_too_long;
347       }
348 
349       switch (dev_name[i])
350       {
351         case '\\':	/* "\" -> "_" */
352           buf[len++] = '_';
353          break;
354 
355         case '/':	/* "/" -> "_" */
356           buf[len++] = '_';
357          break;
358 
359         case '*':	/* "*" -> "_" */
360           buf[len++] = '_';
361          break;
362 
363         case '?':	/* "?" -> "_" */
364           buf[len++] = '_';
365          break;
366 
367 #ifdef _WIN32
368         case ':':	/* ":" -> "_" */
369           buf[len++] = '_';
370          break;
371 #endif
372 
373 #ifdef HAVE_OS2_H
374         case ':':	/* ":" -> "_" */
375           buf[len++] = '_';
376          break;
377 #endif
378 
379         case ' ':	/* erase " " */
380          break;
381 
382         case '_':	/* "_" -> "__" */
383           buf[len++] = '_';
384           /* fall through */
385         default:
386           buf[len++] = dev_name[i];
387          break;
388       }
389     }
390   }
391 
392   if (postfix)
393   {
394     extra = strlen(postfix);
395     if (len + extra >= buf_size)
396     {
397       goto filename_too_long;
398     }
399     memcpy(buf + len, postfix, extra);
400     len += extra;
401   }
402 
403   if (len >= buf_size)
404   {
405     goto filename_too_long;
406   }
407 
408   if (location == XSANE_PATH_TMP) /* tmp dir, add uid */
409   {
410    char tmpbuf[TEXTBUFSIZE];
411    uid_t uid;
412    int fd;
413 
414     uid = getuid();
415     snprintf(tmpbuf, sizeof(tmpbuf), "-%d-", (int) uid);
416 
417     extra = strlen(tmpbuf);
418     if (len + extra >= buf_size)
419     {
420       goto filename_too_long;
421     }
422 
423     memcpy(buf + len, tmpbuf, extra);
424     len += extra;
425 
426     if (len + 7 >= buf_size)
427     {
428       goto filename_too_long;
429     }
430 
431     memcpy(buf + len, "XXXXXX", 6);		/* create unique filename */
432     len += 6;
433     buf[len] = '\0';
434 
435     fd = mkstemp(buf); /* create unique filename and opens/creates the file */
436     if (fd == -1)
437     {
438       xsane_back_gtk_error(ERR_CREATE_TEMP_FILE, FALSE);
439      return -1;
440     }
441     close(fd); /* will be opened again later */
442   }
443   else
444   {
445     buf[len++] = '\0';
446   }
447 
448   DBG(DBG_proc, "path = \"%s\"\n", buf);
449 
450   return 0;
451 
452 
453 filename_too_long:
454   xsane_back_gtk_error(ERR_FILENAME_TOO_LONG, FALSE);
455   errno = E2BIG;
456  return -1;
457 }
458 
459 /* ----------------------------------------------------------------------------------------------------------------- */
460 
xsane_back_gtk_set_option(int opt_num,void * val,SANE_Action action)461 void xsane_back_gtk_set_option(int opt_num, void *val, SANE_Action action)
462 {
463  SANE_Status status;
464  SANE_Int info;
465  char buf[TEXTBUFSIZE];
466  int old_channels = xsane.xsane_channels;
467  int update_gamma = FALSE;
468 
469   DBG(DBG_proc, "xsane_back_gtk_set_option\n");
470 
471   status = xsane_control_option(xsane.dev, opt_num, action, val, &info);
472   if (status != SANE_STATUS_GOOD)
473   {
474     snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_SET_OPTION, xsane_get_option_descriptor(xsane.dev, opt_num)->name,
475              XSANE_STRSTATUS(status));
476     xsane_back_gtk_error(buf, FALSE);
477     return;
478   }
479 
480   if (info & SANE_INFO_RELOAD_PARAMS)
481   {
482     xsane_update_param(0);
483   }
484 
485   if (info & SANE_INFO_RELOAD_OPTIONS)
486   {
487     xsane_back_gtk_panel_rebuild();
488 
489     if (xsane.preview)
490     {
491       preview_update_surface(xsane.preview, 0);
492     }
493 
494     update_gamma = TRUE; /* scanner gamma correction may have changed, medium may need update */
495   }
496   else if (info & SANE_INFO_INEXACT)
497   {
498     /* XXXXXXXXXXXXXX this also has to be handled XXXXXXXXXXXXXXX */
499   }
500 
501   if (xsane.xsane_channels != old_channels)
502   {
503     /* we have to update gamma tables and histogram because medium settings */
504     /* may have changed */
505     update_gamma = TRUE;
506   }
507 
508   if (update_gamma)
509   {
510     xsane_update_gamma_curve(TRUE);
511   }
512 }
513 
514 /* ----------------------------------------------------------------------------------------------------------------- */
515 
xsane_back_gtk_get_option_double(int option,double * val,SANE_Int * unit)516 int xsane_back_gtk_get_option_double(int option, double *val, SANE_Int *unit)
517 /* return values: */
518 /*  0 = OK */
519 /* -1 = option number < 0 */
520 /* -2 = failed to set option */
521 {
522  const SANE_Option_Descriptor *opt;
523  SANE_Handle dev;
524  SANE_Word word;
525 
526   DBG(DBG_proc, "xsane_back_gtk_get_option_double\n");
527 
528   if (option <= 0)
529   {
530     return -1;
531   }
532 
533   if (xsane_control_option(xsane.dev, option, SANE_ACTION_GET_VALUE, &word, 0) == SANE_STATUS_GOOD)
534   {
535     dev = xsane.dev;
536     opt = xsane_get_option_descriptor(dev, option);
537 
538     if (unit)
539     {
540       *unit = opt->unit;
541     }
542 
543     if (val)
544     {
545       if (opt->type == SANE_TYPE_FIXED)
546       {
547         *val = (float) word / 65536.0;
548       }
549       else
550       {
551         *val = (float) word;
552       }
553     }
554 
555    return 0;
556   }
557   else if (val)
558   {
559     *val = 0;
560   }
561   return -2;
562 }
563 
564 /* ----------------------------------------------------------------------------------------------------------------- */
565 
xsane_back_gtk_set_option_double(int option,double value)566 int xsane_back_gtk_set_option_double(int option, double value)
567 {
568  const SANE_Option_Descriptor *opt;
569  SANE_Word word;
570 
571   DBG(DBG_proc, "xsane_set_option_double\n");
572 
573   if (option <= 0 || value <= -INF || value >= INF)
574   {
575     return -1;
576   }
577 
578   opt = xsane_get_option_descriptor(xsane.dev, option);
579   if (opt)
580   {
581     if (opt->type == SANE_TYPE_FIXED)
582     {
583       word = SANE_FIX(value);
584     }
585     else
586     {
587       word = value + 0.5;
588     }
589 
590     if (xsane_control_option(xsane.dev, option, SANE_ACTION_SET_VALUE, &word, 0))
591     {
592       return -2;
593     }
594   }
595   else
596   {
597     return -1;
598   }
599 
600  return 0;
601 }
602 
603 /* ----------------------------------------------------------------------------------------------------------------- */
604 
xsane_back_gtk_decision_delete_event(GtkWidget * widget,GdkEvent * event,gpointer data)605 static int xsane_back_gtk_decision_delete_event(GtkWidget * widget, GdkEvent *event, gpointer data)
606 {
607  gint *decision_flag = (gint *) data;
608 
609   DBG(DBG_proc, "xsane_back_gtk_decision_delete_event\n");
610 
611   xsane.back_gtk_message_dialog_active--;
612 
613   if (decision_flag)
614   {
615     *decision_flag = -1;
616   }
617 
618  return FALSE; /* continue with original delete even routine */
619 }
620 
621 /* ----------------------------------------------------------------------------------------------------------------- */
622 
xsane_back_gtk_decision_ok_callback(GtkWidget * widget,gpointer data)623 static void xsane_back_gtk_decision_ok_callback(GtkWidget *widget, gpointer data)
624 {
625  gint *decision_flag = (gint *) data;
626 
627   DBG(DBG_proc, "xsane_back_gtk_decision_ok_callback\n");
628 
629   gtk_widget_destroy(widget->parent->parent->parent->parent);
630   xsane.back_gtk_message_dialog_active--;
631 
632   if (decision_flag)
633   {
634     *decision_flag = 1;
635   }
636 }
637 
638 /* ----------------------------------------------------------------------------------------------------------------- */
639 
xsane_back_gtk_decision_reject_callback(GtkWidget * widget,gpointer data)640 static void xsane_back_gtk_decision_reject_callback(GtkWidget *widget, gpointer data)
641 {
642  gint *decision_flag = (gint *) data;
643 
644   DBG(DBG_proc, "xsane_back_gtk_decision_reject_callback\n");
645 
646   gtk_widget_destroy(widget->parent->parent->parent->parent);
647   xsane.back_gtk_message_dialog_active--;
648 
649   if (decision_flag)
650   {
651     *decision_flag = -1;
652   }
653 }
654 
655 /* ----------------------------------------------------------------------------------------------------------------- */
656 
xsane_back_gtk_decision(gchar * title,gchar ** xpm_d,gchar * message,gchar * oktext,gchar * rejecttext,int wait)657 gint xsane_back_gtk_decision(gchar *title, gchar **xpm_d,  gchar *message, gchar *oktext, gchar *rejecttext, int wait)
658 {
659  GtkWidget *main_vbox, *hbox, *label, *button, *frame;
660  GdkPixmap *pixmap;
661  GdkBitmap *mask;
662  GtkWidget *pixmapwidget;
663  GtkWidget *decision_dialog;
664  GtkAccelGroup *accelerator_group;
665  gint decision_flag;
666  gint *decision_flag_ptr = NULL;
667 
668   DBG(DBG_proc, "xsane_back_gtk_decision\n");
669 
670   if (wait)
671   {
672     decision_flag_ptr = &decision_flag;
673   }
674 
675   xsane.back_gtk_message_dialog_active++;
676   decision_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
677   gtk_window_set_position(GTK_WINDOW(decision_dialog), GTK_WIN_POS_MOUSE);
678   gtk_window_set_title(GTK_WINDOW(decision_dialog), title);
679   g_signal_connect(GTK_OBJECT(decision_dialog), "delete_event", GTK_SIGNAL_FUNC(xsane_back_gtk_decision_delete_event), (void *) decision_flag_ptr);
680 
681   xsane_set_window_icon(decision_dialog, 0);
682 
683   accelerator_group = gtk_accel_group_new();
684   gtk_window_add_accel_group(GTK_WINDOW(decision_dialog), accelerator_group);
685 
686   /* create a frame */
687   frame = gtk_frame_new(NULL);
688   gtk_container_set_border_width(GTK_CONTAINER(frame), 10);
689   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
690   gtk_container_add(GTK_CONTAINER(decision_dialog), frame);
691   gtk_widget_show(frame);
692 
693   /* create the main vbox */
694   main_vbox = gtk_vbox_new(FALSE, 5);
695   gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 5);
696   gtk_widget_show(main_vbox);
697   gtk_container_add(GTK_CONTAINER(frame), main_vbox);
698 
699   /* create a horizontal box to put the icon and the text insode */
700   hbox = gtk_hbox_new(FALSE, 2);
701   gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
702   gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
703 
704   /* the info icon */
705   if (xpm_d)
706   {
707     pixmap = gdk_pixmap_create_from_xpm_d(decision_dialog->window, &mask, xsane.bg_trans, xpm_d);
708     pixmapwidget = gtk_image_new_from_pixmap(pixmap, mask);
709     gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 10);
710     gtk_widget_show(pixmapwidget);
711     gdk_drawable_unref(pixmap);
712     gdk_drawable_unref(mask);
713   }
714 
715   /* the message */
716   label = gtk_label_new(message);
717   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
718   gtk_widget_show(label);
719 
720   gtk_widget_show(hbox);
721 
722 
723   hbox = gtk_hbox_new(FALSE, 2);
724   gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
725   gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, FALSE, 0);
726 
727   /* the confirmation button */
728   button = gtk_button_new_with_label(oktext);
729   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
730   g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_back_gtk_decision_ok_callback, (void *) decision_flag_ptr);
731   gtk_box_pack_end(GTK_BOX(hbox), button, TRUE, TRUE, 5);
732   gtk_widget_grab_default(button);
733   gtk_widget_show(button);
734 
735 
736   if (rejecttext) /* the rejection button */
737   {
738     button = gtk_button_new_with_label(rejecttext);
739     g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_back_gtk_decision_reject_callback, (void *) decision_flag_ptr);
740     gtk_box_pack_end(GTK_BOX(hbox), button, TRUE, TRUE, 5);
741     gtk_widget_show(button);
742   }
743 
744   /* if rejectbutton is available then the following command is valid for the reject button */
745   /* otherwise it is valid for the ok button */
746   gtk_widget_add_accelerator(button, "clicked", accelerator_group, GDK_Escape, 0, DEF_GTK_ACCEL_LOCKED);
747 
748   gtk_widget_show(hbox);
749   gtk_widget_show(decision_dialog);
750 
751 
752   while (gtk_events_pending())
753   {
754     gtk_main_iteration();
755   }
756 
757   if (!wait)
758   {
759     return TRUE;
760   }
761 
762   decision_flag = 0;
763 
764   while (decision_flag == 0)
765   {
766     gtk_main_iteration();
767   }
768 
769   while (gtk_events_pending())
770   {
771     gtk_main_iteration();
772   }
773 
774   if (decision_flag == 1)
775   {
776     return TRUE;
777   }
778 
779   return FALSE;
780 }
781 
782 /* ----------------------------------------------------------------------------------------------------------------- */
783 
xsane_back_gtk_ipc_dialog_callback(gpointer data,gint source,GdkInputCondition cond)784 void xsane_back_gtk_ipc_dialog_callback(gpointer data, gint source, GdkInputCondition cond)
785 {
786  char message[TEXTBUFSIZE];
787  size_t bytes;
788 
789   DBG(DBG_proc, "xsane_back_gtk_message\n");
790 
791   bytes = read(xsane.ipc_pipefd[0], message, sizeof(message)-1);
792   message[bytes] = 0;
793 
794   xsane_back_gtk_decision(ERR_HEADER_CHILD_PROCESS_ERROR, (gchar **) error_xpm, message, BUTTON_CLOSE, 0 /* no reject text */, FALSE);
795 }
796 
797 /* ----------------------------------------------------------------------------------------------------------------- */
798 
xsane_back_gtk_message(gchar * title,gchar ** icon_xpm,gchar * message,int wait)799 void xsane_back_gtk_message(gchar *title, gchar **icon_xpm, gchar *message, int wait)
800 {
801   DBG(DBG_proc, "xsane_back_gtk_message\n");
802 
803   xsane_back_gtk_decision(title, icon_xpm, message, BUTTON_CLOSE, 0 /* no reject text */, wait);
804 }
805 
806 /* ----------------------------------------------------------------------------------------------------------------- */
807 
xsane_back_gtk_error(gchar * error,int wait)808 void xsane_back_gtk_error(gchar *error, int wait)
809 {
810   DBG(DBG_proc, "xsane_back_gtk_error: %s\n", error);
811 
812   if (wait)
813   {
814    SANE_Int old_sensitivity = xsane.sensitivity;
815 
816     xsane_set_sensitivity(FALSE);
817     xsane_back_gtk_message(ERR_HEADER_ERROR, (gchar**) error_xpm, error, wait);
818     xsane_set_sensitivity(old_sensitivity);
819   }
820   else
821   {
822     xsane_back_gtk_message(ERR_HEADER_ERROR, (gchar **) error_xpm, error, wait);
823   }
824 }
825 
826 /* ----------------------------------------------------------------------------------------------------------------- */
827 
xsane_back_gtk_warning(gchar * warning,int wait)828 void xsane_back_gtk_warning(gchar *warning, int wait)
829 {
830   DBG(DBG_proc, "xsane_back_gtk_warning: %s\n", warning);
831 
832   if (wait)
833   {
834    SANE_Int old_sensitivity = xsane.sensitivity;
835 
836     xsane_set_sensitivity(FALSE);
837     xsane_back_gtk_message(ERR_HEADER_WARNING, (gchar**) warning_xpm, warning, wait);
838     xsane_set_sensitivity(old_sensitivity);
839   }
840   else
841   {
842     xsane_back_gtk_message(ERR_HEADER_WARNING, (gchar**) warning_xpm, warning, wait);
843   }
844 }
845 
846 /* ----------------------------------------------------------------------------------------------------------------- */
847 
xsane_back_gtk_info(gchar * info,int wait)848 void xsane_back_gtk_info(gchar *info, int wait)
849 {
850   DBG(DBG_proc, "xsane_back_gtk_info: %s\n", info);
851 
852   if (wait)
853   {
854    SANE_Int old_sensitivity = xsane.sensitivity;
855 
856     xsane_set_sensitivity(FALSE);
857     xsane_back_gtk_message(ERR_HEADER_INFO, (gchar**) info_xpm, info, wait);
858     xsane_set_sensitivity(old_sensitivity);
859   }
860   else
861   {
862     xsane_back_gtk_message(ERR_HEADER_INFO, (gchar**) info_xpm, info, wait);
863   }
864 }
865 
866 /* ---------------------------------------------------------------------------------------------------------------------- */
867 
xsane_back_gtk_filetype_menu_set_history(GtkWidget * xsane_filetype_option_menu,char * filetype)868 void xsane_back_gtk_filetype_menu_set_history(GtkWidget *xsane_filetype_option_menu, char *filetype)
869 {
870  int filetype_nr;
871  int select_item;
872 
873   filetype_nr = 0;
874   select_item = 0;
875 
876 #ifdef HAVE_LIBJPEG
877   filetype_nr++;
878   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_JPEG)) )
879   {
880     select_item = filetype_nr;
881   }
882 #endif
883 
884   filetype_nr++;
885   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PDF)) )
886   {
887     select_item = filetype_nr;
888   }
889 
890 
891 #ifdef HAVE_LIBPNG
892 #ifdef HAVE_LIBZ
893   filetype_nr++;
894   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PNG)) )
895   {
896     select_item = filetype_nr;
897   }
898 #endif
899 #endif
900 
901   filetype_nr++;
902   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PNM)) )
903   {
904     select_item = filetype_nr;
905   }
906 
907   filetype_nr++;
908   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PS)) )
909   {
910     select_item = filetype_nr;
911   }
912 
913 #ifdef SUPPORT_RGBA
914   filetype_nr++;
915   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_RGBA)) )
916   {
917     select_item = filetype_nr;
918   }
919 #endif
920 
921   filetype_nr++;
922   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_TEXT)) )
923   {
924     select_item = filetype_nr;
925   }
926 
927 #ifdef HAVE_LIBTIFF
928   filetype_nr++;
929   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_TIFF)) )
930   {
931     select_item = filetype_nr;
932   }
933 #endif
934   gtk_option_menu_set_history(GTK_OPTION_MENU(xsane_filetype_option_menu), select_item);
935 }
936 
937 /* ---------------------------------------------------------------------------------------------------------------------- */
938 
xsane_back_gtk_filetype_menu_new(char * filetype,GtkSignalFunc filetype_callback)939 GtkWidget *xsane_back_gtk_filetype_menu_new(char *filetype, GtkSignalFunc filetype_callback)
940 {
941  GtkWidget *xsane_filetype_menu, *xsane_filetype_item;
942  GtkWidget *xsane_filetype_option_menu;
943  int filetype_nr;
944  int select_item;
945 
946   xsane_filetype_menu = gtk_menu_new();
947 
948   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_BY_EXT);
949   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
950   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_BY_EXT);
951   gtk_widget_show(xsane_filetype_item);
952 
953   filetype_nr = 0;
954   select_item = 0;
955 
956 #ifdef HAVE_LIBJPEG
957   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_JPEG);
958   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
959   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_JPEG);
960   gtk_widget_show(xsane_filetype_item);
961   filetype_nr++;
962   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_JPEG)) )
963   {
964     select_item = filetype_nr;
965   }
966 #endif
967 
968   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PDF);
969   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
970   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_PDF);
971   gtk_widget_show(xsane_filetype_item);
972   filetype_nr++;
973   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PDF)) )
974   {
975     select_item = filetype_nr;
976   }
977 
978 #ifdef HAVE_LIBPNG
979 #ifdef HAVE_LIBZ
980   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PNG);
981   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
982   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_PNG);
983   gtk_widget_show(xsane_filetype_item);
984   filetype_nr++;
985   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PNG)) )
986   {
987     select_item = filetype_nr;
988   }
989 #endif
990 #endif
991 
992   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PNM);
993   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
994   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_PNM);
995   gtk_widget_show(xsane_filetype_item);
996   filetype_nr++;
997   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PNM)) )
998   {
999     select_item = filetype_nr;
1000   }
1001 
1002   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PS);
1003   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
1004   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_PS);
1005   gtk_widget_show(xsane_filetype_item);
1006   filetype_nr++;
1007   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_PS)) )
1008   {
1009     select_item = filetype_nr;
1010   }
1011 
1012 #ifdef SUPPORT_RGBA
1013   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_RGBA);
1014   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
1015   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_RGBA);
1016   gtk_widget_show(xsane_filetype_item);
1017   filetype_nr++;
1018   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_RGBA)) )
1019   {
1020     select_item = filetype_nr;
1021   }
1022 #endif
1023 
1024   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_TEXT);
1025   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
1026   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_TEXT);
1027   gtk_widget_show(xsane_filetype_item);
1028   filetype_nr++;
1029   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_TEXT)) )
1030   {
1031     select_item = filetype_nr;
1032   }
1033 
1034 #ifdef HAVE_LIBTIFF
1035   xsane_filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_TIFF);
1036   gtk_container_add(GTK_CONTAINER(xsane_filetype_menu), xsane_filetype_item);
1037   g_signal_connect(GTK_OBJECT(xsane_filetype_item), "activate", filetype_callback, (void *) XSANE_FILETYPE_TIFF);
1038   gtk_widget_show(xsane_filetype_item);
1039   filetype_nr++;
1040   if ( (filetype) && (!strcasecmp(filetype, XSANE_FILETYPE_TIFF)) )
1041   {
1042     select_item = filetype_nr;
1043   }
1044 #endif
1045 
1046   xsane_filetype_option_menu = gtk_option_menu_new();
1047   xsane_back_gtk_set_tooltip(xsane.tooltips, xsane_filetype_option_menu, DESC_FILETYPE);
1048   gtk_option_menu_set_menu(GTK_OPTION_MENU(xsane_filetype_option_menu), xsane_filetype_menu);
1049   gtk_option_menu_set_history(GTK_OPTION_MENU(xsane_filetype_option_menu), select_item);
1050 
1051  return (xsane_filetype_option_menu);
1052 }
1053 
1054 /* ----------------------------------------------------------------------------------------------------------------- */
1055 
xsane_back_gtk_cms_function_menu_new(int select_cms_function,GtkSignalFunc cms_function_menu_callback)1056 GtkWidget *xsane_back_gtk_cms_function_menu_new(int select_cms_function, GtkSignalFunc cms_function_menu_callback)
1057 {
1058  GtkWidget *xsane_cms_function_menu, *xsane_cms_function_item;
1059  GtkWidget *xsane_cms_function_option_menu;
1060 
1061   xsane_cms_function_menu = gtk_menu_new();
1062 
1063   xsane_cms_function_item = gtk_menu_item_new_with_label(MENU_ITEM_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE);
1064   if (cms_function_menu_callback)
1065   {
1066     g_signal_connect(GTK_OBJECT(xsane_cms_function_item), "activate", (GtkSignalFunc) cms_function_menu_callback, (void *) XSANE_CMS_FUNCTION_EMBED_SCANNER_ICM_PROFILE);
1067   }
1068   gtk_container_add(GTK_CONTAINER(xsane_cms_function_menu), xsane_cms_function_item);
1069   gtk_widget_show(xsane_cms_function_item);
1070 
1071   xsane_cms_function_item = gtk_menu_item_new_with_label(MENU_ITEM_CMS_FUNCTION_CONVERT_TO_SRGB);
1072   if (cms_function_menu_callback)
1073   {
1074     g_signal_connect(GTK_OBJECT(xsane_cms_function_item), "activate", (GtkSignalFunc) cms_function_menu_callback, (void *) XSANE_CMS_FUNCTION_CONVERT_TO_SRGB);
1075   }
1076   gtk_container_add(GTK_CONTAINER(xsane_cms_function_menu), xsane_cms_function_item);
1077   gtk_widget_show(xsane_cms_function_item);
1078 
1079   xsane_cms_function_item = gtk_menu_item_new_with_label(MENU_ITEM_FUNCTION_CONVERT_TO_WORKING_CS);
1080   if (cms_function_menu_callback)
1081   {
1082     g_signal_connect(GTK_OBJECT(xsane_cms_function_item), "activate", (GtkSignalFunc) cms_function_menu_callback, (void *) XSANE_CMS_FUNCTION_CONVERT_TO_WORKING_CS);
1083   }
1084   gtk_container_add(GTK_CONTAINER(xsane_cms_function_menu), xsane_cms_function_item);
1085   gtk_widget_show(xsane_cms_function_item);
1086 
1087   xsane_cms_function_option_menu = gtk_option_menu_new();
1088   xsane_back_gtk_set_tooltip(xsane.tooltips, xsane_cms_function_option_menu, DESC_CMS_FUNCTION);
1089   gtk_option_menu_set_menu(GTK_OPTION_MENU(xsane_cms_function_option_menu), xsane_cms_function_menu);
1090   gtk_option_menu_set_history(GTK_OPTION_MENU(xsane_cms_function_option_menu), select_cms_function);
1091 
1092  return (xsane_cms_function_option_menu);
1093 }
1094 
1095 /* ----------------------------------------------------------------------------------------------------------------- */
1096 
1097 #ifdef __GTK_FILE_CHOOSER_H__
1098 
1099 GtkWidget *filechooser;
1100 char *filechooser_filetype = NULL;
1101 
xsane_back_gtk_filetype2_callback(GtkWidget * widget,gpointer data)1102 static void xsane_back_gtk_filetype2_callback(GtkWidget *widget, gpointer data)
1103 {
1104  char *extension, *chooser_filename;
1105  char filename[PATH_MAX];
1106  char *basename;
1107  char *new_filetype = (char *) data;
1108  int pos;
1109 
1110   DBG(DBG_proc, "xsane_filetype2_callback\n");
1111 
1112   chooser_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1113 
1114   if ((new_filetype) && (*new_filetype))
1115   {
1116     extension = strrchr(chooser_filename, '.');
1117 
1118     if ((extension) && (extension != chooser_filename))
1119     {
1120       if ( (!strcasecmp(extension, ".pnm"))  || (!strcasecmp(extension, ".raw"))
1121         || (!strcasecmp(extension, ".png"))  || (!strcasecmp(extension, ".ps"))
1122         || (!strcasecmp(extension, ".pdf"))  || (!strcasecmp(extension, ".rgba"))
1123         || (!strcasecmp(extension, ".tiff")) || (!strcasecmp(extension, ".tif"))
1124         || (!strcasecmp(extension, ".text")) || (!strcasecmp(extension, ".txt"))
1125         || (!strcasecmp(extension, ".jpg"))  || (!strcasecmp(extension, ".jpeg"))
1126          ) /* remove filetype extension */
1127       {
1128         *extension = 0; /* remove extension */
1129       }
1130     }
1131     snprintf(filename, sizeof(filename), "%s%s", chooser_filename, new_filetype);
1132   }
1133   else
1134   {
1135     snprintf(filename, sizeof(filename), "%s", chooser_filename);
1136   }
1137 
1138   if (filechooser_filetype)
1139   {
1140     free(filechooser_filetype);
1141     filechooser_filetype = NULL;
1142   }
1143 
1144   if (data)
1145   {
1146     filechooser_filetype = strdup(new_filetype);
1147   }
1148 
1149 
1150   basename = filename;
1151 
1152   for (pos = strlen(filename) - 1; pos > 0; pos--)
1153   {
1154     if (filename[pos] == '/')
1155     {
1156       filename[pos]=0;
1157 
1158       basename = filename+pos+1;
1159      break;
1160     }
1161   }
1162 
1163   gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filechooser), basename);
1164 
1165   g_free(chooser_filename);
1166 }
1167 
1168 /* ----------------------------------------------------------------------------------------------------------------- */
1169 
xsane_back_gtk_get_filename(const char * label,const char * default_name,size_t max_len,char * filename,char ** filetype,int * cms_function,XsaneFileChooserAction action,int show_extra_widgets,int enable_filters,int activate_filter)1170 int xsane_back_gtk_get_filename(const char *label, const char *default_name, size_t max_len, char *filename, char **filetype, int *cms_function,
1171                                 XsaneFileChooserAction action, int show_extra_widgets, int enable_filters, int activate_filter)
1172 {
1173  int ok = 0;
1174  GtkWidget *xsane_filetype_option_menu;
1175  GtkWidget *xsane_cms_function_option_menu = xsane_cms_function_option_menu;
1176  gint result;
1177  const gchar *accept_text = NULL;
1178  const gchar *reject_text = NULL;
1179  GtkFileChooserAction chooser_action = GTK_FILE_CHOOSER_ACTION_OPEN;
1180  GtkResponseType accept_code;
1181  GtkResponseType reject_code;
1182  char buf[PATH_MAX];
1183 
1184 
1185   DBG(DBG_proc, "xsane_back_gtk_get_filename\n");
1186 
1187   if (filechooser)
1188   {
1189     gdk_beep();
1190     return -1; /* cancel => do not allow to open more than one filechooser dialog */
1191   }
1192 
1193   switch (action)
1194   {
1195     default:
1196     case XSANE_FILE_CHOOSER_ACTION_OPEN:
1197       chooser_action = GTK_FILE_CHOOSER_ACTION_OPEN;
1198       accept_text = GTK_STOCK_OPEN;
1199       accept_code = GTK_RESPONSE_ACCEPT;
1200       reject_text = GTK_STOCK_CANCEL;
1201       reject_code = GTK_RESPONSE_CANCEL;
1202      break;
1203 
1204     case XSANE_FILE_CHOOSER_ACTION_SELECT_OPEN:
1205       chooser_action = GTK_FILE_CHOOSER_ACTION_OPEN;
1206       accept_text = GTK_STOCK_OK;
1207       accept_code = GTK_RESPONSE_ACCEPT;
1208       reject_text = GTK_STOCK_CANCEL;
1209       reject_code = GTK_RESPONSE_CANCEL;
1210      break;
1211 
1212     case XSANE_FILE_CHOOSER_ACTION_SAVE:
1213       chooser_action = GTK_FILE_CHOOSER_ACTION_SAVE;
1214       accept_text = GTK_STOCK_SAVE;
1215       accept_code = GTK_RESPONSE_ACCEPT;
1216       reject_text = GTK_STOCK_CANCEL;
1217       reject_code = GTK_RESPONSE_CANCEL;
1218      break;
1219 
1220     case XSANE_FILE_CHOOSER_ACTION_SELECT_SAVE:
1221       chooser_action = GTK_FILE_CHOOSER_ACTION_SAVE;
1222       accept_text = GTK_STOCK_OK;
1223       accept_code = GTK_RESPONSE_ACCEPT;
1224       reject_text = GTK_STOCK_CANCEL;
1225       reject_code = GTK_RESPONSE_CANCEL;
1226      break;
1227 
1228     case XSANE_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1229       chooser_action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1230       accept_text = GTK_STOCK_OK;
1231       accept_code = GTK_RESPONSE_ACCEPT;
1232       reject_text = GTK_STOCK_CANCEL;
1233       reject_code = GTK_RESPONSE_CANCEL;
1234      break;
1235 
1236     case XSANE_FILE_CHOOSER_ACTION_SELECT_PROJECT:
1237       chooser_action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
1238       accept_text = GTK_STOCK_OK;
1239       accept_code = GTK_RESPONSE_NO; /* when we would use ACCEPT, OK, YES or APPLY then the filechooser_dialog would create non existant directories */
1240       reject_text = GTK_STOCK_CANCEL;
1241       reject_code = GTK_RESPONSE_CANCEL;
1242      break;
1243   }
1244 
1245   filechooser = gtk_file_chooser_dialog_new (label,
1246 				      NULL,
1247 				      chooser_action,
1248 				      reject_text, reject_code,
1249 				      accept_text, accept_code,
1250 				      NULL);
1251 
1252   xsane_set_window_icon(filechooser, 0);
1253 
1254   gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(filechooser), TRUE);
1255 
1256 
1257   /* add paths to filechooser */
1258   if (getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)) != NULL)
1259   {
1260     snprintf(buf, sizeof(buf)-2, "%s", getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)));
1261     gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(filechooser), buf, NULL);
1262   }
1263 
1264   if (getcwd(buf, sizeof(buf)))
1265   {
1266     gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(filechooser), buf, NULL);
1267   }
1268 
1269 
1270   if (enable_filters & XSANE_FILE_FILTER_ALL) /* filter: all files */
1271   {
1272    GtkFileFilter *filter;
1273 
1274     filter = gtk_file_filter_new();
1275     gtk_file_filter_add_pattern(filter, "*");
1276 
1277     gtk_file_filter_set_name(filter, FILE_FILTER_ALL_FILES);
1278     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1279 
1280     if (activate_filter == XSANE_FILE_FILTER_ALL)
1281     {
1282       gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1283     }
1284   }
1285 
1286   if (enable_filters & XSANE_FILE_FILTER_DRC) /* filter: device rc */
1287   {
1288    GtkFileFilter *filter;
1289 
1290     filter = gtk_file_filter_new();
1291     gtk_file_filter_add_pattern(filter, "*.[dD][rR][cC]");
1292 
1293     gtk_file_filter_set_name(filter, FILE_FILTER_DRC);
1294     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1295 
1296     if (activate_filter == XSANE_FILE_FILTER_DRC)
1297     {
1298       gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1299     }
1300 
1301     /* add path to filechooser */
1302     if (getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)) != NULL)
1303     {
1304       gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(filechooser), getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)), NULL);
1305     }
1306   }
1307 
1308   if (enable_filters & XSANE_FILE_FILTER_ICM) /* filter: color management profiles */
1309   {
1310    GtkFileFilter *filter;
1311 
1312     filter = gtk_file_filter_new();
1313     gtk_file_filter_add_pattern(filter, "*.[iI][cC][cCmM]");
1314 
1315     gtk_file_filter_set_name(filter, FILE_FILTER_ICM);
1316     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1317 
1318     if (activate_filter == XSANE_FILE_FILTER_ICM)
1319     {
1320       gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1321     }
1322 
1323     /* add path to filechooser */
1324     if (getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)) != NULL)
1325     {
1326       snprintf(buf, sizeof(buf)-2, "%s%c.color%cicc", getenv(STRINGIFY(ENVIRONMENT_HOME_DIR_NAME)), SLASH, SLASH);
1327       gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(filechooser), buf, NULL);
1328     }
1329     gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(filechooser), "/usr/share/color/icc", NULL);
1330   }
1331 
1332   if (enable_filters & XSANE_FILE_FILTER_IMAGES) /* filter: images */
1333   {
1334    GtkFileFilter *filter;
1335 
1336     filter = gtk_file_filter_new();
1337     gtk_file_filter_add_pattern(filter, "*.[jJ][pP][gG]");
1338     gtk_file_filter_add_pattern(filter, "*.[jJ][pP][eE][gG]");
1339     gtk_file_filter_add_pattern(filter, "*.[pP][nN][gG]");
1340     gtk_file_filter_add_pattern(filter, "*.[tT][iI][fF]");
1341     gtk_file_filter_add_pattern(filter, "*.[tT][iI][fF][fF]");
1342     gtk_file_filter_add_pattern(filter, "*.[pP][sS]");
1343     gtk_file_filter_add_pattern(filter, "*.[pP][dD][fF]");
1344     gtk_file_filter_add_pattern(filter, "*.[pP][nN][mM]");
1345     gtk_file_filter_add_pattern(filter, "*.[pP][bB][mM]");
1346     gtk_file_filter_add_pattern(filter, "*.[pP][gG][mM]");
1347     gtk_file_filter_add_pattern(filter, "*.[pP][pP][mM]");
1348 
1349     gtk_file_filter_set_name(filter, FILE_FILTER_IMAGES);
1350     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1351 
1352     if (activate_filter == XSANE_FILE_FILTER_IMAGES)
1353     {
1354       gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1355     }
1356   }
1357 
1358   if (enable_filters & XSANE_FILE_FILTER_BATCHLIST) /* filter: color management profiles */
1359   {
1360    GtkFileFilter *filter;
1361 
1362     filter = gtk_file_filter_new();
1363     gtk_file_filter_add_pattern(filter, "*.[xX][bV][lL]");
1364 
1365     gtk_file_filter_set_name(filter, FILE_FILTER_XBL);
1366     gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(filechooser), filter);
1367 
1368     if (activate_filter == XSANE_FILE_FILTER_BATCHLIST)
1369     {
1370       gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(filechooser), filter);
1371     }
1372   }
1373 
1374   /* set default filename */
1375   if (default_name) /* select file */
1376   {
1377    const char *basename = default_name;
1378    char *path;
1379    int pos;
1380 
1381     DBG(DBG_info, "xsane_back_gtk_get_filename: default_name =%s\n", default_name);
1382 
1383     path = strdup(default_name);
1384     for (pos = strlen(path)-1; pos > 0; pos--)
1385     {
1386       if (path[pos] == '/')
1387       {
1388         path[pos]=0;
1389 
1390 	basename = path+pos+1;
1391        break;
1392       }
1393     }
1394 
1395     if (pos)
1396     {
1397       gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), path);
1398     }
1399 
1400     if ((action == XSANE_FILE_CHOOSER_ACTION_SAVE) || (action == XSANE_FILE_CHOOSER_ACTION_SELECT_SAVE))
1401     {
1402       gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filechooser), (char *) basename);
1403     }
1404     else
1405     {
1406       gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(filechooser), (char *) default_name);
1407     }
1408   }
1409 
1410 
1411   /* add filetype menu */
1412 
1413   if (show_extra_widgets)
1414   {
1415    GtkWidget *vbox;
1416    GtkWidget *hbox;
1417    GtkWidget *label;
1418 
1419     vbox = gtk_vbox_new(FALSE, 15);
1420     gtk_widget_show(vbox);
1421 
1422     if (show_extra_widgets & XSANE_GET_FILENAME_SHOW_FILETYPE)
1423     {
1424       DBG(DBG_info, "xsane_back_gtk_get_filename: showing filetype menu\n");
1425 
1426       if (filechooser_filetype)
1427       {
1428         free(filechooser_filetype);
1429       }
1430 
1431       if ((filetype) && (*filetype))
1432       {
1433         filechooser_filetype = strdup(*filetype);
1434       }
1435       else
1436       {
1437         filechooser_filetype = NULL;
1438       }
1439 
1440       hbox = gtk_hbox_new(FALSE, 2);
1441       gtk_widget_show(hbox);
1442       gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1443 
1444       label = gtk_label_new(TEXT_FILETYPE);
1445       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1446       gtk_widget_show(label);
1447 
1448       xsane_filetype_option_menu = xsane_back_gtk_filetype_menu_new(filechooser_filetype, (GtkSignalFunc) xsane_back_gtk_filetype2_callback);
1449       gtk_box_pack_start(GTK_BOX(hbox), xsane_filetype_option_menu, TRUE, TRUE, 2);
1450       gtk_widget_show(xsane_filetype_option_menu);
1451     }
1452 
1453 #ifdef HAVE_LIBLCMS
1454     if ((cms_function) && (show_extra_widgets & XSANE_GET_FILENAME_SHOW_CMS_FUNCTION))
1455     {
1456       hbox = gtk_hbox_new(FALSE, 2);
1457       gtk_widget_show(hbox);
1458       gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1459 
1460       label = gtk_label_new(TEXT_CMS_FUNCTION);
1461       gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1462       gtk_widget_show(label);
1463 
1464       xsane_cms_function_option_menu = xsane_back_gtk_cms_function_menu_new(*cms_function, NULL);
1465       gtk_box_pack_start(GTK_BOX(hbox), xsane_cms_function_option_menu, TRUE, TRUE, 2);
1466       gtk_widget_show(xsane_cms_function_option_menu);
1467     }
1468 #endif
1469 
1470     gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(filechooser), vbox);
1471   }
1472 
1473 
1474   gtk_widget_show(filechooser);
1475 
1476   result = gtk_dialog_run(GTK_DIALOG(filechooser));
1477 
1478   DBG(DBG_info, "xsane_back_gtk_get_filename: gtk_dialog_run() returned with result=%d\n", result);
1479 
1480   if (result == accept_code)
1481   {
1482    char *chooser_filename;
1483 
1484     if ((filetype) && (*filetype))
1485     {
1486       free(*filetype);
1487       *filetype = NULL;
1488     }
1489 
1490     if (filechooser_filetype)
1491     {
1492       if (filetype)
1493       {
1494         *filetype = strdup(filechooser_filetype);
1495       }
1496     }
1497 
1498 #ifdef HAVE_LIBLCMS
1499     if ((cms_function) && (show_extra_widgets & XSANE_GET_FILENAME_SHOW_CMS_FUNCTION))
1500     {
1501       *cms_function = gtk_option_menu_get_history(GTK_OPTION_MENU(xsane_cms_function_option_menu));
1502 
1503       DBG(DBG_info, "selected cms_function = %d\n", *cms_function);
1504     }
1505 #endif
1506 
1507     chooser_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
1508     strncpy(filename, chooser_filename, max_len - 1);
1509     g_free(chooser_filename);
1510 
1511     filename[max_len - 1] = '\0';
1512 
1513     ok = TRUE;
1514   }
1515 
1516   gtk_widget_destroy(filechooser);
1517   filechooser = NULL;
1518 
1519  return ok ? 0 : -1;
1520 }
1521 
1522 #else
1523 
1524 GtkWidget *fileselection;
1525 char *fileselection_filetype = NULL;
1526 
1527 /* ----------------------------------------------------------------------------------------------------------------- */
1528 
xsane_back_gtk_get_filename_button_clicked(GtkWidget * w,gpointer data)1529 static void xsane_back_gtk_get_filename_button_clicked(GtkWidget *w, gpointer data)
1530 {
1531  int *clicked = data;
1532 
1533   DBG(DBG_proc, "xsane_back_gtk_get_filename_button_clicked\n");
1534   *clicked = 1;
1535 }
1536 
1537 /* ----------------------------------------------------------------------------------------------------------------- */
1538 
xsane_back_gtk_filetype_callback(GtkWidget * widget,gpointer data)1539 static void xsane_back_gtk_filetype_callback(GtkWidget *widget, gpointer data)
1540 {
1541  char *extension, *filename;
1542  char buffer[PATH_MAX];
1543  char *new_filetype = (char *) data;
1544 
1545   DBG(DBG_proc, "xsane_filetype_callback\n");
1546 
1547   filename = strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection)));
1548 
1549   if ((new_filetype) && (*new_filetype))
1550   {
1551     extension = strrchr(filename, '.');
1552 
1553     if ((extension) && (extension != filename))
1554     {
1555       if ( (!strcasecmp(extension, ".pnm"))  || (!strcasecmp(extension, ".raw"))
1556         || (!strcasecmp(extension, ".png"))  || (!strcasecmp(extension, ".ps"))
1557         || (!strcasecmp(extension, ".pdf"))  || (!strcasecmp(extension, ".rgba"))
1558         || (!strcasecmp(extension, ".tiff")) || (!strcasecmp(extension, ".tif"))
1559         || (!strcasecmp(extension, ".text")) || (!strcasecmp(extension, ".txt"))
1560         || (!strcasecmp(extension, ".jpg"))  || (!strcasecmp(extension, ".jpeg"))
1561          ) /* remove filetype extension */
1562       {
1563         *extension = 0; /* remove extension */
1564       }
1565     }
1566     snprintf(buffer, sizeof(buffer), "%s%s", filename, new_filetype);
1567     free(filename);
1568     filename = strdup(buffer);
1569   }
1570 
1571   if (fileselection_filetype)
1572   {
1573     free(fileselection_filetype);
1574     fileselection_filetype = NULL;
1575   }
1576 
1577   if (data)
1578   {
1579     fileselection_filetype = strdup(new_filetype);
1580   }
1581 
1582   gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), filename);
1583 
1584   free(filename);
1585 }
1586 
1587 /* ---------------------------------------------------------------------------------------------------------------------- */
1588 
xsane_back_gtk_get_filename(const char * label,const char * default_name,size_t max_len,char * filename,char ** filetype,int * cms_function,XsaneFileChooserAction action,int show_filetype_menu,int enable_filters,int activate_filter)1589 int xsane_back_gtk_get_filename(const char *label, const char *default_name, size_t max_len, char *filename, char **filetype, int *cms_function,
1590                                 XsaneFileChooserAction action, int show_filetype_menu, int enable_filters, int activate_filter)
1591 {
1592  int cancel = 0, ok = 0, destroy = 0;
1593  GtkAccelGroup *accelerator_group;
1594  GtkWidget *xsane_filetype_option_menu;
1595  int show_fileopts = 0;
1596  int select_directory = 0;
1597 
1598   if (action == XSANE_FILE_CHOOSER_ACTION_SELECT_FOLDER)
1599   {
1600     select_directory = TRUE;
1601   }
1602   else
1603   {
1604     show_fileopts = TRUE;
1605   }
1606 
1607   DBG(DBG_proc, "xsane_back_gtk_get_filename\n");
1608 
1609   if (fileselection)
1610   {
1611     gdk_beep();
1612     return -1; /* cancel => do not allow to open more than one fileselection dialog */
1613   }
1614 
1615   fileselection = gtk_file_selection_new((char *) label);
1616   accelerator_group = gtk_accel_group_new();
1617   gtk_window_add_accel_group(GTK_WINDOW(fileselection), accelerator_group);
1618 
1619   g_signal_connect(GTK_OBJECT(fileselection), "destroy", GTK_SIGNAL_FUNC(xsane_back_gtk_get_filename_button_clicked), &destroy);
1620 
1621   g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->cancel_button), "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &cancel);
1622   gtk_widget_add_accelerator(GTK_FILE_SELECTION(fileselection)->cancel_button, "clicked",
1623                               accelerator_group, GDK_Escape, 0, DEF_GTK_ACCEL_LOCKED);
1624 
1625   g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fileselection)->ok_button), "clicked", (GtkSignalFunc) xsane_back_gtk_get_filename_button_clicked, &ok);
1626 
1627   if (select_directory)
1628   {
1629     DBG(DBG_info, "xsane_back_gtk_get_filename: select directory\n");
1630     gtk_widget_hide(GTK_FILE_SELECTION(fileselection)->file_list->parent);
1631     gtk_widget_hide(GTK_FILE_SELECTION(fileselection)->fileop_del_file);
1632     gtk_widget_hide(GTK_FILE_SELECTION(fileselection)->fileop_ren_file);
1633     gtk_widget_hide(GTK_FILE_SELECTION(fileselection)->selection_entry);
1634 
1635     gtk_widget_set_size_request(GTK_FILE_SELECTION(fileselection)->dir_list, 280, 230);
1636 
1637     if (default_name) /* add "/." to end of directory name so that the gtkfilesel* behaves correct */
1638     {
1639      char directory_name[PATH_MAX];
1640 
1641       snprintf(directory_name, sizeof(directory_name), "%s%c", default_name, SLASH);
1642       DBG(DBG_info, "xsane_back_gtk_get_filename: directory_name =%s\n", directory_name);
1643       gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), (char *) directory_name);
1644     }
1645   }
1646   else if (default_name) /* select file */
1647   {
1648     DBG(DBG_info, "xsane_back_gtk_get_filename: default_name =%s\n", default_name);
1649     gtk_file_selection_set_filename(GTK_FILE_SELECTION(fileselection), (char *) default_name);
1650   }
1651 
1652   if (show_fileopts)
1653   {
1654     DBG(DBG_info, "xsane_back_gtk_get_filename: showing file-options\n");
1655     gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(fileselection));
1656   }
1657   else
1658   {
1659     DBG(DBG_info, "xsane_back_gtk_get_filename: hiding file-options\n");
1660     gtk_file_selection_hide_fileop_buttons(GTK_FILE_SELECTION(fileselection));
1661   }
1662 
1663   if (show_filetype_menu)
1664   {
1665    GtkWidget *hbox;
1666    GtkWidget *vbox;
1667    GtkWidget *label;
1668 
1669     DBG(DBG_info, "xsane_back_gtk_get_filename: showing filetype menu\n");
1670 
1671     if (fileselection_filetype)
1672     {
1673       free(fileselection_filetype);
1674     }
1675 
1676     if ((filetype) && (*filetype))
1677     {
1678       fileselection_filetype = strdup(*filetype);
1679     }
1680     else
1681     {
1682       fileselection_filetype = NULL;
1683     }
1684 
1685     vbox = gtk_vbox_new(FALSE, 2);
1686     gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(fileselection)->action_area), vbox, TRUE, TRUE, 0);
1687     gtk_widget_show(vbox);
1688 
1689     hbox = gtk_hbox_new(FALSE, 2);
1690     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1691     gtk_widget_show(hbox);
1692 
1693     label = gtk_label_new(TEXT_FILETYPE);
1694     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1695     gtk_widget_show(label);
1696 
1697     xsane_filetype_option_menu = xsane_back_gtk_filetype_menu_new(fileselection_filetype, (GtkSignalFunc) xsane_back_gtk_filetype_callback);
1698     gtk_box_pack_start(GTK_BOX(hbox), xsane_filetype_option_menu, TRUE, TRUE, 2);
1699     gtk_widget_show(xsane_filetype_option_menu);
1700   }
1701 
1702   gtk_widget_show(fileselection);
1703 
1704   DBG(DBG_info, "xsane_back_gtk_get_filename: waiting for user action\n");
1705   while (!cancel && !ok && !destroy)
1706   {
1707     gtk_main_iteration();
1708   }
1709 
1710   if (ok)
1711   {
1712    size_t len, cwd_len;
1713    char *cwd;
1714 
1715     DBG(DBG_info, "ok button pressed\n");
1716 
1717     if ((filetype) && (*filetype))
1718     {
1719       free(*filetype);
1720       *filetype = NULL;
1721     }
1722 
1723     if (fileselection_filetype)
1724     {
1725       if (filetype)
1726       {
1727         *filetype = strdup(fileselection_filetype);
1728       }
1729 
1730       free(fileselection_filetype);
1731       fileselection_filetype = NULL;
1732     }
1733 
1734     strncpy(filename, gtk_file_selection_get_filename(GTK_FILE_SELECTION(fileselection)), max_len - 1);
1735 
1736 #ifndef HAVE_GTK2
1737     /* in gtk1 we have to remove the text that is defined in the selection entry to get a proper behaviour */
1738     if (select_directory)
1739     {
1740       *(filename+strlen(filename)-strlen(gtk_entry_get_text(GTK_ENTRY(GTK_FILE_SELECTION(fileselection)->selection_entry)))) = '\0';
1741     }
1742 #endif
1743 
1744     filename[max_len - 1] = '\0';
1745 
1746     len = strlen(filename);
1747 
1748     cwd = alloca(len + 2); /* alloca => memory is freed on return */
1749     getcwd(cwd, len + 1);
1750     cwd_len = strlen(cwd);
1751     cwd[cwd_len++] = '/';
1752     cwd[cwd_len] = '\0';
1753 
1754     DBG(DBG_info, "xsane_back_gtk_get_filename: full path filename = %s\n", filename);
1755   }
1756 
1757   if (!destroy)
1758   {
1759     gtk_widget_destroy(fileselection);
1760   }
1761 
1762   fileselection = NULL;
1763 
1764   return ok ? 0 : -1;
1765 }
1766 #endif
1767 
1768 /* ----------------------------------------------------------------------------------------------------------------- */
1769 
xsane_back_gtk_autobutton_update(GtkWidget * widget,DialogElement * elem)1770 static gint xsane_back_gtk_autobutton_update(GtkWidget *widget, DialogElement *elem)
1771 {
1772  int opt_num = elem - xsane.element;
1773  const SANE_Option_Descriptor *opt;
1774  SANE_Status status;
1775  SANE_Word val;
1776  char buf[TEXTBUFSIZE];
1777 
1778   DBG(DBG_proc, "xsane_back_gtk_autobutton_update\n");
1779 
1780   opt = xsane_get_option_descriptor(xsane.dev, opt_num);
1781   if (GTK_TOGGLE_BUTTON(widget)->active)
1782   {
1783     xsane_back_gtk_set_option(opt_num, 0, SANE_ACTION_SET_AUTO);
1784 
1785     gtk_widget_set_sensitive(elem->widget, FALSE);
1786 
1787     if (elem->widget2)
1788     {
1789       gtk_widget_set_sensitive(elem->widget2, FALSE);
1790     }
1791   }
1792   else
1793   {
1794     gtk_widget_set_sensitive(elem->widget, TRUE);
1795 
1796     if (elem->widget2)
1797     {
1798       gtk_widget_set_sensitive(elem->widget2, TRUE);
1799     }
1800 
1801     status = xsane_control_option(xsane.dev, opt_num, SANE_ACTION_GET_VALUE, &val, 0);
1802     if (status != SANE_STATUS_GOOD)
1803     {
1804       snprintf(buf, sizeof(buf), "%s %s: %s.", ERR_GET_OPTION, opt->name, XSANE_STRSTATUS(status));
1805       xsane_back_gtk_error(buf, FALSE);
1806     }
1807     xsane_back_gtk_set_option(opt_num, &val, SANE_ACTION_SET_VALUE);
1808   }
1809   return FALSE;
1810 }
1811 
1812 /* ----------------------------------------------------------------------------------------------------------------- */
1813 
xsane_back_gtk_autobutton_new(GtkWidget * parent,DialogElement * elem,GtkTooltips * tooltips)1814 static void xsane_back_gtk_autobutton_new(GtkWidget *parent, DialogElement *elem,
1815 		GtkTooltips *tooltips)
1816 {
1817  GtkWidget *button;
1818 
1819   DBG(DBG_proc, "xsane_back_gtk_autobutton_new\n");
1820 
1821   button = gtk_check_button_new();
1822   gtk_container_set_border_width(GTK_CONTAINER(button), 0);
1823   gtk_widget_set_size_request(button, 20, 20);
1824   g_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_back_gtk_autobutton_update, elem);
1825   xsane_back_gtk_set_tooltip(tooltips, button, DESC_AUTOMATIC);
1826 
1827   gtk_box_pack_end(GTK_BOX(parent), button, FALSE, FALSE, 2);
1828   gtk_widget_show(button);
1829 }
1830 
1831 /* ----------------------------------------------------------------------------------------------------------------- */
1832 
xsane_back_gtk_button_update(GtkWidget * widget,DialogElement * elem)1833 static gint xsane_back_gtk_button_update(GtkWidget * widget, DialogElement * elem)
1834 {
1835  int opt_num = elem - xsane.element;
1836  const SANE_Option_Descriptor *opt;
1837  SANE_Word val = SANE_FALSE;
1838 
1839   DBG(DBG_proc, "xsane_back_gtk_button_update\n");
1840 
1841   opt = xsane_get_option_descriptor(xsane.dev, opt_num);
1842   if (GTK_TOGGLE_BUTTON(widget)->active)
1843   {
1844     val = SANE_TRUE;
1845   }
1846   xsane_back_gtk_set_option(opt_num, &val, SANE_ACTION_SET_VALUE);
1847 
1848   return FALSE;
1849 }
1850 
1851 /* ----------------------------------------------------------------------------------------------------------------- */
1852 
xsane_back_gtk_button_new(GtkWidget * parent,const char * name,SANE_Word val,DialogElement * elem,GtkTooltips * tooltips,const char * desc,SANE_Int settable)1853 void xsane_back_gtk_button_new(GtkWidget * parent, const char *name, SANE_Word val,
1854 	    DialogElement * elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
1855 {
1856  GtkWidget *button;
1857 
1858   DBG(DBG_proc, "xsane_back_gtk_button_new\n");
1859 
1860   button = gtk_check_button_new_with_label((char *) name);
1861   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), val);
1862   g_signal_connect(GTK_OBJECT(button), "toggled", (GtkSignalFunc) xsane_back_gtk_button_update, elem);
1863   gtk_box_pack_start(GTK_BOX(parent), button, FALSE, TRUE, 0);
1864   gtk_widget_show(button);
1865   xsane_back_gtk_set_tooltip(tooltips, button, desc);
1866 
1867   gtk_widget_set_sensitive(button, settable);
1868 
1869   elem->widget  = button;
1870 }
1871 
1872 /* ----------------------------------------------------------------------------------------------------------------- */
1873 
1874 /* called from xsane_back_gtk_value_new and xsane_back_gtk_range_new */
xsane_back_gtk_value_update(GtkAdjustment * adj_data,DialogElement * elem)1875 static void xsane_back_gtk_value_update(GtkAdjustment *adj_data, DialogElement *elem)
1876 {
1877  const SANE_Option_Descriptor *opt;
1878  SANE_Word val, new_val;
1879  int opt_num;
1880  double d;
1881 
1882   DBG(DBG_proc, "xsane_back_gtk_value_update\n");
1883 
1884   opt_num = elem - xsane.element;
1885   opt = xsane_get_option_descriptor(xsane.dev, opt_num);
1886   switch(opt->type)
1887   {
1888     case SANE_TYPE_INT:
1889      val = adj_data->value; /* OLD:  + 0.5 but this mad problems with negative values */
1890      break;
1891 
1892     case SANE_TYPE_FIXED:
1893       d = adj_data->value;
1894       if (opt->unit == SANE_UNIT_MM)
1895       {
1896 	d *= preferences.length_unit;
1897       }
1898       val = SANE_FIX(d);
1899      break;
1900 
1901     default:
1902       DBG(DBG_error, "xsane_back_gtk_value_update: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
1903      return;
1904   }
1905 
1906   xsane_back_gtk_set_option(opt_num, &val, SANE_ACTION_SET_VALUE);
1907   xsane_control_option(xsane.dev, opt_num, SANE_ACTION_GET_VALUE, &new_val, 0);
1908 #if 1
1909   val = new_val;
1910   switch(opt->type)
1911   {
1912     case SANE_TYPE_INT:
1913       if (new_val != val)
1914       {
1915         adj_data->value = val;
1916         g_signal_emit_by_name(GTK_OBJECT(adj_data), "value_changed");
1917       }
1918      break;
1919 
1920     case SANE_TYPE_FIXED:
1921       if (abs(new_val - val) > 1) /* tolarate 1/65536 error, instead of: if (new_val != val) */
1922       {
1923         d = SANE_UNFIX(val);
1924         if (opt->unit == SANE_UNIT_MM)
1925         {
1926           d /= preferences.length_unit;
1927         }
1928         adj_data->value = d;
1929         g_signal_emit_by_name(GTK_OBJECT(adj_data), "value_changed");
1930       }
1931      break;
1932 
1933     default:
1934      break;
1935   }
1936 #endif
1937 #if 0
1938   if (abs(new_val - val) > 1) /* tolarate 1/65536 error, instead of: if (new_val != val) */
1939   {
1940     val = new_val;
1941     switch(opt->type)
1942     {
1943       case SANE_TYPE_INT:
1944         adj_data->value = val;
1945        break;
1946 
1947       case SANE_TYPE_FIXED:
1948         d = SANE_UNFIX(val);
1949         if (opt->unit == SANE_UNIT_MM)
1950         {
1951           d /= preferences.length_unit;
1952         }
1953         adj_data->value = d;
1954        break;
1955 
1956       default:
1957        break;
1958     }
1959     g_signal_emit_by_name(GTK_OBJECT(adj_data), "value_changed");
1960   }
1961 #endif
1962 
1963  return;			/* value didn't change */
1964 }
1965 
1966 /* ----------------------------------------------------------------------------------------------------------------- */
1967 
xsane_back_gtk_range_display_value_right_callback(GtkAdjustment * adjust,gpointer data)1968 static void xsane_back_gtk_range_display_value_right_callback(GtkAdjustment *adjust, gpointer data)
1969 {
1970  gchar buf[TEXTBUFSIZE];
1971  int digits = (int) data;
1972  GtkLabel *label;
1973 
1974   snprintf(buf, sizeof(buf), "%1.*f", digits, adjust->value);
1975   label = (GtkLabel *) gtk_object_get_data(GTK_OBJECT(adjust), "value-label");
1976   gtk_label_set_text(label, buf);
1977 }
1978 /* ----------------------------------------------------------------------------------------------------------------- */
1979 
xsane_back_gtk_range_new(GtkWidget * parent,const char * name,gfloat val,gfloat min,gfloat max,gfloat quant,int automatic,DialogElement * elem,GtkTooltips * tooltips,const char * desc,SANE_Int settable)1980 void xsane_back_gtk_range_new(GtkWidget *parent, const char *name, gfloat val,
1981 	   gfloat min, gfloat max, gfloat quant, int automatic,
1982 	   DialogElement *elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
1983 {
1984  GtkWidget *hbox, *label, *slider = NULL, *spinbutton, *value_label;
1985  int digits;
1986 
1987   DBG(DBG_proc, "xsane_back_gtk_range_new(%s)\n", name);
1988 
1989   if (quant - (int) quant == 0.0)
1990   {
1991     digits = 0;
1992   }
1993   else
1994   {
1995     digits = (int) (log10(1/quant)+0.8); /* set number of digits in dependance of quantization */
1996   }
1997 
1998   if (digits < 0)
1999   {
2000     digits = 0;
2001   }
2002 
2003   hbox = gtk_hbox_new(FALSE, 2);
2004   gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
2005   gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
2006 
2007   label = gtk_label_new((char *) name);
2008   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
2009 
2010   elem->data = gtk_adjustment_new(val, min, max, quant, quant*10, 0);
2011 
2012   /* value label */
2013   if (preferences.show_range_mode & 8)
2014   {
2015     value_label = gtk_label_new("");
2016     gtk_widget_set_size_request(value_label, 45, -1);
2017     gtk_box_pack_end(GTK_BOX(hbox), value_label, FALSE, FALSE, 1);
2018 
2019     g_signal_connect(elem->data, "value_changed", (GtkSignalFunc) xsane_back_gtk_range_display_value_right_callback, (void *) digits);
2020     gtk_object_set_data(GTK_OBJECT(elem->data), "value-label", value_label);
2021     g_signal_emit_by_name(GTK_OBJECT(elem->data), "value_changed"); /* update value */
2022     gtk_widget_show(value_label);
2023     gtk_widget_set_sensitive(value_label, settable);
2024   }
2025 
2026   /* spinbutton */
2027   if (preferences.show_range_mode & 4)
2028   {
2029 #ifndef HAVE_GTK2
2030     if (digits > 5)
2031     {
2032       digits = 5;
2033     }
2034 #endif
2035     spinbutton = gtk_spin_button_new(GTK_ADJUSTMENT(elem->data), 0, digits);
2036 
2037     if (preferences.show_range_mode & 3) /* slider also visible */
2038     {
2039       gtk_widget_set_size_request(spinbutton, 70, -1);
2040     }
2041     else /* slider not visible */
2042     {
2043       gtk_widget_set_size_request(spinbutton, 100, -1);
2044     }
2045 
2046     xsane_back_gtk_set_tooltip(xsane.tooltips, spinbutton, desc);
2047     gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinbutton), FALSE);
2048     gtk_box_pack_end(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 5); /* make spinbutton not sizeable */
2049     gtk_widget_show(spinbutton);
2050     gtk_widget_set_sensitive(spinbutton, settable);
2051     elem->widget = spinbutton;
2052   }
2053 
2054   /* slider */
2055   if (preferences.show_range_mode & 3)
2056   {
2057     if (preferences.show_range_mode & 1) /* bit 0 (val 1) : scale */
2058     {
2059       slider = gtk_hscale_new(GTK_ADJUSTMENT(elem->data));
2060       gtk_scale_set_draw_value(GTK_SCALE(slider), FALSE);
2061     }
2062     else /* bit 1 (val 2) : scrollbar */
2063     {
2064       slider = gtk_hscrollbar_new(GTK_ADJUSTMENT(elem->data));
2065     }
2066     xsane_back_gtk_set_tooltip(xsane.tooltips, slider, desc);
2067     gtk_widget_set_size_request(slider, 140, -1);
2068     /* GTK_UPDATE_CONTINUOUS, GTK_UPDATE_DISCONTINUOUS, GTK_UPDATE_DELAYED */
2069     gtk_range_set_update_policy(GTK_RANGE(slider), preferences.gtk_update_policy);
2070     gtk_box_pack_end(GTK_BOX(hbox), slider, FALSE, FALSE, 5); /* make slider not sizeable */
2071     gtk_widget_show(slider);
2072     gtk_widget_set_sensitive(slider, settable);
2073   }
2074 
2075   if (automatic)
2076   {
2077     xsane_back_gtk_autobutton_new(hbox, elem, tooltips);
2078   }
2079 
2080   g_signal_connect(elem->data, "value_changed", (GtkSignalFunc) xsane_back_gtk_value_update, elem);
2081 
2082   gtk_widget_show(label);
2083   gtk_widget_show(hbox);
2084 
2085   if (elem->widget)
2086   {
2087     elem->widget2 = slider; /* widget is used by spinbutton */
2088   }
2089   else
2090   {
2091     elem->widget  = slider; /* we do not have a spinbutton */
2092   }
2093 }
2094 
2095 /* ----------------------------------------------------------------------------------------------------------------- */
2096 
xsane_back_gtk_value_new(GtkWidget * parent,const char * name,gfloat val,gfloat quant,int automatic,DialogElement * elem,GtkTooltips * tooltips,const char * desc,SANE_Int settable)2097 void xsane_back_gtk_value_new(GtkWidget *parent, const char *name, gfloat val,
2098 	   gfloat quant, int automatic,
2099 	   DialogElement *elem, GtkTooltips *tooltips, const char *desc, SANE_Int settable)
2100 {
2101  GtkWidget *hbox, *label, *spinbutton;
2102  int digits;
2103 
2104   DBG(DBG_proc, "xsane_back_gtk_value_new(%s)\n", name);
2105 
2106   if (quant - (int) quant == 0.0)
2107   {
2108     digits = 0;
2109   }
2110   else
2111   {
2112     digits = (int) (log10(1/quant)+0.8); /* set number of digits in dependance of quantization */
2113   }
2114 
2115   if (digits < 0)
2116   {
2117     digits = 0;
2118   }
2119 
2120   hbox = gtk_hbox_new(FALSE, 2);
2121   gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
2122   gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
2123 
2124   label = gtk_label_new((char *) name);
2125   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
2126 
2127   elem->data = gtk_adjustment_new(val, -1e29, 1e29, 1, 10, 0);
2128 
2129   /* spinbutton */
2130 #ifndef HAVE_GTK2
2131   if (digits > 5)
2132   {
2133     digits = 5;
2134   }
2135 #endif
2136   spinbutton = gtk_spin_button_new(GTK_ADJUSTMENT(elem->data), 0, digits);
2137 
2138   if (preferences.show_range_mode & 3) /* sliders are visible */
2139   {
2140     gtk_widget_set_size_request(spinbutton, 70, -1);
2141   }
2142   else /* sliders not visible */
2143   {
2144     gtk_widget_set_size_request(spinbutton, 100, -1);
2145   }
2146 
2147   xsane_back_gtk_set_tooltip(xsane.tooltips, spinbutton, desc);
2148   gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinbutton), FALSE);
2149   gtk_box_pack_end(GTK_BOX(hbox), spinbutton, FALSE, FALSE, 5); /* make spinbutton not sizeable */
2150   gtk_widget_show(spinbutton);
2151   gtk_widget_set_sensitive(spinbutton, settable);
2152   elem->widget = spinbutton;
2153 
2154   if (automatic)
2155   {
2156     xsane_back_gtk_autobutton_new(hbox, elem, tooltips);
2157   }
2158 
2159   g_signal_connect(elem->data, "value_changed", (GtkSignalFunc) xsane_back_gtk_value_update, elem);
2160 
2161   gtk_widget_show(label);
2162   gtk_widget_show(hbox);
2163 }
2164 
2165 /* ----------------------------------------------------------------------------------------------------------------- */
2166 
xsane_back_gtk_push_button_callback(GtkWidget * widget,gpointer data)2167 void xsane_back_gtk_push_button_callback(GtkWidget * widget, gpointer data)
2168 {
2169  DialogElement *elem = data;
2170  int opt_num;
2171 
2172   DBG(DBG_proc, "xsane_back_gtk_push_button_callback\n");
2173 
2174   opt_num = elem - xsane.element;
2175   xsane_back_gtk_set_option(opt_num, 0, SANE_ACTION_SET_VALUE);
2176 }
2177 
2178 /* ----------------------------------------------------------------------------------------------------------------- */
2179 
xsane_back_gtk_option_menu_lookup(MenuItem menu_items[],const char * string)2180 static int xsane_back_gtk_option_menu_lookup(MenuItem menu_items[], const char *string)
2181 {
2182  int i;
2183 
2184   DBG(DBG_proc, "xsane_back_gtk_option_menu_lookup\n");
2185 
2186   for (i = 0; (menu_items[i].label) && strcmp(menu_items[i].label, string) != 0; ++i);
2187 
2188  return i;
2189 }
2190 
2191 /* ----------------------------------------------------------------------------------------------------------------- */
2192 
xsane_back_gtk_option_menu_callback(GtkWidget * widget,gpointer data)2193 static void xsane_back_gtk_option_menu_callback(GtkWidget * widget, gpointer data)
2194 {
2195  MenuItem *menu_item = data;
2196  DialogElement *elem = menu_item->elem;
2197  const SANE_Option_Descriptor *opt;
2198  int opt_num;
2199  double dval;
2200  SANE_Word val;
2201  void *valp = &val;
2202 
2203   DBG(DBG_proc, "xsane_back_gtk_option_menu_callback\n");
2204 
2205   opt_num = elem - xsane.element;
2206   opt = xsane_get_option_descriptor(xsane.dev, opt_num);
2207   switch(opt->type)
2208   {
2209     case SANE_TYPE_INT:
2210       sscanf(menu_item->label, "%d", &val);
2211      break;
2212 
2213     case SANE_TYPE_FIXED:
2214       sscanf(menu_item->label, "%lg", &dval);
2215       val = SANE_FIX(dval);
2216      break;
2217 
2218     case SANE_TYPE_STRING:
2219       valp = menu_item->label;
2220      break;
2221 
2222     default:
2223       DBG(DBG_error, "xsane_back_gtk_option_menu_callback: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
2224      break;
2225   }
2226   xsane_back_gtk_set_option(opt_num, valp, SANE_ACTION_SET_VALUE);
2227 }
2228 
2229 /* ----------------------------------------------------------------------------------------------------------------- */
2230 
xsane_back_gtk_option_menu_new(GtkWidget * parent,const char * name,char * str_list[],const char * val,DialogElement * elem,GtkTooltips * tooltips,const char * desc,SANE_Int settable)2231 void xsane_back_gtk_option_menu_new(GtkWidget *parent, const char *name, char *str_list[],
2232 		 const char *val, DialogElement *elem,
2233 		 GtkTooltips *tooltips, const char *desc, SANE_Int settable)
2234 {
2235  GtkWidget *hbox, *label, *option_menu, *menu, *item;
2236  MenuItem *menu_items;
2237  int i, num_items;
2238 
2239   DBG(DBG_proc, "xsane_back_gtk_option_menu_new(%s)\n", name);
2240 
2241   hbox = gtk_hbox_new(FALSE, 2);
2242   gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
2243   gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
2244 
2245   label = gtk_label_new((char *) name);
2246   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
2247 
2248   for (num_items = 0; str_list[num_items]; ++num_items)
2249   {
2250   }
2251 
2252   menu_items = malloc((num_items + 1) * sizeof(menu_items[0]));
2253 
2254   menu = gtk_menu_new();
2255   for (i = 0; i < num_items; ++i)
2256   {
2257     item = gtk_menu_item_new_with_label(_BGT(str_list[i]));
2258     gtk_container_add(GTK_CONTAINER(menu), item);
2259     g_signal_connect(GTK_OBJECT(item), "activate", (GtkSignalFunc) xsane_back_gtk_option_menu_callback, menu_items + i);
2260 
2261     gtk_widget_show(item);
2262 
2263     menu_items[i].label = str_list[i];
2264     menu_items[i].elem = elem;
2265     menu_items[i].index = i;
2266   }
2267 
2268   /* add empty element as end of list marker */
2269   menu_items[i].label = NULL;
2270   menu_items[i].elem = NULL;
2271   menu_items[i].index = 0;
2272 
2273   option_menu = gtk_option_menu_new();
2274   gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 2);
2275   gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2276   gtk_option_menu_set_history(GTK_OPTION_MENU(option_menu), xsane_back_gtk_option_menu_lookup(menu_items, val));
2277   xsane_back_gtk_set_tooltip(tooltips, option_menu, desc);
2278 
2279   gtk_widget_show(label);
2280   gtk_widget_show(option_menu);
2281   gtk_widget_show(hbox);
2282 
2283   gtk_widget_set_sensitive(option_menu, settable);
2284 
2285   elem->widget  = option_menu;
2286   elem->menu_size = num_items;
2287   elem->menu = menu_items;
2288 }
2289 
2290 /* ----------------------------------------------------------------------------------------------------------------- */
2291 
xsane_back_gtk_text_entry_callback(GtkWidget * w,gpointer data)2292 static void xsane_back_gtk_text_entry_callback(GtkWidget *w, gpointer data)
2293 {
2294  DialogElement *elem = data;
2295  const SANE_Option_Descriptor *opt;
2296  const gchar *text;
2297  int opt_num;
2298  char *buf;
2299 
2300   DBG(DBG_proc, "xsane_back_gtk_text_entry_callback\n");
2301 
2302   opt_num = elem - xsane.element;
2303   opt = xsane_get_option_descriptor(xsane.dev, opt_num);
2304 
2305   buf = alloca(opt->size);
2306   buf[0] = '\0';
2307 
2308   text = gtk_entry_get_text(GTK_ENTRY(elem->widget));
2309   if (text)
2310   {
2311     strncpy(buf, text, opt->size);
2312   }
2313   buf[opt->size - 1] = '\0';
2314 
2315   xsane_back_gtk_set_option(opt_num, buf, SANE_ACTION_SET_VALUE);
2316 
2317   if (strcmp(buf, text) != 0) /* the backend modified the option value; update widget: */
2318   {
2319     gtk_entry_set_text(GTK_ENTRY(elem->widget), buf);
2320   }
2321 }
2322 
2323 /* ----------------------------------------------------------------------------------------------------------------- */
2324 
xsane_back_gtk_text_entry_new(GtkWidget * parent,const char * name,const char * val,DialogElement * elem,GtkTooltips * tooltips,const char * desc,SANE_Int settable)2325 void xsane_back_gtk_text_entry_new(GtkWidget * parent, const char *name, const char *val, DialogElement *elem,
2326 		        GtkTooltips *tooltips, const char *desc, SANE_Int settable)
2327 {
2328  GtkWidget *hbox, *text, *label;
2329 
2330   DBG(DBG_proc, "xsane_back_gtk_text_entry_new\n");
2331 
2332   hbox = gtk_hbox_new(FALSE, 2);
2333   gtk_container_set_border_width(GTK_CONTAINER(hbox), 0);
2334   gtk_box_pack_start(GTK_BOX(parent), hbox, FALSE, FALSE, 0);
2335 
2336   label = gtk_label_new((char *) name);
2337   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
2338 
2339   text = gtk_entry_new();
2340   gtk_entry_set_text(GTK_ENTRY(text), (char *) val);
2341 /*  gtk_box_pack_start(GTK_BOX(hbox), text, FALSE, TRUE, 0); */ /* text entry fixed */
2342   gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 0); /* text entry sizeable */
2343   g_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_back_gtk_text_entry_callback, elem);
2344   xsane_back_gtk_set_tooltip(tooltips, text, desc);
2345 
2346   gtk_widget_show(hbox);
2347   gtk_widget_show(label);
2348   gtk_widget_show(text);
2349 
2350   gtk_widget_set_sensitive(text, settable);
2351 
2352   elem->widget  = text;
2353 }
2354 
2355 /* ----------------------------------------------------------------------------------------------------------------- */
2356 
xsane_back_gtk_group_new(GtkWidget * parent,const char * title)2357 GtkWidget *xsane_back_gtk_group_new(GtkWidget *parent, const char *title)
2358 {
2359  GtkWidget * frame, * vbox;
2360 
2361   DBG(DBG_proc, "xsane_back_gtk_group_new(%s)\n", title);
2362 
2363   frame = gtk_frame_new((char *) title);
2364   gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
2365   gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
2366   gtk_box_pack_start(GTK_BOX(parent), frame, FALSE, FALSE, 0);
2367 
2368   vbox = gtk_vbox_new(FALSE, 4);
2369   gtk_container_set_border_width(GTK_CONTAINER(vbox), 2);
2370   gtk_container_add(GTK_CONTAINER(frame), vbox);
2371   gtk_widget_show(vbox);
2372   return vbox;
2373 }
2374 
2375 /* ----------------------------------------------------------------------------------------------------------------- */
2376 #if 0
2377 static void tooltips_destroy(void)
2378 {
2379   DBG(DBG_proc, "tooltips_destroy\n");
2380 
2381   gtk_object_unref(GTK_OBJECT(xsane.tooltips));
2382   xsane.tooltips = 0;
2383 }
2384 #endif
2385 /* ----------------------------------------------------------------------------------------------------------------- */
2386 
xsane_back_gtk_panel_destroy(void)2387 static void xsane_back_gtk_panel_destroy(void)
2388 {
2389  const SANE_Option_Descriptor *opt;
2390  DialogElement *elem;
2391  int i, j;
2392 
2393   DBG(DBG_proc, "xsane_back_gtk_panel_destroy\n");
2394 
2395   if (!xsane.xsane_hbox)
2396   {
2397     DBG(DBG_proc, "xsane_back_gtk_panel_destroy: panel does not exist\n");
2398    return;
2399   }
2400 
2401   gtk_widget_destroy(xsane.xsane_hbox);
2402   gtk_widget_destroy(xsane.standard_hbox);
2403   gtk_widget_destroy(xsane.advanced_hbox);
2404 
2405   xsane.xsane_hbox    = NULL;
2406   xsane.standard_hbox = NULL;
2407   xsane.advanced_hbox = NULL;
2408 
2409   /* free the menu labels of integer/fix-point word-lists: */
2410   for (i = 0; i < xsane.num_elements; ++i)
2411   {
2412     if (xsane.element[i].menu)
2413     {
2414       opt = xsane_get_option_descriptor(xsane.dev, i);
2415       elem = xsane.element + i;
2416       if (opt->type != SANE_TYPE_STRING)
2417       {
2418         for (j = 0; j < elem->menu_size; ++j)
2419         {
2420           if (elem->menu[j].label)
2421           {
2422             free(elem->menu[j].label);
2423             elem->menu[j].label = 0;
2424           }
2425         }
2426         free(elem->menu);
2427         elem->menu = 0;
2428       }
2429     }
2430   }
2431   memset(xsane.element, 0, xsane.num_elements * sizeof(xsane.element[0]));
2432 }
2433 
2434 /* ----------------------------------------------------------------------------------------------------------------- */
2435 
xsane_back_gtk_panel_rebuild(void)2436 static void xsane_back_gtk_panel_rebuild(void)
2437 {
2438   DBG(DBG_proc, "xsane_back_gtk_panel_rebuild\n");
2439 
2440   xsane_back_gtk_panel_destroy();
2441   xsane_panel_build();
2442 }
2443 
2444 /* ----------------------------------------------------------------------------------------------------------------- */
2445 
xsane_back_gtk_refresh_dialog(void)2446 void xsane_back_gtk_refresh_dialog(void)
2447 {
2448   DBG(DBG_proc, "xsane_back_gtk_refresh_dialog\n");
2449 
2450   xsane_back_gtk_panel_rebuild();
2451   xsane_update_param(0);
2452 }
2453 
2454 /* ----------------------------------------------------------------------------------------------------------------- */
2455 
xsane_back_gtk_update_scan_window(void)2456 void xsane_back_gtk_update_scan_window(void)
2457 {
2458  const SANE_Option_Descriptor *opt;
2459  double old_val, new_val;
2460  DialogElement *elem;
2461  SANE_Status status;
2462  SANE_Word word;
2463  int i, optnum;
2464  char str[64];
2465 
2466   DBG(DBG_proc, "xsane_back_gtk_update_scan_window\n");
2467 
2468   for (i = 0; i < 4; ++i)
2469   {
2470     if (xsane.well_known.coord[i] > 0)
2471     {
2472       optnum = xsane.well_known.coord[i];
2473       elem = xsane.element + optnum;
2474       opt = xsane_get_option_descriptor(xsane.dev, optnum);
2475 
2476       status = xsane_control_option(xsane.dev, optnum, SANE_ACTION_GET_VALUE, &word, 0);
2477       if (status != SANE_STATUS_GOOD)
2478       {
2479          continue; /* sliently ignore errors */
2480       }
2481 
2482       switch(opt->constraint_type)
2483       {
2484         case SANE_CONSTRAINT_RANGE:
2485           if (opt->type == SANE_TYPE_INT)
2486           {
2487             old_val = GTK_ADJUSTMENT(elem->data)->value;
2488             new_val = word;
2489             GTK_ADJUSTMENT(elem->data)->value = new_val;
2490           }
2491           else
2492           {
2493             old_val = GTK_ADJUSTMENT(elem->data)->value;
2494             new_val = SANE_UNFIX(word);
2495 	    if (opt->unit == SANE_UNIT_MM)
2496             {
2497               new_val /= preferences.length_unit;
2498             }
2499             GTK_ADJUSTMENT(elem->data)->value = new_val;
2500           }
2501 
2502           if (old_val != new_val) /* XXX dangerous comparison of doubles, we should allow tiny differences */
2503           {
2504 	      g_signal_emit_by_name(GTK_OBJECT(elem->data), "value_changed");
2505           }
2506          break;
2507 
2508 	case SANE_CONSTRAINT_WORD_LIST:
2509 	  if (opt->type == SANE_TYPE_INT)
2510           {
2511 	    sprintf(str, "%d", word);
2512           }
2513 	  else
2514           {
2515 	    sprintf(str, "%g", SANE_UNFIX(word));
2516           }
2517 	  /* XXX maybe we should call this only when the value changes... */
2518 	  gtk_option_menu_set_history(GTK_OPTION_MENU(elem->widget), xsane_back_gtk_option_menu_lookup(elem->menu, str));
2519 	 break;
2520 
2521 	default:
2522 	 break;
2523       }
2524     }
2525   }
2526 }
2527 
2528 /* ----------------------------------------------------------------------------------------------------------------- */
2529 
xsane_back_gtk_update_vector(int opt_num,SANE_Int * vector)2530 void xsane_back_gtk_update_vector(int opt_num, SANE_Int *vector)
2531 {
2532  const SANE_Option_Descriptor *opt;
2533  gfloat val;
2534  SANE_Word *optval;
2535  int j, optlen;
2536 
2537   DBG(DBG_proc, "xsane_back_gtk_update_vector\n");
2538 
2539   if (opt_num < 1)
2540     return; /* not defined */
2541 
2542   opt = xsane_get_option_descriptor(xsane.dev, opt_num);
2543   if (!SANE_OPTION_IS_ACTIVE(opt->cap))
2544   {
2545     return; /* inactive */
2546   }
2547 
2548   if (opt->type != SANE_TYPE_INT && opt->type != SANE_TYPE_FIXED)
2549   {
2550     return;
2551   }
2552 
2553   if (opt->size == sizeof(SANE_Word))
2554   {
2555     return;
2556   }
2557 
2558   /* ok, we're dealing with an active vector */
2559 
2560   optlen = opt->size / sizeof(SANE_Word);
2561   optval = alloca(optlen * sizeof(optval[0]));
2562   for (j = 0; j < optlen; ++j)
2563   {
2564     val = vector[j];
2565     if (opt->type == SANE_TYPE_FIXED)
2566     {
2567       optval[j] = SANE_FIX(val);
2568     }
2569     else
2570     {
2571       optval[j] = val + 0.5;
2572     }
2573   }
2574 
2575   xsane_back_gtk_set_option(opt_num, optval, SANE_ACTION_SET_VALUE);
2576 }
2577 
2578 /* ----------------------------------------------------------------------------------------------------------------- */
2579 
xsane_back_gtk_set_tooltips(int enable)2580 void xsane_back_gtk_set_tooltips(int enable)
2581 {
2582   DBG(DBG_proc, "xsane_back_gtk_set_tooltips\n");
2583 
2584   if (!xsane.tooltips)
2585   {
2586     return;
2587   }
2588 
2589   if (enable)
2590   {
2591     gtk_tooltips_enable(xsane.tooltips);
2592   }
2593   else
2594   {
2595     gtk_tooltips_disable(xsane.tooltips);
2596   }
2597 }
2598 
2599 /* ----------------------------------------------------------------------------------------------------------------- */
2600 
xsane_back_gtk_set_sensitivity(int sensitive)2601 void xsane_back_gtk_set_sensitivity(int sensitive)
2602 {
2603  const SANE_Option_Descriptor *opt;
2604  int i;
2605 
2606   DBG(DBG_proc, "xsane_back_gtk_set_sensitivity\n");
2607 
2608   for (i = 0; i < xsane.num_elements; ++i)
2609   {
2610     opt = xsane_get_option_descriptor(xsane.dev, i);
2611 
2612     if (!SANE_OPTION_IS_ACTIVE(opt->cap) || !SANE_OPTION_IS_SETTABLE(opt->cap) ||
2613         opt->type == SANE_TYPE_GROUP || !xsane.element[i].widget)
2614     {
2615       continue;
2616     }
2617 
2618   //  if (!(opt->cap & SANE_CAP_ALWAYS_SETTABLE))
2619     {
2620       gtk_widget_set_sensitive(xsane.element[i].widget, sensitive);
2621     }
2622   }
2623 
2624   if (xsane.xsanemode_widget)
2625   {
2626     gtk_widget_set_sensitive(xsane.xsanemode_widget, sensitive);
2627   }
2628 
2629   while (gtk_events_pending())
2630   {
2631    gtk_main_iteration();
2632   }
2633 }
2634 /* ---------------------------------------------------------------------------------------------------------------------- */
2635 
xsane_set_sensitivity(SANE_Int sensitivity)2636 void xsane_set_sensitivity(SANE_Int sensitivity)
2637 {
2638   DBG(DBG_proc, "xsane_set_sensitivity(%d)\n", sensitivity);
2639 
2640   if (xsane.dialog)
2641   {
2642     /* clear or rebuild histogram */
2643     if (sensitivity)
2644     {
2645       xsane_update_histogram(TRUE /* update raw */);
2646     }
2647     else
2648     {
2649       xsane_clear_histogram(&xsane.histogram_raw);
2650       xsane_clear_histogram(&xsane.histogram_enh);
2651     }
2652 
2653     gtk_widget_set_sensitive(xsane.menubar, sensitivity);
2654     gtk_widget_set_sensitive(xsane.xsane_window, sensitivity);
2655     gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), sensitivity);
2656     gtk_widget_set_sensitive(xsane.standard_options_dialog, sensitivity);
2657     gtk_widget_set_sensitive(xsane.advanced_options_dialog, sensitivity);
2658     gtk_widget_set_sensitive(xsane.histogram_dialog, sensitivity);
2659 #ifdef HAVE_WORKING_GTK_GAMMACURVE
2660     gtk_widget_set_sensitive(xsane.gamma_dialog, sensitivity);
2661 #endif
2662   }
2663 
2664   if (xsane.preview)
2665   {
2666     gtk_widget_set_sensitive(xsane.preview->button_box, sensitivity);   /* button box at top of window */
2667     gtk_widget_set_sensitive(xsane.preview->menu_box, sensitivity);     /* menu box at top of window */
2668 #if 1
2669     gtk_widget_set_sensitive(xsane.preview->viewport, sensitivity);     /* Preview image selection */
2670 #endif
2671     gtk_widget_set_sensitive(xsane.preview->start, sensitivity);        /* Acquire preview button */
2672   }
2673 
2674   if (xsane.project_dialog)
2675   {
2676     /* do not change sensitivity of project_dialog, we want the progress bar */
2677     /* to be sensitive */
2678     gtk_widget_set_sensitive(xsane.project_box, sensitivity);
2679     gtk_widget_set_sensitive(xsane.project_exists, sensitivity);
2680     gtk_widget_set_sensitive(xsane.project_entry_box, sensitivity);
2681   }
2682 
2683   if (xsane.batch_scan_dialog)
2684   {
2685     gtk_widget_set_sensitive(xsane.batch_scan_button_box, sensitivity);
2686     gtk_widget_set_sensitive(xsane.batch_scan_action_box, sensitivity);
2687   }
2688 
2689   xsane.sensitivity = sensitivity;
2690 }
2691 
2692 /* ----------------------------------------------------------------------------------------------------------------- */
2693 
xsane_back_gtk_destroy_dialog(void)2694 void xsane_back_gtk_destroy_dialog(void)
2695 {
2696  SANE_Handle dev = xsane.dev;
2697 
2698   DBG(DBG_proc, "xsane_back_gtk_destroy_dialog\n");
2699 
2700   xsane_back_gtk_panel_destroy();
2701   free((void *) xsane.dev_name);
2702   free(xsane.element);
2703 
2704   sane_close(dev);
2705 }
2706 /* ---------------------------------------------------------------------------------------------------------------------- */
2707 
xsane_set_window_icon(GtkWidget * gtk_window,gchar ** xpm_d)2708 void xsane_set_window_icon(GtkWidget *gtk_window, gchar **xpm_d)
2709 {
2710  GdkPixmap *pixmap;
2711  GdkBitmap *mask;
2712 
2713   DBG(DBG_proc, "xsane_set_window_icon\n");
2714 
2715   gtk_widget_realize(gtk_window);
2716   if (xpm_d)
2717   {
2718     pixmap = gdk_pixmap_create_from_xpm_d(gtk_window->window, &mask, xsane.bg_trans, xpm_d);
2719   }
2720   else
2721   {
2722     if (xsane.window_icon_pixmap)
2723     {
2724       pixmap = xsane.window_icon_pixmap;
2725       mask   = xsane.window_icon_mask;
2726     }
2727     else
2728     {
2729       pixmap = gdk_pixmap_create_from_xpm_d(gtk_window->window, &mask, xsane.bg_trans, (gchar **) xsane_window_icon_xpm);
2730     }
2731   }
2732 
2733   gdk_window_set_icon(gtk_window->window, 0, pixmap, mask);
2734 }
2735 
2736 /* ---------------------------------------------------------------------------------------------------------------------- */
2737