#!/usr/bin/wish
#########################################################################
# before this line, the path to wish should be set!
#########################################################################
set TKAlbumVersion "0.6b"
#########################################################################
#
#  TKAlbum last edit: 02-Oct-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  and you can view the license by 
#  clicking on the license menu entry under Help.
#
#
########################################################################
# ----------------------- GLOBAL SETTINGS ----------------------------
# in test mode, we use a different profile in order to avoid mixups
# with the version in use.
set Testing 0
# The strict motif switch
set tk_strictMotif 0
array unset Option
# Only if we are really alert, we want to see certain 
# error messages:
set Option(Alert) 0
# Root and Current Album:
set Option(RootAlbum) ""
set Option(CurrentAlbum) ""
# program calls:
set ScriptList { SCRIPT_ps SCRIPT_kill SCRIPT_file SCRIPT_album \
		     SCRIPT_jtransform SCRIPT_read_jc SCRIPT_write_jc \
		     SCRIPT_jhead SCRIPT_convert SCRIPT_mogrify \
		     SCRIPT_display_tmp SCRIPT_displaynext_tmp SCRIPT_edit}
set Option(SCRIPT_ps) ps
set Itest(SCRIPT_ps)  { "ps" "--version" \
			     {version (.*)$} "2.0.7"  }
set Option(SCRIPT_kill) kill
set Itest(SCRIPT_kill) { "kill" "-l" \
			     "" ""  }
set Option(SCRIPT_file) file
set Itest(SCRIPT_file) { "file"  "--version" {file-((?:[0-9]|\.)*)\n} \
			     "3.37"  }
set Option(SCRIPT_album) "album"
set Itest(SCRIPT_album) { "album"  "--version" \
			      {album version ((?:[0-9]|\.)*)\n}  "2.36"   }
set Option(SCRIPT_jtransform) "jpegtran"
set Itest(SCRIPT_jtransform) { "jpegtran (Independent JPEG Group)"  "-v -h" \
				   {version ((?:[0-9]|[a-z])*) }  "6b"   }
set Option(SCRIPT_read_jc) "rdjpgcom"
set Itest(SCRIPT_read_jc) { "rdjpgcom (Independent JPEG Group)"  "-h" \
				""  ""   }
set Option(SCRIPT_write_jc) "wrjpgcom"
set Itest(SCRIPT_write_jc) { "wrjpgcom (Independent JPEG Group)"  "-h" \
				 ""  ""   }
set Option(SCRIPT_jhead) "jhead -se"
set Itest(SCRIPT_jhead) { "jhead"  "-h" \
			      {v((?:[0-9]|\.)*) Matthias Wandel}  "1.8"   }
set Option(SCRIPT_convert) "convert"
set Itest(SCRIPT_convert) { "convert (ImageMagick)"  "-help" \
				{ImageMagick ((?:[0-9]|\.)*) }  "5.4.2"   }
set Option(SCRIPT_mogrify) "mogrify"
set Itest(SCRIPT_mogrify) { "mogrify (ImageMagick)"  "-help" \
				{ImageMagick ((?:[0-9]|\.)*) }  "5.4.2"   }
set Option(SCRIPT_display_tmp) "display"
set Itest(SCRIPT_display_tmp) { "display"  "-help" \
				    {ImageMagick ((?:[0-9]|\.)*) }  "5.4.2"   }
set Option(SCRIPT_displaynext_tmp) "display -remote"
set Itest(SCRIPT_displaynext_tmp) { "remote display"  "-help" \
				    {ImageMagick ((?:[0-9]|\.)*) }  "5.4.2"   }
set Option(SCRIPT_edit) "display"
set Itest(SCRIPT_edit) { "image editor" "-help" "" "" }
set Option(SCRIPT_tempdir) "/tmp"
# File handling options: 
set Option(FILE_cc_edit) 1
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(FILE_symlink_warning) 1
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(JPEG_RemovalWarning) 1
set Option(VIEW_cropping) 1
set Option(VIEW_rescale_thumbnail) 0
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_UseAlbumName) 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_cleanup) 0
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_InstallCheck) 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 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 ""
set CurrentProfile ""
set ProfileList { }

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

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

# Times
set HelpDelay 500
set HelpTime 5000

# profile file names
set UserProfileDir "~/.tkalbum"
set UserProfilePointer "profile"
set UserDefaultProfile "default"
set OldUserProfile "~/.tkalbumrc"
if { $Testing } {
    append UserProfileDir $TKAlbumVersion
}


# 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 }
# PreviewSizeList should not exceed MaxPrevSize ( =500 )!


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

entry .dummyent
set TextCursor [  lindex [ .dummyent config -cursor ] end ]
destroy .dummyent

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 UndefPhoto [ image create photo -data \
"R0lGODlhZABkAPYAAP///zk5OffvhPfve+/eKe/eIe/eGO/eEO/eCPfnAO/eAPfvc/fva/fv
Y+/nWvfvWu/nUu/nSu/nQvfvQu/nOe/nMe/nKefeEOfeCPfvKe/nIe/nGP/3GO/nEP/3EPfv
EP/3CPfvCO/nCPfvAO/nAOfeAP//9///7///5///3jk5Mf//1v//zv//xkJCMf//vf//tf//
rUpKMf//pf//nPf3lFJSMf//lP//jFpaMf//hGNjMf//e///c///a+/vY///Y2trKe/vWnNz
Kf//Wu/vUnt7Kf//UoSEKf//SoyMKf//Qu/vOf//OZSUIZycIe/vMaWlIf//Ma2tIbW1Ie/v
Kf//Ke/vIff3If//IbW1GL29GMbGGM7OGNbWGN7eGO/vGP//GPf3GN7eEO/vEOfnEP//EPf3
EP//CPf3CO/vCO/vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAABkAGQAAAf+gAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAD+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAJhoIGGohaiEhISQYJCUl
HQ8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwiIiIIJCRqZ2dnISFqIiIlJGv+gCEhIRUL
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAJyEhIWohIWohIWlhWVlmZ2YgHmkhISAeISEhIgokCCcAAAAA
AAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAABQiISEhaWlpaWcgYWZhYWZoZmZmZ2doHh5naWkhIiIKCg0AAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+gAAAAAAAAAAAAAAAAAAAAAAAAAAA
ACIIJCRqHmhmZmZmZmZmYU1WZmZhOGFmZmZmS1ZLTWZmZmghaiFpaQMAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYiIiIhaYBm
Z2ZmZmZmZmZhYWZmZllZYWZZYWFhZmZhYWFhYWchaWhpIUwAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVyEhIWlpZmhmZmZmZmZmZmZm
ZmZmZmYzNC8sKSsrLDA4PD7+gEBNVmFhSVlmZ2lqJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISFnaWhoHmZmZmZmZmZmZmZmZmZmZmZJ
QEA9PDg0LS0sLS8vOkBLWU1NZmYgISEdAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAJCEeaGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZllSRDwtKy0wNEQ9VlJnISQlBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAD+gAAAAAAAAAAAAAAkJCFnZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmFN
RzErKyw8UlZAGSEiCiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAABoKImloZmZmZmZmZmZmZmZmZoBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZllEMyk3
R0lJYWFpIWkTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkIiEg
aGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmb+gGZmZk0xLT1NWVZh
ZmlpRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIhIWdmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmUjwvN1JWUmFmICEhAAAA
AAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkImlnZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZk04LDRLTU1haSEiAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+gBgYImdmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmFWLys0PllWZmciIgAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAABslIWloZmZmZmZmZmZmZmZmZmZmZmZmZmZmZoBmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZZRCsvTU1hZiEiGwAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAQIiJnHmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmb+gGZmZmZmYTQsPURLVmYhIhQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAiImlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZZRCwtRFZmaCIIAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAIiFq
aWZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZhMCs8SVlmaSIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaiFpHmZmZmb+gGZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
Uj4sM0RNZiAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISEgZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZoBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZhTSstQFlm
ZiFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFpZmhmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmb+gGZmZmZmZjosMT5LWWdqIQAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAVWkeaGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZJNCwzTU0eaiEAAAAAAAAAAAAA
AIAAAAAAAAAAAAAAAAAkImZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZkEyZmZmZmZmZmZmZmZm
ZmZmZmZmZl4BAQFmZmZmZmZmZmZmZmZmZmZmZmZhLyxSWWZpISIAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAJCJmZmZmZmZmZmZmZmb+gGZmZmZmZmZmZmZmSgEBAWZmZmZmZmZmZmZmZmZmZmZm
ZgEBAQFkZmZmZmZmZmZmZmZmZmZmZmZmOCtEUmFpISQAAAAAAAAAAAAAAAAAAAAAAAAAAAAk
IiFmZmZmZmZmZmZmZmZmZmZmZmZmZmZmAQEBAQEBZmZmZmZmZmZmZoBmZmZmZmYBAQEBAQEB
ZmZmZmZmZmZmZmZmZmZmZmZSMDFJRGZpJAoAAAAAAAAAAAAAAAAAAAAAAAAAHSRqaWZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmYBAQEBAQFcZmZmZmZmZmZmZmZmZmZOAQEBAQEBAWZmZmZmZmZm
ZmZmZmb+gGZmZmZhPSw8M2FpGCUAAAAAAAAAAAAAAAAAAAAAAAAAIiJpZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZgEBAQEBAQEBZmZmZmZmZmZmZmZmZmYBAQEBAQEBAUpmZmZmZmZmZmZmZmZm
ZmZmZlkrLEBWZiQKBAAAAAAAAAAAAAAAAIAAAAAAACciIWZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmAQEBAQEBAQFDZmZmZmZmZmZmZmZmZgEBAQEBAQEBAWZmZmZmZmZmZmZmZmZmZmZmZjcp
MFZmISUlAAAAAAAAAAAAAAAAAAAAAAAiISFmZmZmZmZmZmZmZmZmZmZmZmb+gGZmZmZmAQEB
AQEBAQEBZmZmZmZmZmZmZmZmZgEBAQEBAQEBAWZmZmZmZmZmZmZmZmZmZmZmZkcrLWFhIQol
BgAAAAAAAAAAAAAAAAAAAABqaSBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZgEBAQEBAQEBAWZm
ZmZmZmZmZmZmZoBmAQEBAQEBAQEBZmZmZmZmZmZmZmZmZmZmZmZmYTgvWVJpIiUlAAAAAAAA
AAAAAAAAAAAAUCEgaGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmAQEBAQEBAQEBZmZmZmZmZmZm
ZmZmZgEBAQEBAQEBAWZmZmZmZmZmZmZmZmZmZmb+gGZmZkAxYT1naiUkAAAAAAAAAAAAAAAA
AAAAIWlmaGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmAQEBAQEBAQEBZmZmZmZmZmZmZmZmZgEB
AQEBAQEBAWZmZmZmZmZmZmZmZmZmZmZmZmZSLVJhZmkiJAAAAAAAAAAAAAAAAAAAAIAfaWZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYBAQEBAQEBAQFmZmZmZmZmZmZmZmZmAQEBAQEBAQEB
ZmZmZmZmZmZmZmZmZmZmZmZmZlksR1lmaSIlAAAAAAAAAAAAAAAAAAAAIWlmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmb+gGYBAQEBAQEBAU5mZmZmZmZmZmZmZmZmAQEBAQEBAQEBZmZmZmZm
ZmZmZmZmZmZmZmZmZmYvPFlmZwglJQAAAAAAAAAAAAAAAAAAIiFoZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmAQEBAQEBAQFmZmZmZmZmZmZmZmZmZgEBAYABAQEBAVtmZmZmZmZmZmZmZmZm
ZmZmZmZmZjQzTWYeJBgKAAAAAAAAAAAAAAAAAF8kIWhmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmAQEBAQEBSGZmZmZmZmZmZmZmZmZmXgEBAQEBAQFmZmZmZmZmZmZmZmZmZmZmZmZmZmb+
gEQrOmZmIiUlAAAAAAAAAAAAAAAAAAgkIWZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmAQEB
AQEBZmZmZmZmZmZmZmZmZmZmZgEBAQEBAQFmZmZmZmZmZmZmZmZmZmZmZmZmZmZLKzpmZiIl
JQAAAAAAAAAAAAAAAAAkamlmZoBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmAQEBAV5mZmZm
ZmZmZmZmZmZmZmZmZjYBAQFmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZlIrOmZhISIKHQAAAAAA
AAAAAAAAAApqIGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmb+gAEBW2ZmZmZmZmZmZmZm
ZmZmZmZmZmZRNlFmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZlYrOGZhISQlIgAAAAAAAAAAAAAA
ACQhaGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZoBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmWSs8ZllpISUkAAAAAAAAAAAAAAAAJGloZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmEpOGb+gFYeagokAAAAAAAAAAAAAAAAJCFpZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmEpMGZZZiEiCAAAAAAAAAAAAAAAABghHmZmZmZmZoBmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmYSsvZlZnIQoiAAAAAAAAAAAAAAAAJWogZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmb+gGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZiksYVlpJCUkAAAAAAAAAAAAAAAAImppZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZoBmZmZmZmZmZmZmZmZmZmZmZmZmKSxhYSEl
JSQAAAAAAAAAAAAAAAAKImlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYSkxYWEiCiX+gAoAAAAA
AAAAAAAAAAAkImlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmWSkzUmYiJSUlAAAAAAAAAAAAAAAA
CCJpZmZmZmZmZmZmZoBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZZKTNhZiIKCiUAAAAAAAAAAAAAAAAiamlmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmb+gGZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZWKzRmZmoKChcAAAAAAAAAAAAAAAAiamdmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZoBm
ZmZmZmZmZmZmZmZmZlItQGZmISIYBAAAAAAAAAAAAAAAABRqaWZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZEMElmICEiCAAAAAD+gAAAAAAAAAAAAABqHmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmY4
MElhaCEiJAAAAAAAAAAAAAAAAAAAaiFmZmZmZmZmZmZmZmZmZoBmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZi0+ZmYeJAgY
AAAAAAAAAAAAAAAAAAAiIWlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
Z2T+gFxIX2dmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZixNZmYgJCUFAAAAAAAA
AAAAAAAAAAAkJSFpZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZk8BAQEBAQEBAQEB
AQEBAQFnZmZmZmZmZmZmZmZmZmZmZoBmZmZmZmZmZmZWLWFWaGkKCgAAAAAAAAAAAAAAAAAA
ACQYIWlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYuAQEBAQEBAQEBAQEBAQEBAS5e
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmSy9mWWYgJAoAAAAAAAAAAAD+gAAAAAAAAAAkIWlm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZnAQEBATtcZmZmZmZmZmZmZmZGAQEBAQFmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmMTphZmZpIiUAAAAAAAAAAAAAAAAAAAAAIiQhaGZmZmZmZmZm
ZmZmZmZmZoBmZmZmZmZmZmZmYwEBAS4bZmZmZmZmZmZmZmZmZmZmNgEBAV1mZmZmZmZmZmZm
ZmZmZmZmZmZmZlYsSWFmHmklFwAAAAAAAAAAAAAAAAAAAAAnGCRnZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmSAEBZ2ZmZmZmZmZmZmZmZmb+gGZmZmZmZmZnNgEBZmZmZmZmZmZmZmZmZmZm
ZmZmZjgtWWZmZyEiAAAAAAAAAAAAAAAAAAAAAAAAJSJnaGZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmY7AQFBZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYBAQFmZmZmZmZmZmZmZmZmZoBmZmZmKy9m
ZmZnIQYAAAAAAAAAAAAAAAAAAAAAAAAlJSFoZmZmZmZmZmZmZmZmZmZmZmZmZmZmZQEBZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZdAQFmZmZmZmZmZmZmZmZmZmZmUi06ZmZmaSEAAAAA
AAAAAAAAAAAAAAD+gAAAAAAAJCIhZmZmZmZmZmZmZmZmZmZmZmZmZmZmAUFmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZQEyZmZmZmZmZmZmZmZmZmZhLT1hZmhnIWoAAAAAAAAAAAAA
AAAAAAAAAAAAABQkamZmZmZmZmZmZmZmZmZmZmZmZoBmZmcBZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmMgFmZmZlZmZmZmZmZmZmZkQsUmFmaWokPwAAAAAAAAAAAAAAAAAAAAAA
AAAAACUiaWZmZmZmZmZmZmZmZmZmZEMBNmYBLmZmZmZmZmZmZmZmZmZmZmZmZmZmZmb+gGZm
ZmZmZmZmZgEqAQEBZmZmZmZmZmZmZiktWWYgagoWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgK
ISBmZmZmZmZmZmZmZmZmOwEBAQEBZ2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYB
AQFbZmZmZmZmZmZmZk0rOIBmZmciCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIkah5mZmZm
ZmZmZmZmZmZmZmZmKgFmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZgEBZmZmZmZm
ZmZmZmY4Kzw+ZiBqJB0AAAAAAAAAAAAAAAAAAAAAAAAAAAD+gAAAAAAlImloZmZmZmZmZmZm
ZmZmZmZmZipIZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZgE2ZmZmZmZmZmZmZmYp
LFZmZmchIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGCQhHmZmZmZmZmZmZmZmZmZmZmZm
AYBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmYBWmZmZmZmZmZmZmZmRCswZmZnISIA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIiEgZmZmZmZmZmZmZmZmZmZmZmYqXmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmb+gGZmZmYqZmZmZmZmZmZmZmY+KTA4ZmYgIWoAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhZ2ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZNKSs9YWZpaWoAAIAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAIWppZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmYTEoKVlmZmkhIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAD+gAAAACYhISBoZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZWKCkvS2YeaWkmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAJGohZmZmZmZmZmZmZmZmZmZmZmZmZoBmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZjEoKUlmZ2khaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACiRp
aGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmb+gGZmZmZmZmZm
KCkpSWYgIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUhZ2ZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmOigpTWZnISQl
AAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhZyBmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZi0pNGEeaSQkFQAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+gAAAAANpHmceZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmPCgrYR4hISIkAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEhIWloZmZmZmZmZmZmZmZmZmZmZoBmZmZm
ZmZmZmZmZmZmZmZmZmZmZmZmZmZmMykpUmZnaSEhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhISEhaWZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZmZmZmZmZmY6KSz+gFJmaWkhaUIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAJCQJIR8gZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm
ZmZmZj4nLFloaWoiIVcAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAABglCCFpZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZWSWFm
aWoiJRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+
gAAAAAAAAAAkJSEhZ2hmaGZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmdpIgolAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AABQaiEhaWlpHmlmaGZmZmZmZoBmZmZmZmZmZmZmZmZmZmhnaSEhISIkAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGiEi
aiEhIWlnZmZmZmZmZmZmZmZmZmZmaGZmaWlpaiQkImobAAAAAAD+gAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXyIKCCRq
aWkgIGlnaWdpIB5mICAeICEhIiIiJSUEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQICiJpaWkhISFq
aiIiamlpaWkhISQiIggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+gAAAAAAAAAAAAAAAAAAAAAAAAD8VISEkCCUkJWpp
ZxMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAD+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACvgAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAgQA7" ]

image create photo SolidPic -width $MaxPrevSize -height $MaxPrevSize 
SolidPic put [ SolidPic data -background red ]

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 ---------------------------


# It is now a bit more complicated than before. 
# We assume the directory ".tkalbum", which should contain 
# "profile", which in turn should contain a file name of the profile -
# usually to be stored under .tkalbum. The following lines in
# profile contain the list of all known profiles.
# If "profile" is not present, we use "default" in ".tkalbum"
# If this is also missing, we copy  .tkalbumrc (if present) to this place.
# Profiles can now be selected and added

proc ReadProfile { } {
    global CurrentProfile ProfileList
    global UserProfileDir
    global UserDefaultProfile
    global UserProfilePointer
    global OldUserProfile
    global TKAlbumVersion


    if { ! [ file isdirectory $UserProfileDir ] } {
	if { [ catch { file mkdir $UserProfileDir } errmes ] } {
	    tk_dialog .dia "Profile Error" "Could not generate profile directory \"$UserProfileDir.\" Got the following error message: \"$errmes.\" Will abort program execution." error 0 OK
	    exit
	}
    }
    set pointerfile [ file join $UserProfileDir $UserProfilePointer ]
    set defaultprofile [ file join $UserProfileDir $UserDefaultProfile ]
    if { ! [ file exist $pointerfile ] } {
	# This is the initialization for newly installed progs
	set fid [ GuardedWOpen exit "Profile Error" $pointerfile ]
	GuardedWrite exit "Profile Error" $pointerfile $fid \
	    [ file join $UserProfileDir $UserDefaultProfile ]
	catch { close $fid }
	if { ! [ file exist $defaultprofile ] }  {
	    if { [ file exist $OldUserProfile ] } {
		    # if there is an older version, copy it to the 
		    # default place - if wanted
		if { [ tk_dialog .dia "Profile Initialization" "This seems to be the first time that you have started TKAlbum version $TKAlbumVersion. Do you want me to copy your old profile $OldUserProfile to $defaultprofile in order to use it as the new default profile?" questhead 0 Yes No ] == 0 } {
		    catch { file copy -force $OldUserProfile $defaultprofile }
		} 
	    }
	}
    }
    set fid [ GuardedROpen return "Profile Error" $pointerfile ]
    if { $fid == -1 } {
	# should not happen!
	return
    }
    set CurrentProfile ""
    set ProfileList ""
    GuardedRead return "Profile Error" $pointerfile $fid CurrentProfile
    if { $CurrentProfile != "" } {
	if { ! [ file exist $CurrentProfile ] } {
	    set CurrentProfile ""
	} else {
	    set ProfileList [ list $CurrentProfile ]
	}
    }
    while { ! [ eof $fid ] } {
	GuardedRead return "Profile Error" $pointerfile $fid NextProfile
	if { $NextProfile != "" && $NextProfile != $CurrentProfile &&
	     [ file exist $NextProfile ] } {
	    lappend ProfileList $NextProfile
	}
    }
    catch { close $fid }
    if { [ llength $ProfileList ] == 0 } {
	set ProfileList [ list $defaultprofile ]
    }
    if { $CurrentProfile == "" } {
	set CurrentProfile [ lindex $ProfileList 0 ]
    }
    set errmes ""
    if { [ catch { ReadOneProfile $CurrentProfile } errmes ] } {
	if { $errmes != "" && $errmes != "catch" } {
	    set err "The following error happened: \"$errmes.\" "
	} else {
	    set err ""
	}
	tk_dialog .dia "Profile initialization error" "Sorry, but it was impossible to read the profile \"$CurrentProfile.\"$err Currently, no profile is selected.  Try to resolve the problem by deleting inadequate files and set the right file access permissions." error 0 OK
	set CurrentProfile ""
    } 
}
    
proc ReadOneProfile { profile  } {
    global TKAlbumVersion
    global tk_strictMotif
    global Option

    set converted 0
    if { [ file exists $profile ] } {
	set IgnoreList { }
	set fid [ GuardedROpen error "Profile Error" $profile ]
	GuardedRead error "Profile Error" $profile $fid Version
	if { [ eof $fid ] } {
	    return
	}
	if { ! [ regexp {^([0-9]+\.)+[0-9]*[a-z]?$} $Version ] } {
	    catch { close $fid }
	    tk_dialog .dia "Profile Error" "The file \"$profile\" is not a valid profile. The first line contains \"$Version\" instead of a version number" \
		error 0 OK
	    error catch
	}
	set IgnoreList { "STATE_InstallCheck" \
			     "EXIF_RemovalWarning" \
			     "VIEW_ShowDirs" }
	if { $Version != $TKAlbumVersion } {
	    if { [ lindex [ lsort -dictionary \
				[ list $Version  $TKAlbumVersion ] ] \
		       1 ] == $TKAlbumVersion  } {
		set converted 1
		if { [ tk_dialog .dia "Version Warning" "Have detected old profile \"$profile\" with version $Version. Current TKAlbum version is $TKAlbumVersion. Do you want me to proceed and convert the profile to the new TKAlbum version?" \
			   questhead 0 Yes "No, abort" ] == 1 } {
		    error "catch"
		}
	    } else {
		set string "Have detected profile \"$profile\" with incompatible TKAlbum version ($Version). Current version is $TKAlbumVersion. Please delete before proceeding."
		tk_dialog .dia "Version Error" $string error 0 OK
		error "catch" 
	    }
	}
	GuardedRead error "Profile Error" $profile $fid tk_sM
	if { [ eof $fid ] } {
	    return
	}
	if { $tk_sM != 0 && $tk_sM != 1 } {
	    catch { close $fid }
	    tk_dialog .dia "Profile Error" "The file \"$profile\" is not a valid profile. The second line contains \"$Version\" instead of a 0 or 1." \
		error 0 OK
	    error catch
	}
	set tk_strictMotif $tk_sM
	while { [ expr ! [ eof $fid ] ] } {
	    GuardedRead error "Profile Error" $profile $fid Name
	    if { [ eof $fid ] } {
		break
	    }
	    if { [ lsearch $IgnoreList $Name ] == -1 } {
		GuardedRead error "Profile Error" \
		    $profile $fid Option($Name)
	    } else {
		GuardedRead error "Profile Error" $profile $fid dummy
	    }
	}
	close $fid 
	if { $converted } {
	    catch { WriteOneProfile $profile }
	}
    }
}

proc WriteProfile { } {
    global CurrentProfile ProfileList
    global UserProfileDir
    global UserProfilePointer

    if { $CurrentProfile == "" } {
	return
    }
    set pointerfile [ file join $UserProfileDir $UserProfilePointer ]
    set fid [ GuardedWOpen return "Profile Writing Error"  $pointerfile ]
    if { $fid == -1 } { return }
    catch { GuardedWrite error "Profile Writing Error" $pointerfile \
		$fid $CurrentProfile }
    foreach el $ProfileList {
	catch { GuardedWrite error "Profile Writing Error " $pointerfile \
		    $fid $el }
    }
    catch { close $fid }
    catch { WriteOneProfile $CurrentProfile }
    SetGroupPerms $CurrentProfile
}

proc WriteOneProfile { profile } {
    global TKAlbumVersion
    global tk_strictMotif
    global Option
    
    set fid [ GuardedWOpen error "Profile Writing Error" $profile ]
    GuardedWrite error "Profile Writing Error" $profile \
		 $fid $TKAlbumVersion 

    GuardedWrite error "Profile Writing Error" $profile \
		 $fid $tk_strictMotif 
    foreach key [ array names Option ] {
	GuardedWrite error "Profile Writing Error" $profile \
		     $fid $key 
	GuardedWrite return "Profile Writing Error" $profile \
		     $fid $Option($key)
    }    
    close $fid 
}

# menu entry "Load New Profile"
proc LoadNewProfile { } {
    global CurrentProfile
    global ProfileList
    global Option
    global ViewerProcesses
    global CurrentIx


    SaveCaptions

    if { [ PwdFails ] } {
	return
    }
    set newprofile [ tk_getOpenFile \
			 -initialdir [ file dirname $CurrentProfile ] \
			 -initialfile [ file tail $CurrentProfile ] \
			 -title "Load New Profile" ]
    if { $newprofile == "" } {
	return
    }
    set newprofile [ NormalizeFileName \
			 [ file dirname $CurrentProfile ] \
			 $newprofile ] 
    set prefix [ glob ~ ]
    if { [ string first $prefix $newprofile 0 ] == 0 } {
	set newprofile "~[ string range $newprofile \
                                  [ string length $prefix ] end ]"
    }
    if { [ lsearch $ProfileList $newprofile ] != -1 } { 
	tk_dialog .dia "Profile Generation Error" "The selected profile \"$newprofile\" is not a new profile but an already known profile which can be selected" warning 0 OK 
	return
    }
    .listframe.list selection clear 0 end
    set CurrentIx -1
    WriteProfile
    if { [ catch { ReadOneProfile $newprofile } ] } {
	tk_dialog .dia "Profile load error" "Sorry, but it was impossible to read the profile \"$newprofile.\"" error 0 OK	
	return
    }
    lappend ProfileList $newprofile
    set CurrentProfile $newprofile
    StateSettings
    .mbar.pref.menu.profile add radiobutton -label $CurrentProfile \
	-variable CurrentProfile -value $CurrentProfile \
	-command SelectProfile
    KillProcesses $ViewerProcesses
    set ViewerProcesses -1
    ResizePreview
    GotoAlbum $Option(CurrentAlbum) ""
    WriteProfile
}

# Menu entry: Save Profile as
proc SaveProfileAs { } {
    global CurrentProfile
    global ProfileList
    global Option
    global ViewerProcesses
    global CurrentIx
    global UserProfileDir
    global UserProfilePointer

    SaveCaptions

    if { [ PwdFails ] } {
	return
    }
    set newprofile [ tk_getSaveFile \
			 -initialdir [ file dirname $CurrentProfile ] \
			 -initialfile [ file tail $CurrentProfile ] \
			 -title "Save Profile as ..." ]
    if { $newprofile == "" } {
	return
    }
    set newprofile [ NormalizeFileName \
			 [ file dirname $CurrentProfile ] \
			 $newprofile ] 
    set prefix [ glob ~ ]
    if { [ string first $prefix $newprofile 0 ] == 0 } {
	set newprofile "~[ string range $newprofile \
                                  [ string length $prefix ] end ]"
    }
    if { [ lsearch $ProfileList $newprofile ] != -1 } { 
	tk_dialog .dia "Profile Save Error" "The selected profile \"$newprofile\" is not a new profile but an already known profile!" warning 0 OK 
	return
    }
    if { $newprofile == [ file join $UserProfileDir $UserProfilePointer ] } {
	tk_dialog .dia "Profile Save Error" "It is not allowed to overwrite the list of profiles in [ file join $UserProfileDir $UserProfilePointer ]!" warning 0 OK 

	return
    }
    WriteProfile
    lappend ProfileList $newprofile
    set CurrentProfile $newprofile
    .mbar.pref.menu.profile add radiobutton -label $CurrentProfile \
	-variable CurrentProfile -value $CurrentProfile \
	-command SelectProfile
    WriteProfile
}



# command that is invoked after selecting a new profile
proc SelectProfile { } {
    global CurrentProfile Option
    global ViewerProcesses
    global CurrentIx

    SaveCaptions
    if { [ catch { ReadOneProfile $CurrentProfile } ] } { 
	tk_dialog .dia "Profile initialization error" "Sorry, but it was impossible to read the profile \"$CurrentProfile.\"" error 0 OK
	return
    }
    .listframe.list selection clear 0 end
    set CurrentIx -1
    StateSettings
    KillProcesses $ViewerProcesses
    set ViewerProcesses -1
    ResizePreview
    GotoAlbum $Option(CurrentAlbum) ""
}
    

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

 
proc GuardedWOpen { type title file } {
    if { [ catch { set fid [ open $file "w" ] } errmes ] } {
	tk_dialog .dia $title \
	    "Could not open file \"$file\" for writing: \"$errmes.\"" \
	    error 0 OK
	if { $type == "exit" } {
	    exit
	} elseif { $type == "error" } {
	    error catch
	} else {
	    return -1
	}
    }
    return $fid
}

proc GuardedROpen { type title file } {
    if { [ catch { set fid [ open $file "r" ] } errmes ] } {
	tk_dialog .dia $title \
	    "Could not open file \"$file\" for reading: \"$errmes.\"" \
	    error 0 OK
	if { $type == "exit" } {
	    exit	
	} elseif { $type == "error" } {
	    error catch
	} else {
	    return -1
	}
    }
    return $fid
}

proc GuardedWrite { type title file fid string } {
    if { [ catch { puts $fid $string } errmes ] } {
	tk_dialog .dia $title \
	    "Could not write to file \"$file.\" Error message was: \"$errmes.\"" \
	    error 0 OK
	if { $type == "exit" } {
	    exit
	} elseif { $type == "error" } {
	    error catch
	} else {
	    return 0
	}
    }
    return 1
}
    
proc GuardedRead { type title file fid var } {
    upvar $var x
    if { [ catch { gets $fid x } errmes ] } {
	if { [ eof $fid ] } {
	    set x ""
	    return 1
	} else {
	    tk_dialog .dia $title \
		"Could not read from file \"$file.\" Error message was: \"$errmes.\"" \
		error 0 OK
	    if { $type == "exit" } {
		exit
	    } elseif { $type == "error" } {
		error catch
	    } else {
		return 0
	    }
	}
    }
    return 1
}


proc BusyCursor { { cursor watch } } {
    global Option


    . config -cursor $cursor
    foreach ent [ array names Option EXIF_* ] {
	.info.v$ent config -cursor $cursor
    }
    .messageframe.dir.root.val config -cursor $cursor
    .messageframe.dir.current.val config -cursor $cursor
    .textframe.cap.text config -cursor $cursor
    .textframe.nameent config -cursor $cursor    
    .textframe.altent config -cursor $cursor
}

proc IdleCursor { } {
    global Option
    global TextCursor

    . config -cursor {}
    foreach ent [ array names Option EXIF_* ] {
	.info.v$ent config -cursor {}
    }
    .messageframe.dir.root.val config -cursor {}
    .messageframe.dir.current.val config -cursor {}
    .textframe.cap.text config -cursor $TextCursor
    .textframe.nameent config -cursor $TextCursor    
    .textframe.altent config -cursor $TextCursor
}

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.thumbs .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) == "" } {
	return
    }
    if { [ file isdirectory $file ] } {
	set perm "g+rwx"
    } elseif  { [ file isfile $file ] } {
	set perm "g+rw"
    } else {
	return
    }
    if { [ file attr $file -owner ] == $env(USER) } {
	if { [ catch { file attribute $file -perm $perm \
			   -group $Option(STATE_GroupName) } errmes ] } {
	    if { $IgnoreGroupErrors } { return }
	    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
	    }
	}
    }
}

proc NormalizeFileName { prefix new } { 
    set path [ file join $prefix $new ]
    set complist [ file split $path ]
    set updir 0
    set newlist { }
    foreach el [ lreverse $complist ] {
	if { $el != "." } {
	    if { $el == ".." } {
		incr updir
	    } else {
		if { $updir > 0 } {
		    set updir [ expr $updir - 1 ]
		} else {
		    lappend newlist $el
		}
	    }
	}
    }
    lappend newlist "/"
    set name [ eval file join [ lreverse $newlist ] ]
    return $name
}


proc CheckProgramInstalled { scriptname } {
    global Option
    global TKAlbumVersion
    global errorCode
    global Itest

    set call $Option($scriptname)
    set printname [ lindex $Itest($scriptname) 0 ]
    set argstring [ lindex $Itest($scriptname) 1 ]
    set pat [ lindex $Itest($scriptname) 2 ]
    set reqver [ lindex $Itest($scriptname) 3 ]

    set mess ""
    set errmes ""
    set errorCode NONE
    if { [ catch { set helpstr [ eval exec $call \
				     $argstring ] } errmes ] } {
	# error occured when trying to execute the program
	# if it is a CHILDSTATUS error, we interpret this as
        # as the message from the program, otherwise we assume it is
        # an error
	if { [ lindex $errorCode 0 ] == "CHILDSTATUS" } {
	    set helpstr $errmes
	    set errorCode NONE
	} 
    }
    if { $errorCode != "NONE" } {
	set mess "The program \"$printname\" does not seem to be "
	append mess "installed. Got the "
	append mess "error message\n\"[ string range $errmes 0 256 ]"
	if { [string length $errmes ] > 256 } {
	    append mess "..."
	}
	append mess "\"\nwhen executing" 
	append mess "\"$call $argstring.\" Either "
	append mess "the program is not installed at all "
        append mess "or the path to the program "
	append mess "must be specified in the \"General "
	append mess "options\" dialog."
    } else {
	set currversion ""
	regexp $pat $helpstr dummy currversion
	if { $currversion == "" && $pat != "" } {
	    set mess "The program \"$printname\" seems to be installed, "
            append mess "but the version "
	    append mess "information is not accessible. Perhaps it is a "
	    append mess "different program with the same name?\n\nThe call "
	    append mess "\"$call $argstring\" produced the "
	    append mess "following output:\n\"[ string range $helpstr 0 256 ] "
	    if { [ string length $helpstr ] > 256 } {
		append mess " ..."
	    }
	    append mess "\""
	} else {
	    if { $currversion != $reqver } {
		set verorder [ lsort -dictionary \
				   [ list $currversion $reqver ] ]
		if { [ lindex $verorder 0 ] == $currversion } {
		    set mess "The program \"$printname\" seems to be installed, but "
		    append mess "it appears to be an old version. TKAlbum "
		    append mess "$TKAlbumVersion was tested with $printname "
		    append mess "$reqver. The installed version is "
		    append mess "$currversion. This can lead to problems."
		}
	    }
	}
    }
    if { $mess != "" } {
	append mess "\n\nYou can disable the "
	append mess "installation check for future program starts "
	append mess "in the \"General "
	append mess "options\" dialog."
	
	after idle {.dia.msg configure -wraplength 5i }
	set response [ tk_dialog .dia "Installation Problem" $mess \
			   warning 0 "Continue" \
			   "Continue w/o checks" \
			   "Abort program"  ]
	if { $response == 2 } { Exit }
	if { $response == 1 } { return 0 }
	if { $response == 0 } { return 1 }
    }
}

proc InstallCheck { } {
    global Option
    global ScriptList


    if { ! $Option(STATE_InstallCheck) } {
	return
    }
    # Check for component programs
    foreach prog $ScriptList {
	if { [ eval CheckProgramInstalled $prog ] == 0 } {
	    break
	}
    }
    # Check whether temp directory id writeable
    set tempname [ MakeTempName "" ]
    set errmes ""
    set err [ catch { set fid [ open $tempname "w" ] } errmes ]
    if {! $err } {
	set err [ catch { puts $fid "test" } errmes ]
	if {! $err } {
	    set err [ catch { close $fid  } errmes ]
	    if { ! $err } {
		set err [ catch { file delete -force $tempname } errmes ] 
	    }
	}
    }
    if { $err } {
	if { [ tk_dialog .dia "Temporary directory Warning" \
	    "Got an error manipulating files in the temporary directory $Option(SCRIPT_tempdir): \"$errmes\"" warning  0 "Continue" \
		   	   "Abort program"  ] == 1 } {
	    Exit
	}
    }
}
    

# ----------------------- 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  } {
	    set PicName($file)  ""
	    set Caption($file)  ""
	    set AltTag($file)  ""
	    if { [ file isfile $file ] } { 
		lappend FileSubList $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) && \
		  $file != $Option(ALBUM_hide_album) && \
		  ! [ string match "*.txt" $file ] && \
		  ! [ string match "*.htaccess" $file ] && \
		  ! [ string match "*~" $file ] && \
		  $file != "$Option(ALBUM_index).html" && \
		  $file != "$Option(ALBUM_index).htm" && \
		  $file != "CVS" && \
		  $file != "SCCS" && \
		  $file != "RCS" && \
		  $file != ".xvpics" ) ||
		! $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 { $Option(VIEW_ShowDirs) || $file == "." } {
		    if { [ string first "." $file ] != 0 || \
			     ($Option(ALBUM_all) == 1  && $file != "..") || \
			     $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) } {
		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 != "." && $file != ".." } {
		set subdirname [ SubDirName $file ]
		if { $subdirname != "" } {
		    set PicName($file) $subdirname
		}
	    }
	    if { [ file type $file ] == "link" } {
		set type "S"
	    } else {
		set type "A"
		if { $file == $Option(ALBUM_dir) } {
		    set type "T"
		}
	    }
	    set name $file
	    if { $Option(VIEW_UseAlbumName) } {
		set cap ""
		catch { set cap $PicName($file) }
		if { $cap != "" } {
		    set name $cap
		}
	    }
	    if { [ file isfile [ file join $file \
				     $Option(ALBUM_hide_album) ] ] } {
		lappend DisplayList "\[h$type\]$name"
	    } elseif { [ file isfile [ file join $file \
					   $Option(ALBUM_no_album) ] ] } {
		lappend DisplayList "\[n$type\]$name"
	    } else {
		lappend DisplayList "\[$type\] $name"		    
	    }
	}
    }

    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 tail [ pwd ] ] == $fname } {
		set PicName(.) $names
	    }
	    if { [ file exists $fname ] } {
		if { [ file isfile $fname ] || 
		     ( [ file isdirectory $fname ] && \
			   $Option(VIEW_ShowDirs) ) } {
		    if { [ lsearch $NameList $fname ] == -1 } {
			lappend NameList $fname
		    }
		    if { $fname != "." && $fname != ".." } { 
			set PicName($fname) $names
			if { [ file isfile $fname ]  } { 
			    set Caption($fname) $caption
			    set AltTag($fname) $alttag
			} else {
			    set Caption($fname) ""
			    set AltTag($fname) ""
			}
		    }
		}
	    }
	}
	catch { close $fid }
    }
}

# have a look into the caption file of a subdirectory and check
# whether this subdir renames itself by mentioning its own name
proc SubDirName { dir } {
    global Option
    
    set namefound ""
    set cap [ file join $dir $Option(ALBUM_captions) ]
    if { [ file isfile $cap ] } {
	set result [ catch {  set fid [ open $cap r ] } ]
	if { $result } {
	    catch { close $fid }
	    set string "Cannot read from caption file \"$cap\" 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 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 name $rest
		    } else {
			set name \
			    [ string trim [ string range $rest 0 \
						[ expr $doublecolon - 1 ] ] ]
		    } 
		} else {
		    set ll [ split $line "\t" ]
		    set fname [ lindex $ll 0 ]
		    if { [ llength $ll ] > 1 } {
			set name [ lindex $ll 1 ]
		    } else {
			set name ""
		    }
		}
		if { $dir == $fname } {
		    set namefound $name
		}
	    }
	}
	close $fid
    }
    return $namefound
}

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 { $CurrentIx != -1 && $CurrentIx != "" } {
	set CurrentImage [ lindex $NameList $CurrentIx ]
	if { $PicName($CurrentImage) != $NameVar } {
	    set CaptionsEdited 1
	    set namechange 1
	}
	set PicName($CurrentImage)  $NameVar
	set AltTag($CurrentImage) $AltTagVar
	set Caption($CurrentImage) \
	    [ string trim \
		  [ string map { "\n" " " } \
			[ .textframe.cap.text get 1.0 end ] ] ]
	if { $NameVar != "" || $AltTagVar != "" || \
		$Caption($CurrentImage) != "" } {
	    set CaptionsEdited 1
	}
	if { $namechange } {
	    if { ( $Option(VIEW_UsePicName) && \
		       [ file isfile $CurrentImage ] ) || \
		     ( $Option(VIEW_UseAlbumName) && \
			   [ file isdirectory $CurrentImage ] ) } {
		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 ..) 
    # write them to the local captions and to the captions of the subdirs!

    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 :: $PicName($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
	    }
	    WriteToSubDir $fname $PicName($fname)
	}
    }


    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
		}

	    }
	}
	# write name for our own directory!
	if { $PicName(.) != "" } {
	    catch { puts $fid "[ file tail [ pwd ] ] :: $PicName(.)" }
	}
    }
    catch { close $fid }
    SetGroupPerms $Option(ALBUM_captions)
    set SavingCaps 0
}

# write new name "name" to the caption file of the subdir "dir"
proc WriteToSubDir { dir name } {
    global Option

    set old [ SubDirName $dir ]
    if { $name == $old } {
	return
    }
    set cap [ file join $dir $Option(ALBUM_captions) ] 
    set result [ catch {  set fid [ open $cap "a" ] } errmes ]
    if { $result } {
	    catch { close $fid }
	    tk_dialog .dia "Caption Saving Error" "Cannot open caption file \"$cap\"  in album \"$Option(CurrentAlbum).\" in \"append\" mode." error 0 OK
	    return
    } else {
	if { [ catch { puts $fid "$dir :: $name" } ] } {
	    tk_dialog .dia "Caption Saving Error" "Cannot write to caption file \"$cap\" in album \"$Option(CurrentAlbum).\" in \"append\" mode." error 0 OK
	    catch { close $fid }
	    return
	}
	catch { close $fid }
    }
}
    

########### 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 }

    DisplayPicInfo $file $cursel
    if { [ file isfile $file ] } {
	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
	    ShowCrop $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 {
	NoPicture
    }
}

proc ShowCrop { imagename } {
    global Option

    if { $Option(VIEW_cropping) && \
	     [ lsearch [ image names ] $imagename ] != - 1 }  {
	set crop [ GetCropDir $imagename ]
	set imwidth [ image width $imagename ]
	set iheight [ image height $imagename ]
	DrawCropLines $Option(VIEW_prevsize) \
	    $imwidth $iheight \
	    $Option(ALBUM_tn_geom_width) $Option(ALBUM_tn_geom_height) \
	    $crop
    }
}
	
proc GetCropDir { name } {
    set ext [ file extension [ file rootname $name ] ]
    switch -exact  $ext  {
	".CROPtop" { return "top" }
	".CROPbottom" { return "bottom" }
	".CROPleft" { return "left" }
	".CROPright" {return "right" }
	default { return "center" }
    }
}


proc DrawCropLines { size imx imy tnx tny cropdir } {
    set dim [ CroppingDim $imx $imy $tnx $tny ]
    set dimx [ lindex $dim 0 ]
    set dimy [ lindex $dim 1 ]
    
    set xoff 0
    set yoff 0
    if { $dimx == $imx } {
	# width is not changed at all
	# so we can have top, bottom, and center or for cropdir
	if { $cropdir == "top" } {
	    set yoff 0
	} elseif { $cropdir == "bottom" } {
	    set yoff [ expr $imy - $dimy ]
	} else {
	    set yoff [ expr ($imy - $dimy)/2 ]
	}
    } else {
	# height has not changed
	if { $cropdir == "left" } {
	    set xoff 0
	} elseif { $cropdir == "right" } {
	    set xoff [ expr $imx - $dimx ]
	} else {
	    set xoff [ expr ($imx - $dimx)/2 ]
	}
    }
    # now we have all the coordinates wrt to the picture
    # coordinates. Now we only have to transform them to
    # the size x size format.
    set xoff [ expr $xoff + ($size - $imx)/2 ]
    set yoff [ expr $yoff + ($size - $imy)/2 ]
    
    # horizontal top line
    set x1 [ expr $xoff - 1 ]
    if { $x1 < 0 } { set x1 0 }
    set y1 [ expr $yoff - 1 ] 
    if { $y1 < 0 } { set y1 0 }
    set x2 [ expr $x1 + $dimx + 1 ]
    if { $x2 > $size } { set x2 $size }
    set y2 [ expr $y1 + 1 ]
    PreviewImg copy SolidPic -from $x1 $y1 $x2 $y2 -to $x1 $y1 $x2 $y2 
    
    # horizontal bottom line
    set y1 [ expr $yoff  + $dimy ]
    if { $y1 > [ expr $size - 1 ] } { set y1 [ expr $size - 1 ]}
    set y2 [ expr $y1 + 1  ]
    PreviewImg copy SolidPic -from $x1 $y1 $x2 $y2 -to $x1 $y1 $x2 $y2 

    # vertical left line
    set y1 [ expr $yoff - 1 ] 
    if { $y1 < 0 } { set y1 0 }
    set x2 [ expr $x1 + 1 ]
    set y2 [ expr $yoff + $dimy + 1 ]
    if { $y1 > $size } { set y1 $size }
    PreviewImg copy SolidPic -from $x1 $y1 $x2 $y2 -to $x1 $y1 $x2 $y2 

    # vertical right line
    set x1 [ expr $xoff + $dimx ]
    if { $x1 > [ expr $size - 1 ] } { set x1 [ expr $size  -1 ] }
    set x2 [ expr $x1 + 1 ]
    PreviewImg copy SolidPic -from $x1 $y1 $x2 $y2 -to $x1 $y1 $x2 $y2 
}    

proc CroppingDim { imx imy tnx tny } {
    
    set rimx [ expr ($imx * 1.0) ]
    set rimy [ expr ($imy * 1.0) ]
    set rtnx [ expr ($tnx * 1.0) ]
    set rtny [ expr ($tny * 1.0) ]
    if { [ expr ($rimx/$rtnx) > ($rimy/$rtny) ] } {
	set scale [ expr $rimy/$rtny ]
	return [ list [ expr int ($rtnx*$scale) ] $imy ]
    } else {
	set scale [ expr $rimx/$rtnx ]
	return [ list  $imx [ expr int ($rtny*$scale) ]]
    }
}

proc GetEXIFPreview { imagename } {
    global Option 
    global JheadVal
    
    # get original width and height from earlier call to jhead 
    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 } {
	# sorry, nothing known about the orginal picture dimensions
	return
    }
    set transop [ GetTransOp $imagename ] 
    # get transformation description for picture (i.e., whether it 
    # should be rotated etc.)
    if { $transop == "ERROR" } {
	return
    }
    set EXIFPrev [ MakeTempName jpg ]
    catch { eval exec $Option(SCRIPT_jhead) -st $EXIFPrev { $imagename } } 
    if { ! [ file exists $EXIFPrev ] } {
	return
    }
    # now we have the thumbnail extracted, now it must be transformed and
    # scaled for our purposes
    # compute transformation option
    set topt [ GenIMOpt $transop ]
    # compute a file name with correct extension
    set GIFName "[ file rootname $EXIFPrev ].$Option(VIEW_preview_type)" 
    if { $Option(VIEW_rescale_thumbnail) } {
	if { $owidth > $oheight } {
	    set genwidth  $Option(VIEW_prevsize)  
	    set genheight [ expr  ($genwidth * $oheight)/$owidth ]
	} else {
	    set genheight  $Option(VIEW_prevsize)  
	    set genwidth [ expr  ($genheight* $owidth)/$oheight ]
	}
	set sopt "-scale ${genwidth}x${genheight}!"
	if { $topt == "" && $oheight > $owidth } {
	    # heuristic to avoid funny thumbnails:
	    # give up if the height > width and there is no 
	    # transformation specified. Most likely
	    # this picture has been rotated without leaving a notice
	    # in the JPEG commands
	    catch { file delete $EXIFPrev }
	    return
	}
    } else {
	set sopt "-scale $Option(VIEW_prevsize)x$Option(VIEW_prevsize)"
    }
    if { [ catch { eval exec $Option(SCRIPT_mogrify) $topt $sopt -format \
		       $Option(VIEW_preview_type) $EXIFPrev  } errmes ] } {
	# This should not happen but apparently does happen when
	# there are broken EXIF thumbnails. Just ignore and return if
        # we do not want to be alerted by every error
	if { $Option(Alert) } {
	    tk_dialog .dia "Conversion error" "Error while executing the mogrify command \"$Option(SCRIPT_mogrify) $topt $sopt -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  ]
    if { $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 
	}
	ShowCrop $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

    PreviewImg blank
    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 NoPicture { } {
    global UndefPhoto

    CopyToPreview $UndefPhoto
    IdleCursor
}


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

    BusyCursor
    set PreviewName [ MakeTempName $Option(VIEW_preview_type) ]
    set err [ catch { set procid [ eval exec $Option(SCRIPT_convert) \
				       -sample ${scale}x$scale \
				       { $filename }  \
				       { $PreviewName }  & ] } errmes ]
    if { $err } {
	tk_dialog .dia "Convert Execution Error" "Could not execute \"$Option(SCRIPT_convert) -sample ${scale}x$scale $filename $PreviewName \". Got the following error message: \"$errmes\"" error 0 OK 
	IdleCursor
	return
    }
    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
		if { $filename == $CurrentPreviewImageName } {
		    IdleCursor
		}
		return
	    }
	    $filename configure -file $PreviewName 
	    lappend RealPreviewList $filename
	    if { $filename == $CurrentPreviewImageName } {
		PreviewImg blank
		CopyToPreview $filename 
		ShowCrop $filename 
		IdleCursor
	    }
	} else {
	    # no file has been generated, so the file
	    # cannot be a legal picture file
	    # show a "no picture" symbol!
	    NoPicture
	}
    }
    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
    global CurrentPreviewImageName 
    global Option

    
    set CurrentIx $ix
    focus .textframe.nameent

    if { [ file isdirectory $file ] } {
	IdleCursor
	set CurrentPreviewImageName ""
	set CurrentImage ""
	set JheadVal(EXIF_File_Name) $file
	.textframe.cap.text config -state disabled
	.textframe.altent config -state disabled
	if { $file != "." && $file != ".." && \
		 ! [ file exist [ file join \
				      $file $Option(ALBUM_hide_album) ] ]} {
	    .textframe.nameent config -state normal
	    set NameVar $PicName($file)
	} else {
	    .textframe.nameent config -state disabled
	    set PicName($file) ""
	}
    } 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 NameVar $PicName($file)
	    set CurrentImage $file
	    .textframe.nameent icursor end
	    .textframe.nameent config -state normal
	    set AltTagVar $AltTag($file)
	    .textframe.altent config -state normal
	    .textframe.cap.text insert end $Caption($file)
	    .textframe.cap.text mark set insert end
	    .textframe.cap.text config -state normal
	}
    }
}

    
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 NewRootAlbum [ NormalizeFileName $InitialDir $NewRootAlbum ]

    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
    global Option

    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 ] } {
	if { $file == ".." } {
	    set pref [ file tail $Option(CurrentAlbum) ]
	} else {
	    set pref ""
	}
	GotoAlbum [ file join $Option(CurrentAlbum) $file ] $pref
	return
    } elseif { [ IsImageFile $file ] } {
	NextImage 
    }
}
	

########## File menu: Open album
# we show the selection box to goto another album
proc OpenAlbum { } {
    global Option
    global NameList

    SaveCaptions


    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 [ NormalizeFileName $ini $NewCurrentAlbum ] ""
}

# go one directory up
proc UpAlbum { } {
    global Option
    
    if { $Option(CurrentAlbum) != $Option(RootAlbum) } {
	GotoAlbum [ file join $Option(CurrentAlbum) ".." ] \
	    [ file tail $Option(CurrentAlbum) ]
    }
}

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

proc DoGotoAlbum { name pref } {
    global Option
    global NameList
    global CurrentIx

    PreviewImg blank
    update

    set oldcur $Option(CurrentAlbum)

    # 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) ""
    } else {
	if { [ pwd ] != $Option(CurrentAlbum) && \
		 $Option(FILE_symlink_warning) } {
	    tk_dialog .dia "Symlink Warning" "You have reached this directory \"$Option(CurrentAlbum)\" following a symlink. The directory you ended up in is \"[pwd]\". Symlinks are potentially harmful in album structures because browsing an album following a symlink might lead to the situation that the theme files cannot be found." warning 0 OK
	}
    }
    ScanDirectory 
    if { $pref == "" } {
	.listframe.list selection set 0
	.listframe.list see 0
    } else {
	set prefx [ lsearch $NameList $pref ] 
	if { $prefx == -1 } {
	    .listframe.list selection set 0
	    .listframe.list see 0
	} else {
	    .listframe.list selection set $prefx
	    .listframe.list see $prefx

	}
    }	
    set CurrentIx -1 
    LoadPicFile    
}

########## 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
    }
    set NewAlbum [ NormalizeFileName $ini $NewAlbum ]

    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 ""
    } else {
	set savecd [ pwd ]
	if { ! [ catch { cd $NewAlbum } ] } {
	    set newcd [ pwd ]
	    if { $newcd != $NewAlbum && $Option(FILE_symlink_warning) } {
		tk_dialog .dia "Symlink Warning" "The new album has been generated following a symlink! You requested to generate \"$NewAlbum\". It is, however, the directory \"$newcd\". Symlinks in album structures are potentially harmful because when browsing albums following symlinks, the theme files might not be found." warning 0 OK 
	    }
	}
	catch { cd $savecd }
	ScanDirectory
    }
}

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 { BusyCursor 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  }
    # 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 ] }
    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 $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 "%sc%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 "%sc%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
    }


    # check for "." and ".."
    foreach el $cursel {
	if { ( [ lindex $NameList $el ] == "." || \
		 [ lindex $NameList $el ] == ".." ) && ! $conv } {
	    tk_dialog .dia "$Op Warning" "Cannot $op the directories \".\" and \"..\"" warning 0 OK
	    return
	} elseif { [ file isdirectory [ lindex $NameList $el ]  ] && $conv } { 
	    tk_dialog .dia "$Op Warning" "Sorry, cannot $op entire directories (e.g., \"[ lindex $NameList $el ]\")"  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 { $conv && $Option(FILE_cc_rename) } {
	    set newfile [ FileNameFromName $file ]
	} else {
	    set newfile $file
	}
	if { [ file exist [ file join $newdir $newfile ] ] } { 
	    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 && [ llength $cursel ] > 1 } {
	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 $newfile }

	# check whether editing should be done!
	if { $conv && $Option(FILE_cc_edit) && [ llength $cursel ] == 1 } {
	    if { [ tk_dialog .dia "Edit Question" "Do you want to edit the image before it gets converted?" questhead 1 Yes No ] == 0 } {
		BusyCursor gumby
		set procid -1
		if { [ catch { set procid \
				   [ eval exec $Option(SCRIPT_edit) \
					 $Option(FILE_edit_options) \
					 [ list $newfile ]  & ] } errmes ] } {
		    tk_dialog .dia "Edit error" \
			"Sorry, could not start the image editor" error 0 OK 
		    return
		}
		while { [ ProcessAlive $procid ] } {
		    update
		    after 200
		}
		IdleCursor
	    }
	}

	# mogrify - if required
	if { $conv } {
	    if { [ llength $cursel ] == 1 } {
		BusyCursor
	    }
	    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 && [ llength $cursel ] > 1 } {
	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"
	}
    }
    IdleCursor

    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 ] }
    }
    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 

    SaveCaptions

    WriteProfile
    KillProcesses $ViewerProcesses
    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 "Check Installation of Components" \
	STATE_InstallationCheck \
	InstallationCheck \
	"Checks whether all component programs are installed and accessible.\nIn addition the version number is checked for compatibility."
    checkbutton .prefbox.vals.btsM -text "Strict Motif style" \
	-variable tk_strictMotif 
    grid .prefbox.vals.btsM -row 1 -col 0 -padx 2 -sticky w
    RegisterHelp .prefbox.vals.btsM "" \
	"This option should be enabled if strict Motif look & feel is desired"
    MakeBoolEntry 1  1 "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." 
    MakeBoolEntry  2 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  2 1 "Camera functionality enabled" \
	STATE_CameraEnabled \
	{ StateSettings } \
	"Disable if the \"camera download\" functionality is not needed"
    MakeStringEntry  3 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  4 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  5 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  6 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  7 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  8 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 9 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 10 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 11 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 12 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  13 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  14 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  15 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  16 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 17 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; InstallCheck }
    
    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 { } {
    global Option

    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 "Symlink warning" \
	FILE_symlink_warning { } \
	"Give a warning when following a symlink in the album\nstructure, because symlinks are potentially harmful"
    MakeBoolEntry  1 0 "Warning before name clashes occur" \
	FILE_overwrite_warning { } \
	"Give a warning message before an operation is attempted that leads\nto a name clash. Note that no harm can be done because such an\noperation will never overwrite the original file. Either an error\noccurs or the name is changed."
    MakeBoolEntry  1 1 "Change names when they clash" \
	FILE_change_name_when_same { } \
	"Change names before a \"copy\", \"move\", or \"rename\" operation\nif it would result in a name clash. If this option is disabled,\nname clashes lead to errors."
    MakeBoolEntry  2 0 "Use Trash Bin (no immediate deletes)" \
	FILE_use_trash_bin { } \
	"Instead of deleting files immediately, they are moved to a trash\nbin. If this is option is disabled, no file can be restored after\na delete operation."
    MakeBoolEntry  2 1 "Warning before deletion" \
    	FILE_delete_warning { } \
	"If this option is enabled, then before each\ndeletion the user is asked for confirmation"
    MakeBoolEntry  3 0 "Allow deletion of albums" \
	FILE_dir_delete_allow { } \
	"Allow to delete albums, which can be a bit dangerous"
    MakeBoolEntry  3 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  4 0 "Save original images before editing" \
	FILE_save_original_when_edit { } \
	"Save a copy of the image before attempting to edit it."
    MakeBoolEntry  4 1 "Mark files that have been copied" \
	FILE_mark_copied { } \
	"Mark a file that has been copied with an asterisk. Useful when\nselecting a set of files to send off to a printer or something\nsimilar. There is no reason to disable this option"
    MakeBoolEntry  5 0 "Rename files when doing \"Copy & Convert\"" \
	FILE_cc_rename { } \
	"The \"Copy & Convert\" is mainly useful when generating pictures\nto be sent by e-mail. In order to have meaningful file names,\nthis option allows to rename the files using the picture names."
    MakeBoolEntry  5 1 "Remove funny chars when renaming" \
	FILE_cc_remove_funny_chars { } \
	"If this option is enabled, then funny characters (such as\nblank, comma, period, etc.) are all replaced by \"_\" when\na file is renamed to its picture name during a \"copy &\nconvert\" operation."
    MakeBoolEntry  6 0 "Edit before \"Copy & Convert\"" \
	FILE_cc_edit { } \
	"If this option is active, one is asked whether one wants to edit\nan image before it is converted, provided only a single picture\n has been selected"
    MakeStringEntry  7 0 "Edit options for the \"display\" program:" \
	FILE_edit_options none { }  \
	"If one wants to specify extra options for the call to the\nimage editor, here is the right place to do it."
    MakeStringEntry  8 0 "Convert options for \"Copy & Convert:\"" \
	FILE_cc_convert_options none { } \
	"Here one can specify the options for the convert operation\nwhen doing a \"copy & convert\"."
    MakeStringEntry  9 0 "Jhead options for \"Copy & Convert:\"" \
	FILE_cc_jhead_options none { }  \
	"If one wants to remove the EXIF header during a \"copy & convert,\"\nhere one can specify the appropriate options."
    MakeStringEntry  10 0 "Directory name for originals:" \
	FILE_save_dir none { } \
	"Originals of images to be edited are saved on a per album\nbase in  a hidden album under the directory name specified here."
    MakeStringEntry  11 0 "Directory name for trash bin:" \
	FILE_trash_dir none { } \
	"The trash bin is a hidden album stored in the toplevel album\nunder the name specified here."
    MakeStringEntry 12 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\nprogram 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
    global CurrentIx

    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\nsame size and shape. This is accomplished by cropping\npictures 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\nwhen generating the thumbnails and the medium sized\npictures. It is a bit faster, but has lower quality."
    MakeStringEntry  2 0 "Medium preview geometry width" \
	ALBUM_medium_geom_width key { CheckNumber %P } \
	"Maximum width of medium sized images. If no number is\nspecified, 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\nspecified, 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\nspeficied, 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,\nno 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,\nno 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\nspeficied, the type of the original image is used"
# "-CROP" has been removed since there is no way to force center cropping
#         for individual pictures
#     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\ncropped 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
    set CurrentIx -1
    LoadPicFile
}

########## 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 Generation Options"

    label $w.top -text "Album Generation 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 \
	ScanDirectory \
	"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 \
	ScanDirectory \
	"If one also want to see all directories starting with a dot, then\nthis option should be enabled. Usually, there is no reason to do so."
    MakeBoolEntry 3 0 "Cleanup: delete obsolete files" ALBUM_cleanup {} \
	"If enabled, album is always run with the \"-clean\" option,\nwhich implies that obsolete thumbnails, medium sized\n picture, and HTML pages are deleted."
    MakeStringEntry 4 0 "Number of image columns" \
	ALBUM_columns key { CheckNumber %P } \
	"Specifies the number thumbnails per row"
    MakeStringEntry 5 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\nthe thumbnail HTML pages. A non-empty string schould be given\nhere if one wants to browse the images not going through a web\nserver, but using file://... The safest setting is to say \"index\" here."
    MakeStringEntry 6 0 "Body tag for default theme" \
	ALBUM_body none { } \
	"Specifies <body> tag for default theme, e.g., <body color=white>"
    MakeStringEntry 7 0 "File name for captions" \
	ALBUM_captions none { } \
	"This is the file name for the \"captions\" file, in which the\npicture names, the captions, and the AltTags are stored\non a per album base"
    MakeStringEntry 8 0 "Name of thumbnail directory" \
	ALBUM_dir none { } \
	"Thumbnails and medium-sized pictures are stored\nper 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\n(see http://MarginalHacks.com/Hacks/album for varrious\nthemes). If this string is empty, the default theme is used"
    grid $w.vals.button -col 0 -row 9 -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\nspecification of an album theme. If this string is empty, the default\ntheme is used (consult http://MarginalHacks.com/Hacks/album for\nother themes)."
    grid $w.vals.tentry -col 1 -row 9 -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
    global CurrentIx

    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 "Show thumbnail cropping" \
	VIEW_cropping { set CurrentIx -1; LoadPicFile } \
	"Show in the preview area how the thumbnail cropping affects the picture"
    MakeBoolEntry  1 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 1 "Adapt aspect ratio of EXIF thumbnails" \
	VIEW_rescale_thumbnail { } \
	"If the aspect ratio of the EXIF thumbnails differs from the\naspect ratio of the images, there are two possibilities. Either\nonly part of the thumbnail is used and there are black stripes\nor the thumbnail is slightly distorted. In the latter case, this\noption should be enabled."
    MakeBoolEntry  2 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  2 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  3 0 "Show directories" \
	VIEW_ShowDirs  ScanDirectory  \
	"If this option is disabled, only files (and no\ndirectories) are displayed in the diretory listing"
    MakeBoolEntry 3 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  4 0 "Show file extensions" \
	VIEW_ShowFileExt    ScanDirectory  \
	"If disabled, the extension of files are\nnot shown in the directory listing"
    MakeBoolEntry  4 1 "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  5 0 "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  5 1 "Use album names in directory list" \
	VIEW_UseAlbumName    ScanDirectory \
	"Instead of the sub-directory names, the album names\n(if given) are used in the directory listing"
    MakeBoolEntry  6 0 "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  7 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 8 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  9 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  10 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  11 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" \
	JPEG_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 ]}
	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) $file\": \"$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 "Wrjpgcom Execution Error" "Error while executing \"$Option(SCRIPT_write_jc) $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 HideAlbum { } {
    global Option

    set marker $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 "Hiding Error" "Cannot generate file \"$marker\"" error 0 OK 
    }
    ScanDirectory
}

proc UnHideAlbum { } {
    global Option

    set marker(1) $Option(ALBUM_hide_album)
    set marker(2) $Option(ALBUM_no_album)

    foreach m { 1 2 } {
	catch { file delete -force $marker($m) }
	if { [ file exist $marker($m) ] } { 
	    tk_dialog .dia "Unhiding Error" "Cannot delete \"$marker($m)\"" \
		error 0 OK
	    return
	}
    }
    ScanDirectory
}


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
    .listframe.list see $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 (and cleanup) actions
proc GenAlbum { strictly local force clean } {
    global Option

    set error 0
    set dellist { }
    if { $strictly } {
	# restrict album script to current directory by marking all 
        # other (non-marked  dirs) with .no_album
	# if an error occurs, skip everything and delete the markers
	set dirs [ concat [ glob -type d * ] [ glob -type d .* ] ] 
	foreach d $dirs {
	    if { $d != "." && $d != ".." && \
		     ! [ file exists \
			     [ file join $d $Option(ALBUM_hide_album) ] ] && \
		     ! [ file exists \
			     [ file join $d $Option(ALBUM_no_album) ] ] } {
		lappend dellist [ file join $d $Option(ALBUM_no_album) ]
		if { [ catch { set pid [ open [ file \
						    join \
						    $d \
						    $Option(ALBUM_no_album) \
						   ] "w" ] } \
			   errmes ] } {
		    tk_dialog .dia "File Write Open Error" "Could not open file \"[ file join $d $Option(ALBUM_no_album) ]\" for writing: \"$errmes\". Will abort the album generation process" error 0 OK
		    set error 1
		    break 
		} else {
		    catch { close $pid }
		}
	    }
	}
    }
    if { ! $error } {
	SafeCall [ list DoGenAlbum $local $force $clean ] \
	    "Generate and/or Cleanup Album"
    }
    foreach f $dellist {
	    catch { file delete -force $f }
    }
    ScanDirectory
}

proc DoGenAlbum { local force  clean  } {
    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"
    }

    if { $Option(ALBUM_cleanup) || $clean  } {
	append gencall " -clean "
    }
    
    append gencall " 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
    # this is easy now because we always start in the current directory
    # when we do a local regneration
    cd $Option(CurrentAlbum)
    if { $local } {
	set startdir $Option(CurrentAlbum)
    } else {
	set startdir $Option(RootAlbum)
	# delete toplevel index because otherwise we might
	# carry on a wrong toplevel name (do not ask me why)
	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"
    }
    set add ""
    if { $Option(ALBUM_cleanup) || $clean  } {
	set add "& cleaning up"
    } 
     ShowProgressWindow "Generating $albums $add" 1 1

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

    MakeAlbumCall $gencall $nritems

    if { ! $Abort && $Option(STATE_GroupName) != "" && ! $clean } {
	.progbox.top configure -text "Generating $albums (Permission Setting)"
	set ProgressFile ""
	set ProgressDir ""
	set ShowProgress 0.0
	cd $startdir
	SetPermissionsForAllFiles
    }
    if { ! $Abort } {
	set finaltext "Album generation completed"
	set ProgressFile ""
	set ProgressDir ""
	set ShowProgress 1000.0
	.progbox.button.abort configure -text "OK"
	.progbox.top configure -text $finaltext
    }
    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 compare -length 7 \
		       "\[album\]" $newline ] == 0 } {
		tk_dialog .dia "Album Generation Error" \
		    "Album has encountered an error: \"[ string range $newline 7 end ]\"" \
		    error 0 OK
	    }
	    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  } {
    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 != ".." && \
		 ( $d != $Option(FILE_trash_dir) || \
		       $start != $Option(RootAlbum) ) && \
		 ( ! [ string match ".*" $d ] || $Option(ALBUM_all) ) } {
	    set nr [ expr $nr + [ CountAlbumsAndImages $d $alsoimages 0 ] ] 
	}
    }
    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 
    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
    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 ]
    
    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
	if { [ catch { file copy -force  $file   $TempViewerFile } errmes ] } {
	    set TempViewerFile ""
	    tk_dialog .dia "Display Error" "Cannot copy $file to $TempViewerFile for viewing. Error is: \"$errmes\"" error 0 ok
	    return
	}
	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 } {
	    if { $file == ".." } {
		set pref [ file tail $Option(CurrentAlbum) ]
	    } else {
		set pref ""
	    }
	    GotoAlbum [ file join $Option(CurrentAlbum) $file ] $pref
	}
    }
}

########## 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" && \
		 $img != "SolidPic" && \
		 [ 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 ] }
	}
    }
    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(JPEG_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(JPEG_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), October 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 { text title} {
    

    MenuHelpDisplay .licensetext $title $text
}

proc MenuHelpManPage { } {
    global ManPageText 

    MenuHelpDisplay .manualpage "Manual Page" ""

}

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
    if { $w != ".manualpage" } {
	$w.text insert 0.0 $text
    } else {
	FillManualPage $w
    }
    $w.text config -state disabled
    PositionWindowCenter $w
}
    

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

proc DisplayBalloon { path key x y } {
    global HelpText
    global HelpFont
    global UnShowProcess
    
    if { [ info exists HelpText($key) ] } {
	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}]
        }

        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


    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 ] } { 
		return
	    } elseif { [ info exists HelpMenu($path) ] } {
		MenuHelpDispatch select $path
		return
	    } elseif { [ string first ".mbar" [ grab current ] ] == 0 } {
		set type "leave"
	    } else {
		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

    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]
	if { [string compare $index "none"] == 0 } {
	    return
	}
	set key "$path$index"
	if { [ info exists HelpText($key) ] } {
	    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
	bind . <Alt-o> ""
    } else {
	.mbar.file.menu entryconf 2 -state normal
	bind . <Alt-o> { OpenAlbum }
    }

    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 } { 
	bind . <Alt-u> ""
	.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 {
	bind . <Alt-u> { UpAlbum }
	.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 8 -state disabled
	.mbar.pref.menu entryconf 9 -state disabled
    } else { 
	.mbar.pref.menu entryconf 8 -state normal
	.mbar.pref.menu entryconf 9 -state normal
    }    

    if { ! $camera } {
	.mbar.pref.menu entryconf 10 -state disabled
    } else { 
	.mbar.pref.menu entryconf 10 -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
	.mbar.thumbs  config -state disabled
	bind . <Alt-p> ""
	bind . <Alt-s> ""
	bind . <Alt-l> ""
	bind . <Alt-g> ""
	bind . <Alt-f> ""
    } else { 
	.mbar.album  config -state normal
	.mbar.thumbs config -state normal
	bind . <Alt-s> { GenAlbum 1 1 0 0 }
	bind . <Alt-l> { GenAlbum 0 1 0 0 }
	bind . <Alt-g> { GenAlbum 0 0 0 0 }
	bind . <Alt-p> { GenAlbum 0 0 0 1 }
	bind . <Alt-f> { EditHeaderAndFooter }
    }

    if { ! $album } {
	catch { pack forget .mbar.album .mbar.thumbs }
    } else {
	catch { pack forget .mbar.album .mbar.thumbs }
	catch { pack .mbar.album .mbar.thumbs \
		    -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 sorting, and editing headers & footers"
menubutton .mbar.thumbs -text Thumbnail -menu .mbar.thumbs.menu
RegisterHelp .mbar.thumbs "" \
    "Change the appearance of the thumbnail by choosing a different cropping area"
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 .mbar.thumbs -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 cascade -label "Select profile" \
    -menu .mbar.pref.menu.profile
RegisterMenuHelp .mbar.pref.menu 1 \
    "Select a different profile (perhaps with a different root album)"
.mbar.pref.menu add command -label "Load new profile" \
    -command LoadNewProfile
RegisterMenuHelp .mbar.pref.menu 2 \
    "Load a new, existing profile"
.mbar.pref.menu add command -label "Save profile as ..." \
    -command SaveProfileAs
RegisterMenuHelp .mbar.pref.menu 3 \
    "Save the current profile under a new name"
.mbar.pref.menu add separator
.mbar.pref.menu add command -label "General options" \
    -command GeneralOptions 
RegisterMenuHelp .mbar.pref.menu 5 \
    "General settings such as paths to programs"    
.mbar.pref.menu add command -label "File options" \
    -command FileOptions 
RegisterMenuHelp .mbar.pref.menu 6 \
    "Settings for file operations"    
.mbar.pref.menu add command -label "Transform options" \
    -command TransformOptions 
RegisterMenuHelp .mbar.pref.menu 7 \
    "Settings for image transformations"    
.mbar.pref.menu add command -label "Album options" \
    -command  AlbumAAOptions 
RegisterMenuHelp .mbar.pref.menu 8 \
    "Settings for running the \"album\" script"
.mbar.pref.menu add command -label "Thumbnail options" \
    -command AlbumTNOptions 
RegisterMenuHelp .mbar.pref.menu 9 \
    "Settings for the appearance of thumbnails and of\nthe medium sized pictures in the HTML albums" 
.mbar.pref.menu add command -label "Camera options" \
    -command CameraOptions 
RegisterMenuHelp .mbar.pref.menu 10 \
    "Settings for camera download operation"    
.mbar.pref.menu add command -label "View options" \
    -command ViewOptions 
RegisterMenuHelp .mbar.pref.menu 11 \
    "Settings for viewing the albums and previews"    
.mbar.pref.menu add command -label "JpegInfo options" \
    -command JpegInfoOptions 
RegisterMenuHelp .mbar.pref.menu 12 \
    "Settings that determine which EXIF values to show and\nhow to process files  that have EXIF headers"    

# profile sub menu
menu .mbar.pref.menu.profile
foreach el $ProfileList {
    .mbar.pref.menu.profile add radiobutton -label $el -value $el \
	-variable CurrentProfile -command SelectProfile
}

# ----- 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 "Edit header & footer" \
    -command { EditHeaderAndFooter } -underline 14 -accelerator "Meta-F"
RegisterMenuHelp .mbar.album.menu 1 \
    "Edit the header lines and footer lines of the current album"
.mbar.album.menu add separator
.mbar.album.menu add cascade -label "Sort entries" \
    -menu .mbar.album.menu.sort
RegisterMenuHelp .mbar.album.menu 3 \
    "Sort entries in the directory list according to different criteria"
.mbar.album.menu add command -label "Reverse order" \
    -command { ReverseOrder }
RegisterMenuHelp .mbar.album.menu 4 \
    "Reverse the ordering of files and directories in the directory listing"
.mbar.album.menu add separator
.mbar.album.menu add command -label "Hide current album" \
    -command HideAlbum 
RegisterMenuHelp .mbar.album.menu 6 \
    "Make current album invisible by inserting \".hide_album\""
.mbar.album.menu add command -label "Make current album visible" \
    -command UnHideAlbum 
RegisterMenuHelp .mbar.album.menu 7 \
    "Make current album visible by deleting \".hide_album\" and \".no_album\""
.mbar.album.menu add separator
.mbar.album.menu add command -label "Strictly local generation" \
    -command { GenAlbum 1 1 0 0 } -underline 0 -accelerator "Meta-S"
RegisterMenuHelp .mbar.album.menu 9 \
    "Generate new album HTML pages in the current album without descending\ninto sub-albums. Thumbnails will only be regenerated if the respective\npictures have been changed." 
.mbar.album.menu add command -label "Local album generation" \
    -command { GenAlbum 0 1 0 0 } -underline 0 -accelerator "Meta-L"
RegisterMenuHelp .mbar.album.menu 10 \
    "Generate new album HTML pages starting in the current album and descend\ninto sub-album. Thumbnails\nwill only be regenerated if the respective\npictures have been changed." 
.mbar.album.menu add command -label "Global album generation" \
    -command { GenAlbum 0 0 0 0 } -underline 0 -accelerator "Meta-G"
RegisterMenuHelp .mbar.album.menu 11 \
    "Generate new album HTML pages starting from the root album. This means\nthat all index pages will be regenerated. Thumbnails will only be regenerated\nif the respective pictures have been changed." 
.mbar.album.menu add command -label "Global generation & cleanup" \
    -underline 26 -accelerator "Meta-P" \
    -command { GenAlbum 0 0 0 1 } 
RegisterMenuHelp .mbar.album.menu 12 \
    "Generate new album HTML pages starting from the root album. This means\nthat all index pages will be regenerated. Thumbnails will only be regenerated\nif the respective pictures have been changed. In addition, obsolete thumbnails,\nmedium-sized pictures, and HTML pages will be deleted. There is also an option\nthat forces such cleanup durcing each (re-)generation step."
.mbar.album.menu add command -label "Forced strictly local regen." \
    -command { GenAlbum 1 1 1 0 } 
RegisterMenuHelp .mbar.album.menu 13 \
    "Generate new album HTML pages in the current album without\ndescending into sub-albums. All thumbnails will be regenerated." 
.mbar.album.menu add command -label "Forced local regeneration" \
    -command { GenAlbum 0 1 1 0 } 
RegisterMenuHelp .mbar.album.menu 15 \
    "Generate new album HTML pages starting in the current album. All\nthumbnails will be regenerated, which can take some time." 
.mbar.album.menu add command -label "Forced global regeneration" \
    -command { GenAlbum 0 0 1 0 } 
RegisterMenuHelp .mbar.album.menu 16 \
    "Generate new album HTML pages starting in the toplevel album. All\nthumbnails will be regenerated, which will take considerable time!" 
.mbar.album.menu add command -label "Forced global reg. & cleanup" \
    -command { GenAlbum 0 0 1 1 } 
RegisterMenuHelp .mbar.album.menu 16 \
    "Generate new album HTML pages starting in the toplevel album. All\nthumbnails will be regenerated, which will take considerable time.\nIn addition, obsolete files will be deleted." 


# 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)"

# ------- Thumbnail menu
menu .mbar.thumbs.menu
.mbar.thumbs.menu add command -label "Center cropping" \
    -command { PreviewCrop center }
RegisterMenuHelp .mbar.thumbs.menu 1 \
    "Show center of image in thumbnail"
.mbar.thumbs.menu add command -label "Top cropping" \
    -command { PreviewCrop top }
RegisterMenuHelp .mbar.thumbs.menu 2 \
    "Show top of image in thumbnail"
.mbar.thumbs.menu add command -label "Bottom cropping" \
    -command { PreviewCrop bottom }
RegisterMenuHelp .mbar.thumbs.menu 3 \
    "Show bottom of image in thumbnail"
.mbar.thumbs.menu add command -label "Left cropping" \
    -command { PreviewCrop left }
RegisterMenuHelp .mbar.thumbs.menu 4 \
    "Show left part of image in thumbnail"
.mbar.thumbs.menu add command -label "Right cropping" \
    -command { PreviewCrop right }
RegisterMenuHelp .mbar.thumbs.menu 5 \
    "Show right part of image in thumbnail"

# ----- 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; WriteProfile } \
    -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 separator
.mbar.view.menu add checkbutton -label "Show thumbnail cropping" \
    -variable Option(VIEW_cropping) \
    -command { set CurrentIx -1; LoadPicFile; WriteProfile }
RegisterMenuHelp .mbar.view.menu  9 \
    "Show in the preview area how the thumbnail cropping affects the picture"
.mbar.view.menu add cascade -label "Preview window size" \
	-menu .mbar.view.menu.prev 
 RegisterMenuHelp .mbar.view.menu 10 \
    "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 ; WriteProfile } \
    -variable Option(VIEW_UsePicName)
 RegisterMenuHelp .mbar.view.menu 12 \
    "Usually, the directory list displays file names; if this\noption is selected, picture names are displayed"
.mbar.view.menu add checkbutton -label "Use album names in list" \
    -command { ScanDirectory ; WriteProfile } \
    -variable Option(VIEW_UseAlbumName)
 RegisterMenuHelp .mbar.view.menu 13 \
    "This option must be enabled if one wants to see the names\ngiven to album in the directory listing"


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

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



# ----- 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 "Dynamic help for options" \
    -variable Option(STATE_OptionHelp) -command { WriteProfile }
RegisterMenuHelp .mbar.help.menu 1 \
    "Deselect in order to get rid of \"balloon\" help in the option windows"
.mbar.help.menu add checkbutton -label "Dynamic help for main window" \
    -variable Option(STATE_CommandHelp) -command KillBalloon \
    -command { WriteProfile }
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 "TKAlbum license"  \
    -command { MenuHelpLicense $GPLText "TKAlbum License"}
RegisterMenuHelp .mbar.help.menu 4 \
    "Displays the TKAlbum license, which is just the GPL"
.mbar.help.menu add cascade -label "Component licenses" \
    -menu .mbar.help.menu.componentlicenses
RegisterMenuHelp .mbar.help.menu 5 \
    "Displays the licenses for the programs that are used by TKAlbum"
.mbar.help.menu add command -label "Manual Page" -command "MenuHelpManPage"
RegisterMenuHelp .mbar.help.menu 6 \
    "Displays the manual page"
.mbar.help.menu add separator
.mbar.help.menu add command -label "About TKAlbum" -command "MenuHelpAbout" 
RegisterMenuHelp .mbar.help.menu 8 \
    "Gives version information and tells you who wrote this stuff"

menu .mbar.help.menu.componentlicenses
.mbar.help.menu.componentlicenses add command -label "Album license" \
    -command { MenuHelpLicense $AlbumLicenseText "Album License"}
RegisterMenuHelp .mbar.help.menu.componentlicenses 1 \
    "Displays the license text for the album script"
.mbar.help.menu.componentlicenses add command -label "Libjpeg license" \
    -command { MenuHelpLicense $LibjpegLicenseText "Libjpeg (jpegtran, rdjpgcom, wrjpgcom) License"}
RegisterMenuHelp .mbar.help.menu.componentlicenses 2 \
    "Displays the license text for the Libjpeg library and\nassociated programs such as jpegtran, rdjpgcom, wrjpgcom"
.mbar.help.menu.componentlicenses add command -label "Jhead license" \
    -command { MenuHelpLicense $JheadLicenseText "Jhead License"}
RegisterMenuHelp .mbar.help.menu.componentlicenses 3 \
    "Displays the license text for the Jhead utility"
.mbar.help.menu.componentlicenses add command -label "ImageMagick license" \
    -command { MenuHelpLicense $ImageMagickLicenseText "ImageMagick (convert, mogrify, display) License"}
RegisterMenuHelp .mbar.help.menu.componentlicenses 4 \
    "Displays the license text for the ImageMagick tools convert, mogrify, & display"


# 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>     ""

# Non-default cursor assignments
foreach ent [ array names Option EXIF_* ] {
	.info.v$ent config -cursor {}
}
.messageframe.dir.root.val config -cursor {}
.messageframe.dir.current.val config -cursor {}

# 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
InstallCheck
ScanDirectory

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

after 2000 { after idle  CheckTrashBin }



###########################################################################
############################# GPL     Text ################################
###########################################################################

set GPLText {
		    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.
}

###########################################################################
############################# Album License Text ##########################
###########################################################################

set AlbumLicenseText {
             License for programs at DaveSource Marginal Hacks:
                       [1]http://MarginalHacks.com/ 
     _________________________________________________________________

   Disclaimer:
          These  programs  aren't  packaged  in any sort of user-friendly
          format.
          While  most of it does something that I consider useful, plenty
          of it is old and written poorly and lacking documentation.
          Lots of the programs here do not fit my definition of [2]TRW.
          If you're looking to write a script like one of these, yea.
          If you don't program, tough noogies for you.
          Make your browser nice and wide to look at the index, or suffer
          an unreadable table.
          I  recognize  that  this  license is not [3]OpenSource, this is
          quite [4]deliberate
            __________________________________________________________

   Non-Warranty:
          These programs come with absolutely no warranty.
          The  'Rating'  and  'Works (Y/N)' columns in the software index
          are no indication of the quality or ability of the programs.
          Use at your own risk!
            __________________________________________________________

   License for use:
          The  programs  may  be used and distributed (with this license)
          for [5]free.
          The  programs  may  be  modified  as  long  as  they retain the
          original license and author, copyright information.
          If  you  modify any of these programs, please e-mail patches to
          the the author.
          (If  you  think  the  patch  isn't of general use, you can just
          email me about it first to check)
          None  of the programs can be sold or included in a product that
          is sold without prior written permisson from the author.
            __________________________________________________________

   Copyright:
          All these programs are copyright David Ljung Madison
            __________________________________________________________

   Why:
          I've  made  this  software  freely  available for the following
          reasons:

         1. I like to think that I write useful software, so I want other
            people to use it.
         2. I felt like helping out a bit. This is not a common thing for
            me, so enjoy it.
         3. I use [6]free software, and wanted to give something back.
         4. But if you want to help, you can [7]send money
     _________________________________________________________________

   This information is subject to change.

   Back to [8]http://MarginalHacks.com

References

   1. http://MarginalHacks.com/
   2. file://localhost/
   3. http://OpenSource.org/
   4. http://DaveFAQ.com/Opinions/OpenSource.html
   5. http://MarginalHacks.com/Pay.html
   6. http://Fringe.DaveSource.com/Fringe/Computers/Philosophy/Open_Source_not_free.html
   7. http://MarginalHacks.com/Pay
   8. http://MarginalHacks.com/

	 }

###########################################################################
############################# Libjpeg Page ################################
###########################################################################
set LibjpegLicenseText {
    In plain English:

1. We don't promise that this software works.  (But if you find any bugs,
   please let us know!)
2. You can use this software for whatever you want.  You don't have to pay us.
3. You may not pretend that you wrote this software.  If you use it in a
   program, you must acknowledge somewhere in your documentation that
   you've used the IJG code.

In legalese:

The authors make NO WARRANTY or representation, either express or implied,
with respect to this software, its quality, accuracy, merchantability, or
fitness for a particular purpose.  This software is provided "AS IS", and you,
its user, assume the entire risk as to its quality and accuracy.

This software is copyright (C) 1991-1998, Thomas G. Lane.
All Rights Reserved except as specified below.

Permission is hereby granted to use, copy, modify, and distribute this
software (or portions thereof) for any purpose, without fee, subject to these
conditions:
(1) If any part of the source code for this software is distributed, then this
README file must be included, with this copyright and no-warranty notice
unaltered; and any additions, deletions, or changes to the original files
must be clearly indicated in accompanying documentation.
(2) If only executable code is distributed, then the accompanying
documentation must state that "this software is based in part on the work of
the Independent JPEG Group".
(3) Permission for use of this software is granted only if the user accepts
full responsibility for any undesirable consequences; the authors accept
NO LIABILITY for damages of any kind.

These conditions apply to any software derived from or based on the IJG code,
not just to the unmodified library.  If you use our work, you ought to
acknowledge us.

Permission is NOT granted for the use of any IJG author's name or company name
in advertising or publicity relating to this software or products derived from
it.  This software may be referred to only as "the Independent JPEG Group's
software".

We specifically permit and encourage the use of this software as the basis of
commercial products, provided that all warranty or liability claims are
assumed by the product vendor.


ansi2knr.c is included in this distribution by permission of L. Peter Deutsch,
sole proprietor of its copyright holder, Aladdin Enterprises of Menlo Park, CA.
ansi2knr.c is NOT covered by the above copyright and conditions, but instead
by the usual distribution terms of the Free Software Foundation; principally,
that you must include source code if you redistribute it.  (See the file
ansi2knr.c for full details.)  However, since ansi2knr.c is not needed as part
of any program generated from the IJG code, this does not limit you more than
the foregoing paragraphs do.

The Unix configuration script "configure" was produced with GNU Autoconf.
It is copyright by the Free Software Foundation but is freely distributable.
The same holds for its supporting scripts (config.guess, config.sub,
ltconfig, ltmain.sh).  Another support script, install-sh, is copyright
by M.I.T. but is also freely distributable.

It appears that the arithmetic coding option of the JPEG spec is covered by
patents owned by IBM, AT&T, and Mitsubishi.  Hence arithmetic coding cannot
legally be used without obtaining one or more licenses.  For this reason,
support for arithmetic coding has been removed from the free JPEG software.
(Since arithmetic coding provides only a marginal gain over the unpatented
Huffman mode, it is unlikely that very many implementations will support it.)
So far as we are aware, there are no patent restrictions on the remaining
code.

The IJG distribution formerly included code to read and write GIF files.
To avoid entanglement with the Unisys LZW patent, GIF reading support has
been removed altogether, and the GIF writer has been simplified to produce
"uncompressed GIFs".  This technique does not use the LZW algorithm; the
resulting GIF files are larger than usual, but are readable by all standard
GIF decoders.

We are required to state that
    "The Graphics Interchange Format(c) is the Copyright property of
    CompuServe Incorporated.  GIF(sm) is a Service Mark property of
    CompuServe Incorporated."

}

###########################################################################
############################# Jhead Page ##################################
###########################################################################

set JheadLicenseText {
Jhead is public domain software - that is, you can do whatever you want
with it, and include it in software that is licensesed under the GNU or the 
BSD license, or whatever other licence you chose, including proprietary
closed source licenses.

If you do integrate the code into some software of yours, I'd appreciate
knowing about it though. You can reach me at mwandel@rim.net
}

###########################################################################
############################# ImageMagick Page ############################
###########################################################################

set ImageMagickLicenseText {

Copyright (C) 2002 ImageMagick Studio, a non-profit organization
dedicated to making software imaging solutions freely available. 

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files
("ImageMagick"), to deal in ImageMagick without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of ImageMagick, and to
permit persons to whom the ImageMagick is furnished to do so, subject
to the following conditions: 

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of ImageMagick. 

Licensor ("ImageMagick Studio LLC") warrants that the copyright in and
to the Software ("ImageMagick") is owned by ImageMagick Studio LLC or
is distributed by ImageMagick Studio LLC under a valid current
license. Except as expressly stated in the immediately preceding
sentence, ImageMagick is provided by ImageMagick Studio LLC,
distributors, and copyright owners "AS IS", without warranty of any
kind, express or implied, including but not limited to the warranties
of merchantability, fitness for a particular purpose and
non-infringement. In no event shall ImageMagick Studio LLC,
contributors or copyright owners be liable for any claim, damages, or
other liability, whether in an action of contract, tort or otherwise,
arising from, out of or in connection with ImageMagick. 

Except as contained in this notice, the name of the ImageMagick Studio
shall not be used in advertising or otherwise to promote the sale, use
or other dealings in ImageMagick without prior written authorization
from the ImageMagick Studio. 

ImageMagick is available as

ftp://ftp.imagemagick.org/pub/ImageMagick/

The official ImageMagick WWW page is

http://www.imagemagick.org/ 
}

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

#the text below is generated by mkman

proc FillManualPage { w } {
$w.text tag configure bold -font {Courier 12 bold}
$w.text tag configure underline -underline on
$w.text insert end {
TKALBUM(1)                                             TKALBUM(1)



}
$w.text  insert end {NAME} bold
$w.text insert end {
       }
$w.text  insert end {TKAlbum} underline
$w.text insert end {  - a Tcl/Tk GUI that assists in the generation and
       maintenance of HTML photo albums


}
$w.text  insert end {SYNOPSIS} bold
$w.text insert end {
       }
$w.text  insert end {tkalbum} bold
$w.text insert end {


}
$w.text  insert end {DESCRIPTION} bold
$w.text insert end {
       }
$w.text  insert end {TKAlbum} underline
$w.text insert end { is a Tcl/Tk GUI assisting in  the  generation  and
       maintenance of HTML photo albums. It supports

       * downloads  of  photos from digital cameras using the USB
         mass storage protocol;

       * file handling, i.e., copying, moving,  renaming,  delet-
         ing, undeleting;

       * image  viewing  and  editing  (using  the  }
$w.text  insert end {Image} underline
$w.text insert end {  }
$w.text  insert end {Magick} underline
$w.text insert end {
         tools);

       * transformation of images in a lossless  way  (using  the
         }
$w.text  insert end {jpegtran} underline
$w.text insert end { utility);

       * inspection  and  modification of EXIF data in JPEG files
         generated by digital cameras (using the }
$w.text  insert end {jhead} underline
$w.text insert end { tool);

       * editing descriptive data for  the  }
$w.text  insert end {album} underline
$w.text insert end {  script,  e.g.,
         album and picture descriptions, orders of images, etc.;

       * customization of the }
$w.text  insert end {album} underline
$w.text insert end { script execution; and

       * generation  of HTML photo albums using the }
$w.text  insert end {album} underline
$w.text insert end { script.


       The photos are stored in }
$w.text  insert end {albums} underline
$w.text insert end {, which are organized in  a
       hierarchical  manner,  i.e., an album can contain pictures
       and sub-albums. There exists one  designated  }
$w.text  insert end {root} underline
$w.text insert end {  }
$w.text  insert end {album} underline
$w.text insert end {.
       Albums  are  implemented as }
$w.text  insert end {directories} underline
$w.text insert end {, which contain the
       pictures and sub-albums, together with additional descrip-
       tive  data  and some cached data. The }
$w.text  insert end {album} underline
$w.text insert end { script is used
       to generate HTML files and thumbnails from this structure,
       which  can be viewed with any web browser. All pictures in
       one album are displayed as thumbnails on  one  HTML  page,
       which also contains additional navigation elements.


}
$w.text  insert end {DYNAMIC} bold
$w.text insert end { }
$w.text  insert end {HELP} bold
$w.text insert end {
       When  one moves with the mouse over an area of the }
$w.text  insert end {TKAlbum} underline
$w.text insert end {
       main window or one of its option windows, a  dynamic  help
       text  will  pop  up after half a second. This text gives a
       short explanation of what this area of the window is  good
       for,  or in the case of option windows, what the option is
       good for. Since these dynamic help texts are already quite
       extensive,  they are not repeated here and the options are
       not explained in detail here.


}
$w.text  insert end {WINDOW} bold
$w.text insert end { }
$w.text  insert end {ORGANIZATION} bold
$w.text insert end {
       Below the }
$w.text  insert end {menu} underline
$w.text insert end { }
$w.text  insert end {bar} underline
$w.text insert end {, the absolute  directory  path  to  the
       }
$w.text  insert end {root} underline
$w.text insert end {  }
$w.text  insert end {album} underline
$w.text insert end {  and the path to the }
$w.text  insert end {current} underline
$w.text insert end { }
$w.text  insert end {album} underline
$w.text insert end { relative to
       the root album are shown. Next, the four main areas of the
       windows are displayed. From the left to right there are:

       * the }
$w.text  insert end {movement} underline
$w.text insert end { }
$w.text  insert end {area} underline
$w.text insert end {,

       * the }
$w.text  insert end {directory} underline
$w.text insert end { }
$w.text  insert end {list} underline
$w.text insert end {,

       * the }
$w.text  insert end {EXIF} underline
$w.text insert end { }
$w.text  insert end {data} underline
$w.text insert end { }
$w.text  insert end {display} underline
$w.text insert end {, and

       * the }
$w.text  insert end {preview} underline
$w.text insert end { }
$w.text  insert end {area} underline
$w.text insert end {.

       At  the bottom, there is the }
$w.text  insert end {text} underline
$w.text insert end { }
$w.text  insert end {area} underline
$w.text insert end {, which  can be used
       to annotate  the pictures.


   }
$w.text  insert end {Movement} bold
$w.text insert end { }
$w.text  insert end {Area} bold
$w.text insert end {
       The movement area contains two }
$w.text  insert end {arrow} underline
$w.text insert end { }
$w.text  insert end {buttons} underline
$w.text insert end {, which can be
       used  to  move selected items up and down in the }
$w.text  insert end {directory} underline
$w.text insert end {
       }
$w.text  insert end {list} underline
$w.text insert end {. A }
$w.text  insert end {left} bold
$w.text insert end { }
$w.text  insert end {click} bold
$w.text insert end { moves the selected items one slot up or
       down.  A }
$w.text  insert end {right} bold
$w.text insert end { }
$w.text  insert end {click} bold
$w.text insert end { moves them to the beginning or end of
       the list. Note,  however,  that  directories  and  regular
       files  are  never  mixed  and  that  the special directory
       entries "." and ".." always appear  first.  The  order  in
       which the items appear in the directory list is used later
       by the }
$w.text  insert end {album} bold
$w.text insert end { script to generate the HTML  pages  with  the
       thumbnails.


   }
$w.text  insert end {Directory} bold
$w.text insert end { }
$w.text  insert end {List} bold
$w.text insert end {
       The  directory  list displays the names of the directories
       and the pictures contained in the current album.  Directo-
       ries are prefixed with the following codes:

       [A]   this is a regular album

       [nA]  the  contents of this album will not be displayed on
             the HTML pages because  it  contains  a  file  named
             }
$w.text  insert end {.no_album} underline
$w.text insert end {

       [hA]  this  album  will  be  hidden,  i.e.,  even the name
             should not be shown,  because  it  contains  a  file
             named }
$w.text  insert end {.hide_album} underline
$w.text insert end {

       [S]   this is a directory symlink. Such symlinks should be
             avoided in album structures because they  will  con-
             fuse  the  browser.  The  reason is that }
$w.text  insert end {theme} underline
$w.text insert end { }
$w.text  insert end {files} underline
$w.text insert end {
             that are used to configure  the  appearance  of  the
             HTML pages are accessed using relative path informa-
             tion. With symlinks this will easily go  astray  and
             the  HTML  pages will look very funny. For this rea-
             son, }
$w.text  insert end {TKAlbum} underline
$w.text insert end { will by default issue warnings when one
             selects or generates albums following symlinks.

       [T]   this is a thumbnail directory generated by the album
             script, which is usually not displayed  (see  option
             }
$w.text  insert end {Ignore} underline
$w.text insert end { }
$w.text  insert end {album} underline
$w.text insert end { }
$w.text  insert end {aux} underline
$w.text insert end { }
$w.text  insert end {dirs} underline
$w.text insert end { }
$w.text  insert end {and} underline
$w.text insert end { }
$w.text  insert end {files} underline
$w.text insert end { in }
$w.text  insert end {View} underline
$w.text insert end { }
$w.text  insert end {options} underline
$w.text insert end {)

       Any  entry  not prefixed with one of the markers described
       above is a regular file. Usually, it is  a  picture  file,
       provided  the  option  }
$w.text  insert end {Show} underline
$w.text insert end { }
$w.text  insert end {only} underline
$w.text insert end { }
$w.text  insert end {picture} underline
$w.text insert end { }
$w.text  insert end {files} underline
$w.text insert end { in the }
$w.text  insert end {View} underline
$w.text insert end {
       }
$w.text  insert end {options} underline
$w.text insert end { has been selected. Note that if this option is not
       selected,  the  }
$w.text  insert end {album} underline
$w.text insert end { script may fail because it chokes on
       non-image files. So either use this option or do not  keep
       non-image files in the album directories.

       Most  often   file  names  are not very informative and it
       makes more sense to display the }
$w.text  insert end {picture} underline
$w.text insert end { }
$w.text  insert end {names} underline
$w.text insert end { (see below).
       This  mode  can  be  activated by selecting the option }
$w.text  insert end {Use} underline
$w.text insert end {
       }
$w.text  insert end {picture} underline
$w.text insert end { }
$w.text  insert end {names} underline
$w.text insert end { }
$w.text  insert end {in} underline
$w.text insert end { }
$w.text  insert end {directory} underline
$w.text insert end { }
$w.text  insert end {list} underline
$w.text insert end { in the }
$w.text  insert end {View} underline
$w.text insert end { }
$w.text  insert end {options} underline
$w.text insert end {.

       Entries in  the  directory  list  can  be  selected  using
       Tcl/Tks  extended  selection  mode enhanced by a few other
       possibilities:

       }
$w.text  insert end {Mouse-Left-Click} bold
$w.text insert end {
              deselects all entries, selects the entry under  the
              cursor and makes that the }
$w.text  insert end {anchor} underline
$w.text insert end {

       }
$w.text  insert end {Mouse-Left-Click-And-Drag} bold
$w.text insert end {
              selects  all entries over which the mouse cursor is
              dragged

       }
$w.text  insert end {Shift-Mouse-Left-Click} bold
$w.text insert end {
              selects all entries  between  the  anchor  and  the
              entry under the mouse cursor

       }
$w.text  insert end {Ctrl-Mouse-Left-Click} bold
$w.text insert end {
              toggles the selection status of the entry under the
              mouse cursor (and sets or unsets the anchor)

       }
$w.text  insert end {Mouse-Left-Double-Click} bold
$w.text insert end {
              displays the picture using an external  viewer  or,
              if  the  entry  is  a  directory, changes into this
              directory

       }
$w.text  insert end {Up-Arrow-Key} bold
$w.text insert end {
              changes the selection to the entries one slot up

       }
$w.text  insert end {Down-Arrow-Key} bold
$w.text insert end {
              changes the selection to the entries one slot down

       }
$w.text  insert end {Return-Key} bold
$w.text insert end {
              acts in the same way as the }
$w.text  insert end {Down-Arrow-Key} underline
$w.text insert end {. If  the
              last  entry  in the list currently selected entries
              is a directory,  however,  one  changes  into  this
              directory.

       If  more than one file is selected, only information about
       the last file in the list of selected files is  displayed.
       Similarly,  the  preview is generated for the last file in
       the list of selected files.


   }
$w.text  insert end {EXIF} bold
$w.text insert end { }
$w.text  insert end {Data} bold
$w.text insert end { }
$w.text  insert end {Display} bold
$w.text insert end {
       Provided the selected file has an }
$w.text  insert end {EXIF} underline
$w.text insert end { header,  which  are
       generated  by  most  of the digicams nowadays, interesting
       information about the picture is displayed, e.g., when the
       picture  was taken, which camera was used etc. This infor-
       mation is extracted using the }
$w.text  insert end {jhead} bold
$w.text insert end {  program  by  Matthias
       Wandel.  The  set of fields to be displayed can be config-
       ured in the }
$w.text  insert end {JpegInfo} underline
$w.text insert end { }
$w.text  insert end {options} underline
$w.text insert end { dialog.


   }
$w.text  insert end {Preview} bold
$w.text insert end { }
$w.text  insert end {Area} bold
$w.text insert end {
       If the selected file is a picture file, a preview is  gen-
       erated.  First  of  all, }
$w.text  insert end {TKAlbum} underline
$w.text insert end { tries to extract the }
$w.text  insert end {EXIF} underline
$w.text insert end {
       thumbnail that modern digicams store in  each  JPEG  file.
       This  thumbnail  has a very low resolution, but is easy to
       extract and to display. For this reason it is  used  as  a
       first  sketch. Then the original picture is scaled down to
       the preview area size, which takes much longer  time.  For
       this   reason,   this   process  is  carried  out  in  the
       background. Finally, the  generated  previews  are  cached
       internally  and  reused  when  the picture file is visited
       again.

       If the entry }
$w.text  insert end {Show} underline
$w.text insert end { }
$w.text  insert end {thumbnail} underline
$w.text insert end { }
$w.text  insert end {cropping} underline
$w.text insert end { in the }
$w.text  insert end {View} underline
$w.text insert end {  menu  is
       activated,  the  preview  contains  also  an indication of
       which parts of the picture will be displayed in the thumb-
       nails  on  the  HTML  pages.  The  kind of cropping can be
       changed by selection the appropriate action in the  }
$w.text  insert end {Thumb-} underline
$w.text insert end {
       }
$w.text  insert end {nail} underline
$w.text insert end { menu.

       If  a  high  resolution image is needed, it is possible to
       request to look at the picture using an  external  viewer.
       This is invoked using the }
$w.text  insert end {Meta-X} bold
$w.text insert end { key or by }
$w.text  insert end {Mouse-Left-Dou-} bold
$w.text insert end {
       }
$w.text  insert end {ble-Click} bold
$w.text insert end { over an entry in the directory list.


   }
$w.text  insert end {Text} bold
$w.text insert end { }
$w.text  insert end {Area} bold
$w.text insert end {
       Below the four main areas, there is the }
$w.text  insert end {text} underline
$w.text insert end {  }
$w.text  insert end {area} underline
$w.text insert end {,  which
       can  be  used to annotate the pictures. This area consists
       of three fields:

       * picture name,

       * picture caption, and

       * AltTag.

       The }
$w.text  insert end {picture} underline
$w.text insert end { }
$w.text  insert end {name} underline
$w.text insert end {, if specified,  is  displayed  below  the
       thumbnail  on  the  album HTML page. If no picture name is
       given, the file name without extension will be used  as  a
       picture name (where underlines are substituted by blanks).
       Note that directories cannot get  a  picture  name.  Since
       picture  names are usually much more informative than file
       name, there exists the option of  displaying  the  picture
       name instead of the file names (in the }
$w.text  insert end {View} underline
$w.text insert end { }
$w.text  insert end {options} underline
$w.text insert end { dialog
       and in the }
$w.text  insert end {View} underline
$w.text insert end { menu).

       The }
$w.text  insert end {picture} underline
$w.text insert end { }
$w.text  insert end {caption} underline
$w.text insert end { is usually a longer explanation  which
       is  -  depending on the album style - also displayed below
       the thumbnail or only displayed on the  HTML  picture  for
       the single picture.

       Finally,  the  contents  of  }
$w.text  insert end {AltTag} underline
$w.text insert end {  is displayed when one
       moves with  the  mouse  cursor  over  the  picture.  As  a
       default, the picture name is used as the AltTag.

       One  can  move  around between the three text fields using
       }
$w.text  insert end {Tab} bold
$w.text insert end { and }
$w.text  insert end {Shift-Tab} bold
$w.text insert end {.  The entered text will be saved after a
       selection  command  (i.e., a mouse selection in the direc-
       tory list), an }
$w.text  insert end {Up} bold
$w.text insert end { or }
$w.text  insert end {Down} bold
$w.text insert end { }
$w.text  insert end {Arrow} bold
$w.text insert end {, or }
$w.text  insert end {Return} bold
$w.text insert end { key  press,  or
       any other command.

       In  addition to picture annotations, one can also annotate
       an entire album. With the command  }
$w.text  insert end {Edit} underline
$w.text insert end {  }
$w.text  insert end {header} underline
$w.text insert end {  }
$w.text  insert end {&} underline
$w.text insert end {  }
$w.text  insert end {footer} underline
$w.text insert end {
       (}
$w.text  insert end {Meta-F} bold
$w.text insert end {),  one can edit the header and footer of an album.


}
$w.text  insert end {CAMERA} bold
$w.text insert end { }
$w.text  insert end {DOWNLOAD} bold
$w.text insert end {
       Currently, }
$w.text  insert end {TKAlbum} underline
$w.text insert end {  supports  only  cameras  that  can  be
       accessed  using the USB mass storage protocol. Since there
       are only two operations, namely downloading and  deleting,
       it should be easy to add support for cameras using }
$w.text  insert end {gphoto} underline
$w.text insert end {.

       In order to make downloading possible, one has to edit the
       camera  options  and  specify  the right }
$w.text  insert end {mount} underline
$w.text insert end { and }
$w.text  insert end {unmount} underline
$w.text insert end {
       commands. Also the directory path to the  image  directory
       on  the camera needs to be specified. Finally, it might be
       necessary to edit }
$w.text  insert end {/etc/fstab} underline
$w.text insert end { and/or scripts for  the  hot-
       plugging  mechanism.  Modern  Linux  installation probably
       handle all that already.

       If everything has been set up properly, the  menu  command
       }
$w.text  insert end {Download} underline
$w.text insert end {  }
$w.text  insert end {from} underline
$w.text insert end { }
$w.text  insert end {camera} underline
$w.text insert end { downloads all pictures from the cam-
       era. If specified in the }
$w.text  insert end {Camera} underline
$w.text insert end {  }
$w.text  insert end {options} underline
$w.text insert end {,  the  downloaded
       files are post-processed. The default is to rename them to
       a string made up from the date and the  time  the  picture
       was taken. In addition, one may also request in the }
$w.text  insert end {Camera} underline
$w.text insert end {
       }
$w.text  insert end {options} underline
$w.text insert end { dialog that the image files  get  deleted  in  the
       camera  when  they have been downloaded. Instead, however,
       it is possible to delete all images on  the  camera  using
       the }
$w.text  insert end {Delete} underline
$w.text insert end { }
$w.text  insert end {camera} underline
$w.text insert end { }
$w.text  insert end {images} underline
$w.text insert end { command.


}
$w.text  insert end {ALBUM} bold
$w.text insert end { }
$w.text  insert end {GENERATION} bold
$w.text insert end {
       There  are a number of different ways to execute the album
       script depending on what kind of changes had been done:


       }
$w.text  insert end {Strictly} bold
$w.text insert end { }
$w.text  insert end {local} bold
$w.text insert end { }
$w.text  insert end {generation} bold
$w.text insert end { }
$w.text  insert end {(Meta-S):} bold
$w.text insert end {
              The  album  in  the  current  directory   will   be
              (re-)generated  without  descending  into  to  sub-
              directories. Thumbnails and  medium-sized  previews
              will  be regenerated only if the pictures they were
              generated from have changed.  This is the right way
              to  use  the  album  script  when  just  some local
              changes have been made. Note that there is no  dif-
              ference between "strictly local" and "local" if the
              current album does not contain sub-albums.


       }
$w.text  insert end {Local} bold
$w.text insert end { }
$w.text  insert end {generation} bold
$w.text insert end { }
$w.text  insert end {(Meta-L):} bold
$w.text insert end {
              The albums are  (re-)generated  starting  from  the
              current  album  descending  into sub-albums. Thumb-
              nails and medium-sized previews will be regenerated
              only  if the pictures they were generated from have
              changed.  This is the right way to apply the  album
              script  if  an  entire  sub-tree has been edited or
              inserted. If the current  album  is  a  new  album,
              i.e.,  there  exists  no entry in the index file of
              the next higher album to the current album, then it
              is  necessary to start the album script once in the
              the next higher directory in order to get the right
              navigation entries on the HTML pages.


       }
$w.text  insert end {Global} bold
$w.text insert end { }
$w.text  insert end {generation} bold
$w.text insert end { }
$w.text  insert end {(Meta-G):} bold
$w.text insert end {
              All   albums  starting  from  the  root  album  are
              (re-)generated. Again, thumbnails and  medium-sized
              previews  will  be regenerated only if the pictures
              they were generated from have changed. This is  the
              right way to call album after a theme change.


       }
$w.text  insert end {Global} bold
$w.text insert end { }
$w.text  insert end {generation} bold
$w.text insert end { }
$w.text  insert end {&} bold
$w.text insert end { }
$w.text  insert end {Cleanup} bold
$w.text insert end { }
$w.text  insert end {(Meta-P):} bold
$w.text insert end {
              Same  as  above, but with the }
$w.text  insert end {album} underline
$w.text insert end {'s }
$w.text  insert end {-clean} underline
$w.text insert end { option
              enabled.  This will identify and delete  all  obso-
              lete thumbnails, previews, and HTML files. There is
              also an option that enables  cleanup at each regen-
              eration operation.


       }
$w.text  insert end {Forced} bold
$w.text insert end { }
$w.text  insert end {strictly} bold
$w.text insert end { }
$w.text  insert end {local,} bold
$w.text insert end { }
$w.text  insert end {local,} bold
$w.text insert end { }
$w.text  insert end {or} bold
$w.text insert end { }
$w.text  insert end {global} bold
$w.text insert end { }
$w.text  insert end {generation:} bold
$w.text insert end {
              Same  as above, however, all thumbnails and medium-
              sized pictures are regenerated. This kind  of  call
              is  necessary  if the geometry of the thumbnails or
              medium-sized pictures has changed.


              All these actions can be  activated  in  the  }
$w.text  insert end {Album} underline
$w.text insert end {
              menu.


}
$w.text  insert end {OTHER} bold
$w.text insert end { }
$w.text  insert end {MENU} bold
$w.text insert end { }
$w.text  insert end {COMMANDS} bold
$w.text insert end {
       The commands in the }
$w.text  insert end {File} underline
$w.text insert end { menu are for file handling, i.e.,
       creating new albums, moving to an album,  copying,  renam-
       ing, moving, and deleting files. There is also an }
$w.text  insert end {Undelete} underline
$w.text insert end {
       command, which changes into the }
$w.text  insert end {trash} underline
$w.text insert end {  }
$w.text  insert end {bin} underline
$w.text insert end {  directory  and
       allows  one  to  restore deleted files - provided that the
       }
$w.text  insert end {trash} underline
$w.text insert end { }
$w.text  insert end {bin} underline
$w.text insert end { option has not been deselected.

       One particular useful command is the }
$w.text  insert end {Copy} underline
$w.text insert end { }
$w.text  insert end {&} underline
$w.text insert end {  }
$w.text  insert end {Convert} underline
$w.text insert end {  com-
       mands  that  can be used to copy selected files to another
       directory and to convert them to low resolution and highly
       compressed images, which then can be sent by e-mail. There
       exists an option that  allows  an  automatic  renaming  of
       these files using the picture name so that it is easier to
       refer to these pictures. Furthermore, there is  an  option
       called  }
$w.text  insert end {Edit} underline
$w.text insert end {  }
$w.text  insert end {before} underline
$w.text insert end {  }
$w.text  insert end {"Copy} underline
$w.text insert end { }
$w.text  insert end {&} underline
$w.text insert end { }
$w.text  insert end {Convert"} underline
$w.text insert end {, which triggers the
       question whether an image should be edited before  conver-
       sion.


       Note  that  image  files  that  are copied are marked by a
       star. This feature is helpful to keep track  of  the  pic-
       tures  that  have  been  copied  and/or converted for some
       purpose, e.g., to be shipped off for printing  or  for  e-
       mailing them.

       The  dialogs  in the }
$w.text  insert end {Option} underline
$w.text insert end { allow to change a large set of
       options. In  particular,  it  is  possible  to  deactivate
       almost  all  warnings.  In addition, one can also load new
       profiles and switch between existing profiles  (which  may
       have different }
$w.text  insert end {root} underline
$w.text insert end { }
$w.text  insert end {albums} underline
$w.text insert end {).

       The  commands  in the }
$w.text  insert end {Transformation} underline
$w.text insert end { menu are image trans-
       formation operations. Provided the pictures to  be  trans-
       formed are JPEG files, the }
$w.text  insert end {jpegtran} underline
$w.text insert end { tool is used to trans-
       form the pictures in a lossless way.

       The purpose of the commands in the }
$w.text  insert end {Thumbnail} underline
$w.text insert end {  menu  is  to
       change  the  cropping  area for the generation of the HTML
       thumbnails for the selected picture. The effect of such  a
       change can be viewed if the }
$w.text  insert end {Show} underline
$w.text insert end { }
$w.text  insert end {thumbnail} underline
$w.text insert end { }
$w.text  insert end {cropping} underline
$w.text insert end { in the
       }
$w.text  insert end {View} underline
$w.text insert end { menu has been enabled.

       The }
$w.text  insert end {View} underline
$w.text insert end { menu addresses previewing and viewing pictures in
       an  external  window.  Some  of  the options from the }
$w.text  insert end {View} underline
$w.text insert end {
       }
$w.text  insert end {options} underline
$w.text insert end { dialog are repeated in this menu in order to  make
       them more easily accessible.


       The  }
$w.text  insert end {JpegInfo} underline
$w.text insert end { menu contains a number of commands connected
       with the capabilities of the }
$w.text  insert end {jhead} underline
$w.text insert end { tool. For  example,  on
       can  change  the  picture  dates  and one can rename files
       according to the picture date and time.

       Finally, under the }
$w.text  insert end {Help} underline
$w.text insert end { menu one finds supposedly  helpful
       information,  such  as  the  licenses for }
$w.text  insert end {TKAlbumP} underline
$w.text insert end { }
$w.text  insert end {and} underline
$w.text insert end { }
$w.text  insert end {its} underline
$w.text insert end {
       }
$w.text  insert end {component} underline
$w.text insert end { }
$w.text  insert end {programs,} underline
$w.text insert end { }
$w.text  insert end {version} underline
$w.text insert end { }
$w.text  insert end {information,} underline
$w.text insert end { }
$w.text  insert end {and} underline
$w.text insert end {  }
$w.text  insert end {this} underline
$w.text insert end {  }
$w.text  insert end {manual} underline
$w.text insert end {
       }
$w.text  insert end {page.} underline
$w.text insert end {  }
$w.text  insert end {In} underline
$w.text insert end {  }
$w.text  insert end {addition,} underline
$w.text insert end {  }
$w.text  insert end {one} underline
$w.text insert end { }
$w.text  insert end {can} underline
$w.text insert end { }
$w.text  insert end {disable} underline
$w.text insert end { }
$w.text  insert end {dynamic} underline
$w.text insert end { }
$w.text  insert end {help} underline
$w.text insert end { }
$w.text  insert end {in} underline
$w.text insert end { }
$w.text  insert end {this} underline
$w.text insert end {
       }
$w.text  insert end {menu.} underline
$w.text insert end {


}
$w.text  insert end {SHORTCUTS} bold
$w.text insert end {
       The following shortcuts can be used (in addition to  those
       supported by Tcl/Tk for }
$w.text  insert end {entry} underline
$w.text insert end { and }
$w.text  insert end {text} underline
$w.text insert end { fields):

       }
$w.text  insert end {Meta-+} bold
$w.text insert end { Rotate picture by 90 degrees clockwise

       }
$w.text  insert end {Meta--} bold
$w.text insert end { Rotate picture by 90 degrees counter clockwise

       }
$w.text  insert end {Meta-1} bold
$w.text insert end { Rotate picture by }
$w.text  insert end {1} underline
$w.text insert end {80 degrees

       }
$w.text  insert end {Meta-C} bold
$w.text insert end { }
$w.text  insert end {C} underline
$w.text insert end {opy the set of selected files to another directory

       }
$w.text  insert end {Meta-D} bold
$w.text insert end { }
$w.text  insert end {D} underline
$w.text insert end {elete the set of selected files

       }
$w.text  insert end {Meta-E} bold
$w.text insert end { }
$w.text  insert end {E} underline
$w.text insert end {dit the selected image

       }
$w.text  insert end {Meta-F} bold
$w.text insert end { Edit the header & }
$w.text  insert end {f} underline
$w.text insert end {ooter of this album

       }
$w.text  insert end {Meta-G} bold
$w.text insert end { Generate album pages }
$w.text  insert end {g} underline
$w.text insert end {lobally

       }
$w.text  insert end {Meta-H} bold
$w.text insert end { }
$w.text  insert end {H} underline
$w.text insert end {orizointal flip, i.e., mirror  the  picture  along
              the vertical axis

       }
$w.text  insert end {Meta-L} bold
$w.text insert end { Generate album pages }
$w.text  insert end {l} underline
$w.text insert end {ocally

       }
$w.text  insert end {Meta-M} bold
$w.text insert end { }
$w.text  insert end {M} underline
$w.text insert end {ove the set of selected files to another directory

       }
$w.text  insert end {Meta-N} bold
$w.text insert end { Create a }
$w.text  insert end {n} underline
$w.text insert end {ew album

       }
$w.text  insert end {Meta-O} bold
$w.text insert end { }
$w.text  insert end {O} underline
$w.text insert end {pen another album

       }
$w.text  insert end {Meta-O} bold
$w.text insert end { Global album generation & cleanu}
$w.text  insert end {p} underline
$w.text insert end {

       }
$w.text  insert end {Meta-R} bold
$w.text insert end { }
$w.text  insert end {R} underline
$w.text insert end {ename the selected file

       }
$w.text  insert end {Meta-S} bold
$w.text insert end { Generate album pages }
$w.text  insert end {s} underline
$w.text insert end {trictly locally

       }
$w.text  insert end {Meta-T} bold
$w.text insert end { Copy & Conver}
$w.text  insert end {t} underline
$w.text insert end { the selected files

       }
$w.text  insert end {Meta-U} bold
$w.text insert end { Move one album }
$w.text  insert end {u} underline
$w.text insert end {p

       }
$w.text  insert end {Meta-V} bold
$w.text insert end { }
$w.text  insert end {V} underline
$w.text insert end {ertical flip, i.e., mirror  along  the  horizontal
              axis

       }
$w.text  insert end {Meta-X} bold
$w.text insert end { Start e}
$w.text  insert end {x} underline
$w.text insert end {ternal viewer

       }
$w.text  insert end {Return} bold
$w.text insert end { Go  to next picture or, if a directory is selected,
              change into the selected directory

       }
$w.text  insert end {Up-Arrow} bold
$w.text insert end {
              Move selection one slot up

       }
$w.text  insert end {Down-Arrow} bold
$w.text insert end {
              Move selection one slot down

       }
$w.text  insert end {Tab} bold
$w.text insert end {    Move focus to next text field

       }
$w.text  insert end {Shift-Tab} bold
$w.text insert end {
              Move focus to previous text field



}
$w.text  insert end {SHARED} bold
$w.text insert end { }
$w.text  insert end {DEVELOPMENT} bold
$w.text insert end { }
$w.text  insert end {OF} bold
$w.text insert end { }
$w.text  insert end {ALBUMS} bold
$w.text insert end {
       One can configure }
$w.text  insert end {TKAlbum} underline
$w.text insert end { so that it supports  the  shared
       development  of  albums. The idea is to set up a dedicated
       Unix group for this purpose and make all users  who  shall
       be  able  to modify the albums members of this group. Then
       the initial album must be  set  up  such  that  all  files
       belong  to the new group and the group write permission is
       set.

       In TKAlbum, the group name must be entered in the  }
$w.text  insert end {General} underline
$w.text insert end {
       }
$w.text  insert end {options} underline
$w.text insert end {  dialog  in  the field }
$w.text  insert end {Group} underline
$w.text insert end { }
$w.text  insert end {ID} underline
$w.text insert end { }
$w.text  insert end {for} underline
$w.text insert end { }
$w.text  insert end {shared} underline
$w.text insert end { }
$w.text  insert end {work} underline
$w.text insert end { }
$w.text  insert end {on} underline
$w.text insert end {
       }
$w.text  insert end {albums} underline
$w.text insert end {. If this field contains a non-empty string, TKAlbum
       will make sure that all files generated in this album will
       belong to this group and have their  group  write  permis-
       sions set, so that everybody from the group can delete and
       overwrite these files.

       Using the profile }
$w.text  insert end {select/load/save} underline
$w.text insert end {  functionality  in  the
       }
$w.text  insert end {General} underline
$w.text insert end {  }
$w.text  insert end {options} underline
$w.text insert end {  dialog,  one can also establish a common
       profile for all group members. Note that  the  appropriate
       write permissions must be set to store the profile.

       There  are  no  synchronization  methods for assuring that
       changes get safely logged to  the  profile  files  or  the
       albums.  In  other  words,  it would not be a good idea to
       work concurrently on an album since changes most  probably
       will  be  lost.  Only one user at a time should work on an
       album structure!

}
$w.text  insert end {SEE} bold
$w.text insert end { }
$w.text  insert end {ALSO} bold
$w.text insert end {
       }
$w.text  insert end {album,} bold
$w.text insert end {   }
$w.text  insert end {jhead,} bold
$w.text insert end {   }
$w.text  insert end {jpegtran(1),} bold
$w.text insert end {   }
$w.text  insert end {display(1),} bold
$w.text insert end {   }
$w.text  insert end {convert(1),} bold
$w.text insert end {
       }
$w.text  insert end {mogrify(1)} bold
$w.text insert end {


}
$w.text  insert end {FILES} bold
$w.text insert end {
       ~/.tkalbum/profile - list of user preference files
       ~/.tkalbum/default - default user preference file
       captions.txt - contains picture names and captions
       header.txt - header text for an album
       footer.txt - footer text for an album
       tn - thumbnail directory
       <root-album>/.trash - trash bin




}
$w.text  insert end {BUGS} bold
$w.text insert end {
       Caption  files  for single pictures, i.e., <filename>.txt,
       are ignored by }
$w.text  insert end {TKAlbum} underline
$w.text insert end {. The }
$w.text  insert end {album} underline
$w.text insert end { script on the other hand
       considers  these  captions with higher priority than those
       in the captions.txt files.

       The user is not warned when files  necessary  to  run  the
       album script are deleted.

       Files starting with a dash lead to trouble.


}
$w.text  insert end {AUTHOR} bold
$w.text insert end {
       Bernhard Nebel
       bernhard.nebel@gmx.de




                        October 1, 2002             TKALBUM(1)

}}
