###
### ObexTool file list functions
###
### History:
###   20090201: preserve filedate param. implemented -ger
###
 
package provide obexlist 0.5

namespace eval ObexList {

  variable array lb_entries
  variable scroll_window
  variable list_widget
  variable dblclick 0

  variable list_mode [getObexCfg list initmode]

  variable scrollreg [list 0 0 1 1]
  variable array copy_selection 
  variable array icon_actpos
  variable coord_last
  variable menu_id .obexCtxMnu
  variable icon_selected
  variable restore_cmd {}
  variable download_directory {}

################ menu-entry      onitem action
  if [getObexCfg config filemove] {
    variable contextmenu [list \
      [get_text "Start plugin"]  handle ObexList::default_action \
      [get_text "Display text"]  show   ObexList::default_action \
      [get_text "View file"]     view   ObexList::default_action \
      [get_text "Edit file"]     edit   ObexList::default_action \
      [get_text "Open"]          folder ObexList::default_action \
      "-"                        file   "" \
      [get_text "Upload file"]   file   ObexTool::file_put \
      [get_text "Download file"] file   ObexList::file_get \
      "-"                        file   "" \
      [get_text "Cut file"]      file   "ObexList::menu_edit cut" \
      [get_text "Copy file"]     file   "ObexList::menu_edit copy" \
      [get_text "Paste file"]    any    "ObexList::menu_edit paste" \
      "-"                        both   "" \
      [get_text "Delete"]        both   ObexTool::file_delete \
      [get_text "Rename"]        both   ObexTool::file_rename \
      "-"                        any    "" \
      [get_text "New folder"]    any    ObexTree::new_folder \
    ]
  } else {
    variable contextmenu [list \
      [get_text "Start plugin"]  handle ObexList::default_action \
      [get_text "Display text"]  show   ObexList::default_action \
      [get_text "View file"]     view   ObexList::default_action \
      [get_text "Edit file"]     edit   ObexList::default_action \
      [get_text "Open"]          folder ObexList::default_action \
      "-"                        file   "" \
      [get_text "Upload file"]   file   ObexTool::file_put \
      [get_text "Download file"] file   ObexList::file_get \
      "-"                        file   "" \
      [get_text "Cut file"]      file   "ObexList::menu_edit cut" \
      [get_text "Copy file"]     file   "ObexList::menu_edit copy" \
      [get_text "Paste file"]    any    "ObexList::menu_edit paste" \
      "-"                        both   "" \
      [get_text "Delete"]        both   ObexTool::file_delete \
      "-"                        any    "" \
      [get_text "New folder"]    any    ObexTree::new_folder \
    ]
  }

  proc create { w } {
    debug_out "ObexList::create $w"
    variable scroll_window $w
    variable list_mode

    set lmode [getObexCfg list initmode]
  debug_var lmode
    switch $lmode {
      listb   {set list_mode [ObexList::new_listbox]}
      detail  {set list_mode [ObexList::new_detail ]}
      icons   {set list_mode [ObexList::new_iconbox]}
      default {set list_mode [ObexList::new_listbox]}
    }
  debug_var list_mode
    return $list_mode
  }

   proc add_cmenu { menuid mentry cmd args } {
   debug_out "add_cmenu $menuid $mentry $cmd $args" 3
     if [string_equal $mentry "-"] {
       $menuid add separator
     } else {
       $menuid add command -label $mentry -command "$cmd $args"
     }
   }

  proc context_menu { mode args } {
    variable contextmenu
    variable list_widget
    variable lb_entries
    variable menu_id

  debug_out "context_menu $mode $args" 3

  if [winfo exists $menu_id] {
    if [winfo viewable $menu_id] return
  }

  catch {destroy $menu_id}
  menu $menu_id -tearoff false
  set mousex [winfo pointerx .]
  set mousey [winfo pointery .]

  set name ""
  debug_out "mode=$mode"
    switch $mode {
      list {
        set obj [lindex $args 0]
  debug_out "obj=$obj"
        if ![string_empty $obj] {
          set name [$list_widget itemcget $obj  -text]
        }
      }
      icons {
        set name [lindex [$list_widget gettags current] 0]
      }
      detail {
        set slist [$list_widget curselection]
        set yco [expr [winfo y [lindex $args 0]]+[lindex $args 1]]
        set idx [$list_widget nearest $yco]
        set sel [$list_widget get $idx]
        set name [lindex $sel 0]
  #debug_out "slist=$slist ancho=$ancho"
      }
      default {
        internal_error "context_menu: invalid list mode: '$mode'!"
      }
    }

  debug_var name 3
    foreach [list mentry onitem action] $contextmenu {
#  debug_out "onitem=$onitem string_empty \"$name\" = [string_empty $name]"
      switch $onitem {
        none {
          if [string_empty $name] { 
            add_cmenu $menu_id $mentry $action
          } 
        }
        file {
          if ![string_empty $name] {
            set type [lindex $lb_entries($name) 1]
            if ![string_equal $type "folder"] {
              add_cmenu $menu_id $mentry $action $name 
            }
          }
        }
        folder {
          if ![string_empty $name] {
            set type [lindex $lb_entries($name) 1]
            if [string_equal $type "folder"] {
              add_cmenu $menu_id $mentry $action $name
            }
          }
        }
        both {
          if ![string_empty $name] {
            add_cmenu $menu_id $mentry $action $name
          }
        }
        show {
          if ![string_empty $name] {
            set type [lindex $lb_entries($name) 1]
            if [is_viewable $type] {
              add_cmenu $menu_id $mentry $action $name show
            }
          }
        }
        view {
          if ![string_empty $name] {
            set type [lindex $lb_entries($name) 1]
            set exe [getObexCfg view $type]
            if ![string_empty $exe] {
              add_cmenu $menu_id $mentry $action $name view
            }
          }
        }
        edit {
          if ![string_empty $name] {
            set type [lindex $lb_entries($name) 1]
            set exe [getObexCfg edit $type]
            if ![string_empty $exe] {
              add_cmenu $menu_id $mentry $action $name edit
            }
          }
        }
        handle {
          if ![string_empty $name] {
            set type [lindex $lb_entries($name) 1]
            set ns [string toupper $type]
            if [load_plugin $ns] {
              eval "add_cmenu $menu_id \"$mentry\" ${ns}::default_handler $lb_entries($name)"
            }
          }
        }
        default {
          add_cmenu $menu_id $mentry $action 
        }
      }
    }

    tk_popup $menu_id $mousex $mousey
  }

  proc help_text { name } {
    variable lb_entries
    set type [lindex $lb_entries($name) 1]
    set size [lindex $lb_entries($name) 2]
    set date [lindex $lb_entries($name) 3]
    set uprm [lindex $lb_entries($name) 4]
    set gprm [lindex $lb_entries($name) 5]
    set ftyp [ObexTool::file_type $type]
    set sfmt [get_text "%s\nDate: %s\nSize: %s\nUser: %s\nGroup: %s"]
    return [format $sfmt $ftyp $date $size $uprm $gprm]
  }

  proc load_plugin { nsp } {
    global OBEXDIR
    debug_out "load_plugin $nsp" 3

    set pexists [info commands ::${nsp}::default_handler]
    debug_var pexists
    if {$pexists ne ""} {return 1}

    set plugin_path "$OBEXDIR/plugins/[string tolower $nsp]_*"
    set plugins [glob -nocomplain $plugin_path]
    debug_out "plugins=$plugins"

    if {![llength $plugins]} {return 0}

    foreach plug [lsort $plugins] {
      debug_out "loading plugin '$plug'..."
      source $plug
      status_msg [format [get_text "Plugin: %s loaded..."] $plug]
    }

    set setup [info commands ::${nsp}::setup]
    if {$setup ne ""} {return 1}
    
    set menu_entry [$ObexTool::mainframe getmenu Setup]
    if {$menu_entry ne ""} {
      $menu_entry add command -label "$nsp-Plugin" -command ::${nsp}::setup
      status_msg [get_text "New setup section added to options menu."]
    }
     
    return 1
  }

  proc is_viewable { type } {
  debug_out "is_viewable $type" 5
    set vtypes [getObexCfg file viewable]
  debug_var vtypes
    if {[lsearch $vtypes $type] == -1} {return 0}
    return 1
  }

  proc view_file { path } {
  debug_out "view_file $path" 5
    status_msg [format [get_text "Downloading file '%s'..."] $path]
    
    set_cursor on
    set name [ObexFile::read_file_tmp $path]
    set_cursor 
    if [string_empty $name] {
      warning [format [get_text "Unable to open file '%s'!"] $name]
      return
    }
    
    set fd [open $name "r"]
    set txt [read $fd]
    close $fd
    file delete $name

    status_msg [format [get_text "Text view of file '%s'..."] $path]
    ObexTool::text_view [format [get_text "File: %s"] $path] $txt
  }

  proc default_action { args } {
    variable lb_entries
  debug_out "default_action $args" 3

    set item $lb_entries([lindex $args 0])
    set path [lindex $item 0]
    set type [lindex $item 1]
    set size [lindex $item 2]

    set editmode [lindex $args 1]

    if [string_equal $type folder] {
      set node [ObexTree::path2node $path]
      ObexTree::select_node "$node"
      $ObexTree::tree itemconfigure "$node" -open 1 
      return
    } 

###
### Is a viewer ord editor program defined?
###
  debug_var editmode 1
  set exe ""
  if ![string_empty $editmode] {
    set exe [getObexCfg $editmode $type]
  }
  if [string_empty $exe] {
    set exe [getObexCfg view $type] 
    if ![string_empty $exe] { set editmode view }
  }
  if [string_empty $exe] {
    set exe [getObexCfg edit $type]
    if ![string_empty $exe] { set editmode edit }
  }
  debug_var editmode 2
  if ![string_empty $exe] {
      switch $editmode {
        edit {set msg [format [get_text "Executing editor '%s'..."] $exe]}
        view {set msg [format [get_text "Executing viewer '%s'..."] $exe]}
      }
  ###
  ### viewer or editor program is defined
  ###
      status_msg [format [get_text "Downloading file '%s'..."] $path] 
      set_cursor on
      set local [ObexFile::read_file_tmp $path]
      set_cursor 
  debug_var local 8
      status_msg $msg ; update
      set cmd "exec $exe $local"

      set ecode [catch "eval $cmd" error]
      if $ecode {
        set emesg "Error in executing: %s %s"
        append emesg "\nError message: %s\nError code: %s"
        warning [format $emesg $exe $local $error $ecode]
      } 

      if [string_equal $editmode "view"] {
  ### viewer program closed - delete file and out...
        set msg [format [get_text "Viewer '%s' finished."] $exe]
        status_msg $msg 
        file delete $local
        return
      }

  ### edit program closed - let's upload changed file ...
      set msg [get_text "Do you want to upload the modified file?"]
      if ![ask_yes_no [get_text "Question"] $msg] {
        file delete $local
        return
      }

      status_msg [format \
        [get_text "Upoading file '%s' to '%s'..."] $local $path]
      set_cursor on
      ObexFile::obexftp rm $path 
      ObexFile::write_file_tmp $local $path
      ObexTree::refresh_list [format [get_text "File '%s' stored"] $path]
      set_cursor
      return
    } 

  ###
  ### Check if it's a "viewable" file...
  ###
    if ![is_viewable $type] {
      set ftype [ObexTool::file_type $type]
      set msg [get_text "No default action specified for file type '%s'!"]
      warning [format $msg $ftype]
      return
    }

    view_file $path
  }

  proc sel_single { type args } {
    variable dblclick
    variable coord_last
    debug_out "sel_single $type $args" 3
    
    if $dblclick {
      do_select clear 
      do_select set 
      set dblclick 0
      return
    }

    set x [lindex $args 0]
    set y [lindex $args 1]
    set coord_last [list $x $y]

    do_select set $args
    set dblclick 0
  }

  proc double_click { type args } {
    variable dblclick 1
    variable lb_entries
  debug_out "double_click $type $args" 3

    set name [do_select get $args]
    if [string_empty $name] return
  debug_out "tags of selected: $name"

    set entr $lb_entries($name)
    set path [lindex $entr 0]
    set type [lindex $entr 1]
    if [string_equal $type folder] {
      default_action $name
      set dblclick 0
      return
    } 
  ###
  ### If a handler exists (or can be loaded) - lets execute ist...
  ###
    set ns [string toupper $type]
    if [load_plugin $ns] {
      eval "${ns}::default_handler $entr"
      set dblclick 0
      return
    }

  ### otherwise the "normal action" is performed...
    default_action $name
    set dblclick 0
  }

  proc new_listbox { } {
    variable list_mode listb
    variable scroll_window 
    variable list_widget
  debug_out " ObexList::new_listbox" 3

    if [winfo exists $scroll_window.list] {
      set list_widget $scroll_window.list
    } else {
      set list_widget [ListBox::create $scroll_window.list \
                     -background white -relief flat -borderwidth 0 -redraw 1 \
                     -dragevent 1 -dropenabled 0 -dragenabled 1 \
                     -width 20 -highlightthickness 0 -multicolumn true \
                     -highlightbackground blue -selectmode single\
                     -deltay [getObexCfg list lineheight] \
                     -droptypes {
                       TREE_NODE    {copy {} move {} link {}}
                       LISTBOX_ITEM {copy {} move {} link {}}}]
      $scroll_window setwidget $list_widget
    }

  ### Define bindings  
    set c $list_widget
    set left  [getObexCfg mouse_button left]
    set right [getObexCfg mouse_button right]
    bind $c      <ButtonPress-$right>       "ObexList::context_menu list"
    $c bindText  <ButtonPress-$left>        "ObexList::sel_single list"
    $c bindText  <ButtonPress-$right>       "ObexList::context_menu list"
    $c bindText  <Double-ButtonPress-$left> "ObexList::double_click list"
    $c bindImage <Double-ButtonPress-$left> "ObexList::double_click list"

    return $list_mode
  }

  proc new_detail { } {
    variable list_mode detail
    variable scroll_window 
    variable list_widget
  debug_out " ObexList::new_detail" 3

    if [winfo exists $scroll_window.detail] {
      set list_widget $scroll_window.detail
    } else {
      set list_widget [tablelist::tablelist $scroll_window.detail \
                 -background white -font [getObexCfg list font]\
                 -labelrelief flat -showseparators 1 -stretch all\
                 -selectmode single -height 10 -width 100\
                 -columns [list 0 [get_text "Name"] \
                                0 [get_text "File type"] \
                                0 [get_text "Size"] right \
                                0 [get_text "Date"] right \
                                0 [get_text "U-perm"] \
                                0 [get_text "G-perm"]] \
                 -labelcommand tablelist::sortByColumn]
    }
    $scroll_window setwidget $list_widget

  ### Define bindings  
    set c [$list_widget bodypath]
    set left  [getObexCfg mouse_button left]
    set right [getObexCfg mouse_button right]
    bind $c <ButtonPress-$left>   "after 100 ObexList::sel_single detail"
    bind $c <Double-Button-$left> "after 100 ObexList::double_click detail"
    bind $c <ButtonPress-$right>  "ObexList::context_menu detail %W %y"
    return $list_mode
  }

  proc new_iconbox { } {
    variable list_mode icons
    variable scroll_window
    variable list_widget
    variable scrollreg
  debug_out " ObexList::new_iconbox" 3

    if [winfo exists $scroll_window.icons] {
      set list_widget $scroll_window.icons 
      set w [winfo width  $scroll_window.icons]
      set h [winfo height $scroll_window.icons]
      set scrollreg [list 0 0 $w $h]
      $list_widget configure -scrollregion $scrollreg
    } else {
      set list_widget [canvas $scroll_window.icons \
        -scrollregion $scrollreg \
        -relief sunken -borderwidth 2 -bg white ]
    }
    $scroll_window setwidget $list_widget

  # DragSite::include ObexList "LISTBOX_ITEM" 1
  # DragSite::register $list_widget -dragevent 1 \
  #                                  -draginitcmd ObexList::iconic \
  #                                  -dragendcmd ObexList::iconec

  ### Define bindings  
    set c $list_widget
    set left  [getObexCfg mouse_button left]
    set right [getObexCfg mouse_button right]
    bind $c <ButtonPress-$right>       "ObexList::context_menu icons %x %y"
    bind $c <ButtonPress-$left>        "ObexList::sel_single icons %x %y"
    bind $c <Double-ButtonPress-$left> "ObexList::double_click icons %x %y"
    return $list_mode
  }

  proc iconic { args } {
    debug_out "ObexList::iconic $args"
    set i 0
    foreach arg $args { debug_out "arg [incr i]: $arg" 5 }
  }
  proc iconec { args } {
    debug_out "ObexList::iconec $args"
    set i 0
    foreach arg $args { debug_out "arg [incr i]: $arg" 5 }
  }

  proc file_get { args } {
    variable lb_entries
    variable download_directory
  debug_out "file_get $args" 3

    set name [do_select get]
  debug_var name 5
    if [string_empty $name] {set name [lindex $args 0]}
  debug_var name 5
    if [string_empty $name] {
      set msg [get_text "No file selected for downloading"]
      status_msg $msg
      warning $msg
      return
    }

    debug_out "$lb_entries($name)" 8

    set path [lindex $lb_entries($name) 0]
    set type [lindex $lb_entries($name) 1]
    set size [lindex $lb_entries($name) 2]
    set date [lindex $lb_entries($name) 3]

    if [string_equal $type "folder"] {
      set msg [get_text "Only files can be downloaded"]
      status_msg $msg
      warning $msg
      return
    }

    if [string_empty $download_directory] {
      set tmp_dir [getObexCfg download_dir define]
      if {![string_empty $tmp_dir]} {
        set download_directory $tmp_dir
      }
    }
    debug_var download_directory 3

    set sel_dir [getObexCfg download_dir select]
    debug_var sel_dir 3
    if {$sel_dir == 1} {
      set new_dir [select_dirname $download_directory]
      if {$new_dir eq ""} return
      set download_directory $new_dir
    }


    if {![string_empty $download_directory]} {
      set last_dir [pwd]
      set res [catch {cd $download_directory} err]
      if $res {
        set emesg [get_text "Unable to switch to directory: %s\n%s (%s)"]
        warning [format $emesg $err $res]
        return
      }
    }

    if [file exists $name] {
      set query [get_text "File member '%s' already exists!"]
      append query [get_text "\nDo you want to overwrite the existing file?"]
      if ![ask_yes_no [get_text "Name collision"] [format $query $name]] {
        status_msg [get_text "File download cancelled!"]
        if [info exists last_dir] {catch {cd $last_dir}}
        return
      }
    }

    status_msg [format [get_text "Downloading file '%s'..."] $path]
    set_cursor on
    ObexFile::obexftp gf $path 
    set_cursor 

    if ![file exists $name] {
      set msg [get_text "Error in downloading file '%s'!"]
      status_msg [format $msg $path]
      set msg [get_text "Download of file '%s' failed!"]
      no_permission [format $msg $path]
      if [info exists last_dir] {catch {cd $last_dir}}
      return
    }

    set fdate $ObexFile::file_date($name)
    if {[getObexCfg preserve filedate] && $fdate ne ""} {
      set dstr ""       ; # 012345678901234
      debug_var fdate 4 ; # 20090102T104518
      append dstr [string range $fdate  0  3] "-" \
                  [string range $fdate  4  5] "-" \
                  [string range $fdate  6  7] " " \
                  [string range $fdate  9 10] ":" \
                  [string range $fdate 11 12] ":" \
                  [string range $fdate 13 14] 
      set tstmp [clock scan $dstr]
      catch {file mtime $name $tstmp}
    }

    set msg [get_text "File '%s' saved as '%s'"]
    if {![string_empty $download_directory]} {
      append msg [get_text " in directory '%s'"]
      status_msg [format $msg $path $name $download_directory]
    } else {
      status_msg [format $msg $path $name]
    }

    if [info exists last_dir] {catch {cd $last_dir}}
  }

  proc do_select { cmd args } {
    variable list_mode
    variable list_widget
    variable lb_entries
    debug_out "do_select $cmd $args" 3

    set names {}
    switch $cmd {
      get {
        switch $list_mode {
          listb {
            if ![string_empty [set sel [$list_widget selection get]]] {
              set names [file tail [ObexTree::node2path $sel]]
  debug_out "$list_mode:$sel=>$names" 3
            }
          }
          icons {
            if ![string_empty [set sel [$list_widget find withtag selection]]] {
              set names [lindex [$list_widget gettags $sel] 0]
  debug_out "$list_mode:$sel=>$names" 3
            }
          }
          detail {
            if ![string_empty [set sel [$list_widget curselection]]] {
              set names [lindex [$list_widget get $sel] 0]
  debug_out "$list_mode:$sel=>$names" 3
            }
          }
          default {
            internal_error "selection: invalid list mode 1: '$list_mode'!"
          }
        }
      }
      set {
        switch $list_mode {
          listb {
            set node [lindex $args 0]
            $list_widget selection set $node
          }
          icons {
            set rect [$list_widget find withtag selection]
            if ![string_empty $rect] { $list_widget delete $rect }

            set name [lindex [$list_widget gettags current] 0]
            set selt [lindex [$list_widget find withtag $name] 0]
            if ![string_empty $selt] {
              set bbox [eval "$list_widget bbox $selt"]
              if ![string_empty $bbox] {
                set tags [list $name selection]
                eval "$list_widget create rectangle $bbox -tags \"$tags\""
              }
            }

          }
          detail {
            if ![string_empty [set sel [$list_widget curselection]]] {
              $list_widget selection set $sel
            }
          }
          default {
            internal_error "selection: invalid list mode 2: '$type'!"
          }
        }
      }
      clear {
        switch $list_mode {
          listb {
            $list_widget selection clear
          }
          icons {
            set rect [$list_widget find withtag selection]
            if ![string_empty $rect] { $list_widget delete $rect }
          }
          detail {
            if ![string_empty [set sel [$list_widget curselection]]] {
              $list_widget selection clear $sel
            }
          }
          default {
            internal_error "selection: invalid list mode 3: '$list_mode'!"
          }
        }
      }
      default {internal_error "selection: selection command: '$cmd'!"}
    }

    debug_var names 3
    return $names
  }

  proc menu_edit { action args } {
    variable copy_selection
    variable lb_entries
    variable selection
    debug_out "menu_edit $action $args" 3

    set path {}
    set name [do_select get]
    if [string_empty $name] { set name [lindex $args 0] }
    
    if ![string_empty $name] {
      set path [lindex $lb_entries($name) 0]
      set size [lindex $lb_entries($name) 2]
    }

  ###
  ### Action is 'cut' - just save the $path
  ###
    if [string_equal $action "cut"]  {
      if [string_empty $path] {
        status_msg [get_text "Nothing selected for cutting!"]
        return
      }
      if [string_equal [lindex $lb_entries($name) 1] "folder"] {
        warning [get_text "Folder can only be deleted!"]
        return
      } 
      array unset copy_selection
      set copy_selection(cut) $path
      status_msg [format [get_text "Cutting '%s'..."] $path]
      return
    }

  ###
  ### Action is 'copy' - just save the $path
  ###
    if [string_equal $action "copy"]  {
      if [string_empty $path] {
        status_msg [get_text "Nothing selected for copying!"]
        return
      }
      if [string_equal [lindex $lb_entries($name) 1] "folder"] {
        warning [get_text "Folder can only be deleted!"]
        return
      } 
      array unset copy_selection
      set copy_selection(copy) $path
      status_msg [format [get_text "Copying '%s'..."] $path]
      return
    }

  ###
  ### Now action MUST be 'paste'...
  ###
    if ![string_equal $action "paste"]  {
      internal_error "menu_edit: invalid action '$action'!" 
    }
  debug_var copy_selection(copy) 6
  debug_var copy_selection(cut)  6

    if [info exists copy_selection(copy)] {
      set path $copy_selection(copy)
    } elseif [info exists copy_selection(cut)] {
      set path $copy_selection(cut)
    } else {
      status_msg [get_text "Nothing selected for pasting!"]
      return
    }

  ###
  ### Valid $copy_selection(...) found...
  ###
    set node [$ObexTree::tree selection get]
    if [string_empty $node] {
      internal_error "menu_edit: cannot extract current folder name!"
    }
    set dir_name [ObexTree::node2path $node]

  ###
  ### Get current folder name and set $action...
  ###
  debug_var path 3
    set name [file tail $path]
    set destname [make_path $dir_name $name]

  ###
  ### Folder won't be overwritten
  ###
    set_cursor on
    if [ObexFile::path_exists dir $destname] {
      set msg [get_text "Folder '%s' already exists!\nCannot paste file."]
      warning [format $msg $destname]
      set_cursor 
      return 
    }

  ###
  ### Loop until a none-existing file is given
  ###
    set file_exists [ObexFile::path_exists file $destname]
    debug_var file_exists 6
    while {$file_exists != 0} {

      set_cursor 
  debug_out "file_exists=$file_exists" 3
      set m1 [get_text "File '%s' already exists in folder '%s'!"] 
      set m2 [get_text "You must specify a new file name or cancel the command."]
      set name [filename_dlg [get_text "New file name:"] $name \
                  [format $m1 $name $dir_name] $m2] 
      if [string_empty $name] return
      set_cursor on

      set destname [make_path $dir_name $name]
      set file_exists [ObexFile::path_exists any $destname]
    }

  ###
  ### Let's move or copy the file
  ###
    status_msg [format [get_text "Downloading file '%s'..."] $path]
    set local [ObexFile::read_file_tmp $path]
    if [string_empty $local] {
      set_cursor
      return
    }
    status_msg [format [get_text "Uploading file '%s'..."] $local]
    ObexFile::write_file_tmp $local $destname

    if [info exists copy_selection(cut)] {
      set src_name $path
      ObexFile::obexftp rm $src_name 
    }
    set_cursor
    
  ###
  ### (Un)success message
  ###
    if [ObexFile::path_exists file $destname] {
      ObexTree::refresh_list [format [get_text "File '%s' pasted"] $destname]
    } else {
      set msg [format [get_text "File paste of '%s' failed!"] $destname]
      status_msg $msg
      no_permission $msg
    }

    array unset copy_selection
  }

  proc show_props { } {
    variable lb_entries
  debug_out "ObexList::show_props"
    set name [do_select get]
    if [string_empty $name] {
      ObexTree::show_props
    } else {
      debug_var name 3
      set path [lindex $lb_entries($name) 0]
      set txt [help_text $name]
      status_msg [format [get_text "Properties for File '%s'..."] $path]
      ObexTool::text_view [format [get_text "File properties: %s"] $name] $txt
   } 
  }

  proc update_list { new_mode } {
    variable lb_entries
    variable list_mode
    variable list_widget
    variable icon_actpos
    variable scroll_window
    variable scrollreg [list 0 0 1 1]
  debug_out "ObexList::update_list $new_mode"
  debug_var list_mode
  debug_var list_widget

    set old_mode $list_mode
    set old_widget $list_widget

  #  eval $list_widget delete [$list_widget items] end ### BWidget 1.4
    if [winfo exists $old_widget] {
      switch $old_mode {
        listb  { set items [$old_widget items 0 end] 
                 eval $old_widget delete $items 
        }
         icons  { eval $old_widget delete all }
        detail { eval $old_widget delete 0 end }
      }
      destroy [winfo children $scroll_window]
      pack forget $old_widget
    }

    set list_mode $new_mode
    switch $list_mode {
      listb { 
        if [winfo exists $scroll_window.list] {
          set list_widget $scroll_window.list
        } else {
          new_listbox 
        }
        set icon_font [getObexCfg list font]
        foreach item $ObexTree::dir_list {
          set node [ObexTree::path2node $item]
          set name [file tail $item]
          set type [lindex $lb_entries($name) 1]
          set icon [ObexTool::filetype_icon $type]
# debug_out "Bitmap::get $icon" 4
          $list_widget insert end $node -font $icon_font \
                       -text $name -image [Bitmap::get $icon]
        }
        $scroll_window setwidget $list_widget
      }
      icons { 
        if [winfo exists $scroll_window.icons] {
          set list_widget $scroll_window.icons
        } else {
          new_iconbox 
        }
        update
        set icon_actpos(x) [getObexCfg icon xstart]
        set icon_actpos(y) [getObexCfg icon ystart]
        set winw [winfo width $list_widget]
        foreach item $ObexTree::dir_list { insert_icon $item $winw }
        $scroll_window setwidget $list_widget
        update_scrollreg
#        set icon_font [getObexCfg list font]
#        $list_widget insert end $node -font $icon_font 
#        $scroll_window setwidget $list_widget
      }
      detail { 
        if [winfo exists $scroll_window.detail] {
          set list_widget $scroll_window.detail
        } else {
          new_detail 
        }
        foreach item $ObexTree::dir_list {
          set name [file tail $item]
          set type [lindex $lb_entries($name) 1]
          set size [lindex $lb_entries($name) 2]
          set date [lindex $lb_entries($name) 3]
          set uprm [lindex $lb_entries($name) 4]
          set gprm [lindex $lb_entries($name) 5]
          set ftyp [ObexTool::file_type $type]

          set icon [ObexTool::filetype_icon $type]
          $list_widget insert end [list $name $ftyp $size $date $uprm $gprm]
          $list_widget cellconfigure end,0 -image  [Bitmap::get $icon]
        }
        update
        $scroll_window setwidget $list_widget
      }
      default { internal_error "Invalid list display mode: '$new_mode'!" }
    }
    set ObexTool::list_mode $new_mode
  }

  proc icon_drag { w x y } {
    variable coord_last
    variable icon_selected

    if [info exists icon_selected] { 
      set tgs $icon_selected 
    } else {
      set tag [$w gettags current]
      set tgs [$w find withtag [lindex $tag 0]]
    }
    set ox [$w canvasx [lindex $coord_last 0]]
    set oy [$w canvasy [lindex $coord_last 1]]
    set nx [$w canvasx $x]
    set ny [$w canvasy $y]
    foreach ent $tgs { $w move $ent [expr $nx-$ox] [expr $ny-$oy] }
    set coord_last [list $x $y]
  }

  proc update_scrollreg {} {
    variable scroll_window
    variable list_widget
    variable scrollreg
    foreach item [$list_widget find all] {
      set bbox [$list_widget bbox $item]
      if {[lindex $bbox 0]<[lindex $scrollreg 0]} {
        set scrollreg [lreplace $scrollreg 0 0 [lindex $bbox 0]]
      }
      if {[lindex $bbox 1]<[lindex $scrollreg 1]} {
        set scrollreg [lreplace $scrollreg 1 1 [lindex $bbox 1]]
      }
      if {[lindex $bbox 2]>[lindex $scrollreg 2]} {
        set scrollreg [lreplace $scrollreg 2 2 [lindex $bbox 2]]
      }
      if {[lindex $bbox 3]>[lindex $scrollreg 3]} {
        set scrollreg [lreplace $scrollreg 3 3 [lindex $bbox 3]]
      }
    }
    $scroll_window.icons config -scrollregion $scrollreg
  }

  proc icon_enter { w name args } {
  # debug_out "icon_enter: register \$w $name $args"
    variable restore_cmd

    set help [help_text "$name"]
    DynamicHelp::register $w balloon $help

    if ![llength $args] {
      set restore_cmd {}
      return
    }

    set img [lindex [$w itemconfigure current -image] 4]
    set restore_cmd [list $w itemconfigure current -image $img]
  # debug_var restore_cmd
    set nimg [lindex $args 0]
  # debug_var nimg
    $w itemconfigure current -image $nimg
  }

  proc icon_leave { w } {
    variable restore_cmd
  # debug_out "icon_leave: unregister \$w balloon"
    eval $restore_cmd
    DynamicHelp::register $w balloon
    update
    destroy $DynamicHelp::_top
  }

  proc insert_icon { item winw } {
    variable lb_entries
    variable icon_actpos
    variable list_widget
    variable scroll_window
  debug_out "insert_icon $item $winw" 9

    set name [file tail $item]
    set type [lindex $lb_entries($name) 1]
    set alti ""
    if [string equal $type "folder"] {
      set icon [Bitmap::get [ObexTool::filetype_icon folder   large]]
      set alti [Bitmap::get [ObexTool::filetype_icon openfold large]]
    } else {
      set icon [Bitmap::get [ObexTool::filetype_icon $type large]]
    }

    set tgs [list $name icons]
    set icon_font [getObexCfg icon font]

  ### draw icon  
    set w $list_widget
    set itm [$w create image $icon_actpos(x) $icon_actpos(y) \
                -tags $tgs -image $icon] 
    DynamicHelp::register $w balloon [help_text $name]
    $w bind $itm <Enter> "ObexList::icon_enter $w {$name} $alti"
    $w bind $itm <Leave> "ObexList::icon_leave $w"
    DragSite::register $w -dragevent 1 \
                          -draginitcmd ObexList::iconic \
                          -dragendcmd ObexList::iconec
  #  DragSite::setdrag $scroll_window $w\
  #            ObexList::iconic \
  #            ObexList::iconec

  ### get size of icon
    set itms [$list_widget find withtag $name]
    set bbox [eval "$list_widget bbox $itms"]

  ### text position starts at lower bbox value
    set y2 [lindex $bbox 3]

  ### calculate line wrapping
    set max_len [getObexCfg icon txt_maxlen]
    set lines [wrap_line $name $max_len] 
    
  ### draw all text lines  
    set line_height [getObexCfg icon lineheight]
    foreach lin $lines {
      $list_widget create text $icon_actpos(x) $y2 -font $icon_font \
                   -tags [list $name icons] -text $lin -anchor n
      incr y2 $line_height
    }

  ### check if boundingbox exceeds window width
    set itms [$list_widget find withtag $name]
    set bbox [eval "$list_widget bbox $itms"]

  ### ... if yes move last icon to beginning of next line
    if {[lindex $bbox 2]>$winw} {
      set x0 [getObexCfg icon xstart]
      set dy [getObexCfg icon y_offs]
      set dx [expr $x0-$icon_actpos(x)]
      $list_widget move $name $dx $dy
  ### and calculate new icon position
      set icon_actpos(x) [expr $x0+[getObexCfg icon x_offs]]
      incr icon_actpos(y) [getObexCfg icon y_offs]
    } else {
      incr icon_actpos(x) [getObexCfg icon x_offs]
    }
  }
}
