File: dirViewer_tile.tcl

package info (click to toggle)
tklib 0.6-1%2Bdeb8u1
  • links: PTS
  • area: main
  • in suites: jessie
  • size: 16,112 kB
  • ctags: 4,008
  • sloc: tcl: 65,204; sh: 6,870; ansic: 792; pascal: 359; makefile: 73; exp: 21; sed: 16
file content (404 lines) | stat: -rwxr-xr-x 13,070 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#!/bin/sh
# the next line restarts using wish \
exec wish "$0" ${1+"$@"}

#==============================================================================
# Demonstrates how to use a tablelist widget for displaying the contents of a
# directory.
#
# Copyright (c) 2010-2012  Csaba Nemethi (E-mail: csaba.nemethi@t-online.de)
#==============================================================================

package require tablelist_tile 5.7

#
# Add some entries to the Tk option database
#
set dir [file dirname [info script]]
source [file join $dir option_tile.tcl]

#
# Create three images
#
image create photo clsdFolderImg -file [file join $dir clsdFolder.gif]
image create photo openFolderImg -file [file join $dir openFolder.gif]
image create photo fileImg       -file [file join $dir file.gif]

#
# Work around the improper appearance of the tile scrollbars in the aqua theme
#
if {[tablelist::getCurrentTheme] eq "aqua"} {
    interp alias {} ttk::scrollbar {} ::scrollbar
}

#------------------------------------------------------------------------------
# displayContents
#
# Displays the contents of the directory dir in a tablelist widget.
#------------------------------------------------------------------------------
proc displayContents dir {
    #
    # Create a scrolled tablelist widget with 3 dynamic-
    # width columns and interactive sort capability
    #
    set tf .tf
    ttk::frame $tf -class ScrollArea
    set tbl $tf.tbl
    set vsb $tf.vsb
    set hsb $tf.hsb
    tablelist::tablelist $tbl \
	-columns {0 "Name"	    left
		  0 "Size"	    right
		  0 "Date Modified" left} \
	-expandcommand expandCmd -collapsecommand collapseCmd \
	-xscrollcommand [list $hsb set] -yscrollcommand [list $vsb set] \
	-movablecolumns no -setgrid no -showseparators yes -height 18 -width 80
    if {[$tbl cget -selectborderwidth] == 0} {
	$tbl configure -spacing 1
    }
    $tbl columnconfigure 0 -formatcommand formatString -sortmode dictionary
    $tbl columnconfigure 1 -formatcommand formatSize -sortmode integer
    $tbl columnconfigure 2 -formatcommand formatString
    ttk::scrollbar $vsb -orient vertical   -command [list $tbl yview]
    ttk::scrollbar $hsb -orient horizontal -command [list $tbl xview]

    #
    # Create a pop-up menu with one command entry; bind the script
    # associated with its entry to the <Double-1> event, too
    #
    set menu .menu
    menu $menu -tearoff no
    $menu add command -label "Display Contents" \
		      -command [list putContentsOfSelFolder $tbl]
    set bodyTag [$tbl bodytag]
    bind $bodyTag <<Button3>>  [bind TablelistBody <Button-1>]
    bind $bodyTag <<Button3>> +[bind TablelistBody <ButtonRelease-1>]
    bind $bodyTag <<Button3>> +[list postPopupMenu %X %Y]
    bind $bodyTag <Double-1>   [list putContentsOfSelFolder $tbl]

    #
    # Create three buttons within a frame child of the main widget
    #
    set bf .bf
    ttk::frame $bf
    set b1 $bf.b1
    set b2 $bf.b2
    set b3 $bf.b3
    ttk::button $b1 -width 10 -text "Refresh"
    ttk::button $b2 -width 10 -text "Parent"
    ttk::button $b3 -width 10 -text "Close" -command exit

    #
    # Manage the widgets
    #
    grid $tbl -row 0 -rowspan 2 -column 0 -sticky news
    if {[tablelist::getCurrentTheme] eq "aqua"} {
	grid [$tbl cornerpath] -row 0 -column 1 -sticky ew
	grid $vsb	       -row 1 -column 1 -sticky ns
    } else {
	grid $vsb -row 0 -rowspan 2 -column 1 -sticky ns
    }
    grid $hsb -row 2 -column 0 -sticky ew
    grid rowconfigure    $tf 1 -weight 1
    grid columnconfigure $tf 0 -weight 1
    pack $b1 $b2 $b3 -side left -expand yes -pady 10
    pack $bf -side bottom -fill x
    pack $tf -side top -expand yes -fill both

    #
    # Populate the tablelist with the contents of the given directory
    #
    $tbl sortbycolumn 0
    putContents $dir $tbl root
}

#------------------------------------------------------------------------------
# putContents
#
# Outputs the contents of the directory dir into the tablelist widget tbl, as
# child items of the one identified by nodeIdx.
#------------------------------------------------------------------------------
proc putContents {dir tbl nodeIdx} {
    #
    # The following check is necessary because this procedure
    # is also invoked by the "Refresh" and "Parent" buttons
    #
    if {[string compare $dir ""] != 0 &&
	(![file isdirectory $dir] || ![file readable $dir])} {
	bell
	if {[string compare $nodeIdx "root"] == 0} {
	    set choice [tk_messageBox -title "Error" -icon warning -message \
			"Cannot read directory \"[file nativename $dir]\"\
			-- replacing it with nearest existent ancestor" \
			-type okcancel -default ok]
	    if {[string compare $choice "ok"] == 0} {
		while {![file isdirectory $dir] || ![file readable $dir]} {
		    set dir [file dirname $dir]
		}
	    } else {
		return ""
	    }
	} else {
	    return ""
	}
    }

    if {[string compare $nodeIdx "root"] == 0} {
	if {[string compare $dir ""] == 0} {
	    if {[llength [file volumes]] == 1} {
		wm title . "Contents of the File System"
	    } else {
		wm title . "Contents of the File Systems"
	    }
	} else {
	    wm title . "Contents of the Directory \"[file nativename $dir]\""
	}

	$tbl delete 0 end
	set row 0
    } else {
	set row [expr {$nodeIdx + 1}]
    }

    #
    # Build a list from the data of the subdirectories and
    # files of the directory dir.  Prepend a "D" or "F" to
    # each entry's name and modification date & time, for
    # sorting purposes (it will be removed by formatString).
    #
    set itemList {}
    if {[string compare $dir ""] == 0} {
	foreach volume [file volumes] {
	    lappend itemList [list D[file nativename $volume] -1 D $volume]
	}
    } else {
	foreach entry [glob -nocomplain -types {d f} -directory $dir *] {
	    if {[catch {file mtime $entry} modTime] != 0} {
		continue
	    }

	    if {[file isdirectory $entry]} {
		lappend itemList [list D[file tail $entry] -1 \
		    D[clock format $modTime -format "%Y-%m-%d %H:%M"] $entry]
	    } else {
		lappend itemList [list F[file tail $entry] [file size $entry] \
		    F[clock format $modTime -format "%Y-%m-%d %H:%M"] ""]
	    }
	}
    }

    #
    # Sort the above list and insert it into the tablelist widget
    # tbl as list of children of the row identified by nodeIdx
    #
    set itemList [$tbl applysorting $itemList]
    $tbl insertchildlist $nodeIdx end $itemList

    #
    # Insert an image into the first cell of each newly inserted row
    #
    foreach item $itemList {
	set name [lindex $item end]
	if {[string compare $name ""] == 0} {			;# file
	    $tbl cellconfigure $row,0 -image fileImg
	} else {						;# directory
	    $tbl cellconfigure $row,0 -image clsdFolderImg
	    $tbl rowattrib $row pathName $name

	    #
	    # Mark the row as collapsed if the directory is non-empty
	    #
	    if {[file readable $name] && [llength \
		[glob -nocomplain -types {d f} -directory $name *]] != 0} {
		$tbl collapse $row
	    }
	}

	incr row
    }

    if {[string compare $nodeIdx "root"] == 0} {
	#
	# Configure the "Refresh" and "Parent" buttons
	#
	.bf.b1 configure -command [list refreshView $dir $tbl]
	set b2 .bf.b2
	if {[string compare $dir ""] == 0} {
	    $b2 configure -state disabled
	} else {
	    $b2 configure -state normal
	    set p [file dirname $dir]
	    if {[string compare $p $dir] == 0} {
		$b2 configure -command [list putContents "" $tbl root]
	    } else {
		$b2 configure -command [list putContents $p $tbl root]
	    }
	}
    }
}

#------------------------------------------------------------------------------
# formatString
#
# Returns the substring obtained from the specified value by removing its first
# character.
#------------------------------------------------------------------------------
proc formatString val {
    return [string range $val 1 end]
}

#------------------------------------------------------------------------------
# formatSize
#
# Returns an empty string if the specified value is negative and the value
# itself in user-friendly format otherwise.
#------------------------------------------------------------------------------
proc formatSize val {
    if {$val < 0} {
	return ""
    } elseif {$val < 1024} {
	return "$val bytes"
    } elseif {$val < 1048576} {
	return [format "%.1f KB" [expr {$val / 1024.0}]]
    } elseif {$val < 1073741824} {
	return [format "%.1f MB" [expr {$val / 1048576.0}]]
    } else {
	return [format "%.1f GB" [expr {$val / 1073741824.0}]]
    }
}

#------------------------------------------------------------------------------
# expandCmd
#
# Outputs the contents of the directory whose leaf name is displayed in the
# first cell of the specified row of the tablelist widget tbl, as child items
# of the one identified by row, and updates the image displayed in that cell.
#------------------------------------------------------------------------------
proc expandCmd {tbl row} {
    if {[$tbl childcount $row] == 0} {
	set dir [$tbl rowattrib $row pathName]
	putContents $dir $tbl $row
    }

    if {[$tbl childcount $row] != 0} {
	$tbl cellconfigure $row,0 -image openFolderImg
    }
}

#------------------------------------------------------------------------------
# collapseCmd
#
# Updates the image displayed in the first cell of the specified row of the
# tablelist widget tbl.
#------------------------------------------------------------------------------
proc collapseCmd {tbl row} {
    $tbl cellconfigure $row,0 -image clsdFolderImg
}

#------------------------------------------------------------------------------
# putContentsOfSelFolder
#
# Outputs the contents of the selected folder into the tablelist widget tbl.
#------------------------------------------------------------------------------
proc putContentsOfSelFolder tbl {
    set row [$tbl curselection]
    if {[$tbl hasrowattrib $row pathName]} {		;# directory item
	set dir [$tbl rowattrib $row pathName]
	if {[file isdirectory $dir] && [file readable $dir]} {
	    if {[llength [glob -nocomplain -types {d f} -directory $dir *]]
		== 0} {
		bell
	    } else {
		putContents $dir $tbl root
	    }
	} else {
	    bell
	    tk_messageBox -title "Error" -icon error -message \
		"Cannot read directory \"[file nativename $dir]\""
	    return ""
	}
    } else {						;# file item
	bell
    }
}

#------------------------------------------------------------------------------
# postPopupMenu
#
# Posts the pop-up menu .menu at the given screen position.  Before posting
# the menu, the procedure enables/disables its only entry, depending upon
# whether the selected item represents a readable directory or not.
#------------------------------------------------------------------------------
proc postPopupMenu {rootX rootY} {
    set tbl .tf.tbl
    set row [$tbl curselection]
    set menu .menu
    if {[$tbl hasrowattrib $row pathName]} {		;# directory item
	set dir [$tbl rowattrib $row pathName]
	if {[file isdirectory $dir] && [file readable $dir]} {
	    if {[llength [glob -nocomplain -types {d f} -directory $dir *]]
		== 0} {
		$menu entryconfigure 0 -state disabled
	    } else {
		$menu entryconfigure 0 -state normal
	    }
	} else {
	    bell
	    tk_messageBox -title "Error" -icon error -message \
		"Cannot read directory \"[file nativename $dir]\""
	    return ""
	}
    } else {						;# file item
	$menu entryconfigure 0 -state disabled
    }

    tk_popup $menu $rootX $rootY
}

#------------------------------------------------------------------------------
# refreshView
#
# Redisplays the contents of the directory dir in the tablelist widget tbl and
# restores the expanded states of the folders as well as the vertical view.
#------------------------------------------------------------------------------
proc refreshView {dir tbl} {
    #
    # Save the vertical view and get the path names
    # of the folders displayed in the expanded rows
    #
    set yView [$tbl yview]
    foreach key [$tbl expandedkeys] {
	set pathName [$tbl rowattrib $key pathName]
	set expandedFolders($pathName) 1
    }

    #
    # Redisplay the directory's (possibly changed) contents and restore
    # the expanded states of the folders, along with the vertical view
    #
    putContents $dir $tbl root
    restoreExpandedStates $tbl root expandedFolders
    $tbl yview moveto [lindex $yView 0]
}

#------------------------------------------------------------------------------
# restoreExpandedStates
#
# Expands those children of the parent identified by nodeIdx that display
# folders whose path names are the names of the elements of the array specified
# by the last argument.
#------------------------------------------------------------------------------
proc restoreExpandedStates {tbl nodeIdx expandedFoldersName} {
    upvar $expandedFoldersName expandedFolders

    foreach key [$tbl childkeys $nodeIdx] {
	set pathName [$tbl rowattrib $key pathName]
	if {[string compare $pathName ""] != 0 &&
	    [info exists expandedFolders($pathName)]} {
	    $tbl expand $key -partly
	    restoreExpandedStates $tbl $key expandedFolders
	}
    }
}

displayContents ""