1#
2# ftoc.tcl
3#
4# Folder table of contents display.
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 Ftoc_Init {} {
15    global ftoc
16    set ftoc(displayValid) 1		;# 0 => pick results, not full scan
17    set ftoc(displayDirty) 0		;# 1 => display differs from cache
18    set ftoc(mono) [expr {[winfo depth .] <= 4}]
19    # Parameters to the Next button
20    Preferences_Add "Scan Listing" \
21"These settings affect the behavior of Exmh as you move through the scan listing to view and mark messages.
22While the default for Auto Commit is OFF, I suggest you try it out.
23Messages are still temporarily marked, but the commit is done when you need it." {
24    {exwin(ftextLines)	ftextLines 15	{Scan listing lines}
25"Lines in the Scan listing window, which is
26also called Folder-Table-Of-Contents (FTOC)."}
27    {ftoc(implied) impliedDirection ON "Implied Direction"
28"If set, Exmh will remember your current direction,
29next or previous, and go that way after you mark a
30message for deletion or refiling."}
31    {ftoc(nextGuard) nextGuard OFF "Next Guard"
32"If set, Exmh will warn you that you are about to
33change folders when you hit Next.  This means you
34end up hitting Next twice to chain the to next
35folder with unseen messages."}
36    {ftoc(autoCommit) autoCommit OFF "Auto Commit"
37"If set, Exmh will invoke the Commit operation to
38commit deletions and refiles when it would otherwise
39just complain that such a commit is required."}
40    {ftoc(commitDialog) commitDialog ON "Commit Dialog"
41"If set, you get a confirmation dialog when exmh wants
42you to commit pending changes.  Otherwise you just
43get a warning message and have to hit the Commit button."}
44    {ftoc(autoPack) autoPack OFF "Auto Pack"
45"If set, Exmh will pack the folder every time a commit is performed."}
46    {ftoc(autoSort) autoSort OFF "Auto Sort"
47"If set, Exmh will sort the folder every time you change into it"}
48    {ftoc(autoSortType) autoSortType {CHOICE date subject sender custom} {Sorting criterion}
49"Sort by Date:/Subject:/From: or user-defined fields. Uses MH sortm command."}
50    {ftoc(autoSortCrit) autoSortCrit {-textfield keywords} {Custom criterion}
51"Custom parameters for sortm"}
52    {ftoc(showNew) ftocShowNew OFF "Show New Messages"
53"If set, Exmh will scroll the FTOC display to show
54new message that arrive because of an Inc."}
55    {ftoc(linkAdvance) advanceOnLink OFF "Advance after Link"
56"If set, Exmh will advance to the next message after a link."}
57    {ftoc(skipMarked) skipMarked ON "Next/Prev skip marked msgs"
58"If set, Next and Prev will skip over messages that have
59been marked for move or delete."}
60    {flist(cycleBack)	cycleBack ON	"Cycle back to first"
61"If there are no folders with unseen messages, then this
62option causes you to change to the first folder given by your
63Folder-Order MH profile entry."}
64    {ftoc(scanWidth) scanWidth 100 "Default scan width"
65"This value is passed as the -width argument to scan and in."}
66    {ftoc(scanSize) scanSize 100 "Default amount to scan"
67"Only the last N messages are scanned when you first enter a folder.
68The number is controlled by this setting."}
69    }
70}
71proc Ftoc_Reset { numMsgs msgid folder } {
72    global ftoc exwin
73    Exmh_Debug Ftoc_Reset $folder has $numMsgs msgs
74    set ftoc(numMsgs) $numMsgs		;# num msgs in the scan listing
75    set ftoc(changed) 0			;# Number of moves/deletes marked
76    set ftoc(lineset) {}		;# set of selected messages
77    set ftoc(pickone) 1			;# lineset is empty
78    set ftoc(folder) $folder		;# Currently displayed folder
79    set ftoc(direction) next		;# assumed next direction
80    set ftoc(softChange) [expr {! $ftoc(nextGuard)}]
81    set ftoc(lasthit) {}		;# search anchor
82    set ftoc(curLine) $msgid		;# current display line number
83    Ftoc_ClearMsgCache
84}
85proc Ftoc_Update { numMsgs } {
86    # Update size of message list after inc'ing into current folder
87    global ftoc
88    Exmh_Debug Ftoc_Update $ftoc(folder) has $numMsgs msgs (was $ftoc(numMsgs))
89    if {$numMsgs > $ftoc(numMsgs)} {
90	set msgids {}
91	set lineno $ftoc(numMsgs)
92	while {$lineno < $numMsgs} {
93	    incr lineno
94	    lappend msgids [Ftoc_MsgNumber $lineno]
95	}
96	Ftoc_ShowSequences $msgids
97    }
98    set ftoc(numMsgs) $numMsgs
99}
100
101proc Ftoc_Bindings { w } {
102    # Bindings for the ftoc text widget
103
104    bind $w <Configure> FtocDeduceSize
105
106    # The TScroll binding to too general.
107    # We'll do our own scroll bindings here.
108    bindtags $w [list $w Command]
109
110    # Button-1 starts selection range
111    bind $w <Button-1> {
112	FtocRangeStart [lindex [split [%W index current] .] 0]
113	Exmh_Focus
114    }
115    bind $w <Shift-Button-1> {
116	FtocRangeAdd [lindex [split [%W index current] .] 0]
117	Exmh_Focus
118    }
119    bind $w <B1-Motion> {
120	FtocRangeExtendXY %x %y
121    }
122    bind $w <Shift-B1-Motion> {
123	FtocRangeExtendXY %x %y
124    }
125    bind $w <Any-ButtonRelease-1> {
126	FtocRangeEnd [lindex [split [%W index current] .] 0] 0
127    }
128    bind $w <Shift-ButtonRelease-1> {
129	FtocRangeEnd [lindex [split [%W index current] .] 0] 1
130    }
131    bind $w <Button-3> {
132	set lineNumber [lindex [split [%W index current] .] 0]
133	Msg_Pick $lineNumber noshow
134	Exmh_Focus
135    }
136    bind $w <Double-Button-1> { }
137    bind $w <Triple-Button-1> { }
138
139    bind $w <Button-2> {WidgetTextMark %W %y}
140    bind $w <B2-Motion> {WidgetTextDragto %W %y $exwin(scrollSpeed)}
141
142    Drag_Attach $w FtocDragSelect Shift 3
143
144}
145proc FtocDeduceSize {} {
146    global exwin
147    if {$exwin(toplevelMsg)} {
148	set top [lindex [split [$exwin(ftext) index @0,0] .] 0]
149	set h [winfo height $exwin(ftext)]
150	set w [winfo width $exwin(ftext)]
151	set ix [$exwin(ftext) index @[expr $h-1],[expr $w-1]]
152	set bottom [lindex [split $ix .] 0]
153	set exwin(ftextLines) [expr $bottom-$top]
154    }
155}
156proc FtocRangeStart { lineno } {
157    # For normal button-down "start a selection"
158    global ftoc
159    Ftoc_RangeUnHighlight
160    set ftoc(pickstart) $lineno
161    set ftoc(pickend) $lineno
162    set ftoc(pickstate) new
163    set ftoc(extend) 0
164    Ftoc_RangeHighlight $lineno $lineno
165}
166proc FtocRangeAdd { lineno } {
167    # For shift-select "add to selection"
168    global ftoc
169    set ftoc(pickstart) $lineno
170    set ftoc(pickend) $lineno
171    set ftoc(pickstate) invert
172    set ftoc(extend) 0
173    FtocRangeInvert $lineno $lineno
174}
175proc FtocRangeEnd { {lineno {}} {addcurrent 0} } {
176    # For end of button sweep
177    global ftoc exwin
178    catch {unset ftoc(extend)}
179    if ![info exists ftoc(pickend)] {
180	# Spurious button-release event
181	return
182    }
183    if {($lineno == $ftoc(pickstart)) && !$addcurrent} {
184	# regular button click optimization
185	unset ftoc(pickend)
186	Msg_Pick $lineno show
187	return
188    }
189    if {$lineno != {}} {
190	FtocRangeExtend $lineno
191    }
192    FtocPickRange $addcurrent
193    catch {unset ftoc(pickend)}
194}
195proc Ftoc_PickMsgs { msgids addtosel } {
196    # For adding to the selection by message number
197    global ftoc
198    Exmh_Status "Marking [llength $msgids] hits"
199    Ftoc_LinesHighlight [Ftoc_FindMsgs $msgids]
200    FtocPickRange $addtosel
201}
202proc FtocRangeExtendXY { x y } {
203    global ftoc exwin widgetText
204
205    if ![info exists ftoc(extend)] {
206	return
207    }
208    set active $ftoc(extend)
209
210    set h [winfo height $exwin(ftext)]
211    if {$y > $h} {
212	set ftoc(extend) [expr $y-$h]
213    } else {
214	if {$y < 0} {
215	    set ftoc(extend) $y
216	} else {
217	    set ftoc(extend) 0
218	}
219    }
220
221    if {$ftoc(extend) == 0} {
222	FtocRangeExtend [lindex [split [$exwin(ftext) index @$x,$y] .] 0]
223    } else {
224	if {! $active} {
225	    set ftoc(lastmark) [lindex [ split [$exwin(ftext) index @$x,$y] .] 0]
226	    after $widgetText(selectDelay) [list FtocSelExtend]
227	}
228    }
229}
230proc FtocSelExtend {} {
231    global ftoc exwin widgetText
232    set w $exwin(ftext)
233    if {![info exists ftoc(extend)] ||
234	($ftoc(extend) == 0)} {
235	return
236    }
237    catch {
238	set delta [expr {$ftoc(extend) / 16}]
239	if {$delta == 0} {
240	    set delta [expr { ($ftoc(extend) < 0) ? -1 : 1 }]
241	}
242	set newmark [expr {$ftoc(lastmark) + $delta}]
243	FtocRangeExtend $newmark
244	set ftoc(lastmark) $newmark
245	$w yview -pickplace $newmark.0
246	after $widgetText(selectDelay) [list FtocSelExtend]
247    }
248}
249proc FtocRangeExtend { lineno } {
250    global ftoc
251    if ![info exists ftoc(pickend)] {
252	return
253    }
254    if {$lineno <= 0} {
255	set lineno 1
256    }
257    if {$lineno > $ftoc(numMsgs)} {
258	set lineno $ftoc(numMsgs)
259    }
260    if {$lineno == $ftoc(pickend)} {
261	# Invariant, previously defined selection is fine.
262	return
263    }
264    if {$lineno == 0} {
265	# no messages in folder
266	return
267    }
268    if {$ftoc(pickstate) != "invert"} {
269	if {$ftoc(pickstart) < $ftoc(pickend)} {
270	    # Growing downward
271	    if {$lineno > $ftoc(pickend)} {
272		Ftoc_RangeHighlight [expr $ftoc(pickend)+1] $lineno
273	    } else {
274		if {$lineno < $ftoc(pickstart)} {
275		    if {$ftoc(pickstart) != $ftoc(pickend)} {
276			# Change direction
277			FtocRangeClear [expr $ftoc(pickstart)+1] \
278					$ftoc(pickend)
279		    }
280		    Ftoc_RangeHighlight [expr $ftoc(pickstart)-1] $lineno
281		} else {
282		    # Shrink selection
283		    FtocRangeClear [expr $lineno+1] $ftoc(pickend)
284		}
285	    }
286	} else {
287	    # Growing upward
288	    if {$lineno < $ftoc(pickend)} {
289		Ftoc_RangeHighlight [expr $ftoc(pickend)-1] $lineno
290	    } else {
291		if {$lineno > $ftoc(pickstart)} {
292		    if {$ftoc(pickstart) != $ftoc(pickend)} {
293			# Change direction
294			FtocRangeClear [expr $ftoc(pickstart)-1] \
295					$ftoc(pickend)
296		    }
297		    Ftoc_RangeHighlight [expr $ftoc(pickstart)+1] $lineno
298		} else {
299		    # Shrink selection
300		    FtocRangeClear [expr $lineno-1] $ftoc(pickend)
301		}
302	    }
303	}
304    } else {
305	if {$ftoc(pickstart) < $ftoc(pickend)} {
306	    # Growing downward
307	    if {$lineno > $ftoc(pickend)} {
308		FtocRangeInvert [expr $ftoc(pickend)+1] $lineno
309	    } else {
310		if {$lineno < $ftoc(pickstart)} {
311		    if {$ftoc(pickstart) != $ftoc(pickend)} {
312			# Change direction
313			FtocRangeInvert [expr $ftoc(pickstart)+1] \
314					$ftoc(pickend)
315		    }
316		    FtocRangeInvert [expr $ftoc(pickstart)-1] $lineno
317		} else {
318		    # Shrink selection
319		    FtocRangeInvert [expr $lineno+1] $ftoc(pickend)
320		}
321	    }
322	} else {
323	    # Growing upward
324	    if {$lineno < $ftoc(pickend)} {
325		FtocRangeInvert [expr $ftoc(pickend)-1] $lineno
326	    } else {
327		if {$lineno > $ftoc(pickstart)} {
328		    if {$ftoc(pickstart) != $ftoc(pickend)} {
329			# Change direction
330			FtocRangeInvert [expr $ftoc(pickstart)-1] \
331					$ftoc(pickend)
332		    }
333		    FtocRangeInvert [expr $ftoc(pickstart)+1] $lineno
334		} else {
335		    # Shrink selection
336		    FtocRangeInvert [expr $lineno-1] $ftoc(pickend)
337		}
338	    }
339	}
340    }
341    set ftoc(pickend) $lineno
342}
343proc FtocRangeInvert { start end } {
344    global exwin
345    set win $exwin(ftext)
346    if {$start > $end} {
347	set tmp $start ; set start $end ; set end $tmp
348    }
349    for {set lineno $start} {$lineno <= $end} {incr lineno} {
350	catch {
351	    set newtag range
352	    set oldtag {}
353	    set nuke 0
354	    foreach tag [$win tag names $lineno.0] {
355		case $tag {
356		    deleted { set newtag drange ; set oldtag $tag ; break; }
357		    moved { set newtag mrange ; set oldtag $tag ; break; }
358		    range { set newtag {} ; set oldtag $tag ; break; }
359		    drange { set newtag deleted ; set oldtag $tag ;
360			    set nuke 1; break}
361		    mrange { set newtag moved ; set oldtag $tag ;
362			    set nuke 1; break; }
363		}
364	    }
365	    if {$nuke} {
366		set ix [lsearch $ftoc(lineset) $lineno]
367		if {$ix >= 0} {
368		    set ftoc(lineset) [lreplace $ftoc(lineset) $ix $ix]
369		}
370	    }
371	    if {$oldtag != {}} {
372		$win tag remove $oldtag $lineno.0 $lineno.end
373	    }
374	    if {$newtag != {}} {
375		$win tag add $newtag $lineno.0 $lineno.end
376	    }
377	}
378    }
379}
380proc Ftoc_RangeHighlight { start end } {
381    global exwin
382    set win $exwin(ftext)
383    if {$start > $end} {
384	set tmp $start ; set start $end ; set end $tmp
385    }
386    for {set lineno $start} {$lineno <= $end} {incr lineno} {
387	set newtag range
388	foreach tag [$win tag names $lineno.0] {
389	    case $tag {
390		{drange deleted} { set newtag drange ;  break; }
391		{mrange moved} { set newtag mrange ;  break; }
392	    }
393	}
394	$win tag add $newtag $lineno.0 $lineno.end
395    }
396}
397proc Ftoc_LinesHighlight { linenos } {
398    global exwin
399    set win $exwin(ftext)
400    if {$linenos == {}} {
401	return
402    }
403    WidgetTextYview $exwin(ftext) -pickplace [lindex $linenos 0].0
404#    update idletasks
405    foreach lineno $linenos {
406	set newtag range
407	foreach tag [$win tag names $lineno.0] {
408	    case $tag {
409		{drange deleted} { set newtag drange ;  break; }
410		{mrange moved} { set newtag mrange ;  break; }
411	    }
412	}
413	$win tag add $newtag $lineno.0 $lineno.end
414    }
415}
416proc FtocRangeClear { start end } {
417    global exwin
418    set win $exwin(ftext)
419    if {$start > $end} {
420	set tmp $start ; set start $end ; set end $tmp
421    }
422    for {set lineno $start} {$lineno <= $end} {incr lineno} {
423	catch {
424	    set newtag {}
425	    set oldtag range
426	    foreach tag [$win tag names $lineno.0] {
427		case $tag {
428		    drange { set newtag deleted ; set oldtag drange; break; }
429		    mrange { set newtag moved ; set oldtag mrange; break; }
430		    range { break }
431		}
432	    }
433	    $win tag remove $oldtag $lineno.0 $lineno.end
434	    if {$newtag != {}} {
435		$win tag add $newtag $lineno.0 $lineno.end
436	    }
437	}
438    }
439}
440proc Ftoc_RangeUnHighlight { } {
441    global exwin exmh
442    set win $exwin(ftext)
443    foreach tag {range drange mrange} {
444	foreach range [FtocMakePairs [$win tag ranges $tag]] {
445	    eval $win tag remove $tag $range
446	    if {$tag == "drange"} {
447		eval $win tag add deleted $range
448	    }
449	    if {$tag == "mrange"} {
450		eval $win tag add moved $range
451	    }
452	}
453    }
454}
455
456# For user programming
457proc Ftoc_BindDouble { cmd } {
458    global exwin
459    bind $exwin(ftext) <Double-1> $cmd
460}
461proc Ftoc_BindRight { cmd } {
462    global exwin
463    bind $exwin(ftext) <3> $cmd
464}
465
466proc Ftoc_FindMsgs {msgids} {
467    global ftoc msgtolinecache
468    set linenos {}
469    foreach msg $msgids {
470	set lineno [Ftoc_FindMsg $msg]
471	if {$lineno != {}} {
472	    lappend linenos $lineno
473	}
474    }
475    return $linenos
476}
477proc Ftoc_FindMsg { msgid } {
478    global ftoc msgtolinecache
479    if {$msgid == {}} {
480        return {}
481    }
482    if {[info exist msgtolinecache($msgid)]} {
483        return $msgtolinecache($msgid)
484    }
485    if !$ftoc(displayValid) {
486        #
487        # Linear search for pick and thread FTOCs (pseudo-displays)
488        #
489        for {set lineno 1} {$lineno <= $ftoc(numMsgs)} {incr lineno} {
490            if {[Ftoc_MsgNumber $lineno] == $msgid} {
491                return $lineno
492            }
493        }
494        return {}
495    }
496
497    #
498    # Binary search for other FTOCs
499    #
500    set minlineno 1
501    set minmsgid [Ftoc_MsgNumber $minlineno]
502    if {$msgid == $minmsgid} {
503        return $minlineno
504    }
505    set maxlineno $ftoc(numMsgs)  ;# Ignore trailing blank line
506    set maxmsgid [Ftoc_MsgNumber $maxlineno]
507    if {$msgid == $maxmsgid} {
508        return $maxlineno
509    }
510    while (1) {
511        if {$msgid > $maxmsgid || $msgid < $minmsgid} {
512            Exmh_Status "Cannot find $msgid ($minmsgid,$maxmsgid)" warn
513            if {[info exist msgtolinecache($msgid)]} {
514                unset msgtolinecache($msgid)
515            }
516            return {} ;# new message not listed
517        }
518        if {$maxlineno == $minlineno} {
519            if {[info exist msgtolinecache($msgid)]} {
520                unset msgtolinecache($msgid)
521            }
522            return {}   ;# not found
523        }
524        # Original binary search
525        #set nextlineno [expr int(($maxlineno+$minlineno)/2)]
526        # Don't divide in two, guestimate where the line might be instead
527        set nextlineno [expr int($minlineno+1+($msgid-$minmsgid)*($maxlineno-$minlineno-2)/($maxmsgid-$minmsgid))]
528        if {$nextlineno < $minlineno} {
529          set nextlineno $minlineno
530        }
531        if {$nextlineno > $maxlineno} {
532          set nextlineno $maxlineno
533        }
534        set nextmsgid [Ftoc_MsgNumber $nextlineno]
535        # Note that a side effect of Ftoc_MsgNumber was to put this entry in
536	# the cache,so we don't have to do it here.
537        if {$nextmsgid == $msgid} {
538            return $nextlineno
539        } elseif {$nextmsgid > $msgid} {
540            set maxlineno $nextlineno
541            set maxmsgid $nextmsgid
542        } elseif {$minlineno == $nextlineno} {
543            Exmh_Status "Cannot find $msgid" warn
544            if {[info exist msgtolinecache($msgid)]} {
545                unset msgtolinecache($msgid)
546            }
547            return {} ;# new message not listed
548        } elseif {$nextmsgid == ""} {
549            error "Failed to find a message number on line $nextlineno, end is [$::exwin(ftext) index end]\n[$::exwin(ftext) get $nextlineno.0 $nextlineno.end]"
550        } else {
551            set minlineno $nextlineno
552            set minmsgid $nextmsgid
553        }
554    }
555    # not reached
556}
557proc Ftoc_ClearMsgCache {} {
558    global linetomsgcache msgtolinecache
559    foreach x {linetomsgcache msgtolinecache} {
560	if {[info exists $x]} {
561	    unset $x
562	}
563    }
564}
565proc Ftoc_MsgNumbers { linenos } {
566    global exwin
567    set msgids {}
568    foreach lineno $linenos {
569	set msgid [Ftoc_MsgNumber $lineno]
570	if {$msgid != {}} {
571	    lappend msgids $msgid
572	}
573    }
574    return $msgids
575}
576proc Ftoc_MsgNumber { lineno } {
577    global exwin linetomsgcache msgtolinecache
578    if {[info exist linetomsgcache($lineno]} {
579        return $linetomsgcache($lineno)
580    }
581    if [catch {$exwin(ftext) get $lineno.0 $lineno.end} line] {
582        return {}
583    }
584    set msgid [Ftoc_MsgNumberRaw $line]
585    if {$msgid != {}} {
586        set msgtolinecache($msgid) $lineno
587        set linetomsgcache($lineno) $msgid
588    }
589    return $msgid
590}
591proc Ftoc_MsgNumberRaw { line } {
592    if [regexp {^( *)([0-9]+)} $line foo foo2 number] {
593	return $number
594    } else {
595	return ""
596    }
597}
598proc FtocPickRange { {addcurrent 0} } {
599    # Select a range of messages, or add to the current range
600    # Because of toggle/inverted selections, we pretty much
601    # have to recompute the select set from range tags
602    global exwin ftoc
603    set lineset {}
604    if {$ftoc(curLine) != {}} {
605	if {$addcurrent} {
606	    Ftoc_RangeHighlight $ftoc(curLine) $ftoc(curLine)
607	}
608	Ftoc_ClearCurrent
609	Msg_ClearCurrent
610	set ftoc(curLine) {}
611    }
612    foreach range [concat \
613		       [FtocMakePairs [$exwin(ftext) tag ranges range]] \
614		       [FtocMakePairs [$exwin(ftext) tag ranges drange]] \
615		       [FtocMakePairs [$exwin(ftext) tag ranges mrange]]] {
616	set mark1 [lindex $range 0]
617	set lineno [lindex [split $mark1 .] 0]
618	lappend lineset $lineno
619    }
620    if {$lineset == {}} {
621	return			;# spurious <ButtonRelease-1> events
622    }
623    set ftoc(lineset) $lineset
624    set ftoc(pickone) 0
625    if {[llength $ftoc(lineset)] == 1} {
626	# This calls Msg_Change,
627	# which calls Ftoc_ClearCurrent, which sets pickone to 1,
628	# and calls Ftoc_Change, which sets curline
629	Msg_Pick [lindex $ftoc(lineset) 0] show
630    } else {
631	Buttons_Range	;# Enable actions on ranges
632    }
633}
634proc Ftoc_PickSize {} {
635    global ftoc
636    if {$ftoc(curLine) != {}} {
637	return [llength $ftoc(curLine)]
638    } else {
639	return [llength $ftoc(lineset)]
640    }
641}
642proc Ftoc_NewFtoc {{linenos ftoclineset}} {
643    global ftoc
644    if {$linenos == "ftoclineset"} {
645	set linenos $ftoc(lineset)
646    }
647    set msgids [Ftoc_MsgNumbers $linenos]
648    if {[llength $msgids] <= 1} {
649	Exmh_Status "Select more than one message first" warn
650	return
651    }
652    if {[Ftoc_Changes "new ftoc"] == 0} {
653	Exmh_Status $msgids
654	Scan_ProjectSelection $msgids
655    }
656}
657
658# Ftoc_ClearCurrent and Ftoc_Change are two parts of
659# dinking the ftoc display when advancing a message.
660
661proc Ftoc_ClearCurrent {} {
662    # Clear display of current message
663    global ftoc exwin
664    set ftoc(pickone) 1
665    set ftoc(lineset) {}
666
667    if {$ftoc(curLine) == {}} {
668	set ftoc(curLine) [Mh_Cur $ftoc(folder)]
669    }
670    if {$ftoc(curLine) != {}} {
671	$exwin(ftext) tag remove cur $ftoc(curLine).0 $ftoc(curLine).end
672	Ftoc_RescanLine $ftoc(curLine)
673    }
674    return $ftoc(curLine)
675}
676proc Ftoc_Change { lineno {show show} } {
677    global ftoc exwin mhProfile
678    set ftoc(curLine) $lineno
679    if {$ftoc(curLine) == {}} {
680	set ok 0
681    } else {
682	if {$show == "show"} {
683	    $exwin(ftext) tag remove $mhProfile(unseen-sequence) $ftoc(curLine).0 $ftoc(curLine).end
684	}
685	Ftoc_RescanLine $ftoc(curLine) +
686	$exwin(ftext) tag add cur $ftoc(curLine).0 $ftoc(curLine).end
687	set top [$exwin(ftext) index @0,4]
688	if [catch {expr {$top+1}}] {set top 0}	;# trap 100.-1 format, iconic
689	if {$ftoc(curLine) == $top ||
690	    $ftoc(curLine) == $top+$exwin(ftextLines)-1} {
691	    WidgetTextYview $exwin(ftext) [expr $ftoc(curLine)-$exwin(ftextLines)/2].0
692	} else {
693	    WidgetTextYview $exwin(ftext) -pickplace $ftoc(curLine).0
694	}
695	set ok 1
696    }
697    return $ok
698}
699proc Ftoc_InitSequences { w } {
700    global exwin
701    set seqs [option get . sequences {}]
702    foreach seq $seqs {
703	eval $w tag configure $seq \
704	    [option get . sequence_$seq {}]
705	$w tag raise $seq
706    }
707}
708
709# Highlight a set of messages (or all in the folder) that belong
710# to a sequence.  If msgsids is null, then we only work on that
711# subset of the folder.  Otherwise we highlight all the messages
712# in the folder that are in the sequence.
713
714proc Ftoc_ShowSequence { seq {msgids {}} } {
715    global exwin exmh mhProfile
716Exmh_Debug Ftoc_ShowSequence $seq msgids $msgids
717    set seqids [Seq_Msgs $exmh(folder) $seq]
718    if {$msgids != {}} {
719	foreach msg $msgids {
720	    set lineno [Ftoc_FindMsg $msg]
721	    if {$lineno != {}} {
722		if {[lsearch -exact $seqids $msg] == -1} {
723		    $exwin(ftext) tag remove $seq $lineno.0 $lineno.end
724		} else {
725		    $exwin(ftext) tag add $seq $lineno.0 $lineno.end
726		}
727	    }
728	}
729    } else {
730	$exwin(ftext) tag remove $seq 1.0 end
731        if {$seq == $mhProfile(unseen-sequence)} {
732            FtocShowUnseen $seqids
733        } else {
734            foreach lineno [Ftoc_FindMsgs $seqids] {
735                $exwin(ftext) tag add $seq $lineno.0 $lineno.end
736            }
737	}
738    }
739}
740
741proc Ftoc_ShowSequences { {msgids {}} } {
742    global exwin exmh
743    if {$msgids == {}} {
744Exmh_Debug Ftoc_ShowSequences msgids null
745	set seqs [option get . sequences {}]
746	set hiddenseqs [option get . hiddensequences {}]
747	foreach seq $seqs {
748	    if {[lsearch -exact $hiddenseqs $seq] == -1} {
749		$exwin(ftext) tag remove $seq 1.0 end
750	    }
751	}
752    } else {
753Exmh_Debug Ftoc_ShowSequences msgids $msgids
754    }
755    foreach seq [Mh_Sequences $exmh(folder)] {
756        Ftoc_ShowSequence $seq $msgids
757    }
758}
759
760# This is optimized for the unseen sequence, which tends to
761# cluster at the end of a folder, and get big
762
763proc FtocShowUnseen { unseen } {
764    global exwin flist
765    if {[llength $unseen] > 0} {
766Exmh_Debug FtocShowUnseen $unseen
767	set end [$exwin(ftext) index end]
768	set line [lindex [split $end .] 0]
769	set msgNum 0
770	for {} {$line > 0} {incr line -1} {
771	    set msgNum [Ftoc_MsgNumber $line]
772	    set i [lsearch $unseen $msgNum]
773	    if {$i >= 0} {
774		$exwin(ftext) tag add unseen $line.0 $line.end
775		set unseen [lreplace $unseen $i $i]
776		if {[llength $unseen] == 0} {
777		    return 1
778		}
779	    }
780	}
781        # Here is some code from the old Ftoc_ShowUnseen that
782        # I don't think we need any more
783        if {0} {
784          # Repair bogus unseen sequences
785          # msgNum is the smallest message number, but it might not be
786          # the first message in the folder because of short scans
787          # Anything in the unseen sequence above msgNum is probably wrong
788          # and can result from races with the background process
789          foreach id $unseen {
790	    if {$id > $msgNum} {
791                # This API doen't exist anymore
792		Flist_MsgSeenXXX $id
793	    }
794          }
795	}
796    }
797}
798
799proc Ftoc_RescanLine { ix {plus none} } {
800    global exmh exwin ftoc
801    if [catch {
802	set text [$exwin(ftext) get ${ix}.0 ${ix}.end]
803	set ok 0
804	case $plus {
805	    "none" {
806		# Replace + (current marker) with blank
807		set ok [regsub {^( *[0-9]+)(\+)} $text {\1 } newtext]
808	    }
809	    "+" {
810		# Stick a + after the number, if needed
811		if ![regexp {^( *)([0-9]+)(\+)} $text] {
812		    set ok [regsub {^( *[0-9]+)( )} $text {\1+} newtext]
813		}
814	    }
815	    "dash" {
816		# Stick a - after the number, if needed
817		if ![regexp {^( *)([0-9]+).-} $text] {
818		    set ok [regsub {^( *[0-9]+.)(.)} $text {\1-} newtext]
819		}
820		# Annotations result in writes to the directory.
821		# Here we mark the display dirty to force an update
822		# of the cache and prevent later rescans.
823		set ftoc(displayDirty) 1
824		Ftoc_ClearMsgCache
825	    }
826	}
827	if {$ok} {
828	    set tags [$exwin(ftext) tag names ${ix}.0]
829	    $exwin(ftext) configure -state normal
830	    $exwin(ftext) delete ${ix}.0 ${ix}.end
831	    $exwin(ftext) insert ${ix}.0 $newtext
832	    $exwin(ftext) configure -state disabled
833	    foreach tag $tags {
834		$exwin(ftext) tag add $tag ${ix}.0 ${ix}.end
835	    }
836	}
837    } msg] {
838	Exmh_Error "FtocRescanLine $ix : $msg"
839    }
840}
841proc Ftoc_NextImplied { {show show} {implied implied} } {
842    global ftoc
843    if {$ftoc(implied) && $ftoc(direction) == "prev"} {
844	Ftoc_Prev $show
845    } else {
846	Ftoc_Next $show $implied
847    }
848}
849proc Ftoc_Next { show {implied no} } {
850    # Go to the next message in the scan display
851    global exmh flist ftoc mhProfile
852
853    set ftoc(direction) "next"
854    if {$ftoc(curLine) == {}} {
855	if [Msg_Show $mhProfile(unseen-sequence)] {
856	    return
857	}
858    }
859    set next [FtocSkipMarked $ftoc(curLine) 1]
860    if {($ftoc(curLine) == $next) || \
861	    ($ftoc(curLine) >= $ftoc(numMsgs)) || \
862	    ($ftoc(curLine) <= 0)} {
863	# End of folder
864	Ftoc_NextFolder $implied
865    } else {
866	# Simple case - go to the next message.
867	Msg_Pick $next $show
868    }
869}
870proc Ftoc_Prev { {show show} } {
871    global ftoc
872
873    Exmh_Debug Ftoc_Prev
874    if {$ftoc(curLine) == {}} {
875	if {$ftoc(numMsgs) > 0} {
876	    Msg_Pick $ftoc(numMsgs) $show
877	}
878	return
879    }
880    if {$ftoc(curLine) > 1} then {
881	set ftoc(direction) "prev"
882	Msg_Pick [FtocSkipMarked $ftoc(curLine) -1] $show
883    } else {
884	Ftoc_Next $show implied
885    }
886}
887proc Ftoc_NextFolder { {implied no} } {
888    global ftoc exmh mhProfile
889    # Try to chain to the next folder with unread messages.
890    if {$implied != "no"} {
891	# Implied - chained with some other operation - be lenient
892	if {$ftoc(changed) > 0} {
893	    # Dirty folder - do not change.
894	    # If on last message, clear display because the
895	    # message is moved or deleted
896	    if {$ftoc(curLine) != {}} {
897		Ftoc_ClearCurrent
898		Msg_ClearCurrent
899	    }
900	    Exmh_Status ""
901	    Exmh_Status "Changes pending; End of folder" warn
902	    return
903	}
904    }
905    set folder [Flist_NextUnvisited]
906    if {[string length $folder] != 0} {
907	if {$ftoc(softChange)} {
908	    set ftoc(lastFolder) $exmh(folder)
909	    Folder_Change $folder [list Msg_Show $mhProfile(unseen-sequence)]
910	    return
911	} else {
912	    set ftoc(softChange) 1
913	    Ftoc_ClearCurrent
914	    Msg_ClearCurrent
915	    Exmh_Status ""
916	    Exmh_Status "End of folder; <Next> => $folder" warn
917	    return
918	}
919    }
920    Exmh_Status ""
921    Exmh_Status "End of folder" warn
922}
923proc Ftoc_LastFolder {} {
924    global ftoc
925    if {[info exist ftoc(lastFolder)]} {
926	return $ftoc(lastFolder)
927    } else {
928	return ""
929    }
930}
931proc Ftoc_PrevMarked { {show show} } {
932    global ftoc
933    set skip $ftoc(skipMarked)
934    set ftoc(skipMarked) 0
935    Ftoc_Prev $show
936    set ftoc(skipMarked) $skip
937}
938proc Ftoc_Marked { msgid } {
939    global ftoc exwin
940    if {$ftoc(skipMarked) == 0} {
941	return 0	;# Pretend it isn't marked
942    }
943    set lineno [Ftoc_FindMsg $msgid]
944    if {[string length $lineno] == 0} {
945	return 1	;# Can't find it, pretend it's marked
946    }
947    set marked 0
948    foreach tag [$exwin(ftext) tag names $lineno.0] {
949	if [regexp {(deleted|moved|drange|mrange)} $tag] {
950	    set marked 1 ; break;
951	}
952    }
953    return $marked
954}
955proc FtocSkipMarked {start inc} {
956    global exwin ftoc
957
958    if {$start == {}} {
959	return {}
960    }
961    for {set i [expr $start+$inc]} {$i > 0 && $i <= $ftoc(numMsgs)} {incr i $inc} {
962	if {$ftoc(skipMarked) == 0} {
963	    return $i
964	}
965	set marked 0
966	foreach tag [$exwin(ftext) tag names $i.0] {
967	    if [regexp {(deleted|moved|drange|mrange)} $tag] {
968		set marked 1 ; break;
969	    }
970	}
971	if {! $marked} {
972	    return $i
973	}
974    }
975    return $start
976}
977
978proc Ftoc_Changes {type {allowAuto 1} } {
979    global ftoc
980
981    if {$ftoc(changed) != 0} then {
982	Exmh_Debug Ftoc_Changes $type
983	if {("$allowAuto" == "1") && $ftoc(autoCommit)} {
984	    Folder_CommitType $type
985	    return 0
986	}
987	if {$type != {}} {
988	    if {[string compare $type iconified] == 0} {
989		set msg "$ftoc(changed) changes pending"
990	    } else {
991		if {$ftoc(commitDialog) &&
992		    [FtocDialog $ftoc(changed) $type]} {
993		    Folder_CommitType $type
994		    Exmh_Focus
995		    return 0
996		} else {
997		    set msg "$ftoc(changed) changes pending: $type cancelled"
998		}
999	    }
1000	    Exmh_Focus
1001	    Exmh_Status $msg warn
1002	    Sound_Error
1003	} else {
1004	    Exmh_Status "Oops, $ftoc(changed) left over changes" error
1005	    set ftoc(changed) 0
1006	    return 1
1007	}
1008    }
1009    return $ftoc(changed)
1010}
1011proc FtocDialog { changes type } {
1012    global exwin ftoc
1013    if [winfo exists $exwin(mtext).commit] {
1014	destroy $exwin(mtext).commit
1015    }
1016    set f [frame $exwin(mtext).commit -class Dialog -bd 4 -relief ridge]
1017    set blurb [expr {($changes > 1) ? "are $changes changes" : "is one change"}]
1018    Widget_Message $f msg -text \
1019"There $blurb pending.
1020(Press Return to Commit)
1021(Press <Control-c> to Cancel)" -aspect 1000
1022    set but [Widget_Frame $f but Dialog {top expand fill} -bd 10]
1023    set ftoc(okToCommit) 0
1024    Widget_AddBut $but cancel "Cancel" {set ftoc(okToCommit) 0}
1025    Widget_AddBut $but ok "Commit and $type" {set ftoc(okToCommit) 1}
1026    focus $but
1027    bind $but <Return> "$but.ok flash ; $but.ok invoke"
1028    bind $but <KP_Enter> "$but.ok flash ; $but.ok invoke"
1029    bind $but <Control-c> "$but.cancel flash ; $but.cancel invoke"
1030    Widget_PlaceDialog $exwin(mtext) $exwin(mtext).commit
1031    Visibility_Wait $but
1032    catch {grab $but}
1033    tkwait variable ftoc(okToCommit)
1034    catch {grab release $but}
1035    destroy $exwin(mtext).commit
1036    return $ftoc(okToCommit)
1037}
1038
1039proc Ftoc_CurLines {} {
1040    global ftoc
1041    if {$ftoc(curLine) != {}} {
1042	return $ftoc(curLine)
1043    } elseif {!$ftoc(pickone)} {
1044	return $ftoc(lineset);
1045    } else {
1046	return {}
1047    }
1048}
1049
1050proc Ftoc_CurMsgs {} {
1051    Ftoc_MsgNumbers [Ftoc_CurLines]
1052}
1053
1054proc Ftoc_Iterate { linenoVar body } {
1055    global ftoc
1056    upvar $linenoVar lineno
1057    foreach lineno [Ftoc_CurLines] {
1058	uplevel 1 $body
1059    }
1060}
1061proc Ftoc_MsgIterate { msgidVar body } {
1062    global ftoc
1063    upvar $msgidVar msgid
1064    foreach msgid [Ftoc_CurMsgs] {
1065	uplevel 1 $body
1066    }
1067}
1068proc Ftoc_Unmark {} {
1069    global ftoc
1070
1071    set hits 0
1072    Ftoc_Iterate lineno {
1073	if [FtocUnmarkInner $lineno] { incr hits }
1074    }
1075    Exmh_Status "Unmarked $hits msgs"
1076    incr ftoc(changed) -$hits
1077}
1078proc FtocUnmarkInner { lineno {all 0}} {
1079    global exwin
1080    set res 0
1081    if {$all} {
1082	set pat (deleted|moved|drange|mrange|copied)
1083    } else {
1084	set pat (deleted|moved|drange|mrange)
1085    }
1086    foreach tag [$exwin(ftext) tag names $lineno.0] {
1087	if [regexp $pat $tag] {
1088	    $exwin(ftext) tag remove $tag $lineno.0 $lineno.end
1089	    if [regexp {(drange|mrange|crange)} $tag] {
1090		eval $exwin(ftext) tag add range $lineno.0 $lineno.end
1091	    }
1092	    set res 1
1093	}
1094    }
1095    return $res
1096}
1097proc Ftoc_Delete { lineno } {
1098    global exwin ftoc
1099    $exwin(ftext) configure -state normal
1100    $exwin(ftext) delete $lineno.0 "$lineno.end + 1 chars"
1101    $exwin(ftext) configure -state disabled
1102    set ftoc(displayDirty) 1
1103    Ftoc_ClearMsgCache
1104}
1105proc Ftoc_RemoveMark { lineno } {
1106    # Flag the current message(s) for deletion
1107    global ftoc exwin
1108    if ![FtocUnmarkInner $lineno 1] {
1109	incr ftoc(changed)
1110    }
1111
1112    if {$ftoc(pickone)} {
1113	$exwin(ftext) tag add deleted $lineno.0 $lineno.end
1114    } else {
1115	$exwin(ftext) tag remove range $lineno.0 $lineno.end
1116	$exwin(ftext) tag add drange $lineno.0 $lineno.end
1117    }
1118}
1119proc Ftoc_MoveMark { lineno } {
1120    global ftoc exwin exmh
1121    if ![FtocUnmarkInner $lineno] {
1122	incr ftoc(changed)
1123    }
1124    # This tag records the target folder
1125    $exwin(ftext) tag add [list moved $exmh(target)] $lineno.0 $lineno.end
1126
1127    if {$ftoc(pickone)} {
1128	$exwin(ftext) tag add moved $lineno.0 $lineno.end
1129    } else {
1130	$exwin(ftext) tag remove range $lineno.0 $lineno.end
1131	$exwin(ftext) tag add mrange $lineno.0 $lineno.end
1132    }
1133}
1134proc Ftoc_CopyMark { lineno } {
1135    global ftoc exwin exmh
1136    if ![FtocUnmarkInner $lineno] {
1137	incr ftoc(changed)
1138    }
1139    # This tag records the target folder
1140    $exwin(ftext) tag add [list copied $exmh(target)] $lineno.0 $lineno.end
1141
1142    if {$ftoc(pickone)} {
1143	$exwin(ftext) tag add moved $lineno.0 $lineno.end
1144    } else {
1145	$exwin(ftext) tag remove range $lineno.0 $lineno.end
1146	$exwin(ftext) tag add mrange $lineno.0 $lineno.end
1147    }
1148}
1149proc Ftoc_Commit { rmmCommit moveCommit copyCommit } {
1150    global ftoc exwin
1151
1152    # Disable operations on ranges
1153    Ftoc_RangeUnHighlight
1154    if {! $ftoc(pickone)} {
1155	Buttons_Range 0
1156	set ftoc(lineset) {}
1157	set ftoc(pickone) 1
1158    }
1159
1160    Exmh_Status "Committing $ftoc(changed) changes..."
1161    $exwin(ftext) configure -state normal
1162    FtocCommit deleted $rmmCommit
1163    FtocCommit moved $moveCommit $copyCommit
1164    $exwin(ftext) configure -state disabled
1165    set l $ftoc(curLine)
1166    if {$l == {}} {
1167	set l $ftoc(numMsgs)
1168    }
1169    if {$l > 0} {
1170	WidgetTextYview $exwin(ftext) $l
1171    }
1172    if {! [Ftoc_Changes {} noautocommit]} {
1173	Exmh_Status "ok"
1174    }
1175}
1176proc FtocCommit {tagname commitProc {copyCommitProc {}} } {
1177    global ftoc exmh exwin msg mhProfile
1178
1179    set delmsgs {}
1180    set curid [file tail $msg(path)]
1181    set pairs [FtocMakeReversePairs [$exwin(ftext) tag ranges $tagname]]
1182    set seqdelmsgs {}
1183    foreach range $pairs {
1184	set c0 [lindex $range 0]
1185	set ce [lindex $range 1]
1186	scan $c0 "%d" lineno
1187	set msgid [Ftoc_MsgNumber $lineno]
1188	set F {}
1189	set delline 0	;# Nuke display line
1190	foreach tag [$exwin(ftext) tag names $c0] {
1191            # Build up a list of moved or copied messages
1192            # Note that the original order of the messages is maintained,
1193            # (We are going from bottom to top thru the display.)
1194            # The scan lines are reversed, which is handled by Scan_Move.
1195	    if {([llength $tag] == 2) && ([lindex $tag 0] == "moved")} {
1196		set F [lindex $tag 1]
1197		if ![info exists movemsgs($F)] {
1198		    set movemsgs($F) $msgid
1199		} else {
1200		    set movemsgs($F) [concat $msgid " " $movemsgs($F)]
1201		}
1202		lappend movescan($F) [$exwin(ftext) get $c0 "$ce + 1 chars"]
1203		set delline 1
1204	    }
1205	    if {([llength $tag] == 2) && ([lindex $tag 0] == "copied")} {
1206		set F [lindex $tag 1]
1207		if ![info exists copymsgs($F)] {
1208		    set copymsgs($F) $msgid
1209		} else {
1210		    set copymsgs($F) [concat $msgid " " $copymsgs($F)]
1211		}
1212		lappend movescan($F) [$exwin(ftext) get $c0 "$ce + 1 chars"]
1213	    }
1214	}
1215	if {$tagname == "deleted"} {
1216	    # Batch up deletes
1217	    lappend delmsgs $msgid
1218	    set delline 1
1219	}
1220	lappend seqdelmsgs $msgid					;# in case deleted or moved w/out viewing
1221	#Exmh_Debug FtocCommit added $msgid to be deleted from unseen-sequence
1222
1223	if {$delline} {
1224	    $exwin(ftext) delete $c0 "$ce + 1 chars"
1225	    set ftoc(displayDirty) 1
1226	    Ftoc_ClearMsgCache
1227	    if {$msgid == $curid} {
1228		Ftoc_ClearCurrent
1229		Msg_ClearCurrent
1230	    }
1231	    if {$lineno == $ftoc(curLine)} {
1232		set ftoc(curLine) {}
1233	    } elseif {$ftoc(curLine) != {}} {
1234		if {$lineno < $ftoc(curLine)} {
1235		    incr ftoc(curLine) -1
1236		    if {$ftoc(curLine) == 0} {
1237			set ftoc(curLine) {}
1238		    }
1239		}
1240	    }
1241	    incr ftoc(numMsgs) -1
1242	} else {
1243	    FtocUnmarkInner $lineno
1244	}
1245	incr ftoc(changed) -1
1246    }
1247
1248    if {$seqdelmsgs != {}} {
1249	Exmh_Debug FtocCommit deleting msgs from $mhProfile(unseen-sequence) $seqdelmsgs
1250	Seq_Del $exmh(folder) $mhProfile(unseen-sequence) $seqdelmsgs
1251    }
1252
1253    if {$delmsgs != {}} {
1254	Exmh_Status "$commitProc $delmsgs"
1255	if [catch {
1256	    BgAction "Rmm $exmh(folder)" $commitProc $exmh(folder) $delmsgs
1257	} err] {
1258	    Exmh_Status $err error
1259	}
1260    }
1261    # Do copies before links so you can both move and copy a message.
1262    if {[catch {array names copymsgs} flist] == 0} {
1263	foreach f $flist {
1264	    Exmh_Status "Copying to $f, $copymsgs($f)"
1265	    if [catch {
1266		BgAction "Refile $f" $copyCommitProc $exmh(folder) $copymsgs($f) $f
1267	    } err] {
1268		Exmh_Status $err error
1269	    }
1270	}
1271    }
1272    if {[catch {array names movemsgs} flist] == 0} {
1273	foreach f $flist {
1274	    Exmh_Status "Refiling to $f, $movemsgs($f)"
1275	    if [catch {
1276		BgAction "Refile $f" $commitProc $exmh(folder) $movemsgs($f) $f
1277	    } err] {
1278		Exmh_Status $err error
1279	    }
1280	}
1281    }
1282}
1283proc FtocMakePairs { list } {
1284    set result {}
1285    for {set i 0} {$i < [expr [llength $list]-1]} {incr i +2} {
1286	set first [lindex $list $i]
1287	set second [lindex $list [expr $i+1]]
1288	lappend result [list $first $second]
1289    }
1290    if {$result == {}} {
1291	return $list
1292    } else {
1293	return $result
1294    }
1295}
1296proc FtocMakeReversePairs { list } {
1297    set result {}
1298    for {set i [expr [llength $list]-1]} {$i >= 0} {incr i -2} {
1299	set second [lindex $list $i]
1300	set first [lindex $list [expr $i-1]]
1301	lappend result [list $first $second]
1302    }
1303    if {$result == {}} {
1304	return $list
1305    } else {
1306	return $result
1307    }
1308}
1309
1310proc Ftoc_MoveFeedback { msgid } {
1311    global exwin ftoc
1312    set lineno [Ftoc_FindMsg $msgid]
1313    if {$lineno == ""} {
1314        return
1315    }
1316    set msg [Exmh_OldStatus]
1317    foreach tag [$exwin(ftext) tag names $lineno.0] {
1318	if [regexp {moved (.+)} $tag match folder] {
1319	    Exmh_Status "$msgid => +$folder"
1320	    return
1321	} elseif [regexp deleted $tag] {
1322	    Exmh_Status "$msgid Pending Delete"
1323	    return
1324	}
1325    }
1326    Exmh_Status $msg
1327}
1328proc Ftoc_FindNext {} {
1329    Find_It forw
1330}
1331proc Ftoc_FindPrev {} {
1332    Find_It back
1333}
1334proc Ftoc_FindAll {string} {
1335    global exwin find ftoc
1336    if {[string length $string] == 0} {
1337	Exmh_Status "No search string" warn
1338	return -1
1339    }
1340    set msgids {}
1341    for {set L 1} 1 {incr L} {
1342	if [$exwin(ftext) compare $L.end >= end] {
1343	    break
1344	}
1345	if [catch {$exwin(ftext) get $L.0 $L.end} text] {
1346	    break
1347	}
1348	if [regexp -nocase -- $string $text] {
1349	    lappend msgids [Ftoc_MsgNumberRaw $text]
1350	}
1351    }
1352    if {[llength $msgids] == 0} {
1353	Exmh_Status "No match" warn
1354	return 0
1355    } else {
1356	Ftoc_PickMsgs $msgids 0
1357	return 1
1358    }
1359
1360}
1361proc Ftoc_FindMatch {L string} {
1362    global exwin ftoc
1363    if {$L == $ftoc(lasthit)} {
1364	return 0
1365    }
1366    if [catch {$exwin(ftext) get $L.0 $L.end} text] {
1367	return -1	;# off the end or beginning
1368    }
1369    if [regexp -nocase -- $string $text] {
1370	set ftoc(lasthit) $L
1371	Msg_Pick $L show
1372	return 1
1373    }
1374    return 0
1375}
1376proc Ftoc_Yview {args} {
1377    global exwin
1378    eval {WidgetTextYview $exwin(ftext)} $args
1379}
1380proc Ftoc_Advance { advance } {
1381    global ftoc
1382    if {[string compare $advance "advance?"] == 0} {
1383	return $ftoc(linkAdvance)
1384    } else {
1385	return $advance
1386    }
1387}
1388proc Ftoc_PageUp {} {
1389    global exwin
1390    Widget_TextPageUp $exwin(ftext)
1391}
1392proc Ftoc_PageDown {} {
1393    global exwin
1394    Widget_TextPageDown $exwin(ftext)
1395}
1396
1397proc Ftoc_Sort {} {
1398    global ftoc
1399    case $ftoc(autoSortType) {
1400	{date} { Folder_Sort -datefield date }
1401	{subject} { Folder_Sort -textfield subject }
1402	{sender} { Folder_Sort -textfield from }
1403	{custom} { eval Folder_Sort $ftoc(autoSortCrit) }
1404    }
1405}
1406proc Ftoc_SelectAll {} {
1407    global ftoc
1408    FtocRangeStart 1
1409    FtocRangeEnd $ftoc(numMsgs)
1410}
1411proc Ftoc_SelectAllToEnd {} {
1412    global ftoc
1413    if {$ftoc(curLine) != {}} {
1414	FtocRangeStart $ftoc(curLine)
1415    } else {
1416	if {$ftoc(direction) == "next"} {
1417	    FtocRangeStart 1
1418	} else {
1419	    FtocRangeStart $ftoc(numMsgs)
1420	}
1421    }
1422    if {$ftoc(direction) == "next"} {
1423	FtocRangeEnd $ftoc(numMsgs)
1424    } else {
1425	FtocRangeEnd 1
1426    }
1427}
1428proc Ftoc_CatchUp {} {
1429    global ftoc
1430    Ftoc_SelectAll
1431    Msg_Remove
1432    Folder_Commit
1433    Msg_PageOrNext
1434}
1435proc Ftoc_CatchUpToEnd {} {
1436    global ftoc
1437    Ftoc_SelectAllToEnd
1438    Msg_Remove
1439    Folder_Commit
1440    Msg_PageOrNext
1441}
1442#
1443# Interface to Drag & Drop
1444#
1445set ftocDrag(types) {foldermsg}
1446set ftocDrag(formats) {string filename}
1447set ftocDrag(format,foldermsg) string
1448set ftocDrag(format,filename) string
1449set ftocDrag(type,string) foldermsg
1450
1451# Drag Selected
1452proc FtocDragSelect {w x y wx wy} {
1453    global exmh ftoc ftocDrag mhProfile
1454
1455    set folder $ftoc(folder)
1456    if !$ftoc(displayValid) {
1457	set folder $exmh(folder)
1458    }
1459    if $ftoc(pickone) {
1460	set lineno [lindex [split [$w index cur] .] 0]
1461	set msgids [Ftoc_MsgNumber $lineno]
1462	if {$msgids == {} || $msgids == 0} return
1463	set ftocDrag(data,filename) $mhProfile(path)/$folder/$msgids
1464    } else {
1465	set msgids [Ftoc_MsgNumber $ftoc(lineset)]
1466	catch {unset ftocDrag(data,filename)}
1467    }
1468
1469    # Hand off to Drag code
1470    set ftocDrag(source) $w
1471    set ftocDrag(data,foldermsg) "+$folder $msgids"
1472    Drag_Source ftocDrag $x $y
1473}
1474proc FtocToggleSequence { seq } {
1475    global ftoc exmh
1476    set folder $exmh(folder)
1477    set origmsgids [Seq_Msgs $folder $seq]
1478    set selmsgids [Ftoc_CurMsgs]
1479    if {$selmsgids == {}} {
1480	Exmh_Status "No messages were selected"
1481    } else {
1482	# If any selected message is not already in the sequence, then add
1483	#   messages to the sequence.
1484	# If all selected messages are already in the sequence, then remove
1485	#   messages from the sequence.
1486	set flag del
1487	foreach msgid $selmsgids {
1488	    if {[lsearch -exact $origmsgids $msgid] == -1} {
1489		set flag add
1490	    }
1491	}
1492	if {$flag == "del"} {
1493	    Seq_Del $folder $seq $selmsgids
1494	} else {
1495	    Seq_Add $folder $seq $selmsgids
1496	}
1497	Ftoc_ShowSequence $seq $selmsgids
1498    }
1499}
1500
1501# exmh-2.5 APIs
1502# Ftoc_ColorConfigure
1503# Ftoc_MarkSeen
1504# Ftoc_ShowUnseen
1505