1#
2# fdisp.tcl
3#
4# Folder display, handling nesting and highlights to reflect folder state.
5#
6# Copyright (c) 1993 Xerox Corporation.
7# Use and copying of this software and preparation of derivative works based
8# upon this software are permitted. Any distribution of this software or
9# derivative works must comply with all applicable United States export
10# control laws. This software is made available AS IS, and Xerox Corporation
11# makes no warranty about the software, its performance or its conformity to
12# any specification.
13
14proc Fdisp_Init {} {
15    global fdisp mhProfile exmh flist
16
17    if {[info exists exmh(newuser)] && [info exists flist(allfolders)]} {
18	set N [llength $flist(allfolders)]
19	if {$N < 5} {
20	    set fdisp(maxLines) 1
21	} elseif {$N < 15} {
22	    set fdisp(maxLines) 2
23	} elseif {$N < 30} {
24	    set fdisp(maxLines) 3
25	} else {
26	    set fdisp(maxLines) 4
27	}
28    }
29
30    Preferences_Add "Folder Display" \
31"These items affect the display of the labels in the folder display window.
32If you change key bindings on labels, you'll have to toggle one of the other
33options to force a redisplay because that's when the bindings are set." {
34	{fdisp(maxLines) fdispLines 4 {Max fdisp rows}
35"The maximum number of rows of folder labels in
36the folder display.  If there are more folders than
37will fit in this space, the display becomes scrollable." }
38	{fdisp(toplevel) fl_toplevel OFF {Detached fdisp display}
39"The folder display area can be displayed in a separate
40toplevel window.  You can use the *Fltop.position Xresource
41to control its initial placement on the screen, and the
42*Fltop.Canvas.width and *Fltop.Canvas.height to control
43its size."}
44	{fdisp(oneCol) fdisp1Col OFF {Use one column}
45"Enabling this with a detached display aligns all the
46folders in one vertical column."}
47	{fdisp(popdownStyle) fdispPopdownStyle {CHOICE polygon rectangle} {Subfolder popdown}
48"The style of the sub folder popdown menu
49used to display subfolders." }
50	{fdisp(popdownAction) fdispPopdownAction {CHOICE navbutton enter redisplay} {Popdown action}
51"This determines how the popdown display is triggered:
52navbutton - press navigation button to get the popdown.
53
54enter - move the mouse over the button to get the popdown.
55
56redisplay - do not use popdowns at all.  Instead, navbutton
57causes the whole folder display to change.
58
59The navigation button is settable via a X resource fl_navbutton." }
60	{fdisp(popdownRemove) fdispPopdownRemove {CHOICE leave navbutton} {Remove popdown on...}
61"This determines what causes a popdown display to be removed:
62navbutton - press navigation button on another label.
63
64leave - leave the area of the popdown.  This actually is
65implemented by triggering on <Enter> to other labels.
66
67The navigation button is settable via a X resource fl_navbutton." }
68{fdisp(tarbuttonAction) fdispTarbuttonAction {CHOICE {select+move} {select+copy} {select only}} {Action when Target button clicked...}
69"This determines what action is taken when the \"target\"
70mouse button is clicked.  The target button is usually mouse
71button 3 but can be changed by setting X resource fl_tarbutton.
72
73select+move - Selects the folder at the mouse cursor as
74the target folder and moves the current message to the
75target folder.
76
77select+copy - Selects the folder at the mouse cursor as
78the target folder and copies the current message to the
79target folder.
80
81select only - Selects the folder at the mouse cursor as
82the target folder." }
83{findSettings(defaultLoc) findSettingsDefaultLoc {CHOICE FTOC Msg}
84"Default search location"
85"Determines whether, initially, a search takes place in the \"folder
86table of contents\" window (if set to \"FTOC\") or the \"message\" window
87(if set to \"Msg\")."}
88    }
89    # The remaining parameters can be overridden by hand in the user resources
90
91    Preferences_Resource fdisp(font)		fl_font fixed
92    Preferences_Resource fdisp(xgap)		fl_xgap 8
93    Preferences_Resource fdisp(ygap)		fl_ygap 8
94    Preferences_Resource fdisp(curbutton)	fl_curbutton 1
95    Preferences_Resource fdisp(navbutton)	fl_navbutton 2
96    Preferences_Resource fdisp(tarbutton)	fl_tarbutton 3
97
98    Preferences_Resource fdisp(c_fg)		c_foreground black
99    Preferences_Resource fdisp(c_bg)		c_background white
100    Preferences_Resource fdisp(c_current)	c_current red
101    Preferences_Resource fdisp(c_unseen)	c_unseen  blue
102    Preferences_Resource fdisp(c_unseenBg)	c_unseenBg  $fdisp(c_bg)
103    Preferences_Resource fdisp(c_moved)		c_moved   yellow
104    Preferences_Resource fdisp(c_movedFg)	c_movedFg   $fdisp(c_fg)
105    Preferences_Resource fdisp(c_popup)		c_popup   wheat
106
107    trace variable fdisp(font) w FdispFixupFont
108    set fdisp(lastFont) $fdisp(font)
109    trace variable fdisp(maxLines) w FdispFixupMaxLines
110    set fdisp(lastMaxLines) $fdisp(maxLines)
111    trace variable fdisp(toplevel) w FdispFixupToplevel
112    set fdisp(lastToplevel) $fdisp(toplevel)
113    trace variable fdisp(oneCol) w FdispFixupRedisplay
114    trace variable fdisp(popdownStyle) w FdispFixupRedisplay
115}
116
117# Hooks for recreating the folder display when config changes
118proc FdispFixupMaxLines { args } {
119    global exwin fdisp
120
121    if [catch {expr {$fdisp(maxLines) * 2}}] {
122	set fdisp(maxLines) $fdisp(lastMaxLines)
123	return	;# bogus value
124    }
125    if {$fdisp(maxLines) != $fdisp(lastMaxLines)} {
126	set fdisp(width,canvas) 0
127	set fdisp(maxLines,$fdisp(canvas)) $fdisp(maxLines)
128	set fdisp(lastMaxLines) $fdisp(maxLines)
129	set h [expr {$fdisp(maxLines)*($fdisp(itemHeight) + $fdisp(ygap)) + $fdisp(ygap)}]
130	$fdisp(canvas) configure -height $h
131    }
132}
133proc FdispFixupRedisplay { args } {
134    Fdisp_Redisplay
135}
136proc FdispFixupToplevel { args } {
137    global fdisp
138    if {$fdisp(toplevel) != $fdisp(lastToplevel)} {
139	if [info exists fdisp(topWidget)] {
140	    destroy $fdisp(topWidget)
141	    unset fdisp(topWidget)
142	} else {
143	    destroy $fdisp(frame)
144	    unset fdisp(frame)
145	}
146	FdispMake
147    }
148    set fdisp(lastToplevel) $fdisp(toplevel)
149}
150
151# Make the folder display
152proc FdispMake {} {
153    global fdisp
154    if {$fdisp(toplevel)} {
155	FdispMakeToplevel
156    } else {
157	FdispMakeFrame
158    }
159}
160
161# Create folder list in a toplevel and make the canvas inside it
162proc FdispMakeToplevel { } {
163    global fdisp
164    Exwin_Toplevel .fl "Folder list" Fltop nomenu
165    wm protocol .fl WM_TAKE_FOCUS {
166	global exwin
167	focus $exwin(mtext)
168    }
169    set fdisp(topWidget) .fl
170    wm minsize $fdisp(topWidget) 100 30
171    wm protocol .fl WM_DELETE_WINDOW FdispDeleted
172    FdispMakeCanvas $fdisp(topWidget)
173    set icon [option get $fdisp(topWidget) iconposition IconPosition]
174    catch {
175	Exwin_IconPosition $fdisp(topWidget) $icon
176    }
177    set iconic [option get $fdisp(topWidget) iconic Iconic]
178    if {$iconic == {}} {
179	set iconic $exmh(iconic)
180    }
181    if {$iconic} {
182	wm iconify $fdisp(topWidget)
183    }
184}
185proc FdispDeleted {} {
186    wm iconify .fl
187    Exmh_Status "Folder display closed, not destroyed"
188}
189proc Fdisp_Checkpoint { varName } {
190    # Add Xresources lines to $varName that save window size
191    upvar $varName newstuff
192    global fdisp
193    catch {
194	set can $fdisp(topWidget).can
195	set width [winfo width $can]
196	set height [winfo height $can]
197	set bd [$can cget -borderwidth]
198	incr bd [$can cget -highlightthickness]
199	set width [expr $width - 2*$bd]
200	set height [expr $height - 2*$bd]
201	lappend newstuff "*Fltop.Canvas.height:\t$height"
202	lappend newstuff "*Fltop.Canvas.width:\t$width"
203    }
204}
205
206# Create folder list in a frame and make the canvas inside it
207proc FdispMakeFrame { } {
208    global fdisp
209    set fdisp(frame) [Widget_Frame $fdisp(parent) f1 Frame]
210    FdispMakeCanvas $fdisp(frame)
211}
212
213# Create the canvas for the folder display
214proc FdispMakeCanvas { frame } {
215    global fdisp exwin
216    set fdisp(canvas) [canvas $frame.can -bd 2 -relief raised \
217	-highlightthickness 0]
218    set s [scrollbar $frame.sv -command [list $fdisp(canvas) yview] \
219	-highlightthickness 0]
220    $fdisp(canvas) configure -yscrollcommand [list $s set]
221
222    # Find out how big labels are
223    if [catch {
224	set id [$fdisp(canvas) create text 0 0 \
225	    -anchor nw -justify center -text 0123456789 -font $fdisp(font)]
226    } err] {
227	Exmh_Status $err
228	set fdisp(font) fixed
229	set id [$fdisp(canvas) create text 0 0 \
230	    -anchor nw -justify center -text 0123456789 -font $fdisp(font)]
231    }
232    set size [$fdisp(canvas) bbox $id]
233    set fdisp(itemHeight) [expr {[lindex $size 3] - [lindex $size 1]}]
234    set fdisp(charWidth) [expr {([lindex $size 2] - [lindex $size 0])/10}]
235    $fdisp(canvas) delete $id
236
237    catch {
238	$fdisp(canvas) configure -yscrollincrement \
239	    [expr {$fdisp(itemHeight)+$fdisp(ygap)+1}]
240    }
241
242    if {!$fdisp(toplevel)} {
243	set h [expr {$fdisp(maxLines)*($fdisp(itemHeight) + $fdisp(ygap)) + $fdisp(ygap)}]
244	$fdisp(canvas) configure -height $h
245    }
246
247    bindtags $fdisp(canvas) \
248	[list $fdisp(canvas) Command [winfo toplevel $fdisp(canvas)] all]
249    bind $fdisp(canvas) <2> {%W scan mark %x %y}
250    bind $fdisp(canvas) <B2-Motion> {%W scan dragto %x %y}
251    bind $fdisp(canvas) <Configure> FdispCanvasConfigure
252    pack $s -side $exwin(scrollbarSide) -fill y
253    pack $fdisp(canvas) -side $exwin(scrollbarSide) -fill both -expand 1
254
255    # Enable wheelscroll if desired
256    if {$exwin(wheelEnabled)} {
257        fmscroll $fdisp(canvas) 5
258    }
259
260    # fdisp popup color hack
261    if {[winfo depth $fdisp(canvas)] <= 4} {
262	if {! [regexp {black|white} $fdisp(c_popup)]} {
263	    set fdisp(c_popup) [$fdisp(canvas) cget -bg]
264	}
265    }
266
267    FdispDragAttach canvas
268}
269proc FdispFixupFont { args } {
270    global exwin fdisp
271    if {$fdisp(lastFont) != $fdisp(font)} {
272
273	# Find out how big labels are
274	if [catch {
275	    set id [$fdisp(canvas) create text 0 0 \
276		-anchor nw -justify center -text foo -font $fdisp(font)]
277	} err] {
278	    Exmh_Status $err
279	    set fdisp(font) fixed
280	    set id [$fdisp(canvas) create text 0 0 \
281		-anchor nw -justify center -text foo -font $fdisp(font)]
282	}
283	set size [$fdisp(canvas) bbox $id]
284	set fdisp(itemHeight) [expr {[lindex $size 3] - [lindex $size 1]}]
285	$fdisp(canvas) delete $id
286	set fdisp(lastFont) $fdisp(font)
287
288	# Changing canvas size triggers redisplay
289	set h [expr {$fdisp(maxLines)*($fdisp(itemHeight) + $fdisp(ygap)) + $fdisp(ygap)}]
290	$fdisp(canvas) configure -height $h
291	if [info exists fdisp(cache)] {
292	    set h [expr {($fdisp(itemHeight) + $fdisp(ygap)) + $fdisp(ygap)}]
293	    $fdisp(cache) configure -height $h
294	}
295    }
296}
297
298proc Fdisp_Window { parent } {
299    global fdisp exwin
300
301    set fdisp(parent) $parent
302
303    # a bogus child is needed inside fdisp(parent) so it properly
304    # shrinks down when the cache is removed or when the main display
305    # is moved to a separate top-level
306    Widget_Frame $parent bogus Frame
307
308    # The following creates fdisp(canvas), either in a toplevel or a frame
309    FdispMake
310
311    global fcache
312    if $fcache(enabled) {
313	Fcache_CreateWindow
314	FdispDragAttach cache
315    }
316
317    set fdisp(folder) .
318    foreach can {canvas cache} {
319	set fdisp(entered,$can) 0		;# Display routine entered
320	set fdisp(pending,$can) 0		;# Display routine blocked
321	set fdisp(width,$can) 0			;# last display width
322	set fdisp(fset,$can) {}			;# last folder set
323	set fdisp(cur,$can) {}			;# current folder name
324	set fdisp(tar,$can) {}			;# target folder name
325	set fdisp(curid,$can) {}		;# canvas item ids
326	set fdisp(boxid,$can) {}
327	set fdisp(tarid,$can) {}
328	set fdisp(tboxid,$can) {}
329	set fdisp(leafs,$can) {}		;# list of leaf highlight tags
330    }
331
332}
333proc Fdisp_Redisplay {} {
334    global fdisp
335    FdispMain $fdisp(folder) 1
336    Fcache_Display 1
337}
338
339proc FdispCanvasConfigure {} {
340    global fdisp
341    FdispMain $fdisp(folder) 1
342}
343
344proc FdispMain { {folder {.}} {force 0} } {
345    # Layout the current level of folder buttons on the canvas
346    global fdisp exmh
347    Label_Main [expr {[string compare $folder "."]==0 ? {} : "$folder"}]
348    set fdisp(folder) $folder
349    Flist_FindAllFolders
350    set folderSet [Flist_FolderSet $folder]
351    set len [llength $folderSet]
352    set msec [lindex [time [list Fdisp_Layout canvas $folderSet $folder $force]] 0]
353    Exmh_Debug Fdisp_HighlightCanvas [time [list Fdisp_HighlightCanvas canvas]]
354}
355
356proc Fdisp_Layout { can folderSet {folder {}} {force 0} } {
357    # Main layout routine.  Because this is triggered by
358    # <Configure> events, and because it dinks with the
359    # size of the canvas, it needs to be reentrant.
360    #
361    global fdisp
362
363    set canvas $fdisp($can)
364
365    if {$fdisp(entered,$can)} {
366	set fdisp(pending,$can) 1
367	return
368    }
369    set width [winfo width $canvas]
370    set bd [$canvas cget -borderwidth]
371    incr bd [$canvas cget -highlightthickness]
372    set width [expr $width - 2*$bd]
373
374    if {! $force &&
375	($width == $fdisp(width,$can)) &&
376	($folderSet == $fdisp(fset,$can))} {
377	if {$fdisp(pending,$can)} {
378	    set fdisp(pending,$can) 0
379	    after 1 [list Fdisp_Layout $can $folderSet $folder]
380	}
381	return
382    }
383    incr fdisp(entered,$can)
384
385    set fdisp(width,$can) $width
386    set fdisp(fset,$can) $folderSet
387
388    catch { $canvas delete all }
389    Fdisp_ClearSpecials $canvas
390
391    if {$can != "cache"} {
392	FdispPopdownReset
393	Exmh_Status "Building folder display... $folder"
394    }
395    set fdisp(maxy,$can) [FdispLayoutInner $can $fdisp(xgap) $fdisp(ygap) \
396		    $width $folderSet $folder FdispBindLabel]
397
398    set fdisp(bgid,$can) [$canvas create rect 0 0 0 0 \
399		-fill [$canvas cget -bg] -outline ""]
400    $canvas lower $fdisp(bgid,$can)
401    FdispSetCanvasSize $can $fdisp(maxy,$can)
402    if {$can != "cache"} {
403	Exmh_Status ""
404    }
405    incr fdisp(entered,$can) -1
406    if {$fdisp(pending,$can)} {
407	set fdisp(pending,$can) 0
408	after 1 [list Fdisp_Layout $can $folderSet $folder]
409    }
410}
411proc FdispLayoutInner { can x1 y1 width folderSet folder bindProc {skipSelf no} {tag _notag_} } {
412    global fdisp
413    set canvas $fdisp($can)
414    set maxy $fdisp(itemHeight)		;# Per row max item height
415    set x $x1
416    set y $y1
417    set iscache [string match cache $can]
418    foreach f $folderSet {
419	# Determine label text for the folder
420	if {[string compare $f $folder] == 0} {
421	    if [string match skipSelf $skipSelf] {
422		continue
423	    } else {
424		set text ".."
425	    }
426	} else {
427	    if $iscache {
428		set text [Fcache_FolderName $f]
429	    } else {
430		set text [file tail $f]
431	    }
432	}
433	# Create the text (or bitmap) at location 0 0
434	set id [Fdisp_Label $canvas $f $text]
435	set bbox [$canvas bbox $id]
436	set twidth [expr [lindex $bbox 2]-[lindex $bbox 0]]
437	set theight [expr [lindex $bbox 3]-[lindex $bbox 1]]
438	if {($twidth + $fdisp(xgap)/2 + $x > $width) ||
439		(($fdisp(oneCol) && !$iscache) && ($y > $y1 || $x > $x1))} {
440	    incr y [expr {$fdisp(ygap) + $maxy}]
441	    set x $x1
442	    set maxy $fdisp(itemHeight)		;# Per row max item height
443	}
444	if {$theight > $maxy} {
445	    set maxy $theight
446	}
447	# Move it into position after we see how big it is.
448	$canvas move $id $x $y
449	incr x [expr {$fdisp(xgap) + $twidth}]
450
451	# Determine style of the box, depending on nesting
452	if {[string compare $f $folder] == 0} {
453	    set ftype goParent
454	} else {
455	    if [Flist_SubFolders $f] {
456		if $iscache {
457		    # This supresses the drop-shadow in the cache display,
458		    # but also turns off the redisplay mode behavior...
459		    set ftype leaf
460		} else {
461		    set ftype hasNested
462		}
463	    } else {
464		set ftype leaf
465	    }
466	}
467	set box [Fdisp_Box $fdisp($can) $id $ftype $tag]
468	FdispUpdateMap $can $f $id
469	FdispUpdateBmap $can $f $box
470	$bindProc $can $id $ftype $f
471	if {$fdisp(popdownAction) != "enter"} {
472	    $bindProc $can $box $ftype $f
473	}
474    }
475    return [expr $y + $maxy]
476}
477proc Fdisp_Label { canvas f text } {
478    global fdisp folderInfo fdispSpecial
479    if [info exists folderInfo(bitmap,$f)] {
480	set special 0
481	if [info exists folderInfo(fg,$f)] {
482	    set fg $folderInfo(fg,$f)
483	    set special 1
484	} else {
485	    set fg black
486	}
487	if [info exists folderInfo(bg,$f)] {
488	    set bg $folderInfo(bg,$f)
489	    set special 1
490	} else {
491	    set bg white
492	}
493	set id [$canvas create bitmap 0 0 -anchor nw \
494		    -bitmap $folderInfo(bitmap,$f) \
495		    -foreground $fg -background $bg]
496	if {! $special} {
497	    $canvas addtag bitmap withtag $id
498	} else {
499	    lappend fdispSpecial($canvas) $id
500	    set fdispSpecial($canvas,$id) [list $fg $bg]
501	}
502    } else {
503	set id [$canvas create text 0 0 -anchor nw \
504		-justify center -text $text -font $fdisp(font) -tag text]
505    }
506    return $id
507}
508proc Fdisp_FixupSpecials { canvas } {
509    global fdispSpecial
510    if ![info exists fdispSpecial($canvas)] {
511	return
512    }
513    foreach id $fdispSpecial($canvas) {
514	if [info exists fdispSpecial($canvas,$id)] {
515	    set fg [lindex $fdispSpecial($canvas,$id) 0]
516	    set bg [lindex $fdispSpecial($canvas,$id) 1]
517	    $canvas itemconfigure $id -background $bg -foreground $fg
518	}
519    }
520}
521proc Fdisp_ClearSpecials { canvas } {
522    global fdispSpecial
523    if ![info exists fdispSpecial($canvas)] {
524	return
525    }
526    foreach id $fdispSpecial($canvas) {
527	unset fdispSpecial($canvas,$id)
528    }
529    unset fdispSpecial($canvas)
530}
531proc Fdisp_Box { canvas tid ftype {tag {}} } {
532    # outline box.  I note that for variable width fonts,
533    # the bbox is too long.  Oh well.
534    global fdisp
535
536    if {$tag != {}} {
537	$canvas addtag $tag withtag $tid
538    }
539
540    set bbox [$canvas bbox $tid]
541    set x1 [expr {[lindex $bbox 0] - 1}]
542    set x2 [expr {[lindex $bbox 2] + 1}]
543    set y1 [expr {[lindex $bbox 1] - 1}]
544    set y2 [expr {[lindex $bbox 3] + 1}]
545
546    set box [$canvas create rect $x1 $y1 $x2 $y2 -fill $fdisp(c_bg) \
547	-tags [list box $tag]]
548
549    # Need one box for a dropshadow, and then one extra box to ensure
550    # a stippled foreground obscures the dropshadow box
551    if {[string compare $ftype goParent] == 0} {
552	$canvas lower [$canvas create rect $x1 $y1 $x2 $y2 \
553				-fill $fdisp(c_bg) -tags $tag]
554	$canvas lower [$canvas create rect \
555	    [expr $x1+3] [expr $y1+3] [expr $x2+3] [expr $y2+3] \
556				-fill $fdisp(c_bg) -tags $tag]
557    } else {
558	if {[string compare $ftype hasNested] == 0} {
559	    $canvas lower [$canvas create rect $x1 $y1 $x2 $y2 \
560				-fill $fdisp(c_bg) -tags $tag]
561	    $canvas lower [$canvas create rect \
562		[expr $x1+3] [expr $y1+3] [expr $x2+3] [expr $y2+3] \
563				-fill $fdisp(c_fg) -tags $tag]
564	}
565    }
566    $canvas raise $tid	;# display text over top the box
567    return $box
568}
569proc FdispBindLabel { can id ftype f } {
570    global fdisp
571    set canvas $fdisp($can)
572
573    $canvas bind $id <$fdisp(curbutton)> [list Folder_Change $f]
574    if {$fdisp(tarbuttonAction) == "select+move"} {
575        $canvas bind $id <$fdisp(tarbutton)> \
576                [list Folder_TargetMove $f]
577    } elseif {$fdisp(tarbuttonAction) == "select+copy"} {
578        $canvas bind $id <$fdisp(tarbutton)> \
579                [list Folder_TargetCopy $f]
580    } elseif {$fdisp(tarbuttonAction) == "select only"} {
581        $canvas bind $id <$fdisp(tarbutton)> \
582                [list Folder_Target $f]
583    } else {
584        $canvas bind $id <$fdisp(tarbutton)> \
585                [list Folder_TargetMove $f]
586    }
587    $canvas bind $id <Shift-$fdisp(tarbutton)> \
588		    [list Folder_TargetCopy $f]
589    $canvas bind $id <Control-$fdisp(tarbutton)> \
590		    [list Folder_TargetClear]
591
592
593    if {[string compare $ftype goParent] == 0} {
594	$canvas bind $id <$fdisp(navbutton)> \
595	    [list FdispMain [file dirname $f]]
596    } else {
597	if {[string compare $ftype hasNested] == 0} {
598	    if {$can != "cache"} {
599		case $fdisp(popdownAction) {
600		    redisplay {
601			$canvas bind $id <$fdisp(navbutton)> \
602			    [list FdispMain $f]
603		    }
604		    enter {
605			$canvas bind $id <Any-Enter> \
606			    [list FdispDisplayPopdown $f down %x %y]
607		    }
608		    navbutton {
609			$canvas bind $id <$fdisp(navbutton)> \
610			    [list FdispDisplayPopdown $f down %x %y]
611		    }
612		}
613	    } else {
614		if {$fdisp(popdownAction) == "redisplay"} {
615		    $canvas bind $id <$fdisp(navbutton)> \
616			    [list FdispMain $f]
617		}
618	    }
619	} else {
620	    # Leaf
621	    if {$fdisp(popdownAction) == "redisplay"} {
622		$canvas bind $id <$fdisp(navbutton)> {}
623	    } else {
624		if {$fdisp(popdownRemove) == "navbutton"} {
625		    $canvas bind $id <$fdisp(navbutton)> FdispPopdownRemove
626		} else {
627		    # Use enter on another leaf label to simulate Leave
628		    # of the popdown.  Cannot bind to <Leave> on the popdown
629		    # background because that triggers when you enter one
630		    # of its own labels.
631		    $canvas bind $id <Enter> FdispPopdownRemove
632		}
633	    }
634	}
635    }
636}
637proc FdispSetCanvasSize { can maxy {noshrink 0}} {
638    global fdisp
639
640    set canvas $fdisp($can)
641    set w $fdisp(width,$can)
642
643    set height [winfo height $canvas]
644    set bd [$canvas cget -borderwidth]
645    incr bd [$canvas cget -highlightthickness]
646    set height [expr $height - 2*$bd]
647
648    set h [expr $maxy + $fdisp(ygap) + $fdisp(ygap)]
649    if {$height > $h} {set h $height}
650
651    #puts "SetSize $maxy->$h [lindex [$canvas cget -scrollregion] 3]"
652
653    if {$noshrink && [lindex [$canvas cget -scrollregion] 3] > $h} {
654	return
655    }
656
657    $canvas configure -scrollregion [list 0 0 $w $h]
658    # adjust background to cover new scrollregion
659    $canvas coords $fdisp(bgid,$can) 0 0 $w $h
660}
661
662proc FdispUpdateMap { can folder id } {
663    global fdisp
664    $fdisp($can) addtag Ftext=$folder withtag $id
665}
666proc FdispUpdateBmap { can folder box } {
667    global fdisp
668    $fdisp($can) addtag Fbox=$folder withtag $box
669}
670proc FdispGetMap { can folder } {
671    global fdisp
672    return [$fdisp($can) find withtag Ftext=$folder]
673}
674proc FdispGetBmap { can folder } {
675    global fdisp
676    return [$fdisp($can) find withtag Fbox=$folder]
677}
678# Routines to Highlight the folder display
679
680proc Fdisp_ResetHighlights {} {
681    global fdisp
682    Fdisp_ClearHighlights
683    Fdisp_HighlightCanvas canvas
684    if [info exists fdisp(cache)] {
685	Fdisp_HighlightCanvas cache
686    }
687}
688proc Fdisp_ClearHighlights {} {
689    global fdisp
690    FdispClearHighlights canvas
691    if [info exists fdisp(cache)] {
692	FdispClearHighlights cache
693    }
694}
695
696proc Fdisp_HighlightCanvas { can } {
697    global fdisp flist
698    if ![info exist fdisp($can)] {
699	return
700    }
701    if {$fdisp(cur,$can) != {}} {
702	FdispHighlightCur $can $fdisp(cur,$can)
703    }
704    if {$fdisp(tar,$can) != {}} {
705	FdispHighlightTarget $can $fdisp(tar,$can)
706    }
707    foreach f [Flist_UnseenFolders] {
708	FdispHighlightUnseen $can $f
709    }
710    Fdisp_LabelConfigure $fdisp($can)
711}
712
713proc FdispWhichLabel { can f } {
714    # Figure out what label to highlight, handling nesting
715    global fdisp mhProfile
716
717    if {"$can" == "cache" || [FdispNotDotDot $can $f]} {
718	return $f
719    }
720    while {[string compare $f "."] && [string compare $f "/"]} {
721	set nf [file dirname $f]
722	if {[string compare $nf $f] == 0} {
723	    break
724	}
725	set f $nf
726	if [FdispNotDotDot $can $f] {
727	    return $f
728	}
729    }
730    return {}
731}
732proc FdispAllLabels { can f } {
733    # Figure out what labels to highlight, returning
734    # multiple labels if they are present because of popdowns.
735    global fdisp mhProfile
736
737    set res {}
738    if [FdispNotDotDot $can $f] {
739	lappend res $f
740    }
741    while {[string compare $f "."] && [string compare $f "/"]} {
742	set nf [file dirname $f]
743	if {[string compare $nf $f] == 0} {
744	    break
745	}
746	set f $nf
747	if [FdispNotDotDot $can $f] {
748	    lappend res $f
749	}
750    }
751    return $res
752}
753# See if the folder label displayed for $f is ".." (and is displayed at all)
754proc FdispNotDotDot { can f } {
755    global fdisp
756    set map [FdispGetMap $can $f]
757    if {$map != {}} {
758	if [catch {$fdisp($can) itemcget $map -text} l] {
759	    if [string compare $f ".."] {
760		return 1
761	    }
762	} else {
763	    if [string compare $l ".."] {
764		return 1
765	    }
766	}
767    }
768    return 0
769}
770proc Fdisp_HighlightCur { f } {
771    global fdisp
772
773    Fcache_Folder $f
774    foreach can {canvas cache} {
775	if [info exists fdisp($can)] {
776	    FdispHighlightCur $can $f
777	    Fdisp_LabelConfigure $fdisp($can)
778	}
779    }
780}
781proc FdispHighlightCur { can f } {
782    global fdisp
783    set l [FdispWhichLabel $can $f]
784    set canvas $fdisp($can)
785    if {$fdisp(curid,$can) != {}} {
786	$canvas dtag $fdisp(curid,$can) cur[$canvas type $fdisp(curid,$can)]
787	$canvas dtag $fdisp(boxid,$can) curbox
788    }
789
790    set fdisp(cur,$can) $f
791    if {[string compare $l {}]} {
792	set id [FdispGetMap $can $l]
793	set box [FdispGetBmap $can $l]
794	$canvas addtag cur[$canvas type $id] withtag $id
795	$canvas addtag curbox withtag $box
796	set fdisp(curid,$can) $id
797	set fdisp(boxid,$can) $box
798    }
799}
800proc Fdisp_HighlightTarget { f } {
801    global fdisp fcache
802
803    if $fcache(cacheTarget) {
804       Fcache_Folder $f
805    }
806    foreach can {canvas cache} {
807	if [info exists fdisp($can)] {
808	    FdispHighlightTarget $can $f
809	    Fdisp_LabelConfigure $fdisp($can)
810	}
811    }
812}
813proc FdispHighlightTarget { can f } {
814    global fdisp
815    set l [FdispWhichLabel $can $f]
816    set canvas $fdisp($can)
817    if {$fdisp(tarid,$can) != {}} {
818	$canvas dtag $fdisp(tarid,$can) tar[$canvas type $fdisp(tarid,$can)]
819	$canvas dtag $fdisp(tboxid,$can) tarbox
820    }
821
822    set fdisp(tar,$can) $f
823    if {[string compare $l {}]} {
824	set id [FdispGetMap $can $l]
825	set box [FdispGetBmap $can $l]
826	$canvas addtag tar[$canvas type $id] withtag $id
827	$canvas addtag tarbox withtag $box
828	set fdisp(tarid,$can) $id
829	set fdisp(tboxid,$can) $box
830    }
831}
832
833proc Fdisp_HighlightUnseen { f } {
834    global fdisp fcache
835
836    if $fcache(cacheUnseen) {
837       Fcache_Folder $f
838    }
839    foreach can {canvas cache} {
840	if [info exists fdisp($can)] {
841	    FdispHighlightUnseen $can $f
842	    Fdisp_LabelConfigure $fdisp($can)
843	}
844    }
845}
846proc FdispHighlightUnseen { can f } {
847    global exmh fdisp
848    if {$can != "cache"} {
849	set ll [FdispAllLabels $can $f]
850    } else {
851	set ll [list $f]
852    }
853    set canvas $fdisp($can)
854    foreach l $ll {
855	set id [FdispGetMap $can $l]
856	set box [FdispGetBmap $can $l]
857	$canvas addtag leaf=$f withtag $id
858	if {[lsearch $fdisp(leafs,$can) leaf=$f] < 0} {
859	    # needed when resetting highlights
860	    lappend fdisp(leafs,$can) leaf=$f
861	}
862	$canvas addtag unsn[$canvas type $id] withtag $id
863	$canvas addtag unsnbox withtag $box
864    }
865}
866proc Fdisp_UnHighlightUnseen { f } {
867    global fdisp
868    foreach can {canvas cache} {
869	if [info exists fdisp($can)] {
870	    FdispUnHighlightUnseen $fdisp($can) $can $f
871	    Fdisp_LabelConfigure $fdisp($can)
872	}
873    }
874}
875proc FdispUnHighlightUnseen { canvas can f } {
876    global exmh fdisp
877    set ll [FdispAllLabels $can $f]
878    set canvas $fdisp($can)
879    foreach l $ll {
880	set id [FdispGetMap $can $l]
881	set box [FdispGetBmap $can $l]
882	set stillLight 0
883	foreach tag [$canvas gettags $id] {
884	    if [string match leaf=* $tag] {
885		set leaf [lindex [split $tag =] 1]
886		if {[string compare $leaf $f] == 0} {
887		    $canvas dtag $id $tag
888		} else {
889		    set stillLight 1
890		}
891	    }
892	}
893	if {! $stillLight} {
894	    $canvas dtag $id unsn[$canvas type $id]
895	    $canvas dtag $box unsnbox
896	}
897    }
898}
899proc Fdisp_Lines { canvas labels } {
900    # Return the number of lines needed to display the set of labels
901    global fdisp
902    set x $fdisp(xgap)
903    set lines 1
904    set width [winfo width $canvas]
905    set bd [$canvas cget -borderwidth]
906    incr bd [$canvas cget -highlightthickness]
907    set width [expr $width - 2*$bd]
908    foreach folder $labels {
909	set f [Fcache_FolderName $folder]
910	set id [Fdisp_Label $canvas $f $f]
911	set bbox [$canvas bbox $id]
912	set twidth [expr [lindex $bbox 2]-[lindex $bbox 0]]
913	if {$twidth + $fdisp(xgap)/2 + $x > $width} {
914	    incr lines
915	    set x $fdisp(xgap)
916	}
917	incr x [expr {$fdisp(xgap) + $twidth}]
918	$canvas delete $id
919    }
920    return $lines
921}
922
923#
924# Interface to Drag & Drop
925#
926set fdispDrag(callback) FdispDragRelease
927set fdispDrag(types) {folder filename}
928set fdispDrag(formats) string
929set fdispDrag(format,folder) string
930set fdispDrag(format,filename) string
931set fdispDrag(type,string) folder
932set fdispDrag(decorate) FdispDragWindow
933
934proc FdispDragAttach {where} {
935	global fdisp
936
937	Drag_Attach $fdisp($where) FdispDragSelect Shift $fdisp(navbutton)
938	if [string match cache $where] {
939		Drop_Attach $fdisp(cache) FdispDropCache
940	} else {
941		Drop_Attach $fdisp(canvas) FdispDropCanvas
942	}
943}
944
945# A drag was dropped on the cache
946proc FdispDropCache {w args} {
947	global dragging
948
949	if ![info exists dragging(data,folder)] return
950	set folder $dragging(data,folder)
951
952	# Add the folder to the cache
953	Fcache_Folder $folder
954}
955
956# A drag was dropped on the canvas
957proc FdispDropCanvas {w args} {
958	global fdisp dragging
959
960	if ![info exists dragging(data,folder)] return
961	set folder $dragging(data,folder)
962
963	# If dropped on the folder display and source was cache,
964	# remove the folder from the cache
965	if {[info exists fdisp(cache)] &&
966	    $dragging(source) == $fdisp(cache)} {
967		Fcache_FolderDiscard $folder
968	}
969}
970
971# Called when after a drag we sourced has been dropped
972proc FdispDragRelease {dstw args} {
973
974	global fdisp dragging
975	set folder $dragging(data,folder)
976
977tlog-add .t "released on $dstw"
978
979	# If we tossed it somewhere unknown, Add the folder to the cache
980	if {$dragging(source) == $fdisp(canvas) && $dstw != $fdisp(canvas) &&
981	    "$dstw" != {}} {
982		Fcache_Folder $folder
983	}
984}
985
986# Drag Selected
987proc FdispDragSelect {c x y wx wy} {
988	global fdisp
989
990	set closest [$c find closest [$c canvasy $wx] [$c canvasy $wy]]
991
992	# Find what folder we're over
993	set tags [$c gettags $closest]
994	set which [lsearch -glob $tags F*=*]
995	if {$which >= 0} {
996		set tag [lindex $tags $which]
997		regsub -- .*=(.*) $tag {\1} folder
998	}
999	if ![info exists folder] return
1000
1001	# Hand off to Drag code
1002	global fdispDrag mhProfile
1003	set fdispDrag(source) $c
1004	set fdispDrag(data,folder) $folder
1005	set fdispDrag(data,filename) $mhProfile(path)/$folder
1006
1007	Drag_Source fdispDrag $x $y
1008
1009}
1010
1011# How do decorate the Drag window
1012proc FdispDragWindow {w} {
1013	global fdisp dragging
1014
1015	set c $w.fdisp
1016	if ![winfo exists $c] {
1017		set height [expr $fdisp(itemHeight) + $fdisp(ygap)]
1018		canvas $c -height $height
1019	}
1020
1021	pack $c
1022	catch {$c delete all}
1023
1024	set f $dragging(data,folder)
1025
1026	set id [Fdisp_Label $c $f $f]
1027	set bbox [$c bbox $id]
1028	set twidth [expr [lindex $bbox 2]-[lindex $bbox 0]]
1029	set theight [expr [lindex $bbox 3]-[lindex $bbox 1]]
1030	$c move $id [expr $fdisp(xgap)/2 + 1] [expr $fdisp(ygap)/2]
1031	set width [expr $twidth + $fdisp(xgap)]
1032	$c config -width $width
1033	set bid [Fdisp_Box $c $id leaf {}]
1034}
1035