# Module:         poImgBrowse
# Copyright:      Paul Obermeier 2000-2019 / paul@poSoft.de
# First Version:  2000 / 10 / 22
#
# Distributed under BSD license.
#
# Module for browsing and previewing images.

namespace eval poImgBrowse {
    variable ns [namespace current]

    namespace ensemble create

    namespace export Init
    namespace export OpenWin OkWin CancelWin
    namespace export LoadSettings SaveSettings
    namespace export ShowMainWin ParseCommandLine IsOpen
    namespace export GetUsageMsg SetVerbose
    namespace export GetThumbSize

    proc SetWindowPos { winName x y w h } {
        variable sPo

        set sPo($winName,x) $x
        set sPo($winName,y) $y
        set sPo($winName,w) $w
        set sPo($winName,h) $h
    }

    proc GetWindowPos { winName } {
        variable sPo

        if { [info exists sPo($winName,name)] && \
            [winfo exists $sPo($winName,name)] } {
            scan [wm geometry $sPo($winName,name)] "%dx%d+%d+%d" w h x y
        } else {
            set x $sPo($winName,x)
            set y $sPo($winName,y)
            set w $sPo($winName,w)
            set h $sPo($winName,h)
        }
        return [list $winName $x $y $w $h]
    }

    proc SetMainWindowSash { sashX sashY } {
        variable sPo

        set sPo(sashX) $sashX
        set sPo(sashY) $sashY
    }

    proc GetMainWindowSash {} {
        variable sPo

        if { [info exists sPo(paneHori)] && \
            [winfo exists $sPo(paneHori)] } {
            set sashX [$sPo(paneHori) sashpos 0]
            set sashY [$sPo(paneVert) sashpos 0]
        } else {
            set sashX $sPo(sashX)
            set sashY $sPo(sashY)
        }
        return [list $sashX $sashY]
    }

    proc SetThumbOptions { thumbSize saveThumbs useHiddenThumbDir } {
        variable sPo

        set sPo(thumbSize)      $thumbSize
        set sPo(saveThumb)      $saveThumbs
        set sPo(hiddenThumbDir) $useHiddenThumbDir
    }

    proc GetThumbOptions {} {
        variable sPo

        return [list $sPo(thumbSize) \
                     $sPo(saveThumb) \
                     $sPo(hiddenThumbDir)]
    }

    proc GetThumbSize {} {
        variable sPo

        return $sPo(thumbSize)
    }

    proc SetViewOptions { showThumb showFileInfo } {
        variable sPo

        set sPo(showThumb)    $showThumb
        set sPo(showFileInfo) $showFileInfo
    }

    proc GetViewOptions {} {
        variable sPo

        return [list $sPo(showThumb) \
                     $sPo(showFileInfo)]
    }

    proc SetFilterOptions { prefixFilter suffixFilter } {
        variable sPo

        set sPo(prefixFilter) $prefixFilter
        set sPo(suffixFilter) $suffixFilter
    }

    proc GetFilterOptions {} {
        variable sPo

        return [list $sPo(prefixFilter) \
                     $sPo(suffixFilter)]
    }

    proc SetLastUsedDir { dir } {
        variable sPo

        set sPo(lastDir) $dir
    }

    proc GetLastUsedDir {} {
        variable sPo

        return [list $sPo(lastDir)]
    }

    # A slightly modified version of the procedure found in poWin.
    proc CreateScrolledTablelist { w args } {
        variable ns

        if { [winfo exists $w.par] } {
            destroy $w.par
        }
        ttk::frame $w.par
        pack $w.par -side top -fill both -expand 1
        tablelist::tablelist $w.par.widget \
               -xscrollcommand "$w.par.xscroll set" \
               -yscrollcommand "$w.par.yscroll set" {*}$args
        ttk::scrollbar $w.par.xscroll -command "$w.par.widget xview" -orient horizontal
        ttk::scrollbar $w.par.yscroll -command [list ${ns}::yScrollImgTable $w.par.widget]
        grid $w.par.widget $w.par.yscroll -sticky news
        grid $w.par.xscroll               -sticky ew

        grid rowconfigure    $w.par 0 -weight 1
        grid columnconfigure $w.par 0 -weight 1

        autoscroll::autoscroll $w.par.xscroll
        autoscroll::autoscroll $w.par.yscroll

        return $w.par.widget
    }

    # Init module specific variables to default values.
    proc Init {} {
        variable sPo
        variable pkgCol
        variable pkgInt
        variable msgStr
        variable detailWinNo

        set sPo(tw)      ".poImgBrowse" ; # Name of toplevel window
        set sPo(appName) "poImgBrowse"  ; # Name of tool
        set sPo(cfgDir)  ""             ; # Directory containing config files

        set detailWinNo 1

        # Tcl must be greater or equal to 8.7 to have -text option with progressbar.
        set sPo(HaveTcl87AndUp) [expr [package vcompare "8.7" [info tclversion]] <= 0]
        SetStopBrowsing

        set pkgInt(infoWinList) {}
        set pkgInt(settWin) .poImgBrowse:SettWin

        set pkgInt(colNameList)  [list "Index" "Image" "Filename" "Type" \
                                       "Size (KB)" "Date" "Width" "Height"]
        set pkgInt(colWidthList) [list 5 6 20 5 8 15 7 7]
        set pkgInt(colAlignList) [list right center left left \
                                       right left right right]
        set pkgInt(colSortList) [list integer ascii dictionary ascii \
                                      real dictionary integer integer]
        set pkgInt(numColumns)  [llength $pkgInt(colNameList)]

        set pkgCol(Ind)    0
        set pkgCol(Img)    1
        set pkgCol(Name)   2
        set pkgCol(Type)   3
        set pkgCol(Size)   4
        set pkgCol(Date)   5
        set pkgCol(Width)  6
        set pkgCol(Height) 7

        set pkgInt(maxThumbSize) 100
        set pkgInt(selMode) "none"
        set pkgInt(allFiles) "All files"
        set pkgInt(allImgs)  "All images"

        array set msgStr [list \
            SaveOpts        "Save options: " \
            ViewOpts        "View options: " \
            ThumbSize       "Thumbnail size (Pixel): " \
            SaveThumb       "Generate thumbnails" \
            HiddenThumbDir  "Hidden thumbnail directory" \
            ShowThumb       "Show thumbnail" \
            GenAllThumbs    "Generate all thumbnails" \
            DelAllThumbs    "Delete all thumbnails" \
            ShowImgInfo     "Show image info" \
            ShowFileInfo    "Show file info" \
            ShowFileType    "Show file type" \
            OpenFile        "Open selected files (Ctrl+O)" \
            DelFile         "Delete selected files (Del)" \
            RenameFile      "Rename selected file (F2)" \
            FileInfo        "View file info (Ctrl+I)" \
            UpdateList      "Update file list (F5)" \
            ClearImgCache   "Clear image cache" \
            StopImgLoad     "Stop image loading (Esc)" \
            Cancel          "Cancel" \
            OK              "OK" \
            Confirm         "Confirmation" \
            WinTitle        "Image browser options" \
            ProgressWinTitle "Thumbnail generation progress" \
        ]
    }

    proc Str { key args } {
        variable msgStr

        set str $msgStr($key)
        return [eval {format $str} $args]
    }

    # Functions for handling the settings window of this module.

    proc CloseWin { w } {
        if { [winfo exists $w] } {
            SetWindowPos settWin [winfo x $w] [winfo y $w] [winfo width $w] [winfo height $w]
        }
        destroy $w
    }

    proc CancelWin { w args } {
        variable sPo

        foreach pair $args {
            set var [lindex $pair 0]
            set val [lindex $pair 1]
            set cmd [format "set %s %s" $var $val]
            eval $cmd
        }
        CloseWin $w
    }

    proc OkWin { w } {
        CloseWin $w
    }

    proc ClearImgCache { tbl } {
        variable pkgInt
        variable pkgCol

        for { set row 0 } { $row < [$tbl size] } { incr row } {
            $tbl cellconfigure $row,$pkgCol(Img) -image $pkgInt(placeholderImg)
        }
        foreach key [array names pkgInt "*,photo"] {
            if { $pkgInt($key) ne "" } {
                image delete $pkgInt($key)
            }
            unset pkgInt($key)
        }
    }

    proc OpenWin { fr } {
        variable ns
        variable sPo
        variable pkgInt

        set tw $fr

        # Generate left column with text labels.
        set row 0
        foreach labelStr [list \
                           [Str ThumbSize] \
                           [Str SaveOpts] \
                           [Str ViewOpts] ] {
            ttk::label $tw.l$row -text $labelStr
            grid $tw.l$row -row $row -column 0 -sticky new
            incr row
        }

        set varList [list]

        # Generate right column with entries and buttons.
        # Row 0: Thumbnail size option
        set row 0
        ttk::frame $tw.fr$row
        grid $tw.fr$row -row $row -column 1 -sticky new

        poWin CreateCheckedIntEntry $tw.fr$row ${ns}::sPo(thumbSize) -row $row -width 3 -min 10 -max 100

        set tmpList [list [list sPo(thumbSize)] [list $sPo(thumbSize)]]
        lappend varList $tmpList

        # Row 2: Save options
        incr row
        ttk::frame $tw.fr$row
        grid $tw.fr$row -row $row -column 1 -sticky new

        ttk::checkbutton $tw.fr$row.cb1 -text [Str SaveThumb] \
                    -variable ${ns}::sPo(saveThumb) \
                    -onvalue 1 -offvalue 0
        ttk::checkbutton $tw.fr$row.cb2 -text [Str HiddenThumbDir] \
                    -variable ${ns}::sPo(hiddenThumbDir) \
                    -onvalue 1 -offvalue 0
        pack $tw.fr$row.cb1 $tw.fr$row.cb2 -side top -anchor w -in $tw.fr$row

        set tmpList [list [list sPo(saveThumb)] [list $sPo(saveThumb)]]
        lappend varList $tmpList
        set tmpList [list [list sPo(hiddenThumbDir)] [list $sPo(hiddenThumbDir)]]
        lappend varList $tmpList

        # Row 3: View options
        incr row
        ttk::frame $tw.fr$row
        grid $tw.fr$row -row $row -column 1 -sticky new

        ttk::checkbutton $tw.fr$row.cb1 -text [Str ShowThumb] \
                    -variable ${ns}::sPo(showThumb) \
                    -onvalue 1 -offvalue 0
        poToolhelp AddBinding $tw.fr$row.cb1 "Column: Image"
        ttk::checkbutton $tw.fr$row.cb2 -text [Str ShowFileInfo] \
                    -variable ${ns}::sPo(showFileInfo) \
                    -onvalue 1 -offvalue 0
        poToolhelp AddBinding $tw.fr$row.cb2 "Columns: Type, Size, Date, Width, Height"
        pack $tw.fr$row.cb1 $tw.fr$row.cb2 -side top -anchor w -in $tw.fr$row

        set tmpList [list [list sPo(showThumb)] [list $sPo(showThumb)]]
        lappend varList $tmpList
        set tmpList [list [list sPo(showFileInfo)] [list $sPo(showFileInfo)]]
        lappend varList $tmpList

        return $varList
    }

    proc ReadThumbFile { thumbFile } {
        variable sPo

        set retVal [catch {image create photo -file $thumbFile} photoName]
        if { $retVal != 0 } {
            poLog Warning "Can't read thumbnail $thumbFile ($photoName)"
            return ""
        } else {
            if { $sPo(thumbSize) != [image width $photoName] || \
                 $sPo(thumbSize) != [image height $photoName] } {
                poLog Warning "Stored thumbnails do not fit current settings. Delete and then update thumbnails."
            }
            return $photoName
        }
    }

    proc GetFileList {dirName} {
        variable pkgInt
        variable sPo

        set fmt $sPo(suffixFilter)
        if { $sPo(suffixFilter) eq $pkgInt(allFiles) } {
            set matchList [format "%s*" $sPo(prefixFilter)]
        } else {
            if { $sPo(suffixFilter) eq $pkgInt(allImgs) } {
                set fmt ""
            }
            foreach ext [poImgType GetExtList $fmt] {
                lappend matchList [format "%s%s" $sPo(prefixFilter) $ext]
            }
        }

        set fileList [lsort -dictionary [lindex [poMisc GetDirList $dirName 0 1 1 1 * *] 1] ]

        set imgFileList [list]
        foreach f $fileList {
            foreach patt $matchList {
                if { [string match -nocase $patt $f] } {
                    lappend imgFileList $f
                    break
                }
            }
        }
        return $imgFileList
    }

    proc GetSelRows { tbl } {
        return [$tbl curselection]
    }

    proc GetSelFiles { tbl } {
        variable pkgCol

        set fileList [list]
        set rowList [GetSelRows $tbl]
        set dirName [GetSelImgDir]

        foreach row [lsort -integer $rowList] {
            set fileName [file join $dirName [$tbl cellcget $row,$pkgCol(Name) -text]]
            lappend fileList $fileName
        }
        return $fileList
    }

    proc OpenContextMenu { tbl tblBody x y selFunc } {
        variable ns
        variable sPo
        variable pkgInt

        set w .poImgBrowse:contextMenu
        catch { destroy $w }
        menu $w -tearoff false -disabledforeground white

        foreach { ::tablelist::W ::tablelist::x ::tablelist::y } \
            [::tablelist::convEventFields $tblBody $x $y] {}

        set rowList [GetSelRows $tbl]
        set len [llength $rowList]
        set numRows [$tbl size]

        if { $numRows == 0 } {
            set menuTitle "No files in directory"
            $w add command -label "$menuTitle" -state disabled -background "#303030"
        } elseif { $len == 0 } {
            set menuTitle "Nothing selected"
            $w add command -label "$menuTitle" -state disabled -background "#303030"
        } elseif { [RowsSelected $tbl] } {
            set fileList [GetSelFiles $tbl]
            if { [llength $fileList] == 1 } {
                set menuTitle "[file tail [lindex $fileList 0]] selected"
                set imgStr "image"
            } else {
                set menuTitle "[llength $fileList] files selected"
                set imgStr "images"
            }
            $w add command -label "$menuTitle" -state disabled -background "#303030"
            $w add command -label "Info" -command "${ns}::ShowImgInfo $tbl"
            $w add command -label "Slide show" -command "${ns}::SlideShowFile $tbl"
            $w add command -label "HexDump" \
                       -command "poExtProg StartHexEditProg [list $fileList]"
            $w add separator
            $w add command -label "Open" -command "${ns}::LoadImgs $selFunc $fileList"
            if { [llength $fileList] == 1 } {
                $w add separator
                $w add command -label "Rename ..." -command "${ns}::AskRenameFile $tbl"
            } elseif { [llength $fileList] == 2 } {
                $w add separator
                $w add command -label "Diff ..." -command "${ns}::DiffFiles $tbl"
            }
            $w add separator
            $w add command -label "Copy to ..." -underline 0 \
                       -command "${ns}::AskCopyOrMoveTo $tbl copy"
            if { [file isdirectory $sPo(lastDir)] } {
                $w add command -label "Copy to [file tail $sPo(lastDir)]/" -underline 0 \
                       -command "${ns}::AskCopyOrMoveTo $tbl copy $sPo(lastDir)"
            }
            $w add separator
            $w add command -label "Move to ..." -underline 0 \
                       -command "${ns}::AskCopyOrMoveTo $tbl move"
            if { [file isdirectory $sPo(lastDir)] } {
                $w add command -label "Move to [file tail $sPo(lastDir)]/" -underline 0 \
                       -command "${ns}::AskCopyOrMoveTo $tbl move $sPo(lastDir)"
            }
            $w add separator
            $w add command -label "Delete ..." -activebackground "#FC3030" \
                       -command "${ns}::AskDelFile $tbl"
        }
        tk_popup $w [expr {$x +5}] [expr {$y +5}]
    }

    proc ShowSelection { tbl dir } {
        if { $dir eq "Home" } {
            $tbl see 0
        } elseif { $dir eq "End" } {
           $tbl see [$tbl size]
        }
        UpdateVisImgs $tbl
    }

    proc CloseSubWindows {} {
        variable pkgInt

        foreach w $pkgInt(infoWinList) {
            if { [winfo exists $w] } {
                destroy $w
            }
        }
        unset pkgInt(infoWinList)
        set pkgInt(infoWinList) {}
    }

    proc CloseAppWindow {} {
        variable sPo
        variable pkgInt

        SetStopBrowsing
        if { [info exists sPo(imgLoadId)] } {
            after cancel $sPo(imgLoadId)
        }

        if { [poApps GetAutosaveOnExit] } {
            SaveSettings
        }

        # Clear preview photo image.
        poWinPreview Clear $pkgInt(previewWidget)

        # Delete (potentially open) sub-toplevels of this application.
        CloseSubWindows

        # Delete main toplevel of this application.
        destroy $sPo(tw)

        # Show the main app window, which might be iconified.
        poApps StartApp deiconify
    }

    proc ExitApp {} {
        CloseAppWindow
        poApps ExitApp
    }

    proc SetStopBrowsing { { onOff true } } {
        variable pkgInt

        set pkgInt(stopBrowse) $onOff
    }

    proc StopBrowsing {} {
        variable pkgInt

        return $pkgInt(stopBrowse)
    }

    proc RemovePhotos { row1 row2 } {
        variable pkgCol
        variable pkgInt

        for { set row $row1 } { $row <= $row2 } { incr row } {
            $pkgInt(tableId) cellconfigure $row,$pkgCol(Img) -text ""
            $pkgInt(tableId) cellconfigure $row,$pkgCol(Img) -image $pkgInt(placeholderImg)
        }
    }

    proc DelAllThumbs { tbl } {
        variable pkgCol

        set dirName  [GetSelImgDir]
        set thumbDir [GetThumbDir $dirName]
        file delete -force $thumbDir
        WriteInfoStr "Deleted thumbnails of directory $dirName"
    }

    proc CloseProgressWin {} {
        variable pkgInt

        set w .poImgBrowse:ProgressWin
        destroy $w
    }

    proc OpenProgressWin { dirName } {
        variable ns
        variable pkgInt
        variable sPo

        set w .poImgBrowse:ProgressWin

        if { [winfo exists $w] } {
            poWin Raise $w
            return
        }

        toplevel $w
        wm title $w [Str ProgressWinTitle]
        wm resizable $w false false

        ttk::label $w.dir1 -text "Directory:"
        ttk::label $w.dir2 -text "$dirName" -width 30 -anchor w

        ttk::label $w.todo1 -text "Still to do:"
        ttk::label $w.todo2 -textvariable ${ns}::pkgInt(progressTodo) -width 30 -anchor w

        ttk::label $w.name1 -text "Current image:"
        ttk::label $w.name2 -textvariable ${ns}::pkgInt(progressName) -width 30 -anchor w

        if { $sPo(showThumb) } {
            set size $sPo(thumbSize)
        } else {
            set size 1
        }
        ttk::label $w.img -anchor center
        set pkgInt(progressLabel) $w.img

        ttk::button $w.cancel -textvariable ${ns}::pkgInt(progressBtn) \
            -command "${ns}::SetStopBrowsing ; ${ns}::CloseProgressWin" \
            -default active
        bind $w <KeyPress-Escape> ${ns}::SetStopBrowsing

        grid $w.dir1   -row 0 -column 0 -sticky ew
        grid $w.dir2   -row 0 -column 1 -sticky ew
        grid $w.todo1  -row 1 -column 0 -sticky ew
        grid $w.todo2  -row 1 -column 1 -sticky ew
        grid $w.name1  -row 2 -column 0 -sticky ew
        grid $w.name2  -row 2 -column 1 -sticky ew
        grid $w.img    -row 3 -column 0 -columnspan 2 -sticky news
        grid $w.cancel -row 4 -column 0 -columnspan 2 -sticky news -pady 2
        focus $w
    }

    proc GenAllThumbs { tbl { withSubDirs 0 } } {
        variable pkgInt
        variable sPo
        variable pkgCol

        SetStopBrowsing false
        set numRows  [$tbl size]
        set dirName  [GetSelImgDir]
        set thumbDir [GetThumbDir $dirName]
        set pkgInt(progressTodo) "$numRows"
        set pkgInt(progressName) "Initializing ..."
        set pkgInt(progressBtn)  "Cancel"
        OpenProgressWin $dirName
        if { ! [CreateThumbDir $thumbDir] } {
            poLog Warning "Unable to create thumbs directory $thumbDir. \
                          Check permissions."
            tk_messageBox -title "Warning" -type ok -icon warning -message \
               "Unable to create thumbs directory $thumbDir. Check permissions."
        }
        for { set row 0 } { $row < $numRows } { incr row } {
            set imgName   [$tbl cellcget $row,$pkgCol(Name) -text]
            set imgFile   [file join $dirName $imgName]
            set thumbFile [BuildThumbFileName $thumbDir $imgName]
            set thumbExists [expr [file exists $thumbFile]]
            set msgStr "Image: $imgName Thumb: "
            set pkgInt(progressTodo) [expr {$numRows - $row}]
            set pkgInt(progressName) $imgName
            if { ! $thumbExists } {
                set photoName [poImgMisc LoadImgScaled $imgFile \
                               $sPo(thumbSize) $sPo(thumbSize)]
                if { $photoName ne "" } {
                    if { $sPo(showThumb) } {
                        $pkgInt(progressLabel) configure -image $photoName
                        update
                    }
                    set retVal [catch {$photoName write $thumbFile -format PNG}]
                    if { $retVal != 0 } {
                        append msgStr "NOT generated"
                    }
                    image delete $photoName
                } else {
                    append msgStr "NOT generated"
                }
            } else {
                append msgStr "Already exists"
            }

            if { [StopBrowsing] } {
                CloseProgressWin
                return
            }
        }
        set pkgInt(progressTodo) 0
        set pkgInt(progressName) "Finished"
        set pkgInt(progressBtn)  "OK"
        WriteInfoStr "Updated thumbnails of directory $dirName"
        update
        return
    }

    proc LoadThumbnails { tbl } {
        variable ns
        variable sPo
        variable pkgInt
        variable pkgCol

        if { [StopBrowsing] } {
            return
        }
        foreach { row1 row2 } [GetVisibleRows $tbl] { break }
        set dirName  [GetSelImgDir]
        set thumbDir [GetThumbDir $dirName]
        if { $sPo(saveThumb) } {
            CreateThumbDir $thumbDir
        }
        set imgReplaced false
        set row $row1
        set count 0
        while { 1 } {
            if { $row >= [$tbl size] } {
                WriteInfoStr ""
                UpdateProgress "scan" 0
                return
            }
            set imgName [$tbl cellcget $row,$pkgCol(Name) -text]
            set imgFile [file join $dirName $imgName]
            if { ! [info exists pkgInt($imgFile,photo)] } {
                set thumbFile [BuildThumbFileName $thumbDir $imgName]
                set thumbExists [expr [file exists $thumbFile]]

                if { $thumbExists } {
                    set imgInfo [ReadThumbFile $thumbFile]
                } else {
                    set imgInfo [poImgMisc LoadImgScaled $imgFile \
                                               $sPo(thumbSize) $sPo(thumbSize)]
                }

                if { $sPo(saveThumb) } {
                    if { ! $thumbExists } {
                        set retVal [catch {$imgInfo write $thumbFile -format PNG}]
                        if { $retVal != 0 } {
                            poLog Warning "Cannot write thumb $thumbFile"
                        }
                    }
                }

                set imgReplaced true
                set pkgInt($imgFile,photo) $imgInfo
            }

            if { [$tbl cellcget $row,$pkgCol(Img) -image] eq $pkgInt(placeholderImg) } {
                set photoName $pkgInt($imgFile,photo)
                if { $photoName ne "" } {
                    $tbl cellconfigure $row,$pkgCol(Img) -image $photoName
                } else {
                    $tbl cellconfigure $row,$pkgCol(Img) -text $photoName
                }
            }
            set row [expr { ($row + 1) % [$tbl size] }]
            if { $imgReplaced } {
                UpdateProgress "scan" $count
                break
            }
            incr count
            if { $count == [$tbl size] } {
                WriteInfoStr ""
                UpdateProgress "scan" 0
                return
            }
        }
        set sPo(imgLoadId) [after 1 ${ns}::LoadThumbnails $tbl]
    }

    proc UpdateVisImgs { tbl { force false } } {
        variable pkgInt

        if { [$tbl size] == 0 } {
            return
        }

        if { $force } {
            RemovePhotos 0 [expr {[$tbl size] -1}]
        }
        WriteInfoStr "Loading thumbnail images ..."
        InitProgress "scan" [$tbl size]
        LoadThumbnails $tbl
    }

    proc yScrollImgTable { tbl args } {
        $tbl yview {*}$args
    }

    proc ShowImgInfo { tbl } {
        variable ns
        variable sPo
        variable pkgCol
        variable pkgInt

        if { ! [RowsSelected $tbl] } {
            WriteInfoStr "No files selected for info"
            return
        }
        set rowList [GetSelRows $tbl]
        set dirName [GetSelImgDir]
        set numImgs [llength $rowList]

        if { $numImgs == 0 } {
            WriteInfoStr "No files selected for info"
            return
        }

        $sPo(tw) configure -cursor watch
        update

        foreach row $rowList {
            set imgName  [$tbl cellcget $row,$pkgCol(Name) -text]
            set fileName [file join $dirName $imgName]

            # Copy the selected thumbnail image for display in the info window.
            # Thumbnail images are available only for table entries visible.
            set phImg ""
            if { [$tbl cellcget $row,$pkgCol(Img) -image] ne $pkgInt(placeholderImg) } {
                set phImg [image create photo]
                $phImg copy [$tbl cellcget $row,$pkgCol(Img) -image]
            }
            set infoWin [poWin CreateOneFileInfoWin "$fileName" $phImg ]
            lappend pkgInt(infoWinList) $infoWin
        }
        $sPo(tw) configure -cursor top_left_arrow
    }

    proc RowsSelected { tbl } {
        return [llength [$tbl curselection]]
    }

    proc AskCopyOrMoveTo { tbl mode { dstDir "" } } {
        variable pkgCol
        variable pkgInt
        variable sPo

        if { ! [RowsSelected $tbl] } {
            WriteInfoStr "No files selected for copy or move"
            return
        }

        set rowList [GetSelRows $tbl]
        set dirName [GetSelImgDir]
        if { [llength $rowList] == 0 } {
            WriteInfoStr "No files selected for copy or move"
            return
        }

        if { $dstDir eq "" } {
            set dstDir [poTree GetDir -initialdir $sPo(lastDir) -showfiles 0 \
                                      -title "Select directory to copy files into"]
        }
        if { $dstDir ne "" && [file isdirectory $dstDir] } {
            set sPo(lastDir) $dstDir

            foreach row $rowList {
                set imgName [$tbl cellcget $row,$pkgCol(Name) -text]
                set srcName [file join $dirName $imgName]
                set dstName [file join $dstDir  $imgName]
                if { [file exists $dstName] } {
                    set retVal [tk_messageBox \
                      -title "Confirmation" \
                      -message "Overwrite existing file $dstName ?" \
                      -type yesnocancel -default yes -icon question]
                    if { $retVal eq "cancel" } {
                        return
                    } elseif { $retVal eq "no" } {
                        continue
                    }
                }
                if { $mode eq "copy" } {
                    WriteInfoStr "Copying file $srcName to $dstDir"
                    update
                    file copy -force -- $srcName $dstDir
                } else {
                    WriteInfoStr "Moving file $srcName to $dstDir"
                    update
                    file rename -force -- $srcName $dstDir
                }
            }
            if { $mode ne "copy" } {
                ShowFileList
            }
        }
    }

    proc DiffFiles { tbl } {
        variable pkgCol
        variable pkgInt

        if { ! [RowsSelected $tbl] } {
            WriteInfoStr "No files selected for diffing"
            return
        }
        set rowList [GetSelRows $tbl]
        set dirName [GetSelImgDir]

        set numSel [llength $rowList]
        if { $numSel != 2 } {
            WriteInfoStr "Diffing only possible for 2 files"
            return
        }

        set row1 [lindex $rowList 0]
        set imgName [$tbl cellcget $row1,$pkgCol(Name) -text]
        set leftFile [file join $dirName $imgName]
        set row2 [lindex $rowList 1]
        set imgName [$tbl cellcget $row2,$pkgCol(Name) -text]
        set rightFile [file join $dirName $imgName]

        poApps StartApp poImgdiff [list $leftFile $rightFile]
    }

    proc AskRenameFile { tbl } {
        variable pkgCol
        variable pkgInt

        if { ! [RowsSelected $tbl] } {
            WriteInfoStr "No file selected for renaming"
            return
        }

        set rowList [GetSelRows $tbl]
        set dirName [GetSelImgDir]

        set numSel [llength $rowList]
        if { $numSel == 0 } {
            WriteInfoStr "No file selected for renaming"
            return
        } elseif { $numSel != 1 } {
            WriteInfoStr "Renaming only possible for one file"
            return
        }

        set row [lindex $rowList 0]
        set imgName [$tbl cellcget $row,$pkgCol(Name) -text]
        set srcName [file join $dirName $imgName]

        set retName [poWin EntryBox $imgName $pkgInt(mouse,x) $pkgInt(mouse,y)]

        if { $retName eq "" } {
            WriteInfoStr "No file name specified or empty file name. No renaming."
            return
        }

        set newName [file join $dirName $retName]
        if { [file exists $newName] } {
            set retVal [tk_messageBox -icon question -type yesno -default yes \
                -message "File $newName already exists. Overwrite?" \
                -title "Confirmation"]
            if { $retVal eq "no" } {
                return
            }
        }
        set catchVal [catch { file rename -force -- $srcName $newName } errorInfo]
        if { $catchVal } {
            tk_messageBox -message [lindex [split "$errorInfo" "\n"] 0] \
                          -title "Error message" -icon error -type ok
            return
        }
        WriteInfoStr "Renamed $imgName to: $newName"
        ShowFileList
        focus $tbl
    }

    proc UpdateDirList { dirToShow } {
        variable pkgInt

        set treeId $pkgInt(treeId)
        poTree Rescan $treeId $dirToShow
    }

    proc UpdateDirListByDrop { w dirList } {
        foreach dir $dirList {
            if { [file isdirectory $dir] } {
                UpdateDirList $dir
                return
            }
        }
    }

    proc AskRenameDir { x y } {
        variable pkgInt

        set dirList [GetSelDirectories]
        set numSel [llength $dirList]
        if { $numSel == 1 } {
            set dirName [lindex $dirList 0]
        } else {
            WriteInfoStr "No directory or more than 1 selected."
            return
        }
        set retName [poWin EntryBox [file tail $dirName] $x $y]

        if { $retName eq "" } {
            WriteInfoStr "Empty directory name. Can not rename."
            return
        }

        set newName [file join [file dirname $dirName] $retName]
        if { [file isdirectory $newName] } {
            tk_messageBox -icon warning -type ok  -title "Warning" \
                -message "Directory $newName exists. No overwrite possible."
            return
        }

        set catchVal [catch { file rename $dirName $newName } errorInfo]
        if { $catchVal } {
            tk_messageBox -message [lindex [split "$errorInfo" "\n"] 0] \
                          -icon error -type ok -title "Error message"
            return
        }
        poTree DeleteNode $pkgInt(treeId) $dirName
        UpdateDirList $newName
    }

    proc AskDelDir {} {
        variable pkgInt

        set dirList [GetSelDirectories]
        set numSel [llength $dirList]
        if { $numSel == 1 } {
            set dirName [lindex $dirList 0]
        } else {
            WriteInfoStr "No directory or more than 1 selected."
            return
        }

        set msgStr "Delete directory $dirName and all its contents ?"
        set retVal [tk_messageBox \
          -title "Confirmation" -message $msgStr \
          -type yesno -default yes -icon question]
        if { $retVal eq "no" } {
            focus $pkgInt(treeId)
            return
        }

        set catchVal [catch { file delete -force -- $dirName } errorInfo]
        if { $catchVal } {
            tk_messageBox -message [lindex [split "$errorInfo" "\n"] 0] \
                          -icon error -type ok -title "Error message"
            return
        }
        poTree DeleteNode $pkgInt(treeId) $dirName
        UpdateDirList [file dirname $dirName]
    }

    proc AskNewDir { x y } {
        variable pkgInt

        set dirList [GetSelDirectories]
        set numSel [llength $dirList]
        if { $numSel == 1 } {
            set dirName [lindex $dirList 0]
        } else {
            tk_messageBox -icon warning -type ok -title "Warning" \
            -message "No directory or more than 1 selected."
            return
        }
        set retName [poWin EntryBox "New directory" $x $y]

        if { $retName eq "" } {
            WriteInfoStr "Empty directory name. Can not create."
            return
        }
        set newName [file join $dirName $retName]
        if { [file isdirectory $newName] } {
            tk_messageBox -icon warning -type ok  -title "Warning" \
                -message "Directory $newName exists. No creation possible."
            return
        }
        file mkdir "$newName"
        poTree DeleteNode $pkgInt(treeId) $dirName
        UpdateDirList $newName
    }

    proc AskCopyOrMoveDir { mode } {
        variable pkgInt
        variable sPo

        set dirList [GetSelDirectories]
        set numSel [llength $dirList]
        if { $numSel == 1 } {
            set dirName [lindex $dirList 0]
        } else {
            tk_messageBox -icon warning -type ok -title "Warning" \
            -message "No directory or more than 1 selected."
            return
        }

        set tmpDir [poTree GetDir -initialdir $sPo(lastDir) -showfiles 0 \
                                  -title "Select directory to copy to"]
        if { $tmpDir ne "" && [file isdirectory $tmpDir] } {
            set sPo(lastDir) $tmpDir
            set newName [file join $tmpDir [file tail $dirName]]
            if { [file isdirectory $newName] } {
                tk_messageBox -icon warning -type ok  -title "Warning" \
                    -message "Directory $newName already exists. Use file copy instead."
                return
            }
            if { $mode eq "copy" } {
                # OPA TODO Check if directory with that name exists
                WriteInfoStr "Copying directory $dirName to $newName"
                file copy -force -- $dirName $newName
                UpdateDirList [file join $dirName]
            } else {
                # OPA TODO Check if directory with that name exists
                WriteInfoStr "Moving directory $dirName to $newName"
                file rename -force -- $dirName $newName
                poTree DeleteNode $pkgInt(treeId) $dirName
                UpdateDirList [file dirname $dirName]
            }
        } else {
            WriteInfoStr "Empty directory name. Can not create."
            return
        }
    }

    proc AskDelFile { tbl } {
        variable pkgCol
        variable pkgInt
        variable sPo

        if { ! [RowsSelected $tbl] } {
            WriteInfoStr "No files selected for deletion"
            return
        }

        set rowList [GetSelRows $tbl]
        set dirName [GetSelImgDir]

        set numSel [llength $rowList]
        if { $numSel == 0 } {
            WriteInfoStr "No files selected for deletion"
            return
        } elseif { $numSel == 1 } {
            set msgStr "Delete file [$tbl cellcget [lindex $rowList 0],$pkgCol(Name) -text] ?"
        } else {
            set msgStr "Delete selected $numSel files ?"
        }
        set retVal [tk_messageBox \
          -title "Confirmation" -message $msgStr \
          -type yesno -default yes -icon question]
        if { $retVal eq "no" } {
            focus $tbl
            return
        }

        set delError 0
        foreach row $rowList {
            set imgName [$tbl cellcget $row,$pkgCol(Name) -text]
            set srcName [file join $dirName $imgName]
            WriteInfoStr "Deleting file $srcName"
            update
            set catchVal [catch { file delete -force -- $srcName } errorInfo]
            if { $catchVal } {
                tk_messageBox -message [lindex [split "$errorInfo" "\n"] 0] \
                              -title "Error message" -icon error -type ok
                set delError 1
                break
            }
        }
        ShowFileList

        if { $delError } {
            WriteInfoStr "Error trying to delete file $imgName"
        } elseif { $numSel == 1 } {
            WriteInfoStr "Deleted file $imgName"
        } else {
            WriteInfoStr "Deleted $numSel files"
        }
        focus $tbl
    }

    proc LoadImgs { selFunc args } {
        variable pkgInt

        SetStopBrowsing false

        foreach fileName $args {
            $selFunc $fileName
            update
            if { [StopBrowsing] } {
                break
            }
        }
    }

    proc OpenFiles { tbl selFunc } {
        if { ! [RowsSelected $tbl] } {
            WriteInfoStr "No files selected for opening"
            return
        }

        set fileList [GetSelFiles $tbl]
        if { [llength $fileList] == 0 } {
            WriteInfoStr "No files selected for opening"
            return
        }

        foreach f $fileList {
            LoadImgs $selFunc $f
        }
    }

    proc SlideShowFinished { cmdString code result op } {
        variable ns
        variable sPo
        variable pkgInt
        variable pkgCol

        #puts "SlideSHowFinished <$cmdString> <$code> <$result> <$op>"
        if { $result } {
            set markedImgs [poSlideShow::GetMarkedImgs]
            set columnIndex $pkgCol(Name)
            $pkgInt(tableId) selection clear 0 end
            foreach markedImg $markedImgs {
                set rowIndex [$pkgInt(tableId) searchcolumn $columnIndex [file tail $markedImg] -exact]
                if { $rowIndex >= 0 } {
                    $pkgInt(tableId) selection set $rowIndex
                }
            }
        }
        trace remove execution poSlideShow::CloseAppWindow leave ${ns}::SlideShowFinished
    }

    proc SlideShowFile { tbl { viewAllFiles false } } {
        variable ns
        variable pkgCol

        set dirName [GetSelImgDir]

        # If no rows are selected or we want to see all files, show all images in the slide show.
        # In the first case, set marking off for all images, otherwise use current markings.
        # If rows are selected, show these images in the slide show with marking on.

        set fileList [list]
        set markList [list]

        if { $viewAllFiles } {
            set rowList [list]
            for { set i 0 } { $i < [$tbl size] } { incr i } {
                lappend rowList $i
            }
        } else {
            set rowList [GetSelRows $tbl]
        }

        if { [llength $rowList] == 0 } {
            WriteInfoStr "No files selected for slide show"
            return
        }
        set selRowList [GetSelRows $tbl]
        foreach row $rowList {
            set fileName [file join $dirName [$tbl cellcget $row,$pkgCol(Name) -text]]
            lappend fileList $fileName
            if { [lsearch -exact -integer $selRowList $row] >= 0 } {
                lappend markList $fileName
            }
        }
        trace add execution poSlideShow::CloseAppWindow leave ${ns}::SlideShowFinished
        poApps StartApp poSlideShow $fileList
        poSlideShow SetFileMarkList $markList
    }

    proc SetCurImgDir { dirName } {
        variable pkgInt

        set pkgInt(curDir) $dirName
    }

    proc GetCurImgDir {} {
        variable pkgInt

        return $pkgInt(curDir)
    }

    proc GetSelImgDir {} {
        variable pkgInt

        return [lindex [poTree GetSelection $pkgInt(treeId)] 0]
    }

    proc GetThumbDir { dirName } {
        global tcl_platform
        variable sPo

        set name "poThumbs"
        if { $sPo(hiddenThumbDir) && $tcl_platform(platform) eq "unix" } {
            set name ".poThumbs"
        }
        return [file join $dirName $name]
    }

    proc BuildThumbFileName { thumbDir imgName } {
        return [format "%s%s" [file join $thumbDir $imgName] ".png"]
    }

    proc GetThumbFile { imgFileName } {
        set dirName [file dirname $imgFileName]
        set imgName [file tail $imgFileName]

        set thumbDir [GetThumbDir $dirName]
        return [file join $thumbDir $imgName]
    }

    proc CreateThumbDir { thumbDir } {
        global tcl_platform
        variable sPo

        if { ! [file isdirectory $thumbDir] } {
            if { [catch {file mkdir $thumbDir} ] } {
                poLog Warning "Unable to create thumbs directory $thumbDir."
                poLog Warning "Check permissions."
                return 0
            }
            if { $sPo(hiddenThumbDir) && $tcl_platform(platform) eq "windows" } {
                file attributes $thumbDir -hidden 1
            }
        }
        return 1
    }

    proc ShowFileList { { force true } } {
        variable pkgInt
        variable sPo
        variable pkgCol
        global tcl_platform

        if { ([GetCurImgDir] eq [GetSelImgDir]) && ($force == false) } {
            return
        }

        set tbl $pkgInt(tableId)
        foreach { rowVis1 rowVis2 } [GetVisibleRows $tbl] { break }
        if { [GetCurImgDir] ne [GetSelImgDir] } {
            set rowVis1 0
            set rowVis2 0
        }

        set dirName [GetSelImgDir]
        if { $dirName eq "" } {
            return
        }
        WriteInfoStr "Getting file list ..."
        $tbl delete 0 end
        $tbl resetsortinfo
        $sPo(tw) configure -cursor watch
        SetCurImgDir $dirName
        set imgFileList [GetFileList $dirName]

        wm title $sPo(tw) "poApps - [poApps GetAppDescription $sPo(appName)] ([llength $imgFileList] files match filter)"
        InitProgress "scan" [llength $imgFileList]

        if { [llength $imgFileList] == 0 } {
            poLog Info "No files match filter in directory $dirName"
            $sPo(tw) configure -cursor top_left_arrow
            WriteInfoStr "Scanning finished."
            return
        }

        set thumbDir [GetThumbDir $dirName]

        if { $sPo(saveThumb) } {
            if { ! [CreateThumbDir $thumbDir] } {
                set sPo(saveThumb) 0
            }
        }

        if { [info exists pkgInt(placeholderImg)] } {
            image delete $pkgInt(placeholderImg)
        }
        set pkgInt(placeholderImg) [image create photo -width  $sPo(thumbSize) \
                                                       -height $sPo(thumbSize)]

        # Create a default list of row values.
        set defList [list]
        for { set c 0 } { $c < $pkgInt(numColumns) } { incr c } {
            if { [lindex $pkgInt(colSortList) $c] eq "real" ||  [lindex $pkgInt(colSortList) $c] eq "integer" } {
                lappend defList -1
            } else {
                lappend defList ""
            }
        }

        set row 0
        SetStopBrowsing false
        foreach imgFile $imgFileList {
            if { [StopBrowsing] } {
                WriteInfoStr "Scanning cancelled."
                break
            }
            set rowList $defList

            lset rowList $pkgCol(Name) $imgFile
            set pathName [file join $dirName $imgFile]
            if { $sPo(showFileInfo) } {
                set retVal [catch {poType GetFileType $pathName} typeDict]
                if { $retVal == 0 } {
                    set fmt "Unknown"
                    if { [dict exists $typeDict fmt] } {
                        set fmt [dict get $typeDict fmt]
                        if { $fmt eq "graphic" && [dict exists $typeDict subfmt] } {
                            set fmt [dict get $typeDict subfmt]
                            lset rowList $pkgCol(Width)  [dict get $typeDict width]
                            lset rowList $pkgCol(Height) [dict get $typeDict height]
                        }
                    } elseif { [dict exists $typeDict style] } {
                        set fmt [dict get $typeDict style]
                    }
                    lset rowList $pkgCol(Type) $fmt
                }
                set date [clock format [file mtime $pathName] \
                               -format "%Y-%m-%d %H:%M"]
                set size [dict get $typeDict size]
                lset rowList $pkgCol(Size) [format "%.1f" [expr {$size / 1024.0}]]
                lset rowList $pkgCol(Date) $date
            }
            if { [winfo exists $tbl] } {
                if { $row > [$tbl size] } {
                    break
                }
                $tbl insert end $rowList
                if { $sPo(showThumb) } {
                   $tbl cellconfigure $row,$pkgCol(Img) -image $pkgInt(placeholderImg)
                }
            }

            incr row
            if { $row % 25 == 0 || $row == [llength $imgFileList] } {
                WriteInfoStr "Scanned $row files out of [llength $imgFileList] ..."
                UpdateProgress "scan" $row
            }
        }
        if { ! [StopBrowsing] } {
            WriteInfoStr "Scanning finished."
        } else {
            SetStopBrowsing false
        }
        UpdateProgress "scan" 0
        if { [winfo exists $sPo(tw)] } {
            $sPo(tw) configure -cursor top_left_arrow
        }

        if { $sPo(showThumb) && [winfo exists $tbl] } {
            InitProgress "scan" [$tbl size]
            WriteInfoStr "Loading thumbnail images ..."
            LoadThumbnails $tbl
            UpdateProgress "scan" 0
        }
    }

    proc CreateTree { par selDir } {
        variable ns

        set treeId [poTree CreateDirTree $par $selDir "Directory tree"]
        bind $treeId <<TreeviewSelect>>   "+ ${ns}::ShowFileList false"
        bind $treeId <<RightButtonPress>> "${ns}::DirectoryContextMenu %X %Y"
        return $treeId
    }

    proc GetSelDirectories {} {
        variable pkgInt

        set dirList [poTree GetSelection $pkgInt(treeId)]
        return $dirList
    }

    proc CountDirsAndFiles { root } {
        set noDirs  0
        set noFiles 0

        set dirAndFileList [poMisc GetDirList $root 1 1 1 1]
        incr noDirs  [llength [lindex $dirAndFileList 0]]
        incr noFiles [llength [lindex $dirAndFileList 1]]

        foreach dir [lindex $dirAndFileList 0] {
            set subDirCount [CountDirsAndFiles [file join $root $dir]]
            incr noDirs  [lindex $subDirCount 0]
            incr noFiles [lindex $subDirCount 1]
        }
        return [list $noDirs $noFiles]
    }

    proc ShowDirInExplorer {} {
        set dirList [GetSelDirectories]
        foreach dir $dirList {
            poExtProg StartFileBrowser "$dir"
        }
    }

    proc SlideShowDir {} {
        set dirList [GetSelDirectories]
        poApps StartApp poSlideShow $dirList
    }

    proc InfoDir {} {
        variable sPo
        variable pkgInt

        $sPo(tw) configure -cursor watch
        update

        set dirList [GetSelDirectories]
        set numSelDir [llength $dirList]

        set w .poImgBrowse:InfoDirWin
        catch { destroy $w }

        toplevel $w
        wm title $w "Directory Information"
        wm resizable $w true true

        ttk::frame $w.fr0 -borderwidth 1
        grid  $w.fr0 -row 0 -column 0 -sticky nwse
        set textId [poWin CreateScrolledText $w.fr0 true "" -wrap word \
                             -height [poMisc Max 3 [poMisc Min 10 $numSelDir]]]

        foreach selDir $dirList {
            WriteInfoStr "Scanning directory $selDir ..."
            update
            set name [file tail $selDir]
            set dirInfo [CountDirsAndFiles $selDir]
            set msgStr "Directory $name: [lindex $dirInfo 0] subdirs, [lindex $dirInfo 1] files\n"
            $textId insert end $msgStr
        }
        $textId configure -state disabled

        # Create OK button
        ttk::frame $w.fr1
        grid  $w.fr1 -row 1 -column 0 -sticky nwse
        ttk::button $w.fr1.b -text "OK" -command "destroy $w" -default active
        bind $w.fr1.b <KeyPress-Return> "destroy $w"
        pack $w.fr1.b -side left -fill x -padx 2 -pady 2 -expand 1

        grid columnconfigure $w 0 -weight 1
        grid rowconfigure    $w 0 -weight 1

        bind $w <Escape> "destroy $w"
        bind $w <Return> "destroy $w"
        $sPo(tw) configure -cursor top_left_arrow
        focus $w
    }

    proc DirectoryContextMenu { x y } {
        variable ns

        set w .poImgBrowse:directoryContextMenu
        catch { destroy $w }
        menu $w -tearoff false -disabledforeground white

        set selList [GetSelDirectories]
        set numSel [llength $selList]
        if { $numSel == 0 } {
            set menuTitle "Nothing selected"
        } else {
            set menuTitle "$numSel selected"
        }
        $w add command -label "$menuTitle" -state disabled -background "#303030"
        if { $numSel == 0 } {
            tk_popup $w $x $y
            return
        }

        $w add command -label "Open directory" -command "${ns}::ShowDirInExplorer"
        $w add command -label "Slide show"     -command "${ns}::SlideShowDir"
        $w add separator
        $w add command -label "Info" -command "${ns}::InfoDir"
        if { $numSel == 1 } {
            # All of these commands are currently supported only for 1 directory.
            $w add command -label "Update"      -command "${ns}::UpdateDirList $selList"
            $w add separator
            $w add command -label "New ..."     -command "${ns}::AskNewDir $x $y"
            $w add separator
            $w add command -label "Rename ..."  -command "${ns}::AskRenameDir $x $y"
            $w add separator
            $w add command -label "Copy to ..." -command "${ns}::AskCopyOrMoveDir copy"
            $w add command -label "Move to ..." -command "${ns}::AskCopyOrMoveDir move"
            $w add separator
            $w add command -label "Delete ..."  -command "${ns}::AskDelDir"
        }
        tk_popup $w $x $y
    }

    proc GetUsageMsg {} {
        variable sPo

        set msg ""
        append msg "\n"
        append msg "poApps: $sPo(appName) \[Directory\]\n"
        append msg "\n"
        append msg "Start the image browser using specified directory as root.\n"
        append msg "If no directory is specified, the current directory is used.\n"
        append msg "\n"
        append msg "Options:\n"
        append msg "None.\n"
        return $msg
    }

    proc HelpCont {} {
        variable sPo

        set msg [poApps GetUsageMsg]
        append msg [GetUsageMsg]
        poWin CreateHelpWin $msg "Help for $sPo(appName)"
    }

    proc UpdateCombo { cb fmtList showInd } {
        variable sPo
        $cb configure -values $fmtList
        $cb current $showInd
    }

    proc ComboCB { args } {
        variable sPo
        variable pkgInt

        set sPo(suffixFilter) [$pkgInt(combo) get]
        ShowFileList
    }

    proc StartAppImgview { fileName } {
        poApps StartApp poImgview [list $fileName]
    }

    proc SetVerbose { verboseFlag } {
        variable sPo

        set sPo(optVerbose) $verboseFlag
    }

    proc AddMenuCmd { menu label acc cmd args } {
        eval {$menu add command -label $label -accelerator $acc -command $cmd} $args
    }

    proc AddMenuRadio { menu label acc var val cmd args } {
        eval {$menu add radiobutton -label $label -accelerator $acc \
                                    -variable $var -value $val -command $cmd} $args
    }

    proc AddMenuCheck { menu label acc var cmd args } {
        eval {$menu add checkbutton -label $label -accelerator $acc \
                                    -variable $var -command $cmd} $args
    }

    proc AddRecentDirs { menuId } {
        variable pkgInt

        $menuId delete 0 end
        foreach dir [lindex [poAppearance GetRecentDirList] 0] {
            if { [file isdirectory $dir] } {
                set bmp [poWin GetOkBitmap]
            } else {
                set bmp [poWin GetCancelBitmap]
            }
            AddMenuCmd $menuId $dir "" [list poTree Rescan $pkgInt(treeId) $dir] -image $bmp -compound left
        }
    }

    proc UpdateColumnHeaders { tableId } {
        variable sPo
        variable pkgCol

        $tableId columnconfigure $pkgCol(Img)    -hide [expr !$sPo(showThumb)]
        $tableId columnconfigure $pkgCol(Type)   -hide [expr !$sPo(showFileInfo)]
        $tableId columnconfigure $pkgCol(Size)   -hide [expr !$sPo(showFileInfo)]
        $tableId columnconfigure $pkgCol(Date)   -hide [expr !$sPo(showFileInfo)]
        $tableId columnconfigure $pkgCol(Width)  -hide [expr !$sPo(showFileInfo)]
        $tableId columnconfigure $pkgCol(Height) -hide [expr !$sPo(showFileInfo)]
    }

    proc OpenDir { dirName { selFunc ::poImgBrowse::StartAppImgview } } {
        variable ns
        variable sPo
        variable pkgInt
        variable pkgCol

        if { [winfo exists $sPo(tw)] } {
            poWin Raise $sPo(tw)
            return
        }

        toplevel $sPo(tw)
        wm withdraw .

        set sPo(mainWin,name) $sPo(tw)

        focus $sPo(tw)
        wm geometry $sPo(tw) [format "%dx%d+%d+%d" \
                    $sPo(mainWin,w) $sPo(mainWin,h) \
                    $sPo(mainWin,x) $sPo(mainWin,y)]

        ttk::frame $sPo(tw).fr
        pack $sPo(tw).fr -expand 1 -fill both
        set fr $sPo(tw).fr

        ttk::frame $fr.toolfr -relief groove -padding 1 -borderwidth 1
        ttk::frame $fr.workfr
        ttk::frame $fr.infofr -relief sunken -borderwidth 1
        grid $fr.toolfr -row 0 -column 0 -sticky news
        grid $fr.workfr -row 1 -column 0 -sticky news
        grid $fr.infofr -row 2 -column 0 -sticky news
        grid rowconfigure    $fr 1 -weight 1
        grid columnconfigure $fr 0 -weight 1

        ttk::frame $fr.workfr.fr
        pack $fr.workfr.fr -expand 1 -fill both

        set sPo(paneHori) $fr.workfr.fr.pane
        ttk::panedwindow $sPo(paneHori) -orient horizontal
        pack $sPo(paneHori) -side top -expand 1 -fill both

        set lf $sPo(paneHori).lfr
        set rf $sPo(paneHori).rfr
        ttk::frame $lf -relief sunken -borderwidth 1
        ttk::frame $rf -relief sunken -borderwidth 1
        pack $lf -expand 1 -fill both -side left
        pack $rf -expand 1 -fill both -side left
        $sPo(paneHori) add $lf
        $sPo(paneHori) add $rf

        set sPo(paneVert) $lf.pane
        ttk::panedwindow $sPo(paneVert) -orient vertical
        pack $sPo(paneVert) -side top -expand 1 -fill both

        ttk::frame $sPo(paneVert).dirfr -relief sunken -borderwidth 1
        ttk::frame $sPo(paneVert).icofr -relief sunken -borderwidth 1
        ttk::frame $rf.tblfr -relief raised
        grid $sPo(paneVert).dirfr -row 0 -column 0 -sticky news
        grid $sPo(paneVert).icofr -row 1 -column 0 -sticky news
        grid $rf.tblfr -row 0 -column 0 -sticky news
        $sPo(paneVert) add $sPo(paneVert).dirfr
        $sPo(paneVert) add $sPo(paneVert).icofr

        grid rowconfigure    $lf 0 -weight 1
        grid columnconfigure $lf 0 -weight 1
        grid rowconfigure    $rf 0 -weight 1
        grid columnconfigure $rf 0 -weight 1

        ttk::frame $rf.tblfr.tabfr
        pack $rf.tblfr.tabfr -fill both -expand 1

        set tableId [CreateScrolledTablelist $rf.tblfr.tabfr \
                    -width 100 -height 10 \
                    -exportselection false \
                    -setfocus 1 \
                    -columntitles $pkgInt(colNameList) \
                    -stripebackground #e0e8f0 \
                    -selectmode extended \
                    -labelcommand ::tablelist::sortByColumn \
                    -showseparators yes]

        $tableId columnconfigure 0 -showlinenumbers true
        for { set colNum 0 } { $colNum < $pkgInt(numColumns) } { incr colNum } {
            $tableId columnconfigure $colNum -sortmode [lindex $pkgInt(colSortList) $colNum]
            $tableId columnconfigure $colNum -align    [lindex $pkgInt(colAlignList) $colNum]
            $tableId columnconfigure $colNum -width    [lindex $pkgInt(colWidthList) $colNum]
        }
        bind $tableId <<TablelistSelect>> "${ns}::PrintSelInfo $tableId"
        set bodyTag [$tableId bodytag]
        bind $bodyTag <Motion> "${ns}::StoreCurMousePos $tableId %W %x %y %X %Y"
        bind $bodyTag <<RightButtonPress>> \
                   "${ns}::OpenContextMenu $tableId %W %X %Y $selFunc"
        bind $bodyTag <Double-1> "${ns}::OpenFiles $tableId $selFunc"
        bind $bodyTag <Control-a> "$tableId selection set 0 end"
        bind $bodyTag <KeyRelease-Down>  "${ns}::ShowSelection $tableId Down"
        bind $bodyTag <KeyRelease-Up>    "${ns}::ShowSelection $tableId Up"
        bind $bodyTag <KeyRelease-Prior> "${ns}::ShowSelection $tableId Down"
        bind $bodyTag <KeyRelease-Next>  "${ns}::ShowSelection $tableId Up"
        bind $bodyTag <KeyRelease-Home>  "${ns}::ShowSelection $tableId Home"
        bind $bodyTag <KeyRelease-End>   "${ns}::ShowSelection $tableId End"

        set pkgInt(tableId) $tableId

        UpdateColumnHeaders $tableId

        # Create a Drag-And-Drop binding for the tablelist.
        poDragAndDrop AddCanvasBinding $tableId ${ns}::UpdateDirListByDrop

        # Create menus File, Edit, Settings and Help
        set hMenu $sPo(tw).menufr
        menu $hMenu -borderwidth 2 -relief sunken
        if { $::tcl_platform(os) eq "Darwin" } {
            $hMenu add cascade -menu $hMenu.apple -label "poApps"
            set appleMenu $hMenu.apple
            menu $appleMenu -tearoff 0
            AddMenuCmd $appleMenu "About poApps ..."    ""  "poApps HelpProg"
            AddMenuCmd $appleMenu "About Tcl/Tk ..."    ""  "poApps HelpTcl"
            AddMenuCmd $appleMenu "About packages ..."  ""  "poApps PkgInfo"
        }

        set fileMenu $hMenu.file
        set editMenu $hMenu.edit
        set viewMenu $hMenu.view
        set settMenu $hMenu.sett
        set winMenu  $hMenu.win
        set helpMenu $hMenu.help
        $hMenu add cascade -menu $fileMenu -label File     -underline 0
        $hMenu add cascade -menu $editMenu -label Edit     -underline 0
        $hMenu add cascade -menu $viewMenu -label View     -underline 0
        $hMenu add cascade -menu $settMenu -label Settings -underline 0
        $hMenu add cascade -menu $winMenu  -label Window   -underline 0
        $hMenu add cascade -menu $helpMenu -label Help     -underline 0

        # Menu File
        menu $fileMenu -tearoff 0
        AddMenuCmd $fileMenu "Open selected ..." "Ctrl+O" "${ns}::OpenFiles $tableId $selFunc"

        $fileMenu add separator
        $fileMenu add cascade -label "Rebrowse"  -menu $fileMenu.rebrowse
        set sPo(rebrowseMenu) $fileMenu.rebrowse
        menu $sPo(rebrowseMenu) -tearoff 0 -postcommand "${ns}::AddRecentDirs $sPo(rebrowseMenu)"

        $fileMenu add separator
        AddMenuCmd $fileMenu "Close subwindows" "Ctrl+G" ${ns}::CloseSubWindows
        AddMenuCmd $fileMenu "Close window"     "Ctrl+W" ${ns}::CloseAppWindow
        if { $::tcl_platform(os) ne "Darwin" } {
            AddMenuCmd $fileMenu "Quit" "Ctrl+Q" ${ns}::ExitApp
        }

        bind $sPo(tw) <Control-o> "${ns}::OpenFiles $tableId $selFunc"

        bind $sPo(tw) <Control-g> ${ns}::CloseSubWindows
        bind $sPo(tw) <Control-w> ${ns}::CloseAppWindow
        bind $sPo(tw) <Control-q> ${ns}::ExitApp
        if { $::tcl_platform(platform) eq "windows" } {
            bind $sPo(tw) <Alt-F4> ${ns}::ExitApp
        }
        wm protocol $sPo(tw) WM_DELETE_WINDOW ${ns}::CloseAppWindow

        # Menu Edit
        set thumbMenu $editMenu.thumb
        menu $editMenu -tearoff 0

        AddMenuCmd $editMenu "Delete"      "Del" "${ns}::AskDelFile $tableId"
        AddMenuCmd $editMenu "Rename"      "F2"  "${ns}::AskRenameFile $tableId"
        AddMenuCmd $editMenu "Copy to ..." ""    "${ns}::AskCopyOrMoveTo $tableId copy"
        AddMenuCmd $editMenu "Move to ..." ""    "${ns}::AskCopyOrMoveTo $tableId move"

        bind $sPo(tw) <Delete>    "${ns}::AskDelFile $tableId"
        bind $sPo(tw) <F2>        "${ns}::AskRenameFile $tableId"

        $editMenu add separator
        $editMenu add cascade -label "Thumbnails" -menu $thumbMenu

        menu $thumbMenu -tearoff 0
        AddMenuCmd $thumbMenu "Update all" "" "${ns}::GenAllThumbs $tableId 0"
        # OPA TODO With subdirs not yet implemented
        # AddMenuCmd $thumbMenu "Update all with subdirs" \
        #                       "" "${ns}::GenAllThumbs $tableId 1"
        AddMenuCmd $thumbMenu "Delete all" "" "${ns}::DelAllThumbs $tableId"

        # Menu View
        menu $viewMenu -tearoff 0
        AddMenuCmd $viewMenu "File info"           "Ctrl+I" "${ns}::ShowImgInfo $tableId"
        AddMenuCmd $viewMenu "Slide show (all)"    "Ctrl+E" "${ns}::SlideShowFile $tableId true"
        AddMenuCmd $viewMenu "Slide show (marked)" "Ctrl+M" "${ns}::SlideShowFile $tableId false"
        bind $sPo(tw) <Control-i> "${ns}::ShowImgInfo   $tableId"
        bind $sPo(tw) <Control-e> "${ns}::SlideShowFile $tableId true"
        bind $sPo(tw) <Control-m> "${ns}::SlideShowFile $tableId false"

        # Menu Settings
        set imgSettMenu $settMenu.img
        set genSettMenu $settMenu.gen
        menu $settMenu -tearoff 0

        $settMenu add cascade -label "Image settings" -menu $imgSettMenu
        menu $imgSettMenu -tearoff 0
        AddMenuCmd $imgSettMenu "Appearance"          "" [list poSettings ShowImgSettWin "Appearance"]
        AddMenuCmd $imgSettMenu "Image types"         "" [list poSettings ShowImgSettWin "Image types"]
        AddMenuCmd $imgSettMenu "Image browser"       "" [list poSettings ShowImgSettWin "Image browser"]
        AddMenuCmd $imgSettMenu "Slide show"          "" [list poSettings ShowImgSettWin "Slide show"]
        AddMenuCmd $imgSettMenu "Zoom rectangle"      "" [list poSettings ShowImgSettWin "Zoom rectangle"]
        AddMenuCmd $imgSettMenu "Selection rectangle" "" [list poSettings ShowImgSettWin "Selection rectangle"]

        $settMenu add cascade -label "General settings" -menu $genSettMenu
        menu $genSettMenu -tearoff 0
        AddMenuCmd $genSettMenu "Appearance" "" [list poSettings ShowGeneralSettWin "Appearance"]
        AddMenuCmd $genSettMenu "File types" "" [list poSettings ShowGeneralSettWin "File types"]
        AddMenuCmd $genSettMenu "Logging"    "" [list poSettings ShowGeneralSettWin "Logging"]

        $settMenu add separator

        AddMenuCheck $settMenu "Save on exit"       "" poApps::gPo(autosaveOnExit) ""
        AddMenuCmd   $settMenu "View setting files" "" "poApps ViewSettingsDir"
        AddMenuCmd   $settMenu "Save settings"      "" "poApps SaveSettings"

        # Menu Window
        menu $winMenu -tearoff 0
        AddMenuCmd $winMenu [poApps GetAppDescription main]        "" "poApps StartApp main"
        $winMenu add separator
        AddMenuCmd $winMenu [poApps GetAppDescription poImgview]   "" "poApps StartApp poImgview"
        AddMenuCmd $winMenu [poApps GetAppDescription poImgBrowse] "" "poApps StartApp poImgBrowse" -state disabled
        AddMenuCmd $winMenu [poApps GetAppDescription poBitmap]    "" "poApps StartApp poBitmap"
        $winMenu add separator
        AddMenuCmd $winMenu [poApps GetAppDescription poImgdiff]   "" "poApps StartApp poImgdiff"
        AddMenuCmd $winMenu [poApps GetAppDescription poDiff]      "" "poApps StartApp poDiff"
        $winMenu add separator
        AddMenuCmd $winMenu [poApps GetAppDescription poPresMgr]   "" "poApps StartApp poPresMgr"
        AddMenuCmd $winMenu [poApps GetAppDescription poOffice]    "" "poApps StartApp poOffice"

        # Menu Help
        menu $helpMenu -tearoff 0
        if { $::tcl_platform(os) ne "Darwin" } {
            AddMenuCmd $helpMenu "Help ..." "F1" ${ns}::HelpCont
            bind $sPo(tw) <Key-F1>  ${ns}::HelpCont
            AddMenuCmd $helpMenu "About poApps ..."    ""  "poApps HelpProg"
            AddMenuCmd $helpMenu "About Tcl/Tk ..."    ""  "poApps HelpTcl"
            AddMenuCmd $helpMenu "About packages ..."  ""  "poApps PkgInfo"
        }

        # Make the menus available
        $sPo(tw) configure -menu $hMenu

        # Add new toolbar group and associated buttons.
        set toolfr $fr.toolfr
        poToolbar New $toolfr
        poToolbar AddGroup $toolfr

        poToolbar AddButton $toolfr [::poBmpData::open] \
                  "${ns}::OpenFiles $tableId $selFunc" [Str OpenFile]
        poToolbar AddButton $toolfr [::poBmpData::delete] \
                  "${ns}::AskDelFile $tableId" [Str DelFile]
        poToolbar AddButton $toolfr [::poBmpData::rename] \
                  "${ns}::AskRenameFile $tableId" [Str RenameFile]

        poToolbar AddGroup $toolfr

        poToolbar AddButton $toolfr [::poBmpData::infofile] \
                  "${ns}::ShowImgInfo $tableId" [Str FileInfo]
        poToolbar AddButton $toolfr [::poBmpData::slideShowAll] \
                  "${ns}::SlideShowFile $tableId true" "Slide show of all images (Ctrl+E)"
        poToolbar AddButton $toolfr [::poBmpData::slideShowMarked] \
                  "${ns}::SlideShowFile $tableId false" "Slide show of marked images (Ctrl+M)"

        # Add new toolbar group and associated widgets.
        poToolbar AddGroup $toolfr

        poToolbar AddLabel $toolfr "Filter:" ""
        poToolbar AddEntry $toolfr ${ns}::sPo(prefixFilter) "File match" -width 10

        set pkgInt(combo) [poToolbar AddCombobox $toolfr ${ns}::sPo(formatFilter) "Image format" -state readonly]
        set fmtInd 0
        set curInd 2
        set fmtList [list $pkgInt(allFiles) $pkgInt(allImgs)]
        if { $pkgInt(allImgs) eq $sPo(suffixFilter) } {
            set fmtInd 1
        }
        foreach fmt [poImgType GetFmtList] {
            lappend fmtList $fmt
            if { $fmt eq $sPo(suffixFilter) } {
                set fmtInd $curInd
            }
            incr curInd
        }
        UpdateCombo $pkgInt(combo) $fmtList $fmtInd
        $pkgInt(combo) current $fmtInd
        bind $pkgInt(combo) <<ComboboxSelected>> "${ns}::ComboCB"

        poToolbar AddButton $toolfr [::poBmpData::update] \
                  "${ns}::ShowFileList" [Str UpdateList]
        poToolbar AddButton $toolfr [::poBmpData::clear] \
                  "${ns}::ClearImgCache $tableId ; ${ns}::UpdateVisImgs $tableId" \
                  [Str ClearImgCache]
        poToolbar AddGroup $toolfr
        poToolbar AddButton $toolfr [::poBmpData::halt "red"] \
                  ${ns}::SetStopBrowsing [Str StopImgLoad]
        bind $sPo(tw) <Key-F5> "${ns}::ShowFileList"
        bind $sPo(tw) <KeyPress-Escape> ${ns}::SetStopBrowsing

        set pkgInt(previewWidget) [poWinPreview Create $sPo(paneVert).icofr] 

        if { $dirName ne "" } {
            set dirName [file normalize $dirName]
            if { ! [file isdirectory $dirName] } {
                WriteInfoStr "Directory $dirName does not exist."
                if { [file isdirectory $sPo(lastDir)] } {
                    set dirName $sPo(lastDir)
                } else {
                    set dirName [pwd]
                }
            }
        }
        set treeId [CreateTree $sPo(paneVert).dirfr $dirName]
        set pkgInt(treeId) $treeId
        SetCurImgDir ""
        if { $dirName ne "" } {
            set sPo(lastDir) $dirName
        }


        ttk::label $fr.infofr.label -text "No files selected" -anchor w
        set sPo(progress,scan,widget) $fr.infofr.progress
        set sPo(progress,scan,value)  0
        ttk::progressbar $sPo(progress,scan,widget) -variable ${ns}::sPo(progress,scan,value)
        pack $fr.infofr.label -side left -fill x
        pack $sPo(progress,scan,widget) -side right

        set pkgInt(infoId) $fr.infofr.label

        # This must be done, after all other widgets have been created and an update
        # has occured. Otherwise the sash position is not correctly set and restored.
        wm geometry $sPo(tw) [format "%dx%d+%d+%d" \
                    $sPo(mainWin,w) $sPo(mainWin,h) \
                    $sPo(mainWin,x) $sPo(mainWin,y)]
        update
        $sPo(paneVert) pane $sPo(paneVert).dirfr -weight 1
        $sPo(paneVert) pane $sPo(paneVert).icofr -weight 0
        $sPo(paneVert) sashpos 0 $sPo(sashY)
        $sPo(paneHori) sashpos 0 $sPo(sashX)

        poWin Raise $sPo(tw)
    }

    proc ShowMainWin { { dirName "" } { selFunc ::poImgBrowse::StartAppImgview } } {
        OpenDir $dirName $selFunc
    }

    proc InitProgress { widgetName maxValue { mode "determinate" } } {
        variable sPo

        if { ! [winfo exists $sPo(progress,$widgetName,widget)] } {
            return
        }
        $sPo(progress,$widgetName,widget) configure -maximum $maxValue -mode $mode
        update
    }

    proc UpdateProgress { widgetName curValue } {
        variable sPo

        if { ! [winfo exists $sPo(progress,$widgetName,widget)] } {
            return
        }
        set sPo(progress,$widgetName,value) $curValue
        if { [$sPo(progress,$widgetName,widget) cget -mode] eq "determinate" } {
            if { $sPo(HaveTcl87AndUp) } {
                if { $curValue == 0 } {
                    $sPo(progress,$widgetName,widget) configure -text ""
                } else {
                    set maxValue [$sPo(progress,$widgetName,widget) cget -maximum]
                    set percent [format "%.0f%%" [expr {100.0 * $curValue / $maxValue}]]
                    $sPo(progress,$widgetName,widget) configure -text $percent
                }
            }
        }
        update
    }

    proc PrintInfo { tbl row } {
        variable pkgInt
        variable pkgCol

        if { ! [winfo exists $tbl] } {
            return
        }
        if { $row >= [$tbl size] } {
            return
        }
        set dirName [GetSelImgDir]
        set fileName [file join $dirName [$tbl cellcget $row,$pkgCol(Name) -text]]

        poWinPreview Update $pkgInt(previewWidget) $fileName

        catch { unset pkgInt(afterId) }
    }

    proc PrintSelInfo { tbl } {
        set rowList [$tbl curselection]
        if { [llength $rowList] > 0 } {
            PrintInfo $tbl [lindex $rowList 0]
        }
    }

    proc GetTopRow { tbl } {
        set tblBody [$tbl bodypath]
        foreach { ::tablelist::W ::tablelist::x ::tablelist::y } \
            [::tablelist::convEventFields $tblBody 0 0] {}
        set row [$tbl index @$::tablelist::x,$::tablelist::y]
        return $row
    }

    proc GetBottomRow { tbl } {
        set tblBody [$tbl bodypath]
        foreach { ::tablelist::W ::tablelist::x ::tablelist::y } \
            [::tablelist::convEventFields $tblBody 0 [winfo height $tblBody]] {}
        set row [$tbl index @$::tablelist::x,$::tablelist::y]
        return $row
    }

    proc GetVisibleRows { tbl } {
        set rowVis1 [expr {[GetTopRow $tbl]}]
        set rowVis2 [expr {[GetBottomRow $tbl]}]
        set rowVis1 [poMisc Max 0 $rowVis1]
        set rowVis2 [poMisc Max 0 [poMisc Min $rowVis2 [expr {[$tbl size] -1}]]]
        return [list $rowVis1 $rowVis2]
    }

    proc StoreCurMousePos { tbl tblBody x y X Y } {
        variable ns
        variable pkgInt

        set pkgInt(mouse,x) $X
        set pkgInt(mouse,y) $Y

        if { [$tbl size] == 0 } {
            return
        }

        if { [info exists pkgInt(afterId)] } {
            after cancel $pkgInt(afterId)
        }

        foreach { ::tablelist::W ::tablelist::x ::tablelist::y } \
            [::tablelist::convEventFields $tblBody $x $y] {}

        set row [$tbl index @$::tablelist::x,$::tablelist::y]
        if { $row < 0 } {
            return
        }

        set pkgInt(afterId) [after 500 ${ns}::PrintInfo $tbl $row]
    }

    proc WriteInfoStr { str { warn false } } {
        variable pkgInt

        if { [info exists pkgInt(infoId)] && \
             [winfo exists $pkgInt(infoId)] } {
            $pkgInt(infoId) configure -text $str
            if { $warn } {
                $pkgInt(infoId) configure -background "red"
            } else {
                $pkgInt(infoId) configure -background ""
            }
        }
    }

    proc LoadSettings { cfgDir } {
        variable sPo
        variable pkgInt

        # Init all variables stored in the cfg file with default values.
        SetWindowPos mainWin  90  30 800 480
        SetWindowPos settWin 100 100   0   0

        SetMainWindowSash    220 260

        SetThumbOptions      60 0 1
        SetViewOptions       1 1
        SetFilterOptions     "*" $pkgInt(allImgs)
        SetLastUsedDir       [pwd]

        set cfgFile [file normalize [poCfgFile GetCfgFilename $sPo(appName) $cfgDir]]
        if { [poMisc IsReadableFile $cfgFile] } {
            set sPo(initStr) "Settings loaded from file $cfgFile"
            source $cfgFile
        } else {
            set sPo(initStr) "No settings file found. Using default values."
        }
        set sPo(cfgDir) $cfgDir
    }

    proc PrintCmd { fp cmdName } {
        puts $fp "\n# Set${cmdName} [info args Set${cmdName}]"
        puts $fp "catch {Set${cmdName} [Get${cmdName}]}"
    }

    proc SaveSettings {} {
        variable sPo
        variable pkgInt

        set cfgFile [poCfgFile GetCfgFilename $sPo(appName) $sPo(cfgDir)]
        poCfgFile CreateBackupFile $cfgFile
        set retVal [catch {open $cfgFile w} fp]
        if { $retVal == 0 } {
            puts $fp "\n# SetWindowPos [info args SetWindowPos]"

            puts $fp "catch {SetWindowPos [GetWindowPos mainWin]}"
            puts $fp "catch {SetWindowPos [GetWindowPos settWin]}"

            # As we can close the window and reopen through the poApps main window
            # store the current window positions also in the namespace variables.
            eval SetWindowPos [GetWindowPos mainWin]
            eval SetWindowPos [GetWindowPos settWin]

            eval SetMainWindowSash [GetMainWindowSash]

            PrintCmd $fp "MainWindowSash"

            PrintCmd $fp "LastUsedDir"
            PrintCmd $fp "ThumbOptions"
            PrintCmd $fp "ViewOptions"
            PrintCmd $fp "FilterOptions"

            close $fp
        }
    }

    proc ParseCommandLine { argList } {
        variable ns
        variable sPo
        variable pkgInt

        set curArg 0
        set dirList [list]
        while { $curArg < [llength $argList] } {
            set curParam [lindex $argList $curArg]
            if { [string compare -length 1 $curParam "-"]  == 0 || \
                 [string compare -length 2 $curParam "--"] == 0 } {
                set curOpt [string tolower [string trimleft $curParam "-"]]
            } else {
                lappend dirList $curParam
            }
            incr curArg
        }

        if { [llength $dirList] == 0 } {
            lappend dirList [pwd]
        }

        # Loop through all arguments. The first valid directory name will be opened.
        foreach fileOrDirName $dirList {
            if { [file isdirectory $fileOrDirName] } {
                set curDir [poMisc FileSlashName $fileOrDirName]
                poTree Rescan $pkgInt(treeId) $curDir
                poAppearance AddToRecentDirList $curDir
                break
            }
        }
    }

    proc IsOpen {} {
        variable sPo

        return [winfo exists $sPo(tw)]
    }
}

poImgBrowse Init
catch {poLog Debug "Loaded Package poApplib (Module [info script])"}
