#!/usr/bin/tclsh # Part of MCU 8051 IDE ( http://mcu8051ide.sf.net ) ############################################################################ # Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 by Martin Ošmera # # martin.osmera@gmail.com # # # # This program is free software; you can redistribute it and#or modify # # it under the terms of the GNU General Public License as published by # # the Free Software Foundation; either version 2 of the License, or # # (at your option) any later version. # # # # This program is distributed in the hope that it will be useful, # # but WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # # GNU General Public License for more details. # # # # You should have received a copy of the GNU General Public License # # along with this program; if not, write to the # # Free Software Foundation, Inc., # # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################ # >>> File inclusion guard if { ! [ info exists _FSD_TCL ] } { set _FSD_TCL _ # <<< File inclusion guard # -------------------------------------------------------------------------- # DESCRIPTION # This class provides file selection dialog # Usage: # KIFSD::FSD ;# Create dialog object # setokcmd {set filename [ get]} ;# Set command for Ok button # activate ;# Show up the dialog # # Constructor options: # -title String = {} ;# Dialog title # -initialfile String = {} ;# Initial file # -directory String = {~} ;# Initiali directory # -multiple Bool = 0 ;# Allow selection of multiple files (get will return list instead of string) # -filetypes List = {{All} {*}} ;# { {{Some string} {GLOB}} ... } # -defaultmask Int = 0 ;# Number of detault mask (see -filetypes) (1st is zero) # -modal Bool = 1 ;# Create as modal window # -doubleclick Bool = 0 ;# Use double click to open folder instead of single click # -autoclose Bool = 1 ;# Close dialog after pressure of Ok button # -master Widget = . ;# Master window (wm transient $master) # -fileson Bool = 1 ;# 1 == Select file(s); 0 == Select directory/ies # # Other public methods: # set_bookmark_change_command Command ;# Set command to invoke when bookmarks changes # deactivate ;# Deactivate the dialog # close_dialog ;# Close dialog window but keep object alive # get_config_array -> List ;# Get dialog configuration array for proc. load_config_array # load_config_array List ;# Load dialog configuration array # get_window_name -> Widget ;# Get path to dialog window # -------------------------------------------------------------------------- itcl::class KIFSD::FSD { common bookmark_change_command {} ;# Command to invoke on bokmark change # Font for quick navigation panel common quick_nav_panel_font [font create \ -family {helvetica} \ -size [expr {int(-12 * $::font_size_factor)}] \ -weight bold \ ] # Font for files listbox in mode (Short view) common listbox_font_short [font create \ -family {helvetica} \ -size [expr {int(-14 * $::font_size_factor)}] \ -weight normal \ ] # Font for files listbox in mode (Detailed view) and directories listbox common listbox_font_detailed [font create \ -family $::DEFAULT_FIXED_FONT \ -size [expr {int(-12 * $::font_size_factor)}] \ -weight normal \ ] # Font for listbox header common listbox_header_font [font create \ -family $::DEFAULT_FIXED_FONT \ -size [expr {int(-12 * $::font_size_factor)}] \ -weight bold \ ] ## Values given by constructor arguments private variable option_title {Select file} ;# Dialog title private variable option_filetypes {{All} {*}} ;# File types if {!$::MICROSOFT_WINDOWS} { variable option_directory ${::env(HOME)} ;# Initial directory } else { variable option_directory ${::env(USERPROFILE)} ;# Initial directory } private variable option_master {.} ;# Window master private variable option_fileson {1} ;# 1 == Files on (select file); 0 == Files off (select directory) private variable option_doubleclick {0} ;# Use doble click instead of single clicks private variable option_modal {1} ;# Open dialog windown as modal window private variable option_initialfile {} ;# Initial file private variable option_multiple {0} ;# Allow mulstiple selection private variable option_defaultmask {0} ;# Index of default mask in $option_filetypes private variable option_autoclose {1} ;# 1 == Close dialog after press of Ok button private variable bookmark_edit_listbox {} ;# Widget: ListBox in bookmarks editor private variable bookmark_menu {} ;# Widget: Bookmarks menu private variable config_menu {} ;# Widget: Configuration menu private variable listbox_font {} ;# Font: Current font for files listbox private variable current_directory {} ;# String: Current directory private variable back_history {} ;# List: Backward history private variable forward_history {} ;# List: Forward hitory private variable ok_command {} ;# String: Ok command private variable current_mask {*} ;# GLOB: Current fileter mask private variable item_menu_request 0 ;# Bool: Item popup menu request private variable current_item {} ;# String: ID of currenly selected item private variable current_item_index 0 ;# Int: Index of currently selected item private variable cur_listbox {} ;# String currently selected listbox {dir} or {file} private variable dialog_loaded 0 ;# Bool: Dialog is completely loaded private variable win ;# Widget: Dialog window private variable ok_button ;# Widget: Button "Ok" private variable location_cb ;# Widget: Location ComboBox private variable filter_cb ;# Widget: Filter ComboBox private variable dir_combobox ;# Widget: Directory ComboBox private variable toolbar ;# Widget: Frame containing toolbar private variable quick_access_bar ;# Widget: Quick access bar ListBox private variable dir_listbox_scrollbar {} ;# Widget: Directory ListBox scrollbar private variable dir_listbox {} ;# Widget: Directory ListBox private variable main_paned_window ;# Widget: Paned window for quick access bar and other LBs private variable leftframe ;# Widget: Frame contaning quick access bar and its scrollbar private variable rightframe ;# Widget: Frame containing directory & file ListBoxes private variable right_top_right_frame ;# Widget: files ListBox private variable right_top_left_frame ;# Widget: Frame for files ListBox private variable right_top_frame ;# Widget: Frame for $right_paned_window private variable right_paned_window ;# Widget: Paned window for directories ListBox and files ListBox private variable file_listbox ;# Widget: Files ListBox private variable file_listbox_header ;# Widget: Header for files ListBox private variable file_listbox_frame ;# Widget: Frame for $file_listbox_header and $file_listbox private variable file_listbox_vscrollbar {} ;# Widget: Vertical scollbar for files ListBox private variable file_listbox_hscrollbar {} ;# Widget: Hotizontal scollbar for files ListBox private variable right_top_right_top_frame ;# Widget: Frame for $file_listbox_frame and scrollbars proc static_reload {object args} { catch [list $object reload] } ## Dialog constructor # For complite list of possible arguments see desctiption above constructor args { # Configure local ttk styles ttk::style configure FSD_RedBg.TCombobox -fieldbackground {#FFDDDD} ttk::style configure FSD_RedBg.TEntry -fieldbackground {#FFDDDD} ## Parse given arguments and set appropriate object variables set arglen [llength $args] set arg {} for {set i 0} {$i < $arglen} {incr i} { set arg [lindex $args $i] switch -- $arg { -modal { incr i set option_modal [lindex $args $i] if {![string is boolean -strict $option_modal]} { error "-modal must have value either 0 or 1" } } -doubleclick { incr i set option_doubleclick [lindex $args $i] if {![string is boolean -strict $option_doubleclick]} { error "-doubleclick must have value either 0 or 1" } } -autoclose { incr i set option_autoclose [lindex $args $i] if {![string is boolean -strict $option_autoclose]} { error "-autoclose must have value either 0 or 1" } } -initialfile { incr i set option_initialfile [lindex $args $i] } -multiple { incr i set option_multiple [lindex $args $i] if {![string is boolean -strict $option_multiple]} { error "-multiple must have value either 0 or 1" } } -defaultmask { incr i set option_defaultmask [lindex $args $i] if {![string is integer -strict $option_defaultmask]} { error "-defaultmask must be an integer" } } -title { incr i set option_title [lindex $args $i] } -filetypes { incr i set option_filetypes [lindex $args $i] } -directory { incr i set option_directory [lindex $args $i] } -master { incr i set option_master [lindex $args $i] } -fileson { incr i set option_fileson [lindex $args $i] if {![string is boolean -strict $option_modal]} { error "-fileson must have value either 0 or 1" } } default { error "Option '$arg' is not valid" } } } set args {} set current_directory [file normalize $option_directory] # Cretate dialog window create_dialog # Initalize window key shortcuts create_shortcuts # Finalize set dialog_loaded 1 } ## Destrurtor destructor { catch { # Save position of right paned window sash if {[winfo ismapped $right_paned_window]} { set ::KIFSD::FSD::config(right_PW_size) \ [lindex [$right_paned_window sash coord 0] 0] } # Save position of main paned window sash if {[winfo ismapped $main_paned_window]} { set ::KIFSD::FSD::config(main_PW_size) \ [lindex [$main_paned_window sash coord 0] 0] } # Save window geometry set ::KIFSD::FSD::config(win_geometry) [wm geometry $win] } # Destroy dialog window grab release $win destroy $win } ## Create dialog GUI elements # @return void private method create_dialog {} { # Determinate window name (path) set win_base {} if {$option_master != {.}} { set win_base $option_master } append win_base .[string tolower [regsub -all {:} $this {}]] set win $win_base set i 0 while [winfo exists $win] { set win $win_base append win $i incr i } # Create and configure dialog window toplevel $win -bg ${::COMMON_BG_COLOR} wm iconphoto $win ::ICONS::16::fileopen wm withdraw $win wm title $win $option_title wm minsize $win 540 290 wm protocol $win WM_DELETE_WINDOW "catch {itcl::delete object $this}" wm transient $win $option_master wm geometry $win ${::KIFSD::FSD::config(win_geometry)} wm resizable $win 0 0 raise $win update if {$option_modal} { catch { grab $win } } create_popup_menus ;# Create popup menus set topframe [frame $win.topframe] ;# Create frame above ListBoxes set toolbar [frame $topframe.toobar] ;# Create toolbar frame create_tool_bar ;# Create toolbar # Create directory ComboBox set dir_combobox [ttk::combobox $topframe.dir_cb \ -values {} \ -exportselection 0 \ -validate all \ -validatecommand "::KIFSD::FSD::dir_validate $topframe.dir_cb %W %P" \ ] bind $dir_combobox <> [list $this dir_cb_modify] bind $dir_combobox [list $this dir_cb_modify] bind $dir_combobox [list $this dir_cb_modify] DynamicHelp::add $dir_combobox -text [mc "Current directory"] pack $dir_combobox -side right -expand 1 -fill x -padx 5 # Create main paned window and some frames set mainframe [frame $win.mainframe] set main_paned_window [panedwindow $mainframe.main_paned_window \ -orient horizontal -opaqueresize 1 -sashwidth 2 \ -showhandle 0 -sashrelief flat \ ] set leftframe [frame $mainframe.leftframe] set rightframe [frame $mainframe.rightframe] # Create quick access bar set quick_access_bar [ListBox $leftframe.quick_access_bar \ -selectfill 1 -selectbackground white -bd 1 -padx 30 -width 15 \ -selectmode single -highlightthickness 0 -bg white -deltay 30 \ -selectforeground black -highlightcolor {#BBBBFF} \ ] refresh_quick_access_bar $quick_access_bar bindText [list $this quick_access_bar_item_menu %X %Y ] $quick_access_bar bindImage [list $this quick_access_bar_item_menu %X %Y ] $quick_access_bar bindText [list $this quick_access_bar_doubleclick ] $quick_access_bar bindImage [list $this quick_access_bar_doubleclick ] bind $quick_access_bar <> [list $this quick_access_bar_select ] if {[winfo exists $quick_access_bar.c]} { bind $quick_access_bar.c {%W yview scroll +5 units; break} bind $quick_access_bar.c {%W yview scroll -5 units; break} bind $quick_access_bar.c [list $this quick_access_bar_menu %X %Y ] } pack $quick_access_bar -fill both -expand 1 # Create right paned window set right_top_frame [frame $rightframe.topframe] set right_bottom_frame [frame $rightframe.bottomframe] set right_paned_window [panedwindow $right_top_frame.right_paned_window \ -orient horizontal -opaqueresize 1 -sashwidth 2 \ -showhandle 0 -sashrelief flat \ ] set right_top_left_frame [frame $win.left_frame] set right_top_right_frame [frame $win.right_frame] # Create directories ListBox if {$option_fileson} { set dir_listbox [ListBox $right_top_left_frame.dir_listbox \ -bd 1 -padx 19 -selectfill 1 -width 1 -highlightcolor {#BBBBFF} \ -selectmode single -highlightthickness 0 -bg white -deltay 18 \ -yscrollcommand "$this dir_listbox_scroll" \ ] set dir_listbox_scrollbar [ttk::scrollbar \ $right_top_left_frame.scrollbar \ -orient vertical -command "$dir_listbox yview" \ ] $dir_listbox bindText [list $this dir_listbox_doubleclick ] $dir_listbox bindImage [list $this dir_listbox_doubleclick ] $dir_listbox bindText [list $this dir_listbox_item_menu %X %Y ] $dir_listbox bindImage [list $this dir_listbox_item_menu %X %Y ] bind $dir_listbox <> [list $this dir_listbox_select ] if {[winfo exists $dir_listbox.c]} { bind $dir_listbox.c {%W yview scroll +5 units; break} bind $dir_listbox.c {%W yview scroll -5 units; break} bind $dir_listbox.c [list $this dir_listbox_menu %X %Y ] } pack $dir_listbox -side left -fill both -expand 1 } # Create files ListBox if {$option_multiple} { set selmode {multiple} } else { set selmode {single} } set right_top_right_top_frame [frame $right_top_right_frame.right_top_right_top_frame] set file_listbox_frame [frame $right_top_right_top_frame.file_listbox_frame] set file_listbox_header [text $file_listbox_frame.text \ -width 1 -height 1 -takefocus 0 -bg white \ -font $listbox_header_font -bd 1 -relief sunken \ -cursor left_ptr -wrap none \ ] $file_listbox_header delete 1.0 end if {!$::MICROSOFT_WINDOWS} { $file_listbox_header insert end \ [mc " Name Size Rights Date "] } else { $file_listbox_header insert end \ [mc " Name Size Date "] } bindtags $file_listbox_header $file_listbox_header $file_listbox_header configure -state disabled set file_listbox [ListBox $file_listbox_frame.file_listbox \ -bd 1 \ -padx 17 \ -width 1 \ -height 1 \ -bg white \ -deltay 18 \ -selectfill 1 \ -selectmode $selmode \ -highlightthickness 0 \ -selectbackground {#88AAFF} \ -highlightcolor {#BBBBFF} \ -yscrollcommand "$this file_listbox_vscroll" \ -xscrollcommand "$this file_listbox_hscroll" \ ] pack $file_listbox -fill both -expand 1 if {${::KIFSD::FSD::config(detailed_view)}} { $file_listbox configure -multicolumn 0 set listbox_font $listbox_font_detailed pack $file_listbox_header -before $file_listbox -fill x -expand 0 } else { $file_listbox configure -multicolumn 1 set listbox_font $listbox_font_short } set file_listbox_vscrollbar [ttk::scrollbar \ $right_top_right_top_frame.vscrollbar \ -orient vertical -command "$file_listbox yview" \ ] set file_listbox_hscrollbar [ttk::scrollbar \ $right_top_right_frame.hscrollbar \ -orient horizontal \ -command "$this file_listbox_hscrollbar_cmd" \ ] $file_listbox bindText [list $this file_listbox_doubleclick] $file_listbox bindImage [list $this file_listbox_doubleclick] $file_listbox bindText [list $this file_listbox_item_menu %X %Y] $file_listbox bindImage [list $this file_listbox_item_menu %X %Y] bind $file_listbox <> [list $this file_listbox_select] if {[winfo exists $file_listbox.c]} { bind $file_listbox.c [list $this file_listbox_scroll +5 units] bind $file_listbox.c [list $this file_listbox_scroll -5 units] bind $file_listbox.c [list $this file_listbox_menu %X %Y] } pack $file_listbox_frame -fill both -expand 1 -side left pack $right_top_right_top_frame -fill both -expand 1 -side top pack $right_top_frame -side top -fill both -expand 1 # Create Location Label+ComboBox and Filter Label+ComboBox grid [label $right_bottom_frame.location_label \ -text [mc "Location:"] \ ] -sticky w -column 0 -row 0 grid [label $right_bottom_frame.filter_label \ -text [mc "Filter:"] \ ] -sticky w -column 0 -row 1 set location_cb [ttk::combobox $right_bottom_frame.location_cb \ -values {} \ -exportselection 0 \ ] bind $location_cb <> "$file_listbox selection clear" DynamicHelp::add $location_cb -text [mc "Selected file(s)"] bind $location_cb "$file_listbox selection clear" bind $location_cb [list $this ok] bind $location_cb [list $this ok] set tmp_option_filetypes {} foreach type $option_filetypes { set glob_masks [lindex $type 1] if {[regexp {^\*\.\{\w+(,\w+)*\}$} $glob_masks]} { set glob_masks [split $glob_masks {{,}}] set glob_masks [lreplace $glob_masks 0 0] set glob_masks [lreplace $glob_masks end end] set glob_masks_new [list] foreach ext $glob_masks { lappend glob_masks_new [format "*.%s" $ext] } set glob_masks [join $glob_masks_new {, }] } lappend tmp_option_filetypes "[lindex $type 0] ($glob_masks)" } set filter_cb [ttk::combobox $right_bottom_frame.filter_cb \ -state readonly \ -values $tmp_option_filetypes \ -exportselection 0 \ ] DynamicHelp::add $right_bottom_frame.filter_cb -text [mc "Filter"] set tmp_option_filetypes {} foreach type $option_filetypes { lappend tmp_option_filetypes [lindex $type 1] } set option_filetypes $tmp_option_filetypes $filter_cb current $option_defaultmask set current_mask [lindex $option_filetypes $option_defaultmask] bind $filter_cb <> [list $this filter_cb_modify] grid $location_cb -sticky ew -column 1 -row 0 grid $filter_cb -sticky ew -column 1 -row 1 if {!$option_fileson} { $filter_cb configure -state disabled } # Create buttons "Ok" and "Cancel" set ok_button [ttk::button $right_bottom_frame.ok_button\ -text [mc "Ok"] \ -compound left \ -width 8 \ -image ::ICONS::16::ok \ -command [list $this ok] \ ] grid $ok_button -sticky w -column 2 -row 0 -padx 7 -pady 2 grid [ttk::button $right_bottom_frame.cancel_button \ -text [mc "Cancel"] \ -compound left \ -width 8 \ -image ::ICONS::16::button_cancel \ -command "itcl::delete object $this" \ ] -sticky w -column 2 -row 1 -padx 7 -pady 2 grid columnconfigure $right_bottom_frame 1 -weight 1 pack $right_bottom_frame -side bottom -fill x -expand 0 -anchor w pack $topframe -side top -fill x -padx 12 -pady 10 pack $mainframe -side bottom -fill both -expand 1 -padx 12 # Adjust paned windows to current configuration quick_access_panel_onoff separate_folders_onoff # Finalize $location_cb set $option_initialfile focus -force $location_cb catch { $location_cb.e selection range 0 end } } ## Create dialog toolbar # @return void private method create_tool_bar {} { set si 0 foreach item { {up "Parent folder" {1uparrow} {up}} {back "Back" {1leftarrow} {back}} {forward "Forward" {1rightarrow} {forward}} {reload "Reload" {reload} {reload}} {separator} {newdir "New folder" {folder_new} {newdir}} {separator} {short "Short view" {view_icon} {short_view}} {detail "Detailed view" {view_detailed} {detail_view}} {separator} {bookmark "Bookmarks" {bookmark} {bookmark_menu}} {configure "Configure" {configure} {config_menu}} } \ { # Create separator if {$item == {separator}} { pack [ttk::separator $toolbar.sep$si \ -orient vertical \ ] -side left -padx 4 -fill both -expand 1 incr si continue } # Create button if {[lindex $item 0] == {bookmark}} { set buttonWidget [ttk::menubutton $toolbar.[lindex $item 0] \ -image ::ICONS::22::[lindex $item 2] \ -menu $bookmark_menu \ -style Flat.TMenubutton \ ] } elseif {[lindex $item 0] == {configure}} { set buttonWidget [ttk::menubutton $toolbar.[lindex $item 0] \ -image ::ICONS::22::[lindex $item 2] \ -menu $config_menu \ -style Flat.TMenubutton \ ] } else { set buttonWidget [ttk::button $toolbar.[lindex $item 0] \ -command "$this [lindex $item 3]" \ -style Flat.TButton \ -image ::ICONS::22::[lindex $item 2] \ ] } DynamicHelp::add $buttonWidget -text [mc [lindex $item 1]] # Pack it pack $buttonWidget -side left -padx 2 } # Disable button for manipulating history $toolbar.back configure -state disabled $toolbar.forward configure -state disabled # Pack toolbar frame pack $toolbar -side left -expand 0 -fill none } ## Create dialog popup menus # @return void private method create_popup_menus {} { # Create configuration menu set config_menu [menu $win.config_menu -tearoff 0] ## Create menu: Configuration -> Sorting set sorting_menu [menu $win.config_menu.sorting_menu -tearoff 0] # Entry: "By name" $sorting_menu add radiobutton -label [mc "By name"] \ -variable ::KIFSD::FSD::config(sorting) \ -indicatoron 0 -compound left -image ::ICONS::raoff -selectimage ::ICONS::raon \ -value {name} -underline 3 -command [list $this reload] # Entry: "By date" $sorting_menu add radiobutton -label [mc "By date"] \ -variable ::KIFSD::FSD::config(sorting) \ -indicatoron 0 -compound left -image ::ICONS::raoff -selectimage ::ICONS::raon \ -value {date} -underline 3 -command [list $this reload] # Entry: "By size" $sorting_menu add radiobutton -label [mc "By size"] \ -variable ::KIFSD::FSD::config(sorting) \ -indicatoron 0 -compound left -image ::ICONS::raoff -selectimage ::ICONS::raon \ -value {size} -underline 3 -command [list $this reload] $sorting_menu add separator # Entry: "Reverse" $sorting_menu add checkbutton -label [mc "Reverse"] \ -variable ::KIFSD::FSD::config(reverse_sorting) \ -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ -command "$this reload" -underline 0 # Entry: "Folders first" $sorting_menu add checkbutton -label [mc "Folders first"] \ -variable ::KIFSD::FSD::config(folders_first) \ -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ -command "$this reload" -underline 0 # Entry: "Case insensitive" $sorting_menu add checkbutton -label [mc "Case insensitive"] \ -variable ::KIFSD::FSD::config(case_insensitive) \ -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ -command "$this reload" -underline 0 ## Create entries for configuraion menu (accessable from toolbar) # Entry: "Sorting" $win.config_menu add cascade -label [mc "Sorting"] -underline 1 -menu $sorting_menu -image ::ICONS::16::sort_incr -compound left $win.config_menu add separator # Entry: "Short view" $win.config_menu add command -label [mc "Short view"] -compound left \ -accelerator "F6" -command "$this short_view" -underline 0 \ -image ::ICONS::16::view_icon # Entry: "Detailed view" $win.config_menu add command -label [mc "Detailed view"] -compound left \ -accelerator "F7" -command "$this detail_view" -underline 0 \ -image ::ICONS::16::view_detailed $win.config_menu add separator # Entry: "Show hidden files" $win.config_menu add checkbutton -label [mc "Show hidden files"] \ -accelerator "F8" -variable ::KIFSD::FSD::config(show_hidden_files) \ -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ -command "$this reload" -underline 5 # Entry: "Quick access navigation panel" $win.config_menu add checkbutton -label [mc "Quick access navigation panel"] \ -accelerator "F9" -variable ::KIFSD::FSD::config(quick_access_panel) \ -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ -command "$this quick_access_panel_onoff" -underline 0 # Entry: "Separate folders" $win.config_menu add checkbutton -label [mc "Separate folders"] \ -accelerator "F12" -variable ::KIFSD::FSD::config(separate_folders) \ -indicatoron 0 -compound left -image ::ICONS::choff -selectimage ::ICONS::chon \ -command "$this separate_folders_onoff" -underline 9 if {!$option_fileson} { $win.config_menu entryconfigure [mc "Separate folders"] -state disabled $sorting_menu entryconfigure [mc "Folders first"] -state disabled $sorting_menu entryconfigure [mc "By size"] -state disabled } ## Create bookmarks menu (accessable from toolbar) set bookmark_menu [menu $win.bookmark_menu -tearoff 0] # Entry: "Add bookmark" $bookmark_menu add command -label [mc "Add bookmark"] \ -command "$this add_bookmark" \ -underline 0 -image ::ICONS::16::bookmark_add -compound left # Entry: "Edit bookmarks" $bookmark_menu add command -label [mc "Edit bookmarks"] -compound left \ -command "$this edit_bookmarks" -underline 0 -image ::ICONS::16::bookmark $bookmark_menu add separator refresh_bookmarks ## Create ListBox item menu menu $win.listbox_menu -tearoff 0 # Entry: "Up" $win.listbox_menu add command -label [mc "Up"] -compound left \ -underline 0 -command [list $this up] \ -image ::ICONS::16::up # Entry: "Back" $win.listbox_menu add command -label [mc "Back"] -compound left \ -underline 0 -command [list $this back] \ -image ::ICONS::16::left -state disabled # Entry: "Forward" $win.listbox_menu add command -label [mc "Forward"] -compound left \ -underline 0 -command [list $this forward] \ -image ::ICONS::16::right -state disabled $win.listbox_menu add separator # Entry: "Rename" $win.listbox_menu add command -label [mc "Rename"] \ -underline 0 -command [list $this rename_item_command] \ -compound left -image ::ICONS::16::edit # Entry: "Delete" $win.listbox_menu add command -label [mc "Delete"] \ -underline 0 -command [list $this delete_item_command] \ -compound left -image ::ICONS::16::editdelete # Entry: "New folder" $win.listbox_menu add command -label [mc "New folder"] \ -accelerator "F10" \ -underline 0 -command [list $this newdir] \ -compound left -image ::ICONS::16::folder_new # Entry: "Bookmark folder" $win.listbox_menu add command -label [mc "Bookmark folder"] \ -underline 0 -command [list $this item_bookmark_add] \ -compound left -image ::ICONS::16::bookmark_add $win.listbox_menu add separator # Entry: "Properties" $win.listbox_menu add command -label [mc "Properties"] \ -underline 0 -command [list $this properties_item_command] ## Create quick access bar popup menu menu $win.quick_access_panel_menu -tearoff 0 # Entry: "Add entry" $win.quick_access_panel_menu add command -label [mc "Add entry"] \ -underline 0 -image ::ICONS::16::filenew -compound left \ -command "$this quick_access_panel_add_entry" $win.quick_access_panel_menu add separator # Entry: "Hide panel" $win.quick_access_panel_menu add command -label [mc "Hide panel"] \ -underline 0 -image ::ICONS::16::2leftarrow -compound left \ -accelerator "F9" -command " set ::KIFSD::FSD::config(quick_access_panel) \ \[expr {!\${::KIFSD::FSD::config(quick_access_panel)}}\] $this quick_access_panel_onoff" ## Create quick access bar ITEM popup menu menu $win.quick_access_panel_item_menu -tearoff 0 # Entry: "Move up" $win.quick_access_panel_item_menu add command -label [mc "Move up"] \ -underline 0 -image ::ICONS::16::1uparrow -compound left \ -command "$this quick_access_panel_up" # Entry: "Move down" $win.quick_access_panel_item_menu add command -label [mc "Move down"] \ -underline 0 -image ::ICONS::16::1downarrow -compound left \ -command "$this quick_access_panel_down" $win.quick_access_panel_item_menu add separator # Entry: "Edit entry" $win.quick_access_panel_item_menu add command -label [mc "Edit entry"] \ -underline 0 -image ::ICONS::16::edit -compound left \ -command "$this quick_access_panel_edit_entry" $win.quick_access_panel_item_menu add separator # Entry: "Add entry" $win.quick_access_panel_item_menu add command -label [mc "Add entry"] \ -underline 0 -image ::ICONS::16::filenew -compound left \ -command "$this quick_access_panel_add_entry" # Entry: "Remove entry" $win.quick_access_panel_item_menu add command -label [mc "Remove entry"]\ -underline 0 -image ::ICONS::16::editdelete -compound left \ -command "$this quick_access_panel_remove_entry" $win.quick_access_panel_item_menu add separator # Entry: "Hide panel" $win.quick_access_panel_item_menu add command -label [mc "Hide panel"] \ -underline 0 -image ::ICONS::16::2leftarrow -compound left \ -accelerator "F9" \ -command " set ::KIFSD::FSD::config(quick_access_panel) \ \[expr {!\${::KIFSD::FSD::config(quick_access_panel)}}\] $this quick_access_panel_onoff" } ## Define key shortcuts for the dialog # @return void private method create_shortcuts {} { bind $win "$this reload; break" bind $win "$this short_view; break" bind $win "$this detail_view; break" bind $win " set ::KIFSD::FSD::config(show_hidden_files) \ \[expr {!\${::KIFSD::FSD::config(show_hidden_files)}}\] $this reload break " bind $win " set ::KIFSD::FSD::config(quick_access_panel) \ \[expr {!\${::KIFSD::FSD::config(quick_access_panel)}}\] $this quick_access_panel_onoff break " bind $win "$this newdir; break" if {$option_fileson} { bind $win " set ::KIFSD::FSD::config(separate_folders) \ \[expr {!\${::KIFSD::FSD::config(separate_folders)}}\] $this separate_folders_onoff break " } } ## Change current directory # This function checks for directory validity # @parm String dir - New directory # @return void public method change_directory {dir} { if {$::MICROSOFT_WINDOWS} { # Transform for instance "C:" to "C:/" if {[regexp {^\w+:$} $dir]} { append dir {/} } } # Check if the specified directory is valid if {![file exists $dir] || ![file isdirectory $dir]} { tk_messageBox \ -parent $win \ -type ok \ -icon warning \ -title [mc "Invalid folder"] \ -message [mc "The specified folder does not exist:\n%s" $dir] return } set dir [file normalize $dir] # Adjust history if {$dir != $current_directory} { lappend back_history $current_directory set forward_history {} $win.listbox_menu entryconfigure [mc "Forward"] -state disabled $win.listbox_menu entryconfigure [mc "Back"] -state normal $toolbar.forward configure -state disabled $toolbar.back configure -state normal } # Option separate_folders ON FSnotifications::forget $current_directory FSnotifications::watch $dir [list KIFSD::FSD::static_reload $this] set current_directory $dir if {${::KIFSD::FSD::config(separate_folders)} && $option_fileson} { # Fill up directory ListBox with directories $dir_listbox delete [$dir_listbox items] foreach folder [dir_cmd $dir 1] { if {$folder == {..}} { set image {up} } else { set image {fileopen} } $dir_listbox insert end #auto \ -text $folder \ -image ::ICONS::16::$image \ -font $listbox_font_short } # Fill up file ListBox with files $file_listbox delete [$file_listbox items] foreach file [file_cmd $dir $current_mask] { if {${::KIFSD::FSD::config(detailed_view)}} { set filename [lindex $file 1] set file [lindex $file 0] } else { set filename $file } $file_listbox insert end #auto \ -text $file \ -image ::ICONS::16::ascii \ -font $listbox_font \ -data [list $filename {}] } # Option separate_folders OFF } else { # Option folders_first ON or option_fileson OFF $file_listbox delete [$file_listbox items] if {!$option_fileson || ${::KIFSD::FSD::config(folders_first)}} { # Fill up files ListBox with directories foreach folder [dir_cmd $dir] { if {${::KIFSD::FSD::config(detailed_view)}} { set fullname [lindex $folder 1] set folder [lindex $folder 0] } else { set fullname $folder } if {$folder == {..}} { set image {up} set fullname $folder } else { set image {fileopen} } $file_listbox insert end #auto \ -text $folder \ -image ::ICONS::16::$image \ -font $listbox_font \ -data [list {} $fullname] } # Option: option_fileson ON if {$option_fileson} { # Fill up files ListBox with files foreach file [file_cmd $dir $current_mask] { if {${::KIFSD::FSD::config(detailed_view)}} { set filename [lindex $file 1] set file [lindex $file 0] } else { set filename $file } $file_listbox insert end #auto \ -text $file \ -image ::ICONS::16::ascii \ -font $listbox_font \ -data [list $filename {}] } } # Option NOT ( folders_first ON or option_fileson OFF ) } else { # Fill up files ListBox with files and directories foreach file [dir_file_cmd $dir $current_mask] { set filename {} set folder {} if {${::KIFSD::FSD::config(detailed_view)}} { set fullname [lindex $file {0 1}] set text [lindex $file {0 0}] } else { set fullname [lindex $file 0] set text $fullname } switch -- [lindex $file 1] { u { set image {up} set folder {..} } d { set image {fileopen} set folder $fullname } f { set image {ascii} set filename $fullname } } $file_listbox insert end #auto \ -text $text \ -image ::ICONS::16::$image \ -font $listbox_font \ -data [list $filename $folder] } } } # Fill up location ComboBox with available files or directories if {$option_fileson} { $location_cb configure -values [file_cmd $dir $current_mask 1] } else { $location_cb configure -values [dir_cmd $dir 1] } $location_cb set {} # Fill up directory ComboBox set values {} set folder $dir while {1} { lappend values $folder if {$folder == [file separator]} {break} if {$::MICROSOFT_WINDOWS} { if {[regexp {^\w+:[\\\/]?$} $folder]} {break} } set folder [file normalize [file join $folder {..}]] } foreach folder [dir_cmd $dir 1] { if {$folder == {..}} {continue} lappend values [file join $dir $folder] } if {$::MICROSOFT_WINDOWS} { ;# Include drive letters on Microsoft Windows foreach drive_letter {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} { if {[file exists "${drive_letter}:/"]} { lappend values "${drive_letter}:/" } } } $dir_combobox configure -values $values $dir_combobox current 0 $dir_combobox icursor end # Enable / Disable button "Up (Parent folder)" if {$dir == {/} || $dir == "\\"} { $toolbar.up configure -state disabled $win.listbox_menu entryconfigure [mc "Up"] -state disabled } else { $toolbar.up configure -state normal $win.listbox_menu entryconfigure [mc "Up"] -state normal } } ## This function shoul be called after Filter ComboBox change # @return void public method filter_cb_modify {} { set current_mask [lindex $option_filetypes [$filter_cb current]] reload } ## Show / Hide quick access bar according to configuration variable quick_access_panel # @return void public method quick_access_panel_onoff {} { # Show the panel if {${::KIFSD::FSD::config(quick_access_panel)}} { pack $main_paned_window -fill both -expand 1 $main_paned_window add $leftframe $main_paned_window add $rightframe $main_paned_window paneconfigure $leftframe -minsize 100 $main_paned_window paneconfigure $rightframe -minsize 300 if {$dialog_loaded} {update} $main_paned_window sash place 0 ${::KIFSD::FSD::config(main_PW_size)} 0 if {$dialog_loaded} {update} # Hide the panel } else { if {[winfo ismapped $main_paned_window]} { set ::KIFSD::FSD::config(main_PW_size) \ [lindex [$main_paned_window sash coord 0] {0 0}] $main_paned_window forget $leftframe $main_paned_window forget $rightframe pack forget $main_paned_window } pack $rightframe -fill both -expand 1 -padx 5 } } ## Show / Hide folders ListBox according to configuration variable separate_folders # This function will show folders ListBox only if option_fileson == 1 # @return void public method separate_folders_onoff {} { # Show folders ListBox if {${::KIFSD::FSD::config(separate_folders)} && $option_fileson} { pack $right_paned_window -fill both -expand 1 $right_paned_window add $right_top_left_frame $right_paned_window add $right_top_right_frame $right_paned_window paneconfigure $right_top_left_frame -minsize 150 $right_paned_window paneconfigure $right_top_right_frame -minsize 200 if {$dialog_loaded} {update} $right_paned_window sash place 0 ${::KIFSD::FSD::config(right_PW_size)} 0 if {$dialog_loaded} {update} # Hide folders ListBox } else { if {[winfo ismapped $right_paned_window]} { set ::KIFSD::FSD::config(right_PW_size) \ [lindex [$right_paned_window sash coord 0] {0 0}] $right_paned_window forget $right_top_left_frame $right_paned_window forget $right_top_right_frame pack forget $right_paned_window } pack $right_top_right_frame -expand 1 -fill both -in $right_top_frame } # Refresh files and folders ListBoxes change_directory $current_directory } ## Invoke bookmark menu # @return void public method bookmark_menu {} { set x [winfo rootx $toolbar.bookmark] set y [winfo rooty $toolbar.bookmark] incr y [winfo height $toolbar.bookmark] tk_popup $win.bookmark_menu $x $y } ## Invoke configuration menu # @return void public method config_menu {} { set x [winfo rootx $toolbar.configure] set y [winfo rooty $toolbar.configure] incr y [winfo height $toolbar.configure] tk_popup $win.config_menu $x $y } ## Scroll folders ListBox and (Un)Map its scrollbar # @parm Float frac0 - 1st fraction # @parm Float frac0 - 2nd fraction # @return void public method dir_listbox_scroll {frac0 frac1} { # Hide scrollbar if {$frac0 == 0 && $frac1 == 1} { if {[winfo ismapped $dir_listbox_scrollbar]} { pack forget $dir_listbox_scrollbar } # Show scrollbar } else { if {![winfo ismapped $dir_listbox_scrollbar]} { pack $dir_listbox_scrollbar -fill y -expand 1 -after $dir_listbox } $dir_listbox_scrollbar set $frac0 $frac1 } } ## Switch to mode "Short View" # @return void public method short_view {} { if {!${::KIFSD::FSD::config(detailed_view)}} {return} set ::KIFSD::FSD::config(detailed_view) 0 $file_listbox configure -multicolumn 1 set listbox_font $listbox_font_short pack forget $file_listbox_header reload } ## Switch to mode "Detailed View" # @return void public method detail_view {} { if {${::KIFSD::FSD::config(detailed_view)}} {return} set ::KIFSD::FSD::config(detailed_view) 1 $file_listbox configure -multicolumn 0 set listbox_font $listbox_font_detailed pack $file_listbox_header -before $file_listbox -fill x -expand 0 reload } ## Bookmark current folder # @return void public method add_bookmark {} { lappend ::KIFSD::FSD::config(bookmarks) $current_directory $bookmark_menu add command \ -label $current_directory -compound left \ -image ::ICONS::16::fileopen \ -command "$this change_directory {$current_directory}" uplevel #0 $bookmark_change_command } ## Invoke bookmark editor # @return void public method edit_bookmarks {} { # Create dialog window set dialog [toplevel $win.edit_bookmarks -class {Edit bookmarks} -bg ${::COMMON_BG_COLOR}] # Create top frame (ListBox containing bookmarks and its scrollbar) set top_frame [frame $dialog.top_frame] set bookmark_edit_listbox [ListBox $top_frame.listbox \ -yscrollcommand "$top_frame.scrollbar set" \ -bg white -selectfill 1 -selectmode single \ -highlightcolor {#BBBBFF} \ ] $bookmark_edit_listbox bindText "$this edit_bookmarks_edit" pack $bookmark_edit_listbox -side left -fill both -expand 1 pack [ttk::scrollbar $top_frame.scrollbar \ -orient vertical \ -command "$bookmark_edit_listbox yview" \ ] -fill y -expand 1 # Fill up ListBox with defined bookmarks foreach item ${::KIFSD::FSD::config(bookmarks)} { $bookmark_edit_listbox insert end #auto -text $item } ## Create bottom frame (buttons) set bottom_frame [frame $dialog.bottom_frame] # Button: "Remove" pack [ttk::button $bottom_frame.remove \ -text [::mc "Remove"] \ -compound left \ -image ::ICONS::16::editdelete \ -command "$this edit_bookmarks_remove" \ -width 8 \ ] -side left -padx 2 # Button: "Edit" pack [ttk::button $bottom_frame.edit \ -text [::mc "Edit"] \ -compound left \ -image ::ICONS::16::edit \ -command "$this edit_bookmarks_edit" \ -width 8 \ ] -side left -padx 2 # Button: "Up" pack [ttk::button $bottom_frame.up \ -text [::mc "Up"] \ -compound left \ -image ::ICONS::16::up \ -command "$this edit_bookmarks_up" \ -width 8 \ ] -side left -padx 2 # Button: "Down" pack [ttk::button $bottom_frame.down \ -text [::mc "Down"] \ -compound left \ -image ::ICONS::16::down \ -command "$this edit_bookmarks_down" \ -width 8 \ ] -side left -padx 2 # Button: "Ok" pack [ttk::button $bottom_frame.ok \ -text [::mc "Ok"] \ -compound left \ -image ::ICONS::16::ok \ -width 8 \ -command " $this bookmark_edit_ok grab release $dialog destroy $dialog " \ ] -side right -padx 2 # Button: "Cancel" pack [ttk::button $bottom_frame.cancel \ -text [::mc "Cancel"] \ -compound left \ -image ::ICONS::16::button_cancel \ -width 8 \ -command " grab release $dialog destroy $dialog " \ ] -side right -padx 2 # Pack dialog frames (top and bottom) pack $top_frame -side top -fill both -expand 1 -pady 5 -padx 5 pack $bottom_frame -side top -after $top_frame -fill x -expand 0 -pady 5 -padx 5 # Configure dialog window wm iconphoto $dialog ::ICONS::16::bookmark wm title $dialog "Edit bookmarks" wm minsize $dialog 550 240 wm geometry $dialog 550x340 wm protocol $dialog WM_DELETE_WINDOW " grab release $dialog destroy $dialog " if {[winfo ismapped $win]} { wm transient $dialog $win } else { wm transient $dialog . } grab $dialog raise $dialog tkwait window $dialog } ## Auxiliary procedure for bookmark editor # Remove current bookmark # @return void public method edit_bookmarks_remove {} { set item [$bookmark_edit_listbox selection get] if {$item == {}} {return} $bookmark_edit_listbox delete $item } ## Auxiliary procedure for bookmark editor # Edit current bookmark # @return void public method edit_bookmarks_edit args { set item [$bookmark_edit_listbox selection get] if {$item == {}} {return} set text [$bookmark_edit_listbox edit $item \ [$bookmark_edit_listbox itemcget $item -text]] if {$text == {}} {return} $bookmark_edit_listbox itemconfigure $item -text $text } ## Auxiliary procedure for bookmark editor # Move current bookmark up # @return void public method edit_bookmarks_up {} { set item [$bookmark_edit_listbox selection get] if {$item == {}} {return} if { ![$bookmark_edit_listbox index $item] || ([llength [$bookmark_edit_listbox items]] < 2) } then { return } $bookmark_edit_listbox move $item [expr {[$bookmark_edit_listbox index $item] - 1}] } ## Auxiliary procedure for bookmark editor # Move current bookmark down # @return void public method edit_bookmarks_down {} { set item [$bookmark_edit_listbox selection get] if {$item == {}} {return} if { [$bookmark_edit_listbox index $item] >= ([llength [$quick_access_bar items]] - 1) } then { return } $bookmark_edit_listbox move $item [expr {[$bookmark_edit_listbox index $item] + 1}] } ## Auxiliary procedure for bookmark editor # Confirm bookmark edit dialog # @return void public method bookmark_edit_ok {} { set ::KIFSD::FSD::config(bookmarks) {} foreach item [$bookmark_edit_listbox items] { lappend ::KIFSD::FSD::config(bookmarks) \ [$bookmark_edit_listbox itemcget $item -text] } refresh_bookmarks uplevel #0 $bookmark_change_command } ## Reload items to bookmarks menu # @return void private method refresh_bookmarks {} { if {[$bookmark_menu index end] > 2} { $bookmark_menu delete 3 end } foreach dir ${::KIFSD::FSD::config(bookmarks)} { $bookmark_menu add command \ -label $dir -compound left \ -image ::ICONS::16::fileopen \ -command "$this change_directory {$dir}" } } ## Set command to execute when bookmark list changes # @parm String command - Command to invoke from root namespace # @return void proc set_bookmark_change_command {command} { set bookmark_change_command $command } ## Unmap dialog window (but keep object alive) # @return void public method deactivate {} { wm withdraw $win } ## Activate (map) dialog window # And wait until window is unmapped # @return void public method activate {} { wm resizable $win 1 1 wm deiconify $win update idletasks if {[winfo ismapped $right_paned_window]} { $right_paned_window sash place 0 ${::KIFSD::FSD::config(right_PW_size)} 0 } if {[winfo ismapped $main_paned_window]} { $main_paned_window sash place 0 ${::KIFSD::FSD::config(main_PW_size)} 0 } tkwait window $win } ## Get selected item(s) # @return String/List - Full path(s) to selected item(s) public method get {} { # Return List if {$option_multiple} { set result {} foreach item [$file_listbox selection get] { lappend result [file join $current_directory \ [lindex [$file_listbox itemcget $item -data] 0]] } if {$result == {}} { lappend result [file join $current_directory [$location_cb get]] } return $result # Return String } else { return [file join $current_directory [$location_cb get]] } } ## Destroy dialog object # @return void public method close_dialog {} { catch { itcl::delete object $this } } ## Set command to invoke from root namespace on action "Ok" # @parm String command - Command (with arguments) # @return void public method setokcmd {cmd} { set ok_command $cmd } ## Ok action - command for button "Ok" # @return void public method ok {} { if {$option_autoclose} { wm withdraw $win set ok_command_tmp $ok_command set ok_command {} uplevel #0 $ok_command_tmp close_dialog } else { uplevel #0 $ok_command } } ## Command for files ListBox horizontal scrollbar # Takes any list of arguments (see code) # @return void public method file_listbox_hscrollbar_cmd args { eval "$file_listbox xview $args" eval "$file_listbox_header xview $args" } ## Scroll files ListBox vertically # This function manages scrollbar visibility # @parm Float frac0 - 1st fraction (see Tk manual) # @parm Float frac1 - 2nd fraction (see Tk manual) # @return void public method file_listbox_vscroll {frac0 frac1} { # Hide scrollbar if {$frac0 == 0 && $frac1 == 1} { if {[winfo ismapped $file_listbox_vscrollbar]} { pack forget $file_listbox_vscrollbar update } # Show scrollbar } else { if {![winfo ismapped $file_listbox_vscrollbar]} { pack $file_listbox_vscrollbar \ -after $file_listbox_frame \ -fill y -expand 1 update } $file_listbox_vscrollbar set $frac0 $frac1 } } ## Scroll files ListBox horizontaly # This function manages scrollbar visibility # @parm Float frac0 - 1st fraction (see Tk manual) # @parm Float frac1 - 2nd fraction (see Tk manual) # @return void public method file_listbox_hscroll {frac0 frac1} { # Hide scrollbar if {$frac0 == 0 && $frac1 == 1} { if {[winfo ismapped $file_listbox_hscrollbar]} { pack forget $file_listbox_hscrollbar update } # Show scrollbar } else { if {![winfo ismapped $file_listbox_hscrollbar]} { pack $file_listbox_hscrollbar \ -after $right_top_right_top_frame \ -side bottom -fill x -expand 0 update } catch { $file_listbox_hscrollbar set $frac0 $frac1 } } } ## Event handler for quick access bar ListBox, event <> # @return void public method quick_access_bar_select {} { if {$option_doubleclick} {return} catch { change_directory \ [$quick_access_bar itemcget \ [$quick_access_bar selection get] -data] } } ## Event handler for quick access bar ListBox, item event # @parm String item - Item identifier # @return void public method quick_access_bar_doubleclick {item} { if {!$option_doubleclick} {return} catch { change_directory \ [$quick_access_bar itemcget \ [$quick_access_bar selection get] -data] } } ## Event handler for directories ListBox, item event # @parm String item - Item identifier # @return void public method dir_listbox_doubleclick {item} { # Abort if dirs ListBox widget is no longer available if {![winfo exists $dir_listbox]} { return } if {!$option_doubleclick} {return} catch { change_directory [file join $current_directory \ [$dir_listbox itemcget $item -text]] } } ## Event handler for directories ListBox, event <> # @return void public method dir_listbox_select {} { # Abort if dirs ListBox widget is no longer available if {![winfo exists $dir_listbox]} { return } if {$option_doubleclick} {return} catch { change_directory [file normalize [file join $current_directory \ [$dir_listbox itemcget [$dir_listbox selection get] -text]]] } } ## Event handler for files ListBox, item event # @parm String item - Item identifier # @return void public method file_listbox_doubleclick {item} { # Abort if files ListBox widget is no longer available if {![winfo exists $file_listbox]} { return } # Item directory or {} if it's a file if {[catch { set folder [lindex [$file_listbox itemcget $item -data] 1] }]} then { return } if {!$option_fileson} { if {$folder != {}} { change_directory [file join $current_directory $folder] } return } if {$option_doubleclick && !${::KIFSD::FSD::config(separate_folders)}} { if {$folder != {}} { change_directory [file join $current_directory $folder] } } if {!$option_doubleclick && ($folder == {})} { ok } } ## Scroll files listbox # Arguments are passed to yview or xview command # @return void public method file_listbox_scroll args { if {${::KIFSD::FSD::config(detailed_view)}} { set cmd {yview} } else { set cmd {xview} } eval "$file_listbox.c $cmd scroll $args" } ## Event handler for files ListBox, event <> # @return void public method file_listbox_select {} { set selection [$file_listbox selection get] # Change directory if the item represents a directory if {$option_fileson && !${::KIFSD::FSD::config(separate_folders)}} { set folder [$file_listbox itemcget [lindex $selection end] -data] set folder [lindex $folder 1] if {$folder != {}} { if {!$option_doubleclick} { change_directory [file join $current_directory $folder] } return } } # Change content of location ComboBox if item is a file if {[llength $selection] == 1} { set index [lindex [$file_listbox itemcget $selection -data] [expr {$option_fileson ? 0 : 1}]] if {$index != {..}} { set index [lsearch -ascii [$location_cb cget -values] $index] if {$index != -1} { $location_cb current $index } } } elseif {[llength $selection] > 1} { set text {} foreach item $selection { append text "\"" append text [lindex [$file_listbox itemcget $item -data] [expr {$option_fileson ? 0 : 1}]] append text "\" " } $location_cb set $text } } ## Reload content of quick access bar ListBox # @return void private method refresh_quick_access_bar {} { # Remove existing items $quick_access_bar delete [$quick_access_bar items] # Create new items foreach item ${::KIFSD::FSD::config(quick_access_bar_data)} { # Determinate item icon switch -- [lindex $item 0] { 0 {set image hdd_unmount} 1 {set image folder_home} 2 {set image desktop} 3 {set image bookmark_folder} } # Insert item $quick_access_bar insert end #auto \ -font $quick_nav_panel_font \ -image ::ICONS::22::$image \ -text [lindex $item 1] \ -data [lindex $item 2] \ } } ## Invoke popup menu for ListBox of Quick access bar # @parm Int x - Relative position of mouse pointer # @parm Int y - Relative position of mouse pointer # @return void public method quick_access_bar_menu {x y} { if {$item_menu_request} { set item_menu_request 0 return } catch { tk_popup $win.quick_access_panel_menu $x $y } } ## Invoke popup menu for particular item in ListBox of Quick access bar # @parm Int x - Relative position of mouse pointer # @parm Int y - Relative position of mouse pointer # @parm String item - Item identifier # @return void public method quick_access_bar_item_menu {x y item} { set item_menu_request 1 set current_item $item set current_item_index [$quick_access_bar index $item] set len [llength [$quick_access_bar items]] # Enable / Disabled entry "Move down" if {$current_item_index >= ($len - 1)} { $win.quick_access_panel_item_menu entryconfigure [mc "Move down"] -state disabled } else { $win.quick_access_panel_item_menu entryconfigure [mc "Move down"] -state normal } # Enable / Disabled entry "Move up" if {!$current_item_index || ($len < 2)} { $win.quick_access_panel_item_menu entryconfigure [mc "Move up"] -state disabled } else { $win.quick_access_panel_item_menu entryconfigure [mc "Move up"] -state normal } # Invoke the menu tk_popup $win.quick_access_panel_item_menu $x $y } ## Move current item in quick access bar down # @return void public method quick_access_panel_down {} { # Check if the item is not the topmost one if {$current_item_index >= ([llength [$quick_access_bar items]] - 1)} { return } set ::KIFSD::FSD::config(quick_access_bar_data) [lreplace \ ${::KIFSD::FSD::config(quick_access_bar_data)} \ $current_item_index [expr {$current_item_index + 1}] \ [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ [expr {$current_item_index + 1}]] \ [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ $current_item_index] ] refresh_quick_access_bar } ## Move current item in quick access bar up # @return void public method quick_access_panel_up {} { # Check if the item is not the bottommost one if {!$current_item_index || ([llength [$quick_access_bar items]] < 2)} { return } set ::KIFSD::FSD::config(quick_access_bar_data) [lreplace \ ${::KIFSD::FSD::config(quick_access_bar_data)} \ [expr {$current_item_index - 1}] $current_item_index \ [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ $current_item_index] \ [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} \ [expr {$current_item_index - 1}]] ] refresh_quick_access_bar } ## Invoke dialog to add entry to quick access bar # @return void public method quick_access_panel_add_entry {} { set data [qa_panel_dialog "Add entry" {3} [::mc "New entry"] {~}] if {![string length [lindex $data 1]]} {return} if {![string length [lindex $data 2]]} {return} lappend ::KIFSD::FSD::config(quick_access_bar_data) $data refresh_quick_access_bar } ## Invoke dialog to edit current entry in quick access bar # @return void public method quick_access_panel_edit_entry {} { set data [lindex ${::KIFSD::FSD::config(quick_access_bar_data)} $current_item_index] set data [qa_panel_dialog "Edit entry" [lindex $data 0] [lindex $data 1] [lindex $data 2]] if {![string length [lindex $data 1]]} {return} if {![string length [lindex $data 2]]} {return} set ::KIFSD::FSD::config(quick_access_bar_data) [lreplace \ ${::KIFSD::FSD::config(quick_access_bar_data)} \ $current_item_index $current_item_index $data \ ] refresh_quick_access_bar } ## Select icon in quick access bar edit dialog # @parm Int index - Icon index [0; 4] # @return void public method qa_panel_dialog_icon {index} { for {set i 0} {$i < 4} {incr i} { ${win}.qa_panel_dialog.labelframe.button_$i configure -style Flat.TButton } ${win}.qa_panel_dialog.labelframe.button_$index configure -style TButton set ::KIFSD::FSD::qa_panel_dialog_icon $index } ## EntryBox validator # If the content was an empty string then set entry background color to red # @parm Widget widget - EntryBox widget # @parm String content - EntryBox content # @return Bool - Always 1 proc not_empty_entry_validator {widget content} { if {![string length $content]} { $widget configure -style StringNotFound.TEntry } else { $widget configure -style TEntry } return 1 } ## Invoke dialog for editing entries in the quick access bar # Auxiliary procedure for: # * quick_access_panel_add_entry # * quick_access_panel_edit_entry # @parm String title - Dialog title # @parm Int icon - Icon number [0;3] # @parm String name - Item name # @parm String url - Target URL # @return List - {new_icon_number new_name new_url} private method qa_panel_dialog {title icon name url} { # Create dialog window set dialog [toplevel ${win}.qa_panel_dialog -class {Configuration dialog} -bg ${::COMMON_BG_COLOR}] # Set dialog variables set ::KIFSD::FSD::qa_panel_dialog_icon $icon set ::KIFSD::FSD::qa_panel_dialog_name_entry $name set ::KIFSD::FSD::qa_panel_dialog_url_entry $url ## Create main frame (Name: and URL:) set mid_frame [frame $dialog.middle] # Label: "Name" grid [label $mid_frame.name_lbl \ -text [::mc "Name"] \ ] -row 0 -column 0 -sticky w # Label: "URL" grid [label $mid_frame.url_lbl \ -text [::mc "URL"] \ ] -row 1 -column 0 -sticky w # EntryBox: "Name" grid [ttk::entry $mid_frame.name_entry \ -width 1 \ -validate all \ -validatecommand "::KIFSD::FSD::not_empty_entry_validator %W %P" \ -textvariable ::KIFSD::FSD::qa_panel_dialog_name_entry \ ] -row 0 -column 1 -sticky we # EntryBox: "URL" grid [ttk::entry $mid_frame.url_entry \ -width 1 \ -validate all \ -textvariable ::KIFSD::FSD::qa_panel_dialog_url_entry \ -validatecommand "::KIFSD::FSD::dir_validate {} %W %P" \ ] -row 1 -column 1 -sticky we grid columnconfigure $mid_frame 1 -weight 1 pack $mid_frame -padx 10 -pady 5 -fill x -expand 1 # Create frame for selecting icon pack [ttk::labelframe $dialog.labelframe \ -text [::mc "Icon"] \ ] -fill none -expand 1 -anchor w -padx 10 foreach icon {hdd_unmount folder_home desktop bookmark_folder} index {0 1 2 3} { pack [ttk::button $dialog.labelframe.button_$index \ -image ::ICONS::22::$icon \ -command "$this qa_panel_dialog_icon $index" \ -width 6 \ -style Flat.TButton \ ] -side left -padx 5 -pady 5 } $dialog.labelframe.button_${::KIFSD::FSD::qa_panel_dialog_icon} \ configure -style TButton ## Create bottom frame (Buttons "Ok" and "Cancel") set bot_frame [frame $dialog.bot] # Button: "Ok" pack [ttk::button $bot_frame.ok \ -text [::mc "Ok"] \ -compound left \ -image ::ICONS::16::ok \ -command " if \[string length \${::KIFSD::FSD::qa_panel_dialog_name_entry}\] { if \[string length \${::KIFSD::FSD::qa_panel_dialog_url_entry}\] { grab release $dialog destroy $dialog } }" \ ] -side left -fill none -expand 0 -padx 2 # Button: "Cancel" pack [ttk::button $bot_frame.cancel \ -text [::mc "Cancel"] \ -compound left \ -image ::ICONS::16::button_cancel \ -command " set ::KIFSD::FSD::qa_panel_dialog_url_entry {} set ::KIFSD::FSD::qa_panel_dialog_name_entry {} set ::KIFSD::FSD::qa_panel_dialog_icon {} grab release $dialog destroy $dialog" \ ] -side left -fill none -expand 0 -padx 2 pack $bot_frame -anchor e -padx 10 -pady 5 # Configure dialog window wm title $dialog $title wm resizable $dialog 0 0 wm geometry $dialog 380x160 wm protocol $dialog WM_DELETE_WINDOW " set ::KIFSD::FSD::qa_panel_dialog_url_entry {} set ::KIFSD::FSD::qa_panel_dialog_name_entry {} set ::KIFSD::FSD::qa_panel_dialog_icon {} grab release $dialog destroy $dialog " wm transient $dialog $win grab $dialog raise $dialog focus -force $mid_frame.name_entry tkwait window $dialog # Return results return [list \ ${::KIFSD::FSD::qa_panel_dialog_icon} \ ${::KIFSD::FSD::qa_panel_dialog_name_entry} \ ${::KIFSD::FSD::qa_panel_dialog_url_entry} \ ] } ## Remove entry from quick access bar (popup menu action) # @return void public method quick_access_panel_remove_entry {} { set ::KIFSD::FSD::config(quick_access_bar_data) \ [lreplace ${::KIFSD::FSD::config(quick_access_bar_data)} \ $current_item_index $current_item_index] refresh_quick_access_bar } ## Invoke popup menu for directories ListBox # @parm Int x - Relative position of mouse pointer # @parm Int y - Relative position of mouse pointer # @return void public method dir_listbox_menu {x y} { if {$item_menu_request} { set item_menu_request 0 return } foreach entry {Rename Delete Properties {Bookmark folder}} { $win.listbox_menu entryconfigure [mc $entry] -state disabled } tk_popup $win.listbox_menu $x $y } ## Invoke popup menu for item in directories ListBox # @parm Int x - Relative position of mouse pointer # @parm Int y - Relative position of mouse pointer # @parm String item - Item identifier # @return void public method dir_listbox_item_menu {x y item} { set item_menu_request 1 foreach entry {Rename Delete Properties {Bookmark folder}} { $win.listbox_menu entryconfigure [mc $entry] -state normal } set cur_listbox {dir} set current_item $item set current_item_index [$dir_listbox index $item] tk_popup $win.listbox_menu $x $y } ## Invoke popup menu for files ListBox # @parm Int x - Relative position of mouse pointer # @parm Int y - Relative position of mouse pointer # @return void public method file_listbox_menu {x y} { if {$item_menu_request} { set item_menu_request 0 return } foreach entry {Rename Delete Properties} { $win.listbox_menu entryconfigure [mc $entry] -state disabled } $win.listbox_menu entryconfigure [mc {Bookmark folder}] -state normal tk_popup $win.listbox_menu $x $y } ## Invoke popup menu for item in files ListBox # @parm Int x - Relative position of mouse pointer # @parm Int y - Relative position of mouse pointer # @parm String item - Item identifier # @return void public method file_listbox_item_menu {x y item} { set item_menu_request 1 set current_item $item set current_item_index [$dir_listbox index $item] foreach entry {Rename Delete Properties {Bookmark folder}} { $win.listbox_menu entryconfigure [mc $entry] -state normal } set cur_listbox {file} set current_item $item set current_item_index [$dir_listbox index $item] tk_popup $win.listbox_menu $x $y } ## Remove selected file or directory # @return void public method delete_item_command {} { # Determinate URL to delete if {$cur_listbox == {dir}} { set filename [$dir_listbox itemcget $current_item -text] } else { set data [$file_listbox itemcget $current_item -data] if {[lindex $data 0] == {}} { set filename [lindex $data 1] } else { set filename [lindex $data 0] } } if {$filename == {}} {return} # Invoke confirmation dialog if {[tk_messageBox \ -parent $win \ -type yesno \ -icon question \ -title [::mc "Delete file"] \ -message [::mc "Do you really want to delete file:\n%s" $filename]] == {yes} } then { # Delete file/directory (+ invoke error dialog) if {[catch {file delete -force -- [file join $current_directory $filename]}]} { tk_messageBox \ -parent $win \ -type ok \ -icon warning \ -title [::mc "Permission denied"] \ -message [::mc "Unable to remove file:\n%s" $filename] } } reload } ## Bookmark selected folder # @return void public method item_bookmark_add {} { set tmp $current_directory if {$cur_listbox == {dir}} { set current_directory [file join $current_directory \ [$dir_listbox itemcget $current_item -text]] } add_bookmark set current_directory $tmp } ## Rename selected file or directory # @return void public method rename_item_command {} { if {$cur_listbox == {dir}} { set listbox $dir_listbox } else { set listbox $file_listbox } # Determinate old and new name set original [$listbox itemcget $current_item -text] set newname [$listbox edit $current_item \ [$listbox itemcget $current_item -text]] if {$newname == {}} { return } # Adjust old and new name set original [file join $current_directory $original] set newname [file join $current_directory $newname] # Rename file if {[catch {file rename -force $original $newname}]} { tk_messageBox \ -parent $win \ -type ok \ -icon warning \ -title [::mc "Permission denied"] \ -message [::mc "Unable to rename file:\n%s" $original] } reload } ## Invoke item properties dialog # @return void public method properties_item_command {} { # Determinate item name, type (File or Directory) if {$cur_listbox == {dir}} { set name [$dir_listbox itemcget $current_item -text] set type "Directory" } else { set name [$file_listbox itemcget $current_item -data] if {[lindex $name 0] == {}} { set name [lindex $name 1] set type "Directory" } else { set name [lindex $name 0] set type "File" } } # Determinate full name set fullname [file join $current_directory $name] if {![file exists $fullname]} { tk_messageBox \ -parent $win \ -type ok \ -icon warning \ -title [::mc "Unknown Error"] \ -message [::mc "This file apparently does not exist"] return } # Determinate size set size [file size $fullname] append size { B} # Determinate time of the last mofication set modified [clock format [file mtime $fullname] -format {%D %R}] # Determinate time of the last access set accessed [clock format [file atime $fullname] -format {%D %R}] # Determinate group, owner and permissions if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) set perms [file attributes $fullname] set group [lindex $perms 1] set owner [lindex $perms 3] set perms [lindex $perms 5] set perms [string range $perms {end-3} end] foreach var {ur uw ux gr gw gx or ow ox} \ mask {0400 0200 0100 040 020 010 04 02 01} \ { set ::KIFSD::FSD::item_properties($var) [expr {($perms & $mask) > 0}] } } # Create dialog window and Notebook set dialog [toplevel $win.properties_dialog -class {Configuration dialog} -bg ${::COMMON_BG_COLOR}] set nb [NoteBook $dialog.nb -bg ${::COMMON_BG_COLOR}] $nb insert end general -text "General" if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) $nb insert end permission -text "Permissions" } $nb raise general ## Create GUI elements for tag "General" set frame [frame [$nb getframe general].frame] pack $frame -side top -anchor n -fill x -expand 1 # Name: set row 0 grid [label $frame.lbl_$row \ -text [::mc "Name:"] -anchor w \ -font $listbox_font_short \ ] -column 0 -row $row -sticky w -pady 3 set ::KIFSD::FSD::item_properties(name) $name grid [ttk::entry $frame.val_lbl_$row \ -validate all \ -textvariable ::KIFSD::FSD::item_properties(name) \ -validatecommand "::KIFSD::FSD::not_empty_entry_validator %W %P" \ ] -column 1 -row $row -sticky w -pady 3 # Type, Location, Size, Modified, Accessed incr row foreach lbl [list "Type" "Location" "Size" "Modified" "Accessed"] \ value [list $type $current_directory $size $modified $accessed] \ { grid [label $frame.lbl_$row \ -text "[::mc $lbl]:" -anchor w \ -font $listbox_font_short \ ] -column 0 -row $row -sticky w -pady 3 grid [label $frame.val_lbl_$row \ -text $value -anchor w \ ] -column 1 -row $row -sticky w -pady 3 incr row } grid columnconfigure $frame 0 -minsize 100 ## Create GUI elements for tag "Permissions" if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) set frame [$nb getframe permission] set ap_frame [ttk::labelframe $frame.ap_frame \ -text [::mc "Access permissions"] \ ] set i 0 foreach text [list "Class" "Read" "Write" "Exec" "Owner" "Group" "Others"] \ row {0 0 0 0 1 2 3} \ col {0 1 2 3 0 0 0} \ { grid [label $ap_frame.lbl_$i \ -text [::mc $text] \ -justify center \ ] -row $row -column $col -sticky w -padx 4 -pady 4 incr i } foreach var {ur uw ux gr gw gx or ow ox} \ row {1 1 1 2 2 2 3 3 3} \ col {1 2 3 1 2 3 1 2 3} \ { grid [checkbutton $ap_frame.check_$i \ -variable ::KIFSD::FSD::item_properties($var) ] -row $row -column $col incr i } grid columnconfigure $ap_frame 0 -minsize 70 grid columnconfigure $ap_frame 0 -weight 1 pack $ap_frame -side top -fill x -expand 1 -padx 5 -pady 5 -anchor nw set own_frame [ttk::labelframe $frame.own_frame \ -text [::mc "Ownership"] \ ] grid [label $own_frame.owner_lbl \ -text [::mc "Owner"] -font $listbox_font_short \ ] -row 0 -column 0 -padx 10 -pady 3 -sticky w grid [label $own_frame.owner_val_lbl \ -text $owner -anchor w \ ] -row 0 -column 1 -padx 10 -pady 3 -sticky we grid [label $own_frame.group_lbl \ -text [::mc "Group"] -font $listbox_font_short \ ] -row 1 -column 0 -padx 10 -pady 3 -sticky w grid [label $own_frame.group_val_lbl \ -text $group -anchor w \ ] -row 1 -column 1 -padx 10 -pady 3 -sticky we grid columnconfigure $own_frame 0 -minsize 70 grid columnconfigure $own_frame 1 -weight 1 pack $own_frame -side top -fill x -expand 1 -padx 5 -pady 5 } # Create bottom frame (buttons: "Ok" and "Cancel") set bottom_frame [frame $dialog.bottom_frame] pack [ttk::button $bottom_frame.ok \ -text [::mc "Ok"] \ -compound left \ -image ::ICONS::16::ok \ -command "$this properties_ok $dialog $fullname" \ ] -side left -padx 2 pack [ttk::button $bottom_frame.cancel \ -text [::mc "Cancel"] \ -compound left \ -image ::ICONS::16::button_cancel \ -command " grab release $dialog destroy $dialog " \ ] -side left -padx 2 # Pack notebook and bottom frame pack $nb -fill both -expand 1 -padx 10 -pady 5 pack $bottom_frame -anchor e -after $nb -padx 10 -pady 5 # Configure dialog window wm title $dialog [::mc "Item properties"] wm minsize $dialog 280 320 wm protocol $dialog WM_DELETE_WINDOW " grab release $dialog destroy $dialog" wm transient $dialog $win grab $dialog raise $dialog tkwait window $dialog } ## Confirm item properties dialog # @parm Widget dialog - Dialog window # @parm String file - File URL # @return void public method properties_ok {dialog file} { set error 0 set perm 0 if {!$::MICROSOFT_WINDOWS} { ;# Microsoft Windows has no file rights (compatible with posix rights) foreach var {ur uw ux gr gw gx or ow ox} \ val {256 128 64 32 16 8 4 2 1} { if {$::KIFSD::FSD::item_properties($var)} { incr perm $val } } if {[catch {file attributes $file -permissions "0[format {%o} $perm]"}]} { set error 1 tk_messageBox \ -type ok \ -icon warning \ -parent $dialog \ -title [::mc "Permission denied"] \ -message [::mc "Unable to change permissions for file:\n%s" [file tail $file]] } } set dir [file dirname $file] if {${::KIFSD::FSD::item_properties(name)} != [file tail $file]} { if {[catch { file rename -force -- \ $file [file join $dir \ ${::KIFSD::FSD::item_properties(name)}]}] } then { set error 1 tk_messageBox \ -type ok \ -icon warning \ -parent $dialog \ -title [::mc "Permission denied"] \ -message [::mc "Unable to rename file:%s" "\n[file tail $file]\n\t=>\n${::KIFSD::FSD::item_properties(name)}"] } reload } if {!$error} { grab release $dialog destroy $dialog } } ## Validate EntryBox containing directory location (set background color: red/white) # @parm widget combobox - ComboBox widget or {} # @parm Widget widget - EntryBox widget # @parm String content - EntryBox content # @return Bool - Always 1 proc dir_validate {combobox widget content} { if {![file exists $content] || ![file isdirectory $content]} { if {$combobox != {}} { $combobox configure -style FSD_RedBg.TCombobox } else { $widget configure -style FSD_RedBg.TEntry } } else { if {$combobox != {}} { $combobox configure -style TCombobox } else { $widget configure -style TEntry } # Fill directory location combobox if {$combobox != {}} { set folder $content set values {} while {1} { lappend values $folder if {$folder == [file separator]} {break} if {$::MICROSOFT_WINDOWS} { if {[regexp {^\w+:[\\\/]?$} $folder]} {break} } set folder [file normalize [file join $folder {..}]] } foreach folder [::KIFSD::FSD::dir_cmd $content 1] { if {$folder == {..}} {continue} lappend values [file join $content $folder] } if {$::MICROSOFT_WINDOWS} { ;# Include drive letters on Microsoft Windows foreach drive_letter {A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} { if {[file exists "${drive_letter}:/"]} { lappend values "${drive_letter}:/" } } } $combobox configure -values $values } } return 1 } ## Reload content of directories ListBox and files ListBox # @param List args - all arguments are ignored # @return void public method reload {args} { update idletasks change_directory $current_directory } ## Modify command for directory ComboBox # @return void public method dir_cb_modify {} { change_directory [$dir_combobox get] } ## Go to parrent folder # @return void public method up {} { change_directory [file normalize [file join $current_directory {..}]] } ## Go back in history # @return void public method back {} { # Determinate new folder set folder [lindex $back_history end] if {$folder == {}} {return} # Adjust backward and forward history set back_history [lreplace $back_history end end] lappend forward_history $current_directory # Make backup copy of backward and forward history set tmp_forw_hist $forward_history set tmp_back_hist $back_history # Change current directory change_directory $folder # Restore backward and forward history set forward_history $tmp_forw_hist set back_history $tmp_back_hist # Enable / Disable buttons "Back" and "Forward" if {![llength $back_history]} { $toolbar.back configure -state disabled $win.listbox_menu entryconfigure [mc "Back"] -state disabled } else { $toolbar.back configure -state normal $win.listbox_menu entryconfigure [mc "Back"] -state normal } $win.listbox_menu entryconfigure [mc "Forward"] -state normal $toolbar.forward configure -state normal } ## Go forward in history # @return void public method forward {} { # Determinate new folder set folder [lindex $forward_history end] if {$folder == {}} {return} # Adjust backward and forward history set forward_history [lreplace $forward_history end end] lappend back_history $current_directory # Make backup copy of backward and forward history set tmp_forw_hist $forward_history set tmp_back_hist $back_history # Change current directory change_directory $folder # Restore backward and forward history set forward_history $tmp_forw_hist set back_history $tmp_back_hist # Enable / Disable buttons "Back" and "Forward" if {![llength $forward_history]} { $toolbar.forward configure -state disabled $win.listbox_menu entryconfigure [mc "Forward"] -state disabled } else { $toolbar.forward configure -state normal $win.listbox_menu entryconfigure [mc "Forward"] -state normal } $toolbar.back configure -state normal $win.listbox_menu entryconfigure [mc "Back"] -state normal } ## Invoke dialog to create a new directory # @return void public method newdir {} { # Create dialog window set dialog [toplevel $win.new_dir -class {New directory} -bg ${::COMMON_BG_COLOR}] # Create dialog header and EntryBox pack [label $dialog.header -justify left -text [mc "Create new folder in:\n%s" $current_directory]] \ -side top -anchor w -padx 15 -pady 5 pack [ttk::entry $dialog.entry \ ] -side top -fill x -expand 1 -padx 5 -pady 5 # Bind button enter to confirmation action bind $dialog.entry "[list $this create_new_folder]; break" bind $dialog.entry "[list $this create_new_folder]; break" # Create bottom frame (Buttons: "Clear", "Ok" and "Cancel") set button_frame [frame $dialog.bottom] pack [ttk::button $button_frame.clear \ -text [mc "Clear"] \ -compound left \ -image ::ICONS::16::clear_left \ -command "$dialog.entry delete 0 end" \ ] -side left -expand 0 -padx 2 pack [ttk::button $button_frame.ok \ -text [mc "Ok"] \ -compound left \ -image ::ICONS::16::ok \ -command [list $this create_new_folder] \ ] -side left -expand 0 -padx 2 pack [ttk::button $button_frame.cancel \ -text [mc "Cancel"] \ -compound left \ -image ::ICONS::16::button_cancel \ -command " grab release $dialog destroy $dialog " \ ] -side left -expand 0 -padx 2 pack $button_frame -side bottom -anchor e -expand 0 -padx 5 -pady 5 # Configure dialog window wm iconphoto $dialog ::ICONS::16::folder_new wm title $dialog [mc "New folder"] wm resizable $dialog 1 0 wm minsize $dialog 340 120 wm geometry $dialog 340x120 wm protocol $dialog WM_DELETE_WINDOW " grab release $dialog destroy $dialog " wm transient $dialog $win grab $dialog raise $dialog focus -force $dialog.entry tkwait window $dialog } ## Confirm dialog "Create new folder" # @return void public method create_new_folder {} { set dialog ${win}.new_dir set folder [$dialog.entry get] set error 0 if {$folder == {}} { set error 1 } if {$error || [catch {file mkdir [file join $current_directory $folder]}]} { tk_messageBox \ -parent $dialog \ -icon warning \ -type ok \ -title [mc "Unable to create folder"] \ -message [mc "Unable to create the specified folder"] } else { grab release $dialog destroy $dialog reload } } ## Sort the given list of strings # This procedure is closely related to inner logic of this #+ class and it is difficult to properly explain its function # @parm List items - List to sort # @return void proc sort_items {items} { # Determinate sorting order if {${::KIFSD::FSD::config(reverse_sorting)}} { set order "-decreasing" } else { set order "-increasing" } if {${::KIFSD::FSD::config(sorting)} == {name}} { if {${::KIFSD::FSD::config(case_insensitive)}} { set method "-dictionary" } else { set method "-ascii" } return [lsort $method $order $items] } else { if {${::KIFSD::FSD::config(sorting)} == {size}} { # Sort by size set index 2 } else { # Sort by date set index 1 } set items [lsort -index $index $order $items] set result {} foreach file $items { lappend result [lindex $file 0] } return $result } } ## Get unsorted list of subdirectories in the given directory # @parm String dir - Directory # @return List - List of relative URLs proc get_dirs_simple {dir} { # Search for directories set result [list] catch { ;# For Microsoft Windows it has to be enclosed by catch set result [glob -nocomplain -tails -directory $dir -types d *] } # Include hidden directories if {${::KIFSD::FSD::config(show_hidden_files)}} { catch { ;# For Microsoft Windows it has to be enclosed by catch set result [concat $result [glob -nocomplain -tails -directory $dir -types {d hidden} *]] } # Filter "." and ".." set foo_idx [lsearch $result {..}] if {$foo_idx != -1} { set result [lreplace $result $foo_idx $foo_idx] set foo_idx [lsearch $result {.}] if {$foo_idx != -1} { set result [lreplace $result $foo_idx $foo_idx] } } } return $result } ## Get unsorted list of subdirectories in the given directory # @parm String dir - Directory # @return List - {{relative_URL mtime size_in_B} ... } proc get_dirs_extended {dir} { set result {} # Search for directories catch { ;# For Microsoft Windows it has to be enclosed by catch foreach file [glob -nocomplain -tails -directory $dir -types d *] { lappend result [list $file [file mtime [file join $dir $file]] 0] } } # Include hidden directories if {${::KIFSD::FSD::config(show_hidden_files)}} { catch { ;# For Microsoft Windows it has to be enclosed by catch foreach file [glob -nocomplain -tails -directory $dir -types {d hidden} *] { # Filter "." and ".." if {$file == {.} || $file == {..}} { continue } # Translate to full URL lappend result [list $file [file mtime [file join $dir $file]] 0] } } } return $result } ## Get unsorted list of files in the given directory matching the given GLOB # @parm String dir - Directory # @parm GLOB mask - Glob expression # @return List - List of relative URLs proc get_files_simple {dir mask} { set result [list] catch { ;# For Microsoft Windows it has to be enclosed by catch set result [glob -nocomplain -tails -directory $dir -types f $mask] } if {${::KIFSD::FSD::config(show_hidden_files)}} { catch { ;# For Microsoft Windows it has to be enclosed by catch set result [concat $result \ [glob -nocomplain -tails -directory $dir -types {f hidden} $mask]] } } return $result } ## Get unsorted list of files in the given directory matching the given GLOB # @parm String dir - Directory # @parm GLOB mask - Glob expression # @return List - {{relative_URL mtime size_in_B} ... } proc get_files_extended {dir mask} { set result {} # Search for files matching the given GLOB catch { ;# For Microsoft Windows it has to be enclosed by catch foreach file [glob -nocomplain -tails -directory $dir -types f $mask] { if {[catch { lappend result [list \ $file \ [file mtime [file join $dir $file]] \ [file size [file join $dir $file]] \ ] }]} then { lappend result [list $file 0 0] } } } # Include hidden files if {${::KIFSD::FSD::config(show_hidden_files)}} { catch { ;# For Microsoft Windows it has to be enclosed by catch foreach file [glob -nocomplain -tails -directory $dir -types {f hidden} $mask] { if {[catch { lappend result [list \ $file \ [file mtime [file join $dir $file]] \ [file size [file join $dir $file]] \ ] }]} then { lappend result [list $file 0 0] } } } } return $result } ## Get list of items to load to directories ListBox # @parm String dir - Source directory # @parm Bool no_detail=0 - No details # @return List - {text text ...} proc dir_cmd {dir {no_detail 0}} { # Normalize directory and determinate its parent set dir [file normalize $dir] if {$dir != {/}} { set parent {..} } else { set parent {} } if {${::KIFSD::FSD::config(sorting)} == {name}} { set result [sort_items [get_dirs_simple $dir]] } else { set result [sort_items [get_dirs_extended $dir]] } if {!$no_detail && ${::KIFSD::FSD::config(detailed_view)}} { return [concat $parent [add_details $result $dir]] } else { return [concat $parent $result] } } ## Get list of items to load to files ListBox # @parm String dir - Source directory # @parm GLOB mask - GLOB expression which must match each returned file # @parm Bool no_detail=0 - Detailed view # @return List - {text text ...} proc file_cmd {dir mask {no_detail 0}} { if {${::KIFSD::FSD::config(sorting)} == {name}} { set result [sort_items [get_files_simple $dir $mask]] } else { set result [sort_items [get_files_extended $dir $mask]] } if {!$no_detail && ${::KIFSD::FSD::config(detailed_view)}} { return [add_details $result $dir] } else { return $result } } ## Adjust list of files/directories returned by proc. file_cmd to #+ format required to display in detailed view mode # @parm List filelist - List returned by procedure file_cmd # @parm String dir - Directory # @return List - {{text text text ... } ... } proc add_details {filelist dir} { set result {} foreach filename $filelist { set line $filename set fullfilename [file join $dir $filename] if {[string length $line] > 31} { set line [string range $line 0 27] append line {...} } if {[catch { append line [string repeat { } [expr {35 - [string length $line]}]] set size [file size $fullfilename] if {$size < 1024} { append size { B} } elseif {$size < 1048576} { set size [expr {($size * 10) / 1024}] if {$size > 1023} { set size [expr {$size / 10}] } else { set size [string range $size 0 {end-1}].[string range $size end end] } append size { kB} } elseif {$size < 1073741824} { set size [expr {($size * 10) / 1048576}] if {$size > 1023} { set size [expr {$size / 10}] } else { set size [string range $size 0 {end-1}].[string range $size end end] } append size { MB} } elseif {$size < 1099511627776} { set size [expr {($size * 10) / 1073741824}] if {$size > 1023} { set size [expr {$size / 10}] } else { set size [string range $size 0 {end-1}].[string range $size end end] } append size { GB} } else { set size {>1TB} } }]} then { append line { - ---- -------- -----} } else { if {!$::MICROSOFT_WINDOWS} { append line [string repeat { } [expr {8 - [string length $size]}]] $size " " \ [string range [lindex [file attributes $fullfilename] 5] {end-3} end] " " \ [clock format [file mtime $fullfilename] -format {%D %R}] } else { append line [string repeat { } [expr {8 - [string length $size]}]] $size " " \ [clock format [file mtime $fullfilename] -format {%D %R}] } } lappend result [list $line $filename] } return $result } ## Get list of items to load to files ListBox (mode "Separate folders" OFF) # @parm String dir - Source directory # @parm GLOB mask - GLOB expression which must match each returned file # @return List - {text text ...} proc dir_file_cmd {dir mask} { set dir [file normalize $dir] set result {} # Determinate list of directories if {${::KIFSD::FSD::config(sorting)} == {name}} { set result [concat [get_dirs_simple $dir] [get_files_simple $dir $mask]] } else { set result [concat [get_dirs_extended $dir] [get_files_extended $dir $mask]] } if {$dir != {/}} { set parent [list [list {..} {u}]] } else { set parent {} } set tmp_result {} # Determinate list of files if {${::KIFSD::FSD::config(detailed_view)}} { foreach item [sort_items $result] { if {![file exists [file join $dir $item]]} {continue} if {[file isdirectory [file join $dir $item]]} { lappend tmp_result [concat [add_details [list $item] $dir] d] } else { lappend tmp_result [concat [add_details [list $item] $dir] f] } } } else { foreach item [sort_items $result] { if {![file exists [file join $dir $item]]} {continue} if {[file isdirectory [file join $dir $item]]} { lappend tmp_result [list $item d] } else { lappend tmp_result [list $item f] } } } return [concat $parent $tmp_result] } ## Get configuration list for procedure load_config_array # @return List - (List which specifies bookmarks, settings and such things) proc get_config_array {} { return [regsub -all "\n" [array get ::KIFSD::FSD::config] { }] } ## Load configuration list returned by procedure get_config_array # @parm List config - (List which specifies bookmarks, settings and such things) # @return void proc load_config_array {config} { if {$config == {}} { return } if {[catch { array set ::KIFSD::FSD::config $config }]} then { puts stderr "KI File Selection Dialog: Unable to load the given configuration string -- using default" return 0 } else { return 1 } } ## Get descriptor of dialog window # @return Widget - Dialog window public method get_window_name {} { return $win } ## Determinate path to the "Desktop" folder. # @return String - The path, e.g. "~/Arbeitsfläche" in case of German Ubuntu. proc get_desktop_dir {} { if {![catch { set f [open "~/.config/user-dirs.dirs" "r"] }]} then { while {![eof $f]} { set l [gets $f] if {[string first "XDG_DESKTOP_DIR=" $l] != -1} { if {[regexp {"[^\"]+"} $l d]} { set d [string range $d 1 end-1] regsub {\$HOME} $d {~} d return $d } } } close $f } return "~/Desktop" } } ## Text variables for dialog "Edit entry in Quick access bar" set KIFSD::FSD::qa_panel_dialog_url_entry {} ;# Entry URL set KIFSD::FSD::qa_panel_dialog_name_entry {} ;# Entry name set KIFSD::FSD::qa_panel_dialog_icon {} ;# Icon number [0;3] ## Dialog configuration array (these values are daults) # Invalid configuration list may cause program error ! array set KIFSD::FSD::config { win_geometry {720x380} detailed_view 0 separate_folders 1 quick_access_panel 1 sorting name reverse_sorting 0 folders_first 1 case_insensitive 1 show_hidden_files 0 right_PW_size 200 main_PW_size 180 bookmarks {} } if {$::MICROSOFT_WINDOWS} { set KIFSD::FSD::config(quick_access_bar_data) [subst { {0 {System Drive ${::env(SystemDrive)}} {${::env(SystemDrive)}}} {1 {Documents and Settings} {${::env(USERPROFILE)}}} }] } else { set KIFSD::FSD::config(quick_access_bar_data) [subst { {0 {/} {/}} {0 {Removable media} {/media}} {1 {Home} {~}} {2 {Desktop} {[KIFSD::FSD::get_desktop_dir]}} }] } # >>> File inclusion guard } # <<< File inclusion guard