#!/bin/sh
# the next line restarts using wish \
exec wish "$0" "$@"

# TkScan Version and Date
set c(version)     "0.8"
set c(date)        "May 1997"

#----------------------------------------------------------------
# About TkScan
#----------------------------------------------------------------
set about_tkscan "
TkScan
Version $c(version), $c(date)\n
A tcl/tk GUI for command-line scanner driver
Written by Hang-Bae Kim (hbkim@delta.ft.uam.es)
Copying policy : GNU Public License
"

#----------------------------------------------------------------
# README
#----------------------------------------------------------------
set readme_tkscan "
   TkScan version $c(version)
   A tcl/tk GUI for command-line scanner driver
   Copyright (C) 1997 Hang Bae Kim (hbkim@delta.ft.uam.es)

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   See the GNU General Public License for more details.

 TkScan is a tcl/tk GUI for command-line scanner driver.
 I have written it for my own use with my Mustek 800IISP scanner.
 But I put it in the public with the hope that it may be helpful to someone.
 Remember that scanning itself is done by the scanner driver not by this
 GUI.  Therefore you NEED the appropriate command-line scanner driver for
 your scanner. TkScan just does the scanning area selection on a previewed
 image, the choice of options, e.g. scanning mode, resolution and some
 enhancements like gamma correction, running scanner driver and sending the
 output where you want.  You can fit it to your scanner and driver without
 much difficulty, by modifying `scanner_driver'.tcl referring your driver
 manual.

 Currently it supports
    * Epson SCSI scanner via `ep_scan'
    * HP SCSI scanner via `hpscanpbm'
    * Microtek SCSI scanner via `mtekscan'
    * Mustek Flatbed scanner via `scanimage'(SANE) or `mscan'

    - I wrote `epson-ep_scan.tcl' `hp-hpscanpbm.tcl' `microtek-mtekscan.tcl'
      just referring manuals, without testing.  They can be considered as
      scratch.  So they may not work, or even may include simple typos.
      I hope you can cope with this situation. If you write a correctly
      working script, please send a copy to me.
    - I give thanks to Boris Tobotras(tobotras@jet.msk.su) for sending
      the patch for hpscanpbm.

 With TkScan you can
    * preview an image to be scanned on a small canvas whose size and
      mode you can select
    * adjust scanning mode and resolutions and other options
      supported by the scanner and driver with mouse clicks
    * select the one or more scanning areas on a previewed image canvas
      If you select several scanning areas, TkScan will scan them successively.
      TkScan shows the expected screen pixel sizes and raw data sizes of the
      images as well as its real size in several length units (mm,cm,pt,inch).
    * make the output raw data directly go where you want:
        - to image editor (XV, ImageMagic or GIMP),
        - to file in the format you want,
        - to printer or to ghostscript(ghostview or gv) in postscript format
          with the size and position adjustment,
        - to fax modem to be directly sent as a fax,
        - to the ocr software to be processed by ocr
          (_not_ implemented yet in this version).
    * see the progress of scanning both in graphic and numeric percentage
      and file size fraction.
    * interrupt scanning with the Stop button.
    * configure TkScan to fit it to your tastes.
    
 TkScan requires
    * Scanner and command-line driver listed above, or you can add your own.
    * Tcl/Tk, probably most versions will work.
    * ImageMagic, to convert the scanned image to various formats
    * XV, ImageMagic, or GIMP or all of them, for ImageEditor
    * Ghostscript and ghostview(or gv), to view the postscript output
    * Efax, to send the scanned image to a fax modem

    - I left the output to OCR blank, Because I could not find a good
      OCR software for Linux.  Does anyone know about it?
    - All the above binaries should lie in your PATH.

 How to use
    See Guide in the Help menu.

 Bug or Comment to
    Hang-Bae Kim, hbkim@delta.ft.uam.es
"

#----------------------------------------------------------------
# Usage of TkScan
#----------------------------------------------------------------
set usage_tkscan "
 How to use TkScan version $c(version)

 Contents

	0. Command line option
	1. Previewing
	2. Selection of regions to be scanned
	3. Selection of mode and resolution
	4. Selection of output destination
	5. Scanning
	6. Configuration


 Section 0. Command line option

   tkscan \[-geometry +x+y\] \[ImageEditor|File|Printer|Fax|OCR\]


 Section 1. Preview

 * Click \[Preview\] button to initiate preview scanning.
 * Preview scanning can be interrupted by clicking \[Stop\] button.
 * The preview mode and resolution can be chosen in Preference of File menu.


 Section 2. Selection of regions to be scanned

 * The selection of scanning regions on the preview image is done with
   left mouse button. Press left button at one corner of the rectangle you
   want to scan, move mouse while pressing it and release at the other corner.
 * You can finetune the region using finetuning buttons around blue rectangle.
 * The blue rectagle continuously shows a region to be scanned, while the
   scanning region window diplays coordinates of upper-left and lower-right
   coners, the width times height in the length unit chosen (Real Size) and
   in screen pixel (Screen Size), and the file size of the image in raw
   format (Data Size).  Screen Size and Data Size also vary as you change
   scanning mode and resolution.
 * The size of A4, Letter paper or others are preset in \[preset\] button.  You
   can add your own regions in configuration file (See Configuration section).
 * You can select several regions on the preview image canvas.
   Set one blue rectangle and click the middle mouse button inside it (or
   \[Regist\] button).  Blue rectangle becomes red and the number 1 appears
   at the center.  Then make another blue rectangle and press the middle
   button inside it.  Now the second red rectangle is registered.  Repeat
   this as many times as you want.  Click of the right mouse button inside
   the red rectangle will remove it.  Double click of the right button (or
   click of the \[Clear\] button) deletes all rectangles.  TkScan will scan
   red rectangles successively in order of their registration numbers.


 Section 3. Selection of mode, resolution and options

 This is transparent: Use mode \[Mode\] menubutton and \[Resolution\] scale.
 Screen Size and Data Size change accordingly as mode and resolution change.
 * If you want to adjust other options, press \[Open Manager\] button.  Then
   Option manager window appears and you can do it there.  Available options
   depend on the scanning mode.
 * Several frequently used mode are preset in Mode munu.  You can define your
   own quick_mode in configuration file (See Configuration section.)


 Section 4. Selection of output desination

 This is done by \[Output to\] menubutton.

 * Image Editor
   - You can select image editor among XV, ImageMagic and GIMP in Preference.
   - There is also \[SW\] checkbutton, which select that each scanned image
     appears in separate image editor or all images in one image editor
     when several red rectangles are scanned successively.

 * File
   If you select this, File Manager Window appears.
   - Select the output file format.
   - You can specify a filename for scanned image in \[Filename\] entry.
     If you use # in the filename, the last # is replaced with the retangle
     number(0 for blue rectangle, the number at the center for red rectangles).
     The default filename is scanout-#.surfix where appropriate surfix is
     attached for the file format. You can change it in the configuration file.

 * Printer
   If you select this, Print Manager Window appears.
   - Select a printer.  `gv' is for sending output to ghostview(or gv).
   - Use \[Zoom\] scale and \[Position\] menubutton to select the size and
     position of printed image. Zoom scale is percentage of Real Size.
   - \[Reprint\] button is for printing again the scanned image with changed
     zoom and position.

 * Fax
   If you select this, Fax Manager Window appears.
   - With this, each scanned page is converted tiffg3 format using efix and
     listed in Page List Box.
   - In Page List Box, you can
      + view each page converted to tiffg3 format by double click of
        left mouse button,
      + view whole pages by pressing \[View\] button,
      + rearrange the order of pages using \[Up\] and \[Down\] button,
      + remove a page from the list using \[Remove\] button,
      + \[Attach\] button does not work yet.
   - You should specify the fax number.  \[List\] calls Fax Number List Window,
     in which you can maintain frequently used fax numbers.

 * OCR
   - This is not implemented yet. I could not find a good OCR software running
     under Linux. Please let me know if anyone knows or heard of it.


 Section 5. Scanning

 This is done by pressing \[SCAN\] button. TkScan will scan the blue rectangle
 region. If you registered red rectangles, then red rectangle regions are
 scanned successively and the blue rectangle is ignored even when it exists.
 As scanning is in progress, the progress report appears in the status bar.
 If you want to interrupt in the middle of scanning, press \[Stop\] button.
 It kill driver command and recover the state before scanning.
 If it does not work for some reason, double click \[Stop\] button. This
 surely stops scanning and recover TkScan for another scanning.
 Note that while \[Stop\] button kills scanning command immediately,
 it takes some time for scanner to respond.  Scanner is a slowly responding
 peripharal.  I do not recommend frequent use of \[Stop\] button.


 Section 6. Configuration

 If you select Preference in File menu, Preference Window appears.
 * Specify your scanner device if your scanner driver does not find the
   scanner device correctly without device option. Otherwise, to leave it
   blank is OK.
 * You can change the preview image mode and resolution to those you prefer.
   Press \[Apply\] button after selection.
 * Gamma correction can be done either by the driver itself or by the
   external programs like xv or ImageMagic's display or convert if it is
   not available in the driver. Choose what you prefer.
 * TkScan makes ~/.tkscan directory to keep its configuration files and
   scanned image files temporarily. You can delete those temporary files in
   this directory when exit or right now to free your hard disk.
 * If you select Save current config in File menu, the various configuration
   settings are stored in the ~/.tkscan/init file.  You can modify this file
   to fit your taste.  If you feel like doing more changes, observe the
   `tkscan' script and copy necessary parts to ~/.tkscan/init file,
   then modify them.
"

#------------------------------------------------------------------------
# MicroTek SCSI scanner -- mtekscan
#------------------------------------------------------------------------

# Full scanning size. This should be in inches.
set s(scanwidth)	8.5
set s(scanheight)	11.6

# Minimum, maximum and step of scanning resolution in dpi
set s(resolutionRange)	{1 600 1}

# Time(in miliseconds) for coming back from the opposite end 
# after finishing full height scanning
set s(scantime)		20000

# Command-line driver
set s(command)	"mtekscan"

# Scanner device (leave empty for autodetection)
set s(device)		""

# Options other than for mode and resolution
#     Label         Type  Variable      Values or Range  Default value
set s(optionList) { 
    { {Backtrack}      m  backtrack     {yes no}  yes }
    { {Speed}          r  speed         {1 7 1}  1 }
    { {Brightness}     r  brightness    {-100 100 1}  0 }
    { {Exposure Time}  r  exposuretime  {-18 21 3}  0 }
    { {Contrast}       r  contrast      {-42 49 1}  0 }
    { {Halftoning}     r  halftoning    {0 11 1}  0 }
    { {Shadow}         r  shadow        {0 255 1}  0 }
    { {Midtone}        r  midtone       {0 255 1}  128 }
    { {Highlight}      r  highlight     {0 255 1}  255 }
    { {Gamma}          r  gamma         {0.2 5.0 0.1}  1.0 }
}
set s(modedepoptionList) { shadow midtone highlight halftoning gamma }

# Supported scanning modes
#     Mode   Format  bpp  Mode-dependent options
set s(modeList) {
    { Lineart   pbm   1  {} }
    { Halftone  pbm   1  {halftoning} }
    { Gray      pgm   8  {shadow midtone highlight gamma} }
    { Color     ppm  24  {shadow midtone highlight gamma} }
}

# Gamma correction support
# 0 : support by scanner driver
# 1 : support by external (image editor, image converter) commands
set s(gamma_support)	0

#------------------------------------------------------------------------
# Procedure launching scanner driver
#------------------------------------------------------------------------

proc run_scanner_driver { i file } { global s c
    set command $s(command)
    set file "-o $c(tkscandir)/$file.pnm"
    foreach j {tx ty bx by} { set $j [pixelto inch [expr $s($j$i)-$s(o)]] }
    set geometry "-f $tx $ty $bx $by"
    switch $s(mode) {
        Lineart		{ set mode "-b" }
        Halftone	{ set mode "-a" }
        Gray		{ set mode "-g" }
        Color		{ set mode "-c" }
    }
    set resolution "-r $s(resolution)"
    set option "-v $s(speed) -k $s(contrast) -d $s(brightness) \
                -e $s(exposuretime)"
    if {$s(backtrack) == "no"} {append option " -B"}
    foreach i $s($s(mode)option) { switch $i {
      halftoning  {append option " -H $s(halftoning)"}
      shadow      {append option " -s $s(shadow)"}
      midtone     {append option " -m $s(midtone)"}
      highlight   {append option " -l $s(highlight)"}
      gamma       { if {$s(gamma_support)==0} {append option " -G $s(gamma)"}}
    }}
    set cmdline "$command $s(device) $mode $resolution $option $geometry $file"
    exec sh -c "echo $cmdline" &
}

#----------------------------------------------------------------
# Miscellanous Configuration
#----------------------------------------------------------------

# Period in miliseconds for checking scanning progress
set s(loopperiod)	1000

# Preview and Scan mode
set c(prefile)			preview
set s(preresolutionRange)       {15 40 1}
set s(premode)                  Gray
set s(preresolution)            27
set s(Defaultmode)		Color
set s(Defaultresolution)	150

# Quick mode description: unspecified options are set to Default.
set c(mode_quick_set) {
    { Copy     { mode=Gray    resolution=150 } }
    { Fax      { mode=Lineart resolution=200 } } 
    { OCR      { mode=Lineart resolution=300 } }
    { Default  {} }
}

# Paper quick settings, length in inch.
set c(size_quick_set) {
    { Max     { 0.0  0.0    $s(scanwidth) $s(scanheight) } }
    { A4      { 0.0  0.0    8.268  11.6  } }
    { Letter  { 0.0  0.0    8.5    11.0  } }
    { Photo   { 0.0  0.0    5.906  3.937 } }
}

# Length units, their display formats and conversion factors
set s(unitList) {
    { mm    25.5  %0.0f }
    { cm    2.54  %0.1f }
    { inch   1.0  %0.2f }
    { pt    72.0  %0.0f }
}

# Define variables for later use
foreach i $s(optionList) {
    set variable [lindex $i 2]
    set default  [lindex $i 4]
    set s(Default$variable) $default
    set s(pre$variable) $default
}
foreach i $s(modeList) { set modename [lindex $i 0]
    set s([set modename]format) [lindex $i 1]
    set s([set modename]dsizefactor) [lindex $i 2]
    set s([set modename]option) [lindex $i 3]
}
foreach i $s(unitList) { set unitname [lindex $i 0]
    set s(c$unitname) [lindex $i 1]
    set s(f$unitname) [lindex $i 2]
}
foreach i $c(mode_quick_set) { set c(qmode[lindex $i 0]) [lindex $i 1] }
foreach i $c(size_quick_set) { set c(qpaper[lindex $i 0]) [lindex $i 1] }

# Adopted scanning mode options in this program
set s(settingList) "mode resolution"
foreach i $s(optionList) {lappend s(settingList) [lindex $i 2]}
foreach i $s(settingList) {set s($i) $s(Default$i)}

# Fonts
# entry font
set l(f1) -adobe-helvetica-bold-r-*--12-120-*-*-*-*-*-*
# fixed font
set l(f2) -bitstream-courier-bold-r-*--14-140-*-*-*-*-*-*

# Color
# entry color
set l(c1) blue
# primary rectangle
set l(c2) blue
# registed rectangle
set l(c3) red
# currently processed rectangle
set l(c5) blue
# scanning progress bar
set l(c6) blue

#----------------------------------------------------------------
# User Configuration
#----------------------------------------------------------------

# The directory where the configuration file and temporary files are kept
set c(tkscandir) $env(HOME)/.tkscan
if [file exists $c(tkscandir)] {
    if [file isdirectory $c(tkscandir)] {
    } else {
        puts "tkscan: ~/.tkscan exists but not a directory"
        exit
    }
} else {
    puts "tkscan: No $c(tkscandir) directory, create it."
    exec mkdir $c(tkscandir)
}
set c(scanout)		""
set c(userconf)		"$c(tkscandir)/init"
set c(tmpdirclear)	1

# The following entries are saved in the user configuration file.
set s(saveList) "device preresolutionRange premode preresolution pregamma unit"
foreach i $s(settingList) {append s(saveList) " Default$i"}
set c(saveList) "mode_quick_set size_quick_set tmpdirclear"

#----------------------------------------------------------------
# Main Window
#----------------------------------------------------------------

proc mainWindow {} { global s c w l

    # Menubar
    #
    frame .menu -bd 1 -relief flat
    pack .menu -side top -fill x
    mb .menu.f File .menu.f.m
    mb .menu.m Mode .menu.m.m
    mb .menu.h Help .menu.h.m
    label .menu.l -text "TkScan v$c(version)" -width 15 -pady 0 \
          -fg red -font -*-times-bold-r-*--14-140-*
    pack .menu.f .menu.m .menu.l -side left
    pack .menu.h -side right
    menu .menu.f.m -tearoff false
         .menu.f.m add command -label Preference -command preferenceWindow
         .menu.f.m add command -label "Set current as Default" \
                               -command "savecur Default"
         .menu.f.m add command -label "Save current config" \
                               -command "savesettings"
         .menu.f.m add separator
         .menu.f.m add command -label Quit -command do_exit
    menu .menu.m.m -tearoff false
    foreach i $c(mode_quick_set) {
        set qmodename [lindex $i 0]
        .menu.m.m add command -command "setquickmode $qmodename" \
                              -label $qmodename
    }
    menu .menu.h.m -tearoff false
         .menu.h.m add command -label About  -command aboutWindow
         .menu.h.m add command -label Readme -command readmeWindow
         .menu.h.m add command -label Guide  -command usageWindow

    # Separator - not indispensible, just for good looking.
    canvas .sep1  -relief sunken -height 0 -borderwidth 1
    pack .sep1 -side top -fill x

    # Left frame
    #
    frame .left -bd 5 -relief flat
    pack .left -side left -anchor nw -fill y

    # Preview image canvas
    set w(prev) .left.preview
    canvas $w(prev) -relief sunken
    pack $w(prev) -side top
    bind $w(prev) <ButtonPress-1>	"settop		%x %y"
    bind $w(prev) <B1-Motion>		"setbottom	%x %y"
    bind $w(prev) <ButtonPress-2>	"registrect	%x %y"
    bind $w(prev) <ButtonPress-3>	"deleterect	%x %y"
    bind $w(prev) <Double-Button-3>	"deleteallrect"

    # Right frame
    #
    frame .right -bd 5 -relief flat
    pack .right -side left -expand yes -fill both

    # OUTPUT
    set w(ou) .right.output
    set w(outputto) $w(ou).u.m
    frame $w(ou) -bd 1 -relief groove
    frame $w(ou).u
    pack $w(ou).u -side top -fill x
    rl $w(ou).u.l "Output to"
    menubutton $w(outputto) -menu $w(ou).u.m.m \
               -anchor w -width 18 -padx 6 -pady 2 -relief ridge -fg $l(c1)
    pack $w(ou).u.l $w(ou).u.m -side left -fill x
    menu $w(ou).u.m.m -tearoff false
    foreach i $c(outputtoList) { set label [lindex $i 0]
        $w(ou).u.m.m add command -label $label -command "setoutputto $label"
    }

    # Scanning mode selection
    set w(mo) .right.mode
    frame $w(mo) -bd 1 -relief groove
    # MODE
    frame $w(mo).m
    pack $w(mo).m -side top -anchor nw -fill x
    rl $w(mo).m.label "Mode"
    menubutton $w(mo).m.mode -menu $w(mo).m.mode.m \
               -anchor w -width 18 -padx 6 -pady 2 -relief ridge -fg $l(c1)
    pack $w(mo).m.label $w(mo).m.mode -side left
    menu $w(mo).m.mode.m -tearoff false
    foreach i $s(modeList) { set modename [lindex $i 0]
        $w(mo).m.mode.m add command -label $modename \
                                    -command "setmode $modename"
    }
    # RESOLUTION
    frame $w(mo).r
    pack $w(mo).r -side top -anchor nw
    rl $w(mo).r.label "Resolution"
    entry $w(mo).r.entry -textvariable s(resolution) \
          -justify right -relief sunken -width 4 -font $l(f1) -fg $l(c1)
    scale $w(mo).r.scale -variable s(resolution) -command setresolution \
          -from [lindex $s(resolutionRange) 0] \
          -to [lindex $s(resolutionRange) 1] \
          -resolution [lindex $s(resolutionRange) 2] \
          -length 100 -orient horizontal -showvalue false
    pack $w(mo).r.label $w(mo).r.entry $w(mo).r.scale -side left
    # OPTIONS
    frame $w(mo).e
    pack $w(mo).e -side top -anchor nw
    rl $w(mo).e.label "Options"
    button $w(mo).e.button -text "Open Manager >>" \
           -command optionWindow \
           -anchor e -width 19 -relief ridge -padx 0 -pady 0
    set w(optionbutton) $w(mo).e.button
    pack $w(mo).e.label $w(mo).e.button -side left

    # SCANNING GEOMETRY DISPLAY
    set w(ge) .right.geometry
    frame $w(ge) -bd 1 -relief groove
    canvas $w(ge).xy -width 210 -height 80 -bd 1 -relief flat
    pack $w(ge).xy -fill x
    set w(ft) $w(ge).xy
    set finetune_buttons {
        { tx- {  8 17 } {  8 33 } {  0 25 } }
        { tx+ { 13 17 } { 13 33 } { 21 25 } }
        { bx- { 38 17 } { 38 33 } { 30 25 } }
        { bx+ { 43 17 } { 43 33 } { 51 25 } }
        { ty- { 17  8 } { 33  8 } { 25  0 } }
        { ty+ { 17 13 } { 33 13 } { 25 21 } }
        { by- { 17 38 } { 33 38 } { 25 30 } }
        { by+ { 17 43 } { 33 43 } { 25 51 } }
    }
    set xs 86
    set ys 21
    $w(ft) create rectangle [expr 10+$xs] [expr 10+$ys] \
                            [expr 40+$xs] [expr 40+$ys] -outline blue
    foreach i $finetune_buttons {
        set btag [lindex $i 0]
        for {set j 1} {$j<=3} {incr j} {
            set x$j [expr [lindex [lindex $i $j] 0] +$xs]
            set y$j [expr [lindex [lindex $i $j] 1] +$ys]
        }
        $w(ft) create polygon $x1 $y1 $x2 $y2 $x3 $y3 -fill black -tag $btag
        $w(ft) bind $btag <Any-Enter> "$w(ft) itemconfig current -fill red"
        $w(ft) bind $btag <Any-Leave> "$w(ft) itemconfig current -fill black"
        $w(ft) bind $btag <ButtonPress> "set s(xx) 0; finetune $btag"
        $w(ft) bind $btag <ButtonRelease> "set s(xx) 1"
    }
    # Coordinates
    $w(ft) create text [expr 5+$xs] [expr 15+$ys] -tag ul -anchor se \
                       -fill $l(c1) -font $l(f1)
    $w(ft) create text [expr 45+$xs] [expr 35+$ys] -tag lr -anchor nw \
                       -fill $l(c1) -font $l(f1)
    # Label and Buttons
    rl $w(ft).l "Geometry"
    label  $w(ft).u -text "Length Unit"
    menubutton $w(ft).m -text $s(unit) -menu $w(ft).m.m \
                        -width 4 -pady 0 -relief ridge -fg $l(c1)
    menu $w(ft).m.m -tearoff false
    foreach i $s(unitList) { set label [lindex $i 0]
        $w(ft).m.m add command -label $label -command \
                 "setunit $label ; $w(ft).m configure -text $label"
    }
    menubutton $w(ft).p -text "Preset" -menu $w(ft).p.m \
                        -width 6 -pady 0 -relief ridge
    menu $w(ft).p.m -tearoff false
    foreach i $c(size_quick_set) {
        set qpapername [lindex $i 0]
        $w(ft).p.m add command -command "setquickpaper $qpapername" \
                              -label $qpapername
    }
    button $w(ft).r -text Regist -command {registrect $s(tx0) $s(ty0)} \
                    -padx 0 -pady 0 -relief ridge
    button $w(ft).d -text Delete -command {deleterect $s(tx0) $s(ty0)} \
                    -padx 0 -pady 0 -relief ridge
    button $w(ft).c -text Clear  -command deleteallrect \
                    -padx 0 -pady 0  -relief ridge
    $w(ft) create window   0   0 -window $w(ft).l -anchor nw
    $w(ft) create window 102   0 -window $w(ft).u -anchor nw
    $w(ft) create window 177   0 -window $w(ft).m -anchor nw
    $w(ft) create window 163  22 -window $w(ft).p -anchor nw
    $w(ft) create window   0  39 -window $w(ft).r -anchor nw
    $w(ft) create window   0  61 -window $w(ft).d -anchor nw
    $w(ft) create window  46  61 -window $w(ft).c -anchor nw

    # SIZE : Real, Screen, Data
    sizeline $w(ge).rs "Real Size" "mm^2"
    sizeline $w(ge).ss "Screen Size" "pixel^2"
    sizeline $w(ge).ds "Data Size" "KB"

    # BUTTONS
    set w(bt) .right.buttons
    frame $w(bt) -bd 1 -relief flat
    button $w(bt).p -text Preview -command "Preview" -width 5 -pady 4
    button $w(bt).s -text SCAN -command {Scan [timetagscanout]} \
                    -width 10 -pady 4
    button $w(bt).q -text Stop -command "set s(stop) 1" \
                    -width 3 -pady 4 -state disabled
    bind $w(bt).q <Double-Button-1> {
        set s(pid) [getpid $s(command)]
        if {"$s(pid)" != ""} { foreach i $s(pid) { exec kill -9 $i } }
        set s(stop) 0
        set s(runscan) 0
        $w(bt).p configure -state normal
        $w(bt).s configure -state normal
        $w(bt).q configure -state disabled
    }
    pack $w(bt).p $w(bt).s $w(bt).q -side left -expand yes -fill x

    # MESSAGE LINE
    # Scanning progress: percentage in  bar and number
    set w(st) .right.status
    set w(stat_text) $w(st).text
    set w(stat_prog) $w(st).prog
    set w(prog_bar) $w(stat_prog).bar
    set w(prog_perc) $w(stat_prog).percent
    set s(prog_bar_wd) 173
    frame $w(st) -bd 1 -relief groove
    pack $w(st) -side bottom -fill x
    label $w(stat_text) -anchor w -relief flat -font $l(f1)
    frame $w(stat_prog)
    pack $w(stat_text) $w(stat_prog) -side top -fill x
    canvas $w(prog_bar) -width $s(prog_bar_wd) -height 15 -bd 1 -relief ridge
    label $w(prog_perc) -width 5 -padx 1 -pady 0 -anchor e -bd 1 -relief ridge
    pack $w(prog_bar) $w(prog_perc) -side left
    set s(sg) [$w(prog_bar) create rectangle 0 0 0 18 -fill $l(c6)]

    pack $w(ou) $w(mo) $w(ge) $w(bt) -side top -expand yes -fill x
    pack $w(st) -side bottom -expand yes -fill x

}

#-----------------------------------------------------------------
# Preference Window
#-----------------------------------------------------------------

proc preferenceWindow {} { global s c w l
    if [winfo exists .preference] { return }
    toplevel .preference
    wm title .preference "TkScan: Preference"

    # Driver and Device
    set pd .preference.device
    frame $pd -bd 1 -relief groove
    frame $pd.u
    frame $pd.d
    pack $pd.u $pd.d -side top -fill x
    label $pd.u.l -text Command -width 15 -anchor e
    entry $pd.u.d -textvariable s(command) \
                     -width 20 -font $l(f1) -fg $l(c1) -relief sunken
    pack $pd.u.l $pd.u.d -side left
    label $pd.d.l -text Device -width 15 -anchor e
    entry $pd.d.d -textvariable s(device) \
                     -width 20 -font $l(f1) -fg $l(c1) -relief sunken
    pack $pd.d.l $pd.d.d -side left

    # Preview
    set pp .preference.preview
    frame $pp -bd 1 -relief groove
    frame $pp.m
    pack $pp.m -side top -fill x
    label $pp.m.p -text "Preview Mode" -width 15 -anchor e
    menubutton $pp.m.m -menu $pp.m.m.m -width 10 -relief ridge -fg $l(c1)
    set w(premodebutton) $pp.m.m
    pack $pp.m.p $pp.m.m -side left
    menu $pp.m.m.m -tearoff false
    foreach i $s(modeList) { set modename [lindex $i 0]
        $pp.m.m.m add command -label $modename \
                              -command "setpremode $modename"
    }
        $pp.m.m.m add command -label Current \
                              -command "setpremode Current"
    frame $pp.r
    pack $pp.r -side top -fill x
    label $pp.r.l -text "Resolution" -width 15 -padx 0 -anchor e
    set s(prepreresolution) $s(preresolution)
    entryscale $pp.r s(prepreresolution) {} $s(preresolutionRange) 60
    button $pp.r.b -text Apply -width 5 -padx 0 \
              -command {setpreresolution $s(prepreresolution)}
    pack $pp.r.l $pp.r.e $pp.r.s $pp.r.b -side left
    setpremode $s(premode)

    # Gamma support
    set pg .preference.gamma
    frame $pg -bd 1 -relief groove
    label $pg.l -text "Gamma Support" -width 15 -anchor e
    radiobutton $pg.1 -text Driver -variable s(gamma_support) -value 0
    radiobutton $pg.2 -text External -variable s(gamma_support) -value 1
    pack $pg.l $pg.1 $pg.2 -side left

    # Image Editor
    set ie .preference.ie
    frame $ie -bd 1 -relief groove
    label $ie.l -text "Image Editor" -width 15 -anchor e
    menubutton $ie.mb -menu $ie.mb.m -text $c(ImageEditor) \
                      -width 12 -relief ridge -fg $l(c1)
    checkbutton $ie.cb -text SW -variable c(ImageEditorWinmode)
    pack $ie.l $ie.mb $ie.cb -side left
    menu $ie.mb.m -tearoff false
    foreach i $c(ImageEditorList) { set iename [lindex $i 0]
        $ie.mb.m add command -label $iename -command "
            $ie.mb configure -text $iename
            set c(ImageEditor) $iename
        "
    }

    # Image and Fax viewer
    set pv .preference.viewer
    frame $pv -bd 1 -relief groove
    frame $pv.1
    frame $pv.2
    pack  $pv.1 $pv.2 -side top
    label $pv.1.l -text "Image Viewer" -width 15 -anchor e
    entry $pv.1.e -textvariable file(viewer) -width 20 -fg $l(c1) -font $l(f1)
    pack  $pv.1.l $pv.1.e -side left
    label $pv.2.l -text "Fax Viewer" -width 15 -anchor e
    entry $pv.2.e -textvariable fax(viewer)  -width 20 -fg $l(c1) -font $l(f1)
    pack  $pv.2.l $pv.2.e -side left

    # Printer paper
    set pa .preference.paper
    frame $pa -bd 1 -relief groove
    label $pa.l -text "Printer Paper" -width 15 -anchor e
    radiobutton $pa.1 -text A4 -variable pr(paper) -value A4
    radiobutton $pa.2 -text Letter -variable pr(paper) -value Letter
    pack $pa.l $pa.1 $pa.2 -side left

    # Temporary directory
    set pt .preference.tmpdir
    frame $pt -bd 1 -relief groove
    frame $pt.u
    frame $pt.d
    pack $pt.u $pt.d -side top -fill x
    label $pt.u.1 -text "TMP Directory" -width 15 -anchor e
    checkbutton $pt.u.2 -text "Clear when Exit" -variable c(tmpdirclear)
    pack $pt.u.1 $pt.u.2 -side left -fill x
    label $pt.d.1 -text "" -width 15 -anchor e
    button $pt.d.2 -text "Clear Now" -width 12 \
           -command clear_tkscandir
    pack $pt.d.1 $pt.d.2 -side left -fill x

    # Packing
    pack $pd $pp $pg $ie $pv $pa $pt -side top -fill x

    # Buttons
    set pb .preference.button
    frame $pb
    pack $pb -side bottom -expand yes -fill x
    button $pb.h -text Done -pady 0 -command { destroy .preference }
    pack $pb.h -side left -expand yes -fill x
}

#--------------------------------------------------------------------
# optionWindow
#--------------------------------------------------------------------

proc optionWindow {} { global s w l
    if [winfo exists .option] {
        $w(optionbutton) configure -text "Open Manager >>"
        destroy .option
        return
    }
    $w(optionbutton) configure -text "Close Manager <<"
    frame .option -bd 5 -relief flat
    pack  .option -side left -fill both

    label .option.ti -text "TkScan Option Manager" \
                     -width 24 -pady 4 -relief ridge
    canvas .option.sep -width 0 -height 5 -relief flat
    pack  .option.ti .option.sep -side top

    foreach i $s(optionList) {
        set Label       [lindex $i 0]
        set Type        [lindex $i 1]
        set Variable    [lindex $i 2]
        set Values      [lindex $i 3]
        set wn .option.$Variable
        frame $wn
        pack  $wn -side top -fill x
        label $wn.l -text $Label -width 12 -anchor e
        switch $Type {
            s {}
            m {
                menubutton $wn.mb -menu $wn.mb.m -text $s($Variable) \
                  -anchor w -width 13 -padx 10 -pady 2 -relief ridge -fg $l(c1)
                menu $wn.mb.m -tearoff false
                pack $wn.l $wn.mb -side left
                foreach i $Values {
                    $wn.mb.m add command -label $i -command \
                             "set s($Variable) $i ; $wn.mb configure -text $i"
                }
            }
            r {
                entryscale $wn s($Variable) {} $Values 72
                pack $wn.l $wn.e $wn.s -side left
            }
        }
    }
    option_select
}

#-------------------------------------------------------------------------
# Windows for Help menu
#-------------------------------------------------------------------------

proc aboutWindow {} { global about_tkscan
    if [winfo exists .about] return
    toplevel .about
    wm title .about "TkScan: About"
    label .about.l -text $about_tkscan
    button .about.b -width 8 -text OK -command "destroy .about"
    pack .about.l .about.b -side top
}

proc readmeWindow {} {global readme_tkscan l
    if [winfo exists .readme] return
    toplevel .readme
    wm title .readme "TkScan: README"
    frame .readme.b
    pack .readme.b -side top -fill x
    label .readme.b.1 -text "TkScan README" -width 15
    button .readme.b.2 -text Close -command {destroy .readme}
    pack .readme.b.1 .readme.b.2 -side left
    frame .readme.u
    pack .readme.u -side top -fill x
    text .readme.u.t -width 68 -height 20 -font $l(f1) \
                     -yscrollcommand ".readme.u.s set"
    pack .readme.u.t -side left
    scrollbar .readme.u.s -command ".readme.u.t yview" -orient vertical
    pack .readme.u.s -side right -fill y
    .readme.u.t insert end $readme_tkscan
}

proc usageWindow {} {global usage_tkscan l
    if [winfo exists .usage] return
    toplevel .usage
    wm title .usage "TkScan: Guide"
    frame .usage.b
    pack .usage.b -side top -fill x
    label .usage.b.1 -text "TkScan Guide" -width 15
    button .usage.b.2 -text Close -command {destroy .usage}
    pack .usage.b.1 .usage.b.2 -side left
    frame .usage.u
    pack .usage.u -side top -fill x
    text .usage.u.t -width 68 -height 20 -font $l(f1) \
         -yscrollcommand ".usage.u.s set"
    pack .usage.u.t -side left
    scrollbar .usage.u.s -command ".usage.u.t yview" -orient vertical
    pack .usage.u.s -side right -fill y
    .usage.u.t insert end $usage_tkscan
}

#----------------------------------------------------------------
# Procedures for configurations
#----------------------------------------------------------------

proc savesettings {} { global s c
    set file [open "$c(userconf)" w]
    puts $file "# tkscan user configuration
# If you feel like doing more changes, observe the `tkscan' script and
# copy necessary parts here, then change them.\n"
    foreach i $s(saveList) { puts $file "set s($i)\t\"$s($i)\"" }
    foreach i $c(saveList) { puts $file "set c($i)\t\"$c($i)\"" }
    puts $file "\n# end of tkscan user configuration"
    close $file
}

proc setcur { type } { global s w
    foreach i $s(settingList) {set s($i) $s($type$i)}
    setmode $s(mode)
}

proc savecur { type } { global s
    foreach i $s(settingList) {set s($type$i) $s($i)}
}

proc setmode { mode } { global s c w
    set s(mode) $mode
    if $s(premodecurrent) { set s(premode) $mode }
    $w(mo).m.mode configure -text $mode
    if [winfo exists .option] { option_select }
    showsize 0
}

proc option_select {} { global s
    foreach i $s(modedepoptionList) { .option.$i.l configure -fg gray }
    foreach i $s($s(mode)option) { .option.$i.l configure -fg black }
}

proc setquickmode { mode } { global s c
    setcur Default
    foreach k $c(qmode$mode) {
        set l [string first = $k]
        set arg [string range $k 0 [expr [string first = $k]-1]]
        set val [string range $k [expr [string first = $k]+1] end]
        set s($arg) $val
    }
#   if { [set i $c(qmode$mode\outputto)] != "" } { setoutputto $i }
    setmode $s(mode)
}

proc setquickpaper { paper } { global s c
    for {set j 0} {$j<4} {incr j} { set z$j [format "%0.0f" \
        [expr [lindex $c(qpaper$paper) $j]*$s(preresolution)+$s(o)]] }
    settop $z0 $z1 ; setbottom $z2 $z3
}

proc setpremode { mode } { global s w
    $w(premodebutton) configure -text $mode
    if { $mode == "Current" } {
        set s(premodecurrent) 1
        set s(premode) $s(mode)
    } else {
        set s(premodecurrent) 0
        set s(premode) $mode
    }
}

proc setpreresolution { resolution } { global s w
    set s(preresolution) $resolution
    previewcanvas
    deleteallrect
    image create photo preview -data {}
}

proc previewcanvas {} { global s w
    set s(o) 6
    set s(prebd) 2
    set s(scanpwidth) [format "%0.0f" [expr $s(scanwidth) *$s(preresolution)]]
    set s(scanpheight) [format "%0.0f" [expr $s(scanheight)*$s(preresolution)]]
    set s(maxpwidth) [expr $s(scanpwidth) +$s(o)]
    set s(maxpheight) [expr $s(scanpheight)+$s(o)]
    set s(prewidth) [expr $s(scanpwidth) +2*($s(o)-$s(prebd)-1)]
    set s(preheight) [expr $s(scanpheight)+2*($s(o)-$s(prebd)-1)]
    $w(prev) configure -width $s(prewidth) -height $s(preheight) -bd $s(prebd)
}

proc setresolution { resolution } { global s
    set s(resolution) $resolution
    showsize 0
}

proc setunit { unit } { global s w
    set s(unit) $unit
    $w(ge).rs.unit configure -text "$unit^2"
    showsize 0
}

proc showsize { i } { global s w
    set txd [format "$s(f$s(unit))" [pixelto $s(unit) [expr $s(tx$i)-$s(o)]]]
    set tyd [format "$s(f$s(unit))" [pixelto $s(unit) [expr $s(ty$i)-$s(o)]]]
    set bxd [format "$s(f$s(unit))" [pixelto $s(unit) [expr $s(bx$i)-$s(o)]]]
    set byd [format "$s(f$s(unit))" [pixelto $s(unit) [expr $s(by$i)-$s(o)]]]
    set wd [pixelto $s(unit) [expr $s(bx$i)-$s(tx$i)]]
    set ht [pixelto $s(unit) [expr $s(by$i)-$s(ty$i)]]
    set s(sizeReal) [format "$s(f$s(unit)) x $s(f$s(unit))" $wd $ht]
    set pwd [expr [pixelto inch [expr $s(bx$i)-$s(tx$i)]] * $s(resolution).0]
    set pht [expr [pixelto inch [expr $s(by$i)-$s(ty$i)]] * $s(resolution).0]
    set dsizefactor $s($s(mode)dsizefactor)
    set dsize [expr $pwd * $pht * $dsizefactor / 8192.0]
    set s(sizeScreen)	[format "%0.0f x %0.0f" $pwd $pht]
    set s(sizeData)	[format "%0.1f" $dsize]
    $w(ft) itemconfigure ul -text "($txd, $tyd)"
    $w(ft) itemconfigure lr -text "($bxd, $byd)"
    $w(ge).rs.size configure -text "$s(sizeReal)"
    $w(ge).ss.size configure -text "$s(sizeScreen)"
    $w(ge).ds.size configure -text "$s(sizeData)"
}

#----------------------------------------------------------------
# Procedures for Scanning Area Selection
#----------------------------------------------------------------

proc settop { xp yp } { global s w l
    if { $xp < $s(o) } { set xp $s(o) }
    if { $yp < $s(o) } { set yp $s(o) }
    if { $xp > $s(maxpwidth) } { set xp $s(maxpwidth) }
    if { $yp > $s(maxpheight) } { set yp $s(maxpheight) }
    set s(tx0) $xp
    set s(ty0) $yp
    set s(bx0) $s(tx0)
    set s(by0) $s(ty0)
    $w(prev) delete $s(rect0)
    set s(rect0) [$w(prev) create rectangle $s(tx0) $s(ty0) $s(bx0) $s(by0) \
                  -width 1 -outline $l(c2)]
    showsize 0
}

proc setbottom { xp yp } { global s w
    if { $xp < $s(o) } { set xp $s(o) }
    if { $yp < $s(o) } { set yp $s(o) }
    if { $xp > $s(maxpwidth) } { set xp $s(maxpwidth) }
    if { $yp > $s(maxpheight) } { set yp $s(maxpheight) }
    if { $xp > $s(tx0) } { set s(bx0) $xp } else { set s(tx0) $xp }
    if { $yp > $s(ty0) } { set s(by0) $yp } else { set s(ty0) $yp }
    $w(prev) coords $s(rect0) $s(tx0) $s(ty0) $s(bx0) $s(by0)
    showsize 0
}

proc finetune { arg } { global s w
    if $s(xx) { return }
    switch $arg {
        tx- {if {$s(tx0) <= $s(o)} {return}}
        tx+ {if {$s(tx0) >= $s(bx0)} {return}}
        ty- {if {$s(ty0) <= $s(o)} {return}}
        ty+ {if {$s(ty0) >= $s(by0)} {return}}
        bx- {if {$s(bx0) <= $s(tx0)} {return}}
        bx+ {if {$s(bx0) >= $s(maxpwidth)} {return}}
        by- {if {$s(by0) <= $s(ty0)} {return}}
        by+ {if {$s(by0) >= $s(maxpheight)} {return}}
    }
    incr s([string range $arg 0 1]0) [string index $arg 2]1
    $w(prev) coords $s(rect0) $s(tx0) $s(ty0) $s(bx0) $s(by0)
    showsize 0
    after 200 finetune $arg
}

proc registrect { xp yp } { global s w l
    if [isinside $xp $yp $s(tx0) $s(ty0) $s(bx0) $s(by0)] {
        incr s(nrect) 1
        set s(tx$s(nrect)) $s(tx0)
        set s(ty$s(nrect)) $s(ty0)
        set s(bx$s(nrect)) $s(bx0)
        set s(by$s(nrect)) $s(by0)
        $w(prev) coords $s(rect0) 0 0 0 0
        set s(rect$s(nrect)) [$w(prev) create rectangle \
            $s(tx$s(nrect)) $s(ty$s(nrect)) $s(bx$s(nrect)) $s(by$s(nrect)) \
            -width 1 -outline $l(c3)]
        set s(num$s(nrect)) [$w(prev) create text \
                    [expr ( $s(tx$s(nrect)) + $s(bx$s(nrect))) / 2] \
                    [expr ( $s(ty$s(nrect)) + $s(by$s(nrect))) / 2] \
                    -text $s(nrect) -fill $l(c3)]
        stat_text "Registered Regions: $s(nrect)"
    }

}

proc deleterect { xp yp } { global s w l
    for {set i 1} {$i <= $s(nrect)} {incr i} {
        if [isinside $xp $yp $s(tx$i) $s(ty$i) $s(bx$i) $s(by$i)] {
            $w(prev) delete $s(rect$i)
            $w(prev) delete $s(num$i)
            set j $i
            while { $j < $s(nrect) } {
                set k [expr $j + 1]
                set s(tx$j) $s(tx$k)
                set s(ty$j) $s(ty$k)
                set s(bx$j) $s(bx$k)
                set s(by$j) $s(by$k)
                set s(rect$j) $s(rect$k)
                $w(prev) delete $s(num$k)
                set s(num$j) [$w(prev) create text \
                    [expr ( $s(tx$j) + $s(bx$j)) / 2] \
                    [expr ( $s(ty$j) + $s(by$j)) / 2] \
                    -text $j -fill $l(c3)]
                incr j 1
            }
            incr s(nrect) -1
            stat_text "Registered Regions: $s(nrect)"
            break
        }
    }
}

proc deleteallrect {} { global s w l
    for {set i 1} {$i <= $s(nrect)} {incr i} {
        $w(prev) delete $s(rect$i)
        $w(prev) delete $s(num$i)
    }
    set s(nrect) 0
    $w(prev) coords $s(rect0) 0 0 0 0
    stat_text "Registered Regions: $s(nrect)"
}

#----------------------------------------------------------------
# (Combined) Widgets
#----------------------------------------------------------------

proc mb { wn text menu } { menubutton $wn -text $text -menu $menu \
                           -underline 0 -width 8 -relief flat }

proc rl { wn text } { label $wn -text "$text" -width 10 -anchor e }

proc entryscale { wn var command range length } { global l
    entry $wn.e -textvariable $var \
          -justify right -relief sunken -width 4 -font $l(f1) -fg $l(c1)
    scale $wn.s -variable $var -command $command \
          -from       [lindex $range 0] \
          -to         [lindex $range 1] \
          -resolution [lindex $range 2] \
          -length $length -orient horizontal -showvalue false
}

proc sizeline { wn label unit } { global w l
    frame $wn
    pack  $wn -side top -anchor nw
    label $wn.label -text $label -width 10 -anchor e
    label $wn.size -anchor e -relief flat -width 12 -font $l(f1) -fg $l(c1)
    label $wn.unit -text $unit -anchor w -width 6 -padx 4 -relief flat
    pack  $wn.label $wn.size $wn.unit -side left
}

#----------------------------------------------------------------
# Miscellaneous procedures
#----------------------------------------------------------------

proc do_exit {} { global c
    if $c(tmpdirclear) { clear_tkscandir }
    exit
}

proc clear_tkscandir {} { global c
    exec sh -c "rm -f $c(tkscandir)/$c(prefile).* $c(tkscandir)/$c(scanout)*"
}

proc stat_text { message } { global w
    $w(stat_text) configure -text "$message"
}

proc timetagscanout {} { global c
    set date [open "|date +%H%M%S"]
    set Time [gets $date]
    close $date
    return "$c(scanout)$Time"
}

proc getpid { command } {
    set psList [open "|ps xc"]
    set pid ""
    while {[set i [gets $psList]] != {}} {
        if [regexp "$command" "$i"] {
            lappend pid [lindex $i 0]
        }
    }
    close $psList
    return $pid
}

proc kill_scan_command {} { global s
    set s(pid) [getpid $s(command)]
    if {"$s(pid)" != ""} { foreach i $s(pid) { exec kill -HUP $i } }
}

proc isinside { x y tx ty bx by } {
    if { $x >= $tx && $x <= $bx && $y >= $ty && $y <= $by } { return 1
    } else { return 0 }
}

proc pixelto { unit length } { global s
    return [expr $length/$s(preresolution).0*$s(c$unit)]
}

proc gamma_table { gamma } {
    set table 0
    for {set i 1} {$i < 256} {incr i} {
        set value [expr 255.*pow([expr $i./255.],[expr 1./$gamma])]
        append table ,[format "%0.0f" $value]
    }
    return $table
}

#---------------------------------------------------------------
# Procedures for Scanning : Preview and Scan
#----------------------------------------------------------------

proc Preview {} { global s c w
    # WHEN STOP BUTTON IS PRESSED WHILE PREVIEWING
    if $s(stop) {
        # kill scan command
        kill_scan_command
        stat_text "Scanning Preview ... Stopped"
        set s(runscan) 0
        set s(stop) 0
        $w(bt).p configure -state normal
        $w(bt).s configure -state normal
        $w(bt).q configure -state disabled
        return
    }
    # BUTTON STATE DURING PREVIEWING
    $w(bt).p configure -state disabled
    $w(bt).s configure -state disabled
    $w(bt).q configure -state normal
    if {$s(runscan) == 0} {
    # LAUNCH COMMAND-LINE SCANNNER DRIVER
        savecur cur
        setcur pre
        settop $s(o) $s(o)
        setbottom $s(maxpwidth) $s(maxpheight)
        image create photo preview -data {}
        stat_text "Scanning Preview ..."
        update
        run_scanner_driver 0 $c(prefile)
        set s(runscan) 1
    } else {
    # CHECK PREVIEWING PROGRESS
        set s(pid) [getpid $s(command)]
        if {$s(pid) == ""} {
        # PREVIEWING IS FINISHED
            set prefile $c(tkscandir)/$c(prefile)
            if { $s($s(premode)format) == "pbm" } {
                exec convert $prefile.pnm $prefile.xbm
                image create bitmap preview -background white -file $prefile.xbm
            } else {
                if {$s(gamma_support)==1} { set gamma $s(pregamma)
                                   } else { set gamma 1 }
                image create photo preview -gamma $gamma -file $prefile.pnm
            }
            set s(runscan) 0
            stat_text "Scanning Preview ... Done"
            update
            $w(bt).p configure -state normal
            $w(bt).s configure -state normal
            $w(bt).q configure -state disabled
            setcur cur
            return
        }
    }
    # REPEAT UNTIL PREVIEWING IS FINISHED.
    after $s(loopperiod) Preview
}

proc Scan { prefix } { global s c w l
    # WHEN STOP BUTTON IS PRESSED WHILE SCANNING
    if $s(stop) {
        # kill scan command
        kill_scan_command
        stat_text "Scanning $s(curscan) / $s(nrect) ... Stopped"
        # return to the state ready for another scanning.
        if $s(nrect) {
            $w(prev) itemconfigure $s(rect$s(curscan)) -outline $l(c3)
            $w(prev) itemconfigure $s(num$s(curscan)) -fill $l(c3)
        }
        set s(runscan) 0
        set s(curscan) 0
        set s(stop) 0
        $w(bt).p configure -state normal
        $w(bt).s configure -state normal
        $w(bt).q configure -state disabled
        return
    }
    # BUTTON STATE DURING SCANNING
    $w(bt).p configure -state disabled
    $w(bt).s configure -state disabled
    $w(bt).q configure -state normal
    if {$s(runscan) == 0} {
    # LAUNCH COMMAND-LINE SCANNER DRIVER
        if {$s(nrect) == 0} {
            stat_text "Scanning $s(curscan) / $s(nrect) ..."
            run_scanner_driver $s(curscan) $prefix-$s(curscan)
            set s(runscan) 1
        } else {
            incr s(curscan) 1
            stat_text "Scanning $s(curscan) / $s(nrect) ..."
            $w(prev) itemconfigure $s(rect$s(curscan)) -outline $l(c5)
            $w(prev) itemconfigure $s(num$s(curscan)) -fill $l(c5)
            showsize $s(curscan)
            update
            run_scanner_driver $s(curscan) $prefix-$s(curscan)
            set s(runscan) 1
        }
    } else {
    # CHECK SCANNING PROGRESS
        set s(pid) [getpid $s(command)]
        if {$s(pid) != ""} {
        # CURRENT SCANNING IS STILL IN PROGRESS.
            set csize [format "%0.1f" [expr [file size \
                          $c(tkscandir)/$prefix-$s(curscan).pnm] / 1024.0]]
            set percent [format "%0.0f" [expr $csize / $s(sizeData) * 100]]
            stat_text \
                "Scanning $s(curscan) / $s(nrect) ... $csize / $s(sizeData)"
            set x [format "%0.0f" \
                  [expr ($s(prog_bar_wd) + 4) * $percent.0 / 100]]
            $w(prog_perc) configure -text "$percent%"
            $w(prog_bar) coords $s(sg) 0 0 $x 18
            update
        } else {
        # CURRENT SCANNING IS FINISHED.
            # notify the current scanning is finished.
            set s(runscan) 0
            stat_text "Scanning $s(curscan) / $s(nrect) ... Done"
            $w(prog_perc) configure -text ""
            $w(prog_bar) coords $s(sg) 0 0 0 18
            if $s(nrect) {
                $w(prev) itemconfigure $s(rect$s(curscan)) -outline $l(c3)
                $w(prev) itemconfigure $s(num$s(curscan)) -fill $l(c3)
            }
            update
            # PLAY WITH THE OUTPUT OF CURRENT SCANNING.
            make_output $prefix $s(curscan)
            if {$s(curscan) < $s(nrect)} {
            # Wait until scanner is prepared again for another scanning.
                after [format "%0.0f" \
                [expr $s(scantime).0 * $s(by$s(curscan)) / $s(maxpheight)]]
            } else {
            # All scanning regions are exhasted.
                # Recover the state ready for another scanning.
                set s(curscan) 0
                $w(bt).p configure -state normal
                $w(bt).s configure -state normal
                $w(bt).q configure -state disabled
                # EXIT loopscan
                return
            }
        }
    }
    # REPEAT THE ABOVE UNTIL FINISHED OR STOPPED.
    after $s(loopperiod) Scan $prefix
}

#---------------------------------------------------------------
# Output Treatment
#----------------------------------------------------------------

# Supported output pipes
set c(outputtoList) { ImageEditor File Printer Fax OCR }

proc setoutputto { type } { global s c w l
    set c(outputto) $type
    $w(outputto) configure -text $type
    if [winfo exists .output] { destroy .output }
    frame .output -bd 5 -relief flat
    pack  .output -side right -expand yes -fill both
    window_$c(outputto) .output
}

proc make_output { prefix i } { global s c
    make_$c(outputto) $prefix $i
}

#---------------------------------------------------------------
# Image Editor
#----------------------------------------------------------------

#
# Configurations

set c(ImageEditorList) {
    { XV          xv      }
    { ImageMagic  display }
    { GIMP        gimp    }
}
set c(ImageEditor) XV

# ImageEditorWinmode: 0=in one window, 1=separate window
set c(ImageEditorWinmode) 1

foreach i $c(ImageEditorList) { set c([lindex $i 0]command) [lindex $i 1] }

#
# window_ImageEditor and make_ImageEditor

proc window_ImageEditor { wn } {
    setcur Default
}

proc make_ImageEditor { prefix i } { global s c
    set ImageEditor $c($c(ImageEditor)command)
    if {$c(ImageEditorWinmode) == 1 || $s(nrect) == 0} {
        set geometry "-geometry [format "+%d+%d" [expr $i*100] [expr $i*25]]"
        set file "$c(tkscandir)/$prefix-$i.pnm"
    } else {
        if {$i < $s(nrect)} {return}
        set geometry "-geometry +0+0"
        for {set files ""; set j 1} {$j <= $s(nrect)} {incr j} {
            append file " $c(tkscandir)/$prefix-$j.pnm"
        }
    }
    if {$s(gamma_support)==1} {
        switch $ImageEditor {
            xv { set gamma "-gamma $s(gamma) \
                -cgamma $s(gamma_red) $s(gamma_green) $s(gamma_blue)"
            }
            display {
                set gamma "-gamma $s(gamma_red),$s(gamma_green),$s(gamma_blue)"
            }
            gimp {
            }
        }
    } else { set gamma "" }
    exec sh -c \
        "$ImageEditor $geometry $gamma $file ; \
         rm -f $file" &
    stat_text "Scanning $s(curscan) / $s(nrect) ... sent to $ImageEditor"
    update
}

#
# Subroutines

proc setImageEditor { i } { global s c w
    set c(ImageEditor) $i
    set c(ImageEditor) $i
#   $w(outtype) configure -text [arrayel $c(ImageEditorList) $i 0]
}

#---------------------------------------------------------------
# File Output
#----------------------------------------------------------------

#
# Configuration

set file(name) "scanout-#.pnm"
set file(dir) [pwd]
set file(viewer) "xv -geometry +0+0"

set file(typeList) {
    { RAW  pnm  }
    { BMP  bmp  }
    { EPS  eps  }
    { GIF  gif  }
    { JPEG jpg  }
    { PCD  pcd  }
    { PCL  pcl  }
    { PCX  pcx  }
    { PDF  pdf  }
    { PNG  png  }
    { PS   ps   }
    { TIFF tiff }
    { XBM  xbm  }
    { XPM  xpm  }
}
set file(type) RAW
set file(imageonly) 1

foreach i $file(typeList) { set file(surfix_[lindex $i 0]) [lindex $i 1] }
set file(surfixList) "\\.pgm$|\\.ppm$"
foreach i $file(typeList) { append file(surfixList) "|\\.[lindex $i 1]$" }

#
# window_File and make_File

proc window_File { wn } { global file w l
    label $wn.ti -text "TkScan File Manager" -width 24 -pady 4 -relief ridge
    canvas $wn.sep -width 0 -height 5 -relief flat
    pack  $wn.ti $wn.sep -side top

    frame $wn.file -bd 1 -relief groove
    frame $wn.file.ft -bd 0
    pack  $wn.file.ft -side top -fill x
    label $wn.file.ft.l -text "File Type" -width 9 -anchor w
    menubutton $wn.file.ft.mb -menu $wn.file.ft.mb.m \
                              -width 8 -fg $l(c1) -relief ridge
    pack $wn.file.ft.l $wn.file.ft.mb -side left
    set w(filetype) $wn.file.ft.mb
    menu $wn.file.ft.mb.m -tearoff false
    foreach i $file(typeList) { set type [lindex $i 0]
         $wn.file.ft.mb.m add command -label $type -command "setFiletype $type"
    }
    frame $wn.file.fn -bd 0
    pack  $wn.file.fn -side top -fill x
    label $wn.file.fn.l -text "File Name" -width 9 -anchor w
    entry $wn.file.fn.e -textvariable file(name) \
                        -width 21 -fg $l(c1) -font $l(f1)
    pack  $wn.file.fn.l $wn.file.fn.e -side left

    frame $wn.dir -bd 1 -relief groove
    frame $wn.dir.l
    label $wn.dir.l.l -text Directory -width 9 -anchor w
    checkbutton $wn.dir.l.c1 -text "Image files only" \
                             -variable file(imageonly) -anchor w
    pack $wn.dir.l.l $wn.dir.l.c1 -side left -fill x
    entry $wn.dir.e -textvariable file(dir) \
                    -width 30 -relief sunken -font $l(f1) -fg $l(c1)
    frame $wn.dir.f
    listbox $wn.dir.f.b -width 28 -height 9 -relief sunken -font $l(f1) \
                        -yscroll "$wn.dir.f.s set"
    set w(dirlist) $wn.dir.f.b
    scrollbar $wn.dir.f.s -command "$wn.dir.f.b yview"
    pack $wn.dir.f.b -side left  -fill both -expand yes
    pack $wn.dir.f.s -side right -fill y
    frame $wn.dir.b
    button $wn.dir.b.1 -text view   -width 4 -pady 0 -relief ridge -command {
        if {[$w(dirlist) curselection] != {}} {
            set f [lindex [selection get] 1]
            if [file isdirectory $f] {
                cd $f
                set file(dir) [pwd]
                update_dir
            } else {
                exec sh -c "$file(viewer) $f" &
            }
        }
    }
    button $wn.dir.b.2 -text remove -width 4 -pady 0 -relief ridge -command {
        if {[$w(dirlist) curselection] != {}} {
            set f [lindex [selection get] 1]
            if [file isdirectory $f] {
            } else {
                exec sh -c "rm -f $f" &
                update_dir
            }
        }
    }
    button $wn.dir.b.3 -text mkdir  -width 4 -pady 0 -relief ridge -command {
        if ![file exists $file(dir)] {
            exec mkdir $file(dir)
            cd $file(dir)
            update_dir
        }
    }
    button $wn.dir.b.4 -text update -width 4 -pady 0 -relief ridge -command {
        update_dir
    }
    pack $wn.dir.b.1 $wn.dir.b.2 $wn.dir.b.3 $wn.dir.b.4 \
         -side left -fill x -expand yes
    pack $wn.dir.l $wn.dir.e $wn.dir.f $wn.dir.b -side top -fill x
    update_dir
    bind $wn.dir.f.b <Double-Button-1> {
        if {[$w(dirlist) curselection] != {}} {
            set f [lindex [selection get] 1]
            if [file isdirectory $f] {
                cd $f
                set file(dir) [pwd]
                update_dir
            } else {
                exec sh -c "$file(viewer) $f" &
            }
        }
    }
    bind $wn.dir.e <KeyPress-Return> { cd $file(dir) ; update_dir }

    pack $wn.file $wn.dir -side top -expand yes -fill x

    setcur Default
    setFiletype $file(type)
}

proc make_File { prefix i } { global file s c
    set scanned "$c(tkscandir)/$prefix-$i"
    # Output file name: replace # to $i
    set p [string last # $file(name)]
    if {$p >= 0} {
        set outfile [string range $file(name) 0 [expr $p - 1]]$i[string \
                               range $file(name) [expr $p + 1] end]
    } else {
        set outfile $file(name)
    }
    # Run ImageMagic's convert
    if {$s(gamma_support)==1} {
        set gamma "-gamma $s(gamma_red),$s(gamma_green),$s(gamma_blue)"
    } else {
        set gamma ""
    }
    exec sh -c \
        "convert $gamma $scanned.pnm $scanned-x.$file(surfix) ; \
         rm -f $scanned.pnm ; \
         mv $scanned-x.$file(surfix) $file(dir)/$outfile" &
    stat_text "Scanning $s(curscan) / $s(nrect) ... $outfile"
    update
    check_outfile $file(dir)/$outfile
}

#
# Subroutines

proc setFiletype { type } { global file w
    set file(type) $type
    $w(filetype) configure -text $type
    set file(surfix) $file(surfix_$type)
    set p [string last . $file(name)]
    if {$p > 0} {
        set file(name) [string range $file(name) 0 $p]$file(surfix)
    }
}

proc update_dir {} { global file w
    $w(dirlist) delete 0 end
    set filelist [exec ls -a $file(dir)]
    foreach i $filelist {
        if {[file isdirectory $file(dir)/$i] && $i!="." && !$file(imageonly)} {
            $w(dirlist) insert end [format " %9s  %-17s " "Directory" $i]
        }
    }
    foreach i $filelist {
        if { [file isfile $file(dir)/$i] && \
             ([regexp -nocase $file(surfixList) $i] || !$file(imageonly)) } {
            set size [expr [file size $file(dir)/$i] / 1024.0]
            $w(dirlist) insert end [format " %6.0f  %-20s " $size $i]
        }
    }
}

proc check_outfile { filename } {
    if [file exists $filename] {
        update_dir
    } else {
        after 1000 check_outfile $filename
    }
}

#---------------------------------------------------------------
# Printer
#----------------------------------------------------------------

#
# Configurations

set pr(printerList) {
    { gv  { ghostview -geometry +0+0 } }
    { lp  { lpr                      } }
}
set pr(paperList) { A4 Letter }
set pr(positionList) { Center UpperLeft UpperRight LowerLeft LowerRight }
set pr(zoomRange) {10 300 1}
set pr(xoffsetRange) {0 300 1}
set pr(yoffsetRange) {0 300 1}
set pr(printer) gv
set pr(paper) A4
set pr(position) Center
set pr(zoom) 100
set pr(xoffset) 72
set pr(yoffset) 72
set pr(autoprint) 1
set pr(c) 0
set pr(viewer) $file(viewer)

foreach i $pr(printerList) { set pr([lindex $i 0]command) [lindex $i 1] }

#
# window_Printer and make_Printer

proc window_Printer { wn } { global pr c w l
    label $wn.ti -text "TkScan Print Manager" -width 24 -pady 4 -relief ridge
    canvas $wn.sep -width 0 -height 5 -relief flat
    pack  $wn.ti $wn.sep -side top

    frame $wn.pr -bd 1 -relief groove
    label $wn.pr.l -text Printer -width 9 -anchor w
    menubutton $wn.pr.mb -menu $wn.pr.mb.m \
                         -anchor w -width 8 -fg $l(c1) -relief ridge
    set w(printer) $wn.pr.mb
    pack $wn.pr.l $wn.pr.mb -side left
    menu $wn.pr.mb.m -tearoff false
    foreach i $pr(printerList) { set printer [lindex $i 0]
        $wn.pr.mb.m add command -label $printer -command "setPrinter $printer"
    }

    frame $wn.po -bd 1 -relief groove
    frame $wn.po.zo
    label $wn.po.zo.l -text "Zoom" -width 9 -anchor w
    entryscale $wn.po.zo pr(zoom) {} $pr(zoomRange) 113
    pack $wn.po.zo.l $wn.po.zo.e $wn.po.zo.s -side left
    frame $wn.po.po
    label $wn.po.po.l -text Position -width 9 -anchor w
    menubutton $wn.po.po.mb -menu $wn.po.po.mb.m \
                            -anchor w -width 10 -fg $l(c1) -relief ridge
    set w(printposition) $wn.po.po.mb
    pack  $wn.po.po.l $wn.po.po.mb -side left
    menu $wn.po.po.mb.m -tearoff false
    foreach i $pr(positionList) { set position [lindex $i 0]
        $wn.po.po.mb.m add command -label $position \
                                   -command "setPrintposition $position"
    }
    frame $wn.po.xo
    label $wn.po.xo.l -text "X offset" -width 9 -anchor e
    entryscale $wn.po.xo pr(xoffset) {} $pr(xoffsetRange) 113
    pack $wn.po.xo.l $wn.po.xo.e $wn.po.xo.s -side left
    frame $wn.po.yo
    label $wn.po.yo.l -text "Y offset" -width 9 -anchor e
    entryscale $wn.po.yo pr(yoffset) {} $pr(yoffsetRange) 113
    pack $wn.po.yo.l $wn.po.yo.e $wn.po.yo.s -side left
    pack $wn.po.zo $wn.po.po $wn.po.xo $wn.po.yo -side top -fill x
    set w(xoffsetlabel) $wn.po.xo.l
    set w(yoffsetlabel) $wn.po.yo.l

    frame $wn.pl -bd 1 -relief groove
    frame $wn.pl.b
    pack $wn.pl.b -side top -fill x
    set w(printpagelist) $wn.pl.b.b
    listbox $w(printpagelist) -width 19 -height 6 -font $l(f1) -fg blue \
            -yscrollcommand "$wn.pl.b.s set"
    pack $wn.pl.b.b -side left
    scrollbar $wn.pl.b.s -orient vertical -command "$w(printpagelist) yview"
    pack $wn.pl.b.s -side left -fill y
    frame $wn.pl.b.f
    pack $wn.pl.b.f -side right
    bind $w(printpagelist) <Double-Button-1> {
        if {[$w(printpagelist) curselection] != {}} {
            set i $pr([$w(printpagelist) curselection])
            do_print $i
        }
    }
    button $wn.pl.b.f.1 -text Remove -width 5 -pady 2 -command {
        if {[set t [$w(printpagelist) curselection]] != {}} {
            exec sh -c "rm -f $c(tkscandir)/[lindex $pr($t) 0].pnm"
            for {set i $t} {$i < [expr $pr(c)-1]} {incr i} {
                set pr($i) $pr([expr $i+1])
            }
            incr pr(c) -1
            update_printpagelist
            if {$t < $pr(c)} { $w(printpagelist) selection set $t
                      } else { $w(printpagelist) selection set [expr $t-1] }
        }
    }
    button $wn.pl.b.f.2 -text View   -width 5 -pady 2 -command {
        if {[set i [$w(printpagelist) curselection]] != {}} {
            set img [lindex $pr($i) 0].pnm
            exec sh -c "$pr(viewer) $c(tkscandir)/$img" &
        }
    }
    pack $wn.pl.b.f.1 $wn.pl.b.f.2 -side top

    frame $wn.b
    checkbutton $wn.b.0 -text Auto -variable pr(autoprint)
    button $wn.b.1 -text Print -width 21 -command {
        if {[set i [$w(printpagelist) curselection]] != {}} {do_print $i}
    }
    pack $wn.b.0 $wn.b.1 -side left -fill x

    pack $wn.pr $wn.po $wn.pl $wn.b -side top -fill x -expand yes

    setPrinter $pr(printer)
    setPrintposition $pr(position)
}

proc make_Printer { prefix i } { global pr s
    set tag "$prefix-$i"
    stat_text "Scanning $s(curscan) / $s(nrect) ... $tag.pnm"
    set pr($pr(c)) "$tag $s(tx$i) $s(ty$i) $s(bx$i) $s(by$i)"
    if $pr(autoprint) { do_print $pr(c) }
    incr pr(c)
    update_printpagelist
    update
}

#
# Subroutines

proc update_printpagelist {} { global pr w
    $w(printpagelist) delete 0 end
    for {set i 0} {$i < $pr(c)} {incr i} {
        $w(printpagelist) insert end [lindex $pr($i) 0].pnm
    }
}

proc do_print { i } { global pr s c
    set tag [lindex $pr($i) 0]
    set tx  [lindex $pr($i) 1]
    set ty  [lindex $pr($i) 2]
    set bx  [lindex $pr($i) 3]
    set by  [lindex $pr($i) 4]
    set print $pr($pr(printer)command)
    if {$s(gamma_support)==1} {
        set gamma "-gamma $s(gamma_red),$s(gamma_green),$s(gamma_blue)"
    } else {
        set gamma ""
    }
    switch $pr(paper) {
        A4     { set pwd 595. ; set pht 842. ; set offset 72 }
        Letter { set pwd 612. ; set pht 792. ; set offset 72 }
    }
    set rwd [pixelto pt [expr $bx-$tx]]
    set rht [pixelto pt [expr $by-$ty]]
    set zoom [expr $pr(zoom).0/100.0]
    set iwd [expr $rwd*$zoom]
    set iht [expr $rht*$zoom]
    if {$iwd > $pwd} {
        set iwd $pwd
        set zoom [expr $pwd/$rwd]
        set iht [expr $rht*$zoom]
    }
    if {$iht > $pht} {
        set iht $pht
        set zoom [expr $pht/$rht]
        set iwd [expr $rwd*$zoom]
    }
    set pr(zoom) [format "%0.0f" [expr $zoom*100]]
    set xspace [expr $pwd-$iwd]
    set yspace [expr $pht-$iht]
    switch $pr(position) {
        Center {
            set xoffset [expr ($pwd-$iwd)/2]
            set yoffset [expr ($pht-$iht)/2]
        }
        UpperLeft {
            if { $pr(xoffset) <= $xspace } {
                 set xoffset $pr(xoffset)
            } else {
                 set xoffset $xspace
                 set pr(xoffset) [format "%0.0f" $xspace]
            }
            if { $pr(yoffset) <= $yspace } {
                 set yoffset [expr $yspace-$pr(yoffset)]
            } else {
                 set yoffset 0
                 set pr(yoffset) [format "%0.0f" $yspace]
            }
        }
        UpperRight {
            if { $pr(xoffset) <= $xspace } {
                 set xoffset [expr $xspace-$pr(xoffset)]
            } else {
                 set xoffset 0
                 set pr(xoffset) [format "%0.0f" $xspace]
            }
            if { $pr(yoffset) <= $yspace } {
                 set yoffset [expr $yspace-$pr(yoffset)]
            } else {
                 set yoffset 0
                 set pr(yoffset) [format "%0.0f" $yspace]
            }
        }
        LowerLeft {
            if { $pr(xoffset) <= $xspace } {
                 set xoffset $pr(xoffset)
            } else {
                 set xoffset $xspace
                 set pr(xoffset) [format "%0.0f" $xspace]
            }
            if { $pr(yoffset) <= $yspace } {
                 set yoffset $pr(yoffset)
            } else {
                 set yoffset $yspace
                 set pr(yoffset) [format "%0.0f" $yspace]
            }
        }
        LowerRight {
            if { $pr(xoffset) <= $xspace } {
                 set xoffset [expr $xspace-$pr(xoffset)]
            } else {
                 set xoffset 0
                 set pr(xoffset) [format "%0.0f" $xspace]
            }
            if { $pr(yoffset) <= $yspace } {
                 set yoffset $pr(yoffset)
            } else {
                 set yoffset $yspace
                 set pr(yoffset) [format "%0.0f" $yspace]
            }
        }
    }
    set page [format "-page $pr(paper)+%0.0f+%0.0f" $xoffset $yoffset]
    set geometry [format "-geometry %0.0fx%0.0f" $iwd $iht]
    exec sh -c \
        "convert $gamma $page $geometry \
                 $c(tkscandir)/$tag.pnm $c(tkscandir)/$tag.ps ; \
         $print $c(tkscandir)/$tag.ps ; \
         rm -f $c(tkscandir)/$tag.ps" &
}

proc setPrinter { printer } { global pr w
    set pr(printer) $printer
    $w(printer) configure -text $printer
}

proc setPrintposition { position } { global pr s c w
    set pr(position) $position
    $w(printposition) configure -text $position
    if {$position == "Center"} {
        $w(xoffsetlabel) configure -fg gray
        $w(yoffsetlabel) configure -fg gray
    } else {
        $w(xoffsetlabel) configure -fg black
        $w(yoffsetlabel) configure -fg black
    }
}

#---------------------------------------------------------------
# Fax
#----------------------------------------------------------------

#
# Configurations

set fax(viewer) "viewfax -geometry +0+0"
set fax(numberlistfile) "$c(tkscandir)/faxnumber"
set fax(c) 0

#
# window_Fax and make_Fax

proc window_Fax { wn } { global fax c w l
    label $wn.ti -text "TkScan Fax Manager" -width 24 -pady 4 -relief ridge
    canvas $wn.sep -width 0 -height 5 -relief flat
    pack  $wn.ti $wn.sep -side top

    frame $wn.fr -bd 1 -relief groove
    frame $wn.fr.l
    pack  $wn.fr.l -side top -fill x
    label $wn.fr.l.l -text "Fax to" -width 10 -anchor w
    button $wn.fr.l.b -text "Number List" -command faxnumberWindow \
                                    -width 13 -padx 2 -pady 0 -relief ridge
    button $wn.fr.l.c -text "Clear" -width  5 -padx 0 -pady 0 -relief ridge \
           -command {set fax(number) "" ; set fax(recipient) ""}
    pack $wn.fr.l.l $wn.fr.l.b $wn.fr.l.c -side left
    frame $wn.fr.fn
    pack  $wn.fr.fn -side top -fill x
    label $wn.fr.fn.l -text "Number" -width 10 -anchor e
    entry $wn.fr.fn.e -textvariable fax(number) \
                      -width 20 -font $l(f1) -fg $l(c1)
    pack $wn.fr.fn.l $wn.fr.fn.e -side left
    frame $wn.fr.re
    pack  $wn.fr.re -side top -fill x
    label $wn.fr.re.l -text Recipient -width 10 -anchor e
    entry $wn.fr.re.e -textvariable fax(recipient) \
                      -width 20 -font $l(f1) -fg $l(c1)
    pack $wn.fr.re.l $wn.fr.re.e -side left

    frame $wn.pl -bd 1 -relief groove
    frame $wn.pl.a
    pack $wn.pl.a -side top -fill x
    label $wn.pl.a.l -text "Page List" -width 10 -anchor w
    pack $wn.pl.a.l -side left
    frame $wn.pl.b
    pack $wn.pl.b -side top -fill x
    set w(faxpagelist) $wn.pl.b.b
    listbox $w(faxpagelist) -width 19 -height 8 -font $l(f1) -fg blue \
            -yscrollcommand "$wn.pl.b.s set"
    pack $wn.pl.b.b -side left
    scrollbar $wn.pl.b.s -orient vertical -command "$w(faxpagelist) yview"
    pack $wn.pl.b.s -side left -fill y
    frame $wn.pl.b.f
    pack $wn.pl.b.f -side right
    bind $w(faxpagelist) <Double-Button-1> {
        if {[$w(faxpagelist) curselection] != {}} {
            set page $fax([$w(faxpagelist) curselection])
            exec sh -c "$fax(viewer) $c(tkscandir)/$page" &
        }
    }
    button $wn.pl.b.f.1 -text Attach -width 5 -pady 2 -command {
        attachWindow
    }
    button $wn.pl.b.f.2 -text Detach -width 5 -pady 2 -command {
        if {[set t [$w(faxpagelist) curselection]] != {}} {
            for {set i $t} {$i < [expr $fax(c)-1]} {incr i} {
                set fax($i) $fax([expr $i+1])
            }
            incr fax(c) -1
            update_faxpagelist
            if {$t < $fax(c)} { $w(faxpagelist) selection set $t
                       } else { $w(faxpagelist) selection set [expr $t-1] }
        }
    }
    button $wn.pl.b.f.3 -text Up     -width 5 -pady 2 -command {
        if {[set t [$w(faxpagelist) curselection]] != {} && $t > 0} {
            set u [expr $t-1]
            set tmp $fax($u)
            set fax($u) $fax($t)
            set fax($t) $tmp
            update_faxpagelist
            $w(faxpagelist) selection set $u
        }
    }
    button $wn.pl.b.f.4 -text Down   -width 5 -pady 2 -command {
        if {[set t [$w(faxpagelist) curselection]] != {}
            && $t < [expr $fax(c)-1]} {
            set u [expr $t+1]
            set tmp $fax($u)
            set fax($u) $fax($t)
            set fax($t) $tmp
            update_faxpagelist
            $w(faxpagelist) selection set $u
        }
    }
    button $wn.pl.b.f.5 -text View   -width 5 -pady 2 -command {
        if $fax(c) {
            for {set pages ""; set i 0} {$i < $fax(c)} {incr i} {
                append pages " $c(tkscandir)/$fax($i)"
            }
            exec sh -c "$fax(viewer) $pages" &
        }
    }
    pack $wn.pl.b.f.1 $wn.pl.b.f.2 $wn.pl.b.f.3 $wn.pl.b.f.4 \
         $wn.pl.b.f.5 -side top

    frame $wn.b
    button $wn.b.1 -text "SEND FAX" -width 12 -command sendFax
    pack $wn.b.1 -side left -expand yes -fill x

    pack $wn.fr $wn.pl $wn.b -side top -expand yes -fill x

    update_faxpagelist
    setquickmode Fax
    setquickpaper A4
}

proc make_Fax { prefix i } { global fax s
    set tag $prefix-$i
    pnmtofax $tag
    stat_text "Scanning $s(curscan) / $s(nrect) ... $tag.fax"
    set fax($fax(c)) "$tag.fax"
    incr fax(c)
    update_faxpagelist
    update
}

#
# Efax dependent procedures

proc pnmtofax { fn } { global c
    set if "$c(tkscandir)/$fn.pnm"
    set of "$c(tkscandir)/$fn.fax"
    exec sh -c "efix -n $of $if ; rm -f $if" &
}

proc sendFax {} { global fax c
    if {$fax(c) <= 0} { stat_text "sendfax: Nothing to send" ; return }
    if {$c(faxnumber) == {}} { stat_text "sendfax: Empty Fax Number" ; return }
    for {set pages "" ; set i 0} {$i < $fax(c)} {incr i} {
        append pages " $c(tkscandir)/$fax($i)"
    }
    exec sh -c "fax send $fax(number) $pages" &
}

#
# Subroutines

proc update_faxpagelist {} { global fax w
    $w(faxpagelist) delete 0 end
    for {set i 0} {$i < $fax(c)} {incr i} {$w(faxpagelist) insert end $fax($i)}
}

proc faxnumberWindow {} { global fax l w
    if [winfo exists .pbook] {return}
    toplevel .pbook
    wm title .pbook "TkScan: FaxNumberList"
    frame .pbook.1
    label .pbook.1.l -text "Fax Number List"
    pack  .pbook.1.l -side left
    frame .pbook.l
    set w(faxnumberlist) .pbook.l.b
    listbox $w(faxnumberlist) -yscroll ".pbook.l.s set" \
            -width 40 -height 8 -relief sunken -font $l(f1)
    scrollbar .pbook.l.s -command "$w(faxnumberlist) yview"
    pack $w(faxnumberlist) -side left -fill both -expand yes
    pack .pbook.l.s -side right -fill y
    frame .pbook.b -bd 1 -relief ridge
    bind $w(faxnumberlist) <Double-Button-1> select_faxnumber
    button .pbook.b.1 -text Close  -width 6 -pady 0 -command {
        save_faxnumberlist
        destroy .pbook
    }
    button .pbook.b.2 -text Select -width 6 -pady 0 -command select_faxnumber
    button .pbook.b.3 -text New    -width 6 -pady 0 -command {
        set fax(tmprecipient) ""
        set fax(tmpnumber) ""
        set fax(tmpinsert) end
        focus .pbook.i.1.e
    }
    button .pbook.b.4 -text Edit   -width 6 -pady 0 -command {
        if {[set i [$w(faxnumberlist) curselection]] != {}} {
           set line [$w(faxnumberlist) get $i]
           set x [string last : $line]
           set fax(tmprecipient) [string range $line 0 [expr $x-2]]
           set fax(tmpnumber) [string range $line [expr $x+2] end]
           set fax(tmpinsert) $i
           focus .pbook.i.1.e
        }
    }
    button .pbook.b.5 -text Remove -width 6 -pady 0 -command {
        if {[set i [$w(faxnumberlist) curselection]] != {}} {
            $w(faxnumberlist) delete $i
        }
    }
    pack .pbook.b.1 .pbook.b.2 .pbook.b.3 .pbook.b.4 .pbook.b.5 -side left
    frame .pbook.i -bd 1 -relief ridge
    frame .pbook.i.1
    frame .pbook.i.2
    pack  .pbook.i.1 .pbook.i.2 -side top -fill x
    label .pbook.i.1.l -text Name -anchor e -width 12
    label .pbook.i.2.l -text "Fax Number" -anchor e -width 12
    entry .pbook.i.1.e -textvariable fax(tmprecipient) -width 25 \
                       -font $l(f1) -fg $l(c1)
    entry .pbook.i.2.e -textvariable fax(tmpnumber) -width 25 \
                       -font $l(f1) -fg $l(c1)
    button .pbook.i.1.b -text "Clear" -width 6 -pady 0 -command {
        set fax(tmprecipient) ""
        set fax(tmpnumber) ""
    }
    button .pbook.i.2.b -text "Add" -width 6 -pady 0 -command {
        if {$fax(tmpnumber) != {}} {
            if ![string match end $fax(tmpinsert)] {
                $w(faxnumberlist) delete $fax(tmpinsert)
            }
            $w(faxnumberlist) insert $fax(tmpinsert) \
                         "$fax(tmprecipient) : $fax(tmpnumber)"
        }
    }
    bind .pbook.i.1.e <KeyPress-Return> {focus .pbook.i.2.e}
    bind .pbook.i.2.e <KeyPress-Return> {
        if ![string match end $fax(tmpinsert)] {
            $w(faxnumberlist) delete $fax(tmpinsert)
        }
        $w(faxnumberlist) insert $fax(tmpinsert) \
                         "$fax(tmprecipient) : $fax(tmpnumber)"
        focus .pbook.i.1.e
    }
    pack .pbook.i.1.l .pbook.i.1.e .pbook.i.1.b -side left
    pack .pbook.i.2.l .pbook.i.2.e .pbook.i.2.b -side left

    pack .pbook.1 .pbook.i .pbook.l .pbook.b -side top -fill x

    set fax(tmprecipient) ""
    set fax(tmpnumber) ""
    set fax(tmpinsert) end
    read_faxnumberlist
}

proc select_faxnumber {} { global fax w
    if {[set s [$w(faxnumberlist) curselection]] != {}} {
        set line [$w(faxnumberlist) get $s]
        set x [string last : $line]
        set fax(number) [string range $line [expr $x+2] end]
        set fax(recipient) [string range $line 0 [expr $x-2]]
    }
}

proc read_faxnumberlist {} { global fax w
    if ![file exists $fax(numberlistfile)] {return}
    set file [open $fax(numberlistfile) r]
    while {[set i [gets $file]]!={}} { $w(faxnumberlist) insert end $i }
    close $file
}

proc save_faxnumberlist {} { global fax w
    set file [open $fax(numberlistfile) w]
    for {set i 0} {$i < [$w(faxnumberlist) size]} {incr i} {
        puts $file [$w(faxnumberlist) get $i]
    }
    close $file
}

proc attachWindow {} { global fax c s w
    stat_text "Attach: Not implemented yet."
}

#---------------------------------------------------------------
# OCR
#----------------------------------------------------------------

#
# Configurations

#
# window_OCR and make_OCR

proc window_OCR { wn } { global c w l
    label $wn.ti -text "TkScan OCR Manager" -width 24 -pady 4 -relief ridge
    canvas $wn.sep -width 0 -height 5 -relief flat
    pack  $wn.ti $wn.sep -side top

    frame $wn.ex -bd 1 -relief groove
    label $wn.ex.l -width 32  \
                   -text "
OCR is not implemented yet
in this version.
"
    pack  $wn.ex.l -side left

    pack $wn.ex -side top -expand yes -fill x

    setquickmode OCR
}

proc make_OCR { prefix i } { global s c w
}

#----------------------------------------------------------------
# StartUp
#----------------------------------------------------------------

eval destroy [winfo child .]
wm title . "TkScan"

set s(unit)	mm
set c(outputto)	ImageEditor
set s(premodecurrent) 0
# The user configuration file, if exists, overwrites the above assignments.
if [file exists $c(userconf)] {source $c(userconf)}

# Initialization of some variables
set s(nrect)	0
set s(runscan)	0
set s(curscan)	0
set s(stop)	0

# Launch main window
mainWindow
previewcanvas
image create photo preview -data {}
set ip [expr $s(o)-1]
$w(prev) create image $ip $ip -image preview -anchor nw
set s(rect0) [$w(prev) create rectangle 0 0 0 0 -width 1 -outline $l(c2)]
settop $s(o) $s(o)
setbottom $s(maxpwidth) $s(maxpheight)
setunit $s(unit)
setmode $s(mode)
if [regexp "ImagEeditor|File|Printer|Fax|OCR" [lindex $argv [expr $argc-1]]] {
    set c(outputto) [lindex $argv [expr $argc-1]]
}
setoutputto $c(outputto)
stat_text "TkScan v$c(version)"

# End of tkscan
