1 /* pisa_view_manager.cc
2 Copyright (C) 2001, 2004, 2005, 2008 SEIKO EPSON Corporation
3
4 This file is part of the `iscan' program.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 As a special exception, the copyright holders give permission
21 to link the code of this program with the esmod library and
22 distribute linked combinations including the two. You must obey
23 the GNU General Public License in all respects for all of the
24 code used other then esmod.
25 */
26
27 #include <config.h>
28
29 #include "gettext.h"
30 #define _(msg_id) gettext (msg_id)
31
32 /*------------------------------------------------------------*/
33 #include <gtk/gtk.h>
34 #ifndef HAVE_GTK_2
35 #include <gdk_imlib.h>
36 #endif
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <assert.h>
43 #include <regex.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <dirent.h>
47 #include <locale.h>
48
49 /*------------------------------------------------------------*/
50 #include "pisa_view_manager.h"
51 #include "pisa_error.h"
52 #include "pisa_main.h"
53 #include "pisa_scan_tool.h"
54 #include "pisa_default_val.h"
55 #include "pisa_gimp.h"
56 #include "pisa_aleart_dialog.h"
57 #include "pisa_change_unit.h"
58 #include "pisa_preference.h"
59 #include "pisa_scan_selector.h"
60
61 #include "file-selector.h"
62 // FIXME: lump these together in ../lib/image-stream.hh or something
63 #include "../lib/imgstream.hh"
64 #include "../lib/pnmstream.hh"
65 #include "../lib/pngstream.hh"
66 #include "../lib/jpegstream.hh"
67 #include "../lib/colour.hh"
68
69 #define DEFAULT_RESOLUTION 300 // dpi
70
71 /*------------------------------------------------------------*/
72 view_manager * g_view_manager = 0;
73
74 /*------------------------------------------------------------*/
create_view_manager(int argc,char * argv[])75 int view_manager::create_view_manager ( int argc, char * argv [ ] )
76 {
77 ::gtk_set_locale ( );
78 ::setlocale (LC_NUMERIC, "C");
79 ::gtk_init ( & argc, & argv );
80
81 #ifndef HAVE_GTK_2
82 ::gdk_imlib_init ( );
83 ::gtk_widget_push_visual ( ::gdk_imlib_get_visual ( ) );
84 ::gtk_widget_push_colormap ( ::gdk_imlib_get_colormap ( ) );
85 #endif
86
87 try
88 {
89 if ( ::g_view_manager )
90 throw pisa_error ( PISA_ERR_PARAMETER );
91
92 ::g_view_manager = new view_manager;
93 if ( ::g_view_manager == 0 )
94 throw pisa_error ( PISA_ERR_OUTOFMEMORY );
95
96 // initialize parameters
97 ::g_view_manager->init ( );
98
99 }
100 catch ( pisa_error & err )
101 {
102 aleart_dialog aleart_dlg;
103
104 aleart_dlg.message_box ( 0, err.get_error_string ( ) );
105
106 if ( ::g_view_manager )
107 {
108 delete ::g_view_manager;
109 ::g_view_manager = 0;
110 }
111
112 return err.get_error_id ( );
113 }
114
115 return PISA_ERR_SUCCESS;
116 }
117
118 /*------------------------------------------------------------*/
release_view_manager(void)119 void view_manager::release_view_manager ( void )
120 {
121 if ( ::g_view_manager )
122 {
123 delete ::g_view_manager;
124 ::g_view_manager = 0;
125 }
126 }
127
128 /*------------------------------------------------------------*/
main(void)129 int view_manager::main ( void )
130 {
131 gtk_main ( );
132
133 return PISA_ERR_SUCCESS;
134 }
135
136 /*------------------------------------------------------------*/
init(void)137 void view_manager::init ( void )
138 {
139 // initialize
140 m_scanmanager_cls = 0;
141 m_main_cls = 0;
142 m_prev_cls = 0;
143 m_imgctrl_cls = 0;
144 m_gamma_cls = 0;
145 m_config_cls = 0;
146 m_filsel_cls = 0;
147 m_scansel_cls = 0;
148
149 // open scanner
150 m_scanmanager_cls = new scan_manager;
151
152 if ( ! m_scanmanager_cls )
153 throw pisa_error ( PISA_ERR_OUTOFMEMORY );
154
155 open_device ( );
156
157 m_set.option = PISA_OP_FLATBED;
158 m_set.film = PISA_FT_REFLECT;
159 m_set.resolution = DEFAULT_RESOLUTION;
160 m_set.enable_start_button = false;
161 m_set.usm = 1;
162 m_set.unit = PISA_UNIT_INCHES;
163
164 if ( pisa_gimp_plugin ( ) )
165 m_set.destination = PISA_DE_GIMP;
166 else
167 m_set.destination = PISA_DE_FILE;
168
169 m_main_cls = new main_window;
170 m_prev_cls = new preview_window;
171 m_imgctrl_cls = new image_controls;
172 m_gamma_cls = new gamma_correction;
173 m_config_cls = new config_window;
174 m_filsel_cls = new file_selector;
175
176 if ( ! m_main_cls ||
177 ! m_prev_cls ||
178 ! m_imgctrl_cls ||
179 ! m_gamma_cls ||
180 ! m_config_cls ||
181 ! m_filsel_cls )
182 {
183 destroy ( );
184 throw ( PISA_ERR_OUTOFMEMORY );
185 }
186
187 init_img_info ( static_cast <pisa_option_type> ( m_set.option ),
188 static_cast <pisa_film_type> ( m_set.film ) );
189
190 m_main_cls->init ( );
191 m_prev_cls->init ( );
192 m_gamma_cls->init ( );
193 m_config_cls->init ( );
194 m_filsel_cls->init ( );
195
196 load_preference ( );
197
198 }
199
200 /*------------------------------------------------------------*/
destroy(void)201 void view_manager::destroy ( void )
202 {
203 save_preference ( );
204
205 m_set.delete_all ( );
206
207 if ( m_scanmanager_cls )
208 {
209 try
210 {
211 close_device ( );
212 }
213 catch ( pisa_error & err )
214 {
215 aleart_dialog aleart_dlg;
216
217 aleart_dlg.message_box ( 0, err.get_error_string ( ) );
218 }
219
220 delete m_scanmanager_cls;
221 m_scanmanager_cls = 0;
222 }
223
224 if ( m_main_cls )
225 {
226 delete m_main_cls;
227 m_main_cls = 0;
228 }
229
230 if ( m_prev_cls )
231 {
232 delete m_prev_cls;
233 m_prev_cls = 0;
234 }
235
236 if ( m_imgctrl_cls )
237 {
238 delete m_imgctrl_cls;
239 m_imgctrl_cls = 0;
240 }
241
242 if ( m_gamma_cls )
243 {
244 delete m_gamma_cls;
245 m_gamma_cls = 0;
246 }
247
248 if ( m_config_cls )
249 {
250 delete m_config_cls;
251 m_config_cls = 0;
252 }
253
254 if ( m_filsel_cls )
255 {
256 delete m_filsel_cls;
257 m_filsel_cls = 0;
258 }
259
260 if ( m_scansel_cls )
261 {
262 delete m_scansel_cls;
263 m_scansel_cls = 0;
264 }
265
266 ::pisa_quit ( );
267 }
268
269 /*------------------------------------------------------------*/
create_window(pisa_window_id id)270 GtkWidget * view_manager::create_window ( pisa_window_id id )
271 {
272 GtkWidget * ret = 0;
273
274 switch ( id )
275 {
276 case ID_WINDOW_MAIN:
277 ret = m_main_cls->create_window ( 0 );
278 break;
279
280 case ID_WINDOW_PREV:
281 ret = m_prev_cls->create_window ( 0 );
282 break;
283
284 case ID_WINDOW_IMGCTRL:
285 ret = m_imgctrl_cls->create_window ( 0 );
286 break;
287
288 case ID_WINDOW_GAMMA:
289 ret = m_gamma_cls->create_window ( m_main_cls->get_widget ( ) );
290 break;
291
292 case ID_WINDOW_CONFIG:
293 ret = m_config_cls->create_window ( m_main_cls->get_widget ( ) );
294 break;
295 }
296
297 return ret;
298 }
299
300 /*------------------------------------------------------------*/
close_window(pisa_window_id id,int destroy_flag)301 int view_manager::close_window ( pisa_window_id id, int destroy_flag )
302 {
303 switch ( id )
304 {
305 case ID_WINDOW_MAIN:
306 m_main_cls->close_window ( destroy_flag );
307 destroy ( );
308 break;
309
310 case ID_WINDOW_PREV:
311 m_prev_cls->close_window ( destroy_flag );
312 break;
313
314 case ID_WINDOW_GAMMA:
315 m_gamma_cls->close_window ( destroy_flag );
316 break;
317
318 case ID_WINDOW_CONFIG:
319 m_config_cls->close_window ( destroy_flag );
320 break;
321
322 default:
323 return PISA_ERR_PARAMETER;
324 }
325
326 return PISA_ERR_PARAMETER;
327 }
328
329 /*------------------------------------------------------------*/
get_window_cls(pisa_window_id id)330 void * view_manager::get_window_cls ( pisa_window_id id )
331 {
332 void * ret = 0;
333
334 switch ( id )
335 {
336 case ID_WINDOW_MAIN:
337 ret = m_main_cls;
338 break;
339
340 case ID_WINDOW_PREV:
341 ret = m_prev_cls;
342 break;
343
344 case ID_WINDOW_IMGCTRL:
345 ret = m_imgctrl_cls;
346 break;
347
348 case ID_WINDOW_GAMMA:
349 ret = m_gamma_cls;
350 break;
351
352 case ID_WINDOW_CONFIG:
353 ret = m_config_cls;
354 break;
355
356 }
357
358 return ret;
359 }
360
361 /*------------------------------------------------------------*/
sensitive(void)362 void view_manager::sensitive ( void )
363 {
364 int is_img = m_prev_cls->is_prev_img ( );
365
366 m_main_cls->sensitive ( is_img );
367 m_imgctrl_cls->sensitive ( is_img );
368 m_gamma_cls->sensitive ( is_img );
369 }
370
371 /*------------------------------------------------------------*/
is_gimp(void)372 int view_manager::is_gimp ( void )
373 {
374 return pisa_gimp_plugin ( );
375 }
376
377 /*------------------------------------------------------------*/
start_scan(void)378 void view_manager::start_scan ( void )
379 {
380 if (PISA_ERR_SUCCESS != init_scan_param ())
381 return;
382
383 // All types of scans need to provide visual feedback on their
384 // progress and for consecutive scans (via ADF or with the start
385 // button enabled) it is better to create it here. That way, it
386 // will not disappear and reappear between calls to scan_file() and
387 // scan_gimp(), eliminating rather annoying flicker.
388 _feedback = new progress_window (m_main_cls->get_widget ());
389 switch ( m_set.destination )
390 {
391 case PISA_DE_FILE:
392 do_scan_file ();
393 break;
394
395 case PISA_DE_PRINTER:
396 do_scan_printer ();
397 break;
398
399 case PISA_DE_GIMP:
400 do_scan_gimp ();
401 break;
402 }
403 delete _feedback;
404 }
405
406 int
update_lut(long index)407 view_manager::update_lut (long index)
408 {
409 return update_lut (m_set.get_marquee (index));
410 }
411
412 int
update_lut(marquee & m)413 view_manager::update_lut (marquee& m)
414 {
415 iscan::build_LUT (&m_set, &m, m_scanmanager_cls->is_dumb ());
416
417 return PISA_ERR_SUCCESS;
418 }
419
420 bool
change_document_source(char option,char film,const imagetype * img_type)421 view_manager::change_document_source (char option, char film,
422 const imagetype *img_type)
423 {
424 bool success = true;
425
426 if (option == m_set.option && film == m_set.film)
427 return success; // nothing to change
428
429 try
430 {
431 memcpy (&m_set.imgtype, img_type, sizeof (imagetype));
432 m_set.delete_all (); // init_img_info will call init()
433 init_img_info (static_cast <pisa_option_type> (option),
434 static_cast <pisa_film_type> (film));
435
436 m_set.option = option;
437 m_set.film = film;
438
439 main_window *main_cls
440 = static_cast <main_window *> (get_window_cls (ID_WINDOW_MAIN));
441 main_cls->enable_start_button ( PISA_OP_ADF != option
442 && PISA_OP_ADFDPLX != option);
443 }
444 catch (pisa_error& err)
445 {
446 aleart_dialog aleart_dlg;
447
448 aleart_dlg.message_box (m_main_cls->get_widget (),
449 err.get_error_string ());
450 success = false;
451 }
452
453 set_resolution (DEFAULT_RESOLUTION);
454 m_gamma_cls->reset (1);
455 m_prev_cls->resize_window ();
456 sensitive ();
457
458 return success;
459 }
460
set_device(char * name)461 void view_manager::set_device( char * name )
462 {
463 m_scanmanager_cls->close_device();
464 m_scanmanager_cls->open_device( name );
465 }
466
get_device_name() const467 char * view_manager::get_device_name() const
468 {
469 return m_scansel_cls->get_device( true );
470 }
471
472 /*------------------------------------------------------------*/
open_device(void)473 void view_manager::open_device ( void )
474 {
475 sane_init( 0, 0 );
476 if (!m_scansel_cls)
477 m_scansel_cls = new scan_selector( true ); // dialog box
478 m_scanmanager_cls->open_device ( m_scansel_cls->get_device() );
479 }
480
481 /*------------------------------------------------------------*/
close_device(void)482 void view_manager::close_device ( void )
483 {
484 m_scanmanager_cls->close_device ( );
485 }
486
487 /*------------------------------------------------------------*/
load_preference(void)488 void view_manager::load_preference ( void )
489 {
490 char pref_path [ 256 ];
491 char pips_path [ 1024 ] = "lpr";
492
493 cfg_struct cfg [ ] =
494 {
495 { "PIPS", CFG_STRING, pips_path }
496 };
497
498 ::strcpy ( pref_path, ::getenv ( "HOME" ) );
499 ::strcat ( pref_path, "/" );
500 ::strcat ( pref_path, PREFERENCE );
501
502 ::get_cfg ( pref_path, cfg, sizeof ( cfg ) / sizeof ( cfg [ 0 ] ) );
503
504 ::strcpy ( m_config_cls->m_cmd, pips_path );
505 }
506
507 /*------------------------------------------------------------*/
save_preference(void)508 void view_manager::save_preference ( void )
509 {
510 char pref_path [ 256 ];
511 char pips_path [ 1024 ] = "";
512
513 cfg_struct cfg [ ] =
514 {
515 { "PIPS", CFG_STRING, pips_path }
516 };
517
518 ::strcpy ( pref_path, ::getenv ( "HOME" ) );
519 ::strcat ( pref_path, "/" );
520 ::strcat ( pref_path, PREFERENCE );
521
522 ::strcpy ( pips_path, m_config_cls->m_cmd );
523
524 ::set_cfg ( pref_path, cfg, sizeof ( cfg ) / sizeof ( cfg [ 0 ] ) );
525 }
526
527 /*------------------------------------------------------------*/
init_img_info(pisa_option_type option,pisa_film_type film)528 int view_manager::init_img_info ( pisa_option_type option,
529 pisa_film_type film )
530 {
531 int i, j;
532 marquee * marq;
533 double max_width, max_height;
534 double coef [ 9 ];
535
536 max_width = 8.5;
537 max_height = 11.7;
538
539 // initialize marquee
540 marq = new marquee;
541 m_set.init ( & marq );
542
543 marq->offset.x = 0.0;
544 marq->offset.y = 0.0;
545 marq->area.x = max_width;
546 marq->area.y = max_height;
547
548 marq->scale = 100;
549
550 marq->gamma = ( long ) ( 100 * DEFGAMMA );
551 marq->highlight = DEFHIGHLIGHT;
552 marq->shadow = DEFSHADOW;
553 marq->threshold = DEFTHRESHOLD;
554
555 for ( i = 0; i < 4; i++ )
556 for ( j = 0; j < 256; j++ )
557 marq->gamma_table [ i ] [ j ] = j;
558
559 marq->graybalance = DEFGRAYBALANCE;
560 marq->saturation = DEFSATURATION;
561
562 if ( option == PISA_OP_TPU )
563 marq->focus = 25;
564 else
565 marq->focus = 0;
566
567 for ( i = 0; i < 3; i++ )
568 {
569 marq->film_gamma [ i ] = 1.0;
570 marq->film_yp [ i ] = 0.0;
571 marq->grayl [ i ] = 0.0;
572 }
573
574 for ( i = 0; i < 256; i++ )
575 {
576 marq->lut.gamma_r [ i ] = i;
577 marq->lut.gamma_g [ i ] = i;
578 marq->lut.gamma_b [ i ] = i;
579 }
580
581 // get max area
582 m_scanmanager_cls->set_option ( option );
583 m_scanmanager_cls->set_film_type ( film );
584 m_scanmanager_cls->get_current_max_size ( & max_width, & max_height );
585 m_scanmanager_cls->get_color_profile ( coef );
586
587 m_set.max_area [ 0 ] = max_width;
588 m_set.max_area [ 1 ] = max_height;
589
590 // update marquee to reflect new scanner status
591 marq->area.x = max_width;
592 marq->area.y = max_height;
593
594 for ( i = 0; i < 9; i++ )
595 m_set.coef [ i ] = coef [ i ];
596
597 return PISA_ERR_SUCCESS;
598 }
599
600 int
init_scan_param(void)601 view_manager::init_scan_param (void)
602 {
603 marquee marq = get_marquee (m_set.get_marquee_size ( ) - 1);
604
605 pisa_error_id err = m_scanmanager_cls->set_scan_parameters (m_set, marq);
606
607 if (PISA_ERR_SUCCESS != err)
608 {
609 aleart_dialog dlg;
610
611 dlg.message_box (m_main_cls->get_widget (),
612 pisa_error (err).get_error_string ());
613 }
614 return err;
615 }
616
617 int
do_scan_file(file_selector * fs_cls,bool first_time_around)618 view_manager::do_scan_file (file_selector *fs_cls,
619 bool first_time_around)
620 {
621 int cancel;
622
623 if (!fs_cls)
624 {
625 fs_cls = (file_selector *) m_filsel_cls;
626
627 fs_cls->init();
628 #ifndef HAVE_GTK_2
629 fs_cls->create_window (m_main_cls->get_widget ( ), m_set.option,
630 m_set.enable_start_button,
631 PISA_PT_BW == m_set.imgtype.pixeltype);
632 #else
633 GtkWidget *w = m_main_cls->get_widget ();
634 fs_cls->create_window (GTK_WINDOW (gtk_widget_get_parent (w)), w,
635 ( PISA_OP_ADF == m_set.option
636 || PISA_OP_ADFDPLX == m_set.option
637 || m_set.enable_start_button));
638 #endif /* HAVE_GTK_2 */
639 fs_cls->hide();
640 }
641
642 char *filename = fs_cls->get_filename();
643
644 if ( PISA_OP_ADF != m_set.option
645 && PISA_OP_ADFDPLX != m_set.option
646 && !m_set.enable_start_button)
647 {
648 fs_cls->destroy();
649
650 if (!filename)
651 return PISA_ERR_SUCCESS;
652
653 scan_file (filename, & cancel);
654 }
655 else // ADF scanning
656 if (filename)
657 {
658 char *dash = strrchr( filename, '-' );
659 char *dot = strrchr( filename, '.' );
660 size_t len = strlen( filename );
661 int digits = dot - dash - 1;
662
663 assert( digits < 10 ); // assumed for string lengths!
664 char *format = new char[len + dash - dot + 5];
665 char *regexp = new char[len + dash - dot + 29];
666 char *f_name = new char[len + dash - dot + 11];
667
668 ++*dash = '\0'; // cut filename
669 sprintf( format, "%s-%%%dd%s" , filename, digits, dot );
670 sprintf( regexp, "%s-([1-9][0-9]{%d,}|[0-9]{%d})\\%s$",
671 filename, digits, digits, dot );
672
673 cancel = check_overwrite( regexp );
674 delete[] regexp;
675
676 bool eos = (0 != cancel); // scan if NOT cancelled
677 int cnt = fs_cls->get_sequence_number();
678
679 while (!eos)
680 {
681 sprintf( f_name, format, cnt );
682 char *p = strrchr( f_name, '-' );
683 while (' ' == *(++p))
684 *p = '0'; // zero out any spaces
685
686 // FIXME: update fs_cls' filename?
687 eos = scan_file (f_name, & cancel, first_time_around);
688 first_time_around = false;
689 if (!eos)
690 {
691 fs_cls->set_sequence_number( ++cnt );
692 }
693 }
694 delete[] format;
695 delete[] f_name;
696 if (!cancel) {
697 do_scan_file (fs_cls, first_time_around);
698 }
699 fs_cls->destroy();
700 }
701 else
702 fs_cls->destroy();
703
704 free( filename );
705
706 return PISA_ERR_SUCCESS;
707 }
708
709 //! Sends scan results to a printer.
710 /*!
711 */
712 int
do_scan_printer(int fd,bool first_time_around)713 view_manager::do_scan_printer (int fd, bool first_time_around)
714 {
715 int cancel;
716 char cmd[256]; // FIXME: buffer overflow!
717
718 if ( PISA_OP_ADF != m_set.option
719 && PISA_OP_ADFDPLX != m_set.option
720 && !m_set.enable_start_button)
721 {
722 char *filename = NULL;
723 int status = get_temp_filename (&filename);
724
725 if (PISA_ERR_SUCCESS != status)
726 {
727 free (filename);
728 return PISA_ERR_SUCCESS; // FIXME: should return status;
729 }
730
731 scan_file (filename, &cancel);
732
733 if (!cancel)
734 {
735 while (gtk_events_pending ())
736 gtk_main_iteration ();
737
738 sprintf (cmd, "%s %s", m_config_cls->m_cmd, filename);
739 system (cmd);
740 }
741 remove (filename);
742 free (filename);
743 }
744 else // ADF scanning
745 {
746 bool eos = false;
747 while (!eos)
748 {
749 char *filename = NULL;
750 int status = get_temp_filename (&filename);
751
752 if (PISA_ERR_SUCCESS != status)
753 {
754 free (filename);
755 return PISA_ERR_SUCCESS; // FIXME: should return status;
756 }
757
758 eos = scan_file (filename, &cancel, first_time_around);
759 first_time_around = false;
760
761 if (!cancel && !eos)
762 {
763 while (gtk_events_pending ())
764 gtk_main_iteration ();
765
766 sprintf (cmd, "%s %s", m_config_cls->m_cmd, filename);
767 system (cmd);
768 }
769 remove (filename);
770 free (filename);
771 }
772 if (!cancel)
773 do_scan_printer (fd, first_time_around);
774 }
775
776 return PISA_ERR_SUCCESS;
777 }
778
779 int
do_scan_gimp(bool first_time_around)780 view_manager::do_scan_gimp (bool first_time_around)
781 {
782 int cancel = 0;
783
784 if ( PISA_OP_ADF != m_set.option
785 && PISA_OP_ADFDPLX != m_set.option
786 && !m_set.enable_start_button)
787 return scan_gimp (&cancel);
788
789 bool eos = false;
790
791 while (!eos)
792 {
793 eos = scan_gimp (&cancel, first_time_around);
794 first_time_around = false;
795 }
796 if (!cancel)
797 do_scan_gimp (first_time_around);
798
799 return PISA_ERR_SUCCESS;
800 }
801
802 int
dialog_reply(const pisa_error & err) const803 view_manager::dialog_reply( const pisa_error& err ) const
804 {
805 int reply = 0;
806
807 aleart_dialog dlg;
808
809 if ( ( PISA_STATUS_GOOD < err.get_error_id() ) &&
810 ( PISA_OP_ADF == m_set.option ||
811 PISA_OP_ADFDPLX == m_set.option ) )
812 {
813 int i = dlg.message_box( m_main_cls->get_widget(),
814 err.get_error_string(),
815 _(" Continue "), _(" Cancel ") );
816 if (2 == i)
817 reply = 1;
818 }
819 else if ( ( PISA_STATUS_GOOD == err.get_error_id() ) &&
820 m_set.enable_start_button )
821 {
822
823 int i = dlg.message_box( m_main_cls->get_widget(),
824 "Waiting for ...",
825 _(" Finish ") );
826
827 if ( 1 == i )
828 reply = 1;
829 }
830 else
831 {
832 dlg.message_box( m_main_cls->get_widget(),
833 err.get_error_string() );
834
835 if (PISA_ERR_FILEOPEN != err.get_error_id())
836 reply = 1;
837 }
838
839 return reply;
840 }
841
842 /*------------------------------------------------------------*/
843 bool
scan_gimp(int * cancel,bool first_time_around)844 view_manager::scan_gimp (int *cancel, bool first_time_around)
845 {
846
847 #ifndef HAVE_ANY_GIMP
848
849 *cancel = 1; // can't scan to GIMP
850 return true;
851
852 #else
853 bool error = false; // be optimistic
854 try
855 {
856 *cancel = 0;
857 bool wait_for_button = (m_set.enable_start_button
858 && !( PISA_OP_ADF == m_set.option
859 || PISA_OP_ADFDPLX == m_set.option));
860
861 bool reset_params = false;
862 if (wait_for_button
863 && m_scanmanager_cls->is_button_pressed ()) {
864 // We have an impatient user here who pressed the scanner's
865 // button *before* we even got a chance to show our WAITING
866 // message.
867 // We ensure the user gets to see this message be (re)setting
868 // the document source (which indirectly resets the scanner's
869 // push button status) for lack of a more elegant way and raise
870 // a flag so that any parameters that may have been erased as
871 // a result are reset before we request scan data.
872 m_scanmanager_cls
873 ->set_option (static_cast <pisa_option_type> (m_set.option));
874 reset_params = true;
875 }
876
877 _feedback->set_text (wait_for_button
878 ? progress_window::WAITING
879 : progress_window::WARMING_UP);
880 _feedback->set_progress (0, 1);
881 _feedback->show ();
882
883 if (wait_for_button) {
884 while (!m_scanmanager_cls->is_button_pressed ()
885 && !_feedback->is_cancelled ()) {
886 sleep (1);
887 while (gtk_events_pending ()) {
888 gtk_main_iteration ();
889 }
890 }
891 if (_feedback->is_cancelled ()) {
892 *cancel = 1;
893 return true;
894 }
895 }
896
897 _feedback->set_text (progress_window::WARMING_UP);
898
899 int width, height;
900 m_scanmanager_cls->init_scan (&width, &height,
901 first_time_around || reset_params);
902
903 while ( ::gtk_events_pending ( ) )
904 ::gtk_main_iteration ( );
905
906 char depth;
907 int rowbytes;
908 switch (m_set.imgtype.pixeltype)
909 {
910 case PISA_PT_RGB:
911 depth = 8;
912 rowbytes = width * 3;
913 break;
914 case PISA_PT_GRAY:
915 depth = 8;
916 rowbytes = width;
917 break;
918 case PISA_PT_BW:
919 depth = 1;
920 rowbytes = (width + 7) / 8;
921 break;
922 default:
923 rowbytes = 0;
924 }
925
926 gimp_scan gimp_cls;
927 if (PISA_ERR_SUCCESS !=
928 gimp_cls.create_gimp_image (width, height,
929 m_set.imgtype.pixeltype, depth))
930 {
931 m_scanmanager_cls->acquire_image ( 0, 0, 1, 1 );
932
933 throw pisa_error ( PISA_ERR_OUTOFMEMORY );
934 }
935
936 for (int i = 0; i < height; i++)
937 {
938 m_scanmanager_cls->acquire_image ( gimp_cls.get_next_buf ( ),
939 rowbytes,
940 1,
941 *cancel );
942
943 if ( i == 0 )
944 _feedback->set_text (progress_window::SCANNING);
945
946 if (*cancel)
947 {
948 error = true;
949 break;
950 }
951
952 _feedback->set_progress (i, height);
953 *cancel = _feedback->is_cancelled ();
954
955 gimp_cls.set_image_rect ( );
956
957 while ( ::gtk_events_pending ( ) )
958 ::gtk_main_iteration ( );
959 }
960 _feedback->set_progress (height, height);
961
962 gimp_cls.finish_scan ( *cancel );
963 }
964 catch ( pisa_error & err )
965 {
966 error = true;
967 *cancel = dialog_reply( err );
968 }
969
970 m_scanmanager_cls->finish_acquire ( );
971
972 return error;
973 #endif // HAVE_ANY_GIMP
974 }
975
976 bool
scan_file(const char * filename,int * cancel,bool first_time_around)977 view_manager::scan_file (const char *filename, int *cancel,
978 bool first_time_around)
979 {
980 bool error = false; // be optimistic
981 try
982 {
983 *cancel = 0;
984 bool wait_for_button = (m_set.enable_start_button
985 && !( PISA_OP_ADF == m_set.option
986 || PISA_OP_ADFDPLX == m_set.option));
987
988 bool reset_params = false;
989 if (wait_for_button
990 && m_scanmanager_cls->is_button_pressed ()) {
991 // We have an impatient user here who pressed the scanner's
992 // button *before* we even got a chance to show our WAITING
993 // message.
994 // We ensure the user gets to see this message be (re)setting
995 // the document source (which indirectly resets the scanner's
996 // push button status) for lack of a more elegant way and raise
997 // a flag so that any parameters that may have been erased as
998 // a result are reset before we request scan data.
999 m_scanmanager_cls
1000 ->set_option (static_cast <pisa_option_type> (m_set.option));
1001 reset_params = true;
1002 }
1003
1004 _feedback->set_text (wait_for_button
1005 ? progress_window::WAITING
1006 : progress_window::WARMING_UP);
1007 _feedback->set_progress (0, 1);
1008 _feedback->show ();
1009
1010 while (::gtk_events_pending())
1011 ::gtk_main_iteration();
1012
1013 if (wait_for_button) {
1014 while (!m_scanmanager_cls->is_button_pressed ()
1015 && !_feedback->is_cancelled ()) {
1016 sleep (1);
1017 while (gtk_events_pending ()) {
1018 gtk_main_iteration ();
1019 }
1020 }
1021 if (_feedback->is_cancelled ()) {
1022 *cancel = 1;
1023 return true;
1024 }
1025 }
1026
1027 _feedback->set_text (progress_window::WARMING_UP);
1028
1029 int width, height;
1030 m_scanmanager_cls->init_scan (&width, &height,
1031 first_time_around || reset_params);
1032
1033 while (::gtk_events_pending())
1034 ::gtk_main_iteration();
1035
1036 int rowbytes;
1037 iscan::colour_space cs;
1038
1039 switch (m_set.imgtype.pixeltype)
1040 {
1041 case PISA_PT_RGB:
1042 rowbytes = width * 3;
1043 cs = iscan::RGB;
1044 break;
1045 case PISA_PT_GRAY:
1046 rowbytes = width;
1047 cs = iscan::gray;
1048 break;
1049 case PISA_PT_BW:
1050 rowbytes = (width + 7) / 8;
1051 cs = iscan::mono;
1052 break;
1053 default:
1054 rowbytes = 0;
1055 throw pisa_error (PISA_ERR_PARAMETER);
1056 }
1057
1058 iscan::imgstream *save_cls = NULL;
1059 iscan::imgstream::filebuf fb (fopen (filename, "wb"));
1060
1061 switch (get_file_type( filename )) // FIXME: use factory method
1062 {
1063 case PISA_FI_PNM:
1064 save_cls = new iscan::pnmstream (&fb);
1065 break;
1066 case PISA_FI_PNG:
1067 save_cls = new iscan::pngstream (&fb);
1068 break;
1069 case PISA_FI_JPG:
1070 save_cls = new iscan::jpegstream (&fb);
1071 break;
1072 default:
1073 throw pisa_error( PISA_ERR_FILEOPEN );
1074 }
1075
1076 try
1077 {
1078 try {
1079 save_cls->size (width, height);
1080 save_cls->depth (PISA_PT_BW == m_set.imgtype.pixeltype
1081 ? 1 : 8);
1082 save_cls->colour (cs);
1083 save_cls->resolution (m_set.resolution, m_set.resolution);
1084 } catch (std::ios_base::failure& oops) {
1085 // map to old API and rethrow
1086 throw (save_cls->rdbuf ()
1087 ? pisa_error (PISA_ERR_OUTOFMEMORY)
1088 : pisa_error (PISA_ERR_FILEOPEN));
1089 }
1090 }
1091 catch (pisa_error& oops)
1092 {
1093 m_scanmanager_cls->acquire_image( 0, 0, 1, 1 );
1094 ::remove( filename );
1095 delete save_cls;
1096 throw oops;
1097 }
1098
1099 unsigned char *img = new unsigned char[rowbytes];
1100 for (int i = 0; i < height; ++i)
1101 {
1102 m_scanmanager_cls->acquire_image( img, rowbytes, 1, *cancel );
1103
1104 if (0 == i)
1105 _feedback->set_text (progress_window::SCANNING);
1106
1107 if (*cancel)
1108 {
1109 ::remove( filename );
1110 error = true;
1111 break;
1112 }
1113
1114 _feedback->set_progress (i, height);
1115 *cancel = _feedback->is_cancelled ();
1116
1117 try
1118 {
1119 try {
1120 save_cls->write ((const char *)img, rowbytes);
1121 } catch (std::ios_base::failure& oops) {
1122 // map to old API and rethrow
1123 throw (save_cls->rdbuf ()
1124 ? pisa_error (PISA_ERR_OUTOFMEMORY)
1125 : pisa_error (PISA_ERR_FILEOPEN));
1126 }
1127 }
1128 catch (pisa_error& oops)
1129 {
1130 *cancel = 1;
1131
1132 if (i < height)
1133 m_scanmanager_cls->acquire_image( img, rowbytes, 1,
1134 *cancel );
1135 aleart_dialog aleart_dlg;
1136 aleart_dlg.message_box( m_main_cls->get_widget(),
1137 oops.get_error_string() );
1138
1139 ::remove( filename );
1140 break;
1141 }
1142 while (::gtk_events_pending())
1143 ::gtk_main_iteration();
1144 }
1145 delete[] img;
1146 delete save_cls;
1147
1148 _feedback->set_progress (height, height);
1149 }
1150 catch (pisa_error& oops)
1151 {
1152 error = true;
1153 *cancel = dialog_reply( oops );
1154 ::remove (filename);
1155 }
1156
1157 m_scanmanager_cls->finish_acquire();
1158
1159 while (::gtk_events_pending())
1160 ::gtk_main_iteration();
1161
1162 return error;
1163 }
1164
1165 //! Indicates the expected output file format.
1166 /*! File format is normally indicated by the file extension, but for
1167 scanning to printer we use temporary files that can not fit that
1168 rule.
1169 */
1170 pisa_file_type
get_file_type(const char * filename)1171 view_manager::get_file_type (const char *filename)
1172 {
1173 char *dot = strrchr (filename, '.');
1174 if (!dot)
1175 {
1176 char *slash = strrchr (filename, '/');
1177 if ( (strlen (slash) == strlen ("/" PACKAGE_TARNAME "XXXXXX"))
1178 && (0 == strncmp (slash, "/" PACKAGE_TARNAME,
1179 strlen ("/" PACKAGE_TARNAME))))
1180 return PISA_FI_PNG; // temporary file for output to printer
1181
1182 return PISA_FI_UNSUPPORTED;
1183 }
1184
1185 if (0 == strcmp (dot, ".pnm"))
1186 return PISA_FI_PNM;
1187 if (0 == strcmp (dot, ".png"))
1188 return PISA_FI_PNG;
1189 if ((0 == strcmp (dot, ".jpg")) || (0 == strcmp (dot, ".jpeg")))
1190 return PISA_FI_JPG;
1191
1192 return PISA_FI_UNSUPPORTED;
1193 }
1194
1195 //! Creates a unique temporary filename and opens that file.
1196 /*! The unique file is created and opened in the directory indicated
1197 by the TMPDIR environment variable, whatever your system's stdio.h
1198 file has defined for \c P_tmpdir or the \c /tmp directory. The
1199 first directory that is writable will be used.
1200
1201 Caller will need to free the memory pointed to by *filename.
1202 */
1203 int
get_temp_filename(char ** filename)1204 view_manager::get_temp_filename (char **filename)
1205 {
1206 const char *tmpnam = "/" PACKAGE_TARNAME "XXXXXX";
1207 const char *tmpdir;
1208 {
1209 const char *dir_array[] = {
1210 getenv ("TMPDIR"),
1211 #ifdef P_tmpdir
1212 P_tmpdir,
1213 #endif
1214 "/tmp",
1215 NULL
1216 };
1217 struct stat buf;
1218
1219 unsigned int i = 0;
1220 tmpdir = dir_array[i];
1221 while (i < sizeof (dir_array) / sizeof (char *)
1222 && (!tmpdir
1223 || (0 != access (tmpdir, W_OK | X_OK))
1224 || (0 != stat (tmpdir, &buf))
1225 || !S_ISDIR(buf.st_mode)))
1226 {
1227 tmpdir = dir_array[++i];
1228 }
1229 if (!tmpdir)
1230 return PISA_ERR_FILEOPEN;
1231 }
1232
1233 *filename = (char *) malloc ((strlen (tmpdir) + strlen (tmpnam) + 1)
1234 * sizeof (char));
1235 if (!*filename)
1236 return PISA_STATUS_NO_MEM;
1237
1238 memset (*filename, 0, (strlen (tmpdir) + strlen (tmpnam) + 1));
1239 strcpy (*filename, tmpdir);
1240 strcpy (*filename + strlen (tmpdir), tmpnam);
1241
1242 mode_t previous_mask = umask (0077);
1243
1244 if (0 > mkstemp (*filename))
1245 {
1246 umask (previous_mask);
1247 free (*filename);
1248 *filename = NULL;
1249 return PISA_ERR_FILEOPEN;
1250 }
1251
1252 umask (previous_mask);
1253
1254 return PISA_ERR_SUCCESS;
1255 }
1256
1257 // FIXME: file_selector can and should be responsible for this!
1258 int
check_overwrite(const char * regexp)1259 view_manager::check_overwrite( const char *regexp )
1260 {
1261 int cancel = 0; // default: don't cancel
1262
1263 char *slash = strrchr( regexp, '/' );
1264
1265 if (!slash)
1266 return cancel = 1;
1267
1268 *slash = '\0'; // regexp now holds the directory name
1269 char dirname[ strlen( regexp )];
1270 strcpy( dirname, regexp );
1271
1272 *slash = '^'; // re-anchor the regexp
1273
1274 regex_t *comp_regex = new regex_t;
1275 int comp = regcomp( comp_regex, slash, REG_EXTENDED );
1276
1277 if (0 == comp)
1278 {
1279 size_t nsub = comp_regex->re_nsub + 1;
1280 regmatch_t match[nsub];
1281
1282 file_selector *fs = (file_selector *) m_filsel_cls;
1283
1284 DIR *dir = opendir( dirname );
1285 if (!dir)
1286 return 0; // file creation failure handles this
1287
1288 struct dirent *file = 0;
1289 bool overwrite = false; // be conservative ;-)
1290 while (0 == cancel && !overwrite && (file = readdir( dir )))
1291 {
1292 int result = regexec( comp_regex, file->d_name, nsub, match, 0 );
1293 if (0 == result)
1294 {
1295 size_t digits = match[1].rm_eo - match[1].rm_so;
1296 char num[digits + 1];
1297 char *c = num;
1298 {
1299 char *p = file->d_name + match[1].rm_so;
1300 while (0 < digits--)
1301 *c++ = *p++;
1302 }
1303 int seq_num = atoi( num );
1304
1305 if (seq_num >= fs->get_sequence_number())
1306 {
1307 aleart_dialog dlg;
1308 int answer = dlg.message_box(m_main_cls->get_widget ( ),
1309 _("Overwrite?"),
1310 _(" Yes "), _(" No ") );
1311
1312 if (2 == answer)
1313 cancel = 1;
1314 if (1 == answer)
1315 overwrite = true;
1316 }
1317 }
1318 else
1319 if (REG_NOMATCH != result)
1320 regerror( comp, comp_regex );
1321 }
1322 closedir( dir );
1323 }
1324 else
1325 regerror( comp, comp_regex );
1326
1327 regfree( comp_regex );
1328 delete comp_regex;
1329
1330 return cancel;
1331 }
1332
1333 void
regerror(int code,regex_t * regex)1334 view_manager::regerror( int code, regex_t *regex )
1335 {
1336 size_t length = ::regerror( code, regex, 0, 0 );
1337 char *message = new char[length];
1338
1339 ::regerror( code, regex, message, length );
1340 fprintf( stderr, "%s\n", message );
1341
1342 delete[] message;
1343 }
1344