#!/bin/sh
# the next line restarts using wish \
exec wish "$0" "$@"
set TKAlbumVersion "2.00"
#########################################################################
#
#  TKAlbum last edit: 09-12-2004
#  
#  Copyright (C) 2002-2004 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.
#
#
########################################################################

# ----------------------- 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.4 } {
    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.4" \
		error 0 Exit
    exit
}

# ----------------------- Determine Host Name ------------------------
if { [ info exists env(HOST) ] } {
    set HOST $env(HOST)
} elseif { [ info exists env(HOSTNAME) ] } {
    set HOST $env(HOSTNAME)
} elseif { [ catch { set HOST [ eval exec  uname -n  ] } ] } {
    set HOST "?"
}

# ----------------------- GLOBAL SETTINGS ----------------------------
# Finishing flag
set Finishing 0
# The strict motif switch
set tk_strictMotif 0
# our option data structure:
array unset Option
# options we do not want to save or load
# because they either cause trouble, they are obsolete 
# or they are for internal use only:
set OptionIgnoreList { "Alert" \
                 "ALBUM_verbose" \
		"ALBUM_Forced_cropping" \
		"EXIF_RemovalWarning" \
		"STATE_ProfileReadOnly" \
		"STATE_AlbumReadOnly" \
		"STATE_InstallationCheck" 
                "EXIF_Camera"
                "ALBUM_index"}
# Only if we are really alert, we want to see certain 
# error messages:
if { ! [ info exists TKAlbumVersion ] } {
    set TKAlbumVersion "test"
    set Option(Alert) 1
} else {
    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_viewer 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.33"  }
set Option(SCRIPT_album) "album"
set Itest(SCRIPT_album) { "album"  "--version" \
			      {This is album v((?:[0-9]|\.)*)\n}  "3.01"   }
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}  "2.2"   }
set Option(SCRIPT_convert) "convert"
set Itest(SCRIPT_convert) { "convert (ImageMagick)"  "-help" \
				{ImageMagick ((?:[0-9]|\.)*) }  "5.5.7"   }
set Option(SCRIPT_mogrify) "mogrify"
set Itest(SCRIPT_mogrify) { "mogrify (ImageMagick)"  "-help" \
				{ImageMagick ((?:[0-9]|\.)*) }  "5.5.7"   }
set Option(SCRIPT_viewer) "xzgv --full --zoom"
set Itest(SCRIPT_viewer) { "viewer"  "-version" "" "" }

set Option(SCRIPT_edit) "display"
set Itest(SCRIPT_edit) { "image editor" "-help" "" "" }
set Option(SCRIPT_tempdir) "/tmp"

set Option(SCRIPT_browser) "mozilla"
set Option(SCRIPT_browser_remote) "mozilla -remote openURL(file://@f)"

# File handling options: 
set Option(FILE_copy_captions) 1
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) 200
set Option(FILE_delete_warning) 1
set Option(FILE_multiple_delete_warning) 1
set Option(FILE_dir_delete_warning) 1
set Option(FILE_symlink_warning) 1
set Option(FILE_aux_dir_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_make) 1
set Option(EXIF_Camera_model) 1
set Option(EXIF_Pic_Date) 1
set Option(EXIF_Resolution) 1
set Option(EXIF_JQual) 0
set Option(JPEG_JQualError) 0
set Option(JPEG_JQualErrorCamera) ""
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_IgnoreAlbumFiles) 1
set Option(VIEW_IgnoreDashFilenames) 1
set Option(VIEW_OnlyPics) 1
set Option(VIEW_ShowFileExt) 1
set Option(VIEW_ShowHiddenDirs) 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_dir_height) 4
set Option(VIEW_dir_width)  20
set Option(VIEW_preview_type) "ppm"
set Option(VIEW_CachePreviews) 1
set Option(VIEW_CacheSize) 400
set Option(VIEW_InternalCacheSize) 100
set Option(VIEW_CacheDir) ".preview-cache"
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) 1
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_filename) "index.html"
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_just_medium) 0
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_theme_url) ""
set Option(ALBUM_no_theme) 1
set Option(ALBUM_charset) "iso-8859-1"
set Option(ALBUM_image_loop) 1
set Option(ALBUM_animated_gifs) 1
set Option(CUSTOM_ALBUM) ""

# new album options (v.3.06)
set Option(ALBUM_dir_thumbs) ""
set Option(ALBUM_embed) ""
set Option(ALBUM_album_captions) ""
set Option(ALBUM_caption_edit) ""
set Option(ALBUM_default_index) ""
set Option(ALBUM_top) ""
set Option(ALBUM_html) ""
set Option(ALBUM_theme_path) ""
set Option(ALBUM_sharpen) ""
set Option(ALBUM_medium_scale_opts) ""
set Option(ALBUM_thumb_scale_opts) ""
set Option(ALBUM_exif) ""
set Option(ALBUM_exif_album) ""
set Option(ALBUM_exif_image) ""

set Option(STATE_InstallCheck) 1
set Option(STATE_CopyAndConvertTarget) ""
set Option(STATE_MoveTarget) ""
set Option(STATE_CopyTarget) ""
set Option(STATE_UploadTarget) ""
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
set Option(STATE_ProfileReadOnly) 0
set Option(STATE_AlbumReadOnly) 0

# ----------------------- Global Vars --------------------------------

array set JheadVal { }
array set PicName { }
array set Caption { }
array set AltTag { }
set NameList { }
set DirNameList { } 
set FileNameList { }
set TempCounter 0
set ViewerProcesses -1
set TempViewerFile ""
set TempDirs { }
set AlbumLock ""
set ProfileLock ""
set RootAlbumText "*** no root album selected ***"
set CurrentAlbumText "*** no current album selected ***"
array set InfoRow { }
set CurrentImage ""
set NameVar ""
set AltTagVar ""
set SavingCaps 0
set CaptionsEdited 0
set FilesCopied { } 
set RealPreviewList { }
set IgnoreJheadErrors 0
set DashIgnore 0
set IgnoreGroupErrors 0
set CurrentWindow ""
set ShowProcess ""
set UnShowProcess ""
set CurrentProfile ""
set ProfileList { }
set TrashList { }
set CacheList { }

# ----------------------- 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 { $TKAlbumVersion == "test" } {
    append UserProfileDir "-test"
}

# file names
set LockFile ".lock$Option(ALBUM_not_img)"
set ListFile(TrashList) ".trash_list$Option(ALBUM_not_img)"
set ListFile(CacheList) ".cache_list$Option(ALBUM_not_img)"

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


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 [ expr $MaxPrevSize + 1 ]  \
	-height [ expr $MaxPrevSize + 1 ] 
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)  "no_album"
set OptionName(ALBUM_hide_album) "hide_album"
set OptionName(ALBUM_not_img) "not_img"
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_filename) "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_just_medium) "just_medium"
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_theme_url) "theme_url"
set OptionName(ALBUM_no_theme) "*special*"
set OptionName(ALBUM_captions) "captions"
set OptionName(ALBUM_header) "header"
set OptionName(ALBUM_footer) "footer"
set OptionName(ALBUM_charset) "charset"
set OptionName(ALBUM_animated_gifs) "animated_gifs"
set OptionName(ALBUM_image_loop) "image_loop"
set OptionName(ALBUM_dir_thumbs) "dir_thumbs"
set OptionName(ALBUM_embed) "embed"
set OptionName(ALBUM_album_captions) "album_captions"
set OptionName(ALBUM_caption_edit) "caption_edit"
set OptionName(ALBUM_default_index) "default_index"
set OptionName(ALBUM_top) "top"
set OptionName(ALBUM_html) "html"
set OptionName(ALBUM_theme_path) "theme_path"
set OptionName(ALBUM_sharpen) "sharpen"
set OptionName(ALBUM_medium_scale_opts) "*special*"
set OptionName(ALBUM_thumb_scale_opts) "*special*"
set OptionName(ALBUM_exif) "*special*"
set OptionName(ALBUM_exif_album) "*special*"
set OptionName(ALBUM_exif_image) "*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 ALBUM_just_medium ALBUM_animated_gifs \
        ALBUM_image_loop ALBUM_dir_thumbs ALBUM_embed ALBUM_album_captions \
	ALBUM_caption_edit }
set StringAlbumOptions { ALBUM_columns ALBUM_body ALBUM_depth \
	ALBUM_name_length ALBUM_index_filename ALBUM_tn_type \
	ALBUM_medium_type ALBUM_Forced_cropping \
	ALBUM_theme ALBUM_captions ALBUM_dir ALBUM_theme_url ALBUM_charset \
	ALBUM_default_index ALBUM_top ALBUM_html ALBUM_theme_path \
        ALBUM_sharpen }
set SpecialAlbumOptions { \
	ALBUM_medium_geom_height ALBUM_medium_geom_width \
	ALBUM_tn_geom_width ALBUM_tn_geom_height \
	ALBUM_no_theme ALBUM_scale_opts
        ALBUM_medium_scale_opts ALBUM_thumb_scale_opts \
        ALBUM_exif ALBUM_exif_album ALBUM_exif_image }
set NoAlbumOptions {  ALBUM_cleanup ALBUM_header ALBUM_footer ALBUM_no_album \
	ALBUM_hide_album ALBUM_not_img}
			     

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

proc ProcessAlive { procidlist } {
    # check whether procid is still running
    # works also for lists of proc ids and returns 1 if at least one is alive
    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
    global OptionIgnoreList

    set converted 0
    if { [ file exists $profile ] } {
	set ConvIgnoreList { }
	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 ] && \
	     $Version != "test" } {
	    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 ConvIgnoreList { "VIEW_preview_type" \
				 "STATE_InstallCheck" \
				 "ALBUM_image_sizes" }
	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 \"$tk_sM\" 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 $OptionIgnoreList $Name ] != -1 || \
		    ( $converted && \
		    [ lsearch $ConvIgnoreList $Name ] != -1 ) } {
		GuardedRead error "Profile Error" $profile $fid dummy
	    } else {
		GuardedRead error "Profile Error" \
		    $profile $fid Option($Name)
	    }
	}
	close $fid 
	if { $converted } {
	    catch { WriteOneProfile $profile }
	}
    }
}

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

    if { $CurrentProfile == "" || \
	    $Option(STATE_ProfileReadOnly) || \
	    $Option(STATE_AlbumReadOnly) } {
	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
    global OptionIgnoreList
    
    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 ] {
	if { [ lsearch $OptionIgnoreList $key ] == -1 } {
	    GuardedWrite error "Profile Writing Error" $profile \
		    $fid $key 
	    GuardedWrite return "Profile Writing Error" $profile \
		    $fid $Option($key)
	}    
    }
    close $fid 
}

# menu entry "Load Profile"
proc LoadNewProfile { } {
    global CurrentProfile
    global ProfileList
    global Option
    global CacheList TrashList


    SaveCaptions

    WriteFileList CacheList
    WriteFileList TrashList


    if { [ PwdFails ] } {
	return
    }
    set newprofile [ tk_getOpenFile \
			 -initialdir [ file dirname $CurrentProfile ] \
			 -initialfile [ file tail $CurrentProfile ] \
			 -title "Load 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 ]"
    }
    .listframe.list selection clear 0 end
    NoCurrentPic
    WriteProfile
    set Option(STATE_AlbumReadOnly) 0
    if { [ catch { ReadOneProfile $newprofile } ] } {
	tk_dialog .dia "Profile load error" "Sorry, but it was impossible to read the profile \"$newprofile.\"" error 0 OK	
	return
    }
    if { [ lsearch $ProfileList $newprofile ] == -1 } { 
	lappend ProfileList $newprofile
	.mbar.pref.menu.profile add radiobutton -label $newprofile \
		-variable CurrentProfile -value $newprofile \
		-command SelectProfile
    }
    set CurrentProfile $newprofile
    SetAlbumLock
    ReadFileList CacheList
    ReadFileList TrashList
    StateSettings
    ResizePreview
    GotoAlbum $Option(CurrentAlbum) ""
    WriteProfile
}

# Menu entry: Create new profile
proc SaveProfileAs { } {
    global CurrentProfile
    global ProfileList
    global Option
    global UserProfileDir
    global UserProfilePointer

    SaveCaptions

    if { [ PwdFails ] } {
	return
    }
    set newprofile [ tk_getSaveFile \
			 -initialdir [ file dirname $CurrentProfile ] \
			 -initialfile [ file tail $CurrentProfile ] \
			 -title "Create 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 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 CacheList TrashList

    SaveCaptions
    WriteFileList CacheList
    WriteFileList TrashList

    set Option(STATE_AlbumReadOnly) 0
    if { [ catch { ReadOneProfile $CurrentProfile } ] } { 
	tk_dialog .dia "Profile initialization error" "Sorry, but it was impossible to read the profile \"$CurrentProfile.\"" error 0 OK
	return
    }
    SetAlbumLock
    ReadFileList CacheList
    ReadFileList TrashList
    .listframe.list selection clear 0 end
    NoCurrentPic
    StateSettings
    ResizePreview
    GotoAlbum $Option(CurrentAlbum) ""
}
    

# ------------------------- Cache and Trash Bin Management -----
proc ReadFileList { ListVar } { 
    global Option
    global ListFile
    global CacheList
    global TrashList

    upvar $ListVar l
    set l { }

    if { $Option(STATE_ReadOnly) || \
	     $Option(STATE_AlbumReadOnly) || \
	     $Option(RootAlbum) == "" || \
	     ! [ file exists [file join $Option(RootAlbum)  \
				  $ListFile($ListVar) ] ] } {
	return
    }
    set file [ file join $Option(RootAlbum) $ListFile($ListVar) ]
    set fid [ GuardedROpen return "$ListVar Error"  $file ] 
    if { $fid <= 0 } {
	set Option(STATE_AlbumReadOnly) 1
	return
    }
    while { ! [ eof $fid ] } {
	if { ! [ GuardedRead return "$ListVar Error" $file \
		     $fid input ] } {
	    catch { close $fid }
	    set Option(STATE_AlbumReadOnly) 1
	    return
	}
	if { $input == "-" } {
	    set l [ lreplace $l end end ]
	} elseif { $input != "" } { 
	    # delete any doublette, which may have resulting
	    # from re-appending an element
	    set l [ lremove $l $input ]
	    lappend l $input 
	}
    }
    catch { close $fid }

    # now we write the (parhaps shortened) list 
    # and check whether we have the right permissions!
    WriteFileList $ListVar
}

proc WriteFileList { ListVar } { 
    global Option
    global ListFile

    upvar $ListVar l

    if { $Option(STATE_ReadOnly) || \
	     $Option(STATE_AlbumReadOnly) || \
	     $Option(RootAlbum) == "" } {
	return
    }
    set file [ file join $Option(RootAlbum) $ListFile($ListVar) ]
    set fid [ GuardedWOpen return "$ListVar Error"  $file ] 
    if { $fid <= 0 } {
	set Option(STATE_AlbumReadOnly) 1
	return
    }
    foreach el $l {
	if { ! [ GuardedWrite  return "$ListVar Error"  $file $fid $el ] } {
	    set Option(STATE_AlbumReadOnly) 1
	    catch { close $fid } 
	    SetGroupPerms $file
	    return
	}
    }
    catch { close $fid } 
    SetGroupPerms $file
}

# $del = 0 means append
# $del = 1 means delete from list and the file(s)
# $del = -1 means delete from list but not file(s)
proc DelOrAppendToFileList { ListVar Element del } {
    global Option
    global ListFile

    upvar $ListVar l

    if { $Option(STATE_ReadOnly) || \
	     $Option(STATE_AlbumReadOnly) || \
	     $Option(RootAlbum) == "" } {
	return
    }
    if { $del != 0 && [ lsearch -glob $l $Element ] == -1 } {
	return
    }
    set file [ file join $Option(RootAlbum) $ListFile($ListVar) ]
    if { [ catch { set fid [ open $file "a+" ] } errmes ] } {
	tk_dialog .dia $title \
	    "Could not open file \"$file\" for in append mode: \"$errmes.\"" \
	    error 0 OK
	set Option(STATE_AlbumReadOnly) 1
	return
    }
    if { ! [ GuardedWrite  return "$ListVar Error"  $file $fid $Element ] } {
	set Option(STATE_AlbumReadOnly) 1
    }
    if { $del != 0 } {
	if { ! [ GuardedWrite  return "$ListVar Error"  $file $fid "-" ] } {
		 set Option(STATE_AlbumReadOnly) 1
	 }
    }
    catch { close $fid } 
    SetGroupPerms $file

    # now modify list 
    while { [ lsearch -glob $l $Element ] != -1 } {
	set ix [ lsearch -glob $l $Element ]
	if { $del == 1 } { 
	    catch { file delete -force -- [ lindex $l $ix ] } 
	}
	set l [ lreplace $l $ix $ix ]
    }
    if { $del == 0 } {
	lappend l $Element
    }
}

proc AdjustFileListLength { ListVar size notmatch } {
    global Option
    global CacheList
    global TrashList
    global ListFile

    upvar $ListVar l

    if { $ListVar == "TrashList" && $size == 0 } {
	# empty trash!
	file delete -force -- [ file join $Option(RootAlbum) \
					    $Option(FILE_trash_dir) ] 
	file delete -force -- [ file join $Option(RootAlbum) \
				    $ListFile(TrashList) ]
	set l ""
	return 
    }
    if { $ListVar == "CacheList" && $size == 0 } {
	# empty external cache
	file delete -force -- [ file join $Option(RootAlbum) \
				    $ListFile(CacheList) ]
	RemoveExternalCache $Option(RootAlbum)
	cd $Option(CurrentAlbum)
	set l ""
	return
    }
    if { $Option(STATE_ReadOnly) || \
	     $Option(STATE_AlbumReadOnly) || \
	     [ llength $l ] <= $size  } { return }
    while {  [ llength $l ] > $size  } {
	# try to find something that does not match $notmatch
	set delel ""
	if { $notmatch != "" } {
	    foreach el $l {
		if { ! [ string match $notmatch $el ] } {
		    set delel $el
		    break
		}
	    }
	}
	if { $delel != "" } {
	    DelOrAppendToFileList $ListVar $delel 1
	} else {
	    DelOrAppendToFileList $ListVar  [ lindex $l 0 ] 1
	}
    }
}

proc RemoveExternalCache { start } {
    global Option
    
    cd $start
    puts "remove in $start"
    file delete -force $Option(VIEW_CacheDir)
    foreach d [ concat [ glob -nocomplain -type d * ]  \
		    [ glob -nocomplain -type d .* ] ] {
	if { $Option(FILE_save_dir) != $d && \
		 $Option(FILE_trash_dir) != $d && \
		 $Option(VIEW_CacheDir) != $d && \
		 $Option(ALBUM_dir) != $d && \
		 "." != $d && \
		 ".." != $d } {
	    RemoveExternalCache [ file join $start $d ] 
	}
    }
}



    

# ------------------------- Locking routines -------------------

# check whether a lock file is present
# arguments are: PROFILE or ALBUM
# if there is a lock file return owner (or SELF), process id, 
#     hostname, and boot time (in secs) or "?"
#     otherwise return ""
proc LockPresent { type } {

    global LockFile
    global Option
    global UserProfileDir
    global env

    if { $Option(RootAlbum)  == "" && $type == "ALBUM" } {
	return ""
    }
    if { $type == "ALBUM" } {
	set lock [ file join $Option(RootAlbum) $LockFile ]
    } else {
	set lock [ file join $UserProfileDir $LockFile ]
    }
    set procid "?"
    set hostname "?"
    set btime "?"
    if { [ file exist $lock ] } {
	if { ! [ catch { set fid [ open $lock r ] } ] } {
	    catch { gets $fid procid }
	    catch { gets $fid hostname }
	    catch { gets $fid btime }
	    catch { close $fid }
	}
	if { [ file owned $lock ] } {
	    set owner "SELF"
	} else {
	    set owner [ file attributes $lock -owner ]
	}
	return [ list $owner $procid $hostname $btime ]
    } else {
	return ""
    }
}

# try to retrieve boot time from /proc/stat
# if unsuccessful, return "?"
proc BootTime { } {
    set fid 0
    catch { set fid [ open "/proc/stat" "r" ] }
    if { $fid <= 0 } {
	return "?"
    }
    while { ! [ eof $fid ] } {
	gets $fid line
	if { [ string match "btime *" $line ] } {
	    close $fid
	    return [ string range $line 6 end ]
	}
    }
    close $fid
    return "?"
}
	    

# try to write a lock file (should be done only if no 
# other lockfile is present). In any case, if the lockfile
# exists, an error occurs!
proc WriteLock { type } {
    global Option
    global LockFile
    global UserProfileDir
    global AlbumLock
    global ProfileLock
    global env HOST

    if { $Option(RootAlbum)  == "" && $type == "ALBUM" } {
	return 1
    }

    if { $type == "ALBUM" } {
	set lock [ file join $Option(RootAlbum) $LockFile ]
    } else {
	set lock [ file join $UserProfileDir $LockFile ]
    }

    if { [ catch { set fid [ open $lock "WRONLY CREAT EXCL" ] } errmes ] } {
	if { [ tk_dialog .dia "Lock Setting" "Cannot write lock file $lock (Error message is: \"$errmes\"). Do you want to continue read-only?" \
		error 0 Yes "No, abort" ] == 0 } {
	    set Option(STATE_ReadOnly) 1
	    if { $type == "PROFILE" } { 
		set Option(STATE_ProfileReadOnly) 1
	    }
	    StateSettings
	    return -1
	} else {
	    return 0
	}
    } else {
	catch { puts $fid [ pid ] }
	catch { puts $fid $HOST }
	catch { puts $fid [ BootTime ] }
	catch { close $fid }
#	SetGroupPerms $lock
	if { $type == "ALBUM" } {
	    set AlbumLock $lock
	} else {
	    set ProfileLock $lock
	}
	return 1
    }
}

# try to set lock - if successful (incl. going R/O) , return 1 else 0
proc TryToSetLock { type } {
    global Option
    global LockFile
    global UserProfileDir
    global CurrentProfile
    global env HOST
    
    if { $Option(RootAlbum)  == "" && $type == "ALBUM" } {
	return 1
    }
    set ownerprops [ LockPresent $type ]
    if  { $ownerprops != "" } { 
	if { [ lindex $ownerprops 0 ] == "SELF" } {
	    if { [ pid ] == [ lindex $ownerprops 1 ] } {
		if { $HOST == [ lindex $ownerprops 2 ] } {
		    return 1
		}
	    }
	}
	set testpid [ lindex $ownerprops 1 ]
	if { $testpid != "?" } {
	    if { $HOST == [ lindex $ownerprops 2 ] && $HOST != "?" } {
		if { ! [ ProcessAlive $testpid ] || \
			 ( [ lindex $ownerprops 3 ] != "?" &&
			   [ BootTime ] != "?" &&
			   [ lindex $ownerprops 3 ] != [ BootTime ] ) } {
		    if { [ DeleteLock $type ] } {
			set ownerprops ""
		    }
		}
	    }
	}
    }

    if  { $ownerprops == "" } {
	if { [ WriteLock $type ] != 0 } {
	    return 1
	} else {
	    return 0
	}
    } else {
	if { $type == "ALBUM" } {
	    set dirtext "root album \"$Option(RootAlbum)\""
	} else {
	    set dirtext "profile directory \"$UserProfileDir\""
	}
	if { [ lindex $ownerprops 0 ] == "SELF" } {
	    set question "Another instance of TKAlbum is active and has locked the $dirtext (process [ lindex $ownerprops 1 ] on host [ lindex $ownerprops 2]). Do you want to continue read-only or do you want to give up?"
	} else { 
	    set question "User [ lindex $ownerprops 0 ] on host [ lindex $ownerprops 2] has locked the $dirtext. Do you want to continue read-only or to give up?"
	}
	set resp [ tk_dialog .dia "Locking Problem" \
		$question \
		questhead \
		0 "Continue read-only" "Give up" ]
	if { $resp == 0 } {
	    set Option(STATE_ReadOnly) 1
	    if { $type == "ALBUM" } { 
		if { [ string first \
			$UserProfileDir $CurrentProfile 0 ] != 0 || \
			[ file writable $Option(RootAlbum) ] } {
		    tk_dialog .dia "Locking Info" \
			    "Changes to the album will not be written to disk in order to avoid confusion with changes by the concurrently active instance of TKAlbum" info 0 OK
		    set Option(STATE_AlbumReadOnly) 1
		}
	    } else {
		tk_dialog .dia "Locking Info" \
			"Changes to the current profile will not be written to disk in order to avoid confusion with changes by the concurrently active instance of TKAlbum" info 0 OK
		set Option(STATE_ProfileReadOnly) 1
	    }
	    StateSettings
	    return 1
	} else {
	    tk_dialog .dia "Help" "If you believe that the lock file is obsolete (because, e.g., the program has crashed), you can delete the lock file \".lock$Option(ALBUM_not_img)\" in the $dirtext." info 0 OK
	    return 0
	}
    }
}

# try to delete a lock file (should only be done
# if the lock file is stale)
# return 1 if successful, otherwise 0
proc DeleteLock { type } {
    global LockFile
    global UserProfileDir
    global Option 

    if { $type == "ALBUM" } {
	set lock [ file join $Option(RootAlbum) $LockFile ]
    } else {
	set lock [ file join $UserProfileDir $LockFile ]
    }


    if { [ catch { file delete -force -- $lock } errmes ] } { 
	tk_dialog .dia "Lock File Error" \
		"Could no delete stale lock file \"$lock.\" Got the following error message: \"$errmes\"" error 0 OK
	return 0
    }
    return 1
}

# try to set the lock file for the profile directory
# if unsuccessful, exit
# Successful could mean that ReadOnly and ProfileReadOnly has been set
proc SetProfileLock { } {
    if { ! [ TryToSetLock "PROFILE" ] } {
	set Option(RootAlbum) ""
	set Option(CurrentAlbum) ""
	set CurrentProfile ""
	Exit
    }
}

# try to set the lock file for a new root album directory
# do not attempt if profile read only
# if unsuccessful, exit
# Successful could mean that ReadOnly and AlbumReadOnly has been set
proc SetAlbumLock { } {
    global Option
    global CurrentProfile
    global AlbumLock

    if { $AlbumLock != "" } {
	catch { file delete -force -- $AlbumLock }
	set AlbumLock ""
    }
    if { $Option(STATE_ProfileReadOnly) } {
	set Option(STATE_ReadOnly) 1
    }
    if { $Option(STATE_ReadOnly) } {
	return 1
    }
    if { ! [ TryToSetLock "ALBUM" ] } {
	set Option(RootAlbum) ""
	set Option(CurrentAlbum) ""
	set CurrentProfile ""
	Exit
    }
    return 1
}

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

# return 1 if it is an file used by album
# If delete = 1, then we return only 1 if
# it is not an essential file for album 
proc AlbumFile { file { delete 0 } } {
    global Option

    set tail [ file tail $file ]
    if { ! $delete } { 
	if { [ lsearch -glob [ list $Option(ALBUM_captions) \
				   $Option(ALBUM_header) \
				   $Option(ALBUM_footer) \
				   $Option(ALBUM_no_album) \
				   $Option(ALBUM_hide_album) \
				   $Option(ALBUM_not_img) \
				   "$Option(ALBUM_index_filename)" \
				   "index.html" \
				   "index.htm" \
				   "*.txt" \
				   "*.htaccess" \
				   "*~" \
				   "\#*\#" ] $tail ] != -1 } {
	    return 1
	} 
    } else {
	if { [ lsearch -glob [ list "$Option(ALBUM_index_filename)" \
				   "index.html" \
				   "index.htm" \
				   "*.htaccess" \
				   "*~" \
				   "\#*\#" ] $tail ] != -1 } {
	    return 1
	}
    }
    return 0
}

# return 1 if the file can be ignored (and deleted if necessary)
proc IgnorableFile { file } {
    global Option
    
    set tail [ file tail $file ]
    if { [ string match "-*" $tail ] && $Option(VIEW_IgnoreDashFilenames) } {
	return 1
    }
    if { ! [ IsImageFile $file ] && \
	     $Option(ALBUM_known_images) && \
	     ! [ AlbumFile $file ] } {
	return 1
    }
    if {  [ NotImageMark $file ] } {
	return 1
    }
    return 0
}


# return 1 if the directory is a album or tklabum generated one
# if trash = 1, then we check, whether the dir is in the trash bin 
proc AlbumDir { file { trash 0 } } {
    global Option
    
    set tail [ file tail $file ]
    set dirname [ file dirname [ file join $Option(CurrentAlbum) $file ] ]
    if { [ lsearch -exact [ list $Option(ALBUM_dir) \
				$Option(VIEW_CacheDir) \
				$Option(FILE_save_dir) \
				"CVS" "SCCS" "RCS" ] $tail ] != -1 } {
	return 1
    } 
    if { $trash && \
	     $tail == $Option(FILE_trash_dir) && \
	     $Option(RootAlbum) == $dirname } {
	return 1
    }
    return 0
}

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
    set x ""
    if { [ catch { gets $fid x } errmes ] } {
	if { [ eof $fid ] } {
	    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 } {
    global Option

    BlockInput
    if { $Option(Alert) } {
	eval $cmd
    } else {
	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 lremovelist { list rem } {
    if { $rem == "" } {
	return $list 
    } else {
	return [ lremovelist [ lremove $list [ lhead $rem ] ] [ ltail $rem ] ]
    }
}

proc lremove { list el } {
    while { [ lsearch -glob $list $el ] != -1 } {
	set list [ lreplace $list \
		       [ lsearch -glob $list $el ] \
		       [ lsearch -glob $list $el ] ]
    }
    return $list 
}

proc lhead { list } {
    return [ lindex $list 0 ]
}

proc ltail { list } {
    return [ lreplace $list 0 0 ]
}

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 [ lsearch $list $el ]
# - provided EnableGroupPerm is true
proc SetGroupPerms { file } {
    global Option
    global IgnoreGroupErrors
    global env

    if { $Option(STATE_GroupName) == "" } {
	catch { file attr $file -perm "go-w" }
	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 attributes $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 ]

    if { $Option($scriptname) == "" } { 
	if { $scriptname == "SCRIPT_browser_remote"  } {
	    return 1
	} else {
	    set mess "There is no program call entry for $printname. This will cause problems when a call for $printname becomes necessary"
	}
    } else {
	set mess ""
	set errmes ""
	set errorCode NONE
	set helpstr ""
	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
	    } else {
		if { $helpstr == "" } {
		    set helpstr $errmes
		}
	    } 
	}
	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 DirNameList
     global FileNameList
     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
     # mentioned in caption file
     set NameList [ concat  { . .. } $DirNameList $FileNameList ] 
     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 { . .. } \
 		      $DirNameList \
 		      $DirSubList \
 		      $FileNameList \
 		      $FileSubList ] 
     set NameList { }

     # now remove those files and dirs that we do not want to see
     foreach file $DirList {
 	if { ( ! [ AlbumFile $file ] && ! [ AlbumDir $file 1 ] ) || \
 		 ! $Option(VIEW_IgnoreAlbumFiles) } {
 	    if { [ file isfile $file ] } { 
 		if { ! [ IgnorableFile $file ] } { 
 		    lappend NameList $file
 		}
 	    } elseif { [ file isdirectory $file ] } {
 		if { [ string first "." $file ] != 0 || \
 			 ($Option(ALBUM_all) == 1  && $file != "..") || \
 			 $file == "." || \
 			 ( $file == ".." && \
 			       $Option(CurrentAlbum) != \
 			       $Option(RootAlbum) ) } {
 		    if { $file == "." || \
 			     $file == ".." || \
 			     $Option(VIEW_ShowHiddenDirs) || \
 				      ! [ file exists \
 					      [ file join $file \
 						    $Option(ALBUM_hide_album) ] ] } { 
 			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 != ".." && \
		 $PicName($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 ""
		if { $file != "." && $file != ".." } {
		    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"		    
 	    }
 	}
     }

     if { ! [ info exists PicName(.) ] } {
 	set PicName(.) ""
 	set Caption(.) ""
 	set AltTag(.) ""
     }

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

 proc NotImageMark { filename } {
     global Option

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

 proc IsImageFile { file } {
     global ImageFileExtensions

     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 FileNameList DirNameList 
     global CurrentImage

     set CurrentImage ""
     catch { array unset PicName }
     array set PicName { }
     catch { array unset Caption }
     array set Caption { }
     catch { array unset AltTag }
     array set AltTag  { }
     set DirNameList { }
     set FileNameList { }

     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 line [ string map { " :: " "\t" } $line ] 
 		}
 		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 && \
		     ! [ file exists $fname ] } {
 		set PicName(.) $names
 		set Caption(.) ""
 		set AltTag(.) ""
 	    } elseif { [ file exists $fname ] } {
 		if { [ file isfile $fname ] || 
 		     [ file isdirectory $fname ] } {
 		    if { [ lsearch $FileNameList $fname ] == -1 && \
 			     [ file isfile $fname ] } { 
 			lappend FileNameList $fname
 			set PicName($fname) $names
 			set Caption($fname) $caption
 			set AltTag($fname) $alttag
 		    }
 		    if { ( [ lsearch $DirNameList $fname ] == -1 || \
			       $fname == "." ) && \
 			     [ file isdirectory $fname ] } {
 			if { $fname != ".." } {
			    if { $fname != "." } {
				lappend DirNameList $fname
			    }
 			    set PicName($fname) $names
 			    set Caption($fname) ""
 			    set AltTag($fname) ""			
 			} else {
 			    tk_dialog .dia \
 				"Internal Error" \
 				"Directory \"$fname\" is named, something that should not happen!" \
 				error 0 OK
			    if { $Option(Alert) } {
				error "Internal Error"
			    }
 			}
 		    }
 		}
 	    }
 	}
 	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 
 # or by mentioning "."
 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 != "" } {
		if { [ string first  " :: " $line 0 ] > -1 } {
		    set line [ string map { " :: " "\t" } $line ] 
 		}
		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 && \
			   ! [ file exists [ file join $dir $fname ] ]) || \
			 $fname == "." } {
		    set namefound $name
 		}
 	    }
 	}
 	close $fid
     }
     return $namefound
 }

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

     if { $Option(STATE_ReadOnly) || \
 	     $Option(RootAlbum) == "" || \
 	     $Option(CurrentAlbum) == "" || \
 	     ! $Option(STATE_AlbumEnabled) || \
 	     $CurrentImage == ".." } {
 	return
     }
     set namechange 0
     if { $CurrentImage != "" && $CurrentImage != "." } {
 	set curix [ lsearch $NameList $CurrentImage ]
 	if { $curix == -1 } {
	    if { $Option(Alert) } {
		tk_dialog .dia \
 		    "Internal Error (in SaveCaptions)" \
 		    "Could not find index for pic \"$CurrentImage\". Current album is \"$Option(CurrentAlbum)\"" \
 		    error 0 OK
		error "Internal Error"
	    }
	    # well, the safest thing is to return
	    # since there appears to be something out of sync!
 	    return
 	}
 	set NewPicName $NameVar
 	set NewAltTag  $AltTagVar
 	set NewCaption \
 	    [ string trim \
 		  [ string map { "\n" " " } \
 			[ .textframe.cap.text get 1.0 end ] ] ]
 	if { $NewPicName != $PicName($CurrentImage)  } {
 	    set namechange 1
 	    set PicName($CurrentImage) $NewPicName
 	}
 	if { $namechange || \
 		$NewCaption != $Caption($CurrentImage) || \
 		$NewAltTag  != $AltTag($CurrentImage) } {
 	    set CaptionsEdited 1
 	}
 	set Caption($CurrentImage) $NewCaption
 	set AltTag($CurrentImage) $NewAltTag
 	if { $namechange } {
 	    if { ( $Option(VIEW_UsePicName) && \
 		       [ file isfile $CurrentImage ] ) || \
 		     ( $Option(VIEW_UseAlbumName) && \
 			   [ file isdirectory $CurrentImage ] ) } {
 		set prefix [ string range \
 				 [ .listframe.list get $curix ] 0 3 ]
 		set isselected [ .listframe.list \
 			selection includes $curix ]
 		.listframe.list delete $curix
 		if { $NewPicName != "" } {
 		    set NewString $NewPicName
 		} else {
 		    set NewString [ lindex $NameList $curix ]
 		} 
 		.listframe.list insert $curix "${prefix}$NewString"
 		if { $isselected } {
 		    .listframe.list selection set $curix
 		}
 	    }
 	}
     }
     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 this error message 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 { ! [ AlbumFile $fname ] && \
 		     ! [ IgnorableFile $fname ] && \
 		     ! [ AlbumDir $fname ] } {
 		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
 		}

 	    }
 	}
     }

     # now write "."
     if { $PicName(.) != "" } {
	 catch { puts $fid ". :: $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 ". :: $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 }
 	SetGroupPerms $cap
     }
 }
    

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

# set CurrentImage to "unknown"
proc NoCurrentPic { } {
    global CurrentImage

    set CurrentImage ""
}

########## 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
 
    SaveCaptions
    update

    # determine whether it is a selection
    # if not, blank display and return
    set cursel [ .listframe.list curselection ]
    if { $cursel == "" } {
	ClearDisplay
	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 { $CurrentImage == $file } { return }

    ClearDisplay
    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
    global CacheList

    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
		set RealPreviewList [ lremove $RealPreviewList $ImageName ]
		lappend RealPreviewList $ImageName
		set pic [ PreviewInCache $Option(CurrentAlbum) $file \
			      $Option(VIEW_prevsize) ]
		if { $pic != "" } {
		    DelOrAppendToFileList CacheList $pic 0
		}
	    } else {
		BusyCursor
	    }
	    return
	}
    }

    image create photo $ImageName

    if { $Option(VIEW_UseEXIFTN) && [ IsJpeg $file ] } {
	if { [ PreviewInCache $Option(CurrentAlbum) $file \
		   $Option(VIEW_prevsize) ] == "" } {
	    if { [ IsJpeg $file ] } {
		BusyCursor
		GetEXIFPreview $ImageName 
		IdleCursor
	    }
	}
    }

    if { [ IsImageFile $file ] } {
	after 1 "GeneratePreview [ list $Option(CurrentAlbum) ] \
                 [ list $file ] [ list $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 == "" || $owidth == "" } {
	# 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 { dir file picname scale } {
    global CurrentPreviewImageName
    global RealPreviewList
    global Option
    global CacheList

    BusyCursor
    set CacheName [ PreviewInCache $dir $file $scale ] 
    set PreviewName [ MakeTempName $Option(VIEW_preview_type) ]
    
    if { $CacheName != "" } {
	catch { file copy -- $CacheName $PreviewName }
    } else {
	set err [ catch { set procid [ eval exec $Option(SCRIPT_convert) \
					   -sample ${scale}x$scale \
					   [ list $picname ]  \
					   [ list $PreviewName ]  & ] } errmes ]
	if { $err } {
	    tk_dialog .dia "Convert Execution Error" "Could not execute \"$Option(SCRIPT_convert) -sample ${scale}x$scale $picname $PreviewName \". Got the following error message: \"$errmes\"" error 0 OK 
	    IdleCursor
	    return
	}
	set running 1
	while { $running } {
	    update
	    after 100
	    set running [ ProcessAlive $procid ]
	}
    }
    if { $scale == $Option(VIEW_prevsize) } {
	if { [ file exist $PreviewName ] } { 
	    if { [ lsearch [ image names ] $picname ] == - 1 } {
		# image name has been deleted -- probably because of
		# rename operation
		if { $picname == $CurrentPreviewImageName } {
		    IdleCursor
		}
		return
	    }
	    $picname configure -file $PreviewName 
	    CachePreview $dir $file $scale $PreviewName
	    lappend RealPreviewList $picname
	    if { $picname == $CurrentPreviewImageName } {
		PreviewImg blank
		CopyToPreview $picname 
		ShowCrop $picname 
		IdleCursor
		if { $Option(VIEW_InternalCacheSize) > 0 } {
		    while { $Option(VIEW_InternalCacheSize) < \
			     [ llength $RealPreviewList ]  } {
			catch { image delete [ lhead $RealPreviewList ] }
			set RealPreviewList [ ltail $RealPreviewList ]
		    }
		}
	    }
	} 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 PreviewCacheName { dir file scale } {
    global Option 

    return [ file join $dir $Option(VIEW_CacheDir) \
		 "$file.$scale.$Option(VIEW_preview_type)" ]
}

proc CachePreview { dir file scale tempfile } {
    global Option
    global CacheList
    global TrashList

    if { ! $Option(VIEW_CachePreviews) || $Option(STATE_ReadOnly) || \
	 $Option(STATE_AlbumReadOnly) } { return }
    set cname [ PreviewCacheName $dir $file $scale ] 
    if { [ catch { file mkdir [ file join $dir \
				    $Option(VIEW_CacheDir) \
				    ] } ] } { return }
    set marker [ file join $dir \
		     $Option(VIEW_CacheDir) \
		     $Option(ALBUM_hide_album) ]
    if { ! [ file exists $marker ] } {
	catch { set fid [ open $marker w ] }
	catch { close $fid }
    }
    SetGroupPerms $dir
    SetGroupPerms [ file join $dir $Option(VIEW_CacheDir) ]
    SetGroupPerms $marker
    catch { file copy -- $tempfile $cname }
    DelOrAppendToFileList CacheList $cname 0
    AdjustFileListLength CacheList $Option(VIEW_CacheSize) \
	"*.$scale.$Option(VIEW_preview_type)"
}

proc KillCachedPreview { imagename } {
    global Option
    global CacheList
    global TrashList

    set dir [ file dirname $imagename ]
    set file [ file tail $imagename ]
    set cfile [ PreviewCacheName $dir $file $Option(VIEW_prevsize) ]
    DelOrAppendToFileList CacheList $cfile 1
}

proc PreviewInCache { dir file scale } {
    global Option
    global CacheList
    global TrashList

    if { ! $Option(VIEW_CachePreviews) } { return "" }
    set pfile [ PreviewCacheName $dir $file $scale ]
    if { [ file exists $pfile ] } {
	if { [ file mtime [ file join $dir $file ]] < [ file mtime $pfile ] } {
	    return $pfile
	}
    }
    DelOrAppendToFileList CacheList $pfile 1 
    return ""
}

proc ClearDisplay { } {
    global JheadVal
    global NameVar AltTagVar
    global CurrentImage

    set CurrentImage ""
    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
    catch { PreviewImg blank }
    update
}

proc DisplayPicInfo { file ix } {
    global JheadVal
    global CurrentImage
    global NameVar
    global AltTagVar
    global Caption PicName AltTag
    global CurrentPreviewImageName 
    global Option

    
    focus .textframe.nameent

    set CurrentImage $file
    if { [ file isdirectory $file ] } {
	IdleCursor
	set CurrentPreviewImageName ""
	set JheadVal(EXIF_File_Name) $file
	
	set albcnt [ expr [ CountAlbumsAndImages $file \
				0 $Option(VIEW_ShowHiddenDirs) 0 ] - 1 ]
	if { $albcnt == 1 } {
	    set albstring "album"
	} else {
	    set albstring "albums"
	}
	set imgcnt [ expr [ CountAlbumsAndImages $file \
				1 $Option(VIEW_ShowHiddenDirs) \
				0 ] - $albcnt - 1 ]
	if { $imgcnt == 1 } {
	    set imgstring "image"
	} else {
	    set imgstring "images"
	}
	set JheadVal(EXIF_File_Size) "$imgcnt $imgstring, $albcnt $albstring"
	.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
	    if { $file == ".." } {
		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)
	    .textframe.nameent icursor end
	    .textframe.nameent config -state normal
	    set AltTagVar $AltTag($file)
	    .textframe.altent config -state normal
	    .textframe.altent icursor end
	    .textframe.cap.text config -state normal
	    .textframe.cap.text insert end $Caption($file)
	    .textframe.cap.text mark set insert end
	}
    }
}

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

    if { [ string match "-*" $file ] } {
	# we only would get an error message, so just ignore
	if { ! $DashIgnore } {
	    tk_dialog .dia "Filename Starting with Dash" "File names starting with dashes cannot be handled by all programs. Please rename the file and/or consider activating the option \"Ignore file names starting with '-'\" under \"Options > View Options\"" info 0 OK
	    set DashIgnore 1
	}
	return
    }

    if { [ catch { set InfoString \
		       [ eval exec $Option(SCRIPT_jhead) -se -v { $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

    if { $JheadVal(EXIF_JQual) == "0" && $JheadVal(EXIF_Camera_model) == "" } {
	set JheadVal(EXIF_JQual) ""
    } elseif { $Option(JPEG_JQualError) && \
		   [ string match  \
			 "*$Option(JPEG_JQualErrorCamera)*" \
			 $JheadVal(EXIF_Camera_model) \
			  ] } {
	switch $JheadVal(EXIF_JQual) {
	    "0" { set JheadVal(EXIF_JQual) "basic" }
	    "basic" { set JheadVal(EXIF_JQual) "normal" }
	    "3" { set JheadVal(EXIF_JQual) "fine" }
	}
    }
}

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
    global CacheList TrashList

    SaveCaptions
    WriteFileList CacheList
    WriteFileList TrashList

    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(STATE_AlbumReadOnly) 0
    set Option(RootAlbum) $NewRootAlbum
    SetAlbumLock
    ReadFileList CacheList
    ReadFileList TrashList

    # now enable "Open Album" entry
    StateSettings

    GotoAlbum $Option(RootAlbum) ""
}

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

    SaveCaptions
    ClearDisplay

    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 tail [ file tail $name ]
    if { $Option(FILE_aux_dir_warning) } {
	if { [ lsearch -exact [ list $Option(ALBUM_dir) \
				    $Option(VIEW_CacheDir) \
				    $Option(FILE_save_dir) ] $tail ] != -1 } {
	    set res [ tk_dialog .dia "Goto Album Warning" \
			  "You attempt to enter a directory (\"$tail\") that is used for internal purposes, e.g., saving pictures and previews. Do you really want to continue?" warning 1 "Yes, continue" "No, abort"  ]
	    if { $res == 1 } {
		LoadPicFile
		return 
	    }
	}
    }
    set Option(CurrentAlbum) $name
    WriteProfile
    ShortenCurrentAlbumName
    .listframe.list delete 0 end

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

    if { [ catch { cd $Option(CurrentAlbum) } ] } {
	set Option(CurrentAlbum) ""
	return
    } 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

	}
    }	
    NoCurrentPic
    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 change 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 RealPreviewList
    
    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  }
    set RealPreviewList [ lremove $RealPreviewList $ImageName ]
    KillCachedPreview $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 } {
	ScanDirectory
	.listframe.list selection set $cursel
	.listframe.list see $cursel 
	ClearDisplay
	LoadPicFile
    }
}

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

    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
	if { $PicName($file) != "" } {
	    set PicName($backup) "$PicName($file) (copy)"
	}
	set Caption($backup) $Caption($file)
	set AltTag($backup) $AltTag($file)
	set NameList [ linsert $NameList [ expr $cursel + 1 ] $backup ]
    }
    if { [ catch { file rename -force -- $savefile $file } errmes ] } {
	tk_dialog .dia "Restore Error" "An error occured during the restoration of $file: \"$errmes\"" \
		error 0 OK
	return
    }
    SetGroupPerms $file
    NoCurrentPic
    SaveCaptions

    # delete old preview 
    set ImageName [ file join $Option(CurrentAlbum) $file ]
    catch { image delete $ImageName }
    set RealPreviewList [ lremove $RealPreviewList $ImageName ]
    KillCachedPreview $ImageName
    ClearDisplay
    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 RealPreviewList

    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 }
    set new [ NormalizeFileName $Option(CurrentAlbum) $new ]
    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:
    set ImageName [ file join $Option(CurrentAlbum) $file ]
    catch { image delete $ImageName }
    set RealPreviewList [ lremove $RealPreviewList $ImageName ]
    KillCachedPreview [ file join $Option(CurrentAlbum) $file ]
    catch { image delete $newname }
    KillCachedPreview $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
    ClearDisplay

    # 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 ]
    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 ]
	    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
    if { $cursel >= [ llength $NameList ] } {
	set cursel end
    }
    .listframe.list selection set $cursel
    .listframe.list see $cursel
    NoCurrentPic
    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
		if { $Option(Alert) } {
		    error "Internal Error"
		}
		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 || \
	      $Option(FILE_copy_captions) ) && \
	     [ 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
    global CacheList TrashList
    global RealPreviewList

    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 }

    set tail [ file tail $newdir ]
    if { $Option(FILE_aux_dir_warning) } {
	if { [ lsearch -exact [ list $Option(ALBUM_dir) \
				    $Option(VIEW_CacheDir) \
				    $Option(FILE_save_dir) ] $tail ] != -1 || \
		 ( [ string match "*$Option(FILE_trash_dir)*" $newdir ] && \
		   $Option(FILE_trash_dir) != "" ) } {
	    set res [ tk_dialog .dia "Internal Directrory Warning" \
			  "You attempt to $op files to a directory (\"$tail\") that is used for internal purposes, e.g., saving pictures and previews. Do you really want to continue?" warning 1 "Yes, continue" "No, abort" ]
	    if { $res == 1 } {
		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
    ClearDisplay
    .listframe.list selection clear 0 end

    # setup vars for progress display - just in case 
    # and in order to have ShowProgress initialized
    set ProgressFile ""
    set ProgressStep [ expr 1000.0 / (1.0* [ llength $cursel ]) ]
    set ShowProgress 0.0
    # Setup progress window if we also do conversion
    if { $conv && [ llength $cursel ] > 1 } {
	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
	}
	# if move from trash, delete files from TrashList
	if { [ string match \
		   "[file join $Option(RootAlbum) $Option(FILE_trash_dir)]*" \
		   $Option(CurrentAlbum) ] && \
		 $fileop == "rename" } {
	    # delete all files with common prefix from trash list
	    DelOrAppendToFileList TrashList \
		"[ file join $Option(CurrentAlbum) $file ]*" -1
	}
	SetGroupPerms $newfile
	if { [ file isdirectory $newfile ] } { 
	    DelOrAppendToFileList CacheList \
		"[ file join $Option(CurrentAlbum) $file ]*" 1
	    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 ] }
	    set RealPreviewList [ lremove $RealPreviewList \
				      [ file join $Option(CurrentAlbum) \
					    $file ] ]
	    KillCachedPreview [ file join $Option(CurrentAlbum) $file ]
	}
	catch { image delete $newfile }
	set RealPreviewList [ lremove $RealPreviewList $newfile ]
	KillCachedPreview $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) or we want to captions
        # always
	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 { [ catch { cd $Option(CurrentAlbum) } errmess ] } {
	tk_dialog .dia "Directory Error" "Cannot cd into current directory. Error message was: \"$errmess\"" error 0 OK
    }
    set dircnt 0
    set filcnt 0
    foreach el $cursel {
	if { [ lindex $NameList $el ] == "." } {
	    return [ tk_dialog .dia "Delete Error" "You cannot delete the album that is your \"current album\"!" \
			 warning 0 "OK" ]
	}
	if {  [ lindex $NameList $el ] == ".." } {
	    return [ tk_dialog .dia "Delete Error" "You cannot delete the album that is above your \"current album\"!" \
			 warning 0 "OK" ]
	}
	if { [ string first "\[" [ .listframe.list get $el ] 0 ] == 0 } {
	    set albumcnt [ CountAlbumsAndImages [ lindex $NameList $el ] \
			       0 1 ]
	    cd $Option(CurrentAlbum) 
	    set allcnt  [ CountAlbumsAndImages [ lindex $NameList $el ] \
			      1 1 ]
	    cd $Option(CurrentAlbum) 
	    set dircnt [ expr $dircnt + $albumcnt ]
	    set filcnt [ expr $filcnt + $allcnt - $albumcnt ]
	} 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) || \
	       ($Option(FILE_multiple_delete_warning) && \
		    [ expr $filcnt + $dircnt ] > 1 ) ) && \
	     ($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
    }
    
    if { $Option(FILE_use_trash_bin) && \
	     $Option(FILE_trash_size) < [ expr $filcnt + $dircnt ] } {
	set res [ tk_dialog .dia "Too much trash" \
		      "You want to delete [ expr $filcnt + $dircnt ] objects, but the trash bin can only accomodate $Option(FILE_trash_size) objects. For this reason, you will not be able to undo this delete operation completely. Do you want to continue anyway?" warning 1 "Yes, continue" "No, abort" ]
	if { $res == 1 } { return }
    }

    ClearDisplay

    # we count how many objects in the current dir 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 
    global CacheList TrashList
    global RealPreviewList

    # 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" ]
	}
    }
    # 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 { [ file isfile $file ] } {
	    # delete old previews
	    catch { image delete [ file join $Option(CurrentAlbum) $file ] }
	    set RealPreviewList [ lremove $RealPreviewList \
				      [ file join $Option(CurrentAlbum) \
					    $file ]]
	    KillCachedPreview [ file join $Option(CurrentAlbum) $file ]
	    if { $Option(CurrentAlbum) == $trash } {
		# delete file  from trash list
		DelOrAppendToFileList TrashList \
		    [ file join $Option(CurrentAlbum) $file ] -1
	    }
	} else {
	    # delete all previews with the current dir as prefix!
	    DelOrAppendToFileList CacheList \
		"[ file join $Option(CurrentAlbum) $file ]*" 1
	    if { $Option(CurrentAlbum) == $trash } {
		# delete all files with common prefix from trash list
		DelOrAppendToFileList TrashList \
		    "[ file join $Option(CurrentAlbum) $file ]*" -1
	    }
	}
	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 - and register them in the Trash List
	if { [ file isfile $file ] } {
	    # delete old preview 
	    catch { image delete [ file join $Option(CurrentAlbum) $file ] }
	    set RealPreviewList [ lremove $RealPreviewList \
				      [ file join $Option(CurrentAlbum) \
					    $file ] ]
	    KillCachedPreview [ file join $Option(CurrentAlbum) $file ]
	} else {
	    # it is directory and we have to delete all cached previews 
	    DelOrAppendToFileList CacheList \
		"[ file join $Option(CurrentAlbum) $file ]*" 1	    
	}
	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
	if { [ file isdirectory $newfile ] } {
	    RegisterAllTrashFiles $newfile
	} else {
	    DelOrAppendToFileList TrashList $newfile 0
	}
	# append to caption file
	if { [ AppendToCaptionsFileInDir $trash $newfile \
		   $file [ file tail $newfile ] ] == 0 } {
	    return 1
	}
	AdjustFileListLength TrashList $Option(FILE_trash_size) ""
    }
    return 2
}

 
proc RegisterAllTrashFiles { dir } {
    global Option
    global TrashList

    
    set flist [ concat [ glob -type f -nocomplain [ file join $dir * ] ] \
		    [ glob -type f -nocomplain [ file join $dir .* ] ] ]
    set dlist [ concat [ glob -type d -nocomplain [ file join $dir * ] ] \
		    [ glob -type d -nocomplain [ file join $dir .* ] ] ]

    foreach d $dlist {
	set tail [ file tail $d ]
	if { [ AlbumDir $d ] } {
	    # delete internal directories!
	    catch { file delete -force -- $d }
	} elseif { $tail != "." && $tail != ".." } {
	    RegisterAllTrashFiles $d
	    DelOrAppendToFileList TrashList $d 0
	}
    }
    foreach f $flist {
	if { [ AlbumFile $f 1 ] } {
	    catch { file delete -force -- $f }
	} elseif { ! [ AlbumFile $f ] } {
	    # caption.txt, header.txt, and footer.txt will be saved, but
	    # not explicitely recorded. They will vanish together 
	    # with the directory
	    # all other files will be 
	    DelOrAppendToFileList TrashList $f 0
	}
    }
}

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
}

######################## Obsolete! ################################
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
    global CacheList TrashList

    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 objects "object"
	    set them it
	} else {
	    set are are
	    set objects "objects"
	    set them them
	}
	set response [ tk_dialog .dia "Trash Bin Question" "There $are $size $objects in the trash bin. Do you really want to irrevocably delete $them now?" \
	questhead 1 Yes No ]
	if { $response == 1 } { return }
	AdjustFileListLength TrashList 0 ""
	DelOrAppendToFileList CacheList "$trash*" 1
	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 
    global ProfileLock AlbumLock
    global CacheList TrashList
    global Finishing

    SaveCaptions
    WriteProfile
    WriteFileList TrashList
    WriteFileList CacheList
    KillProcesses $ViewerProcesses
    if { $Finishing } {
	return
    }
    set Finishing 1
    if { $TempViewerFile != "" } {
	catch { file delete -force -- $TempViewerFile }
    }
    foreach td $TempDirs { 
	catch { file delete -force -- $td }
    }
    if { $AlbumLock != "" } {
	catch { file delete -force -- $AlbumLock }
    }
    if { $ProfileLock != "" } {
	catch { file delete -force -- $ProfileLock }
    }
    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 -column $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 -column $col -padx 2 -sticky w
    grid $vwin -row $row -column [ 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 -column $col -padx 2 -sticky w
    grid $owin -row $row -column [ 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
    global CacheList TrashList

    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_InstallCheck \
	{} \
	"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 -column 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 \
	{ WriteFileList CacheList; WriteFileList TrashList; SetAlbumLock; StateSettings; ReadFileList CacheList; ReadFileList TrashList
} \
	    "If no changes are intended, one can use this option.\nIn addition, when browsing an album of another user,\nthis option will be set semi-automatically in order\nto avoid the error messages when trying to write\nthe caption files. The option will also be enabled when\nan album is locked." 
    # cannot be changed if profile or album is completely readonly
    if { $Option(STATE_ProfileReadOnly) || $Option(STATE_AlbumReadOnly) } {
	$w.vals.bSTATE_ReadOnly configure -state disabled
    }

    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 do not have it installed 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 "External viewer program:" \
	SCRIPT_viewer none { } \
	"Name of (and possibly path to) an external viewer program, e.g. ImageMagick's\n\"display\", or any other image viewer such as \"xli\", \"xzgv\", \"xv\""
    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."
    
    MakeStringEntry 18 0 "Browser:" \
	SCRIPT_browser none { } \
	"Call to Internet browser for previewing HTML pages"

    MakeStringEntry 19 0 "Browser remote call:" \
	SCRIPT_browser_remote none { } \
	"How call the Internet browser remotely (@f = file name).\nThis is tried first before starting a new browser instance.\n"


    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
    global CacheList TrashList

    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 "Allow deletion of albums" \
	FILE_dir_delete_allow { } \
	"Allow to delete albums, which can be a bit dangerous"
    MakeBoolEntry  2 1 "Warning before album removal" \
    	FILE_dir_delete_warning { } \
	"If this option is enabled, the user is always asked\nfor confirmation before an album is removed."
    MakeBoolEntry  3 0 "Use Trash Bin (no immediate deletes)" \
	FILE_use_trash_bin \
	{ AdjustFileListLength TrashList \
	      [ expr $Option(FILE_trash_size) * $Option(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  3 1 "Warning before each deletion" \
    	FILE_delete_warning { } \
	"If this option is enabled, then before each\ndeletion the user is asked for confirmation"
    MakeBoolEntry  4 0 "Warning before multiple deletions" \
    	FILE_multiple_delete_warning { } \
	"If this option is enabled, then before multiple\ndeletions the user is asked for confirmation"
    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"
    MakeBoolEntry  6 1 "Save original images before editing" \
	FILE_save_original_when_edit { } \
	"Save a copy of the image before attempting to edit it."
    MakeBoolEntry  7 0 "Warning when adressing an internal directory" \
	FILE_aux_dir_warning { } \
	"Give a warning when attempting to move a file to or open one of the\ninternal directories used for cached previews or saved pictures"
    MakeBoolEntry  7 1 "Copy/move titles & captions always" \
	FILE_copy_captions { } \
	"Copy/move picture titles and captions regardless of whether the\ndestination is inside or outside of the current root album"
    MakeStringEntry  8 0 "Edit options for the image editor:" \
	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  9 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  10 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  11 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  12 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 13 0 "Maximal number of images in trash bin:" \
	FILE_trash_size \
	none \
	{ CheckNumber %P }  \
	"If more than this number of files are in the trash bin,\nthen the least recently files moved to the trash bin will\nirrevocably deleted"
    frame $w.button
    button $w.button.ok -text OK \
	-command  { grab release .prefbox; \
			destroy .prefbox; \
			AdjustFileListLength TrashList \
			$Option(FILE_trash_size) "" }
    
    pack $w.button.ok -side top -expand 0 
    pack $w.button -side top -expand 1 -fill x -padx 4m -pady 4m
    PositionWindowCenter $w

    tkwait window $w
    WriteProfile
}

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

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

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

    tkwait window $w
    WriteProfile
}

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

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

    label $w.top -text "Album Thumbnail & Preview Options" -justify center 
    pack $w.top -pady 5m -padx 4m -side top
    
    frame $w.vals
    pack $w.vals -side top
    
    MakeBoolEntry 0 0  "Use cropping for thumbs"  ALBUM_crop {} \
	    "If this option is enabled, all thumbnails will have the\nsame size and shape. This is accomplished by cropping\npictures that do not fit into the size restrictions\n(album option -crop)"
    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.\n(album option -sample)"
    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.\n(one half of album option -medium)"
    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.\n(the other half of album option -medium)"
    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\n(album option -medium_type)"
    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\n(one half of album option -geometry)"
    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\n(the other half of album option -geometry)"
    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\n(album option -type)"
    # "-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.\n(album option -scale)"
    
    
    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
    NoCurrentPic
    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\n(album option -image_pages)"
    MakeBoolEntry 0 1  "Only medium images"  ALBUM_just_medium {} \
	"Do not link the medium pictures to full size images\n(album option -only_medium)"

    MakeBoolEntry 1 0 "Loop in the image presentation" ALBUM_image_loop {} \
	"The last image links back to the first one, if this option is enabled\n(album option -image_loop)"
    MakeBoolEntry 1 1 "Handle animated GIFs" ALBUM_animated_gifs {} \
	"Display first frame of an animated GIF\n(album option -animated_gifs)"

    MakeBoolEntry 2 0  "Show file sizes"  ALBUM_file_sizes {} \
	"Include file sizes in picture name "
    MakeBoolEntry 2 1  "Image sizes"  ALBUM_image_sizes {} \
	"Pass image size to theme (should be enabled\nsince it is needed by some themes)\n(album option -file_sizes)"
    MakeBoolEntry 3 0  "Convert space to %20 in urls"  ALBUM_fix_urls {} \
	"Translate all spaces in URLs into %20. This is a safe
setting and I recommend to leave this option enabled.\n(album option -fix_urls)"
    MakeBoolEntry 3 1  "Process only known image types " ALBUM_known_images \
	ScanDirectory \
	"Ignore all files with extensions that imply that they are\nnot images. There is no reason to disable this option.\n(album option -known_images)"
    MakeBoolEntry 4 0  "Do not hide dirs with \".\""  ALBUM_all \
	ScanDirectory \
	"If one also wants to see all directories starting with a dot, then\nthis option should be enabled. Usually, there is no reason to do so.\n(album option -all)"
    MakeBoolEntry 4 1 "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.\n(album option -clean)"
    MakeStringEntry 5 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\n(album option -captions)"
    MakeStringEntry 6 0 "Number of image columns" \
	ALBUM_columns key { CheckNumber %P } \
	"Specifies the number of thumbnails per row\n(album option -columns)"
    MakeStringEntry 7 0 "Name of index file to use in HTML tags" \
	ALBUM_index_filename 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.html\" here.\n(album option -index)"
    MakeStringEntry 8 0 "Body tag for default theme" \
	ALBUM_body none { } \
	"Specifies <body> tag for default theme, e.g., <body color=white>\n(album option -body)"
    MakeStringEntry 9 0 "Character set"  ALBUM_charset none {} \
	"The character set for non-theme specific strings\n(album option -charset)"
    MakeStringEntry 10 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\n(album option -dir)"
    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\n(album option -theme)"
    grid $w.vals.button -column 0 -row 11 -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).\n(album option -theme)"
    grid $w.vals.tentry -column 1 -row 11 -sticky w
    MakeStringEntry 12 0 "Theme URL" \
	ALBUM_theme_url none { } \
	"You can specify an absolute URL for the theme files here,\nwhich will be inserted in all generated HTML files.\n(album option -theme_url)"
    MakeStringEntry 13 0 "Free option string" \
	CUSTOM_ALBUM none { } \
	"Here you can pass any string you want to album. Of course,\nthis string should be a valid option string."


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

    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 { NoCurrentPic; 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  3 0 "Show hidden directories" \
	VIEW_ShowHiddenDirs  ScanDirectory  \
	"If this option is enabled, also hidden albums 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."
    MakeBoolEntry  6 1 "Ignore file names starting with '-'" \
	VIEW_IgnoreDashFilenames ScanDirectory \
	"Ignore file names that start with a dash, because\nthey cannot be handled correctly anyway" 
    MakeBoolEntry  7 0 "Cache previews externally" \
	VIEW_CachePreviews \
	{ AdjustFileListLength CacheList \
	      [ expr $Option(VIEW_CacheSize) * $Option(VIEW_CachePreviews) ] \
	      "" } \
	"Cache all previews in order to save time. The additional amount\nof disk space is not dramatic (approx. 200-500KB per picture).\nIf this option is disabled, even only temporarily, all external\npreviews are deleted."
    MakeStringEntry  8 0 "External cache size (number of files)" \
	VIEW_CacheSize \
	none \
	{ CheckNumber %P }  \
	"Maximal number of cached previews on hard disk (least recent will be deleted)"
    MakeStringEntry  9 0 "Internal cache size (number of images)" \
	VIEW_InternalCacheSize \
	none \
	{ CheckNumber %P }  \
	"Maximal number of cached previews in main memory (0 means unlimited)"
    MakeOptionEntry  10 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 11 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  12 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  14 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; \
		    	AdjustFileListLength CacheList \
			$Option(VIEW_CacheSize) \
			"*.$Option(VIEW_prevsize).$Option(VIEW_preview_type)" }
    
    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 picture date"  EXIF_Pic_Date
    MakeInfoEntryOption 2 0 "Show camera make"  EXIF_Camera_make
    MakeInfoEntryOption 2 1 "Show camera model"  EXIF_Camera_model
    MakeInfoEntryOption 3 0 "Show resolution"  EXIF_Resolution
    MakeInfoEntryOption 3 1 "Show color/b&w"  EXIF_Color
    MakeInfoEntryOption 4 0 "Show flash"  EXIF_Flash
    MakeInfoEntryOption 4 1 "Show focal length"  EXIF_Focal
    MakeInfoEntryOption 5 0 "Show CCD width"  EXIF_CCD
    MakeInfoEntryOption 5 1 "Show exposure time"  EXIF_ExposureTime
    MakeInfoEntryOption 6 0 "Show exposure"  EXIF_ExposureProg
    MakeInfoEntryOption 6 1 "Show aperture"  EXIF_Aperture
    MakeInfoEntryOption 7 0 "Show distance"  EXIF_Dist
    MakeInfoEntryOption 7 1 "Show ISO equiv."  EXIF_ISO
    MakeInfoEntryOption 8 0 "Show whitebalance"  EXIF_Whiteb
    MakeInfoEntryOption 8 1 "Show metering"  EXIF_Meter
    MakeInfoEntryOption 9 0 "Show Jpeg process"  EXIF_JProcess
    MakeInfoEntryOption 9 1 "Show orientation"  EXIF_Orientation
    MakeInfoEntryOption 10 0 "Show Jpeg comment" EXIF_JComment
    MakeInfoEntryOption 10 1 "Show Jpeg quality"  EXIF_JQual
    MakeBoolEntry 11 0 "Correct quality code error" \
	JPEG_JQualError { } \
	"Account for the quality code error in cameras such\nas Ricoh Caplio RR120: Simply add 1 to code."
    MakeStringEntry 12 0 "Quality code error camera model:" \
	JPEG_JQualErrorCamera none { } \
	"Camera model that has the quality code error. For\npictures of this camera, the quality codes will be corrected."

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

    set files [ SelectedFiles ]
    SaveCaptions
    ClearDisplay

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

    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 CaptionsEdited

    SaveCaptions

    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
    NoCurrentPic
    SaveCaptions
    ScanDirectory
    .listframe.list selection set $cursel
    .listframe.list see $cursel
    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 -column 0 -sticky nw 
    label $w.edit.flab -text "Footer:"
    grid $w.edit.flab -row 1 -column 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 -column 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 -column 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 -nocomplain -type d * ] [ glob -nocomplain -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 } {
			    if { $Option(Alert) } {
				error "Internal Error"
			    }
			    return
			}
		    }
		}
	    }
	}
    }


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

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

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

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

    # now append -no_save_conf because we do not want to generate album 
    # specific settings
    append albumcall " -no_save_conf"

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

    # finally the custom options:
    append albumcall " $Option(CUSTOM_ALBUM)"

    set gencall $albumcall

    # now the one time options:
    if { $force } {
	append gencall " -force"
    }

    if { $Option(ALBUM_cleanup) || $clean  } {
	append gencall " -clean "
    }

    if { $local && ($Option(CurrentAlbum) ne $Option(RootAlbum)) } {
	append gencall " -add"
    }
    
    append gencall " . 2>@stdout" 

    puts $gencall

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

    # 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 with 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
    global env

    # Check for .album.conf
    
    if { ! [ file exists [ file join $env(HOME) ".album.conf" ] ] } {
	tk_dialog .dia "Execution Problem" \
	    "Please call \"album\" once manually in order to initialize certain settings!" \
	    error 0 Ok
	return 0
    }
	

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

    set running 1
    set seenitems 0 
    while { $running } {
	set newline ""
	update 
	after 100
	if { $Abort } {
	    KillProcesses $pids
	    AbortProcess
	}
	if { [ eof $proc ] } { set running 0 }
	while { ! [ eof $proc ] } {
	    set newline [ gets $proc ]
	    if { ! [ string match "* Remove unused *" $newline ] } {
		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 "Images: *" $newline ] } {
		    incr seenitems
		    set ShowProgress [ expr $ShowProgress + $ProgressStep ]
		    set dirstart [ string last "/" $newline ]
		    if { $dirstart < 0 } {
			set ProgressDir [ string range $newline 8 end ]
		    } else {
			set ProgressDir [ string range $newline [ expr $dirstart + 1 ] 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 (perhaps the theme directory is wrong?): \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 particular dirrectory descending down
# of course, ignore those that album won't touch
# start is start directory
# alsoimages controls of whether we want to count also all pictures
# alsohidden controls whether we want to count hidden directories as well
# recursive controls whether we do a recursive descent
proc CountAlbumsAndImages { start alsoimages alsohidden { recursive 1 } } {
    global Option

    set savecd [ pwd ]
    set nr 1
    if { [ catch { cd $start } ] } {
	catch { cd $savecd }
	return 0
    }
    set start [ pwd ]
    if { ! $alsohidden } { 
	if { [ glob -nocomplain $Option(ALBUM_hide_album) ] != "" } {
	    catch { cd $savecd }
	    return 0
	}
	if { [ glob -nocomplain $Option(ALBUM_no_album) ] != "" } {
	    catch { cd $savecd }
	    return 1
	}
    }
    if { $alsoimages } {
	set filelist [ concat [ glob -nocomplain  -type f *  ] \
			   [ glob -nocomplain  -type f  .*  ] ] 
	foreach f $filelist {
	    if { ! [ AlbumFile $f ] && ! [ IgnorableFile $f ] } {
		incr nr
	    } 
	}
    }
    set dirlist [ concat [ glob -nocomplain  -type d *  ] \
		      [ glob -nocomplain  -type d  .*  ] ] 
    foreach d $dirlist {
	cd $start
	if { (! [ AlbumDir $d ] || \
		  ! $Option(VIEW_IgnoreAlbumFiles) && ! $recursive)  && \
		 $d != "." && $d != ".." && \
		 ( ! [ string match ".*" $d ] || $Option(ALBUM_all) ) } {
	    if { $recursive } {
		set nr [ expr $nr + [ CountAlbumsAndImages $d $alsoimages $alsohidden ] ] 
	    } else {
		if { ! [ file exists \
			     [ file join \
				   $d $Option(ALBUM_hide_album) ] ] || \
		     $alsohidden } {
		    incr nr
		}
	    }
	}
    }
    catch { cd $savecd }
    return $nr
}


proc AbortProcess { } {
    global Option
    global Abort

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


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

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

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

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

    if { $showdir } {
	label $w.info.dirlab -text Album: 
	entry $w.info.dirent -state disabled -width $DirWidth \
	    -textvariable ProgressDir -relief flat \
	    -background $DefaultBackground 
	grid $w.info.dirlab -column 0 -row 0 -sticky w
	grid $w.info.dirent -column 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 -column 0 -row 1 -sticky w
	grid $w.info.fileent -column 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"
DoSortEntries $type
}



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

    SaveCaptions
    ClearDisplay
    .listframe.list selection clear 0 end

    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 != "CmpPictureDates" } {
	# dirs do not have picture dates, so leave them alone!
	# the rest (PictureNames, 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 

    SaveCaptions
    ClearDisplay
    .listframe.list selection clear 0 end

    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
}


proc UploadAlbum { } {
    # copy all files essential for a website photo album to a 
    # particular directory, well, most probably the directory 
    # from which you upload to your web site.
    # The following files are considered as essential:
    # 1. If the "only medium images" option is not enabled, then
    #    all picture files (if not marked by .not_img)
    #    are considered as essential
    # 2. all *.html[l] and .ht* files are considered as essential
    # 3. The album thumbnail directory is always essential
    # 4. The remaining directories are copied provided
    #    - they are not internal directories such as .preview-cache or .trash
    #    - they do not start with a dot when such directories are ignored
    #    - they do not contain a .hide_album or .no_album file
    # Note that the theme directory is not copied!

    global Option
    global Abort ShowProgress ProgressStep
    global ProgressFile

    if { [ PwdFails ] } { return }

    SaveCaptions

    set savecd  [ pwd ]

    if { $Option(STATE_UploadTarget) == "" } {
	set $Option(STATE_UploadTarget)  "~"
    } 

    set NewUploadTarget [ tk_chooseDirectory \
			      -initialdir $Option(STATE_UploadTarget)\
			      -title "Set Upload Target" \
			      -mustexist 1 ]
    if { $NewUploadTarget == "" } {
	return
    }

    set OldUploadTarget $Option(STATE_UploadTarget)
    set Option(STATE_UploadTarget) [ NormalizeFileName \
					  $OldUploadTarget \
					  $NewUploadTarget ]

    set tmptest [ file join $Option(STATE_UploadTarget) [ MakeTempName "" ] ]
    # check whether we are allowed to write in this directory
    if { [ catch { set fid [ open $tmptest "WRONLY CREAT EXCL" ] } errmes ] } {
	tk_dialog .dia "Upload Error" "Cannot write in directory $Option(STATE_UploadTarget) (Error message is: \"$errmes\")." \
	    error 0 OK 
	return
    }
    close $fid
    catch { file delete -force $tmptest }
    
    # count directors
    set dirs [ CountAlbumsAndImages $Option(RootAlbum) 0 0 1 ]

    set Abort 0
    set ShowProgress 0.0
    set ProgressFile ""
    ShowProgressWindow "Uploading entire album ..." 0 1

    update

    set ProgressStep [ expr 1000.0 / (1.0 * ($dirs+1)) ]
    set ShowProgress [ expr $ShowProgress + $ProgressStep ]	
    set start $Option(RootAlbum)
    cd $start

    if { [ catch { UploadRecursively \
		       $start  \
		       $Option(STATE_UploadTarget) } errmes ] } {
	tk_dialog  .dia "Upload Error" "Error during upload to $Option(STATE_UploadTarget) (Error message is: \"$errmes\")." \
	    error 0 OK 
	AbortProcess
    }
    cd $savecd

    if { ! $Abort } {
	set ProgressFile ""
	set ProgressDir ""
	.progbox.button.abort configure -text "OK"
	.progbox.top configure -text "Upload complete"
    }
    update
    ScanDirectory
}

    
proc UploadRecursively { source target } { 
    
    global Option
    global Abort ShowProgress ProgressStep
    global ProgressFile

    set ProgressFile $source
    cd $source
    set newdir [ file tail $source ]
    set targetdir [ file join $target $newdir ]
    file mkdir $targetdir

    set filelist [ concat [ glob -nocomplain -type f * ] \
		       [ glob -nocomplain -type f .* ] ]
    set dirlist  [ concat [ glob -nocomplain -type d * ] \
		       [ glob -nocomplain -type d .* ] ]

    foreach f $filelist {
	if { [ string match ".ht*" $f ] || \
		 [ string match -nocase "*.html" $f ] || \
		 [ string match -nocase "*.htm" $f ] || \
		 ( ! $Option(ALBUM_just_medium) && 
		   ( [ IsImageFile $f ] || ! $Option(ALBUM_known_images) )) } {
	    update
	    if { $Abort } {
		error "User Abort"
	    }
	    file copy -force $f $targetdir
	}
    }
    if { [ file exists $Option(ALBUM_dir) ] } {
	update
	if { $Abort } {
	    error "User Abort"
	}
	file delete -force [ file join $targetdir $Option(ALBUM_dir) ]
	file copy -force $Option(ALBUM_dir) $targetdir
    }

    set ShowProgress [ expr $ShowProgress + $ProgressStep ]	
    update
    if { $Abort } {
	error "User Abort"
    }
    foreach d $dirlist {
	if { $Option(FILE_save_dir) != $d && \
		 $Option(FILE_trash_dir) != $d && \
		 $Option(VIEW_CacheDir) != $d && \
		 $Option(ALBUM_dir) != $d && \
		 ! [ file exists [ file join \
				       $d $Option(ALBUM_hide_album) ] ] && \
		 ! [ file exists [ file join \
				       $d $Option(ALBUM_no_album) ] ] && \
		 ( ! [ string match ".*" $d ] || $Option(ALBUM_all) ) } {
	    update
	    if { $Abort } {
		error "User Abort"
	    }
	    UploadRecursively [ file join $source $d ] $targetdir
	}
    }
}   

# -------------------------- 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 [ lremovelist [ glob -nocomplain * ] \
		[ list $Option(ALBUM_captions) \
		"*.html" "*.htm" "*~" "*.txt" \
		$Option(ALBUM_index_filename) \
		$Option(ALBUM_dir) \
		$Option(ALBUM_header) \
		$Option(ALBUM_footer) \
		$Option(FILE_save_dir) \
		$Option(ALBUM_no_album) \
		$Option(ALBUM_hide_album) \
		"*.htaccess" "CVS" "SCCS" "RCS" ".xvpics" ] ]
	if { $files == "" } {
	    after idle {.dia.msg configure -wraplength 5i }
	    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 [ lremovelist [ glob -nocomplain * ] \
		[ list $Option(ALBUM_captions) \
		"*.html" "*.htm" "*~" "*.txt" \
		$Option(ALBUM_index_filename) \
		$Option(ALBUM_dir) \
		$Option(ALBUM_header) \
		$Option(ALBUM_footer) \
		$Option(FILE_save_dir) \
		$Option(ALBUM_no_album) \
		$Option(ALBUM_hide_album) \
		"*.htaccess" "CVS" "SCCS" "RCS" ".xvpics" ] ]
	if { $files != "" } {
	    after idle {.dia.msg configure -wraplength 4i }
	    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 } {
		catch { cd $Option(CurrentAlbum) }
		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
	AbortProcess
	catch { cd $Option(CurrentAlbum) }
	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 ] } {
		after idle {.dia.msg configure -wraplength 5i }
		
		set res [ tk_dialog .dia "Camera Download Error" "Error while post-processing $f after successful download. You probably should either disable post-processing or use a different post-processing command. This can be done in the \"Camera download\" dialog in the \"Option\" menu.\n\nThe error message was: \"$errmes.\" Do you want to continue the download?" error 0 "Yes, continue" "Continue w/o post-processing" "Abort" ] 
		if { $res == 2 } { 
		    AbortProcess
		    return
		}
		if { $res == 1 } {
		    set Option(CAMERA_postprocess) 0
		}
	    }
	}
	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
	    catch { cd $Option(CurrentAlbum) }
	}
    }
    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 ] == "" } {
	    ClearDisplay
	} 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: Start browser on current album html page
proc StartBrowser { } {
    global Option
    
    set albumfile [ AlbumIndexName ]

    if { [ llength [ split $albumfile ] ] > 1 } {
	tk_dialog .dia "File Name Problem" "Sorry, cannot handle file names containing blanks such as \"$albumfile\"" warning 0 OK 
	return
    }

    if { ! [ file exist $albumfile ] } { 
	tk_dialog .dia "File Error" "The file \"$albumfile\", which you want to preview in the browser, does not exist." warning 0 OK
	return
    }

    set callstring [ GenerateRemoteBrowserCall $albumfile ]

    if { $callstring != "" } {
	if { ! [ catch { eval exec $callstring } ] } {
	    # done!
	    return
	}
    }
    if { [ catch { eval exec $Option(SCRIPT_browser) $albumfile  & } errmes ] } {
	tk_dialog .dia "Browser Call Error" "Tried to start browser and got the error: \"$errmes\"" error 0 OK
    }
}

proc AlbumIndexName { } {
    global Option

    if { $Option(ALBUM_index_filename) == "" } {
	set indexfile "index.html"
    } else {
	set indexfile "$Option(ALBUM_index_filename)"
    }
    return [ file join $Option(CurrentAlbum) $indexfile ] 
}

proc GenerateRemoteBrowserCall { file } {
    global Option

    if { $Option(SCRIPT_browser_remote) == "" } {
	return ""
    } else {
	return [ string map [ list "@f" $file ]  $Option(SCRIPT_browser_remote) ]
    }
}

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

    DisplayOrGotoAlbum 1
}

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

    SaveCaptions
    # check for selection
    set cursel [ .listframe.list curselection ]
    if { [ llength $cursel ] == 0 } {
	set flist $NameList
    } elseif { $cursel == "0" && $OnlyView } {
	set flist $NameList
    } else {
	set flist ""
	foreach el $cursel {
	    lappend flist [ lindex $NameList $el ]
	}
    }
    set filelist ""
    set dirlist ""
    foreach file $flist {
	if { [ file isfile $file ] } {
	    lappend filelist [ list $file ] 
	} else {
	    lappend dirlist $file 
	}
    }
    if { $filelist != "" } {
	catch { lappend ViewerProcesses \
		    [ eval exec $Option(SCRIPT_viewer) \
			  $filelist & ] }
	after 100
	update
    } elseif { [ llength $dirlist ] == 1 } {
	if { ! $OnlyView } {
	    if { $file == ".." } {
		set pref [ file tail $Option(CurrentAlbum) ]
	    } else {
		set pref ""
	    }
	    GotoAlbum [ file join $Option(CurrentAlbum) $file ] $pref
	}
    } else {
	tk_dialog .dia "View error" "There is nothing to show" error 0 OK
    }
}


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

    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

#	    KillCachedPreview $img 
#           -- not necessary since it will be deleted anyway!
	}
    }
    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 }
    }
    NoCurrentPic
    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
    global RealPreviewList

    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
    }

    ClearDisplay

    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 ] }
	    set RealPreviewList [ lremove $RealPreviewList \
				      [ file join $Option(CurrentAlbum) \
					    $file ] ]
	    KillCachedPreview [ 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 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
	    NoCurrentPic
	    LoadPicFile
	}
    }
    NoCurrentPic
    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 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
	    }
	}
    }
    NoCurrentPic
    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

    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
	    }
	}
    }
    NoCurrentPic
    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 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
	}
    }
    NoCurrentPic
    LoadPicFile
}

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

proc DoRemoveEXIFAndJPEG { } {

    global NameList 
    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
	}
    }
    NoCurrentPic
    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), January 2003
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
    global tcl_version

    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 { $tcl_version >= 8.4 } {
	.info.$valname config -disabledforeground black
    }
    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
#  profile readonly / profile 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 pro $Option(STATE_ProfileReadOnly)
    set aro $Option(STATE_AlbumReadOnly)
    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 4 -state disabled
	bind . <Alt-n> ""
    } else {
	.mbar.file.menu entryconf 4 -state normal
	bind . <Alt-n> { NewAlbum }
    }

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

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


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

    # Option Menu
    if { $pro } {
	.mbar.pref.menu entryconf 2 -state disabled
    } else {
	.mbar.pref.menu entryconf 2 -state normal
    }

    if { $pro || $aro } {
	.mbar.pref.menu entryconf 3 -state disabled
    } else {
	.mbar.pref.menu entryconf 3 -state normal
    }

    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> ""
	bind . <Alt-a> ""
    } 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 }
	bind . <Alt-a> { UploadAlbum }
    }

    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 . <Alt-b> ""
	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 . <Alt-b> { StartBrowser }
	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 "Up one level" \
    -command OpenAlbum -underline 0 -accelerator "Meta-U" 
RegisterMenuHelp .mbar.file.menu 3 \
    "Go one level up in the directory hierarchy"
.mbar.file.menu add command -label "New album" -command NewAlbum \
	-accelerator "Meta-N" -underline 0
RegisterMenuHelp .mbar.file.menu 4 \
    "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 6 \
    "Edit an image"
.mbar.file.menu add command -label "Restore image"  \
    -command RestoreImage 
RegisterMenuHelp .mbar.file.menu 7 \
    "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 9 \
    "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 10 \
    "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 11 \
    "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 12 \
    "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 13 \
    "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 15 \
    "Delete a set of image files"    
.mbar.file.menu add command -label "Undelete" -command UndeleteFiles
RegisterMenuHelp .mbar.file.menu 16 \
    "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 17 \
    "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 19 \
    "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\nroot album) from the list of known profiles"
.mbar.pref.menu add command -label "Load profile" \
    -command LoadNewProfile
RegisterMenuHelp .mbar.pref.menu 2 \
    "Load an existing profile (perhaps with a different\nroot album) and add it to the list of known profiles"
.mbar.pref.menu add command -label "Create new profile" \
    -command SaveProfileAs
RegisterMenuHelp .mbar.pref.menu 3 \
    "Create a new profile using the current settings"
.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 generation options" \
    -command  AlbumAAOptions 
RegisterMenuHelp .mbar.pref.menu 8 \
    "Settings for running the \"album\" script"
.mbar.pref.menu add command -label "Album 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 will 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 14 \
    "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 15 \
    "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." 
.mbar.album.menu add separator
.mbar.album.menu add command -label "Album upload" \
    -command UploadAlbum -underline 0 -accelerator "Meta-A"
RegisterMenuHelp .mbar.album.menu 18 \
    "Upload all essential files to another directory (but always\nignore album internal files and ignore the original pictures\nif only medium sized pictures are displayed)"

# 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 checkbutton -label "Show thumbnail cropping" \
    -variable Option(VIEW_cropping) \
    -command { NoCurrentPic; LoadPicFile; WriteProfile }
RegisterMenuHelp .mbar.view.menu  4 \
    "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 5 \
    "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 7 \
    "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 8 \
    "This option must be enabled if one wants to see the names\ngiven to album in the directory listing"
.mbar.view.menu add separator
.mbar.view.menu add command -label "EXternal viewer" \
	-command { ViewImage } -accelerator "Meta-X" \
	-underline 1
 RegisterMenuHelp .mbar.view.menu 10 \
    "Load selected pictures into external viewer and display;\nif no picture is selected the entire directory will be\nloaded and can then be presented in a slide show."
.mbar.view.menu add command -label "Start browser" \
    -command StartBrowser \
    -accelerator "Meta-B" \
    -underline 6
 RegisterMenuHelp .mbar.view.menu 11 \
    "Start Internet browser on current album"


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 "IJG license" \
    -command { MenuHelpLicense $LibjpegLicenseText "IJG (jpegtran, rdjpgcom, wrjpgcom) License"}
RegisterMenuHelp .mbar.help.menu.componentlicenses 2 \
    "Displays the license text for IJG's JPEG programs"
.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.:  "

set DefaultBackground [  lindex [ .messageframe.dir.current.label \
				      config -background ] end ]

entry .messageframe.dir.current.val -state disabled -width $DirWidth \
    -relief flat -textvariable CurrentAlbumText \
    -background $DefaultBackground 
if { $tcl_version >= 8.4 } {
    .messageframe.dir.current.val config -disabledforeground black
}
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 
if { $tcl_version >= 8.4 } {
      .messageframe.dir.root.val config -disabledforeground black
}

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_make "Camera make:" "Camera make  : " "" \
    "Camera make that was used to take the picture"
MakeInfoEntry  5 EXIF_Camera_model "Camera model:" "Camera model : " "" \
    "Camera model that was used to take the picture"
MakeInfoEntry  6 EXIF_Pic_Date   "Picture date:"  "Date/Time    : " "" \
    "Date and time when the picture was taken"
MakeInfoEntry  7 EXIF_Resolution "Resolution:"  "Resolution   : " "" \
    "Width and height of the picture measured in pixels"
MakeInfoEntry  8 EXIF_Color      "Color/B&W:"   "Color/bw     : " "" \
    "Information whether the picture is in color or black & white"
MakeInfoEntry  9 EXIF_Flash      "Flash:"  "Flash used   : " "" \
    "Information about the use of the flash when the picture was taken"
MakeInfoEntry 10 EXIF_Focal      "Focal length:"  "Focal length : " "" \
    "Information about the focal length"
MakeInfoEntry 11 EXIF_CCD        "CCD width:"  "CCD width    : " "" \
    "Information about the CCD"
MakeInfoEntry 12 EXIF_ExposureTime "Exposure time:"  "Exposure time: " ""  \
    "The exposure time that was used when taking the picture"
MakeInfoEntry 13 EXIF_ExposureProg "Exposure:"   "Exposure     : " "" \
    "The method to compute the exposure that was used when taking the picture"
MakeInfoEntry 14 EXIF_Aperture   "Aperture:"   "Aperture     : " "" \
    "The aperture used when taking the picture"
MakeInfoEntry 15 EXIF_Dist       "Distance:"  "Focus dist.  : " "" \
    "Distance measured by autofocus system in camera when taking the picture"
MakeInfoEntry 16 EXIF_ISO        "ISO equiv.:"  "ISO equiv.   : " "" \
    "The ISO equivalent sensitivity used when taking the picture"
MakeInfoEntry 17 EXIF_Whiteb     "Whitebalance:"  "Whitebalance : " "" \
    "The whitebalance adjustment used when taking the picture"
MakeInfoEntry 18 EXIF_Meter      "Metering:"   "Metering Mode: " "" \
    "The metering method that had been used to determine brightness"
MakeInfoEntry 19 EXIF_JQual      "Jpeg quality:"  "Jpeg Quality : " "" \
    "JPEG compression quality used to store the image (basic, normal, fine)"
MakeInfoEntry 20 EXIF_JProcess   "Jpeg process:"  "Jpeg process : " "" \
    "JPEG process to generate the image (baseline, progressive,...)"
MakeInfoEntry 21 EXIF_Orientation "Orientation:"  "Orientation  : " "" \
    "The image orientation (might need rotation, flipping etc.)"
MakeInfoEntry 22 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 -column 0  -sticky w
grid .textframe.nameent -row 0 -column 1  -sticky we
grid .textframe.altlab -row 2 -column 0  -sticky w
grid .textframe.altent -row 2 -column 1  -sticky we
grid .textframe.caplab -row 1 -column 0  -sticky wn 
grid .textframe.cap -row 1 -column 1  -sticky we
grid columnconfigure  .textframe 1 -weight 1

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

bind .listframe.list <<ListboxSelect>> { LoadPicFile }
bind .listframe.list <Double-1> { DisplayOrGotoAlbum 0 }
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
    }
}

## Does not work any longer:
#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
SetProfileLock
SetAlbumLock
ShortenCurrentAlbumName
ReadFileList CacheList
ReadFileList TrashList
ScanDirectory

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



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

	 }

###########################################################################
############################# IJG Page ################################
###########################################################################
set LibjpegLicenseText {
                    Independent JPEG Group License



    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 License

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 {
                     Image Magick License

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 mktkalbum ##############
###########################################################################
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, deleting, 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 { util-
         ity);

       * 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 pic-
         ture 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 {directo-} underline
$w.text insert end {
       }
$w.text  insert end {ries} underline
$w.text insert end {,  which  contain  the pictures and sub-albums, together with addi-
       tional descriptive 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 regu-
       lar 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 pic-
       tures contained in the current album. Directories 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 confuse the browser. The  rea-
             son 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  information.
             With  symlinks this will easily go astray and the HTML pages will
             look very funny. For this reason, }
$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  pic-
       ture  is  displayed, e.g., when the picture was taken, which camera was
       used etc. This information 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 configured 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  generated.  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 digi-
       cams 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 thumbnails on the HTML pages. The kind of cropping
       can be changed by selection the appropriate  action  in  the  }
$w.text  insert end {Thumbnail} 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-Double-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 substi-
       tuted by blanks). Note  that  also  directories  can  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 - depend-
       ing 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-} bold
$w.text insert end {
       }
$w.text  insert end {Tab} bold
$w.text insert end {.  The entered text will be saved after a selection command (i.e., a
       mouse  selection in the directory 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 speci-
       fied. 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 han-
       dle 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 camera. If specified in the }
$w.text  insert end {Cam-} underline
$w.text insert end {
       }
$w.text  insert end {era} 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 { dia-
       log  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  with-
              out  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 difference 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. Thumbnails 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 obsolete thumbnails, previews, and
              HTML files. There is also an option  that  enables   cleanup  at
              each regeneration 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,  renaming,  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 { commands 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 conversion.


       Note that image files that are copied are marked by a star.  This  fea-
       ture  is  helpful  to  keep track of the pictures that have been copied
       and/or converted for some  purpose, e.g., to be shipped off for  print-
       ing 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  addi-
       tion,  one  can also load new profiles and switch between existing pro-
       files (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 transformation opera-
       tions.  Provided  the  pictures  to  be transformed are JPEG files, the
       }
$w.text  insert end {jpegtran} underline
$w.text insert end { tool is used to transform 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, one 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 {TKAlbum} underline
$w.text insert end { and its component programs, version
       information, and this manual page. In addition, one can disable }
$w.text  insert end {dynamic} underline
$w.text insert end {
       }
$w.text  insert end {help} underline
$w.text insert end { in this menu.


}
$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-A} bold
$w.text insert end { }
$w.text  insert end {A} underline
$w.text insert end {lbum upload to a given directory

       }
$w.text  insert end {Meta-B} bold
$w.text insert end { Preview current album page with a WWW }
$w.text  insert end {b} underline
$w.text insert end {rowser

       }
$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-P} 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 {  dia-
       log 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 con-
       tains a non-empty string, TKAlbum will make sure that all files  gener-
       ated in this album will belong to this group and have their group write
       permissions set, so that everybody from the group can delete and  over-
       write 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.

       It is not a good idea to work at the same time on the same  profile  or
       root album. For this reason, TKAlbum contains a basic locking mechanism
       that prohibits that two users or one user with two instances of TKAlbum
       work on the same album and/or profile.


}
$w.text  insert end {PREVIEWING} bold
$w.text insert end { }
$w.text  insert end {WITH} bold
$w.text insert end { }
$w.text  insert end {A} bold
$w.text insert end { }
$w.text  insert end {BROWSER} bold
$w.text insert end {
       It  is  now possible to preview a album page with a WWW browser. Before
       you can do that, you have to setup the correct  command  under  }
$w.text  insert end {General} underline
$w.text insert end {
       }
$w.text  insert end {options:} underline
$w.text insert end {

       * For }
$w.text  insert end {Mozilla:} bold
$w.text insert end {
         }
$w.text  insert end {Browser:} underline
$w.text insert end { mozilla
         }
$w.text  insert end {Browser} underline
$w.text insert end { }
$w.text  insert end {remote} underline
$w.text insert end { }
$w.text  insert end {call:} underline
$w.text insert end { mozilla -remote openURL(file://@f).br

       * }
$w.text  insert end {For} bold
$w.text insert end { }
$w.text  insert end {Netscape:} bold
$w.text insert end {
         }
$w.text  insert end {Browser:} underline
$w.text insert end { netscape
         }
$w.text  insert end {Browser} underline
$w.text insert end { }
$w.text  insert end {remote} underline
$w.text insert end { }
$w.text  insert end {call:} underline
$w.text insert end { netscape -remote openURL(file://@f).br

       * }
$w.text  insert end {For} bold
$w.text insert end { }
$w.text  insert end {Konqueror:} bold
$w.text insert end {
         }
$w.text  insert end {Browser:} underline
$w.text insert end { konqueror
         }
$w.text  insert end {Browser} underline
$w.text insert end { }
$w.text  insert end {remote} underline
$w.text insert end { }
$w.text  insert end {call:} underline
$w.text insert end {

         Now  it  is possible with }
$w.text  insert end {Meta-B} bold
$w.text insert end { to start the browser and preview the
         album page corresponding to the currently selected album  -  provided
         the album page has been generated.

}
$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.

       Image files starting with a dash lead to trouble.

       When  creating  a  new  album, one has to click twice on the OK button.
       This appears to be a TK feature.

       Another TK feature seems to be that directory specifications typed into
       the }
$w.text  insert end {selection} underline
$w.text insert end { field in the directory dialog are always interpreted rel-
       ative to the "current directory" and not to the one currently  selected
       in the dialog.


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





                                January 9 2003                      TKALBUM(1)

}}
