#!/usr/bin/wish
#########################################################################
# before this line, the path to wish should be set!
#########################################################################
#
#  TKAlbum last edit: 19-Sep-2002
#  
#  Copyright (C) 2002 Bernhard Nebel (bernhard.nebel@gmx.de)
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You have received a copy of the GNU General Public License
#  along with this program (see end of the program) and you can
#  view the license by clicking on the license menu entry under Help.
#
#
########################################################################

# ----------------------- GLOBAL SETTINGS ----------------------------


set TKAlbumVersion "0.4"
set tk_strictMotif 0
array unset Option
set Option(RootAlbum) ""
set Option(CurrentAlbum) ""
set Option(FILE_cc_rename) 1
set Option(FILE_cc_remove_funny_chars) 1
set Option(FILE_cc_convert_options) "-geometry 350x350 -quality 65" 
set Option(FILE_cc_jhead_options) "-de -dc"
set Option(FILE_overwrite_warning) 1
set Option(FILE_change_name_when_same) 1
set Option(FILE_dir_delete_allow) 1 
set Option(FILE_use_trash_bin) 1
set Option(FILE_mark_copied) 1
set Option(FILE_save_original_when_edit) 1
set Option(FILE_edit_options) "-quality 95"
set Option(FILE_save_dir) ".originals"
set Option(FILE_trash_dir) ".trash"
set Option(FILE_trash_size) 100
set Option(FILE_delete_warning) 1
set Option(FILE_dir_delete_warning) 1
set Option(SCRIPT_jtransform) "jpegtran"
set Option(SCRIPT_read_jc) "rdjpgcom"
set Option(SCRIPT_write_jc) "wrjpgcom"
set Option(SCRIPT_album) "album"
set Option(SCRIPT_jhead) "jhead -se"
set Option(SCRIPT_convert) "convert"
set Option(SCRIPT_mogrify) "mogrify"
set Option(SCRIPT_display_tmp) "display"
set Option(SCRIPT_displaynext_tmp) "display -remote"
set Option(SCRIPT_edit) "display"
set Option(SCRIPT_kill) kill
set Option(SCRIPT_ps) ps
set Option(SCRIPT_file) file
set Option(SCRIPT_tempdir) "/tmp"
set Option(TRANS_preserve_tn) 1
set Option(TRANS_dont_touch_jpeg_comments) 0
set Option(TRANS_UseJpegtran) 1
set Option(EXIF_File_Name) 1
set Option(EXIF_File_Size) 1
set Option(EXIF_Camera) 1
set Option(EXIF_Pic_Date) 1
set Option(EXIF_Resolution) 1
set Option(EXIF_JQual) 1
set Option(EXIF_JComment) 1
set Option(JPEG_rename) "%y%m%d-%H%M%S"
set Option(EXIF_RemovalWarning) 1
set Option(VIEW_small) 1
set Option(VIEW_IgnoreAlbumFiles) 1
set Option(VIEW_OnlyPics) 1
set Option(VIEW_ShowFileExt) 1
set Option(VIEW_ShowDirs) 1
set Option(VIEW_caption_height) 3
set Option(VIEW_UsePicName) 0
set Option(VIEW_prevsize) 200
set Option(VIEW_UseEXIFTN) 1
set Option(VIEW_OnlyOneViewerWindow) 1
set Option(VIEW_viewer_size) 500
set Option(VIEW_dir_height) 4
set Option(VIEW_dir_width)  20
set Option(VIEW_preview_type) ppm
set Option(CAMERA_delete_after_download) 0
set Option(CAMERA_warn_if_not_new) 1
set Option(CAMERA_postprocess) 1
set Option(CAMERA_always_ask_for_fresh_dir) 1
set Option(CAMERA_warn_before_delete) 1
set Option(SCRIPT_postprocess) "jhead -nf%y%m%d-%H%M%S"
set Option(SCRIPT_camera_mount) "mount /media/sda1"
set Option(SCRIPT_camera_umount) "umount /media/sda1"
set Option(SCRIPT_camera_dir) "/media/sda1/dcim/"
set Option(ALBUM_all) 0
set Option(ALBUM_cleanup) 1
set Option(ALBUM_no_album) ".no_album"
set Option(ALBUM_hide_album) ".hide_album"
set Option(ALBUM_not_img) ".not_img"
set Option(ALBUM_captions) "captions.txt"
set Option(ALBUM_header) "header.txt"
set Option(ALBUM_footer) "footer.txt"
set Option(ALBUM_medium_geom_height) 500
set Option(ALBUM_medium_geom_width) 500
set Option(ALBUM_image_pages) 1
set Option(ALBUM_columns) 4
set Option(ALBUM_file_sizes) 0
set Option(ALBUM_image_sizes) 0
set Option(ALBUM_fix_urls) 1
set Option(ALBUM_known_images) 1
set Option(ALBUM_body) ""
set Option(ALBUM_all) 0
set Option(ALBUM_depth) ""
set Option(ALBUM_hashes) 0
set Option(ALBUM_name_length) 255
set Option(ALBUM_date_sort) 0
set Option(ALBUM_name_sort) 0
set Option(ALBUM_reverse_sort) 0
set Option(ALBUM_index) "index"
set Option(ALBUM_tn_geom_width) 150
set Option(ALBUM_tn_geom_height) 100
set Option(ALBUM_tn_type) ""
set Option(ALBUM_medium_type) ""
set Option(ALBUM_crop) "1"
set Option(ALBUM_Forced_cropping) ""
set Option(ALBUM_dir) "tn"
set Option(ALBUM_sample) 1
set Option(ALBUM_scale_opts) ""
set Option(ALBUM_theme) ""
set Option(ALBUM_no_theme) 1


set Option(STATE_CopyAndConvertTarget) ""
set Option(STATE_MoveTarget) ""
set Option(STATE_CopyTarget) ""
set Option(STATE_AlbumEnabled) 1
set Option(STATE_CameraEnabled) 1
set Option(STATE_ReadOnly) 0
set Option(STATE_GroupName) ""
set Option(STATE_OptionHelp) 1
set Option(STATE_CommandHelp) 1
# ----------------------- CHECK Display Variable ------------------------

# check for DISPLAY variable
if { [ lsearch -exact [ array names env ] DISPLAY ] == -1 } {
    puts stderr ""
    puts stderr  "\$DISPLAY environment variable is not defined!"
    puts stderr  "Aborting!"
    exit 1
}

# ----------------------- CHECK Tcl/Tk VERSION ------------------------

if { $tcl_version < 8.3 } {
    tk_dialog .dia "Version Error" \
		"Your Tcl/Tk system has version number: $tcl_version. In order to run tkalbum you need at least Tcl/Tk version 8.3" \
		error 0 Exit
    exit
}
# ----------------------- Global Vars --------------------------------

array set JheadVal { }
array set PicName { }
array set Caption { }
array set AltTag { }
set NameList { }
set TempCounter 0
set ViewerProcesses -1
set TempViewerFile ""
set TempDirs { }
set BackgroundProcIds { }
set RootAlbumText "*** no root album selected ***"
set CurrentAlbumText "*** no current album selected ***"
array set InfoRow { }
set CurrentImage ""
set CurrentIx -1
set NameVar ""
set AltTagVar ""
set SavingCaps 0
set CaptionsEdited 0
set FilesCopied { } 
set RealPreviewList { }
set IgnoreJheadErrors 0
set IgnoreGroupErrors 0
set CurrentWindow ""
set ShowProcess ""
set UnShowProcess ""

# ----------------------- Global Constants ---------------------------

# Sizes of elements (minimal)
set MessageWidth 40
set DirWidth 30
set InfoWidth 19
set PrefEntryLength 30
set HeaderHeight 7
set HeaderWidth 70

# Times
set HelpDelay 700
set HelpTime 5000

# profile file name
set UserProfile "~/.tkalbumrc"


# properties
set ViewerSizeList { 200 250 300 350 400 450 500 550 600 650 700 \
			 750 800 850 900 950 1000 }
set PreviewSizeList { 100 120 140 160 180 200 220 240 260 280 300 320 \
			  340 360 380 400 420 440 460 480 500 }

set DefaultBackground [  lindex [ . config -background ] end ]

set HelpFont {Helvetica -10}

set AlbumBitmap [ image create bitmap -data "
#define camera_width 32
#define camera_height 32
static char camera_bits[] = {
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x0f,0x00,0x00,0x08,0x10,0x00,0x7c,0x0f,
 0x10,0x1f,0xfa,0x07,0xa0,0x2e,0x42,0x07,0xa0,0x50,0xa3,0x03,0xc0,0xe0,0xff,
 0xee,0x77,0xbf,0x01,0xf9,0x9f,0x40,0x01,0x1d,0xb8,0xa0,0xff,0xe5,0xa7,0xff,
 0xff,0xba,0x5a,0xff,0xff,0x55,0xb5,0xff,0xff,0x8d,0xaa,0xff,0xff,0x16,0x55,
 0xff,0xff,0xa2,0x6a,0xff,0xff,0x46,0x55,0xff,0xff,0xaa,0x6a,0xff,0xff,0x56,
 0x55,0xff,0xfe,0xae,0x6a,0x7f,0x00,0x55,0xb5,0x00,0x00,0xbd,0xba,0x00,0x00,
 0xfa,0x5f,0x00,0x00,0xe4,0x27,0x00,0x00,0x18,0x18,0x00,0x00,0xe0,0x07,0x00,
 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
"
]

set UpBitmap [ image create bitmap -data "
#define small.arrup_3d_width 21
#define small.arrup_3d_height 18
static unsigned char small.arrup_3d_bits[] = {
  0x00, 0x04, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x1f, 0x00, 0x80, 0x3f, 0x00,
  0xc0, 0x7f, 0x00, 0xe0, 0xff, 0x00, 0xf0, 0xff, 0x01, 0xf8, 0xff, 0x03,
  0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00,
  0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00,
  0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, };
"]

set DownBitmap [ image create bitmap -data "
#define arrow_down_width 21
#define arrow_down_height 18
static unsigned char arrow_down_bits[] = {
  0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00,
  0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00,
  0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0xf0, 0xff, 0x01,
  0xe0, 0xff, 0x00, 0xc0, 0x7f, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x1f, 0x00,
  0x00, 0x0e, 0x00, 0x00, 0x04, 0x00, };
"]

set ImageFileExtensions { avs bie bmp bmp24 cgm cmyk dcx dib dicom \
			      epdf epi eps eps2 \
			      epsf epsi ept fax fig fits fpx gif gif87 \
			      gradient granite gray hdf histogram hpgl \
			      ico jpg jpeg  jpeg24 label \
			      map matte miff mng mono mpg mpeg mtv mvg \
			      null pbm pcd pcds pcl pct pcx pdf pic \
			      pict pict24 pix plasma pgm pm png pnm ppm \
			      ps ps2 psd ptif pwp rad rgb rgba \
			      rla rle sgi stegano sun \
			      tga tif tiff tiff24 tile tim ttf  uil \
			      uyvy vicar vid viff win x xbm xc xpm \
			      xwd yuv }

# Album options
set OptionName(ALBUM_cleanup) "*special*"
set OptionName(ALBUM_no_album)  "*special*"
set OptionName(ALBUM_hide_album) "*special*"
set OptionName(ALBUM_not_img) "*special*"
set OptionName(ALBUM_medium_geom_height) "*special*"
set OptionName(ALBUM_medium_geom_width) "*special*"
set OptionName(ALBUM_image_pages) "image_pages"
set OptionName(ALBUM_columns) "columns"
set OptionName(ALBUM_file_sizes) "file_sizes"
set OptionName(ALBUM_image_sizes) "image_sizes"
set OptionName(ALBUM_fix_urls) "fix_urls"
set OptionName(ALBUM_known_images) "known_images"
set OptionName(ALBUM_body) "body"
set OptionName(ALBUM_all) "all"
set OptionName(ALBUM_depth) "depth"
set OptionName(ALBUM_hashes) "hashes"
set OptionName(ALBUM_name_length) "name_length"
set OptionName(ALBUM_date_sort) "date_sort"
set OptionName(ALBUM_name_sort) "name_sort"
set OptionName(ALBUM_reverse_sort) "reverse_sort"
set OptionName(ALBUM_index) "index"
set OptionName(ALBUM_tn_geom_width) "*special*"
set OptionName(ALBUM_tn_geom_height) "*special*"
set OptionName(ALBUM_tn_type) "type"
set OptionName(ALBUM_medium_type) "medium_type"
set OptionName(ALBUM_crop) "crop"
set OptionName(ALBUM_Forced_cropping) "CROP"
set OptionName(ALBUM_dir) "dir"
set OptionName(ALBUM_sample) "sample"
set OptionName(ALBUM_scale_opts) "*special*"
set OptionName(ALBUM_theme) "theme"
set OptionName(ALBUM_no_theme) "*special*"
set OptionName(ALBUM_captions) "captions"
set OptionName(ALBUM_header) "*special*"
set OptionName(ALBUM_footer) "*special*"
 
set BinaryAlbumOptions { ALBUM_image_pages ALBUM_file_sizes ALBUM_image_sizes \
	ALBUM_fix_urls ALBUM_known_images ALBUM_all \
	ALBUM_hashes  ALBUM_date_sort \
	ALBUM_name_sort ALBUM_reverse_sort \
	ALBUM_crop ALBUM_sample }
set StringAlbumOptions { ALBUM_columns ALBUM_body ALBUM_depth \
	ALBUM_name_length ALBUM_index ALBUM_tn_type \
	ALBUM_medium_type ALBUM_Forced_cropping \
	ALBUM_theme ALBUM_captions ALBUM_dir}
set SpecialAlbumOptions { \
	ALBUM_medium_geom_height ALBUM_medium_geom_width \
	ALBUM_tn_geom_width ALBUM_tn_geom_height \
	ALBUM_no_theme ALBUM_scale_opts}
set NoAlbumOptions {  ALBUM_header ALBUM_footer ALBUM_no_album \
	ALBUM_hide_album ALBUM_not_img ALBUM_name_length \
	ALBUM_cleanup }
			     

# ----------------------- Platform dependent job control ------------

proc ProcessAlive { procidlist } {
    # check whether procid is still running
    # works also for lists of proc ids
    global Option
    
    foreach pid $procidlist { 
	set psline ""
	catch { set psline [ eval exec $Option(SCRIPT_ps) $pid ] }
	if { [ string first $pid $psline ] != -1 } {
	    return 1
	}
    }
    return 0
}

proc KillProcesses { procids } {
    # kill procid
    global Option

    foreach pid $procids {
	catch { eval exec $Option(SCRIPT_kill) $pid }
    }
}


# ---------------------- PROFILE HANDLING ---------------------------
 
proc ReadProfile {  } {
    global TKAlbumVersion
    global UserProfile
    global tk_strictMotif
    global Option

    if { [ file exists $UserProfile ] } {
	set result [ catch { set fid [ open $UserProfile r ] } ]
	if { $result } {
	    catch { close $fid }
	    set string "Cannot read from profile  $UserProfile"
	    tk_dialog .dia "Profile Error" $string error 0 OK
	    return
	}
	gets $fid Version
	if { $Version != $TKAlbumVersion } {
	    set string "Have detected old profile \"$UserProfile\" \
with incompatible TKAlbum version ($Version). Current version is $TKAlbumVersion. Please delete before proceedings"
	    tk_dialog .dia "Profile Error" $string error 0 OK
	    exit
	}
	gets $fid tk_sM
	# kludge to get around a bug in Tcl/Tk
	set tk_strictMotif $tk_sM
	while { [ expr ! [ eof $fid ] ] } {
	    gets $fid OptionName
	    if { [ eof $fid ] } {
		break
	    }
	    gets $fid Option($OptionName)
	}
	catch { close $fid } 
    }
}

proc WriteProfile { } {
    global TKAlbumVersion
    global UserProfile
    global tk_strictMotif
    global OptionList Option
    
    set result [ catch { set fid [ open $UserProfile w ] } ]
    if { $result } {
	set string "Cannot write to profile $UserProfile"
	tk_dialog .dia "File Error" $string error 0 OK
	return
    }
    puts $fid $TKAlbumVersion
    puts $fid $tk_strictMotif
    foreach key [ array names Option ] {
	puts $fid $key
	puts $fid $Option($key)
    }    
    close $fid 
}



# ------------------------- Some auxiliary functions -----------

proc BusyCursor { } {
    . config -cursor watch 
}

proc IdleCursor { } {
    . config -cursor {}
}

proc BlockInput { } {
    focus .messageframe.dir.root.label
    grab .messageframe.dir.root.label
    BusyCursor
}

proc ReleaseInput { } {
    global Option

    grab release .messageframe.dir.root.label
    if $Option(STATE_AlbumEnabled) {
	focus .textframe.nameent
    } else {
	focus .
    }
    IdleCursor
}

proc SafeCall { cmd cmdname } {
    BlockInput
    if { [ catch { eval $cmd } errmes ] } {
	tk_dialog .dia "$cmdname Error" "Error while executing the \"$cmdname\" command: \"$errmes\"" error 0 OK
    }
    ReleaseInput
}

proc PositionWindowCenter { w } {
    global Color

    wm withdraw $w
    update idletasks
    set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
	    - [winfo vrootx [winfo parent $w]]]
    set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
	    - [winfo vrooty [winfo parent $w]]]
    wm geometry $w +$x+$y
    wm deiconify $w
}

proc FlattenMenu { NotThis } {
    foreach el { .mbar.file .mbar.pref .mbar.transform .mbar.view \
		     .mbar.album .mbar.camera  .mbar.jpeg .mbar.help } {
	if { $el != $NotThis } {
	    $el config -relief flat 
	}
    }
}

proc SelectedFiles { } {
    global NameList

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	return ""
    }

    set res { }

    foreach el $cursel {
	lappend res [ lindex $NameList $el ]
    }
    return $res
}
	
proc IsJpeg { file } {
    global Option

    if { [ catch { set res [ eval exec $Option(SCRIPT_file) \
				 { $file} ] } errmes ] } {
	tk_dialog .dia "File Execution Error" "Error while executing \"$Option(SCRIPT_file) $file\": \"$errmes" error 0 OK
	return 0
    }
    return [ string match "*JPEG*" $res ] 
}

proc lreverse { list } {

    set newlist { }
    for {set lix [ expr [ llength $list ] - 1 ] } \
	{ $lix >= 0 } \
	{ set lix [ expr $lix - 1 ] } {
	    lappend newlist [ lindex $list $lix ]
	}
    return $newlist
}

proc lequal { l1 l2 } {
    foreach  e1 $l1 e2 $l2 {
	if { $e1 != $e2 } {
	    return 0
	}
    }
    return 1
}

proc CheckNumber { newval } {
    if { $newval == "" } {
	return 1
    } else {
	return [ IsNumber $newval ]
    }
}

proc IsNumber { val } {
    return [ expr ! [ catch { expr int($val) } ] ]
}

# set file's group permission to +w 
# - provided EnableGroupPerm is true
proc SetGroupPerms { file } {
    global Option
    global IgnoreGroupErrors
    global env

    if { $Option(STATE_GroupName) == "" || $IgnoreGroupErrors } {
	return
    }
    if { [ file attr $file -owner ] == $env(USER) } {
	if { [ file isdirectory $file ] } {
	    set perm "g+rwx"
	} else {
	    set perm "g+rw"
	}
	if { [ catch { file attribute $file -perm $perm \
			   -group $Option(STATE_GroupName) } errmes ] } {
	    if { [ tk_dialog .dia "Group Error" \
		       "There occured an error when setting the attributes of \"$file\" to \"-perm $perm -group $Option(STATE_GroupName):\" \"$errmes\". \n\nYou probably should use a different group name or no group name at all. Do you want to ignore further errors related to this problem during this session?" error 0 Yes No ] == 0 } {
		set IgnoreGroupErrors 1
	    }
	}
    }
}


# ----------------------- Directory scanning & modifications ---

proc ScanDirectory { } {
    global DirList
    global NameList
    global Option
    global PicName
    global AltTag
    global Caption
    global FilesCopied

    catch { .listframe.list delete 0 end }

    if { [ catch { cd $Option(RootAlbum) } ] } {
	set Option(RootAlbum) ""
	set Option(CurrentAlbum) ""
	ShortenCurrentAlbumName
    } elseif { [ catch { cd $Option(CurrentAlbum) } ] } {
	set Option(CurrentAlbum) ""
	ShortenCurrentAlbumName
    }

    if {  $Option(CurrentAlbum) == "" } {
	return
    }

    update

    catch { PreviewImg blank }

    ReadCaptionFile
    set CaptionsEdited 0

    set DirList [ concat [ glob -nocomplain * ] [ glob -nocomplain .* ] ]
    
    set FileSubList { }
    set DirSubList { } 
    set DirList [ lsort $DirList ]

    # find list of files and list of dirs not already
    # metioned in caption file
    foreach file $DirList {
	if { [ lsearch $NameList $file ] == -1  } {
	    if { [ file isfile $file ] } { 
		lappend FileSubList $file 
		set PicName($file)  ""
		set Caption($file)  ""
		set AltTag($file)  ""
	    } elseif { [ file isdirectory $file ] } {
		lappend DirSubList $file
	    }
	}
    }
    set DirList [ concat $DirSubList $NameList $FileSubList ] 
    set NameList { }

    # now remove those files and dirs that we do not want to see
    foreach file $DirList {
	if { ($file != $Option(ALBUM_dir) && \
		  $file != $Option(ALBUM_captions) && \
		  $file != $Option(ALBUM_header) && \
		  $file != $Option(ALBUM_footer) && \
		  $file != $Option(ALBUM_not_img) && \
		  $file != $Option(ALBUM_no_album) && \
		  ! [ string match "*.txt" $file ] && \
		  ! [ string match "*.htaccess" $file ] && \
		  ! [ string match "*~" $file ] && \
		  $file != "$Option(ALBUM_index).html" && \
		  $file != "$Option(ALBUM_index).htm" ) || \
		! $Option(VIEW_IgnoreAlbumFiles) } {
	    if { [ file isfile $file ] } { 
		if { [ IsImageFile $file ] || ! $Option(ALBUM_known_images) } {
		    if { [ NoNotImageMark $file ] } {
			lappend NameList $file
		    }
		}
	    } elseif { [ file isdirectory $file ] } {
		if { $file != "CVS" && $file != "SCCS" && \
			 $file != "RCS" && $file != ".xvpics" } {
		    if { [ string first "." $file ] != 0 || \
			     $Option(ALBUM_all) == 1  || \
			     $file == "." || \
			     ( $file == ".." && \
				   $Option(VIEW_ShowDirs) && \
				   $Option(CurrentAlbum) != \
				   $Option(RootAlbum) ) } {
			lappend NameList $file
		    }
		}
	    }
	}
    }

    # finally create list of files or names as we want see them
    set DisplayList   { }
    foreach file $NameList {
	set forig $file
	if { [ file isfile  $file ] } { 
	    if { $Option(VIEW_UsePicName) == 1 } {
		set cap ""
		catch { set cap $PicName($file) }
		if { $cap != "" } {
		    set file $cap
		}
	    } elseif { $Option(VIEW_ShowFileExt) == 0 } {
		set file [ file rootname $file ]
	    }
	    if { [ lsearch $FilesCopied \
		       [ file join $Option(CurrentAlbum) $forig ] ] != -1 &&  \
		     $Option(FILE_mark_copied) } {
		lappend DisplayList "*   $file"
	    } else {
		lappend DisplayList "    $file"
	    }
	} elseif { [ file isdirectory $file ] } {
	    if { [ file isfile [ file join $file \
				     $Option(ALBUM_hide_album) ] ] } {
		lappend DisplayList "\[HA\]$file"
	    } elseif { [ file isfile [ file join $file \
					   $Option(ALBUM_no_album) ] ] } {
		lappend DisplayList "\[NA\]$file"
	    } else {
		lappend DisplayList "\[A\] $file"
	    }
	}
    }

    catch { eval .listframe.list insert 0 $DisplayList }
    ClearInfo
}

proc NoNotImageMark { filename } {
    global Option

    if { [ file isfile \
	       "[ file rootname $filename ]$Option(ALBUM_not_img)" ] || \
	 [ file isfile \
	       "[ file rootname $filename ]$Option(ALBUM_no_album)" ]} {
	return 0
    } else {
	return 1
    }
}

proc IsImageFile { file { TestJpeg 1 } } {
    global ImageFileExtensions

    if {  $TestJpeg  } {
	if { [ IsJpeg $file ] } {
	    return 1
	}
    }
    set ext [ file extension $file ]
    if { $ext == "" } {
	return 0
    } else {
	set ext [ string tolower [ string range $ext 1 end ] ]
	if { [ lsearch $ImageFileExtensions $ext ] != -1 }  {
	    return 1
	} else {
	    return 0
	}
    }
}
    

proc ReadCaptionFile { } {
    global Option
    global PicName Caption AltTag NameList
    
    catch { array unset PicName }
    array set PicName { }
    catch { array unset Caption }
    array set Caption { }
    catch { array unset AltTag }
    array set AltTag  { }
    set NameList { }

    if {  $Option(STATE_AlbumEnabled) == 0 } {
	return
    }
    if { [ file isfile $Option(ALBUM_captions) ] } {
	set result [ catch {  set fid [ open $Option(ALBUM_captions) r ] } ]
	if { $result } {
	    catch { close $fid }
	    set string "Cannot read from caption file \"$Option(ALBUM_captions)\" in album \"$Option(CurrentAlbum).\""
	    tk_dialog .dia "Read Error" $string error 0 OK
	    return
	}
	while { [ expr ! [ eof $fid ] ] } {
	    gets $fid line
	    if { [ eof $fid ] } {
		break
	    }
	    if { $line != "" } {
		set fname ""
		set names ""
		set caption ""
		set alttag ""
		set doublecolon [ string first  " :: " $line 0 ]
		if { $doublecolon > -1 } {
		    set fname \
			[ string trim [ string range $line 0 \
					    [ expr $doublecolon - 1 ] ] ]
		    set rest [ string range $line \
				   [ expr $doublecolon + 4 ] end ]
		    set doublecolon [ string first  " :: " $rest 0 ]
		    if { $doublecolon == -1 } {
			set names $rest
		    } else {
			set names \
			    [ string trim [ string range $rest 0 \
						[ expr $doublecolon - 1 ] ] ]
			set rest [ string range $rest \
				       [ expr $doublecolon + 4 ] end ]
			set doublecolon [ string first  " :: " $rest 0 ]
			if { $doublecolon == -1 } {
			    set caption $rest
			} else {
			    set caption \
				[ string trim \
				      [ string range $rest 0 \
					    [ expr $doublecolon - 1 ] ]]
			    set alttag [ string range $rest \
					   [ expr $doublecolon + 4 ] end ]
			}
		    }
		} else {
		    # no double colon: so it must be TABS
		    set ll [ split $line "\t" ]
		    set fname [ lindex $ll 0 ]
		    if { [ llength $ll ] > 1 } {
			set names [ lindex $ll 1 ]
			if { [ llength $ll ] > 2 } {
			    set caption [ lindex $ll 2 ]
			    if { [ llength $ll ] > 3 } {
				set alttag [ lindex $ll 3 ]
			    }
			}
		    }
		}
	    }
	    if { [ file exists $fname ] } {
		if { [ file isfile $fname ] || 
		     ( [ file isdirectory $fname ] && \
			   $Option(VIEW_ShowDirs) ) } {
		    if { [ lsearch $NameList $fname ] == -1 } {
			lappend NameList $fname
		    }
		    if { [ file isfile $fname ]  } { 
			set PicName($fname) $names
			set Caption($fname) $caption
			set AltTag($fname) $alttag
		    }
		}
	    }
	}
	catch { close $fid }
    }
}

proc SaveCaptions { } {
    global Option
    global PicName Caption AltTag NameList
    global NameVar AltTagVar
    global CurrentImage
    global CurrentIx
    global SavingCaps
    global CaptionsEdited 

    if { $Option(STATE_ReadOnly) || \
	     $Option(RootAlbum) == "" || \
	     $Option(CurrentAlbum) == "" || \
	     ! $Option(STATE_AlbumEnabled) } {
	return
    }

    set namechange 0

    if { $CurrentImage != "" } {
	if { $PicName($CurrentImage) != $NameVar } {
	    set CaptionsEdited 1
	    set namechange 1
	}
	set PicName($CurrentImage)  $NameVar
	set AltTag($CurrentImage) $AltTagVar
	set Caption($CurrentImage) ""
	set Caption($CurrentImage) \
	    [ string trim \
		  [ string map { "\n" " " } \
			[ .textframe.cap.text get 1.0 end ] ] ]
	if { $NameVar != "" || $AltTagVar != "" || \
		$Caption($CurrentImage) != "" } {
	    set CaptionsEdited 1
	}
	if { $Option(VIEW_UsePicName) && $namechange } {
	    if { $CurrentIx != -1 } {
		set prefix [ string range \
				 [ .listframe.list get $CurrentIx ] 0 3 ]
		set isselected [ .listframe.list \
			selection includes $CurrentIx ]
		.listframe.list delete $CurrentIx
		if { $NameVar != "" } {
		    set NewString $NameVar
		} else {
		    set NewString [ lindex $NameList $CurrentIx ]
		} 
		.listframe.list insert $CurrentIx "${prefix}$NewString"
		if { $isselected } {
		    .listframe.list selection set $CurrentIx
		}
	    }
	}
    }
    if { $NameList == "" || \
	     ! $CaptionsEdited } {
	return
    }

    if { $SavingCaps } {
	return
    } else {
	set SavingCaps 1
    }
    set result [ catch {  set fid [ open $Option(ALBUM_captions) w ] } ]
    if { $result } {
	    catch { close $fid }
	    set string "Cannot open caption file \"$Option(ALBUM_captions)\" for writing in album \"$Option(CurrentAlbum).\" This means that no changes can be done to the album. Do you want me to continue in READONLY mode in order to avoid these error messages in the future? Usually one should \"Yes\" here."
	if { [ tk_dialog .dia "Open Error" \
		   $string error 0 Yes "No, Cancel" ] == 0 } {
	    set Option(STATE_ReadOnly) 1
	    StateSettings
	    set SavingCaps 0
	    return
	} else {
	    return
	}
    }
    # write first directories (but ignore . and .. and 
    # do not write any captions for directories
    # also ignore if directories are not shown

    foreach fname $NameList {
	if { [ file isdirectory $fname ] && $fname != "." && \
		 $fname != ".." && \
		 $fname != $Option(ALBUM_dir) && \
		 $fname != "CVS" && $fname != "SCCS" && \
		 $fname != "RCS" && $fname != ".xvpics" && \
		 ! [ file isfile [ file join \
				       $fname $Option(ALBUM_hide_album) ] ] } {
	    if { [ catch { puts $fid $fname } errmess ] } {
		catch { close $fid }
		set string "Error while writing to caption file \"$Option(ALBUM_captions)\" in album \"$Option(CurrentAlbum):\" \"$errmes\""
		tk_dialog .dia "Write Error" $string error 0 OK
		set SavingCaps 0
		return
	    }
	}
    }

    foreach fname $NameList {
	if { [ file isfile $fname ] } {
	    if { ($fname != $Option(ALBUM_captions) && \
		      $fname != $Option(ALBUM_header) && \
		      $fname != $Option(ALBUM_footer) && \
		      $fname != $Option(ALBUM_not_img) && \
		      $fname != $Option(ALBUM_no_album) && \
		      ! [ string match "*.txt" $fname ] && \
		      ! [ string match "*.htaccess" $fname ] && \
		      ! [ string match "*~" $fname ] && \
		      $fname != "$Option(ALBUM_index).html" && \
		      $fname != "$Option(ALBUM_index).htm" ) } { 
		if { [ catch { puts $fid \
   "$fname :: $PicName($fname) :: $Caption($fname) :: $AltTag($fname)" } errmes ] } {
		    catch { close $fid }
		    set string "Error while writing to caption file \"$Option(ALBUM_captions)\" in album \"$Option(CurrentAlbum):\" \"$errmes\""
		    tk_dialog .dia "Write Error" $string error 0 OK
		    set SavingCaps 0
		    return
		}

	    }
	}
    }
    catch { close $fid }
    SetGroupPerms $Option(ALBUM_captions)
    set SavingCaps 0
}

########### Bound: Press-UpArrowButton
# move everything selected up one slot
proc MoveEntriesUp { finalize } {
    global Option
    global NameList
    global movelist
    global CaptionsEdited

    SaveCaptions

    set movelist [ .listframe.list curselection ]
    if { $movelist == "" } { return }

    set CaptionsEdited 1
    set skipnext 0
    set newsel { }
    # move each element one position up, but ignore that 
    # if it is a series of selected directories going up to the start or
    # if it is a series of selected files going up to the first file
    # after the directories
    foreach el $movelist {
	if { ( $el == 0 ) ||
	     ( [ file isfile [ lindex $NameList $el ] ] && \
		   [ file isdirectory [ lindex $NameList \
					    [ expr $el -1 ] ] ]) } {
	    set skipnext [ expr $el + 1 ]
	    lappend newsel $el
	    continue
	}
	if { $el == $skipnext } {
	    incr skipnext
	    lappend newsel $el
	    continue
	}
	# Here starts the regular case: exchange the 
	# element with the previous one
	set moveN [ lindex $NameList $el ]
	set NameList [ lreplace $NameList $el $el ]
	set NameList [ linsert $NameList [ expr $el - 1 ] $moveN ]
	set moveL [ .listframe.list get $el ]
	.listframe.list delete $el
	.listframe.list insert [ expr $el - 1 ] $moveL
	lappend newsel [ expr $el - 1 ]
    }
    if { $finalize } {
	SaveCaptions
	ScanDirectory
    }
    if { $newsel != "" } { 
	.listframe.list selection clear 0 end
	foreach el $newsel {
	    .listframe.list selection set $el
	}
	.listframe.list see [ lindex $newsel 0 ]
	update
    }
    update
    set CurrentIx -1
    LoadPicFile
}


########### Bound: Press-DownArrowButton
# move everything selected one slot down
proc MoveEntriesDown { finalize } {
    global Option
    global NameList
    global movelist
    global CaptionsEdited

    SaveCaptions

    set movelist [ .listframe.list curselection ]
    if { $movelist == "" } { return }

    set CaptionsEdited 1
    set skipnext -1
    set newsel { }
    # move each element one position down, but ignore that 
    # if it is a series of selected directories going down to 
    # the end of the directory section or
    # if it is a series of selected files going up down the last file
    # after the directories
    foreach el [ lreverse $movelist ] {
	if { ( $el == [ expr [ llength $NameList ] - 1] ) ||
	     ( [ file isdirectory [ lindex $NameList $el ] ] && \
		   [ file isfile [ lindex $NameList \
					    [ expr $el + 1 ] ] ]) } {
	    set skipnext [ expr $el - 1 ]
	    lappend newsel $el
	    continue
	}
	if { $el == $skipnext } {
	    set skipnext [ expr $skipnext - 1 ]
	    lappend newsel $el
	    continue
	}
	# Here starts the regular case: exchange the 
	# element with the previous one
	set del [ expr $el + 1 ] 
	set moveN [ lindex $NameList $del ]
	set NameList [ lreplace $NameList $del $del ]
	set NameList [ linsert $NameList $el $moveN ]
	set moveL [ .listframe.list get $del ]
	.listframe.list delete $del
	.listframe.list insert $el $moveL
	lappend newsel [ expr $el + 1 ]
    }
    if { $finalize } {
	SaveCaptions
	ScanDirectory
    }
    if { $newsel != "" } { 
	.listframe.list selection clear 0 end
	foreach el $newsel {
	    .listframe.list selection set $el
	}
	.listframe.list see [ lindex $newsel 0 ]
	update
    }
    set CurrentIx -1
    LoadPicFile
}

########### Bound: Ctrl-PressUpArrowButton
# Move up selected entries to front
proc MoveAllUp { } {
    
    set oldsel [ .listframe.list curselection ] 
    set newsel ""

    while { ! [ lequal $oldsel $newsel ] } {
	set oldsel [ .listframe.list curselection ] 
	MoveEntriesUp 0
	set newsel [ .listframe.list curselection ] 
    }
    MoveEntriesUp 1
}

########### Bound: Ctrl-PressDownArrowButton
# Move up selected entries to tail
proc MoveAllDown { } {
    
    set oldsel [ .listframe.list curselection ] 
    set newsel ""

    while { ! [ lequal $oldsel $newsel ] } {
	set oldsel [ .listframe.list curselection ] 
	MoveEntriesDown 0
	set newsel [ .listframe.list curselection ] 
    }
    MoveEntriesDown 1
}

# -------- Display Info about a picture and generate preview -------

########## Bound: ListboxSelect
# Load selected picture (info & preview). If more than one is selected,
# load last one in list
proc LoadPicFile { } {
    global Option
    global NameList
    global JheadVal
    global CurrentImage
    global CurrentIx

    update

    SaveCaptions

    update

    # determine whether it is a selection
    # if not, blank display and return
    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	ClearInfo
	PreviewImg blank
	set CurrentIx -1
	return
    }

    # in case of multiple selections, use the last element
    set cursel [ lindex $cursel end ]
    set file [ lindex $NameList $cursel ]

    # If we are already processing this picture, do nothing
    if { $CurrentIx == $cursel } { return }

    set CurrentImage ""
    set CurrentIx -1
    ClearInfo
    catch { PreviewImg blank }

    if { [ file isdirectory $file ] } {
	set JheadVal(EXIF_File_Name) $file
	return
    } elseif { [ file isfile $file ] } {
	DisplayPicInfo $file $cursel
	DisplayPreview $file
    }
    if { $Option(STATE_AlbumEnabled) } {
	focus .textframe.nameent
    }
}

proc DisplayPreview { file } {
    global Option
    global CurrentPreviewImageName
    global RealPreviewList

    if { $Option(VIEW_prevsize) == 0 } {
	return
    }
    set ImageName [ file join $Option(CurrentAlbum) $file ]
    set CurrentPreviewImageName $ImageName

    if { [ lsearch [ image names ] $ImageName ] != -1 } {
	if { [ image height $ImageName ] > 0 && \
		 [ image width $ImageName ] > 0 } {
	     CopyToPreview $ImageName
	     if { [ lsearch $RealPreviewList $ImageName ] != -1 } {
		 IdleCursor
	     } else {
		 BusyCursor
	     }
	     return
	}
    }

    image create photo $ImageName

    if { $Option(VIEW_UseEXIFTN) && [ IsJpeg $file ] } {
	if { [ IsJpeg $file ] } {
	    BusyCursor
	    GetEXIFPreview $ImageName 
	    IdleCursor
	}
    }

    if { [ IsImageFile $file ] } {
	after 1 { GeneratePreview $CurrentPreviewImageName \
		      $Option(VIEW_prevsize) }
    } else {
	catch { PreviewImg blank }
    }
}

proc GetEXIFPreview { imagename } {
    global Option 
    global JheadVal
    
    set transop [ GetTransOp $imagename ] 
    if { $transop == "ERROR" } {
	return
    }
    set EXIFPrev [ MakeTempName jpg ]
    catch { eval exec $Option(SCRIPT_jhead) -st $EXIFPrev { $imagename } } 
    if { ! [ file exists $EXIFPrev ] } {
	return
    }
    set opt [ GenIMOpt $transop ]
    set GIFName "[ file rootname $EXIFPrev ].$Option(VIEW_preview_type)" 
    if { [ catch { eval exec $Option(SCRIPT_mogrify) $opt -format \
		       $Option(VIEW_preview_type) $EXIFPrev  } errmes ] } {
	tk_dialog .dia "Conversion error" "Error while executing the mogrify command \"$Option(SCRIPT_mogrify) [ join $opt ] -format  $Option(VIEW_preview_type) $EXIFPrev\": \"$errmes\"" error 0 OK
	catch { file delete $EXIFPrev }
	catch { file delete $GIFName }
	return
    }
    catch { file delete $EXIFPrev }
    catch { image delete TempImg }
    image create photo TempImg
    catch { TempImg configure -file $GIFName }
    catch { file delete $GIFName }
    set pwidth [ image width TempImg ]
    set pheight [ image height TempImg  ]
    set owidth [ string range $JheadVal(EXIF_Resolution) 0 \
		     [ expr \
			   [ string first " x " $JheadVal(EXIF_Resolution) ] \
			   - 1 ] ]
    set oheight [ string range \
		      $JheadVal(EXIF_Resolution) \
		      [ expr 3 + \
			    [ string first " x " $JheadVal(EXIF_Resolution) ]\
			   ] \
		      end ]
    if { $oheight == 0 || $owidth == 0 || $pheight == 0 || $pwidth == 0 } {
	TempImg blank
	return
    }
    if { [ OrientationSimilar $owidth $oheight $pwidth $pheight ] } {
	if { $pwidth < $pheight } {
	    set maxsize $pheight
	} else {
	    set maxsize $pwidth
	}
	if { $maxsize > $Option(VIEW_prevsize) } {
	    set shrinkfac [ expr (( $maxsize - 1) / $Option(VIEW_prevsize)) + 1]
	    $imagename copy TempImg -subsample $shrinkfac
	    CopyToPreview $imagename 
	} else {
	    set zoomfac [ expr ($Option(VIEW_prevsize) /  $maxsize) ]
	    $imagename copy TempImg -zoom $zoomfac 
	    CopyToPreview $imagename 
	}
    } else {
	$imagename blank
    }
}

# generate the options for Image Magick' mogrify
# from the transformation information found in the
# in the jpeg file
proc GenIMOpt { transop } {
    if { $transop == "" } {
	return ""
    } elseif { $transop == {FLIP H} } {
	return {-flop}
    } elseif { $transop == {FLIP V} } {
	return {-flip}
    } elseif { [ lindex $transop 0 ] == "ROTATE" } {
	return [ concat -rotate [ lindex $transop 1 ] ]
    } else {
	tk_dialog .dia "Internal error" "Wrong TKAlbum transformation information in Jpeg comment -- should not happen: $transop" warning 0 OK
	return ""
    }
}


proc OrientationSimilar { w1 h1 w2 h2 } {

    return [ expr ($w1 < $h1 ) == ( $w2 < $h2 ) ]  
}

proc CopyToPreview { img } {
    global Option

    if { $Option(VIEW_prevsize) == 0 } {
	return
    }
    set xstart [ expr ($Option(VIEW_prevsize) - [image width $img]) / 2 ]
    set ystart [ expr ($Option(VIEW_prevsize) - [image height $img]) / 2 ]
    PreviewImg copy $img -to $xstart $ystart
}

proc GeneratePreview { filename scale } {
    global CurrentPreviewImageName
    global RealPreviewList
    global Option

    BusyCursor
    set PreviewName [ MakeTempName $Option(VIEW_preview_type) ]
    set procid [ eval exec $Option(SCRIPT_convert) -sample ${scale}x$scale \
		     { $filename }  { $PreviewName }  & ]
    set running 1
    while { $running } {
	update
	after 200
	set running [ ProcessAlive $procid ]
    }
    if { $scale == $Option(VIEW_prevsize) } {
	if { [ file exist $PreviewName ] } { 
	    if { [ lsearch [ image names ] $filename ] == - 1 } {
		# image name has been deleted -- probably because of
		# rename operation
		return
	    }
	    $filename configure -file $PreviewName 
	    lappend RealPreviewList $filename
	    if { $filename == $CurrentPreviewImageName } {
		PreviewImg blank
		CopyToPreview $filename 
		IdleCursor
	    }
	}
    }
    catch { file delete $PreviewName }
}

proc ClearInfo { } {
    global JheadVal
    global NameVar AltTagVar
    global CurrentImage
    global CurrentIx

    set CurrentImage ""
    set CurrentIx -1
    set NameVar ""

    set AltTagVar ""
    catch { .textframe.cap.text delete 1.0 end }

    # clear all info fields
    set searchid [ array startsearch JheadVal ]
    while { [ array anymore JheadVal $searchid ] } {
	set el [ array nextelement JheadVal $searchid ]
	set JheadVal($el) ""
    }
    array donesearch JheadVal $searchid
}

proc DisplayPicInfo { file ix } {
    global JheadVal
    global CurrentImage
    global CurrentIx
    global NameVar
    global AltTagVar
    global Caption PicName AltTag
    
    if { [ file isdirectory $file ] } {
	set JheadVal(EXIF_File_Name) $file
	return
    } elseif { [ file isfile $file ] } {
	ParseJheadOutput $file
	set JheadVal(EXIF_File_Name) $file
	catch { set JheadVal(EXIF_File_Size) \
		    "[ expr [ file size $file ] / 1024 ]KB" }
	if { [ IsImageFile $file ] } {
	    set CurrentImage $file
	    set CurrentIx $ix
	    set NameVar $PicName($file)
	    focus .textframe.nameent
	    .textframe.nameent icursor end
	    set AltTagVar $AltTag($file)
	    .textframe.cap.text delete 1.0 end 
	    .textframe.cap.text insert end $Caption($file)
	    .textframe.cap.text mark set insert end
	}
    }
}

    
proc ParseJheadOutput { file } {
    global JheadVal
    global JheadMatch1 JheadMatch2
    global Option
    global IgnoreJheadErrors

    if { [ catch { set InfoString \
		       [ eval exec $Option(SCRIPT_jhead) -se { $file } ] } errmes ] } {
	if { $IgnoreJheadErrors } { return }
	if { [ regexp -nocase "couldn't execute" $errmes ] } {
	    set resp [ tk_dialog .dia "Jhead Error" "You do not seem to have the \"jhead\" program installed. It is recommened to do so because it makes it possible to view interesting details about the photos  such as the time the picture was taken, the resolution, the quality, the white balance, etc. Do you want to avoid the error message about the missing \"jhead\" program during this session?" error 0 Yes No ]
	    if { $resp == 0 } {
		set IgnoreJheadErrors 1
		return
	    }
	}
	tk_dialog .dia "Jhead Call Error" "Error when trying to execute the command \"$Option(SCRIPT_jhead)  $file\": \"$errmes\"" error 0 OK
	return
    }
    set searchid [ array startsearch JheadVal ]
    while { [ array anymore JheadVal $searchid ] } {
	set el [ array nextelement JheadVal $searchid ]
	set JheadVal($el) [ FindInfos $InfoString \
				$JheadMatch1($el) $JheadMatch2($el) ]
    }
    array donesearch JheadVal $searchid
}

proc FindInfos { string match1 match2 } {
    return "[ FindOneInfo $string $match1 0 ][ FindOneInfo $string $match2 1]"
}

proc FindOneInfo { string match sep } {
    set res ""
    set six [ string first $match $string ]
    if { $six == -1 } {
	return ""
    }
    set string \
	[ string range $string \
	      [ expr $six + [ string length $match ] ] \
	      end
	  ]
    set end [ expr [ string first "\n" $string ] -1 ]
    if { $end == -2 } {
	set end end
    }
    set res [ string range $string 0 $end ]
    if { $res != "" && $sep == 1 } {
	set res " $res"
    }
    return $res
}

# Generate a temporary name for holding the preview
# Kill after having read it!
proc MakeTempName { ext } {
    global TempCounter
    global Option

    incr TempCounter 
    if { $ext != "" } {
	set ext ".$ext"
    }
    return [ file join $Option(SCRIPT_tempdir) \
		 .tka-tmp-[ pid ]-$TempCounter-[ clock second ]$ext ]
}

# ------------------------- File Menu ------------------------------

proc PwdFails { } {
    if { [ catch { pwd } ] } {
	tk_dialog .dia "Directory Problem" "Sorry, I cannot access the current  directory. Either it has been deleted or there is a severe file system problem. Please try to solve the problem before continuing." error 0 OK
	return 1
    }
    return 0
}

########## File menu: Set root album
proc ChooseRootDir { } {
    global Option
    global NameList

    SaveCaptions

    if { $Option(RootAlbum) == "" } {
	set InitialDir "~"
    } else {
	set InitialDir [ file dirname $Option(RootAlbum) ]
    }

    if { [ PwdFails ] } { return }

    set NewRootAlbum [ tk_chooseDirectory -initialdir $InitialDir \
				-title "Set Root Album" \
				-mustexist 1 ]
    if { $NewRootAlbum == "" } {
	return
    }
    set Option(RootAlbum) $NewRootAlbum

    # now enable "Open Album" entry
    StateSettings

    GotoAlbum $NewRootAlbum
}

########## Bound: Return Key
# context dependent: 
#    if over a directory, open it
#    if in the name field, Save and select next picture
#    otherwise just do a SaveCaption

proc ReturnKeyAction { } {

    global NameList

    SaveCaptions
    set cursel [ .listframe.list curselection ]

    if { [ llength $cursel ] == 0 } { return }
    set cursel [ lindex $cursel end ]
    set file [ lindex $NameList $cursel ] 
    if { [ file isdirectory $file ] } {
	OpenAlbum
	return
    } elseif { [ IsImageFile $file ] } {
	NextImage 
    }
}
	

########## File menu: Open album
# either we open the selected directory or we show the selection box
proc OpenAlbum { } {
    global Option
    global NameList

    SaveCaptions

    set cursel [ .listframe.list curselection ]

    if { [ llength $cursel ] == 1 } {
	set newdir [ file join $Option(CurrentAlbum) \
			 [ lindex $NameList $cursel ] ]
	if [ file isdirectory $newdir ] {
	    GotoAlbum $newdir
	    return
	}
    }

    if { $Option(CurrentAlbum) != "" } {
	set ini $Option(CurrentAlbum)
    } else {
	set ini $Option(RootAlbum)
    }

    if { [ PwdFails ] } { return }

    set NewCurrentAlbum [ tk_chooseDirectory \
			      -initialdir $ini \
			      -title "Open Album" \
			      -mustexist 1 ]
    if { $NewCurrentAlbum == "" } {
	return
    }
    GotoAlbum $NewCurrentAlbum
}

proc GotoAlbum { name } {
    SafeCall [ list DoGotoAlbum $name ] "Goto Album"
}

proc DoGotoAlbum { name } {
    global Option


    PreviewImg blank
    update

    # remove trailing "/."
    if { [ string range \
	       $name \
	       [ expr [ string length $name ] - 2 ] \
	       end ] == "/." } {
	set name [ string range $name 0 [ expr [ string length $name ] - 3 ] ]
    }
    # get rid of "/.." and the directory name just before that
    if { [ string range \
	       $name \
	       [ expr [ string length $name ] - 3 ] \
	       end ] == "/.." } {
	set name [ string range $name 0 [ expr [ string length $name ] - 4 ] ]
	set nname [ file dirname $name ]
	if { $name == $nname } {
	    set name $Option(RootAlbum)
	} else {
	    set name $nname
	}
    }

    set Option(CurrentAlbum) $name
    WriteProfile
    ShortenCurrentAlbumName
    if { [ catch { cd $Option(CurrentAlbum) } ] } {
	set Option(CurrentAlbum) ""
    }
    ScanDirectory 
    .listframe.list selection set 0
}

########## File menu: New album
proc NewAlbum { } {
    global Option

    SaveCaptions

    if { $Option(CurrentAlbum) == "" } {
	set ini $Option(RootAlbum)
    } else {
	set ini $Option(CurrentAlbum)
    }

    if { [ PwdFails ] } { return }

    set NewAlbum [ tk_chooseDirectory \
			      -initialdir $ini \
			      -title "New Album" \
			      -mustexist 0 ]
    if { $NewAlbum == "" } {
	return
    }

    if { [ string first $Option(RootAlbum) $NewAlbum 0 ] != 0 } {
	tk_dialog .dia "Album Generation Error" "Album \"$NewAlbum\" cannot be generated because it is not below the root album \"$Option(RootAlbum).\""  error 0 OK
	return
    }

    if { [ file isdirectory $NewAlbum ] } {
	tk_dialog .dia "Album Generation Error" "\"$NewAlbum\" is an existing directory and can therefore not be generated as a new album." \
	    error 0 OK
	return
    }

    if { [ file isfile $NewAlbum ] } {
	tk_dialog .dia "Album Generation Error" "\"$NewAlbum\" is an existing file and can therefore not be generated as a new album." error 0 OK
	return
    }

    if { [ catch { file mkdir $NewAlbum } errmes ] } {
	tk_dialog .dia "Directory Creation Error" "Cannot create directory \"$NewAlbum\": \"$errmes\"" error 0 OK
	return
    }
    SetGroupPerms $NewAlbum
    if { [ tk_dialog .dia "Album Generation Success" "The new album \"$NewAlbum\" has been successfully generated. Do you want to go into this new album?" \
	       questhead 0 Yes No ] == 0 } {
	GotoAlbum $NewAlbum
    }
}

proc ShortenCurrentAlbumName { } {

    global Option CurrentAlbumText RootAlbumText

    if { $Option(RootAlbum) == "" } {
	set $Option(CurrentAlbum) "" 
	set RootAlbumText "*** no root album selected ***"
	set CurrentAlbumText "*** no current album selected ***"
	StateSettings
	return
    } else {
	set RootAlbumText $Option(RootAlbum)
    }
    if { $Option(CurrentAlbum) == "" } {
	set CurrentAlbumText "*** no current album selected ***"
	StateSettings
	return
    }
    if { [ string first $Option(RootAlbum) $Option(CurrentAlbum) 0 ] == 0 } {
	set CurrentAlbumText ".[ string range $Option(CurrentAlbum) \
				   [ string length $Option(RootAlbum) ] end ]"
    } else {
	set Option(CurrentAlbum) $Option(RootAlbum)
	set CurrentAlbumText "."
    }
    StateSettings
}

########## File menu: Edit image
proc EditImage { } {
    SafeCall { . config -cursor gumby ; DoEditImage } "Edit Image"
}

proc DoEditImage { } {
    global Option NameList
    global CurrentImage  CurrentIx 
    
    set cursel [ .listframe.list curselection ] 

    SaveCaptions
    
    if { $cursel == "" } {
	tk_dialog .dia "Edit Warning" "No file selected for editing" \
	    warning 0 OK
	return
    }
    if { [ llength $cursel ] > 1 } {
	tk_dialog .dia "Edit Warning" "Cannot edit multiple files. Please select exactly one file for editing." \
	    warning 0 OK
	return
    }

    set file [ lindex $NameList $cursel ]

    if { [ file isdirectory $file ] } { 
	tk_dialog .dia "Edit Warning" "Cannot edit directories with the image editor" \
	    warning 0 OK
	return
    }


    set ImageName [ file join $Option(CurrentAlbum) $file ]
    set CurrDir $Option(CurrentAlbum)
    
    if { $Option(FILE_save_original_when_edit) && \
	     $Option(FILE_save_dir) != "" } {
	set savefile [ file join $Option(FILE_save_dir) $file ]
	if { ! [ file isfile $savefile ] } {
	    if { ! [ file isdirectory    $Option(FILE_save_dir) ] } { 
		file mkdir $Option(FILE_save_dir) 
		SetGroupPerms $Option(FILE_save_dir)
		catch { set fid [ open  [ list \
			[ file join $Option(FILE_save_dir) \
			      $Option(ALBUM_hide_album) ] ] "w"  ] }
		catch { puts $fid "" }
		catch { close $fid }
		SetGroupPerms [ file join $Option(FILE_save_dir) \
			      $Option(ALBUM_hide_album) ]
	    }
	    if { [ catch { file copy $file $savefile } errmes ] } { 
		tk_dialog .dia "Save Error" "Could not save \"$file\" to \"$savefile\": \"$errmes\"" error 0 OK
		return
	    }
	    SetGroupPerms $savefile
	}
    }
    set procid -1
    catch { set procid [ eval exec $Option(SCRIPT_edit) \
			     $Option(FILE_edit_options) \
			     [ list $file ]  & ] }
    while { [ ProcessAlive $procid ] } {
	update
	after 200
    }
    # kill cached previews
    catch { image delete $ImageName  }
    catch { image delete "TN::$ImageName"  }
    # try to remove thumbnail
    catch { eval exec $Option(SCRIPT_jhead) -dt {$ImageName} }
    # now reload picture if still the same:
    set newsel [ .listframe.list curselection ]
    if { $CurrDir == $Option(CurrentAlbum) && $newsel != "" && \
	     [ lindex $newsel end ] == $cursel } {
	PreviewImg blank
	set CurrentImage ""
	set CurrentIx -1
	LoadPicFile
    }
}

########## File menu: Restore image
proc RestoreImage { } {
   
    global Option NameList
    global CurrentImage  CurrentIx 
    global PicName Caption AltTag

    SaveCaptions

    set cursel [ .listframe.list curselection ] 
    if { $cursel == "" } {
	tk_dialog .dia "Restore Warning" "No file selected for restoring" \
	    warning 0 OK
	return
    }
    if { [ llength $cursel ] > 1 } {
	tk_dialog .dia "Restore Warning" "Cannot restore multiple files. Please select exactly one file." \
		warning 0 OK
	return
    }

    set file [ lindex $NameList $cursel ]

    set savefile [ file join $Option(FILE_save_dir) $file ]
    if { ! [ file isfile $savefile ] } {
	tk_dialog .dia "Restore Error" "The original of \"$file\" has not been saved, so it cannot be restored. Perhaps the \"save original\" feature was not  enabled when the file was edited?" error 0 OK
	return
    }
    set response [ tk_dialog .dia "Restore Question" "Do you want to delete the edited image with the original one or do you want to keep a backup copy of the edited version?" questhead 0 Delete "Keep backup" ]
    if { $response == 0 } {
	if { [ MakeTrashBin ] == 0 || [ DeleteOneFile $file ] != 2 } {
	    return
	}
    } else {
	set backup [ MakeFreshName "" $file ]
	catch { file rename $file $backup }
	SetGroupPerms $backup
	set PicName($backup) "$PicName($file) (copy)"
	set Caption($backup) $Caption($file)
	set AltTag($backup) $AltTag($file)
	SaveCaptions
    }
    if { [ catch { file rename -force $savefile $file } errmes ] } {
	tk_dialog .dia "Restore Error" "An eror occured during the restoration of $file: \"$errmes\"" \
		error 0 OK
	return
    }
    SetGroupPerms $file


    # delete old preview 
    catch { image delete [ file join $Option(CurrentAlbum) $file ] }
    catch { image \
	    delete \
	    "TN::[ file join $Option(CurrentAlbum) $file ]" } 
    PreviewImg blank
    set CurrentImage ""
    set CurrentIx -1
    ScanDirectory
    .listframe.list selection set $cursel
    LoadPicFile
}


########## File menu: Rename
# Rename an image (perhaps moving it)
proc RenameFile { } {
    SafeCall  { DoRenameFile  } "Rename"
}

proc DoRenameFile { } {

    global Option
    global PicName Caption AltTag NameList
    global CurrentIx

    SaveCaptions

    set cursel [ .listframe.list curselection ] 
    if { $cursel == "" } {
	tk_dialog .dia "Rename Warning" "No file selected for renaming" \
	    warning 0 OK
	return
    }
    if { [ llength $cursel ] > 1 } {
	tk_dialog .dia "Rename Warning" "Cannot rename multiple files. Please select exactly one file for renaming." \
	    warning 0 OK
	return
    }

    # file to rename:
    set file [ lindex $NameList $cursel ]
    # get new name:

    if { [ PwdFails ] } { return }

    set new [ tk_getSaveFile -initialdir $Option(CurrentAlbum) \
		      -initialfile $file \
		  -title "Rename File" ]
    
    if { $new == "" } { return }
    if { $new == [ file join $Option(CurrentAlbum) $file ] } {
	tk_dialog .dia "Rename Warning" "Why do you want to rename the file to itself? This is a noop." warning 0 OK
	return
    }
	 
    # if new name coincides with already existing, choose a new one
    if { [ file exist $new ] } {
	set newname [ MakeFreshName "" $new ]
	if { $Option(FILE_overwrite_warning) } {
	    set res [ tk_dialog .dia "Rename Warning" "Since the chosen new name \"$new\" clashes with an existing file, we will rename the file to \"$newname\" instead" warning 0 OK Cancel ]
	    if { $res == 1 } { return }
	} 
    } else {
	set newname $new
    }

    # kill previews for old and new file:
    catch { image delete [ file join $Option(CurrentAlbum) $file ] }
    catch { image \
		delete \
		"TN::[ file join $Option(CurrentAlbum) $file ]" } 
    catch { image delete $newname }
    catch { image delete "TN::$newname" }

    # try to rename:
    if { [ catch { eval file rename \
		       [ list $file ] [ list $newname ] } errmes ] } {
	tk_dialog .dia "Rename Error" "An error occured while renaming \"$file\" to \"$newname\": \"$errmes\"" \
	    error 0 OK
	LoadPicFile
	return
    }
    SetGroupPerms $newname

    # the new directory:
    set newdir [ file dirname $newname ]
    # the new base name:
    set newfname [ file tail $newname ]
    # remove file from current window (perhaps comes in again if same dir)
    set NameList [ lreplace $NameList $cursel $cursel ]
    set prefix [ string range [ .listframe.list get $cursel ] 0 3 ]
    .listframe.list delete $cursel
    ClearInfo
    catch { PreviewImg blank }
    if { [ string first $Option(RootAlbum) $newdir 0 ] == 0 } {
	# file has been moved to a dir below the root album
	if { $Option(CurrentAlbum) == $newdir } {
	    # file is not moved, so integrate information back into curr dir
	    set NameList [ linsert $NameList $cursel $newfname ]
	    if { [ file isfile $newname ] } { 
		set PicName($newfname) $PicName($file)
		set Caption($newfname) $Caption($file)
		set AltTag($newfname) $AltTag($file)
	    }
	} else {
	    # file is moved; move caption information as well (if it is a file)
	    AppendToCaptionsFileInDir $newdir $newname $file $newfname 
	}
    }
    SaveCaptions
    ScanDirectory
    .listframe.list selection set $cursel
    set CurrentIx -1
    LoadPicFile
}

proc MakeFreshName { dir name } {

    if { $dir != "" } { 
	set file [ file join $dir $name ]
    } else {
	set file $name
    }
    if { ! [ file exist $file ] } {
	return $file
    } else {
	set root [ file rootname $file ]
	set ext [ file extension $file ]
	if { [ regexp {^(.*)-c([0-9][0-9][0-9])$} \
		   $root dummy head copynum] } {
	    set num [ expr "1$copynum" - 1000 ]
	    set root $head
	} else { 
	    set num 0
	}
	while { [ file exist [ join [ list $root "-c999" $ext ] "" ] ] } {
	    set root [ concat $root "x" ]
	}
	while { [ file exist [ format "%s-c%03d%s" $root $num $ext ] ] } {
	    incr num
	    if { $num >= 1000 } {
		tk_dialog .dia "Internal Error" "Copy count > 999. Should not have happened!" error 0 OK
		return ""
	    }
	}
	return [ format "%s-c%03d%s" $root $num $ext ]
    }
}
	    

proc AppendToCaptionsFileInDir { newdir newfile ofname nfname } {
    global Option
    global PicName
    global Caption
    global AltTag

    if { [ string first $Option(RootAlbum) $newdir 0 ] == 0 && \
	     [ file isfile $newfile ] } {
	if { [ catch { set fid \
			   [ open [ file join \
					$newdir \
					$Option(ALBUM_captions) ] a ] } \
		   errmes ] } {
	    tk_dialog .dia "Append Error" "An error occured when opening \"[ file join $newdir $Option(ALBUM_captions) ]\" in append mode\": \"$errmes\"" \
		error 0 OK
	    catch { close $fid }
	    return 0
	}
	if { [ catch { puts $fid \
			   "$nfname :: $PicName($ofname) :: $Caption($ofname) :: $AltTag($ofname)" } \
		   errmes ] } {
	    tk_dialog .dia "Append Error" "An error occured when appending a line to \"[ file join $newdir $Option(ALBUM_captions) ]\": \"$errmes\"" \
		error 0 OK
	    catch { close $fid }
	    return 0
	}
	catch { close $fid }
	SetGroupPerms [ file join $newdir $Option(ALBUM_captions) ]
    }
    return 1
}


########## File menu: Move (or copy)
# move (or copy) - perhaps more than one - file to new directory
proc MoveOrCopyFile { move conv } {
    if { $move } {
	set op "Move"
    } else {
	set op "Copy"
    }
    if { $conv } {
	set op "Copy & Convert"
    }
    SafeCall [ list DoMoveOrCopyFile $move $conv ] $op
}

proc DoMoveOrCopyFile { move conv } {
    
    global Option
    global FilesCopied
    global NameList
    global Abort
    global ProgressFile
    global ShowProgress
    global ProgressStep

    SaveCaptions

    set Op "Copy"
    set op "copy"
    set oping "copying"
    set fileop "copy"
    set oped "copied"
    if { $move } {
	set Op "Move"
	set op "move"
	set oping "moving"
	set fileop "rename"
	set oped "moved" 
    }
    if { $conv } {
	set Op "Copy & Convert" 
	set op "copy & convert"
	set oping "copying & converting"
	set oped "copied & converted"
    }
    set cursel [ .listframe.list curselection ] 
    if { $cursel == "" } {
	tk_dialog .dia "$Op Warning" "No file selected for $oping" \
	    warning 0 OK
	return
    }


    # get new directoy:
    if { $move } {
	if { $Option(STATE_MoveTarget) == "" } {
	set $Option(STATE_MoveTarget) $Option(CurrentAlbum) 
	}
	set ini $Option(STATE_MoveTarget)
    } elseif { $conv } {
	if { $Option(STATE_CopyAndConvertTarget) == "" } {
	    set $Option(STATE_CopyAndConvertTarget) $Option(CurrentAlbum) 
	}
	set ini $Option(STATE_CopyAndConvertTarget)
    } else {
	if { $Option(STATE_CopyTarget) == "" } {
	    set $Option(STATE_CopyTarget) $Option(CurrentAlbum) 
	}
	set ini $Option(STATE_CopyTarget)
    }

    if { [ PwdFails ] } { return }

    set newdir [ tk_chooseDirectory \
		     -initialdir  $ini \
		     -title "$Op Files" \
		     -mustexist 1 ]
	       
    if { $newdir == "" } { return }

    if { $move } {
	set Option(STATE_MoveTarget) $newdir
    } elseif { $conv } {
	set Option(STATE_CopyAndConvertTarget) $newdir
    } else {
	set Option(STATE_CopyTarget) $newdir
    }

    if { $newdir == $Option(CurrentAlbum) } {
	if { $move || ! $Option(FILE_change_name_when_same) } {
	    tk_dialog .dia "$Op Warning" "It does not make much sense to $op the files into the directory where they already are." warning 0 OK
	    return
	}
    }

    # check whether the operation would result in overwriting other files
    set overwrite  0
    foreach el $cursel {
	set file [ lindex $NameList $el ]
	if { [ file exist [ file join $newdir $file ] ] } { 
	    set overwrite 1
	    break
	}
    }

    if { $overwrite && $Option(FILE_overwrite_warning) } {
	if { ! $Option(FILE_change_name_when_same) } {
	    tk_dialog .dia "$Op Warning" "The $op operation would result in overwriting some files in the target directory leading to an error. Please rename before $oping." warning 0 OK 
	    return
	} elseif { $Option(FILE_change_name_when_same) } {
	    set res [ tk_dialog .dia "Move Warning" "The $op operation would result in a name clash with existing files. For this reason, the new names for the files to be $oped will be generated by appending \"-cNNN\" to the file name. Do you really want to continue?" warning 1 Yes {No, cancel} ]
	    if { $res == 1 } {
		return
	    }
	} 
    }

    # blank everything
    ClearInfo
    PreviewImg blank
    .listframe.list selection clear 0 end

    # Setup progress window if we also do conversion
    if { $conv } {
	set ProgressFile ""
	set ProgressStep [ expr 1000.0 / (1.0* [ llength $cursel ]) ]
	set ShowProgress 0.0
	ShowProgressWindow "Copying & Converting ..." 0 1
    }
    set Abort 0

    # move/copy one by one
    foreach el $cursel {
	after 2
	update
	after 2
	set file [ lindex $NameList $el ]
	if { $conv && $Option(FILE_cc_rename) } {
	    set newfile [ FileNameFromName $file ]
	} else {
	    set newfile $file
	}
	if { $Option(FILE_change_name_when_same) } {
	    set newfile [ MakeFreshName $newdir $newfile ]
	} else {
	    set newfile [ file join $newdir $newfile ]
	}
	set ProgressFile [ file tail $newfile ]
	after 2
	update
	after 2
	if { [ catch { eval file $fileop  \
			   [list $file ] [ list $newfile ] } errmes ] } {
	    tk_dialog .dia "$Op Error" "An error occured while $oping \"$file\" to \"$newfile\": \"$errmes\"" \
		error 0 OK
	    if { $conv } { AbortProcess }
	    break
	}
	SetGroupPerms $newfile
	if { [ file isdirectory $newfile ] } { 
	    set save [ pwd ]
	    cd $newfile
	    SetPermissionsForAllFiles
	    cd $save
	}
	update
	lappend FilesCopied [ file join $Option(CurrentAlbum) $file ]
	# delete old preview (if move), 
	# also delete new (possible) preview image
	if { $move } {
	    catch { image delete [ file join $Option(CurrentAlbum) $file ] }
	    catch { image \
			delete \
			"TN::[ file join $Option(CurrentAlbum) $file ]" } 
	}
	catch { image delete $newfile }
	catch { image delete "TN::$newfile" }

	# mogrify - if required
	if { $conv } {
	    if { $Option(FILE_cc_convert_options) != "" } {
		if { [ catch { eval exec \
				   $Option(SCRIPT_mogrify) \
				   $Option(FILE_cc_convert_options) \
				   [ list $newfile ] } errmes ] } {
		    tk_dialog .dia "Conversion Error" "An error occured while \"mogrifying\"  \"$newfile\": \"$errmes\"" \
			error 0 OK
		    set Abort 1
		}
	    }
	    if { $Option(FILE_cc_jhead_options) != "" } {
		if { [ catch { eval exec \
				   $Option(SCRIPT_jhead) \
				   $Option(FILE_cc_jhead_options) \
				   [ list $newfile ] } errmes ] } {
		    tk_dialog .dia "Conversion Error" "An error occured while \applying jhead to  \"$newfile\": \"$errmes\"" \
			error 0 OK
		    set Abort 1
		}
	    }
	    update
	    if { $Abort } {
		AbortProcess
		break
	    }
	    set ShowProgress [ expr $ShowProgress + $ProgressStep ]
	    update
	}

	# append to other caption file, if file is not a directory and
        # target is an album (i.e. below RootAlbum)
	if { [ AppendToCaptionsFileInDir $newdir $newfile \
		   $file [ file tail $newfile ] ] == 0 } {
	    break
	}
    }
    ScanDirectory
    if { $conv } {
	if { ! $Abort } {
	    set ProgressFile ""
	    set ProgressDir ""
	    set ShowProgress 1000.0
	    .progbox.button.abort configure \
		-text "OK" \
		-command { grab release .progbox; destroy .progbox }
	    .progbox.top configure -text "Copying & Conversion Completed"
	}
    }
    
    if { ! $move } {
	# if we have copied stuff, we just reselect what we
	# had selected before 
	foreach el $cursel {
	    .listframe.list selection set $el
	}
	.listframe.list see [ lindex $cursel end ]
	LoadPicFile
    } else {
	# if it had bee moved somewhere else, we select the file just after
	# the last one that has been moved
	set cur [ expr [ lindex $cursel end ] - [ llength $cursel ] + 1 ]
	.listframe.list selection set $cur
	.listframe.list see $cur
	LoadPicFile
    }	
}

# create a new file name from the name given to the image
proc FileNameFromName { file } {
    global Option
    global PicName

    if { $PicName($file) == "" } {
	return $file
    }
    if { $Option(FILE_cc_remove_funny_chars) } {
	set basename [ string map {"\"" _ "&" _ "/" _ "(" _ ")" _ \
				       "{" _ "}" _ "\[" _ "\]" _ "\\" _ \
				       ":" _ "." _ "|" _ "~" _ "*" _ "?" \
				       _ "`" _ "'" _ "'" _ " " _ } \
			   $PicName($file) ]
    } else {
	set basename $PicName($file)
    }
    return "$basename[ file extension $file ]"
}
	


proc ClearCopyMarks { } {
    global FilesCopied
    global Option

    SaveCaptions
    set FilesCopied { }
    set cursel [ .listframe.list curselection ] 
    ScanDirectory
    
    if { $cursel != "" } {
	foreach el $cursel {
	    .listframe.list selection set $el
	}
	.listframe.list see [ lindex $cursel end ]
	LoadPicFile
    }
}


########## File menu: Delete
proc DeleteFiles { } {
    SafeCall { DoDeleteFiles } "Delete"
}

proc DoDeleteFiles { } {
    global Option
    global NameList

    SaveCaptions

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } { 
	tk_dialog .dia "Delete Warning" "No files for deletion selected" \
		warning 0 OK
	return 
    }

    if { $Option(FILE_delete_warning) || $Option(FILE_dir_delete_warning)} {
	set dircnt 0
	set filcnt 0
	foreach el $cursel {
	    if { [ string first "\[" [ .listframe.list get $el ] 0 ] == 0 } {
		incr dircnt
	    } else {
		incr filcnt
	    }
	}
	if { $filcnt == 0 } {
	    set fils "" 
	} elseif { $filcnt == 1 } {
	    set fils "1 file"
	} else {
	    set fils "$filcnt files"
	}
	if { $dircnt == 0 } {
	    set dirs ""
	} elseif { $dircnt == 1 }  {
	    set dirs "1 directory"
	} else  {
	    set dirs "$dircnt directories"
	}
    }
    if { $Option(FILE_delete_warning) && \
	     ($dircnt == 0 || $Option(FILE_dir_delete_allow) ) } {
	set mes ""
	if { $fils != "" } {
	    set mes "          $fils"
	}
	if { $dirs != "" && $fils != "" } {
	    set mes "$mes and\n"
	}
	if { $dirs != "" } {
	    set  mes "$mes          $dirs"
	}
	set resp [ tk_dialog .dia "Delete Confirmation" "Do you really intend to delete\n\n$mes\n\nnow?" \
		       questhead 1 Yes "No, cancel" ]
	if { $resp == 1 } { return }
    } elseif { $Option(FILE_dir_delete_warning) && \
		   $Option(FILE_dir_delete_allow) && \
		   $dircnt != 0 } {
	set resp [ tk_dialog .dia "Delete Confirmation" "Do you really intend to delete $dirs?" \
		       questhead 1 Yes "No, cancel" ]
	if { $resp == 1 } { return }
    } elseif { ! $Option(FILE_dir_delete_allow) && $dircnt != 0 } {
	tk_dialog .dia "Delete Error" "You cannot delete directories!" \
	    error 0 OK 
	return
    }

    
    ClearInfo
    PreviewImg blank

    # we count how many files we do not delete!
    set notdel 0

    # The trash bin:
    if { $Option(FILE_trash_dir) == "" } {
	set Option(FILE_trash_dir) ".trash"
    }
    set trash [ file join $Option(RootAlbum) $Option(FILE_trash_dir) ]
    
    # generate trash bin - if necessary
    if { [ MakeTrashBin ] == 0 } { return }

    foreach el $cursel {
	set file [ lindex $NameList $el ] 
	set result [ DeleteOneFile $file ]
	if { $result == 1 } { return }
	if { $result == 0 } {
	    incr notdel
	}
    }
    ScanDirectory
    # select the file just after the last one that has been moved/deleted
    set cur [ expr [ lindex $cursel end ] - [ llength $cursel ] + 1 + $notdel ]
    .listframe.list selection set $cur
    .listframe.list see $cur
    LoadPicFile
}

# delete one file
# if successful: 2
# if error: 1
# if unsuccessful, but cont: 0

proc DeleteOneFile { file } {
    global Option 

    # The trash bin:
    if { $Option(FILE_trash_dir) == "" } {
	set Option(FILE_trash_dir) ".trash"
    }
    set trash [ file join $Option(RootAlbum) $Option(FILE_trash_dir) ]
    
    # first check for directories
    if { [ file isdirectory $file ] } {
	if { ! $Option(FILE_dir_delete_allow) } {
	    return [ tk_dialog .dia "Delete Error" "\"$file\" is a directory and you have not given permission to delete directories." \
			 warning 0 "Continue with rest" "Abort" ]
	}
	if { $file == "." } {
	    return [ tk_dialog .dia "Delete Error" "You cannot delete the album that is your \"current album\"!" \
			 warning 0 "Continue with rest" "Abort" ]
	}
	if { $file == ".." } {
	    return [ tk_dialog .dia "Delete Error" "You cannot delete the album that is above your \"current album\"!" \
			 warning 0 "Continue with rest" "Abort" ]
	}
    }
    # Now the first case: We are in the trash bin
    # or we do not use the trash bin. In this case 
    # we simply delete everything.
    if { ! $Option(FILE_use_trash_bin) || \
	    $Option(CurrentAlbum) == $trash } {
	if { [ catch { file delete -force $file } errmes ] } {
	    return [ tk_dialog .dia "Delete Error" "Error while deleting \"$file\": \"$errmess\"" error "OK, continue" "Abort" ]
	}
    } else {
	# now the normal case: instead of deleting we move the files into the
	# trash can
	set newfile [ MakeFreshName $trash $file  ]
	if { [ catch { eval file rename  \
		[list $file ] [ list $newfile ] } errmes ] } {
	    return [ tk_dialog .dia "Delete Error" "An error occured while moving \"$file\" to \"$newfile\": \"$errmes\"" \
			 error 0 "OK, continue" "Abort" ]
	}
	SetGroupPerms $newfile
	
	# append to caption file
	if { [ AppendToCaptionsFileInDir $trash $newfile \
		   $file [ file tail $newfile ] ] == 0 } {
	    return 1
	}
	# delete old preview 
	catch { image delete [ file join $Option(CurrentAlbum) $file ] }
	catch { image \
		    delete \
		    "TN::[ file join $Option(CurrentAlbum) $file ]" } 
    }
    return 2
}

 

proc MakeTrashBin { } {
     
    global Option

    if { $Option(FILE_use_trash_bin) } {
	if { $Option(FILE_trash_dir) == "" } {
	    set Option(FILE_trash_dir) ".trash"
	}
	set trash [ file join $Option(RootAlbum) $Option(FILE_trash_dir) ]
	
	if { ! [ file exist $trash ] } {
	    if { [ catch {file mkdir $trash } errmes ] } {
		tk_dialog .dia "Delete Error" "Cannot generate trash bin \"$trash\": \"$errmes\"" error 0 OK 
		return 0
	    }
	    SetGroupPerms $trash
	}
	if { ! [ file isdirectory $trash ] } {
	    tk_dialog .dia "Delete Error" "Trash bin \"$trash\" is not a directory\"" error 0 OK 
	    return 0
	} 
	set marker [ file join $trash $Option(ALBUM_hide_album) ]
	if { ! [ file exist $marker ] } {
	    catch { set fid [ open $marker w ] }
	    catch { close $fid }
	    SetGroupPerms $marker
	}
	if { ! [ file exist $marker ] } {
	    tk_dialog .dia "Delete Error" "Cannot generate file \"$marker\"" error 0 OK 
	    return 0
	}
    }
    return 1
}


proc CheckTrashBin { } {
    
    global Option

    if { $Option(FILE_use_trash_bin) && \
	     $Option(FILE_trash_dir) != "" && \
	     [ IsNumber $Option(FILE_trash_size) ] && \
	     $Option(FILE_trash_size) != 0 && \
	     $Option(RootAlbum) != "" } {
	set trash [ file join $Option(RootAlbum) $Option(FILE_trash_dir) ]
	if { [ file isdirectory $trash ] } {
	    set entries [ CountAlbumsAndImages $trash 1  1 ]
	    if { [ catch { cd $Option(CurrentAlbum) } ] } {
		if { [ catch { cd $Option(RootAlbum) } ] } {
		    return
		}
	    }
	    if { $entries > $Option(FILE_trash_size) } {
		set res [ tk_dialog \
			      .dia \
			      "Trash Bin Full" \
			      "The trash bin contains $entries entries. Shall it be emptied before we start?" \
			      questhead 0 Yes No ]
		if { $res == 0 } {
		    if { [ catch { file delete -force $trash } errmes ] } {
			tk_dialog .dia "Delete error" "Error while deleting the directory \"$trash\": \"$errmes\"" error 0 OK
			return
		    }
		}
	    }
	}
    }
}	

########## File menu: Undelete (switch to trash directory)
proc UndeleteFiles { } {

    global Option

    SaveCaptions

    set trash ""
    catch { set trash [ file join $Option(RootAlbum) $Option(FILE_trash_dir) ]}
    if { $Option(FILE_use_trash_bin) } {
	if { $Option(FILE_trash_dir) == "" || \
		 $trash == "" || \
		 ! [ file isdirectory $trash ] || \
		 [ glob -nocomplain [ file join $trash "*" ] ] == "" } {
	    tk_dialog .dia "Trash bin information" \
		"Sorry, there are no deleted files in the trash bin" \
		info 0 OK
	    return
	}
	if { [ catch { cd $trash } errmes ] } {
	    tk_dialog .dia "Undelete Error" \
		"Sorry, cannot change into the trash bin: \"$errmes\"" \
		error 0 OK
	    return
	}
	tk_dialog .dia "Undelete Information" "The current album will be set to the trash bin, which contains the most recently deleted files. Copy or move to any album." info 0 OK
	GotoAlbum $trash
    } else {
	tk_dialog .dia "Undelete Information" \
	    "Since the trash bin is not used (see file options), files can not be undeleted." \
	    info 0 OK
    }
}
    
		    
########## File menu: Empty trash bin
proc EmptyTrash { } {
    global Option


    if { $Option(FILE_trash_dir) != ""  && \
	     $Option(RootAlbum) != "" } {
	set trash [ file join $Option(RootAlbum) \
		$Option(FILE_trash_dir) ]
	set size [ expr [ CountAlbumsAndImages $trash 1  1 ] - 1 ]
	cd $Option(CurrentAlbum)
    } else {
	set size 0
    }
    if { $size > 0 } {
	if { $size == 1 } {
	    set are is
	    set images image
	    set them it
	} else {
	    set are are
	    set images images
	    set them them
	}
	set response [ tk_dialog .dia "Trash Bin Question" "There $are $size $images in the trash bin. Do you really want to irrevocably delete $them now?" \
	questhead 1 Yes No ]
	if { $response == 1 } { return }
	catch { file delete -force $trash }
	if { $trash == $Option(CurrentAlbum) } {
	    GotoAlbum $Option(RootAlbum)
	}
    } else {
	tk_dialog .dia "Empty Trash Bin Warning" "The trash bin is empty!" \
		warning 0 OK
    }
}


########## File menu: Exit
proc Exit { } {
    global TempViewerFile ViewerProcesses
    global TempDirs BackgroundProcIds

    SaveCaptions

    KillProcesses $ViewerProcesses
    KillProcesses $BackgroundProcIds
    WriteProfile
    if { $TempViewerFile != "" } {
	catch { file delete $TempViewerFile }
    }
    foreach td $TempDirs { 
	catch { file delete -force $td }
    }
    bind . <Destroy> 
    exit
}

# ------------------------- Option Menu ------------------------------

proc MakeBoolEntry { row col string opt script help } {
    set win ".prefbox.vals.b$opt"
    checkbutton $win -text $string -variable Option($opt) \
	-command $script
    grid $win -row $row -col $col -padx 2 -sticky w
    RegisterHelp $win "" $help
}

proc MakeStringEntry {  row col string opt type vscript help } {
    global PrefEntryLength

    set lwin ".prefbox.vals.l$opt"
    set vwin ".prefbox.vals.v$opt"
    label $lwin -text $string
    entry $vwin -width $PrefEntryLength -relief sunken \
	-textvariable Option($opt) -validate $type \
	-vcmd $vscript 
    grid $lwin -row $row -col $col -padx 2 -sticky w
    grid $vwin -row $row -col [ incr col ] -padx 2 -sticky w
    RegisterHelp $lwin $vwin $help
}

proc MakeOptionEntry {  row col string opt vallist script help } {
    global PrefEntryLength

    set lwin ".prefbox.vals.l$opt"
    set owin ".prefbox.vals.o$opt"
    label $lwin -text $string
    eval tk_optionMenu $owin Option($opt) $vallist
    grid $lwin -row $row -col $col -padx 2 -sticky w
    grid $owin -row $row -col [ incr col ] -padx 2 -sticky w
    if { $script != { } } {
	set lix 0
	foreach x $vallist {
	    $owin.menu entryconfigure $lix -command $script
	    incr lix
	}
    }
    RegisterHelp $lwin $owin $help
}

########## Option menu: General options
proc GeneralOptions { } {
    global Option

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "General Options"

    label $w.top -text "General Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry  0 0 "Read only access to the albums" \
	STATE_ReadOnly \
	{ StateSettings } \
	"If no changes are intended, one can use this option.
In addition, when browsing an album of another user,
this option will be set semi-automatically in order
to avoid the error messages when trying to write
the caption files." 
    checkbutton .prefbox.vals.btsM -text "Strict Motif style" \
	-variable tk_strictMotif 
    grid .prefbox.vals.btsM -row 0 -col 1 -padx 2 -sticky w
    RegisterHelp .prefbox.vals.btsM "" \
	"This option should be enabled if strict Motif look & feel is desired"
    MakeBoolEntry  1 0 "Album functionality enabled" \
	STATE_AlbumEnabled \
	{ StateSettings; ScanDirectory } \
	"Disable if the \"album\" script is not installed (will remove
the \"album\" menu and the capability of storing picture names
and captions)"
    MakeBoolEntry  1 1 "Camera functionality enabled" \
	STATE_CameraEnabled \
	{ StateSettings } \
	"Disable if the \"camera download\" functionality is not needed"
    MakeStringEntry  2 0 "Group ID for shared work on albums:" \
	STATE_GroupName none	{ } \
	"If this field is a non-empty string, it is assumed that it is
a Unix group id which will be the group owner of all files
that are created. Additionally, all created files will have
the \"write group\" permission enabled. This means that a group
of users can all work on the same albums without using the
same user id."
    MakeStringEntry  3 0 "Temporary directory:" \
	SCRIPT_tempdir none { } \
	"Absolute path to a global or private directory that can
be used for temporary storage of image files. Since images
can be fairly large, the temporary directory should be able to
hold a few mega bytes."
    MakeStringEntry  4 0 "\"ps\" command (list active processes):" \
	SCRIPT_ps none { } \
	"Name of the \"ps\" program, the program reporting on the process
status of running processes. Should accept a process id as an 
argument and should return a line with this process id if the 
process is alive."
    MakeStringEntry  5 0 "\"kill\" command (kill process):" \
	SCRIPT_kill none { } \
	"Name of \"kill\" program, the program that is used to terminate
processes. Should accept the process id as an argument."
    MakeStringEntry  6 0 "\"file\" command (determine file type):" \
	SCRIPT_file none { } \
	"Name of \"file\" program, the program used to determine file
types using magic numbers. Should accept file name as argument
and return one line containg the word \"JPEG\" if and only if
it is a JPEG file."
    MakeStringEntry  7 0 "\"album\" program (generate albums):" \
	SCRIPT_album none { } \
       "Name of (and possibly path to) \"album\" script by David Ljung 
Madison. If you do not have it installed yet, check out 
http://marginalhacks.com/Hacks/album/"
    MakeStringEntry 8 0 "\"convert\" (Image Magick) program:" \
	SCRIPT_convert none { } \
	"Name of (and possibly path to) ImageMagick's \"convert\" program.\nIf you have do not have it yet, try http://www.imagemagick.org/."
    MakeStringEntry 9 0 "\"mogrify\" (Image Magick) program:" \
	SCRIPT_mogrify none { } \
	"Name of (and possibly path to) ImageMagick's \"mogrify\" program.\nIf you have do not have it yet, try http://www.imagemagick.org/."
    MakeStringEntry 10 0 "\"display\" (Image Magick) program:" \
	SCRIPT_display_tmp none { } \
	"Name of (and possibly path to) ImageMagick's \"display\" program.\nIf you have do not have it yet, try http://www.imagemagick.org/."
    MakeStringEntry 11 0 "\"display\" (Image Magick) remote call:" \
	SCRIPT_displaynext_tmp none { } \
	"Remote call to \"display,\" i.e., a request to load a\npicture into an existing viewer window"
    MakeStringEntry  12 0 "\"jhead\" program (EXIF Jpeg header):" \
	SCRIPT_jhead none { } \
	"Name of (and possibly path to) the \"jhead\" program by Matthias
Wandel. This program extracts useful digicam information from the 
EXIF headers of JPEG images recorded by digital cameras. See 
http://www.sentex.net/~mwandel/jhead/"
    MakeStringEntry  13 0 "\"jpegtran\" program (lossless Jpeg trans.):" \
	SCRIPT_jtransform none { } \
	"Name of (and possibly path to) the \"jpegtran\" program, 
a program for lossless transformations of JPEG images. 
If you do not have the program installed, check 
http://www.ijg.org/."
    MakeStringEntry  14 0 "\"rdjpegcom\" program (read Jpeg com.):" \
	SCRIPT_read_jc none { } \
	"Name of (and possibly path to) the \"wrjpegcom\" program, 
a program for reading the comment field in a JPEG file. 
If you do not have the program installed, check 
http://www.ijg.org/."
    MakeStringEntry  15 0 "\"wrjpegcom\" program (write Jpeg com.):" \
	SCRIPT_write_jc none { } \
	"Name of (and possibly path to) the \"wrjpegcom\" program, 
a program for writing the comment field in a JPEG file. 
If you do not have the program installed, check 
http://www.ijg.org/."
    MakeStringEntry 16 0 "Image editor:" \
	SCRIPT_edit none { } \
	"Name of (and possibly path to) an image editor.
Used by the \"Edit Image\" command."
    
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

########## Option menu: File options
proc FileOptions { } {

    SaveCaptions

    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "File Operation Options"

    label $w.top -text "File Operation Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry  0 0 "Warning before name clashes occur" \
	FILE_overwrite_warning { } \
	"Give a warning message before an operation is attempted that leads 
to a name clash. Note that no harm can be done because such an 
operation will never overwrite the original file. Either an error 
occurs or the name is changed."
    MakeBoolEntry  0 1 "Change names when they clash" \
	FILE_change_name_when_same { } \
	"Change names before a \"copy\", \"move\", or \"rename\" operation
if it would result in a name clash. If this option is disabled, 
name clashes lead to errors."
    MakeBoolEntry  1 0 "Use Trash Bin (no immediate deletes)" \
	FILE_use_trash_bin { } \
	"Instead of deleting files immediately, they are moved to a trash
bin. If this is option is disabled, no file can be restored after
a delete operation."
    MakeBoolEntry  1 1 "Warning before deletion" \
    	FILE_delete_warning { } \
	"If this option is enabled, then before each\ndeletion the user is asked for confirmation"
    MakeBoolEntry  2 0 "Allow deletion of albums" \
	FILE_dir_delete_allow { } \
	"Allow to delete albums, which can be a bit dangerous"
    MakeBoolEntry  2 1 "Warning before album removal" \
    	FILE_dir_delete_warning { } \
	"If this option is enabled, the user is always asked\nfor confirmation before an album is removed."
    MakeBoolEntry  3 0 "Save original images before editing" \
	FILE_save_original_when_edit { } \
	"Save a copy of the image before attempting to edit it."
    MakeBoolEntry  3 1 "Mark files that have been copied" \
	FILE_mark_copied { } \
	"Mark a file that has been copied with an asterisk. Useful when
selecting a set of files to send off to a printer or something
similar. There is no reason to disable this option"
    MakeBoolEntry  4 0 "Rename files when doing \"Copy & Convert\"" \
	FILE_cc_rename { } \
	"The \"Copy & Convert\" is mainly useful when generating pictures 
to be sent by e-mail. In order to have meaningful file names, 
this option allows to rename the files using the picture names."
    MakeBoolEntry  4 1 "Remove funny chars when renaming" \
	FILE_cc_remove_funny_chars { } \
	"If this option is enabled, then funny characters (such as 
blank, comma, period, etc.) are all replaced by \"_\" when
a file is renamed to its picture name during a \"copy & 
convert\" operation."
    MakeStringEntry  5 0 "Edit options for the \"display\" program:" \
	FILE_edit_options none { }  \
	"If one wants to specify extra options for the call to the 
image editor, here is the right place to do it."
    MakeStringEntry  6 0 "Convert options for \"Copy & Convert:\"" \
	FILE_cc_convert_options none { } \
	"Here one can specify the options for the convert operation
when doing a \"copy & convert\"."
    MakeStringEntry  7 0 "Jhead options for \"Copy & Convert:\"" \
	FILE_cc_jhead_options none { }  \
	"If one wants to remove the EXIF header during a \"copy & convert,\"
here one can specify the appropriate options."
    MakeStringEntry  8 0 "Directory name for originals:" \
	FILE_save_dir none { } \
	"Originals of images to be edited are saved on a per album
base in  a hidden album under the directory name specified here."
    MakeStringEntry  9 0 "Directory name for trash bin:" \
	FILE_trash_dir none { } \
	"The trash bin is a hidden album stored in the toplevel album 
under the name specified here."
    MakeStringEntry 10 0 "Maximal number of images in trash bin:" \
	FILE_trash_size none { CheckNumber %P }  \
	"If the number of files in the trash bin exceeds this number on
program start, the user is asked to empty the trash bin."
    
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

########## Option menu: Transformation options
proc TransformOptions { } {
    global Option

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Transformation Options"

    label $w.top -text "Transformation Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry  0 0 "Try to preserve integrated EXIF thumbnails" \
	TRANS_preserve_tn \
	{ if { $Option(TRANS_preserve_tn) == 1 } \
	      { set Option(TRANS_dont_touch_jpeg_comments) 0} } \
	"When transforming JPEG pictures, try to preserve the EXIF 
thumbnails and record which kind of transformation has been 
applied using a JPEG comment. When giving a preview of the 
image, the transformation can be applied to the EXIF 
thumbnail. Note that this option depends on the next one 
- and actually disables it."
    MakeBoolEntry  1 0 "Do not touch JPEG comments" \
	TRANS_dont_touch_jpeg_comments \
	{ if { $Option(TRANS_dont_touch_jpeg_comments) == 1 } \
	      { set Option(TRANS_preserve_tn) 0} } \
	"If this option is enabled, we try to preseve JPEG comment 
fields as good as we can. In particular, we will not 
attempt to preserve EXIF thumbnails and record the 
necessary transformation."
    MakeBoolEntry  2 0 "Use JPEGTRAN to transform JPEG images" \
	TRANS_UseJpegtran { } \
	"Enable to use the \"jpegtran\" program to do the transformations.
Since the \"jpegtran\" transformations are lossless, there is no 
reason to disable this option, except when the \"jpegtran\" 
program is not installed."
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

########## Option menu: Sub-menu: Thumbnail options
proc AlbumTNOptions { } {
    global Option

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Album Thumbnail & Preview Options"

    label $w.top -text "Album Thumbnail & Preview Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry 0 0  "Use cropping for thumbs"  ALBUM_crop {} \
	"If this option is enabled, all thumbnails will have the 
same size and shape. This is accomplished by cropping 
pictures that do not fit into the size restrictions"
    MakeBoolEntry 0 1  "Use \"sample\" instead of \"geom\""  ALBUM_sample {} \
	"Use the -sample option instead of the -geom option 
when generating the thumbnails and the medium sized 
pictures. It is a bit faster, but has lower quality."
    MakeBoolEntry 1 0 "Delete obsolete thumbnails" ALBUM_cleanup {} \
	"If enabled, album is run with the \"-clean\" option after
each (re-)generation of albums in order to find and delete 
obsolete thumbnails and HTML pages."
    MakeStringEntry  2 0 "Medium preview geometry width" \
	ALBUM_medium_geom_width key { CheckNumber %P } \
	"Maximum width of medium sized images. If no number is
specified, then no medium-sized images are generated."
    MakeStringEntry  3 0 "Medium preview geometry height" \
	ALBUM_medium_geom_height key { CheckNumber %P } \
	"Maximum height of medium sized images. If no number is
specified, then no medium-sized images are generated."
    MakeOptionEntry  4 0 "Medium preview file type" \
	ALBUM_medium_type { "" jpg gif png ppm tiff } { } \
	"Type of the medium sized images. If the empty string is
speficied, the type of the original image is used"
    MakeStringEntry  5 0 "Thumbnail geometry width" \
	ALBUM_tn_geom_width key { CheckNumber %P } \
	"Width of thumbnail images. If it is zero or empty,
no thumbnails will be generated"
    MakeStringEntry  6 0 "Thumbnail geometry height" \
	ALBUM_tn_geom_height key { CheckNumber %P } \
		"Height of thumbnail images. If it is empty or zero,
no thumbnails will be generated"
   MakeOptionEntry  7 0 "Thumbnail file type" \
	ALBUM_tn_type { "" jpg gif png ppm tiff } { } \
	"Type of the thumbnail images. If the empty string is
speficied, the type of the original image is used"
    MakeOptionEntry 8 0 "Forced cropping of thumbnails" \
	ALBUM_Forced_cropping { "" top bottom left right } { } \
	"Here one can specify which part of the image should be 
cropped by default when generating the thumbnails"
    MakeStringEntry 9 0 "Options to send to convert" \
	ALBUM_scale_opts none { } \
	"Here one can specify additional options for the \"convert\" program."

    
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

########## Option menu: Sub-menu: Album appearence options
proc AlbumAAOptions { } {
    global Option
    global PrefEntryLength

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Album Appearance Options"

    label $w.top -text "Album Appearance Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry 0 0  "Generate pages for images"  ALBUM_image_pages {} \
	"For each image (or medium-sized image), one HTML page will be generated"
    MakeBoolEntry 0 1  "Show file sizes"  ALBUM_file_sizes {} \
	"Include file sizes in picture name "
    MakeBoolEntry 1 0  "Show image sizes"  ALBUM_image_sizes {} \
	"Include image size in picture name"
    MakeBoolEntry 1 1  "Convert space to %20 in urls"  ALBUM_fix_urls {} \
	"Translate all spaces in URLs into %20. This is a safe
setting and I do not recommend to disable this option."
    MakeBoolEntry 2 0  "Process only known image types " ALBUM_known_images {}\
	"Ignore all files that are apparently not images. 
There is no reason to disable this option"
    MakeBoolEntry 2 1  "Do not hide dirs with \".\""  ALBUM_all {} \
	"If one also want to see all directories starting with a dot, then
this option should be enabled. Usually, there is no reason to do so."
    MakeStringEntry 3 0 "Number of image columns" \
	ALBUM_columns key { CheckNumber %P } \
	"Specifies the number thumbnails per row"
    MakeStringEntry 4 0 "Name of index file to use in HTML tags" \
	ALBUM_index none { } \
	"Specifies the rootname of the \"index\" file name to be used for
the thumbnail HTML pages. A non-empty string schould be given 
here if one wants to browse the images not going through a web 
server, but using file://... The safest setting is to say \"index\" here."
    MakeStringEntry 5 0 "Body tag for default theme" \
	ALBUM_body none { } \
	"Specifies <body> tag for default theme, e.g., <body color=white>"
    MakeStringEntry 6 0 "File name for captions" \
	ALBUM_captions none { } \
	"This is the file name for the \"captions\" file, in which the
picture names, the captions, and the AltTags are stored
on a per album base"
    MakeStringEntry 7 0 "Name of thumbnail directory" \
	ALBUM_dir none { } \
	"Thumbnails and medium-sized pictures are stored
per album in a directory with the name specified here"
    button $w.vals.button -text "Album theme (browse)" -command ChooseTheme
    RegisterHelp $w.vals.button "" \
	"Browse directories in order to select a theme directory 
(see http://MarginalHacks.com/Hacks/album for varrious
themes). If this string is empty, the default theme is used"
    grid $w.vals.button -col 0 -row 8 -sticky w
    entry $w.vals.tentry -width $PrefEntryLength -relief sunken \
	-textvariable Option(ALBUM_theme) 
    RegisterHelp $w.vals.tentry "" \
	"This should be an absolute path to a directory containing the 
specification of an album theme. If this string is empty, the default 
theme is used (consult http://MarginalHacks.com/Hacks/album for 
other themes)."
    grid $w.vals.tentry -col 1 -row 8 -sticky w


    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}


proc ChooseTheme { } {
    global Option


    if { $Option(ALBUM_theme) == "" } {
	set ini [ glob ~ ]
    } else {
	set ini [ file dirname $Option(ALBUM_theme) ]
    }

    if { [ PwdFails ] } { return }

    set NewTheme [ tk_chooseDirectory -initialdir $ini \
				-title "Choose Theme" \
				-mustexist 1 ]
    if { $NewTheme == "" } { return }
    if { [ glob -nocomplain [ file join $NewTheme "album.th" ] ] == "" } {
	tk_dialog .dia "Theme Error" "Sorry, \"$NewTheme\" is not a theme since it does not contain the file \"album.th\"." error 0 OK
	return
    }
    set Option(ALBUM_theme) $NewTheme
}

########## Option menu: Camera options
proc CameraOptions { } {
    global Option

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Camera Options"

    label $w.top -text "Camera Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry  0 0 "Delete after download" \
	CAMERA_delete_after_download { } \
	"Delete files on the camera that have been\nsuccessfully downloaded to the computer"
    MakeBoolEntry  0 1 "Postprocess after download" \
	CAMERA_postprocess { } \
	"Apply the postprocess command given below to each file\nthat has been downloaded, e.g., to give it a unique\nfile name based on the date & time the picture was taken" 
    MakeBoolEntry  1 0 "Warn if not new album" \
	CAMERA_warn_if_not_new { } \
	"Give a warning when the album the camera images 
are downloaded into is not an empty album"
    MakeBoolEntry  1 1 "Force fresh album before download" \
	CAMERA_always_ask_for_fresh_dir { } \
	"Insist on a fresh album for new pictures from the camera"
    MakeBoolEntry  2 0 "Warn before deleting all files on camera" \
	CAMERA_warn_before_delete { } \
	"Give a warning before the pictures in the camera will be deleted"

    MakeStringEntry  3 0 "Mount command:" \
	SCRIPT_camera_mount none { }  \
	"This is the command for mounting the mass storage USB camera.\nDepending on the setup, it might be necessary to edit the\nfile \"/etc/fstab\" to allow for user level mounting of the camera."
    MakeStringEntry  4 0 "Unmount command:" \
	SCRIPT_camera_umount none { } \
	"This is the unmount command. Again, it might be necessary\nto edit \"/etc/fstab\" before the command can work."
    MakeStringEntry  5 0 "Post processing:" \
	SCRIPT_postprocess none { } \
	"This is the postprocessing command that is applied\nto each image downloaded from the camera"
    MakeStringEntry  6 0 "Camera directory:" \
	SCRIPT_camera_dir none { } \
	"This is the starting point on the camera mass storage device\nfrom which one descends in the directory tree in order to\nfind the image directory on the camera. This starting point\nshould be a non-empty directory after mounting the camera\nand contain either the images or a single directory, from which\nwe then descend."
    
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

########## Option menu: View options
proc ViewOptions { } {
    global Option
    global ViewerSizeList
    global PreviewSizeList

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Viewer, Previewer & Directory Options"

    label $w.top -text "Viewer, Previewer & Directory Options" \
	-justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry  0 0 "Use EXIF thumbnails" \
	VIEW_UseEXIFTN { } \
	"Make use of the EXIF thumbnails in order to give a quick\nglimpse on what is in the image. Since these thumbnails are\nlow-resolution, it does not take much time to display them\nand they also do not take up much processing power.\nBetter quality previews are computed in parallel and replace\nthe EXIF thumbnails as soon as they become available."
    MakeBoolEntry  1 0 "Only one viewer window" \
	VIEW_OnlyOneViewerWindow { } \
	"If this option is enabled, the external viewer\nwindow is reused when a new image is displayed."
    MakeBoolEntry  1 1 "Use small viewer window" \
	VIEW_small { RestartViewer mode } \
	"Restrict the the size of the external\nviewer window. The size can be set below."
    MakeBoolEntry  2 0 "Show directories" \
	VIEW_ShowDirs  ScanDirectory  \
	"If this option is disabled, only files (and no\ndirectories) are displayed in the diretory listing"
    MakeBoolEntry 2 1 "Show dirs starting with \".\"" \
	ALBUM_all ScanDirectory \
	"If enabled, also directories starting with a dot are displayed.\nThis option is actually linked to the \"album\" option with a\nsimilar name."
    MakeBoolEntry  3 0 "Show file extensions" \
	VIEW_ShowFileExt    ScanDirectory  \
	"If disabled, the extension of files are\nnot shown in the directory listing"
    MakeBoolEntry  3 1 "Use picture names in directory list" \
	VIEW_UsePicName    ScanDirectory \
	"Instead of the file names, the picture names\n(if given) are used in the directory listing"
    MakeBoolEntry  4 0 "Show only image files" \
	ALBUM_known_images ScanDirectory \
	"Ignore all non-image files, such as HTML files or text files. This\noption is also linked to the \"album\" option with a similar name."
    MakeBoolEntry  4 1 "Ignore \"album\" aux dirs and files" \
	VIEW_IgnoreAlbumFiles ScanDirectory \
	"Do not show directories and files that are generated\nby \"album\" such as \"tn\", \"captions.txt\" etc."
    MakeOptionEntry  5 0 "Directory list height:" \
	VIEW_dir_height { 4 6 8 10 12 14 16 18 20 22 } \
	{ .listframe.list configure -height $Option(VIEW_dir_height) } \
	"Minimum number of rows for directory listing. The actual\nnumber of rows depends on the size of the other window\ncomponents"
    MakeOptionEntry 6 0 "Directory list width:" \
	VIEW_dir_width { 10 15 20 25 30 35 40 45 50 } \
	{ .listframe.list configure -width $Option(VIEW_dir_width) } \
	"Width of directory listing in number of characters"
    MakeOptionEntry  7 0 "Preview file type:" \
	VIEW_preview_type { gif ppm }   { } \
	"Image type for previews. Since \"ppm\" images can\nbe much faster generated than \"gif\" images, it is\nnot recommended to change this option to \"gif.\""
    MakeOptionEntry  8 0 "Viewer window size:" \
	VIEW_viewer_size $ViewerSizeList   { RestartViewer size } \
	"Maximum width and height of the external viewer window,\nprovided one uses the size restricted window" 
    MakeOptionEntry  9 0 "Preview size:" \
	VIEW_prevsize $PreviewSizeList   { ResizePreview } \
	"This is the width and height for the preview area"

    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}


########## Option menu: JpegInfo options
proc JpegInfoOptions { } {
    global Option

    SaveCaptions
    set w .prefbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "JpegInfo Options"

    label $w.top -text "JpegInfo Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeInfoEntryOption 0 0 "Show file name" EXIF_File_Name 
    MakeInfoEntryOption 0 1 "Show file size"  EXIF_File_Size
    MakeInfoEntryOption 1 0 "Show file date"  EXIF_File_Date
    MakeInfoEntryOption 1 1 "Show camera"  EXIF_Camera
    MakeInfoEntryOption 2 0 "Show picture date"  EXIF_Pic_Date
    MakeInfoEntryOption 2 1 "Show resolution"  EXIF_Resolution
    MakeInfoEntryOption 3 0 "Show color/b&w"  EXIF_Color
    MakeInfoEntryOption 3 1 "Show flash"  EXIF_Flash
    MakeInfoEntryOption 4 0 "Show focal length"  EXIF_Focal
    MakeInfoEntryOption 4 1 "Show CCD width"  EXIF_CCD
    MakeInfoEntryOption 5 0 "Show exposure time"  EXIF_ExposureTime
    MakeInfoEntryOption 5 1 "Show exposure"  EXIF_ExposureProg
    MakeInfoEntryOption 6 0 "Show aperture"  EXIF_Aperture
    MakeInfoEntryOption 6 1 "Show distance"  EXIF_Dist
    MakeInfoEntryOption 7 0 "Show ISO equiv."  EXIF_ISO
    MakeInfoEntryOption 7 1 "Show whitebalance"  EXIF_Whiteb
    MakeInfoEntryOption 8 0 "Show metering"  EXIF_Meter
    MakeInfoEntryOption 8 1 "Show Jpeg quality"  EXIF_JQual
    MakeInfoEntryOption 9 0 "Show Jpeg process"  EXIF_JProcess
    MakeInfoEntryOption 9 1 "Show orientation"  EXIF_Orientation
    MakeInfoEntryOption 10 0 "Show Jpeg comment" EXIF_JComment

    MakeBoolEntry 12 0 "Warning before removal of EXIF info" \
	EXIF_RemovalWarning { } \
	"Give a warning before EXIF information is removed from a file"
    MakeStringEntry 13 0 "Format string for picture date rename:" \
	JPEG_rename none { } \
	"Format string used for renaming image files to\ntheir picture date (see \"jhead\" documentation)"
    
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; destroy .prefbox }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

proc MakeInfoEntryOption { row col str opt } {
    checkbutton .prefbox.vals.v$opt -text $str \
	-variable Option($opt) \
	-command [ concat ChangeEntry $opt ]
    grid .prefbox.vals.v$opt -row $row -col $col -padx 2 -sticky w
}

proc ChangeEntry { entryname } {
    global Option
    global InfoRow

    set valname "v$entryname"
    set labname "l$entryname"
    if { $Option($entryname) == 0 } {
	catch { grid forget .info.$valname }
	catch { grid forget .info.$labname }
    } else {
	catch { grid .info.$labname -row $InfoRow($entryname) \
		    -column 0 -sticky w }
	catch { grid .info.$valname -row  $InfoRow($entryname) \
		    -column 1 -padx 2 }
    }
}

# ------------------------- Transformation Menu --------------------------

proc RotateImage { deg } {
    RotateOrFlipImage   $deg ""
}

proc FlipImage { dir } {
    RotateOrFlipImage   0 $dir 
}

proc RotateOrFlipImage { deg dir } {
    SafeCall [ list DoRotateOrFlipImage $deg $dir ] "Transform"
}

proc DoRotateOrFlipImage { deg dir } {
    global Option
    global CurrentImage
    global CurrentIx

    set files [ SelectedFiles ]
    if { $files == "" } {
	tk_dialog .dia "Transformation Warning" "No files have been selected" warning 0 OK
	return
    }

    PreviewImg blank
    set CurrentImage ""
    set CurrentIx ""
    update

    foreach file $files {
	update
	catch { image  delete [ file join $Option(CurrentAlbum) $file ]}
	catch { image  delete "TN::[ file join $Option(CurrentAlbum) $file ]" }
	if { [ RotateOrFlipOneImage $deg $dir $file ] == 0 } {
	    LoadPicFile
	    return
	}
    }
    LoadPicFile
}


proc RotateOrFlipOneImage { deg dir file } {


    if { [ IsJpeg $file ] } {
	return [ RotateOrFlipJpegFile $file $deg $dir ]
    } else { 
	return [ RotateOrFlipGeneralImage $file $deg $dir ]
    }
}


# the non-JPEG case (well, might be JPEG but do not use jpegtran)
proc  RotateOrFlipGeneralImage {  file deg dir  } {
    global Option
    
    if { $deg == 0 } {
	set opt [ GenIMOpt [ concat FLIP $dir ] ]
    } else {
	set opt [ concat -rotate $deg ]
    }
    if { [ catch { eval exec $Option(SCRIPT_mogrify) \
		       $opt { $file } } errmes ] } {
	tk_dialog .dia "Mogrify Execution Error" "Error while executing \"$Option(SCRIPT_mogrify) [join $opt] $file\": \"$errmes\"" error 0 OK
	return 0
    }
    return 1
}


proc RotateOrFlipJpegFile { file deg dir } {
    global Option

    set ext [ file extension $file ]
    if { [ string index $ext 0 ] == "." } {
	set ext [ string range $ext 1 end ] 
    }
    set tfile [ MakeTempName $ext ]
    if { $deg == 0 } {
	if { $dir == "H" } {
	    set opt { -flip horizontal }
	} else {
	    set opt { -flip vertical }
	}
    } else {
	set opt [ concat -rotate $deg ]
    }
    if { $Option(TRANS_UseJpegtran) } {
	set succ [ DoJpegtran $file $opt $tfile ]
	if { $succ == 0 } {
	    catch { file delete $tfile } 
	    return 0
	}
    } else {
	set fail [ catch { file copy -force $file $tfile } ] 
	SetGroupPerms $tfile
	set succ [ RotateOrFlipGeneralImage $tfile $deg $dir ]
	if { $fail || $succ == 0 } {
	    return 0
	}
    }
    if { $Option(TRANS_preserve_tn) && \
	     $Option(TRANS_dont_touch_jpeg_comments) == 0 } {
	# if we want to preserve thumbnails and are allowed 
	# to mess with the jpeg comments:
	# read comment, interpret and write a transformation comment
	set oldop [ GetTransOp $tfile ]
	if { [ JpegHasThumbnail $tfile ] } {
	    # makes only sense if there is a thumbnail!
	    if { $oldop != "" } {
		# found a comment from TKAlbum  about earlier transformations
		if { $oldop == "ERROR" || \
			 ( [ lindex $oldop 0 ] == "FLIP" && \
			       [ lindex $oldop 1 ] != $dir ) || \
			 ( [ lindex $oldop 0 ] == "ROTATE" && $deg == 0 ) } {
		    # forget about it, remove comment and thumbnail
		    if { [ RemoveFromJpeg $tfile { -dc -dt } ] == 0 } {
			catch { file delete $tfile }
			return 0
		    }
		} elseif { [ lindex $oldop 0 ] == "ROTATE" } {
		    # ROTATE is ok, compute resulting rotation
		    set newdeg [ expr $deg + [ lindex $oldop 1 ] ]
		    if { $newdeg >= 360 } {
			set newdeg [ expr $newdeg - 360 ]
		    }
		    if { $newdeg == 0 } {
			# Resulting rotation is zero: delete comments
			if { [ RemoveFromJpeg $tfile { -dc  } ] == 0 } {
			    catch { file delete $tfile }
			    return 0
			}
		    } else {
			# write new rotation as comment
			if { [ WriteJpegComment \
				   "TkaTrans: ROTATE $newdeg" \
				   $tfile  ] == 0 }  {
			    catch { file delete $tfile }
			    return 0
			}
		    }
		} elseif { [ lindex $oldop 0 ] == "FLIP" } {
		    # Transformation inverts the previous one
		    # just delete comment!
		    if { [ RemoveFromJpeg $tfile { -dc  } ] == 0 } {
			catch { file delete $tfile }
			return 0
		    }
		} else {
		    # Transformation should either be FLIP, ERROR, or ROTATE
		    tkalbum .dia "Comment error" "Illegal TKAlbum comment in Jpeg file" error 0 OK 
		    catch { file delete $tfile }
		    return 0
		}
	    } else { 
		# No previous comment. Write transformation to file
		if { $deg != 0 } {
		    set transinfo "ROTATE $deg" 
		} else {
		    set transinfo "FLIP $dir"
		}
		if { [ WriteJpegComment "TkaTrans: $transinfo" \
			   $tfile  ] == 0 }  {
		    catch { file delete $tfile }
		    return 0
		}
	    }
	} else {
	    # there is no thumbnail
	    # remove TkaTrans comment if there is one
	    if { $oldop != "" } {
		catch { RemoveFromJpeg $tfile { -dc  } }
	    }
	}
    } else {
	# JPEG case, but  we do not insist on keeping the thumbnail 
	# or do not allow to mess with the Jpeg comments
	# simply remove EXIF thumbnail
	if { [ RemoveFromJpeg $tfile { -dt  } ] == 0 } {
	    catch { file delete $tfile }
	    return 0
	}
    }
    # now we can copy back to the original file
    if { [ catch { file copy -force $tfile $file } errmes ] } {
	tk_dialog .dia "Copy Error" "Error while copying \"$tfile\" to \"$file\": \"$errmes\"" error 0 OK
	catch { file delete $tfile }
	return 0
    }
    SetGroupPerms $file
    return 1
}

proc JpegHasThumbnail { file } {
    global Option

    set res ""
    if { [ catch { set res \
		       [ eval exec $Option(SCRIPT_jhead) \
			     -v { $file } ] } errmes ] } {
	tk_dialog .dia "Jhead Execution Error" "Error while executing \"$Option(SCRIPT_jhead) -v $file\": \"$errmes\"" error 0 OK
	return 0
    }
    return [ string match "*Thumbnail size:*" $res ]
}
	

proc DoJpegtran { file cmd target } {
    global Option

    if { $target == "" } {
	set newfile [ MakeTempName jpg ] 
	set temp $newfile
    } else {
	set newfile $target
	set temp ""
    }
    set fail [ catch { eval exec $Option(SCRIPT_jtransform) \
			       -copy all $cmd \
			   { $file } > { $newfile } } errmes ]
    if { $fail } {
	tk_dialog .dia "Jpegtran Execution Error" "Error while executing \"$Option(SCRIPT_jtransform) -copy all [ join $cmd ] $file > $newfile\": \"$errmes\"" error 0 OK
	if { $temp != "" } {
	    catch { file delete $temp }
	}
	return 0
    }
    if { $temp != "" } {
	if { [ catch { file copy -force $temp $file } ] } {
	    tk_dialog .dia "Copy Error" "Error while copying \"$tfile\" to \"$file\": \"$errmes\"" error 0 OK
	    catch { file delete $temp }
	    return 0
	}
	SetGroupPerms $file
	catch { file delete $temp }
	return 1
    }
    return 1 
}

proc GetTransOp { file } {
    global Option

    set com  ""
    set fail [ catch { set com [ eval exec \
					     $Option(SCRIPT_read_jc) \
				     { $file } ] } errmes ]
    if { $fail } {
	tk_dialog .dia "Rdjpgcom Execution Error" "Error while executing \"$Option(SCRIPT_read_jc) $TempFile\": \"$errmes\"" error 0 OK
	return "ERROR"
    }
    return [ InterpretJpegCom $com ]
}

proc InterpretJpegCom { com } {
    
    if { $com == "" || \
	     [ string match "TkaTrans: *" $com ] == 0 } {
	    return ""
    }
    if { [ regexp \
	       "^TkaTrans: (ROTATE|FLIP) (H|V|90|180|270)$"  \
	       $com match op arg  ] == 0 } {
	return "ERROR"
    } else {
	return [ concat $op $arg ]
    }
}

proc RemoveFromJpeg { file sw } {
    global Option
    
    if { [ catch { eval exec $Option(SCRIPT_jhead) $sw { $file } } errmes ] } {
	tk_dialog .dia "Jhead Execution Error" "Error while executing \"$Option(SCRIPT_jhead) [ join $sw ] $file\": \"$errmes\"" error 0 OK
	return 0
    }
    return 1
}

proc WriteJpegComment { com file } {
    global Option
    
    set temp [ MakeTempName jpg ]
    if { [ catch { eval exec $Option(SCRIPT_write_jc) \
		       -replace -comment { \"$com\" } { $file } > { $temp } } errmes ] } {
	tk_dialog .dia "Jhead Execution Error" "Error while executing \"$Option(SCRIPT_write_jc) [ join $sw ] $file >  $temp \": \"$errmes\"" error 0 OK
	catch { file delete $temp }
	return 0
    }
    if { [ catch { file copy -force $temp $file } ] } {
	tk_dialog .dia "Copy Error" "Error while copying \"$temp\" to \"$file\": \"$errmes\"" error 0 OK
	catch { file delete $temp }
	return 0
    }
    SetGroupPerms $file
    catch { file delete $temp }
    return 1
}



# ------------------------- Album Menu -----------------------------------
########## Album menu: preview cropping

proc PreviewCrop { dir } { 
    global Option
    global NameList PicName Caption AltTag
    global CurrentIx
    global CaptionsEdited

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } { 
	tk_dialog .dia "Cropping Warning" \
	    "No image for cropping selected" \
	    warning 0 OK 
	return
    }  elseif { [ llength $cursel ] > 1 } {
	tk_dialog .dia "Cropping Warning" \
	    "Can only crop one image at a time. Please select exactly one file." \
	    warning 0 OK 
	return
    } 
    set file [ lindex $NameList $cursel ]
    if { ! [ IsImageFile $file ] } {
	tk_dialog .dia "Cropping Warning" \
	    "Can only crop images. \"file\" is not an image." \
	    warning 0 OK 
	return
    }
    set extension [file extension $file]
    set rootname [file rootname $file] 
    while { [file extension $rootname] != "" } {
	if { [ string first ".CROP" [file extension $rootname] 0 ] == 0 } {
	    set rootname [file rootname $rootname]
	} else {
	    break
	}
    }
    if { $dir == "center" } {
	set newname "$rootname$extension"
    } else {
	set newname "$rootname.CROP$dir$extension"
    }
    if { [ string compare $newname $file ] == 0 } {
	return
    }
    if { [ catch { file rename $file $newname } errmes ] } {
	tk_dialog .dia "Cropping Error" \
	    "Cannot rename \"$file\" to \"$newname\":\"$errmes\"" \
	    error 0 OK 
	return
    }
    set NameList [ lreplace $NameList $cursel $cursel $newname ]
    set PicName($newname) $PicName($file)
    set Caption($newname) $Caption($file)
    set AltTag($newname) $AltTag($file)
    set CaptionsEdited 1
    SaveCaptions
    ScanDirectory
    .listframe.list selection set $cursel
    set CurrentIx -1
    LoadPicFile
}
    

########## Album menu: edit header & footer
proc EditHeaderAndFooter { } {
    global OK
    global HeaderHeight
    global HeaderWidth
    global HeaderText
    global FooterText
    global Option

    SaveCaptions

    if { $Option(CurrentAlbum) == "" } {
	tk_dialog .dia "Edit Warning" "Sorry, cannot edit header & footer because no album has been selected" warning 0 OK
	return
    }

    set OK 0
    set ftext ""
    set htext ""

    set w .editbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Edit Header & Footer"

    label $w.top -text "Edit Header & Footer" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.edit
    pack $w.edit -side top
    
    label $w.edit.hlab -text "Header:"
    grid $w.edit.hlab -row 0 -col 0 -sticky nw 
    label $w.edit.flab -text "Footer:"
    grid $w.edit.flab -row 1 -col 0 -sticky nw 
    frame $w.edit.htext 
    text  $w.edit.htext.text -relief sunken -bd 2 \
	-yscrollcommand "$w.edit.htext.scroll set" -setgrid 1 \
	-height $HeaderHeight -width $HeaderWidth -wrap word
    scrollbar $w.edit.htext.scroll -command "$.edit.htext.text yview" \
	-takefocus 0
    pack $w.edit.htext.scroll -side right -fill y
    pack $w.edit.htext.text -expand yes -fill both
    grid $w.edit.htext -row 0 -col 1 -sticky we
    frame $w.edit.ftext 
    text  $w.edit.ftext.text -relief sunken -bd 2 \
	-yscrollcommand "$w.edit.ftext.scroll set" -setgrid 1 \
	-height $HeaderHeight -width $HeaderWidth -wrap word
    scrollbar $w.edit.ftext.scroll -command "$.edit.ftext.text yview" \
	-takefocus 0
    pack $w.edit.ftext.scroll -side right -fill y
    pack $w.edit.ftext.text -expand yes -fill both
    grid $w.edit.ftext -row 1 -col 1 -sticky we
    
    frame $w.button
    button $w.button.ok -text OK \
	-command  { set OK 1; \
			set HeaderText [ .editbox.edit.htext.text get 1.0 end ]; \
			set FooterText [ .editbox.edit.ftext.text get 1.0 end ]; \
			grab release .editbox; \
			destroy .editbox }
    button $w.button.cancel -text Cancel \
	-command  { set OK 0; grab release .editbox; destroy .editbox }
    
    pack $w.button.ok -side left -expand 0 
    pack $w.button.cancel -side right -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m

    PositionWindowCenter $w

    if { ! [ ReadHF ALBUM_header $w.edit.htext.text ] || \
	     ! [ ReadHF ALBUM_footer $w.edit.ftext.text ] }  {
	grab release .editbox; destroy .editbox
	return
    }

    tkwait window $w

    if { $OK } {
	WriteHF ALBUM_header $HeaderText
	WriteHF ALBUM_footer $FooterText
    }
}

proc ReadHF { opt win } {
    global Option

    set file $Option($opt)
    if { ! [ file exist $file ] } {
	return 1
    } elseif { [ catch { set fid [ open $file r] } errmes ] } {
	tk_dialog .dia "Open Error" "Cannot open $file for reading: \"$errmes\"" error 0 OK
	return 0
    }
    if { [ catch { $win insert end [ read -nonewline $fid ] } errmes ] } {
	tk_dialog .dia "Read Error" "Cannot read from $file: \"$errmes\"" \
	    error 0 OK
	return 0
    }
    catch { close $fid }
    return 1
}

proc WriteHF { opt str } {
    global Option

    set file $Option($opt)
    set str  [ string trim $str ]
    if { $str == "" } {
	if { [ file exist $file ] } {
	    if { [ catch { file delete -force $file  } errmes ] } {
		tk_dialog .dia "Delete Error" "Cannot delete $file: \"$errmes\"" error 0 OK
		return 0
	    } else {
		return 1
	    } 
	} else {
	    return 1
	}
    } elseif { [ catch { set fid [ open $file w] } errmes ] } {
	tk_dialog .dia "Open Error" "Cannot open $file for writing: \"$errmes\"" error 0 OK
	return 0
    }
     if { [ catch { puts $fid $str  } errmes ] } {
 	tk_dialog .dia "Write Error" "Cannot write to $file: \"$errmes\"" \
 	    error 0 OK
 	return 0
     }
    if { [ catch { close $fid } errmes ] } {
	tk_dialog .dia "Close Error" "Cannot close $file after writing: \"$errmes\"" \
	    error 0 OK
	return 0
    }
    SetGroupPerms $file
    return 1
}


########## Album menu: all album generation menus 
proc GenAlbum { local force } {
    global Option
    global OptionName 
    global BinaryAlbumOptions StringAlbumOptions 
    global SpecialAlbumOptions NoAlbumOptions
    global Abort
    global ProgressDir
    global ProgressFile
    global ShowProgress
    global ProgressStep

    SaveCaptions
    set allprogops [ concat $BinaryAlbumOptions $StringAlbumOptions \
			$SpecialAlbumOptions $NoAlbumOptions ]
    set allalbnames [ array names OptionName]
    set allalbops [ array names Option "ALBUM_*" ]

    # check whether all "album" options have been dealt with
    # (the only ones, we deal not explicitely with are "clean" and "force")
    foreach { oplistname oplist } [ list "program options" $allprogops \
					"option names" $allalbnames \
					"TKAlbum internal options" \
					$allalbops ] {
	foreach { othername other } [ list "program options" $allprogops \
					"option names" $allalbnames \
					"TKAlbum internal options" \
					  $allalbops ] {
	    if { $oplistname != $othername } {
		foreach el $oplist {
		    if { [ lsearch $other $el ] == -1 } {
			if { [ tk_dialog .dia "Internal Error" "The album option \"$el\" appears in $oplistname but not in $othername. This may lead to problems. Please consult program author or other people who can help." \
				 warning 0 OK Abort ] == 1 } {
				     return
			}
		    }
		}
	    }
	}
    }


    # generate call to program
    set albumcall $Option(SCRIPT_album)

    # first the binary options
    foreach el $BinaryAlbumOptions {
	if { $Option($el) } {
	    append albumcall " -$OptionName($el)" 
	} else {
	    append albumcall " -no_$OptionName($el)"
	}
    }

    # now the string (and numerical args)
    foreach el $StringAlbumOptions {
	if { $Option($el) != "" } {
	    append albumcall " -$OptionName($el) {$Option($el)}" 
	}
    }

    # now the specials
    
    # first append the debug option "-d" which we use to give progress reports 
    append albumcall " -d"

    if { ! [ IsNumber $Option(ALBUM_tn_geom_width) ] || \
	     ! [ IsNumber $Option(ALBUM_tn_geom_height) ] } {
	append albumcall "-geometry 0x0"
    } else { 
	append albumcall \
	    " -geometry $Option(ALBUM_tn_geom_width)x$Option(ALBUM_tn_geom_height)"
    }
    if {  [ IsNumber $Option(ALBUM_medium_geom_width) ] && \
	     [ IsNumber $Option(ALBUM_medium_geom_height) ] } {
		 append albumcall " -medium $Option(ALBUM_medium_geom_width)x$Option(ALBUM_medium_geom_height)"
    }
    if { $Option(ALBUM_theme) == "" } {
	append albumcall " -no_theme"
    }

    if { $Option(ALBUM_scale_opts) != "" } {
	append albumcall " --scale_opts $Option(ALBUM_scale_opts) --"
    }

    set gencall $albumcall

    if { $force } {
	append gencall " -force"
    }

    append gencall " 2>@stdout" 

    set cleancall $albumcall
    append cleancall " -clean 2>@stdout" 

    # name of index file
    if { $Option(ALBUM_index) == "" } {
	set index "index.html"
    } else {
	set index "$Option(ALBUM_index).html"
    }

    # find out where to start 
    # if local, find the next album upwards that contains a "tn" dir
    cd $Option(CurrentAlbum)
    if { $local } {
	# depends on whether we generated a album before in this
	# directory. If so, it is enough when we stay here
	set startdir $Option(CurrentAlbum)
	while { $startdir != $Option(RootAlbum) } {
	    set dirlist [ concat [ glob -nocomplain -type f * ] \
			      [ glob -nocomplain -type f .* ] ]
	    if { [ lsearch $dirlist $index ] != -1 } {
		# album has been here before
		break
	    } else {
		# fresh one, goto next higher dir - if possible
		if { $startdir == [ file dirname $startdir ] } {
		    break 
		}
		set startdir [ file dirname $startdir ]
		cd $startdir
	    }
	}
    } else {
	set startdir $Option(RootAlbum)
		if { [ file exist [ file join $startdir $index ] ] } {
	    if { [ catch { file delete -force [ file join $startdir $index ]} \
		       errmes ] } {
		if { [ tk_dialog .dia \
			   "Album Generation Error" \
			   "Could not delete [ file join $startdir $index ] before starting global regeneration: \"$errmes\"." \
			   error 0 Continue Abort
		      ] == 1 } {
		    return
		}
	    }
	}
    }
    set Abort 0
    set ProgressDir ""
    set ProgressFile ""
    set ShowProgress 0.0

    if { $local } {
	set albums "album"
    } else {
	set albums "albums"
    }
    ShowProgressWindow "Generating $albums" 1 1


    set nritems [ CountAlbumsAndImages $startdir 1 ] 
    cd $startdir
    set ProgressStep [ expr 1000.0 / (1.0 * ($nritems+1.0)) ]
    MakeAlbumCall $gencall $nritems

    if { ! $Abort && $Option(ALBUM_cleanup) } {
	.progbox.top configure -text "Generating $albums (cleaning up)"
	set ProgressFile ""
	set ProgressDir ""
	set ShowProgress 0.0
	cd $startdir
	set verbose " -d"
	MakeAlbumCall $cleancall $nritems
    }
    if { ! $Abort && $Option(STATE_GroupName) != "" } {
	.progbox.top configure -text "Generating $albums (Permission Setting)"
	set ProgressFile ""
	set ProgressDir ""
	set ShowProgress 0.0
	cd $startdir
	SetPermissionsForAllFiles
    }
    if { ! $Abort } {
	set ProgressFile ""
	set ProgressDir ""
	set ShowProgress 1000.0
	.progbox.button.abort configure -text "OK"
	.progbox.top configure -text "Album generation completed"
    }
    GotoAlbum $Option(CurrentAlbum)
}


   
proc MakeAlbumCall { call items }  {
    global Option
    global ShowProgress ProgressDir ProgressFile ProgressStep
    global Abort

    set res [ catch { set proc \
			  [ open "| $call"] } \
		  ErrorMessage ] 
    if { $res != 0 } {
	tk_dialog .dia "Execution Error" \
	    "The call of \"$call\" led to an error. Got message: \"$ErrorMessage\". Please check the 'general option' dialog!" \
		error 0 Ok
	return 0
    }	
    catch { set pids [ pid $proc ] } 
    fconfigure $proc -blocking 0 

    set running 1
    set seenitems 0 
    while { $running } {
	set newline ""
	update 
	after 100
	if { $Abort } {
	    KillProcesses $pids
	    AbortProcess
	}
	if { [ eof $proc ] } { set running 0 }
	while { ! [ eof $proc ] } {
	    set newline [ gets $proc ]
	    if { [ string match "Album: *" $newline ] } {
		incr seenitems
		set ShowProgress [ expr $ShowProgress + $ProgressStep ]
		set ProgressDir [ string range $newline 7 end ]
		update
	    } 
	    if { [ string match "IMAGE: *" $newline ] } {
		incr seenitems
		set ShowProgress [ expr $ShowProgress + $ProgressStep ]
		set ProgressFile [ file tail [ string range $newline 7 end ] ]
		update
	    } 
	    if { [ fblocked $proc ] } {
		break
	    }
	}
    }
    while { ! [ eof $proc ] && ! [ fblocked $proc ]} {
	gets $proc newline
    }
    if { ! $Abort && $seenitems == 0 && $items != 0 } {
	# probably some problem when making the call
	tk_dialog .dia "Album Error" "The \"album\" program did not process anything. Either the program has not been installed or the call was somehow flawed: \n $call ." error 0 OK
	AbortProcess
    }

}

# go down the directory tree and set the right attributes - if possible 
proc SetPermissionsForAllFiles { } {
    global Option
    global ShowProgress
    global ProgressFile
    global ProgressStep

    set filelist [ concat [ glob -nocomplain * ] [ glob -nocomplain .* ] ]
    
    foreach el $filelist {
	if { $el != "." && $el != ".." } {
	    set ProgressFile $el
	    catch { set ShowProgress [ expr  $ShowProgress + $ProgressStep ] }
	    SetGroupPerms $el
	}
    }
    foreach el $filelist {
	set savedir [ pwd ]
	if { $el != "." && $el != ".." && [ file isdirectory $el ] } {
	    if { [ catch { cd $el } errmes ] } {
		tk_dialog .dia "Album Generation Error" "Cannot change into directory $el: \"$errmes\"." error 0 OK
	    }
	    SetPermissionsForAllFiles
	}
	cd $savedir
    }
}    

# count all directories starting at a particualr startdir
# of course, ignore those that album won't touch
proc CountAlbumsAndImages { start alsoimages { alsohidden 0 } } {
    global Option

    set nr 1
    if { [ catch { cd $start } ] } {
	return 0
    }
    set start [ pwd ]
    if { ! $alsohidden } { 
	if { [ glob -nocomplain $Option(ALBUM_hide_album) ] != "" } {
	    return 0
	}
    }
    if { [ glob -nocomplain $Option(ALBUM_no_album) ] != "" } {
	return 1
    }
    if { $alsoimages } {
	set filelist [ concat [ glob -nocomplain  -type f *  ] \
			   [ glob -nocomplain  -type f  .*  ] ] 
	if { $Option(ALBUM_known_images) } {
	    foreach f $filelist {
		if { [ IsImageFile $f 0 ] } {
		    incr nr
		} 
	    }
	} else {
	    set nr [ expr $nr + [ llength $filelist ] ]
	}
    }
    set dirlist [ concat [ glob -nocomplain  -type d *  ] \
		      [ glob -nocomplain  -type d  .*  ] ] 
    foreach d $dirlist {
	cd $start
	if { $d != $Option(ALBUM_dir) && $d != "." && $d != ".." && \
		 ( ! [ string match ".*" $d ] || $Option(ALBUM_all) ) } {
	    set nr [ expr $nr + [ CountAlbumsAndImages $d $alsoimages ] ] 
	}
    }
    cd $start
    return $nr
}


proc AbortProcess { } {
    global Option
    global Abort

    catch { eval exec $Option(SCRIPT_camera_umount) }
    set Abort 1
    grab release .progbox
    destroy .progbox 
    ScanDirectory
    IdleCursor
}


proc ShowProgressWindow { title showdir showfile } {
    global AlbumBitmap
    global DirWidth
    global DefaultBackground

    BusyCursor
    set w .progbox
    destroy $w 
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w $title
    
    label $w.top -text $title -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    label $w.img -image $AlbumBitmap -justify center
    pack $w.img -pady 5m -padx 4m -side top

    scale $w.scale -showvalue 0 -sliderlength 20 -width 10 -length 280 \
		-orient horizontal -variable ShowProgress \
		-state disabled -from 0.0 -to 1000.0 -resolution 1.0
    pack $w.scale -pady 5m -padx 4m -side top

    frame $w.info 
    pack $w.info -pady 5m -padx 4m -side top
  

    if { $showdir } {
	label $w.info.dirlab -text Album: 
	entry $w.info.dirent -state disabled -width $DirWidth \
	    -textvariable ProgressDir -relief flat \
	    -background $DefaultBackground 
	grid $w.info.dirlab -col 0 -row 0 -sticky w
	grid $w.info.dirent -col 1 -row 0 -sticky w
    }

    if { $showfile } {
	label $w.info.filelab -text Image: 
	entry $w.info.fileent -state disabled -width $DirWidth \
	    -textvariable ProgressFile -relief flat \
	    -background $DefaultBackground 
	grid $w.info.filelab -col 0 -row 1 -sticky w
	grid $w.info.fileent -col 1 -row 1 -sticky w 
    }

    frame $w.button
    button $w.button.abort -text "Abort" \
	-command  { AbortProcess }
    
    pack $w.button.abort -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w
}

# sort entries
proc SortEntries { type } {
    SafeCall [ list DoSortEntries $type ] "Sort"
}



proc DoSortEntries { cmd } {
    global Option
    global NameList 
    global CaptionsEdited

    set dirlist { }
    set filelist { } 
    set ix 0
    foreach el $NameList {
	if { [ string range [ .listframe.list get $ix ] 0 0 ] == "\[" } {
	    lappend dirlist $el 
	} else {
	    lappend filelist $el
	}
	incr ix
    }
    set filelist [ lsort -command $cmd $filelist ]
    if { $cmd == "CmpPictureNames" } {
	# since dirs do not have internal names
	# just use the dir names
	set dirlist [ lsort -command CmpFileNames $dirlist ]
    } elseif { $cmd != "CmpPictureDates" } {
	# dirs do not have picture dates, so leave them alone!
	# the rest (FileNames and FileDates go through)
	set dirlist [ lsort -command $cmd $dirlist ]
    }
    set NameList [ concat $dirlist $filelist ]
    set CaptionsEdited 1
    SaveCaptions
    ScanDirectory
}

proc CmpFileNames { a b } {
    return [ string compare $a $b ]
}

proc CmpPictureNames { a b } {
    global PicName

    if { $PicName($a) != "" } {
	set astring $PicName($a)
    } else {
	set astring $a
    }

    if { $PicName($b) != "" } {
	set bstring $PicName($b)
    } else {
	set bstring $b
    }
    return [ string compare $astring $bstring ]
}

proc CmpFileDates { a  b } {
    if { [ file mtime $a ] < [ file mtime $b ] } {
	return -1 
    } elseif { [ file mtime $a ] > [ file mtime $b ] } {
	return 1
    } else {
	return 0
    }
}


proc CmpPictureDates { a  b } {
    global JheadVal

    set JheadVal(EXIF_Pic_Date) ""
    ParseJheadOutput $a
    set atime $JheadVal(EXIF_Pic_Date)
    set JheadVal(EXIF_Pic_Date) ""
    ParseJheadOutput $b
    set btime $JheadVal(EXIF_Pic_Date)

    return [ string compare $atime $btime ]
}

proc ReverseOrder { } {
    SafeCall [ list DoReverseOrder ] "Reversing"
}



proc DoReverseOrder { } {
    global Option
    global NameList 
    global CaptionsEdited 

    set dirlist { }
    set filelist { } 
    set ix 0
    puts "Reverse/Vorher: $NameList"
    foreach el $NameList {
	if { [ string range [ .listframe.list get $ix ] 0 0 ] == "\[" } {
	    lappend dirlist $el 
	} else {
	    lappend filelist $el
	}
	incr ix
    }
    
    set filelist [ lreverse $filelist ]
    set dirlist [ lreverse $dirlist ]

    set NameList [ concat $dirlist $filelist ]
    puts "Reverse/Nachher: $NameList"
    
    set CaptionsEdited 1
    SaveCaptions
    ScanDirectory
}


# ------------------------- Camera Menu ----------------------------------

########## Camera menu: all entries
proc CameraDownloadOrDelete { del } {
    # download everything from the camera (if $del == 0)
    # delete otherwise

    global Option
    global Abort ShowProgress
    global ProgressFile

    SaveCaptions
    if { $del } {
	set Operation "delete" 
    } else {
	set Operation "download"
    }
    catch { eval exec $Option(SCRIPT_camera_umount) }

    if { $del && $Option(CAMERA_warn_before_delete) } {
	set resp [ tk_dialog .dia \
		       "Massive Delete Warning" \
		       "You are going to delete all images on your camera. Are you sure you want to continue?" \
		       warning 1 "Yes, delete" "No, cancel" ]
	if { $resp == 1 } { return }
    }

    if { ! $del  && $Option(CAMERA_always_ask_for_fresh_dir) } {
	set res 1
	set files [ glob -nocomplain * ]
	if { $files == "" || $files == $Option(ALBUM_captions) } {
	    set res [ tk_dialog .dia \
			  "Download Directory Dialog" \
			  "Your current album is a fresh album. Do you want to download to this album or do you want to generate a new one?" \
			  questhead 0 \
			  "Use the Current Album" \
			  "Generate a New Album" "Abort" ]
	    if { $res == 2 } { return }
	}
	if { $res == 1 } {
	    set last $Option(CurrentAlbum) 
	    while { $last == $Option(CurrentAlbum) } {
		NewAlbum
		if { $last == $Option(CurrentAlbum) } {
		    set res [ tk_dialog .dia \
				  "Download Directory Dialog" \
				  "You need to name a fresh album for the download" \
				  info 0 OK Abort ]
		}
		if { $res == 1 } { return }
	    }
	}
    }
    if { ! $del  && $Option(CAMERA_warn_if_not_new) } {
	set files [ glob -nocomplain * ]
	if { $files != "" && $files != $Option(ALBUM_captions) } {
	    set res [ tk_dialog .dia \
			  "Warning" \
			  "The download album \"$Option(CurrentAlbum)\" is not empty. Do you want to cancel the download or do you really want to continue?"\
			  warning 1 "Continue download" "Cancel download" ] 
	    if { $res == 1 } { return }
	}
    }

    set Abort 0
    set ShowProgress 0.0
    set ProgressFile "(mounting)"
    if { $del == 0 } {
	ShowProgressWindow "Download Images from Camera" 0 1
    } else {
	ShowProgressWindow "Remove all Images from Camera" 0 1
    }
    update
    if { $Option(SCRIPT_camera_mount) != "" } { 
	if { [ catch {  eval exec $Option(SCRIPT_camera_mount) } errmes ] } {
	    tk_dialog .dia\
		"Camera Mount Error" \
		"The camera is probably not connected or switched off. Please connect the camera and switch it on. \n\nIf the camera is connected and switched on, then the mount command may not be the right one (see below and change in Camera options).\n\nCould not mount camera mass storage device: \"$errmes\"" \
		error 0 OK
	    
	    AbortProcess 
	    
	    return
	}
    }
    if { [ catch { cd $Option(SCRIPT_camera_dir) } errmes ] } {
	tk_dialog .dia "Camera Mount Error" "Camera directory \"$Option(SCRIPT_camera_dir)\" not found: \"$errmes\"" error 0 OK
	AbortProcess 
	return
    }
    set try 1
    while { $try } {
	set files [ glob -nocomplain -type d * ] 
	if { [ llength $files ] == 1 } {
	    cd $files
	} elseif { [ llength $files ] > 1 } {
	    set resp [ tk_dialog .dia "Camera Directory Problem" "I ended up in a directory with 2 sub-directories below \"$Option(SCRIPT_camera_dir).\" Do you really want to continue and operate in this directory?" \
			   warning 0 {Yes, continue}  {No, abort} ]
	    if { $resp == 1 } {
		AbortProcess
		return
	    }
	    set try 0
	} else {
	    set try 0
	}
    }
    set files [ glob -nocomplain -type f * ]
    if { $files == "" } {
	tk_dialog .dia \
	    "Camera Problem" \
	    "There are no files to $Operation in the camera directory [pwd]"\
	    warning 0 OK
	catch { eval exec $Option(SCRIPT_camera_umount) } 
	return
}
    set cameradir [ pwd ]
    cd $Option(CurrentAlbum)
		       
    set ProgressStep [ expr 1000.0 / (1.0 * ([llength $files] + 1.0)) ]
    set ShowProgress [ expr $ShowProgress + $ProgressStep ]	

    foreach f $files {
	set ProgressFile $f
	update
	if { $Abort } {
	    AbortProcess 
	    return
	}
	if { ! $del } {
	    if { [ catch { file copy [ file join $cameradir $f ] \
			       $Option(CurrentAlbum) } errmes ] } {
		set res [ tk_dialog .dia \
			      "Camera Download Error" \
			      "Error while downloading $f from the camera: \"$errmes\"" \
	      error 0 "Continue download" "Abort download" ] 
		if { $res == 1 } { 
		    AbortProcess
		    return 
		}
	    } else {
		update
	    }
	    SetGroupPerms [ file join $Option(CurrentAlbum) $f ]
	    if { $Abort } {
		AbortProcess 
		return
	    }
	}

	if { $del || $Option(CAMERA_delete_after_download) } {
	    update
	    if { [ catch { file delete [ file join $cameradir $f ] } \
		       errmes ] } {
		    set res [ tk_dialog .dia "Camera Image Deletion Error" "Error while deleting [ join $cameradir $f ] on the camera: \"$errmes\"" error 0 "Continue $Operation" "Abort $Operation" ] 
		if { $res == 1 } { 
		    AbortProcess
		    return 
		}
	    }
	}
	if { ! $del && $Option(CAMERA_postprocess) } {
	    update
	    if { [ catch {  eval exec \
				$Option(SCRIPT_postprocess)  \
				$f  }   errmes ] } {
		set res [ tk_dialog .dia "Camera Download Error" "Error while postprocessing $f  after successful downloading: \"$errmes\"" error 0 "Continue download" "Abort download" ] 
		if { $res == 1 } { 
		    AbortProcess
		    return
		}
	    }
	}
	set ShowProgress [ expr $ShowProgress + $ProgressStep ]	
	update
    }
    after 2000
    update
    if { $Option(SCRIPT_camera_umount) != "" } {
	if { [ catch { eval exec $Option(SCRIPT_camera_umount) } errmes ] } {
	    tk_dialog .dia "Camera Connection Warning" "Error while unmounting the camera mass storage device after successful $Operation: \"$errmes\"" \
		warning 0 OK
	}
    }
    if { ! $Abort } {
	set ProgressFile ""
	set ProgressDir ""
	.progbox.button.abort configure -text "OK"
	if { $del } {
	    set text "Camera Image Deletion Completed"
	} else {
	    set text "Camera Download Completed"
	}
	.progbox.top configure -text $text
    }
    update
    ScanDirectory
}

########## Bound:  Down
proc NextImage {   } {
    global NameList

    SaveCaptions

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	if { [ llength $NameList ] != 0 } {
	    .listframe.list selection set 0
	    .listframe.list see 0
	    LoadPicFile
	}
    } else {
	.listframe.list selection clear 0 end
	foreach el $cursel {
	    .listframe.list selection set [ expr $el + 1 ]
	    .listframe.list see [ expr $el + 1 ]
	}
	if { [ .listframe.list curselection ] == "" } {
	    ClearInfo
	    PreviewImg blank
	} else {
	LoadPicFile
	}
    }
}

########## Bound:  Up
proc PreviousImage {   } {
    global NameList

    SaveCaptions

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	if { [ llength $NameList ] != 0 } {
	    .listframe.list selection set end
	    .listframe.list see end
	    LoadPicFile
	}
    } else {
	.listframe.list selection clear 0 end
	foreach el [ lreverse $cursel ] {
	    .listframe.list selection set [ expr $el - 1 ]
	    .listframe.list see [ expr $el - 1 ]
	}
	LoadPicFile
    }
}

# ------------------------- View Menu ------------------------------------

########## View menu: Display picture in viewer window
proc ViewImage { } {
    SaveCaptions

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" }  {
	tk_dialog .dia "View Warning" \
		"There is no file selected that could be viewed" warning 0 OK 
	return
    }

    DisplayOrGotoAlbum 1
}

########## Bound: Double-Button-1 in listframe
proc DisplayOrGotoAlbum { { OnlyView 0 } } {
    global Option
    global NameList
    global ViewerProcesses
    global TempViewerFile

    SaveCaptions
    # check for selection
    set cursel [ .listframe.list curselection ]
    if { [ llength $cursel ] == 0 } {
	return
    } else {
	set cursel [ lindex $cursel end ]
    }
    
    set file [ lindex $NameList $cursel ]

    if { [ file isfile $file ] } {
	# we never view the actual file, but only copies!

	if { $TempViewerFile == "" } {
	    set TempViewerFile [ MakeTempName "" ]
	}
	set started 0
	file copy -force  $file   $TempViewerFile 
	SetGroupPerms $TempViewerFile
	if { $Option(VIEW_OnlyOneViewerWindow) && \
		 $Option(SCRIPT_displaynext_tmp) != "" } {
	    if { [ ProcessAlive $ViewerProcesses ] } {
		eval exec $Option(SCRIPT_displaynext_tmp) \
		    { $TempViewerFile } & 
		set started 1
	    }
	}
	if { $started == 0 } {
	    set ViewerProcesses -1
	    if { $Option(VIEW_small) } {
		catch { set ViewerProcesses \
			    [ eval exec $Option(SCRIPT_display_tmp) \
				  -geometry \
				  $Option(VIEW_viewer_size)x$Option(VIEW_viewer_size) \
				  { $TempViewerFile } & ] }
	    } else { 
		catch { set ViewerProcesses \
			    [ eval exec $Option(SCRIPT_display_tmp) \
				  { $TempViewerFile } & ] }
	    }
	}
	after 100
	update
    } elseif { [ file isdirectory $file ] } {
	if { ! $OnlyView } {
	    GotoAlbum [ file join $Option(CurrentAlbum) $file ]
	}
    }
}

########## View menu: Viewer size
proc RestartViewer { type } {
    global Option
    global TempViewerFile
    global ViewerProcesses

    SaveCaptions
    if { $type == "size" && $Option(VIEW_small) == 0 } {
	return
    }
    if { [ ProcessAlive $ViewerProcesses ] } { 
	if { $Option(VIEW_OnlyOneViewerWindow) } {
	    KillProcesses $ViewerProcesses
	    set ViewerProcesses -1
	}
	if { $Option(VIEW_small) } {
	    set opt "-geometry $Option(VIEW_viewer_size)x$Option(VIEW_viewer_size)"
	} else {
	    set opt ""
	}
	set SaveDir [ pwd ]
	cd $Option(SCRIPT_tempdir)
	catch { set ViewerProcesses \
		    [ eval exec $Option(SCRIPT_display_tmp) \
			  $opt $TempViewerFile & ] }
	after 2000

	cd $SaveDir
    }
}

########## View menu: Preview window size   
proc ResizePreview { } {
    global Option
    global RealPreviewList
    global  CurrentImage  CurrentIx 

    SaveCaptions

    set RealPreviewList { }
    foreach img [ image names ] {
	PreviewImg blank
	if { $img != "PreviewImg" && $img != "TempImg" && \
		 [ string range $img 0 3 ] != "TN::" && \
		 [ string range $img 0 4 ] != "image" } {
	    image delete $img
	}
    }
    PreviewImg configure \
	-width $Option(VIEW_prevsize) \
	-height $Option(VIEW_prevsize)
    if { $Option(VIEW_prevsize) == 0 } {
	PreviewImg configure -width 1 -height 1
	update
	catch { pack forget .picframe.preview }
	PreviewImg configure -width 0 -height 0
    } else {
	catch { pack .picframe.preview -side top -expand 0 \
		    -padx 3 -pady 3 -fill both }
    }
    set CurrentImage ""
    set CurrentIx -1
    LoadPicFile
}


# ------------------------- Jpeg Info Menu -------------------------------

########## Menu JpegInfo: Rename file to picture date
proc RenameFileToPictureDate { } {
    SafeCall DoRenameFileToPictureDate "Rename File to Pic Date"
}

proc DoRenameFileToPictureDate { } {
    global Option
    global PicName Caption AltTag
    global NameList 
    global JheadVal

    SaveCaptions

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	tk_dialog .dia "Rename Warning" "No file selected for renaming" \
	    warning 0 OK
	return
    }

    if { [ llength $cursel ] == 1 && $JheadVal(EXIF_Pic_Date) == "" } {
	tk_dialog .dia "Rename Warning" "File \"[lindex $NameList $cursel]\" does not have a picture date, so we cannot rename the file" \
	    warning 0 OK
	return
    }

    ClearInfo
    PreviewImg blank
    update

    foreach el $cursel { 
	set file [ lindex $NameList $el ]
	set result ""
	catch { set result [ eval exec  $Option(SCRIPT_jhead) \
				 -nf$Option(JPEG_rename) [ list $file ] ] }
	if { [ regexp "^$file --> (.*)$" $result dummy newname ] } {
	    set newname [ string trim $newname ]
	    set PicName($newname) $PicName($file)
	    set Caption($newname) $Caption($file)
	    set AltTag($newname) $AltTag($file)
	    set NameList [ lreplace $NameList $el $el $newname ]
	    catch { image delete [ file join $Option(CurrentAlbum) $file ] }
	    catch { image \
			delete \
			"TN::[ file join $Option(CurrentAlbum) $file ]" } 
	}
    }
    SaveCaptions
    ScanDirectory
    foreach el $cursel {
	.listframe.list selection set $el
    }
    if { $cursel != "" } {
	.listframe.list see [ lindex $cursel end ]
    }
    LoadPicFile
}


########## Menu JpegInfo: Set file date to picture date
proc SetFileDateToPictureDate { } {
    SafeCall DoSetFileDateToPictureDate  "Set File Date" 
}

proc DoSetFileDateToPictureDate { } {

    global NameList 
    global CurrentIx
    global Option
    global JheadVal

    set cursel [ .listframe.list curselection ]

    SaveCaptions

    if { $cursel == "" } {
	tk_dialog .dia "Set File Date Warning" \
		"There is no file selected." warning 0 OK 
	return
    }

    if { [ llength $cursel ] == 1 && $JheadVal(EXIF_Pic_Date) == "" } {
	tk_dialog .dia "Set File Date Warning" "File \"[lindex $NameList $cursel]\" does not have a picture date, so we cannot set the file date" \
	    warning 0 OK
	return
    }

    foreach el $cursel { 
	set file [ lindex $NameList $el ]
	if { [ catch { set x [ eval exec $Option(SCRIPT_jhead) \
				   -ft { $file } ] } errmes ] } {
	    tk_dialog .dia "Set File Date Error" "An error occured while trying to set the file date of \"$file\": \"$errmes\"" error 0 OK
	    set CurrentIx -1
	    LoadPicFile
	}
    }
    set CurrentIx -1
    LoadPicFile
}


########## Menu JpegInfo: Adjust picture time
proc AdjustPictureTime { } {
    SafeCall DoAdjustPictureTime "Adjust Pic Time"
}

proc DoAdjustPictureTime { } {

    global OK
    global Option NameList
    global AdjPM AdjHr AdjMin
    global CurrentIx
    global JheadVal

    SaveCaptions

    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	tk_dialog .dia "Adjust Time Warning" "There is nothing selected for which the time can be adjusted" \
		warning 0 OK
	return
    }

    if { [ llength $cursel ] == 1 && $JheadVal(EXIF_Pic_Date) == "" } {
	tk_dialog .dia "Adjust Time Warning" "File \"[lindex $NameList $cursel]\" does not have a picture time, so we cannot adjust the time" \
	    warning 0 OK
	return
    }

    set OK 0
    set AdjPM "+"
    set AdjHr 0
    set AdjMin 0

    set w .adjbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Adjust Picture Time"

    label $w.top -text "Adjust Picture Time" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.time
    pack $w.time -side top
    
    label $w.time.pml -text "+/-" 
    tk_optionMenu $w.time.pmv AdjPM + - 
    
    label $w.time.hrl -text "Hours"
    tk_optionMenu $w.time.hrv AdjHr 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \
				     16 17 18 19 20 21 22 23 24 

    label $w.time.minl -text "Minutes" 
    tk_optionMenu $w.time.minv AdjMin  0 5 10 15 20 25 30 35 40 45 50 55 

    pack $w.time.pml $w.time.pmv $w.time.hrl $w.time.hrv \
	$w.time.minl $w.time.minv -side left -expand 0 -fill both

    frame $w.button
    button $w.button.ok -text OK \
	-command  { set OK 1; \
			grab release .adjbox; \
			destroy .adjbox }
    button $w.button.cancel -text Cancel \
	-command  { set OK 0; grab release .adjbox; destroy .adjbox }
    
    pack $w.button.ok -side left -expand 0 
    pack $w.button.cancel -side right -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m

    PositionWindowCenter $w

    tkwait window $w

    if { $OK } {
	set cursel [ .listframe.list curselection ]
	foreach el $cursel { 
	    set file [ lindex $NameList $el ]
	    if { [ file isdirectory $file ] } {
		if { [ tk_dialog .dia "Time Adjustment Warning" \
			 "Cannot adjust picture time of directory \"$file\"" \
			   warning 0 "OK, continue" "Abort" ] == 1 } {
		    return
		}
	    }
	    if { [ catch { eval exec $Option(SCRIPT_jhead) \
			       -ta$AdjPM$AdjHr:$AdjMin {$file}  } errmes ] } {
		tk_dialog .dia "Time Adjustment Error" "An error occured while executing \"$Option(SCRIPT_jhead) -ta$AdjPM$AdjHr:$AdjMin $file\"; \"$errmes\"" \
		    error 0 OK
	    }
	}
    }
    set CurrentIx -1
    LoadPicFile
}


########## Menu JpegInfo: Change picture date
proc ChangePictureDate { } {
    SafeCall DoChangePictureDate "Change Picture Date & Time"
}

proc DoChangePictureDate { } {
    global OK
    global Option NameList
    global JheadVal
    global NYear 
    global NMonth
    global NDay 
    global NHour
    global NMin N10Min N1Min
    global NSec N10Sec N1Sec
    global CurrentIx

    set cursel [ .listframe.list curselection ]

    SaveCaptions

    set warn ""
    if { $cursel == "" } { 
	set warn "No file selected" 
    } elseif { [ llength $cursel ] > 1 } {
	set warn "Can only change the picture date of one file at a time. Please select exactly one file"
    } elseif { $JheadVal(EXIF_Pic_Date) == "" } {
	set warn "The file does not contain a picture date"
    }
    if { $warn != "" } {
	tk_dialog .dia "Change Picture Date Warning" \
		$warn warning 0 OK 
	return
    }

    regexp "^(.*):(.*):(.*) (.*):(.*):(.*)$" $JheadVal(EXIF_Pic_Date) dummy \
	NYear NMonth NDay NHour NMin NSec 

    RemoveLeadingZero NMonth
    RemoveLeadingZero NDay
    RemoveLeadingZero NHour
    RemoveLeadingZero NMin
    RemoveLeadingZero NSec

    set N10Min [ expr ( $NMin / 10 ) * 10 ]
    set N1Min [ expr $NMin % 10 ]
    set N10Sec [ expr ( $NSec / 10 ) * 10 ]
    set N1Sec [ expr $NSec % 10 ]

    set w .adjbox
    destroy $w
    toplevel $w
    wm transient $w .
    grab set $w
    wm title $w "Change Picture Date & Time"

    label $w.top -text "Change Picture Date & Time" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.date
    pack $w.date -side top

    frame $w.time
    pack $w.time -side top
    
    
    label $w.date.yrl -text "Year"
    tk_optionMenu $w.date.yrv NYear 1990 1991 1992 1993 1994 1995 \
	1996 1997 1998 1999 2000 2001 \
	2002 2003 2004 2005 2006 2007 2008 2009 2010

    label $w.date.mol -text "Month"
    tk_optionMenu $w.date.mov NMonth 1 2 3 4 5 6 7 8 9 10 11 12

    label $w.date.dayl -text "Day"
    tk_optionMenu $w.date.dayv NDay 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \
	16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 \
	
    pack $w.date.yrl  $w.date.yrv $w.date.mol $w.date.mov \
	$w.date.dayl $w.date.dayv -side left -padx 2m -pady 2m

    label $w.time.hrl -text "Hours"
    tk_optionMenu $w.time.hrv NHour 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 \
				     16 17 18 19 20 21 22 23 24 

    label $w.time.min10l -text "10 Minutes" 
    tk_optionMenu $w.time.min10v N10Min  0 10  20 30  40  50  

    label $w.time.min1l -text "1 Minute" 
    tk_optionMenu $w.time.min1v N1Min  0 1  2 3  4  5 6 7 8 9  

    label $w.time.sec10l -text "10 Seconds" 
    tk_optionMenu $w.time.sec10v N10Sec  0 10  20 30  40  50  

    label $w.time.sec1l -text "1 Second" 
    tk_optionMenu $w.time.sec1v N1Sec  0 1  2 3  4  5 6 7 8 9  

    pack $w.time.hrl $w.time.hrv $w.time.min10l $w.time.min10v \
	$w.time.min1l $w.time.min1v $w.time.sec10l $w.time.sec10v \
	$w.time.sec1l $w.time.sec1v -side left -padx 2m -pady 2m

    frame $w.button
    button $w.button.ok -text OK \
	-command  { set OK 1; \
			grab release .adjbox; \
			destroy .adjbox }
    button $w.button.cancel -text Cancel \
	-command  { set OK 0; grab release .adjbox; destroy .adjbox }
    
    pack $w.button.ok -side left -expand 0 
    pack $w.button.cancel -side right -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m

    PositionWindowCenter $w

    tkwait window $w

    if { $OK } {
	set cursel [ .listframe.list curselection ]
	foreach el $cursel { 
	    set file [ lindex $NameList $el ]
	    if { [ catch { eval exec $Option(SCRIPT_jhead) \
			"-ts$NYear:$NMonth:$NDay-$NHour:[ expr $N10Min / 10 ]$N1Min:[ expr $N10Sec / 10 ]$N1Sec" \
			       {$file} } errmes ] } {
		tk_dialog .dia "Change Picture Time Error" "Error while executing \"$Option(SCRIPT_jhead) -ts$NYear:$NMonth:$NDay-$NHour:[ expr $N10Min / 10 ]$N1Min:[ expr $N10Sec / 10 ]$N1Sec $file: \"$errmes\"" \
		    error 0 OK
		return
	    }
	}
    }
    set CurrentIx -1
    LoadPicFile
}


proc RemoveLeadingZero { var } {
    upvar $var x

    if { [ string first "0" $x 0 ] == 0 } {
	set x [ string range $x 1 end ] 
    }
}

########## Menu JpegInfo: Remove EXIF thumbnail
proc RemoveEXIFTN { } {
    SafeCall DoRemoveEXIFTN "Remove Thumbnail"
}

proc DoRemoveEXIFTN { } {

    global NameList 
    global CurrentIx
    global Option

    SaveCaptions

    update
    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	tk_dialog .dia "Remove Thumbnail Warning" \
		"There is no file selected from which we could remove the EXIF thumbnails" warning 0 OK 
	return
    }

    if { $Option(EXIF_RemovalWarning) } {
	if { [ tk_dialog .dia "EXIF Removal Warning" \
		   "Do you really want to remove the EXIF thumbnails from the selected files? These thumbnails are not essential, but they provide a faster preview when working with the pictures" \
		   questhead 0 "Yes, continue" "No, cancel" ] == 1 } {
	    return
	}
    }

    foreach el $cursel { 
	update
	set file [ lindex $NameList $el ]
	if { [ catch { eval exec $Option(SCRIPT_jhead) -dt {$file}  } errmes ] } {
	    tk_dialog .dia "EXIF Removal Error" "An error occured while execution \"$Option(SCRIPT_jhead) -dt $file\": \"$errmes\"" \
		error 0 OK
	    return
	}
    }
    set CurrentIx -1
    LoadPicFile
}

########## Menu JpegInfo: Remove EXIF thumbnail
proc RemoveEXIFAndJPEG { } {
    SafeCall DoRemoveEXIFAndJPEG "Remove EXIF & JPEG data"
}

proc DoRemoveEXIFAndJPEG { } {

    global NameList 
    global CurrentIx
    global Option

    SaveCaptions

    update
    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	tk_dialog .dia "Remove EXIF & JPEG Command Warning" \
		"There is no file selected from which we could remove EXIF & JPEG info" warning 0 OK 
	return
    }

    if { $Option(EXIF_RemovalWarning) } {
	if { [ tk_dialog .dia "EXIF & JPEG Removal Warning" \
		   "Do you really want to remove the EXIF header & JPEG comment data from the selected files? This data is not essential for viewing the pictures, but contains valuable information when and how the picture was taken." \
		   questhead 1 "Yes, continue" "No, cancel" ] == 1 } {
	    return
	}
    }

    foreach el $cursel { 
	update
	set file [ lindex $NameList $el ]
	if { [ catch { eval exec $Option(SCRIPT_jhead) -de -dc {$file}  } errmes ] } {
	    tk_dialog .dia "EXIF Removal Error" "An error occured while execution \"$Option(SCRIPT_jhead) -de -dc $file\": \"$errmes\"" \
		error 0 OK
	    return
	}
    }
    set CurrentIx -1
    LoadPicFile
}


# ------------------------- Help Menu ------------------------------
proc MenuHelpAbout { } {
    global AlbumBitmap
    global TKAlbumVersion

    SaveCaptions
    after idle {.dia.msg configure -wraplength 5i }
    after idle {.dia.msg configure -justify center }
    after idle {.dia.bitmap configure -image $AlbumBitmap }

    tk_dialog .dia "About" "\
TKAlbum Version $TKAlbumVersion

A Tcl/Tk GUI for USB mass-storage cameras using
the \"album\" script, the \"jhead\" utility, 
the \"jpegtran\" utility and the \"ImageMagick\" tools 

Copyright (C) Bernhard Nebel 
(bernhard.nebel@gmx.de), September 2002
TKAlbum comes with absolutely no warranty
This is free software under the GPL
(see License in the Help menu)
" info 0 OK 

}

proc MenuHelpLicense { } {
    global LicenseText

    MenuHelpDisplay .licensetext "License" $LicenseText
}

proc MenuHelpManPage { } {
    global ManPageText 

    MenuHelpDisplay .manpagetext "Manual Page" $ManPageText

}

proc MenuHelpDisplay { w title text } {
    
    destroy $w
    toplevel $w
    wm title $w $title
    wm iconname $w $title

    frame $w.buttons
    pack $w.buttons -side bottom -fill x -pady 2m
    button $w.buttons.dismiss -text Close -command "destroy $w"
    pack $w.buttons.dismiss -anchor center -side left -expand 1

    text $w.text -relief sunken -bd 2 -yscrollcommand \
	"$w.scroll set" -setgrid 1 \
	-height 25 
    scrollbar $w.scroll -command "$w.text yview"
    pack $w.scroll -side right -fill y
    pack $w.text -expand yes -fill both
    $w.text insert 0.0 $text
    $w.text config -state disabled
    PositionWindowCenter $w
}
    

# ----------------------- Dynamic Help -----------------------------------

proc DisplayBalloon { path key x y } {
    global HelpText
    global HelpFont
    global UnShowProcess
    
#    puts "DisplayBalloon: $path / $key / $x / $y"
    if { [ info exists HelpText($key) ] } {
#	puts "DisplayBalloon: help text exist"
	destroy .help
	toplevel .help -relief flat -background black \
	    -bd 1 -screen [winfo screen $path]

	wm overrideredirect .help 1
        wm transient .help
        wm withdraw .help

	label .help.label -text $HelpText($key) \
	    -relief flat -bd 0 -highlightthickness 0 \
	    -foreground black -background "#FFFFC0" \
	    -justify left -font $HelpFont
	pack .help.label -side left
	update idletasks

	set  scrwidth  [winfo vrootwidth  .]
        set  scrheight [winfo vrootheight .]
        set  width     [winfo reqwidth  .help]
        set  height    [winfo reqheight .help]
        incr y 12
        incr x 8

	if { $x+$width > $scrwidth } {
            set x [expr {$scrwidth - $width}]
        }
        if { $y+$height > $scrheight } {
            set y [expr {$y - 12 - $height}]
        }

#	puts "DisplayBalloon geom: +$x+$y"
        wm geometry  .help "+$x+$y"
        update idletasks
        wm deiconify .help
	set UnShowProcess [ after [ TimeToShow  $HelpText($key) ] \
				{ destroy .help } ]
    }
}

# compute the time necessary to display a help string
# which is dependend on the number of lines that are displayed
proc TimeToShow { str } {
    global HelpTime

    return [ expr $HelpTime + ( $HelpTime / 2 ) * [ CountNL $str ] ]
}

# count number of new lines in a string
proc CountNL { str } {
    set nextnl [ string first "\n" $str 0 ]
    if { $nextnl == -1 } {
	return 0
    } else {
	return  [ expr 1 + [ CountNL [ string range $str \
					   [ expr $nextnl + 1 ] end ] ] ]
    }
}

# kill Balloon window and associated processes
proc KillBalloon { } {
    global ShowProcess UnShowProcess 

    after cancel $ShowProcess
    set ShowProcess ""
    after cancel $UnShowProcess
    set UnShowProcess
    if { [ winfo exists .help ] } {
	destroy .help
	update idletasks
    }
}

proc MotionHelpDispatch { type path x y } {
    global Option
    global ShowProcess
    global UnShowProcess
    global HelpDelay
    global HelpTime
    global HelpText
    global HelpMenu

#    puts "MHD: $type $path $x $y"

    if { ( $Option(STATE_CommandHelp) && \
	       [ string first ".prefbox" $path 0 ] != 0 ) || \
	     ( $Option(STATE_OptionHelp) && \
		   [ string first ".prefbox" $path 0 ] == 0 ) } {
	if { $type == "motion" } {
	    if { [ winfo exists .help ] } { 
#		puts "ignore"
		return
	    } elseif { [ info exists HelpMenu($path) ] } {
#		puts "pass on to MenuHelpDispatch"
		MenuHelpDispatch select $path
		return
	    } elseif { [ string first ".mbar" [ grab current ] ] == 0 } {
#		puts "handle as leave"
		set type "leave"
	    } else {
#		puts "handle as enter"
		set type "enter"
	    }
	}

	KillBalloon

	if { $type == "enter" && [ info exists HelpText($path) ] } { 
	    set ShowProcess [ after $HelpDelay "DisplayBalloon $path $path $x $y" ]
	}
    }
}

    

proc RegisterHelp { w1 w2 text } {
    RegOneHelp $w1 $text
    RegOneHelp $w2 $text
}

proc RegOneHelp { w text } {
    global HelpText

    if { $w != "" } {
	set HelpText($w) $text
    }
}
    
proc MenuHelpDispatch { type path } {
    global Option
    global HelpText
    global ShowProcess
    global UnShowProcess
    global HelpDelay
    global HelpTime

#    puts "MenuHelpDispatch: $type $path"
    if { ! $Option(STATE_CommandHelp) } {
	return 
    }
    if { $type == "motion" && [ winfo exists .help ] } {
	return
    } else {
	set type "select"
    }
    KillBalloon
    if { $type == "select" &&  [ winfo class $path ] == "Menu" } {
	set index [$path index active]
#	puts "index: $index"
	if { [string compare $index "none"] == 0 } {
	    return
	}
	set key "$path$index"
#	puts "Key: $key"
	if { [ info exists HelpText($key) ] } {
#	    puts "Display HelpText($key) at [ winfo pointerx . ] [winfo pointery . ]"
	    set ShowProcess [ after $HelpDelay \
	    "DisplayBalloon $path $key [ winfo pointerx . ] [winfo pointery . ]" ]
	}
    }
}


    
# register a help text with a menu entry 
proc RegisterMenuHelp { w x text } {
    global HelpText
    global HelpMenu

    set HelpMenu($w) 1
    set HelpText($w$x) $text
}


# ------------------------- Menu generation ------------------------------

proc MakeInfoEntry { row optionname string  match1 match2 help } {
    global irow
    global Option
    global JheadMatch1
    global JheadMatch2
    global JheadVal
    global InfoRow
    global HelpText

    set valname "v$optionname"
    set labname "l$optionname"
    set JheadMatch1($optionname) $match1
    set JheadMatch2($optionname) $match2
    set JheadVal($optionname) ""
    set InfoRow($optionname) $row
    label .info.$labname -text $string
    entry .info.$valname -state disabled -relief sunken \
	-textvariable JheadVal($optionname)

    if { ! [ info exists Option($optionname) ]  } { 
	set Option($optionname) 0
    } elseif { $Option($optionname) } {
        grid .info.$labname -row $row -column 0 -sticky w
        grid .info.$valname -row $row -column 1 -padx 2 
    }
    set HelpText(.info.$labname) $help
    set HelpText(.info.$valname) $help
    set HelpText(.prefbox.vals.v$optionname) $help
}

proc MakeRadioButtonEntry { w lab val var script } {
    $w add radiobutton -label $lab -variable $var -value $val -command $script
}


# change widget configurations and bindings according to current state
# States:
#  fresh (no root defined) / no current / initialized
#  readonly / readwrite
#  album enabled / disabled 
#  camera enabled / disabled
proc StateSettings { } {
    global Option

    set fresh [ string equal $Option(RootAlbum)  ""  ]
    set curr [ expr ! [ string equal $Option(CurrentAlbum)  "" ] ]
    set ro  $Option(STATE_ReadOnly) 
    set album $Option(STATE_AlbumEnabled) 
    set camera $Option(STATE_CameraEnabled) 

    # File menu
    if { $fresh } {
	.mbar.file.menu entryconf 2 -state disabled
    } else {
	.mbar.file.menu entryconf 2 -state normal
    }

    if { $fresh || $ro } {
	.mbar.file.menu entryconf 3 -state disabled
	bind . <Alt-n> ""
    } else {
	.mbar.file.menu entryconf 3 -state normal
	bind . <Alt-n> { NewAlbum }
    }

    if { $ro || ! $curr }  {
	.mbar.file.menu entryconf 5 -state disabled
	bind . <Alt-e> ""
	.mbar.file.menu entryconf 6 -state disabled
	.mbar.file.menu entryconf 8 -state disabled
	bind . <Alt-r> ""
	.mbar.file.menu entryconf 9 -state disabled
	bind . <Alt-m> ""
    } else { 
	.mbar.file.menu entryconf 5 -state normal
	bind . <Alt-e> { EditImage }
	.mbar.file.menu entryconf 6 -state normal
	.mbar.file.menu entryconf 8 -state normal
	bind . <Alt-r> { RenameFile }
	.mbar.file.menu entryconf 9 -state normal
	bind . <Alt-m> { MoveOrCopyFile 1 0 }
    }

    if { ! $curr } { 
	.mbar.file.menu entryconf 10 -state disabled
	bind . <Alt-c> 
	.mbar.file.menu entryconf 11 -state disabled
	bind . <Alt-t> 
	.mbar.file.menu entryconf 12 -state disabled
    } else {
	.mbar.file.menu entryconf 10 -state normal
	bind . <Alt-c> { MoveOrCopyFile 0 0 }
	.mbar.file.menu entryconf 11 -state normal
	bind . <Alt-t> { MoveOrCopyFile 0 1 }
	.mbar.file.menu entryconf 12 -state normal
    }


    if { $ro || ! $curr }  {
	.mbar.file.menu entryconf 14 -state disabled
	bind . <Alt-d> {}
	.mbar.file.menu entryconf 15 -state disabled
	.mbar.file.menu entryconf 16 -state disabled
    } else {
	.mbar.file.menu entryconf 14 -state normal
	bind . <Alt-d> { DeleteFiles }
	.mbar.file.menu entryconf 15 -state normal
	.mbar.file.menu entryconf 16 -state normal
    }

    # Option Menu
    if { ! $album } {
	.mbar.pref.menu entryconf 4 -state disabled
    } else { 
	.mbar.pref.menu entryconf 4 -state normal
    }    

    if { ! $camera } {
	.mbar.pref.menu entryconf 5 -state disabled
    } else { 
	.mbar.pref.menu entryconf 5 -state normal
    }    

    # Transformation menu
    if { $ro || ! $curr } {
	.mbar.transform  config -state disabled
	bind . <Alt-plus> ""
	bind . <Alt-minus> ""
	bind . <Alt-Key-1> ""
	bind . <Alt-h>  ""
	bind . <Alt-v>  ""
    } else { 
	.mbar.transform  config -state normal
	bind . <Alt-plus> { RotateImage 90 }
	bind . <Alt-minus> { RotateImage 270 }
	bind . <Alt-Key-1> { RotateImage 180 }
	bind . <Alt-h> { FlipImage H }
	bind . <Alt-v> { FlipImage V }
    } 
    
    # Album menu
    if { $ro || ! $curr || ! $album } {
	.mbar.album  config -state disabled
	bind . <Alt-l> { }
	bind . <Alt-g> { }
	bind . <Alt-f> { }
    } else { 
	.mbar.album  config -state normal
	bind . <Alt-l> { GenAlbum 1 0 }
	bind . <Alt-g> { GenAlbum 0 0 }
	bind . <Alt-f> { EditHeaderAndFooter }
    }

    if { ! $album } {
	catch { pack forget .mbar.album }
    } else {
	catch { pack forget .mbar.album }
	catch { pack .mbar.album -after .mbar.transform -padx 1m -side left }
    }


    # Camera menu
    if { $ro || ! $curr || ! $camera } {
	.mbar.camera  config -state disabled
    } else {
	.mbar.camera  config -state normal
    }

    if { ! $camera } {
	catch { pack forget .mbar.camera }
    } else {
	catch { pack forget .mbar.camera }
	catch { pack .mbar.camera -before .mbar.view -padx 1m -side left }
    }

    # View menu
    if { ! $curr } { 
	.mbar.view config -state disabled
	bind . <Key-Up> ""
	bind . <Key-Down>  ""
	bind . <Alt-x>  ""
	bind .textframe.nameent <Key-Return>  ""
	bind .textframe.cap.text <Key-Return> ""
	bind .textframe.altent <Key-Return>  ""
    } else {
	.mbar.view config -state normal
	bind . <Key-Up> { PreviousImage  }
	bind . <Key-Down> { NextImage  }
	bind . <Alt-x> { ViewImage }
	bind .textframe.nameent <Key-Return> { ReturnKeyAction }
	bind .textframe.cap.text <Key-Return> { ReturnKeyAction }
	bind .textframe.altent <Key-Return> { ReturnKeyAction }
    }

    # JpegInfo menu
    if { $ro || ! $curr } { 
	.mbar.jpeg  config -state disabled
    } else {
	.mbar.jpeg  config -state normal
    }

    # arrow buttons
    if { $ro || ! $curr || ! $album } {
	.arrowframe.up config -state disabled
	.arrowframe.down config -state disabled
    } else {
	.arrowframe.up config -state normal
	.arrowframe.down config -state normal
    }
    
    if { ! $album } { 
	catch { pack forget .arrowframe } 
	catch { pack forget .textframe }
    } else {
	catch { pack forget .arrowframe }
	catch { pack  .arrowframe -side left  -expand 0 \
		    -fill both -in .showframe -before .listframe }
	catch { pack forget .textframe } 
	catch { pack .textframe -side top -fill x -after .showframe -expand 0}
    }
}

# ------------------------- MAIN Prog ------------------------------

ReadProfile 

# ------------------------- WINDOW Setup ------------------------------

wm title . TKAlbum
wm iconname . TKAlbum
wm resizable . 0 1

frame .mbar -relief raised -bd 2
frame .messageframe -relief groove -bd 1
frame .showframe -relief groove -bd 1
frame .textframe -relief groove -bd 1
frame .buttonframe -relief groove -bd 1

frame .listframe -relief groove -bd 1 
frame .arrowframe -relief groove -bd 1
frame .infoframe -relief groove -bd 1 
frame .picframe -relief groove -bd 1 
pack .listframe  -side left -expand 1 -fill both \
     -in .showframe
if { $Option(STATE_AlbumEnabled) } {
pack .arrowframe  -side left -expand 0 -fill both -in .showframe \
    -before .listframe
}
pack .infoframe -side left -fill both -anchor w \
     -in .showframe
pack .picframe -side left  -fill both \
     -in .showframe

pack .mbar -side top -fill x
pack .messageframe -side top -fill x
pack .showframe -side top -expand 1 -fill both
if { $Option(STATE_AlbumEnabled) } {
    pack .textframe -side top -fill x -expand 0
}
pack .buttonframe -side top -fill x 
 
# ------------------------- Menu ------------------------------


menubutton .mbar.file -text File  -menu .mbar.file.menu
RegisterHelp .mbar.file "" \
    "Operations on files, e.g. copying, renaming, deleting"
menubutton .mbar.pref -text Options  -menu .mbar.pref.menu
RegisterHelp .mbar.pref "" \
    "Setting options for different operations"
menubutton .mbar.transform -text Transformations  -menu .mbar.transform.menu 
RegisterHelp .mbar.transform "" \
    "Transformations such as rotation and flipping"
menubutton .mbar.album -text Album  -menu .mbar.album.menu
RegisterHelp .mbar.album "" \
    "Album generation and a few album relevant operations,\nsuch as preview cropping, sorting, and editing headers"
menubutton .mbar.camera -text Camera  -menu .mbar.camera.menu
RegisterHelp .mbar.camera "" \
    "Download pictures from camera and delete pictures in camera"
menubutton .mbar.view -text View  -menu .mbar.view.menu
RegisterHelp .mbar.view "" \
    "Select picture to preview, view pictures in external\nwindow, and adjust viewer parameters"
menubutton .mbar.jpeg -text "JpegInfo"  -menu .mbar.jpeg.menu
RegisterHelp .mbar.jpeg "" \
    "Change JPEG EXIF data in image file\nand use it to change file name or date" 
menubutton .mbar.help -text Help  -menu .mbar.help.menu
RegisterHelp .mbar.help "" \
    "Guess what"

pack .mbar.file .mbar.pref .mbar.transform  -padx 1m -side left
pack .mbar.album  -padx 1m -side left
pack .mbar.view  .mbar.jpeg -padx 1m -side left
pack .mbar.help 	-padx 1m -side right

# ----- File Menu
menu .mbar.file.menu \
	-postcommand { FlattenMenu .mbar.file }
.mbar.file.menu add command -label "Set root album" -command ChooseRootDir 
RegisterMenuHelp .mbar.file.menu 1 \
    "Define the \"root\" album, which contains all other albums"
.mbar.file.menu add command -label "Open album" \
    -command OpenAlbum -underline 0 -accelerator "Meta-O" 
RegisterMenuHelp .mbar.file.menu 2 \
    "Open another album (selected in file\nlist or choose from directory menu)"
.mbar.file.menu add command -label "New album" -command NewAlbum \
	-accelerator "Meta-N" -underline 0
RegisterMenuHelp .mbar.file.menu 3 \
    "Create a fresh, empty album"
.mbar.file.menu add separator
.mbar.file.menu add command -label "Edit image"  \
    -command EditImage  -underline 0  -accelerator "Meta-E"
RegisterMenuHelp .mbar.file.menu 5 \
    "Edit an image"
.mbar.file.menu add command -label "Restore image"  \
    -command RestoreImage 
RegisterMenuHelp .mbar.file.menu 6 \
    "Restore the image to the state before editing (if possible)"
.mbar.file.menu add separator
.mbar.file.menu add command -label "Rename" \
    -command RenameFile -underline 0 -accelerator "Meta-R"
RegisterMenuHelp .mbar.file.menu 8 \
    "Rename the currently selected image file or directory"
.mbar.file.menu add command -label "Move" \
    -command { MoveOrCopyFile 1 0 } \
    -underline 0 -accelerator "Meta-M" 
RegisterMenuHelp .mbar.file.menu 9 \
    "Move a set of image files into another album"    
.mbar.file.menu add command -label "Copy" \
    -command { MoveOrCopyFile 0 0 } \
    -underline 0 -accelerator "Meta-C"
RegisterMenuHelp .mbar.file.menu 10 \
    "Copy a set of image files to another album"    
.mbar.file.menu add command -label "Copy & converT" \
    -command { MoveOrCopyFile 0 1 } \
    -underline 13 -accelerator "Meta-T"
RegisterMenuHelp .mbar.file.menu 11 \
    "Copy & convert a set of image files into another\ndirectoy. This is the right operation to generate\n pictures to be sent by e-mail."    
.mbar.file.menu add command -label "Clear copy marks" \
    -command { ClearCopyMarks } 
RegisterMenuHelp .mbar.file.menu 12 \
    "Delete all the marks for files that have been copied"
.mbar.file.menu add separator
.mbar.file.menu add command -label "Delete" \
    -command DeleteFiles -underline 0 -accelerator "Meta-D"
RegisterMenuHelp .mbar.file.menu 14 \
    "Delete a set of image files"    
.mbar.file.menu add command -label "Undelete" -command UndeleteFiles
RegisterMenuHelp .mbar.file.menu 15 \
    "Change into the trash bin in order to undelete image files"    
.mbar.file.menu add command -label "Empty trash bin" -command EmptyTrash
RegisterMenuHelp .mbar.file.menu 16 \
    "Get rid of all files in the trash bin. After that it is\nimpossible to undelete a previously deleted file."    
.mbar.file.menu add separator  
.mbar.file.menu add command -label Exit -command Exit 
RegisterMenuHelp .mbar.file.menu 18 \
    "Exit the program"    


# ----- Option menu
menu .mbar.pref.menu -postcommand { FlattenMenu .mbar.pref }
.mbar.pref.menu add command -label "General options" \
    -command GeneralOptions 
RegisterMenuHelp .mbar.pref.menu 1 \
    "General settings such as paths to programs"    
.mbar.pref.menu add command -label "File options" \
    -command FileOptions 
RegisterMenuHelp .mbar.pref.menu 2 \
    "Settings for file operations"    
.mbar.pref.menu add command -label "Transform options" \
    -command TransformOptions 
RegisterMenuHelp .mbar.pref.menu 3 \
    "Settings for image transformations"    
.mbar.pref.menu add cascade -label "Album options" \
    -menu .mbar.pref.menu.album 
RegisterMenuHelp .mbar.pref.menu 4 \
    "Settings for running the \"album\" program"    
.mbar.pref.menu add command -label "Camera options" \
    -command CameraOptions 
RegisterMenuHelp .mbar.pref.menu 5 \
    "Settings for camera download operation"    
.mbar.pref.menu add command -label "View options" \
    -command ViewOptions 
RegisterMenuHelp .mbar.pref.menu 6 \
    "Settings for viewing the albums and previews"    
.mbar.pref.menu add command -label "JpegInfo options" \
    -command JpegInfoOptions 
RegisterMenuHelp .mbar.pref.menu 7 \
    "Settings that determine which EXIF values to show and\nhow to process files concerning the EXIF values"    

# - Album sub-menu
menu .mbar.pref.menu.album 
.mbar.pref.menu.album add command -label "Thumbnails & preview options" \
    -command AlbumTNOptions 
RegisterMenuHelp .mbar.pref.menu.album 1 \
    "Settings for the appearance of thumbnails and of\nthe medium sized pictures in the HTML albums" 
.mbar.pref.menu.album  add command -label "Album appearance options" \
    -command AlbumAAOptions 
RegisterMenuHelp .mbar.pref.menu.album 2 \
    "General settings for album generation" 

# ----- Transformation menu
menu .mbar.transform.menu -postcommand { FlattenMenu .mbar.transform }
.mbar.transform.menu add command -label "Rotate  +90 deg" \
    -command { RotateImage 90 }  -accelerator "Meta-+" \
    -underline 8
RegisterMenuHelp .mbar.transform.menu 1 \
    "Rotate picture clockwise by 90 degrees" 
.mbar.transform.menu add command -label "Rotate  180 deg" \
    -command { RotateImage 180 } -accelerator "Meta-1" \
    -underline 8
RegisterMenuHelp .mbar.transform.menu 2 \
    "Rotate picture by 180 degrees" 
.mbar.transform.menu add command -label "Rotate  -90 deg" \
    -command { RotateImage 270 } -accelerator "Meta--" \
    -underline 8
RegisterMenuHelp .mbar.transform.menu 3 \
    "Rotate picture counter-clockwise by 90 degrees" 
.mbar.transform.menu add separator 
.mbar.transform.menu add command -label "Vertical flip" \
    -command { FlipImage V } -accelerator "Meta-V" \
    -underline 0
RegisterMenuHelp .mbar.transform.menu 5 \
    "Mirror picture vertically; top becomes bottom and vice versa" 
.mbar.transform.menu add command -label "Horizontal flip" \
    -command { FlipImage H } -accelerator "Meta-H" \
    -underline 0
RegisterMenuHelp .mbar.transform.menu 6 \
    "Mirror picture horizontally; left becomes right and vice versa." 




# ----- Camera menu
menu .mbar.camera.menu -postcommand { FlattenMenu .mbar.camera }
.mbar.camera.menu add command -label "Download from camera" \
	-command { CameraDownloadOrDelete 0 }
RegisterMenuHelp .mbar.camera.menu 1 \
    "Download images from the digital camera" 
.mbar.camera.menu add command -label "Delete camera images" \
	-command { CameraDownloadOrDelete 1 }
RegisterMenuHelp .mbar.camera.menu 2 \
    "Delete all images in the digital camera" 


# ----- Album menu
menu .mbar.album.menu -postcommand { FlattenMenu .mbar.album }
.mbar.album.menu add command -label "Local album generation" \
    -command { GenAlbum 1 0 } -underline 0 -accelerator "Meta-L"
RegisterMenuHelp .mbar.album.menu 1 \
    "Generate a new album starting in the next higher directory\nthat contains an \"index\" file (thumbnails will only be\nregenerated if the respective pictures have been changed)" 
.mbar.album.menu add command -label "Global album regeneration" \
    -command { GenAlbum 0 0 } -underline 0 -accelerator "Meta-G"
RegisterMenuHelp .mbar.album.menu 2 \
    "Generate new album by starting from the toplevel album. This means\nthat all index pages will be regenerated (thumbnails will only be\nregenerated if the respective pictures have been changed)" 
.mbar.album.menu add command -label "Forced local generation" \
    -command { GenAlbum 1 1 } 
RegisterMenuHelp .mbar.album.menu 3 \
    "Generate a new album starting in the next higher directory\nthat contains an \"index\" file. All thumbnails will be\nregenerated (this can take some time!)" 
.mbar.album.menu add command -label "Forced global regeneration" \
    -command { GenAlbum 0 1 } 
RegisterMenuHelp .mbar.album.menu 4 \
    "Generate a new album starting in the toplevel album. All thumbnails\nwill be regenerated (this will take considerable time!)" 
.mbar.album.menu add separator
.mbar.album.menu add command -label "Edit header & footer" \
    -command { EditHeaderAndFooter } -underline 14 -accelerator "Meta-F"
RegisterMenuHelp .mbar.album.menu 6 \
    "Edit the header lines and footer lines of the current album"
.mbar.album.menu add separator
.mbar.album.menu add cascade -label "Crop preview" \
    -menu .mbar.album.menu.crop
RegisterMenuHelp .mbar.album.menu 8 \
    "Specify cropping of thumbnail for selected image. This is used only if necessary."
.mbar.album.menu add separator
.mbar.album.menu add cascade -label "Sort entries" \
    -menu .mbar.album.menu.sort
RegisterMenuHelp .mbar.album.menu 10 \
    "Sort entries in the directory list according to different criteria"

# crop sub.menu
menu .mbar.album.menu.crop
.mbar.album.menu.crop add command -label "center" \
    -command { PreviewCrop center }
RegisterMenuHelp .mbar.album.menu.crop 1 \
    "Show center of image in thumbnail"
.mbar.album.menu.crop add command -label "top" \
    -command { PreviewCrop top }
RegisterMenuHelp .mbar.album.menu.crop 2 \
    "Show top of image in thumbnail"
.mbar.album.menu.crop add command -label "bottom" \
    -command { PreviewCrop bottom }
RegisterMenuHelp .mbar.album.menu.crop 3 \
    "Show bottom of image in thumbnail"
.mbar.album.menu.crop add command -label "left" \
    -command { PreviewCrop left }
RegisterMenuHelp .mbar.album.menu.crop 4 \
    "Show left part of image in thumbnail"
.mbar.album.menu.crop add command -label "right" \
    -command { PreviewCrop right }
RegisterMenuHelp .mbar.album.menu.crop 5 \
    "Show right part of image in thumbnail"

# sort sub-menu
menu .mbar.album.menu.sort
.mbar.album.menu.sort add command -label "Sort by file name" \
    -command { SortEntries  CmpFileNames }
RegisterMenuHelp .mbar.album.menu.sort 1 \
    "Sort the images according to the alphabetical ordering of the file names"
.mbar.album.menu.sort add command -label "Sort by picture name" \
    -command { SortEntries  CmpPictureNames }
RegisterMenuHelp .mbar.album.menu.sort 2 \
    "Sort the images according to the alphabetical ordering of\npicture names (use file names if no picture names supplied)"
.mbar.album.menu.sort add command -label "Sort by file date" \
    -command { SortEntries  CmpFileDates }
RegisterMenuHelp .mbar.album.menu.sort 3 \
    "Sort the images according to the file modification/creation time"
.mbar.album.menu.sort add command -label "Sort by picture date" \
    -command { SortEntries  CmpPictureDates }
RegisterMenuHelp .mbar.album.menu.sort 4 \
    "Sort the images according to the picture time (all pictures\nwithout picture time are handled as if they have been\ntaken before the beginning of time)"
.mbar.album.menu.sort add command -label "Reverse order" \
    -command { ReverseOrder }
RegisterMenuHelp .mbar.album.menu.sort 5 \
    "Reverse the ordering of files and directories in the directory listing"

# ----- View menu
menu .mbar.view.menu -postcommand { FlattenMenu .mbar.view }
.mbar.view.menu add command -label "Previous image" \
	-command { PreviousImage  } -accelerator "Up"
 RegisterMenuHelp .mbar.view.menu 1 \
    "Select image file (or album) one slot up in the directory list"
.mbar.view.menu add command -label "Next image" \
	-command { NextImage  } -accelerator "Down" 
 RegisterMenuHelp .mbar.view.menu 2 \
    "Select image file (or album) one slot down in the directory list"
.mbar.view.menu add separator
.mbar.view.menu add command -label "EXternal viewer" \
	-command { ViewImage } -accelerator "Meta-X" \
	-underline 1
 RegisterMenuHelp .mbar.view.menu 4 \
    "Load selected picture into external viewer and display"
.mbar.view.menu add separator
.mbar.view.menu add checkbutton -label "Use small viewer window" \
	-command { RestartViewer mode } -variable Option(VIEW_small) \
	-underline 4
 RegisterMenuHelp .mbar.view.menu 6 \
    "Use restricted size for the external viewer (as specified by the\nnext menu entry); if unselected, the picture is viewed in\nits original size (which usually does not fit on the screen)"
.mbar.view.menu add cascade -label "Viewer size (reduced)" \
	-menu .mbar.view.menu.viewer 
 RegisterMenuHelp .mbar.view.menu  7 \
    "Specify maximum size for external viewer, which is used\nwhen the option in the previous menu entry is selected"
.mbar.view.menu add cascade -label "Preview window size" \
	-menu .mbar.view.menu.prev 
 RegisterMenuHelp .mbar.view.menu 8 \
    "Specify maximum size for the right previewer window"
.mbar.view.menu add separator
.mbar.view.menu add checkbutton -label "Use picture names in list" \
	-command ScanDirectory -variable Option(VIEW_UsePicName)
 RegisterMenuHelp .mbar.view.menu 10 \
    "Usually, the directory list displays file names; if this\noption is selected, picture names are displayed"


menu .mbar.view.menu.viewer
foreach el $ViewerSizeList { 
    MakeRadioButtonEntry .mbar.view.menu.viewer \
	"${el}x$el" $el Option(VIEW_viewer_size) \
	{ RestartViewer size }
}

menu .mbar.view.menu.prev
foreach el $PreviewSizeList { 
    MakeRadioButtonEntry .mbar.view.menu.prev \
	"${el}x$el" $el Option(VIEW_prevsize) \
	{ ResizePreview }
}



# ----- JpegInfo
menu .mbar.jpeg.menu -postcommand { FlattenMenu .mbar.jpeg }
.mbar.jpeg.menu add command -label "Rename file to picture date" \
    -command RenameFileToPictureDate 
 RegisterMenuHelp .mbar.jpeg.menu 1 \
    "Rename the selected files to the \"picture date & time\" given in the\nEXIF header of  the JPEG image files (do nothing if file does\nnot have an EXIF header); these names should be (almost) unique\nidentifiers for pictures"
.mbar.jpeg.menu add separator
.mbar.jpeg.menu add command -label "Set file date from picture date" \
    -command SetFileDateToPictureDate
 RegisterMenuHelp .mbar.jpeg.menu 3 \
    "Use \"picture date & time\" (if present) to set file modification date"
.mbar.jpeg.menu add command -label "Adjust picture time" \
    -command AdjustPictureTime 
 RegisterMenuHelp .mbar.jpeg.menu 4 \
    "Change the picture time of the selected files by an relative amount;\nthis is very useful if one has forgotten to adjust the time when\ntravelling across time zones"
.mbar.jpeg.menu add command -label "Change picture date & time" \
    -command ChangePictureDate 
 RegisterMenuHelp .mbar.jpeg.menu 5 \
    "Change the picture date & time (provided the selected\nfile has an EXIF header with picture date & time)" 
.mbar.jpeg.menu add command -label "Remove EXIF thumbnail" \
    -command RemoveEXIFTN 
 RegisterMenuHelp .mbar.jpeg.menu 6 \
    "Remove the thumbnail in the EXIF header that has been created\nby the digital camera; this thumbnail is non-essential but can\nbe used to give a faster preview - as demonstrated in the\nTKAlbum program" 
.mbar.jpeg.menu add command -label "Remove EXIF & JPEG data" \
    -command RemoveEXIFAndJPEG
 RegisterMenuHelp .mbar.jpeg.menu 7 \
    "Remove the entire EXIF header and additional JPEG comments\nfrom the JPEG file; this does not do any harm to the\npicture, but valuable information on how the picture has\nbeen taken and what has been done with it will be lost"

# ----- Help menu
menu .mbar.help.menu -postcommand { FlattenMenu .mbar.help }

.mbar.help.menu add checkbutton -label "Option dynamic help" \
    -variable Option(STATE_OptionHelp) 
RegisterMenuHelp .mbar.help.menu 1 \
    "Deselect in order to get rid of \"balloon\" help in the option windows"
.mbar.help.menu add checkbutton -label "Main window dynamic help " \
    -variable Option(STATE_CommandHelp) -command KillBalloon
RegisterMenuHelp .mbar.help.menu 2 \
    "Deselect in order to get rid of \"balloon\" help in the main window"
.mbar.help.menu add separator
.mbar.help.menu add command -label "License" -command "MenuHelpLicense"
RegisterMenuHelp .mbar.help.menu 4 \
    "Displays the Gnu Public License"
.mbar.help.menu add command -label "Manual" -command "MenuHelpManPage"
RegisterMenuHelp .mbar.help.menu 5 \
    "Displays the manual page"
.mbar.help.menu add separator
.mbar.help.menu add command -label "About TKAlbum" -command "MenuHelpAbout" 
RegisterMenuHelp .mbar.help.menu 7 \
    "Gives version information and tells you who wrote this stuff"


# enable menu bar
tk_menuBar .mbar .mbar.file .mbar.transform .mbar.view .mbar.album \
    .mbar.camera .mbar.help 
focus .mbar

# ------------------------- Messages ------------------------------

frame .messageframe.dir
frame .messageframe.dir.root
frame .messageframe.dir.current

pack .messageframe.dir -side top -expand 1 -fill x
pack .messageframe.dir.root -side top -expand 1 -fill x
pack .messageframe.dir.current -side top -expand 1 -fill x

label .messageframe.dir.current.label -text "Curr.:  "
entry .messageframe.dir.current.val -state disabled -width $DirWidth \
    -relief flat -textvariable CurrentAlbumText \
    -background $DefaultBackground 
RegisterHelp .messageframe.dir.current.label .messageframe.dir.current.val \
    "The directory path to the current album relative to the toplevel \"root album\""
pack .messageframe.dir.current.label -side left -pady 1m -padx 1m -anchor w
pack .messageframe.dir.current.val -side right -anchor w -fill x -expand 1

label .messageframe.dir.root.label -text "Root:  "
entry .messageframe.dir.root.val -state disabled -width $DirWidth \
    -relief flat -textvariable RootAlbumText \
    -background $DefaultBackground 
RegisterHelp .messageframe.dir.root.label .messageframe.dir.root.val \
    "The directory path to the  toplevel \"root album\""
pack .messageframe.dir.root.label -side left -pady 1m -padx 1m -anchor w
pack .messageframe.dir.root.val -side right -anchor w -fill x -expand 1

# ------------------------- File List ------------------------------

scrollbar .listframe.xscroll -orient horizontal \
          -command "llviewx" \
	  -width 10 -takefocus 0
scrollbar .listframe.yscroll -command ".listframe.list yview" -width 10 \
	  -takefocus 0
listbox .listframe.list  -width $Option(VIEW_dir_width) \
    -height $Option(VIEW_dir_height) \
    -setgrid 1 \
    -yscroll ".listframe.yscroll set" \
    -xscroll ".listframe.xscroll set" \
    -selectmode extended  -takefocus 0 -exportselection 0
grid .listframe.list -row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news
grid .listframe.yscroll  -row 1 -column 1 -rowspan 1 -columnspan 1 \
      -sticky news
grid .listframe.xscroll -row 2 -column 0 -rowspan 1 -columnspan 1 \
      -sticky news
grid rowconfig .listframe 1 -weight 1 -minsize 0
grid columnconfig .listframe 0 -weight 1 -minsize 0

proc llviewx { args } {
    catch { eval .listframe.list xview $args }
}

RegisterHelp .listframe.list "" \
    "List of image files and sub-albums: select and browse\nwith mouse (extended browsing) and with arrow keys"

# ------------------------- Up/Down arrows --------------------------

button .arrowframe.up -image $UpBitmap -command { MoveEntriesUp 1 } -takefocus 0
frame  .arrowframe.fill 
button .arrowframe.down -image $DownBitmap -command { MoveEntriesDown 1 } \
    -takefocus 0
RegisterHelp .arrowframe.up "" \
    "Move selected items up in the list:
     Mouse-1: Move up one slot
     Mouse-3: Move up to the start
                     of the list"
RegisterHelp .arrowframe.down "" \
    "Move selected items down in the list:
     Mouse-1: Move down one slot
     Mouse-3: Move down to the end 
                     of the list"


pack .arrowframe.up -side top -expand 0 -anchor n
pack .arrowframe.fill -expand 1 -fill both -side top
pack .arrowframe.down -side bottom -expand 0 -anchor s


# ------------------------- Info Table ------------------------------

frame .info
frame .dummy
pack .info -side top -anchor n -in .infoframe
pack .dummy -side top -expand 1 -fill y -in .infoframe

MakeInfoEntry  1 EXIF_File_Name  "File name:"  "File name    : " "" \
    "File name of image file"
MakeInfoEntry  2 EXIF_File_Size  "File size:"  "File size    : " "" \
    "Size of the image file measured in kilo bytes"
MakeInfoEntry  3 EXIF_File_Date  "File date:"  "File date    : " "" \
    "Date and time when image file was last modified"
MakeInfoEntry  4 EXIF_Camera    "Camera:" "Camera make  : " "Camera model : " \
    "Camera make and model that was used to take the picture"
MakeInfoEntry  5 EXIF_Pic_Date   "Picture date:"  "Date/Time    : " "" \
    "Date and time when the picture was taken"
MakeInfoEntry  6 EXIF_Resolution "Resolution:"  "Resolution   : " "" \
    "Width and height of the picture measured in pixels"
MakeInfoEntry  7 EXIF_Color      "Color/B&W:"   "Color/bw     : " "" \
    "Information whether the picture is in color or black & white"
MakeInfoEntry  8 EXIF_Flash      "Flash:"  "Flash used   : " "" \
    "Information about the use of the flash when the picture was taken"
MakeInfoEntry  9 EXIF_Focal      "Focal length:"  "Focal length : " "" \
    "Information about the focal length"
MakeInfoEntry 10 EXIF_CCD        "CCD width:"  "CCD width    : " "" \
    "Information about the CCD"
MakeInfoEntry 11 EXIF_ExposureTime "Exposure time:"  "Exposure time: " ""  \
    "The exposure time that was used when taking the picture"
MakeInfoEntry 12 EXIF_ExposureProg "Exposure:"   "Exposure     : " "" \
    "The method to compute the exposure that was used when taking the picture"
MakeInfoEntry 13 EXIF_Aperture   "Aperture:"   "Aperture     : " "" \
    "The aperture used when taking the picture"
MakeInfoEntry 14 EXIF_Dist       "Distance:"  "Focus dist.  : " "" \
    "Distance measured by autofocus system in camera when taking the picture"
MakeInfoEntry 15 EXIF_ISO        "ISO equiv.:"  "ISO equiv.   : " "" \
    "The ISO equivalent sensitivity used when taking the picture"
MakeInfoEntry 16 EXIF_Whiteb     "Whitebalance:"  "Whitebalance : " "" \
    "The whitebalance adjustment used when taking the picture"
MakeInfoEntry 17 EXIF_Meter      "Metering:"   "Metering Mode: " "" \
    "The metering method that had been used to determine brightness"
MakeInfoEntry 18 EXIF_JQual      "Jpeg quality:"  "Jpeg Quality : " "" \
    "JPEG compression quality used to store the image (basic, normal, fine)"
MakeInfoEntry 19 EXIF_JProcess   "Jpeg process:"  "Jpeg process : " "" \
    "JPEG process to generate the image (baseline, progressive,...)"
MakeInfoEntry 20 EXIF_Orientation "Orientation:"  "Orientation  : " "" \
    "The image orientation (might need rotation, flipping etc.)"
MakeInfoEntry 21 EXIF_JComment    "Jpeg comment:" "Comment      : " "" \
    "JPEG comment field. Often set by image editors such as Gimp."

# -------------------------- Preview --------------------------------
image create photo PreviewImg -height $Option(VIEW_prevsize) -width $Option(VIEW_prevsize)
PreviewImg blank
image create photo TempImg
TempImg blank

label .picframe.preview -image PreviewImg -relief raised
RegisterHelp .picframe.preview "" \
    "Preview window for images (size can be changed in View menu)"

pack .picframe.preview -side top -expand 0 -padx 3 -pady 3 -fill both


# ------------------------- Captions ----------------------

label .textframe.namelab -text "Picture Name:"
entry .textframe.nameent -relief sunken \
    -textvariable NameVar 
RegisterHelp .textframe.namelab .textframe.nameent \
    "The picture name is shown below the thumbnail on the album page.\nIf there is no picture name, the file name will be used."
label .textframe.caplab -text "Picture Caption:"
frame .textframe.cap
text .textframe.cap.text -relief sunken -bd 2 \
    -yscrollcommand ".textframe.cap.scroll set" -setgrid 1 \
    -height $Option(VIEW_caption_height) -wrap word \
    -font [ lindex [ .textframe.nameent config -font ] end ] 
scrollbar .textframe.cap.scroll -command ".textframe.cap.text yview" \
    -takefocus 0
pack .textframe.cap.scroll -side right -fill y
pack .textframe.cap.text -expand yes -fill both
RegisterHelp .textframe.cap.text .textframe.caplab \
    "The picture caption is put below the picture on the HTML\npage that displays the large (or medium-sized) photo"
label .textframe.altlab -text AltTag:
entry .textframe.altent -relief sunken \
    -textvariable AltTagVar 
RegisterHelp .textframe.altlab .textframe.altent \
    "The AltTag pops up as a balloon, if the mouse is moved over\nthe picture. If no AltTag is specified, the picture name is used"
grid .textframe.namelab -row 0 -col 0  -sticky w
grid .textframe.nameent -row 0 -col 1  -sticky we
grid .textframe.altlab -row 2 -col 0  -sticky w
grid .textframe.altent -row 2 -col 1  -sticky we
grid .textframe.caplab -row 1 -col 0  -sticky wn 
grid .textframe.cap -row 1 -col 1  -sticky we
grid columnconfigure  .textframe 1 -weight 1

# ------------------------- Additional Bindings----------------------

bind .listframe.list <<ListboxSelect>> { LoadPicFile }
bind .listframe.list <Double-1> { DisplayOrGotoAlbum }
bind . <Destroy> { Exit }
bind .arrowframe.up <Control-Button-1> { MoveAllUp }
bind .arrowframe.up <3> { MoveAllUp }
bind .arrowframe.down <Control-Button-1> { MoveAllDown }
bind .arrowframe.down <3> { MoveAllDown }

bind all <Leave> "MotionHelpDispatch leave %W %X %Y"
bind all <Enter> "MotionHelpDispatch enter %W %X %Y"
bind all <Motion> "MotionHelpDispatch motion %W %X %Y"
bind all <Button> "MotionHelpDispatch button %W %X %Y"

bind all <<MenuSelect>> "MenuHelpDispatch select %W"
bind all <Unmap>        "MenuHelpDispatch unmap %W"
bind all <Key>          "KillBalloon"
bind all <Button>        "KillBalloon"

bind Entry <Meta-d>     ""
bind Text <Meta-d>     ""

# keep selection of listframe window when moving focus
proc RememberSelection { } {
    global CurrSel
    set CurrSel [ .listframe.list curselection ]
}

proc SetSelection { } {
    global CurrSel
    foreach el $CurrSel {
	.listframe.list selection set $el
    }
}

bind all <Tab> { RememberSelection; \
		     tkTabToWindow [tk_focusNext %W]; \
		     SetSelection }
bind all <<PrevWindow>> {  RememberSelection; \
			   tkTabToWindow [tk_focusPrev %W]; \
			   SetSelection }

# redefine tab key in text widget to move focus!
bind Text <Tab> { RememberSelection; \
		     tkTabToWindow [tk_focusNext %W]; \
		     SetSelection }

# undefine the Alt-Key for the menu since it leads always to problems
bind all <Alt-Key> ""

# ----Compute Display Name of Dir & Start Scan of Current Album -------------

ShortenCurrentAlbumName

ScanDirectory

# ------------------------- Check the Trash Bin -------------

after 2000 { after idle  CheckTrashBin }


###########################################################################
############################# License Text ################################
###########################################################################

set LicenseText {
		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                          675 Mass Ave, Cambridge, MA 02139, USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

			    NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	Appendix: How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

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

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

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19yy name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.
}

###########################################################################
############################# Man Page ####################################
###########################################################################

set ManPageText {
-- No Man Page written yet --
}

###########################################################################
############################# End of Text #################################
###########################################################################
