#!/usr/local/bin/wish -f

#
# arTCLs -  article reading TCL script
#
proc Update {} {update}

# ---------- user re-defineable globals -----------------------------

## font to use in listboxes
set ListFont "lucidasanstypewriter-12"

## interval in minutes to use for re-checking for new articles
## if the interval is <= 0 then no rescanning is done
set RescanInterval 60

# more profile stuff is set later, this is just so it can bootstrap itself.
# do not touch the next line !!
set Profile(ArtclsLib) /usr/local/lib/artcls;#SEDMARK

# -------------------------------------------------------------------

#### --- debug stuff

set FT ""
trace variable FT w newsTraceCmd
proc newsTraceCmd {n1 n2 op} {
    global  $n1
    puts stderr "$n1:  [set $n1]"; flush stderr
}
# there are better ways of doing function tracing than this
proc InFunc {s} { 
    # global FT; set FT $s+ 
}
proc OutFunc {s} {
    # global FT; set FT $s-
}

#### --- globals and related init functions
set Groups(.)		{ _First _Last _Flag }; # list of all available groups
set GroupCriteria	new; # last criteria for retrieving group list

# load external files
foreach f { 
    setProfile.t
    nntp.t
    timer.t
    menuBind.t
    makeMenuBar.t 
    listBoxHelper.t
    filterSubject.t
    showMsg.t
} {
    source $Profile(ArtclsLib)/$f
}

# set up user profile
initProfile 1

### --- rc init functions

# externalize the global newsrc related data to the caller
proc rcExterns {} {
    uplevel 1 { global Rc_artnums Rc_description Rc_subscribed Rc_last RcList }
}

# initialize the arrays accciated with the news rc file
proc rcInitData {{notfirst 1}} {
    rcExterns
    if {$notfirst} {
	unset Rc_description
	unset Rc_subscribed
	unset Rc_artnums
	unset Rc_last
	unset RcList
    }
    set Rc_description(.)	"bogus_newsgroup"; # description of group
    set Rc_subscribed(.)	0;	# 0 or 1
    set Rc_artnums(.)		{ {} };	# arrayof articles
    set Rc_last(.)		{ 1 };	# ??
    set RcList			{ };	# ordered list of indexes into Rc_X's
}

#### ---- misc

# tk error handler
proc tkerror {s} { puts stderr "\narTCLs: '$s'\n" }

proc setArticleHeader {tag val} {
    global ArticleHeader
    set ArticleHeader($tag) $val
}

####
#### ---- newsrc articles list functions
####

proc inSpan {artnum first last} {
    if { $artnum<=$last && $artnum>=$first } {
	return 1
    }
    return 0
}

#+
# boolean <== rcNewArticle {groupname artnum}
#
# Returns a boolean value wihich indicates whether article 'artnum'
# in 'groupname' is unread  or not
#
#    1 - article unread
#    0 - article already read
#-
proc rcNewArticle {groupname artnum} {
    global Rc_artnums
    foreach span $Rc_artnums($groupname) {
	if {[llength $span]==1} {
	    if {$span==$artnum} {
		return 0
	    }
	} else {
	    if {[inSpan $artnum [lindex $span 0] [lindex $span 1]]} {
		if {([lindex $span 0]-[lindex $span 1])>0} {
		    error "bogus span <$span> in $groupname rc data"
		}
		return 0
	    }
	}
    }
    return 1
}

# any new articles to be read for group 'gname' articles f thru l ???
# return 1 if so
#        0 if not
proc rcAnyNewArticles {gname f l} {
    while { $l >= $f } {
	if {[rcNewArticle $gname $l]} {
	    return 1
	}
	incr l -1
    }
    # all read. A good place to compact the articles read array so do so
    rcCatchup $gname
    return 0
}

#+
# rval <== rcSpanMerge {s a {g .}}
#
# where rval is:
#
#    {new_span}	if (a)rticle was merged into supplied (s)pan
#    "less"	if span is less than article
#    "greater"	if span is greater than article
#    "read"	if article is in span
#
# XXX
#
# Note: (g)roupname is supplied merely to allow rcSPanMerge to make a 
#       descriptiveerror message
#-

proc rcSpanMerge {s a {g .}} {
    # take care of the single elem list here..
    if {[llength $s] == 1} {
	if { $s==$a }		{ return read }

	##
	if { ($s-1)==$a }	{ return [list $a $s] }
	if { ($s+1)==$a } 	{ return [list $s $a] }
	## not merge-able
	if { $s<$a } 	  	{ return less }
	if { $s>$a }		{ return greater }

    } else {
	set first [lindex $s 0]
	set last  [lindex $s 1]

	if {($a>=$first) && \
	    ($a<=$last)} 	{ return read }

	if {($first-1)==$a}	{ return [list $a $last]}
	if {($last+1)==$a}	{ return [list $first $a]}
	## not merge-able
	if { $first<$a }	{ return less }
	if { $last>$a }		{ return greater }
    }

    ## we should not get here
    rcExterns
    puts stderr "rcSpanMerge is confused, is the newsrc line for $g bogus ??"
    puts stderr "rc line is <$Rc_artnums($g)>"
    return "error"
}

proc rcMarkDbg {str} { }

proc rcMark {g a} {
    rcExterns
    set x 0
    set rval "less"

    if {![rcNewArticle $g $a]} {
	return 0; # already read
    }

    rcMarkDbg "$g MARK <$a> in <$Rc_artnums($g)>"
    foreach s $Rc_artnums($g) {
	set rval [rcSpanMerge $s $a $g]
	case $rval {read} {
	    ## already marked as read
	    rcMarkDbg "already read"
	    return 0;
	} {less} {
	} {greater} {
	    ## spans are greater than article, insert it in front of s
	    set rval [linsert $Rc_artnums($g) $x $a]
	    set Rc_artnums($g) $rval
	    rcMarkDbg "Gmark <$a> in <$Rc_artnums($g)>"
	    return 0
	} {error} {
	    rcMarkDbg "ERROR <$a> in <$Rc_artnums($g)>"
	    return -1
	} default {
	    rcMarkDbg "$rval merge into $x"
	    set Rc_artnums($g) [lreplace $Rc_artnums($g) $x $x $rval]
	    rcMarkDbg "Mmark <$a> in <$Rc_artnums($g)>"
	    return 0
	}
	incr x 1
    }

    ## spans are less than article
    case $rval {less} {
	lappend Rc_artnums($g) $a
	rcMarkDbg "Lmark <$a> in <$Rc_artnums($g)>"
	return 0
    }

    puts stderr "rcMark: cannot Mark $a in $g"
    puts stderr "rc line is <$Rc_artnums($g)>"
    return -1
}

####
#### ---- newsrc file i/o functions
####

# return the name of the newsrc file
proc rcName {} { 
    return [glob ~/.newsrc]
}

# write the internal representation of the rc file out to disk
proc rcWrite {fname} {
    rcExterns
    exec touch $fname
    if {[file writable $fname]} {
	exec mv $fname $fname.BAK; # try to protect against ill timed kills 
	set rcout [open $fname w]
	# write out subscribed ones first then unsubscribed ones
	foreach subflag {1 0} {
	    foreach gname $RcList {
		if {$Rc_subscribed($gname)==$subflag} {
		    if {$Rc_subscribed($gname)} { set s ":" } else { set s "!" }
		    # format the associated article numbers into the 
		    # traditional .newsrc format. Do it in one big nasty stmt!
		    puts $rcout \
		    "$gname$s [join [join $Rc_artnums($gname) ,] -]"
		}
	    }
	}
	close $rcout
	## exec rm -f $fname.BAK
	return 0
    } else {
	# XXX
	# really want to ask user what to do here..
	statusMsg [format "cannot write news rc file '%s'" $fname]
	return -1
    }
}


#
# proc rcReadGroup {fname getgrp}
#
# description:
#    read a specific newsrc 'getgrp' into Rc data structures 
#    from rc file 'fname'
#
# return values:
#    0 if found and read
#   -1 if not found
#
proc rcReadGroup {fname getgrp} {
    rcExterns; rcInitData
    set fin [open $fname r]
    while{[gets $fin ln]>=0} {
	set junk [set group [set flag ""]]
	set rangeset "0-0"
	# break out the / group name / subscribed flag / article ranges /
	regexp {([^:!]+)([:!])[ 	]+(.*)} $ln junk group flag rangeset
	if {$group==$getgrp} {
	    #
	    # if not in already RcList data structures add it
	    # else just (re)set the articles read list
	    #
	    if {[lsearch $RcList $group]==-1} {
		if {$flag==":"} { 
		    set Rc_subscribed($group) 1 
		} else { 
		    set Rc_subscribed($group) 0 
		}
		lappend RcList $group
		set Rc_artnums($group) [split [split $rangeset -] ,]
	    } else {
		set Rc_artnums($group) [split [split $rangeset -] ,]
	    }
	    close $fin
	    return 0
	}
    }
    close $fin
    return -1
}

proc rcCheck {fname {makeone 1}} {
    if {[file exists $fname]} {
	if {![file readable $fname]} {
	    puts stderr "your rc file '$fname' not readable"
	    return -1
	}
	if {![file writable $fname]} {
	    puts stderr "your rc file '$fname' not writable"
	    return -1
	}
    } else {
	if {$makeone} {
	    exec touch $fname
	}
	return 1
    }
    return 0
}

# read a newsrc file into Rc data structures
proc rcRead {fname} {
    rcExterns; rcInitData
    if {[rcCheck $fname]==-1} {
	Die . 0
    }
    set fin [open $fname r]
    set RcList { }
    while {1} {
	set group [set flag ""]
	set rangeset "0-0"
	set ln [gets $fin]
	# break out the / group name / subscribed flag / article ranges /
	regexp \
	    {([^:!]+)([:!])[ 	]+(.*)} $ln \
	    junk group flag rangeset

	case $flag in ! {
	    set Rc_subscribed($group) 0 
	} : {
	    set Rc_subscribed($group) 1 
	} default {
	    if {[eof $fin]} {
		return
	    }
	    puts stderr "Warning: semi-bogus newsrc line '$ln'"
	    # maybe just the range is missing..
	    regexp {([^:!]+)([:!])} $ln junk group flag
	    case $flag in ! {
		set Rc_subscribed($group) 0 
		set rangeset "0"
	    } : {
		set Rc_subscribed($group) 1 
		set rangeset "0"
	    } default {
		puts stderr "Warning: bogus newsrc line '$ln'"
		continue
	    }
	}

	# the nested split below splits a .newsrc article range set 
	# into an tcl list
	# e.g. , it take the article ranges	100-200,501,505,600-700 
	#        and makes the list	       {100 200}501 505 {600 700}
	set Rc_artnums($group) [split [split $rangeset -] ,]

	lappend RcList $group
	## puts stderr "rcRead: $group $Rc_artnums($group)"
    }
    close $fin
}

proc rcNewGroup {group} {
    rcExterns
    set Rc_subscribed($group) 1
    set Rc_artnums($group) { {0 0} }
    lappend RcList $group
}

proc rcDeleteGroup {group} {
    rcExterns
    unset Rc_subscribed($group) Rc_artnums($group)
    set idx [lsearch $RcList $group]
    set RcList [lreplace $RcList $idx $idx]
}

proc rcCatchup {group} {
    rcExterns
    global Groups
    set resetlist {}
    set Rc_artnums($group) \
	[lappend $resetlist [list 1 [lindex $Groups($group) 1]]]
}

# Return any groups known by the server that are not in the newsrc 
# file in a list. An empty list means all known groups
# are a subset of the newsrc file
proc checkForNewGroups {} {
    global Groups; rcExterns
    set rlist {}
    foreach n [array names Groups] {
	set r [catch {set x $Rc_subscribed($n)}]
	if {$r} {
	    lappend rlist $n
	}
    }
    return $rlist
}

###
### --- application
###


proc resetGroupList {criteria} {
    rcExterns
    global Groups GroupCriteria

    set GroupCriteria $criteria
    set t [getMsg .]
    Update

    # get list of all groups available from server
    showMsg . "Getting available newsgroups.."
    for {set i 0} {$i<20} {incr i} {
	if {[nntpGetGroups]<0} {
	    statusMsg "error reading from server. Trying to reconnect.."
	    after 10000
	    nntpRestart
	} else {
	    break;
	}
    }

    # check for new groups...
    showMsg .  "Checking for new groups.."
    foreach grp [set newgroups [checkForNewGroups]] {
	showMsg . "adding '$grp' to end of your rc list"
	rcNewGroup $grp
	after 500
    }

    # check for bogus groups...
    showMsg .  "Checking for bogus groups.."
    foreach grp $RcList {
	if {![info exists Groups($grp)]} {
	    showMsg .  "Bogus group '$grp'. Deleting it from rc list"
	    rcDeleteGroup $grp
	    after 500
	}
    }

    # load listbox...
    showMsg .  "Loading subscribed groups.."
    groupList $RcList $criteria
    showMsg . $t
    return 0;
}

###
### -- ui (mostly) related stuff
###


# function to allow user to bind keys etc.
# to grouplist list widget
proc userGroupListBind {w} {
    # r = get a new list of groups and there available articles
    # from the server
    bind $w <KeyPress-r> {resetGroupList new}
    bind $w <KeyPress-R> {resetGroupList subscribed}

    bind $w <KeyPress-q> {Die .}
    bind $w <KeyPress-w> {
	set t [getMsg .]
	showMsg . "Writing rc file"
	rcWrite [rcName]
	showMsg . $t
    }
}

proc userGroupListMenuBar {w} {
    makeMenuBar $w {
	"News rc" {
	    "Write Rc" command			{ rcWrite [rcName] }
	    "Write Rc and Quit" command 	{ Die . }
	}
	"Groups" {
	    "List subscribed groups" command	{ resetGroupList subscribed }
	    "List unread groups" command	{ resetGroupList new }
	}
    }
}

# function to allow user to bind keys etc.
# to article list widget
proc userArticleListBind {w} {
    # C == cancel all Articles
    bind $w <KeyPress-C> {cancelArticlesThatMatch *}

    # c == cancel all Articles on screen
    bind $w <KeyPress-c> {cancelArticlesOnPage}

    # spacebar == article at top of list viewport
    bind $w <space> {newArt}

    # n == next article of the current subject
    bind $w <KeyPress-n> {newArt bysubject}
}

# Returns:
# index number of next subject line with matches 'subject'
# (read articles are ignored.)
# -1 if an index was not found
# 
proc indexBySubject {w filter subject} {
    set subject [$filter $subject]
    for {set last [$w size]; set x 0} {$x<$last} {incr x 1} {
	set entry [$w get $x]
	regexp {[ 	]*(.)[ 	]+([0-9]+)[   ]+(.*)} $entry junk flg artnum txt
	if {$flg=="!"} {
	    continue
	}
	if {$filter!=""} {
	    set txt [$filter $txt]
	}
	if {[string match $subject $txt]} {
	    return $x
	}
    }
    return -1
}

# Returns:
#     index number of next subj line with flag matching 'suppliedflg'
#    -1 if an index was not found
proc indexByFlag {w suppliedflg} {
    for {set last [$w size]; set x 0} {$x<$last} {incr x 1} {
	set entry [$w get $x]
	regexp {[ 	]*(.)[ 	]+([0-9]+)[   ]+(.*)} $entry junk flg artnum txt
	if {$flg==$suppliedflg} {
	    return $x
	}
    }
    return -1
}

proc markListLine {w {with ""} first {last ""}} {
    if {$last==""} { set last $first }
    if {$with==""} {
	# delete articles from the the pager
	# if last article on page scroll up one
	if {($first==[$w size]-1)&&([$w nearest 0]==$first)&&($first>0)} {
	    $w view [expr $first-1]
	}
	$w delete $first $last
    } else {
	for {set idx $last} {$idx>=$first} {incr idx -2} {
	    set entry [$w get $idx]
	    regexp {[ 	]*(.)[ 	]+([0-9]+)[   ]+(.*)} $entry junk flg artnum txt
	    set entry "$with $artnum $txt"
	    $w insert $idx $entry 
	    incr idx
	    $w delete $idx
	}
    }
    return $entry
}

# return 0 for OK
#       -1 for error (connection went away)
# indicator:
#	first = get first viewable article in box
#	bysubject = get next article with subject same as current one
#	<n> = get article that corresponds to the y location <n>
#
proc newArt {{i "first"} {groupname ""}} {
    rcExterns
    global Server Profile Text_Interp
    set w .artList.list.box
    set donewithlist 0

    if {$groupname==""} {
	set groupname [widgetVal .artList.message -text]
    }
    case $i bysubject {
	global ArticleHeader
	if {[info exists ArticleHeader(Subject)]!=0} {
	    set idx [indexBySubject $w filterSubject $ArticleHeader(Subject)]
	    if {$idx<0} { set idx [indexByFlag $w "."] }
	} else {
	    set idx 0
	}
    } {[0-9]*} {
	if {[set idx [$w nearest $i]]<0} {
	    return 0
	}
    } default {
	set idx [indexByFlag $w "."]
    }

    if {$idx<0} { 
	set idx 0 
	set donewithlist 1
    } else {
	# do not adjust view if picking with the mouse
	if {![string match {[0-9]*} $i]} {
	    $w view $idx
	}
    }

    if {$donewithlist} {
	if {[.GroupList.list.box size]>0} { NextGroup $groupname }
    }

    set val [$w get $idx]
    scan $val "%s %d" flg artnum

    set groupname [widgetVal .artList.message -text]
    send $Text_Interp "loadNewArticle $groupname $artnum"

    # mark article in list
    markListLine $w $Profile(DeleteWhenRead) $idx 
    # mark article read in rc data structures
    rcMark $groupname $artnum

    # if list is empty (all read) get the list of articles for the next group
    if {[$w size]<1} { 
	if {[.GroupList.list.box size]>0} { NextGroup $groupname }
    }
    return 0
}

# determine the 'right' article criteria to use and return it
proc artCriteria {} {
    global GroupCriteria
    if {$GroupCriteria=="subscribed"} {
	# looking at all subscribed groups get all articles in group. 
	return "."
    } else {
	# looking at just groups w/ new news get just new articles
	return ":"
    }
}

proc NextGroup {groupname} {
    rcExterns
    InFunc NextGroup
    getGroupArticles .GroupList.list.box [artCriteria] next $groupname
    # remove the read group from the group listbox
    groupDelete .GroupList $groupname
    OutFunc NextGroup
}
                     

#
# for val = 
#    next 	 get article list for group after gname
#    prev 	 get article list for group before gname
#    current 	 get article list for group gname
#    ddd  	 get article for ddd-th group in list
#
proc getGroupArticles {w criteria val {gname ""}} {
  InFunc getGroupArticles
  rcExterns; global Groups Text_Interp
  set origval $val

  while {1} {
    if {[.GroupList.list.box size] <= 0} {
	# no groups in list, return
	return
    }
    case $val {current prev next} { 
	set last [$w size]
	for {set x 0} {$x<$last} {incr x 1} {
	    if {[$w get $x]==$gname} {
		case $val {prev} {
		    set val [expr $x-1]
		    if {$val<0} { set val $last }; # wrap to end
		} {current} {
		    set val $x
		} {next} {
		    set val [expr $x+1]
		}
		break;
	    }
	}
	if {$val>=$last} { set val 0 }; # wraparound if at end
	set val [$w get $val]
    } {[0-9]*} {
	set val [$w get [$w nearest $val]] 
    } default {
	puts stderr "getGroupArticles: bogus argument val=<$val>"
	OutFunc getGroupArticles
	return
    }

    listBoxClear .artList.list.box
    .artList.message config -text "fetching $val..."; Update
    ###
    ### XXX BUG XXX
    ### if we do the bind below garbage is executed once in a while
    ### on the end of some the callbacks. you can get it to show up 
    ### by banging away on the 'c' key in the subject listbox
    ###
    ### somehow <ButtonPress-1> getting re-bound a lot causes the problem
    ### is there is re-entrancy problem somewhere ??? i can turn off 
    ### all the update calls and it still happens so update is not causing
    ### the problem
    ###
    ### bind .artList.list.box <ButtonPress-1> "newArt %y $val"
    ###
    set artlist [nntpGetArticles status $val subject $criteria]
    .artList.message config -text "$val"

    # tell the text script we are on a new group
    send $Text_Interp "setGroup $val"

    if {[set rval [llength $artlist]]>0} {
	foreach art $artlist {
	    listBoxAppendLine .artList \
		"[lindex $art 0] [lindex $art 1] [lindex $art 2]"
	}
    } {
	set t [getMsg .]
	showMsg . "articles unavailable for $val, Next Group.."
	after 3000
	showMsg . $t
	# cancelled article maybe ??
	rcCatchup $val
	if {[.GroupList.list.box size] > 0} {
	    # delete the empty group and try again..
	    groupDelete .GroupList $val
	    set val $origval
	    continue
	}
    }
    break;
  }
  OutFunc getGroupArticles
  return $rval
}

proc widgetVal {w key} {
    set l [$w config $key]
    return [lindex $l [expr [llength $l]-1]]
}

proc adjustSubjectView {w groupname} {
    # if list is all read go to next group else go to next unread article
    if {[$w size]<1 || [set idx [indexByFlag $w "."]]<0} {
	if {[.GroupList.list.box size] > 0} {
	    rcCatchup $groupname
	    NextGroup $groupname
	}
    } else { 
	if {$idx >=0} {
	    $w view $idx
	}
    }
}

proc cancelArticlesOnPage {} {
    global Profile
    set topw .artList
    set groupname [widgetVal $topw.message -text]
    set w $topw.list.box

    InFunc cancelArticlesonPage
    # get the first and last index of the listbox
    if {[set x [set first [$w nearest 0]]] < 0} {
	# in between loading pages most likely. blow it off.
	return
    }
    set last  [$w nearest [winfo height $w]]
    for {} {$x <= $last} {incr x 1} {
	scan [$w get $x] "%s %d" flag artnum
	rcMark $groupname $artnum
    }
    if {$first-1>=0} {
	$w view [expr $first-1]
    }
    markListLine $w $Profile(DeleteWhenRead) $first $last 
    adjustSubjectView $w $groupname
}

# cancel all articles in w matching matchstr
proc cancelArticlesThatMatch {matchstr {filter ""}} {
    global Profile
    set topw .artList
    set currentgroup [widgetVal .artList.message -text]
    set w $topw.list.box
    set dellist {}
    set last [$w size]

    for {set x 0} {$x<$last} {incr x 1} {
	set entry [$w get $x]
	regexp {([0-9]+)[   ]+(.*)} [$w get $x] junk artnum txt
	if {$filter!=""} {
	    set txt [$filter $txt]
	}
	if {[string match $matchstr $txt]} {
		rcMark $currentgroup $artnum
		set dellist [linsert $dellist 0 $x]
	    if {$filter!=""} {
		# statusMsg "KILL-ing <$entry>"
	    }
	}
    }
    $w view 0; # kludge, need to come up with something better
    foreach x $dellist { 
	markListLine $w $Profile(DeleteWhenRead) $x 
    }
    adjustSubjectView $w $currentgroup
}

# delete groupname out of the GroupList
proc groupDelete {topw gname} {
    set w $topw.list.box
    InFunc  groupDelete
    set last [$w size]
    for {set x 0} {$x<$last} {incr x 1} {
	if {[$w get $x]==$gname} {
	    $w delete $x
	    OutFunc  groupDelete
	    return $x
	}
    }
    OutFunc  groupDelete
    return -1
}

# make list of newsgroups, if list already exists just reset the list
proc groupList {glist criteria} {
    # externalize rc data structures
    rcExterns
    global Groups ListFont

    # XXX should probably use winfo to check, not catch
    set isnew [expr ![catch {
	set w [frame .GroupList]
	wm geometry . 600x350
	wm minsize . 300 300
	wm maxsize . \
		[expr [winfo screenwidth .]-64] \
		[expr [winfo screenheight .]-64]
    }]]

    # group list scrolling area
    if {$isnew} {

	# -- create
	frame $w.list -relief raised -borderwidth 2
	scrollbar $w.list.bar -relief sunken -command "$w.list.box view"
	listbox	$w.list.box -geometry 600x5 \
	    -font $ListFont -scroll "$w.list.bar set"

	# -- pack
	pack append  $w.list \
		$w.list.bar {left filly frame w} \
		$w.list.box {right fill frame e}
	pack append $w \
	    [userGroupListMenuBar $w] {top fill} \
	    $w.list {top fill}

	# -- bind 
	bind $w.list.box <ButtonPress-1> \
		"getGroupArticles $w.list.box \[artCriteria\] %y"
	bind $w.list.box <Shift-ButtonPress-1> \
		"getGroupArticles $w.list.box : %y"
	bind $w.list.box <Control-ButtonPress-1> \
		"getGroupArticles $w.list.box . %y"

	# -- user binds
	userGroupListBind $w

    } else {
	set w .GroupList
	listBoxClear $w.list.box
    }

    # article list scrolling area
    if {$isnew} {
	# -- create
	frame .artList
	message .artList.message -font lucidasans-bold-14 \
		-text "articles" -justify left -aspect 8000
	frame .artList.list -relief raised -borderwidth 2
	scrollbar .artList.list.bar \
		-relief sunken -command ".artList.list.box view"
	listbox	.artList.list.box -font $ListFont \
		-scroll ".artList.list.bar set"

	# -- pack
	pack append  .artList.list \
		.artList.list.bar {left filly frame w} \
		.artList.list.box {right expand fill frame e}

	pack append .artList \
		.artList.message {top fillx} \
		.artList.list {bottom expand fill}

    } else {
	listBoxClear .artList.list.box
    }

    # pack the whole ball 'O wax
    if {$isnew} {
	pack append . $w {top fillx} .artList {bottom expand fillx filly}
	###
	### XXX BUG XXX
	### putting the bind here so it gets called just once
	### makes it behave OK. you can go wild with the 'c' key
	### in the subject list window and everything works OK.
	###
	bind .artList.list.box <ButtonPress-1> "newArt %y" 
	userArticleListBind .artList
    }

    # load the group listing
    set any 0
    foreach gname $glist {
	case $criteria subscribed {
	    if { $Rc_subscribed($gname) } {
		$w.list.box insert end [string trim $gname " \r\n"]
	    }
	} new {
	    if { $Rc_subscribed($gname) } {
		if { [rcAnyNewArticles $gname [lindex $Groups($gname) 0] \
					      [lindex $Groups($gname) 1]] } { 
		    $w.list.box insert end [string trim $gname " \r\n"]
		    incr any 1
		}
	    }
	}
    }
    if {!$any} {
	.artList.message config -text "No news."
    }
    return $w
}

###
### -- go !
###

# XXX -- You need mike hoegeman's WM_PROTOCOL patches for Tk for this to work
proc Die {w {write_rc 1}} { 
    global Text_Interp
    send $Text_Interp Die .
    if {$write_rc} {
	showMsg $w "writing out your rc file '[rcName]'.."
	rcWrite [rcName]
    }
    destroy $w; nntpDisconnect;
    exit
}
catch {wm protocol . delete {Die .}}

# The catch here is to clean up the open'ed telnet child process if we
# happen blow up somewhere, not needed for mconnect but does not hurt
#
proc Catch {body dummy} {
    # eval $body
}

proc checkGroups {{x ""}} {
    global GroupCriteria
    set t [getMsg .]
    showMsg . "Writing rc file.."
    rcWrite [rcName]
    showMsg . $t
    if {[file readable /usr/local/lib/sounds/gong.au]} {
	catch { exec play -v 25 /usr/local/lib/sounds/gong.au & }
    }
    set groupname [widgetVal .artList.message -text]
    resetGroupList $GroupCriteria
    getGroupArticles .GroupList.list.box [artCriteria] current $groupname
}

set x "OK"
catch {
    global RescanInterval Text_Interp
    rcInitData 0
    wm geometry . 600x64
    wm minsize . 300 64
    map .
    update

    nntpClientInit

    set Text_Interp [format "%s_text" [wm title .]]
    exec wish -file $Profile(ArtclsLib)/text.t -name $Text_Interp &

    statusMsg "Reading news rc file"
    rcRead [rcName]
    statusMsg "done."

    resetGroupList new
    timerstart resetGroupListTimer checkGroups [expr $RescanInterval*1000*60]
    return "OK"
} x 

if {$x!="OK"} {
    puts stderr "arTCLs: error \n$x"
    Die . 
}
