#
# $Source: /home/nlfm/Working/Zircon/Released/lib/RCS/Channel.tcl,v $
# $Date: 2002/01/18 12:22:22 $
# $Revision: 1.18.1.199 $
#
#
package provide zircon 1.18
#
proc nil {op args} {
    switch $op {
    extendTime -
    window { return {} }
    nosort -
    nocase -
    active -
    operator -
    isa { return 0 }
    opText -
    addText {global currentNet ; eval [$currentNet info] addText $args}
    osTag { return [lindex $args 1] }
    }
    error "***** \007\007 nil called : $op $args"
}
#
proc getDefault {var net} {
    upvar #0 $var default
    if {[info exists default($net)]} { return $default($net) }
    global defaultNet
    return $default($defaultNet)
}
#
proc Channel {name args} {
    global currentNet
    switch -- $name :: {return [eval Channel_[lindex $args 0] [lrange $args 1 end] ]}
    switch nil [set id [Channel :: find $name $currentNet]] {
	set id [makeChannel $name]
    }
    eval $id configure $args
    return $id
}
#
proc cleanup {name} {
    set pattern "\[ ,\n\r\]"
    regsub -all -- $pattern $name {} name
    return $name
}
#
proc oneLiner {win chid} {
    notIdle $win [set net [$chid net]]
    set x [split [$win get]]
    if {![string match {} [set who [lindex $x 0]]] &&
      ![string match {} [set msg [join [lrange $x 1 end]]]]} {
	$net q1Send "PRIVMSG $who :$msg"
	$chid addText {} ">>$who : $msg"
    }
    $win delete 0 end
}
#
proc channel_popPalette {this prt x y} {
    popPalette .@pp$this$prt $this $prt [$this colour] $x $y
}
#
proc channel_makePalette {this prt} {
    global Ft
    # FRINK : array Ft
    makePalette .@pal$this$prt $this $prt "[$this name] $prt" $Ft($this) [$this colour]
}
#
proc setClose {this val} {
    if {[regexp {^[0-9]+$} $val]} { $this configure -closetime $val }
}
#
proc clearClose {this args} {
    uplevel #0 set ${this}(close) 0
    [$this wid] checkAllClose
}
#
proc channel_set {this flag} {
    switch $flag close {
	if {[$this close]} {
	    switch 0 [set v [$this closetime]] {set v {}}
	    tkwait window [mkEntryBox .@cl$this {Close Time} \
	      "Enter the close time for [$this name]:" \
	      [list [list Time $v]] [list ok "setClose $this"] \
	      [list cancel "clearClose $this"]]
	} {
	    [$this wid] checkAllClose
	}
    }
}
#
proc channel_setCrypt {this key} { $this configure -crypt $key }
#
proc channel_clearCrypt {this args} { $this setCrypt {} }
#
proc channel_getCrypt {this} {
    tkwait window [mkEntryBox .@cr$this {Encryption Key} \
      "Enter the encryption key for [$this name]:" \
      [list [list key [$this crypt]]] [list ok "$this setCrypt"] \
      [list clear "$this clearCrypt"] [list cancel {}]]
}
#
proc channel_drawSet {this} {
    set mn [$this window].channel.menu
    $mn entryconfigure [$mn index [trans draw]] \
      -state [expr {[$this draw] ? {normal} : {disabled}}]
}
#
proc makeChannel {chan} {
    set this [objName Channel]
    initObj $this Channel
    proc $this {unusedop args} "objCall channel $this \$unusedop \$args"
    set lchan [string tolower $chan]
    upvar #0 currentNet net
    upvar #0 $this cdata CTO$net CTO
    # FRINK : array cdata
    catch {array set cdata [uplevel #0 array get [getDefault defChan $net]] }
    array set cdata [list \
	name	$chan \
	lname	$lchan \
	keep	0 \
	net	$net \
	ircIImode [$net ircIImode] \
    ]
    $net register channels $this
    set CTO($lchan) $this
    return $this
}
#
proc channel_onShow {this} {
    if {[$this active]} { $this setTopic {} }
    switch -glob -- [$this name] +* {} default {
	[$this net] q1Send "MODE :[$this name]"
	$this configure -modewait 1
    }
}
#
proc channel_show {this args} {
    if {[$this active]} {
	$this flag normal
	$this unmarkV [set myid [[$this net] myid]]
	$this unmarkOp $myid
    } {
	set rchan [$this name]
   	set chan [$this lname]
	set net [$this net]
	set look [findLook $this $rchan $net]
	set p [findStyle $this $rchan $net]
   	switch default [set nn [$net name]] {set nn IRC}	
   	foreach {wname ttl} [$this setTitles] break
   	set wndw [Window :: make [lindex $p 1]]
	$wndw configure -cols [lindex $p 2]
	set xc {}
	switch [lindex $p 0] {
	  hicaffiene -
	  diet {
		if {[isNotDefaultNet $net]} { append ttl "- [$net name]" }
		set w [switchFrame \
    		  [set wn [$wndw name]] $this $ttl [$wndw nextCol] -handles]
		set ttl "Shared Channels"
		set del "deleteFrame [$wndw name] $this"
  		if {![$this open] || ![$this join]} {set xc "$wndw expose $this"}
	  }
	  original -
	  default {
  		  if {[isNotDefaultNet $net]} { append ttl "- [$net name]" }
  		  set w [$wndw name]
  		  set del {}
  		  if {[$this open] && [$this join]} {set xc "$wndw [$this iconify]"}
	  }
	}
	$wndw configure -title $ttl -iconop [$this iconify]
   	$this configure -wid $wndw -window $w
	$wndw register $this "$this doLeave {}" $del
  	$wndw setIcon $this $wname
  	makeCBody $this $w $rchan $chan $net $args $look
#	switch {} [$this logfile] {} default {
#	    switch {} [$this logfd] {
#	    	logOpen $this a 0 [$this logfile]
#	    }
#	}
	switch {} $xc {} default { eval $xc }
	$this defaultTags
	$this configure -dwin [WBoard -channel $this]
	handleOn $net POPUP [list $rchan [$this type]]
    }
    $this onShow
}
#
proc channel_operator {this} {return [$this isOp [[$this net] myid]]}
#
proc channel_type {this} {return [uplevel #0 set OType($this)] }
#
proc channel_speaker {this} {return [$this isSpeaker [[$this net] myid]]}
#
proc channel_isa {this kind} {
    global OType
    switch $OType($this) $kind {return 1}
    return 0
}
#
proc channel_active {this} {
    switch {} [$this wid] {return 0}
    return 1
}
#
proc channel_setTopic {this str} {
    if {![winfo exists [set w [$this window].topic.entry]]} return
    set state [$w cget -state]
    $w configure -state normal
    $w delete 1.0 end
    insertText $this $w $str {}
    $this topicState $state
}
#
proc channel_changeMode {this mode} {
    upvar #0 $this cdata
    set val $cdata(lcl$mode)
    set cdata(lcl$mode) [expr {!$val}]
    [$this net] MODE [$this name] [expr {$val ? {+} : {-}}]${mode}
}
#
proc channel_optText {this name str} {
    if {[$this wantMessage $name]} { $this addText @$name $str }
}
#
proc channel_addText {this tag txt} {
    switch {} [set win [$this window]] {} default {
	if {[$this open] && ![winfo ismapped $win]} {
	    if {[[$this net] noPopup]} {
		[$this wid] noPopUp $this
	    } {
		switch nil [$this wid] {
		    if {[$this isa Info]} {
		        exposeFrame [MainControl] [$this net]
		    }
		} default {
		    [$this wid] expose $this
		}
		handleOn [$this net] POPUP [list [$this lname]]
	    }
	}
    }
    $this doAddText $tag $txt
}
#
proc channel_send {this str args} {
    global user host
    switch {} $str {} default {
	global Secure
	$this addHist $str
	set lc [$this lname]
	set rchan [expr \
	  {[info exists Secure($lc)] ? $Secure($lc) : [$this name]}]
	set net [$this net]
	switch {} [set key [$this crypt]] {
	    $net q1Send "PRIVMSG $rchan :$str"
	} default {
	    set gsh $str
	    $net q1Send "PRIVMSG $rchan :[encrypt $gsh $key]"
	}
	regsub -all %n [$this mytag] [[$net myid] name] pr
	switch -- $args {} {
	    $this addText @me "$pr $str"
	} default {
	    $this doAddText @me "$pr $str"
	}
	if {[$this selfpattern]} {
	    doPatterns $this [[$net myid] name]!$user@$host $str
	}
    }
}
#
proc channel_isOp {this usr} {
    upvar #0 $this cdata
    if {![info exists cdata(Op,$usr)]} { return 0 }
    return $cdata(Op,$usr)
}
#
proc channel_isSpeaker {this usr} {
    upvar #0 $this cdata
    if {![info exists cdata(Spk,$usr)]} { return 0 }
    return $cdata(Spk,$usr)
}
#
proc channel_kickUser {this usr prefix kill} {
    $usr protect $this $prefix [expr {$kill ? {#} : {!}}]
    $this killUser $usr
}
#
proc channel_killUser {this usr} {
    upvar #0 $this cdata
    catch {destroy $cdata(ufrm).userBtn.$usr}
    set w $cdata(window)
    if {[set x [indexHack $w.users.menu [$usr name] 3]] >=0} {
	$w.users.menu delete $x
    }
    catch {$this delTag $usr}
    catch {$usr leave $this}
    safeUnset ${this}(Op,$usr) ${this}(Spk,$usr)
    listkill cdata(users) $usr
    listkill cdata(splitusers) $usr
}
#
proc channel_clearNames {this} {
    if {![$this active]} return
    set myid [[$this net] myid]
    foreach n [$this users] {
	switch $myid $n {} default {$this killUser $n}
    }
}
#
proc channel_doNames {this param} {
    upvar #0 $this cdata
    switch {} $cdata(mwin) {} default {
# We are monitoring this channel.
	$cdata(mwin) update $param
	return
    }
    if {![$this active]} {
	mkInfoBox $cdata(net) NAMES .@names$this [list names $cdata(name)] $param
	return
    }
    set kids $cdata(users)
    set net [$this net]
    set myid [$net myid]
    foreach n $param {
	set op 0
	set sp 0
	while {[string match {[@+]*} $n]} {
	    switch -glob -- $n @* {set op 1} default {set sp 1}
	    set n [string range $n 1 end] ;
	}
	set usr [User :: make $net $n]
	switch $usr $myid {
	    set cdata(Op,$myid) $op
	    set cdata(Spk,$myid) $sp
	    if {$op} { $this markOp $usr } elseif {$sp} { $this markV $usr }
	} default {
	    $this addUser $usr $op $sp
	}
	listkill kids $usr
    }
    foreach n $kids {
	if {[listmember $cdata(splitusers) $n]} { $this killUser $n }
    }
}
#
proc channel_isJoined {this usr} { return [listmember [$this users] $usr] }
#
proc channel_slowJoin {this} {
    $this clearNames
    [$this net] sysQ "JOIN [$this name] :[$this key]"
    upvar #0 $this cdata
    array set cdata {p 0 lclp 0 m 0 lclm 0 s 0 lcls 0 i 0 \
      lcli 0 t 0 lclt 0 n 0 lcln 0 a 0 lcla 0 }
}
#
proc channel_sendJoin {this args} {
    if {[$this active]} {
	[$this wid] expose $this
    } {
	$this putJoin [lindex $args 0]
    }
}
#
proc channel_putJoin {this key} {   
    switch {} $key {} default { $this configure -key $key }
    [$this net] q1Send "JOIN [$this name] :[$this key]"
    upvar #0 $this cdata
    array set cdata {p 0 lclp 0 m 0 lclm 0 s 0 lcls 0 i 0 \
      lcli 0 t 0 lclt 0 n 0 lcln 0 a 0 lcla 0 }
}
#
proc channel_rejoin {this} {
    $this optText KICKME "*** Attempting to rejoin"
    $this putJoin {}
}
#
proc channel_replace {this usr1 usr2} {
    if {[$this active]} {
	$this optText NICK "*** [$usr1 name] is now known as [$usr2 name]"
	set op [$this isOp $usr1]
	set sp [$this isSpeaker $usr1]
	$this killUser $usr1
	$this addUser $usr2 $op $sp
    }
}
#
proc channel_doJoin {this usr nm prefix ov} {
    if {![$this active]} { $this show }
    if {![$this isJoined $usr]} {
	$this optText JOIN \
	  "*** [$usr name] ($nm) has joined channel [$this name]"
	$this addUser [$usr ref] 0 0
    } {
	upvar #0 $this cdata
	set w $cdata(window)
	catch {if {[set x [indexHack $w.users.menu [$usr name] 3]] >=0} {
	    $w.users.menu entryconfigure $x -state normal
	}}
	catch {$cdata(ufrm).userBtn.$usr conf -state normal}
	listkill cdata(splitusers) $usr
	$usr heal
#	[$this net] q1Send "NAMES :[$this name]"
	$this optText BACK \
	  "*** [$usr name] ($nm) is back"
    }
    $this handleOV $ov $usr
    $this setOps $prefix $usr
    foreach x [$this ban] {
	if {[regexp -nocase -- [lindex $x 0] $prefix]} {
	    set net [$this net]
	    $net q1Send "MODE [$this name] +b [lindex $x 1]"
	    $net q1Send "KICK [$this name] [$usr name] :Ban List for [$this name]"
	    break
	}
    }
}
#
proc channel_monitorOn {this} {
   $this configure -monitor 1 -mwin [Monitor -net [$this net] -channel $this]
}
#
proc channel_handleOV {this ov usr} {
    switch $ov o {
	$this markOp $usr
	$this optText MODE \
	  "*** Mode change \"+o [$usr name]\" on channel [$this name]"
    } v {
	$this markV $usr
	$this optText MODE \
	  "*** Mode change \"+v [$usr name]\" on channel [$this name]"
    } ov - vo {
	$this markOp $usr
	$this markV $usr
	$this optText MODE \
	  "*** Mode change \"+ov [$usr name]\" on channel [$this name]"
    }
}
#
proc setOption {this sub opt val} {
    switch {} $val return
    switch *default* [$this lname] {
	set id *${sub}[capitalise $opt]
    } default {
	set id $this*${sub}$opt
    }
    option add *$id $val
}
#
proc channel_control {this args} { return [[$this net] control] }
#
proc channel_configure {this args} {
    upvar #0 $this cdata
    foreach {name val} $args {
	set opt [string range $name 1 end]
	switch -glob -- $name {
	-history {
	    switch {} $val { set val 50 }
	    if {$val < 0} { set val 0 }
	    set cdata(history) $val
	}
	-scrollback {
	    switch {} $val { set val 0 }
	    if {$val < 0} { set val [expr {-$val}] }
	    set cdata(scrollback) $val
	}
	-ban {
		set cdata(ban) {}
		foreach x $val {
		    if {[catch {regexp -nocase -- [set v [lindex $x 0]] abcde}]} {
			tellError {} {Ban List Error} "Bad regular\
expression in -ban for channel [$this name] - \"$v\""
			continue
		    }
		    switch {} [lindex $val 1] {
			tellError {} {Ban List Error} "No ban value\
for regular expression \"$v\" in -ban for channel [$this name]"
			continue
		    }
		    lappend cdata(ban) $x
		}
	    }
	-ops {
		set cdata(ops) {}
		foreach x [fixList $val] {
		    if {[catch {regexp -nocase -- $x abcde}]} {
			tellError {} {Ops Error} "Bad regular expression in -ops for channel [$this name] - \"$x\""
		    } {
			lappend cdata(ops) $x
		    }
		}
	    }
	-foreground -
	-background -
	-font {
		switch {} [set cdata($opt) $val] {} default {
		    setOption $this {} $opt $val
		    if {[$this active]} { [$this text] configure $name $val }
		}
	    }
	-geometry {
		set cdata($opt) $val
		setOption $this {*} $opt $val
	    }
	-height -
	-width  {
		global TypeCode
		set cdata($opt) $val
		set type $TypeCode([$this type])
		setOption $this $type* $opt $val
	    }
	-boldfont {
		global TypeCode
		set cdata($opt) $val
		set type $TypeCode([$this type])
		setOption $this $type* boldFont $val
	    }
	-topic {
		switch {} $val {} default {
		    regsub -all "\[\r\n\]" $val {} val
		    [$this net] TOPIC [$this name] $val
		}
	    }
	-closetime {
		if {$val < 0} {set val 0}
		set ct [expr {$val * 1000}]
		if {$ct > 0 && $ct < [[$this net] testTime]} {
		    [$this net] configure -testTime $ct
		}
		set cdata(closetime) $val
		set cdata(closemsec) $ct
		switch {} $cdata(wid) {} default { $cdata(wid) checkAllClose }
	    }
	-buttons {
		set cdata(buttons) $val
		if {[winfo exists $cdata(window).users.menu]} {
		    $cdata(window).users.menu entryconfigure 1 -label \
		      [trans [expr {[$this buttons] ? {no buttons} : {buttons}}]]
		}
	    }
	-name {
		upvar #0 CTO$cdata(net) CTO
		catch {unset CTO($cdata(lname))}
		set cdata(name) $val
		set cdata(lname) [string tolower $val]
		set CTO($cdata(lname)) $this
	    }
	-wid {
		switch {} $cdata(wid) {} default {
		    if {[string compare $cdata(window) [$cdata(wid) name]]} {
		    }
		    $cdata(wid) deregister $this
		}
		switch {} [set cdata(wid) $val] {
		    set cdata(window) {}
		} default {
		    switch {} $cdata(geometry) {} default {
			$val configure -geometry $cdata(geometry)
		    }
		}
	    }
	-monitor {
		if {!$val} {
		    switch {} $cdata(mwin) {} default {
			$cdata(mwin) delete
			set cdata(mwin) {}
		    }
		    [$this net] deMonitor $this
		}
		set cdata(monitor) $val
	    }
	-dwin -
	-mwin {
		switch {} $cdata($opt) {} default { $cdata($opt) delete }
		set cdata($opt) $val
	    }
	-joinat {
		checkJoinTime $this $val
	    }
	-msg { set cdata(msg) [string toupper $val] }
	+msg { lappend cdata(msg) [string toupper $val] }
	+*  { lappend cdata($opt) $val }
	-*  { set cdata($opt) $val }
	}
    }
}
#
proc channel_log {this val} {
    upvar #0 $this cdata
    switch {} $cdata(logfd) {} default {
	puts $cdata(logfd) $val
	$this doLog Flush
    }
    $cdata(wid) extendTime
}
#
proc channel_popdown {this} {
    if {[$this closeleave]} {
	$this doLeave {}
    } {
	handleOn [$this net] POPDOWN [list [$this lname]]
    }
}
#
proc channel_setTitles {this} {
   $this configure -mwin {}
   return [list [$this name] [$this name]]
}
#
proc makeCUsers  {this unusednet w unusedchan unusedrchan idx cdx look} {
    set f [frame $w.r$idx.uFrm -borderwidth 2 -relief raised]
    $this configure -ufrm $f
    scrollbar $f.vs -command "$f.userBtn yview" 
    text $f.userBtn -yscrollcommand "gvsSet $f.vs" -width 10 \
      -relief flat -borderwidth 0 -highlightthickness 0 -padx 0 -pady 0
    set on 1
    set scr 0
    foreach x $look {
	switch $x {
	off { set on 0 }
	scroll { $f.userBtn configure -yscrollcommand "$f.vs set" ; set scr 1}
	}
    }
    bindtags $f.userBtn {null}
    grid rowconfigure $f 0 -weight 1
    grid $f.userBtn -row 0 -column 0 -sticky ns
    if {$scr} { grid $f.vs -row 0 -column 1 -sticky ns }
    grid rowconfigure $w $idx -weight 1
    $this configure -anonpos "-row 0 -column $cdx -sticky nsew"
    if {$on} {grid $f -row 0 -column $cdx -sticky nsew}
}
#
proc makeCTopic {this net w unusedchan unusedrchan idx cdx unusedlook} {
    grid columnconfigure $w.r$idx $cdx -weight 1
    frame $w.topic -borderwidth 0
    grid columnconfigure $w.topic 1 -weight 1
    menubutton $w.topic.label -text [trans topic] -relief raised \
      -menu $w.topic.label.menu
    set om [menu $w.topic.label.menu -tearoff 0]
    $om add command -label [trans refresh] -command "$net TOPIC {[$this name]}"
    $om add command -label [trans new] -command "getTopic $this" -state disabled
    $om add separator
    foreach nn [$this topics] {
	$om add command -label [prune $nn 15] \
	  -command "$this configure -topic [list $nn]" -state disabled
    }
    emacsTEntry $w.topic.entry
    bind $w.topic.entry <Escape> "notIdle %W $net ; $this makePalette Topic"
    bind $w.topic.entry <ButtonPress-3> "notIdle %W $net ; $this popPalette Topic %X %Y"
    $w.topic.entry configure -state disabled -relief sunken -width 0
    grid $w.topic.label
    setProps $this $w.topic.entry
    grid $w.topic.entry -column 1 -row 0 -sticky ew
    bind $w.topic.entry <Return> "sendTopic $this %W ; notIdle %W $net"
    grid $w.topic -sticky ew -column $cdx -row 0 -in $w.r$idx
}
#
proc makeCText {this net w unusedchan unusedrchan idx cdx look} {
    global zLock zUnlock TypeCode
    set f [frame $w.r$idx.textFrm -borderwidth 0]
    grid rowconfigure $f 0 -weight 1
    $this configure -text [set ot $f.$TypeCode([$this type])]
    scrollbar $f.vs -command "$ot yview"
    text $ot -yscrollcommand "$f.vs set" \
      -width [$this width] -height [$this height]
    setProps $this $ot
    rebind $ot $net
    if {[$this jump]} { set im $zUnlock } { set im $zLock }
    button $f.jump -command "$this toggle jump $f.jump" -image $im
    switch {} $look {
	set look [list text [list scroll lock]]
    } nowrap {
	lappend look [list text [list scroll lock]]
    }
    set cl 0
    set rs 0
    foreach x $look {
	switch -- [lindex $x 0] {
	nowrap {
		scrollbar -orient horizontal $f.hs -command "$ot xview"
		$ot configure -wrap none -xscrollcommand "$f.hs set"
		set rs 1
		incr cl -1
	    }
	text {
		grid $ot -sticky nsew -row 0 -column $cl -rowspan 2
		if {$rs} {grid $f.hs -sticky ew -row 2 -column $cl}
		grid columnconfigure $f $cl -weight 1
	    }
	scroll {
		switch lock [lindex $x 1] {
		    grid $f.vs -sticky ns -row 0 -column $cl
		    grid $f.jump -row 1 -column $cl
		} default {
		    grid $f.vs -sticky ns -row 0 -column $cl -rowspan 2
		}
	    }
	lock {
		grid $f.jump -row 0 -column $cl
		grid $f.vs -sticky ns -row 1 -column $cl
		grid rowconfigure $f 0 -weight 0
		grid rowconfigure $f 1 -weight 1
	    }
	}
	incr cl
    }
    grid rowconfigure $w $idx -weight 1
    grid rowconfigure $w.r$idx 0 -weight 1
    grid columnconfigure $w.r$idx $cdx -weight 1
    grid $f -column $cdx -row 0 -sticky nsew
}
#
proc makeBindTopic {this} {
    global Fg Bg Ft BF
    set w [$this window].topic.entry
    confTag $w {} $Fg($this) $Bg($this) $Ft($this) $BF($this)
    switch {} $Fg($this) {} default { $w configure -foreground $Fg($this) }
    switch {} $Bg($this) {} default { $w configure -background $Bg($this) }
    switch {} $Ft($this) {} default { $w configure -font $Ft($this) }
}
#
proc makeBindInfo {unusedthis} { } 
#
proc makeBindPopper {unusedthis} { }
#
proc makeBindFlags {unusedthis} { }
#
proc makeBindEntry {this} {
    global Fg Bg Ft BF
    set w [$this window].cmd.entry
    switch {} $Fg($this) {} default { $w configure -foreground $Fg($this) }
    switch {} $Bg($this) {} default { $w configure -background $Bg($this) }
    switch {} $Ft($this) {} default { $w configure -font $Ft($this) }
}
#
proc makeBindControl {unusedthis} { }
#
proc makeBindUsers {unusedthis} { }
#
proc makeBindText {this} {
    upvar #0 $this cdata
    set tgw [$this tagWindow]
    set ot [$this text]
    foreach tag {foreground background font selectForeground
      selectBackground width height} {
	set lt [string tolower $tag]
	if {![string match {} \
	  [set v [option get $tgw $tag [capitalise $tag]]]] ||
	  ([info exists cdata($lt)] &&
	  ![string match {} [set v $cdata($lt)]])} {
	    catch {$ot configure -$lt $v}
	}
    }
}
#
proc toggleII {chid win} {
    global zII zNOII
    set w [$chid window]
    set net [$chid net]
    if {[$chid ircIImode]} {
	bind $w.cmd.entry <Return> "chDo $chid %W $net send \[%W get\]"
	set val 0
	set im $zNOII
    } {
	bind $w.cmd.entry <Return> "notIdle %W $net ; $net doMisc2 $chid %W"
	set val 1
	set im $zII
    }
    $chid configure -ircIImode $val
    $win configure -image $im
}
#
proc channel_toggle {this val win} {
    global zLock zUnlock
    if {[$this $val]} {
	set flg 0
	set im $zLock
    } {
	set flg 1
	set im $zUnlock
    }
    $this configure -$val $flg
    $win configure -image $im
}
#
proc channel_remove {this usr} {
    if {[$this isJoined $usr]} {
	$this killUser $usr
    } elseif {[$this monitor]} {
	switch {} [$this mwin] {} default { [$this mwin] remove $usr }
    }
}
#
proc makeCEntry {this net w chan unusedrchan idx cdx look} {
    global zII zNOII zLock zUnlock
    grid columnconfigure $w.r$idx $cdx -weight 1
    set om [frame $w.cmd -borderwidth 0]
    grid columnconfigure $om 0 -weight 1
    set occ $om.entry
    emacsEntry $occ
    grid $occ -row 0 -column 0 -sticky ew
    switch -- $look {} - scroll {
	scrollbar $om.hs -orient horizontal -command "$om.entry xview"
	$occ configure -xscrollcommand "$om.hs set"
	grid $om.hs -row 1 -column 0 -sticky ew
    }
    doBindings $occ $this $chan
    if {[$this ircIImode]} { set im $zII } { set im $zNOII }
    button $om.ii -command "toggleII $this $om.ii" -image $im
    grid $om.ii -column 1 -row 0
    set ot [$this text]
    bind $ot <Enter> "focus $occ ; notIdle %W $net"
    bind $ot <Configure> "%W see end ; notIdle %W $net"
    bind $w <Enter> "focus $occ ; notIdle %W $net"
    grid $om -sticky ew -row 0 -column $cdx -in $w.r$idx
}
#
proc makeCtlMode {this net w unusedchan rchan idx unusedlook} {
    upvar #0 $this cdata
    set om [makeMB $w.mode Mode]
    addImage $w.mode mode $w.cmds $idx $net
    $om add checkbutton -label [trans {pop up}] -variable ${this}(open) \
	-command "$this set open"
    $om add checkbutton -label [trans {pop down}] -variable ${this}(close) \
	-command "$this set close"
    $om add checkbutton -label [trans draw] -variable ${this}(draw) \
      -command "$this drawSet"
    if {[$this isa Chat]} { $om entryconfigure last -state disabled }
    $om add checkbutton -label [trans quiet] -variable ${this}(quiet) \
	-command "$this set quiet"
    $om add checkbutton -label [trans actions] -variable ${this}(actions) \
      -command "$this flipActions"
    if {$cdata(timestamp)} {
	$om add checkbutton -label [trans timestamp] -variable ${this}(stampMsg)
	set cdata(stampMsg) 1
    }
    if {[$this isa Channel]} {
	$om add checkbutton -label [trans nocase] -variable ${this}(nocase) \
	-command "$this set nocase"
	set efs29 [string match 2.9* [$net sVersion]]
	if {!$efs29 || ![string match +* $rchan]} {
	    $om add command -command "$this setBan" -label [trans ban]
	    foreach {v tx} "p private m moderated \
	      s secret i {Invite Only} t topic n {No Msg}" {
		uplevel #0 trace variable ${this}($v) w "{updVar $this}"
		set cdata($v) 0
		$om add checkbutton -command "$this changeMode $v" \
		  -variable ${this}(lcl$v) -state disabled -label [trans $tx]
	    }
	    if {$efs29 && [string match {&*} $rchan]} {
		uplevel #0 trace variable ${this}(a) w "{updVar $this}"
		set cdata(a) 0
		$om add checkbutton -command "$this changeMode a" \
		  -variable ${this}(lcla) -state disabled -label [trans anonymous]
	    }
	    $om add command -command "$this setLimit" -label [trans limit]
	    $om add command -command "$this setKey" -label [trans key]
	}
    }
}
#
proc makeCtlInfo {this net w chan rchan idx unusedlook} {
    if {[$this isa Channel]} {
	set om [makeMB $w.channel Channel]
	addImage $w.channel channel $w.cmds $idx $net
	$om add command -command "$net WHO [list $rchan]" -label [trans who]
	$om add command -command "userInvite $net [list $chan]" -label [trans invite]
	$om add command -command "channelNotice $net [list $chan]" -label [trans notice]
	$om add cascade -label [trans messages] -menu $om.msgs
	set omm [menu $om.msgs]
	catch {$omm configure  -tearoffcommand "retitle [list Messages for $chan]"}
	$omm add command -label [trans new] -command "$this newMsg"
	$omm add separator
	foreach x [$this messages] {
	    $omm add command -label [prune $x 15] -command "$this send [list $x]"
	}
	$om add command -command "\[$this dwin\] show" -label [trans draw]
	if {![$this draw]} { $om entryconfigure 3 -state disabled }
	addCTCPMenu $net $om $this
	addSoundMenu $net $om $this
    } {
	set usr [User :: find $rchan $net]
	set om [makeMB $w.channel User]
	addImage $w.channel user $w.cmds $idx $net
	$om add command -command "$net WHOIS \[$usr name\]" -label [trans whois]
	$om add command -label [trans invite] -state disabled
	$om add command  -command "channelNotice $net \[$usr name\]" \
	  -label [trans notice]
	$om add command -command "\[$this dwin\] show" -label [trans draw]
	if {![$this draw] || [$this isa Chat]} {
	    $om entryconfigure last -state disabled
	}
	addCTCPMenu $net $om $this
	addDCCMenu $om $this
	addSoundMenu $net $om $this
	addIgnoreMenu $om $usr
    }
    $om add cascade -label [trans log] -menu $om.log
    menu $om.log -tearoff 0
    foreach cmd {Close Open Flush Empty} {
	$om.log add command -label [trans $cmd] -command "$this doLog $cmd" \
	  -state disabled
    }
    if {![logOpen $this a 0 [$this logfile]]} {
	$om.log entryconfigure 1 -state normal
    } 
    $om add command -label [trans crypt] -command "$this getCrypt"
    $om add command -label [trans history] -command "$this sizeHistory"
    $om add command -label [trans exec] -command "doExec $net $this"
    $om add cascade -label [trans script] -state disabled
    addPluginMenu $om $net $this {}
    if {[$this isa Chat]} { $om entryconfigure last -state disabled }
}
#
proc makeCtlAction {this net w chan unusedrchan idx unusedlook} {
    set om [makeMB $w.action Action]
    catch {$w.action.menu configure -tearoffcommand "retitle {Actions for $chan}"}
    addImage $w.action action $w.cmds $idx $net
    $om add command -label [trans new] -command "$this getAction"
    $om add separator
    foreach act [$net actions] {
	$om add command -label [prune $act 15] \
	  -command "$this action [list $act]"
    }
}
#
proc makeCtlUsers {this net w unusedchan unusedrchan idx unusedlook} {
    $this buildUsersMenu [makeMB $w.users Names]
    addImage $w.users names $w.cmds $idx $net
}
#
proc buttonHack {btn cmd} {
    set sc [$btn cget -command]
    $btn configure -command $cmd
    tkButtonUp $btn
    $btn configure -command $sc
}
#
proc makeCtlQuit {this net w unusedchan unusedrchan idx unusedlook} {
    buttonmenu $w.quit -command "$this leave" -text [trans leave]
    bind $w.quit <Shift-1> [bind Button <1>]
    bind $w.quit <Shift-ButtonRelease-1> "buttonHack %W {$net quit}"
    bind $w.quit <Alt-1> [bind Button <1>]
    bind $w.quit <Alt-ButtonRelease-1> "buttonHack %W {$this doLeave {}}"
    set qm [menu $w.quit.menu -tearoff 0]
    if {[$this isa Channel]} {
	bind $w.quit <Control-1> {tkButtonDown %W}
	bind $w.quit <Control-ButtonRelease-1> "
	    $this configure -monitor 1
	    tkButtonUp %W
	"
	$qm add command -label [trans leave] -command "$this doLeave {}"
	$qm add command -label [trans monitor] \
	  -command "$this configure -monitor 1 ; $this doLeave {}"
    } {
	$qm add command -label [trans leave] -command "$this leave"
    }
    $qm add cascade -label [trans quit] -menu $qm.menu
    menu $qm.menu -tearoff 0
    set ctl [$this control]
    $qm.menu add command -label [trans new] -command "getQuit $ctl"
    $qm.menu add separator
    foreach x [$net signoffs] { $qm.menu add command -label [prune $x 15] \
	  -command "$net doQuit [list $x]" }
    if {[$this isa Channel]} {	
	$qm add separator
	$qm add command -label [trans new] -command "getLeave $this"
	$qm add separator
	foreach x [$net leaves] {
	    $qm add command -label [prune $x 15] -command "$this doLeave [list $x]"
	}
    }
    addImage $w.quit leave $w.cmds $idx $net
}
#
proc makeCtlClear {this net w unusedchan unusedrchan idx unusedlook} {
    button $w.clear -command "$this clear 0" -text [trans clear]
    addImage $w.clear clear $w.cmds $idx $net
    bind $w.clear <Shift-1> [bind Button <1>]
    bind $w.clear <Shift-ButtonRelease-1> "
	set sc \[lindex \[%W configure -command \] 4 \]
	%W configure -command {$this clear 1}
	[bind Button <ButtonRelease-1>]
	%W configure -command \$sc
    "
}
#
proc makeCFlags {unusedthis unusednet w unusedchan unusedrchan idx cdx look} {
    grid [frame $w.cflags -borderwidth 0] -row 0 -column $cdx -in $w.r$idx
    foreach x [lindex $look 0] { label $w.cflags.$x -relief flat }
}
#
proc makeCInfo {unusedthis unusednet w unusedchan unusedrchan idx cdx look} {
    grid [frame $w.cinfo -borderwidth 0] -row 0 -column $cdx -in $w.r$idx
    grid columnconfigure $w.r$idx $cdx -weight 1
    foreach x [lindex $look 0] { label $w.cinfo.$x -relief flat }
}
#
proc makeCControl {this net w chan rchan idx cdx look} {
    frame $w.cmds -borderwidth 0
    set p -1
    foreach x [lindex $look 0] {
	makeCtl[lindex $x 0] $this $net $w $chan $rchan [incr p] [lrange $x 1 end]
    }
    grid columnconfigure $w.r$idx $cdx -weight 1
    grid $w.cmds -row 0 -column $cdx -sticky ew -in $w.r$idx
}
#
proc rowHide {unusedthis unusednet w idx} {
    grid remove $w.r$idx
    if {![winfo exists $w.phide]} {
	grid [frame $w.phide -borderwidth 0 -relief flat] \
	  -row 0 -column 0 -sticky nw
    }
    grid [frame $w.phide.u$idx -relief raised -borderwidth 2] \
      -row 0 -column $idx -ipady 3 -ipadx 10 -sticky w
    bind $w.phide.u$idx <1> "
	destroy %W
	grid $w.r$idx
	switch {} \[winfo children $w.phide\] { destroy $w.phide }
    "
}
#
proc makeCPopper {this net w unusedchan unusedrchan idx cdx unusedlook} {
    grid [frame $w.p$idx -relief raised -borderwidth 2] \
      -row 0 -column $cdx -in $w.r$idx -sticky ns -ipadx 3
    grid columnconfigure $w.r$idx $cdx -weight 0
    bind $w.p$idx <1> "rowHide $this $net $w $idx"
}
#
proc makeCBody {this w rchan chan net unusedopts look} {
    grid columnconfigure $w 0 -weight 1
    set idx 0
    foreach x $look {
	set cdx 0
	grid [frame $w.r$idx -borderwidth 0] -row $idx -sticky nsew
	foreach y $x {
	    makeC[lindex $y 0] $this $net $w $chan $rchan \
	      $idx $cdx [lrange $y 1 end]
	    incr cdx
	}
	incr idx
    }
    global BF Fg Bg Ft Bl
    set ot [$this text]
    set BF($this) [getOValue $this $ot font boldFont Font]
    set Ft($this) [getOValue $this $ot font font Font]
    set Fg($this) [getOValue $this $ot foreground foreground Foreground]
    set Bg($this) [getOValue $this $ot background background Background]
    set Bl($this) [option get [$this tagWindow] bell Bell]
    $ot configure -font $Ft($this) -foreground $Fg($this) -background $Bg($this)
    foreach x $look {
	foreach y $x { makeBind[lindex $y 0] $this }
    }

    $this addUser [$net myid] 0 0
}
#
proc channel_newMsg {this} {
    mkEntryBox .@m$this [trans message] {Enter your new message:} \
      [list [list message {}]] [list ok "$this send"] \
      [list keep "$this keepMsg"] [list cancel {}]
}
#
proc channel_keepMsg {this str} {
    switch {} $str {} default {
	$this send $str
	[$this window].channel.menu.msgs add command \
	  -label "[prune $str 15]" -command "$this send [list $str]"
	$this configure +messages $str
	[$this net] configure +confChange Channels
    }
}
#
proc lclTrace {chan var} {
    switch {} [uplevel #0 trace vinfo ${chan}($var)] {
	uplevel #0 trace variable ${chan}($var) w "{updVar $chan}"
	uplevel #0 trace variable ${chan}($var) u "{unsVar $chan}"
    }
}
#
proc updVar {chan unused n2 unused2} {
    upvar #0 $chan cdata
    set cdata(lcl$n2) $cdata($n2)
}
#
proc unsVar {chan unused n2 unused2} { uplevel #0 unset ${chan}(lcl$n2) }
#
proc channel_buildUsersMenu {this w} {
    upvar #0 $this cdata
    set myid [[$this net] myid]
    lclTrace $this Op,$myid
    set cdata(Op,$myid) 0
    lclTrace $this Spk,$myid
    set cdata(Spk,$myid) 0
    $w add command -label [trans [expr {[$this buttons] ? {no buttons} : {buttons}}]] \
      -command "$this toggleUsers"
    $w add separator
}
#
proc channel_cmdState {this} {
    switch {} [set w [$this window]] return
    if {[$this m] && ![$this operator] && ![$this speaker]} {
	set st disabled
    } {
	set st normal
    }
    catch {$w.action configure -state $st}
    if {[winfo exists [set mn $w.channel.menu]]} {
	foreach x {notice messages draw ctcp sound} {
	    catch {$mn entryconfigure [$mn index [trans $x]] -state $st}
	}
    }
}
#
proc channel_toggleUsers {this} {
    set f [$this ufrm]
    set w [$this window]
    if {[winfo ismapped $f]} {
	$this configure -anonpos [grid info $f]
	grid forget $f
	$w.users.menu entryconfigure 1 -label [trans buttons]
    } {	
	eval grid $f [$this anonpos]
	$w.users.menu entryconfigure 1 -label [trans {no buttons}]
    }
}
#
proc channel_delete {this} { mcnDelete $this CTO[$this net] channels}
#
proc mcnDelete {this nvar reg} {
    upvar #0 $this cdata
    set net $cdata(net)
    $this doLog Close
    foreach x $cdata(users) {
	$x leave $this
	$this delTag $x
	safeUnset ${this}(Op,$x) ${this}(Spk,$x)
    }
    set cdata(users) {}
    set cdata(splitusers) {}
    $this configure -wid {} -window {} -dwin {}
    if {![$this keep] && ![$this monitor]} {
	safeUnset OType($this) [list ${nvar}([$this lname])] $this
	rename $this {}
	$net deregister $reg $this
    } {
	if {[$this monitor]} { [$this net] monitor $this } {
	    $this configure -mwin {}
	}
	if {[$this keep]} { $this configure -joinat [$this joinat] }
	$this configure -leaving 0
    }
    safeUnset Bl($this) Bg($this) Fg($this) BF($this) Ft($this) MkOp${net}($this)
    safeDestroy .@pal${this}Entry .@pal${this}Topic .@pp${this}Entry .@pp${this}Topic
}
#
proc setHistory {this val} {
    if {[regexp {^[0-9]+$} $val]} { $this configure -history $val }
}
#
proc channel_sizeHistory {this} {
    mkEntryBox .@sh$this {History Size} \
      "Enter the history size for [$this name]:" \
      [list [list history [$this history]]] [list ok "setHistory $this"]\
      [list cancel {}]
    tkwait window .@sh$this
}
#
proc pickOut {unused win pos} {
    switch -glob -- [set txt [$win get "$pos linestart" "$pos lineend"]] {
    {[=->]*} { return [string range $txt 2 end] }
    {!>*} { return [string range $txt 3 end] }
    {\**} { regsub {^[*] [^ ]+ } $txt {} txt }
    {[0-9]*} {regsub {^[^>]*> } $txt {} txt}
    }
    return $txt
}
#
proc channel_addHist {this str} {
    upvar #0 $this cdata
    set cdata(histbuff) [lrange [linsert $cdata(histbuff) 0 $str] 0 $cdata(history)]
    set cdata(hpos) -1
}
#
proc channel_getPrev {this} {
    upvar #0 $this cdata
    incr cdata(hpos)
    switch -- [set line [lindex $cdata(histbuff) $cdata(hpos)]] {} {
	set cdata(hpos) 0
	return [lindex $cdata(histbuff) 0]
    }
    return $line
}
#
proc channel_getNext {this} {
    upvar #0 $this cdata
    incr cdata(hpos) -1
    switch -- [set line [lindex $cdata(histbuff) $cdata(hpos)]] {} {
	set cdata(hpos) [expr {[llength $cdata(histbuff)] - 1}]
	return [lindex $cdata(histbuff) end]
    }
    return $line
}
#
proc channel_flipActions {this} {
    global zircon
    set win [$this window].cmd.entry
    set ret [bind $win <Return>]
    set sret [bind $win <$zircon(action)>]
    bind $win <Return> $sret
    bind $win <$zircon(action)> $ret
    upvar #0 $this cdata
    set cdata(actionmode) [expr {!$cdata(actionmode)}]
}
#
# Leaving channels :
#	doLeave sends the PART message
#	leaveChannel does the are you sure dialog
#
proc channel_doLeave {this msg} {
    set net [$this net]
    if {[$this isa Channel] && [$net active]} {
	set w [$this window]
	catch {$w.quit configure -state disabled -text [trans leaving]}
	$this configure -leaving 1
	catch {$w.cmd.entry configure -state disabled}
	$net PART [$this name] $msg
    } {
	$this delete
    }
}
#
proc channel_leave {this} {
    set chan [$this name]
    mkDialog LEAVE .@$this "Leaving $chan" "Really leave channel $chan?" \
      [list [list message {} {} palette]] [list ok "$this doLeave"]\
      [list cancel {}]
}
#
proc channel_doAddText {this tag txt} {
    upvar #0 $this cdata
    switch {} [set name [$this text]] return
    if {[catch {$name configure -state normal}]} {
	return
    }
    if {$cdata(stampMsg)} {
	set time [clock format [clock seconds] -format $cdata(timeformat)]
	$name insert end $time
    } {
	set time {}
    }
    insertText $this $name $txt $tag
    $name insert end "\n"
    if {$cdata(scrollback)} { $name delete 1.0 "end - $cdata(scrollback)lines" }
    $name configure -state disabled
    $this log $time$txt
    if {$cdata(jump)} {$name see end}
}
#
proc channel_keepAction {this value} {
    $this action $value
    [$this window].action.menu add command -label "[prune $value 15]" \
      -command "$this action [list $value]"
    [$this net] configure +actions $value +confChange Channels
}
#
proc channel_getAction {this} {
    mkEntryBox .@${this}action [trans action] "Enter your action:" \
      [list [list action {} {} palette]] \
      [list ok "$this action"] [list keep "$this keepAction"] [list cancel {}]
}
#
# Send string to channel as an ACTION and echo to channel window
#
proc channel_action {this str} {
    if {$str != {}} {
	[$this net] q1Send "PRIVMSG [$this name] :\001ACTION $str\001"
	$this addText @me "* [[$this net] nickname] $str"
	$this addHist $str
    }
}
#
# Currently this next proc doesnt work!! It's a tk problem *not* a
# zircon problem
#
proc channel_clear {this hist} {
    set t [$this text]
    if {$hist} {
	$t configure -state normal
	$t delete 1.0 end
	$t configure -state disabled
    } {
	if {![tk_dialog .@clr$this [trans clear] "Due to a change in the way that\
tk handles text windows it is no longer possible to make the clear function\
clear only the viewable area.
	
You can however clear the window AND ALL THE SCROLLBACK BUFFER.
	
Click \"Yes\" if you wish to do this.

Shift-Clicking the Clear button will always do this\
and you won't see this message..." info 0 \
	[trans Yes] [trans No]]} {
	    $t configure -state normal
	    $t delete 1.0 end
	    $t configure -state disabled
	}
    }
}
#
proc channel_insert {this txt} {
    if {[$this m] && ![$this operator] && ![$this speaker]} return
    if {$txt != {}} {
	set op send
	if {[$this actionmode]} { set op action }
	set ent [$this window].cmd.entry
	while {[regexp "(\[^\n\]*)\n(.*)" $txt d line txt]} {
	    switch -- $line {} continue
	    tkEntryInsert $ent $line
	    $this $op [$ent get]
	    $ent delete 0 end
	}
	if {$txt != {}} {
	    $ent insert insert $txt
	    tkEntrySeeInsert $ent
	}
    }
}
#
proc channel_insertSelect {this} {
    if {![catch {selection get} bf]} { $this insert $bf }
}
#
proc channel_markOp {this usr} {
    doMarking $this $usr Op operator normal 1
}
#
proc channel_unmarkOp {this usr} {
    set par [expr {[$this isSpeaker $usr] ? "speaker" : {}}]
    doMarking $this $usr Op $par disabled 0
}
#
proc doMarking {this usr knd op state val} {
    uplevel #0 set ${this}($knd,$usr) $val
    if {[[$this net] me $usr]} { $this opItems $state }
    set w [$this window]
    markButton [$this net] [$this ufrm].userBtn.$usr $op
    markEntry $w.users.menu [indexHack $w.users.menu [$usr name] 3] $op
    if {[$this m]} { $this showFlags mod Moderated }
}
#
proc channel_Vmarker {this usr v1 v2} {
    uplevel #0 set ${this}(Spk,$usr) $v2
    if {![$this isOp $usr]} {
	set w [$this window]
	markButton [$this net] [$this ufrm].userBtn.$usr $v1
	markEntry $w.users.menu [indexHack $w.users.menu [$usr name] 3] $v1
	$this cmdState
    }
    if {[$this m]} { $this showFlags mod Moderated }
}
#
proc channel_markV {this usr} { $this Vmarker $usr speaker 1 }
#
proc channel_unmarkV {this usr} { $this Vmarker $usr {} 0 }
#
proc channel_topicState {this state} {
    switch {} [set win [$this window]] return
    if {![winfo exists $win.topic]} return
    set ix [$win.topic.label.menu index end]
    $win.topic.label.menu entryconfigure 1 -state $state
    for {set i 3} {$i <= $ix} {incr i} {
        $win.topic.label.menu entryconfigure $i -state $state
    }
    $win.topic.entry configure -state $state -relief [expr {($state == "disabled") ? "sunken" : "raised"}]
}
#
proc channel_flag {this state} {
    if {![$this active]} return
    set win [$this window]
    foreach w {mode channel action} { catch {$win.$w conf -state $state} }
    $this topicState disabled
    if {[winfo exists [set uu [$this ufrm].userBtn]]} {
	foreach w [winfo children $uu] {$w conf -state $state}
    }
    if {[winfo exists $win.users.menu]} {
	set l [$win.users.menu index last]
	while {$l > 2} {
	    $win.users.menu entryconfigure $l -state $state
	    incr l -1
	}
    }
    switch normal $state {
	foreach usr [$this splitusers] {
	    catch {[set w [$this ufrm].userBtn.$usr] conf -state disabled}
	    if {[set x [indexHack $win.users.menu [$usr name] 3]] >=0} {
		$win.users.menu entryconfigure $x -state disabled
	    }
	}
    } default {
	if {[$this isa Channel]} {
	    foreach p [$this users] {
		$this unmarkOp $p
		$this unmarkV $p
	    }
	}
    }
}
#
proc channel_osTag {this usr nnk} {
    if {[[$this net] ircIIops]} {
	if {[$this isSpeaker $usr]} { set nnk +$nnk }
	if {[$this isOp $usr]} { set nnk @$nnk }
    }
    return $nnk
}
#
proc channel_nickChange {this usr nnk} {
    switch [set w [$this window]] {} return
    set uf [$this ufrm]
    set net [$this net]
    $this optText NICK "*** [$usr name] is now known as $nnk"
    if {[$net me $usr]} {
	catch {$uf.userBtn.$usr configure -text [$this osTag $usr $nnk]}
	catch {$w.users.menu entryconfigure 3 -label $nnk}
    } {
	if {[$this isJoined $usr]} {
	    catch {
		set win $uf.userBtn.$usr
		set fg [$win cget -foreground]
		set bg [$win cget -background]
		set ft [$win cget -font]
		destroy $win
		wsortIns $this $uf.userBtn $nnk $usr
		$uf.userBtn.$usr configure -fg $fg -bg $bg -font $ft
	    }
	    if {[set x [indexHack $w.users.menu [$usr name] 4]] >=0} {
		$w.users.menu delete $x
		sortIns $this $nnk $usr $w.users.menu
	    }
	}
    }
}
#
proc nop {args} { return 1}
#
proc cpriv {chan op spk menuw x} {
    return [cpriv1 $chan $op $spk [winfo name [$menuw entrycget $x -menu]]]
}
#
proc cpriv1 {chan op spk usr2} {
    return [cpriv2 $op $spk [$chan isOp $usr2] [$chan isSpeaker $usr2]]
}
#
proc cpriv2 {op spk op1 spk1} {
    if {$op} {
        if {$op1} { return 1}
	return 2
    } elseif {$spk} {
        if {$op1} { return 0}
	if {$spk1} { return 1 }
	return 2
    }
    if {$op1 || $spk1} { return 0 }
    return 1
}
#
proc sc {a b} { return [string compare $a $b]}
#
proc sc1 {a b} { return [string compare $a [string tolower $b]] }
#
proc sortIns {chan nk usr w} {
    set last [$w index last]
    makeUserMenu $chan $w.$usr $usr
    if {![$chan nosort]} {
    	set op [$chan isOp $usr]
	set spk [$chan isSpeaker $usr]
	if {[$chan nocase]} {
	    set cnk [string tolower $nk]
	    set fn sc1
	} {
	    set cnk $nk
	    set fn sc
	}
	if {[$chan nopriv]} {
	    set fn1 nop
	} {
	    set fn1 cpriv
	}
	for {set x 4} {$x <= $last} {incr x} {
	    switch [$fn1 $chan $op $spk $w $x] {
	      0 { }
	      1 {
	    	    if {[$fn $cnk [$w entrycget $x -label]] < 0} {
			$w insert $x cascade -label $nk -menu $w.$usr
			return $x
	    	    }
	        }
	      2 {
		    $w insert $x cascade -label $nk -menu $w.$usr
		    return $x
	       }
	    }
	}
    }
    $w add cascade -label $nk -menu $w.$usr
    return last
}
#
proc wsortIns {chan win un usr} {
    set wnk $win.$usr
    set fn name
    set fn1 cpriv1
    set txt [$chan osTag $usr $un]
    set srt [expr {![$chan nosort]}]
    if {[$chan nocase]} {
	set un [string tolower $un]
	set fn lname
    }
    if {[$chan nopriv]} {
	set fn1 nop
    }
    set op [$chan isOp $usr]
    set spk [$chan isSpeaker $usr]
    set top text
    set wd [$win cget -width]
    switch nil [set fr [$usr fobj]] {} default {
	switch {} [set fg [$fr images]] {} default {
	   set top image
	   set img [lindex $fg 0]
	   set wd {}
	   switch -glob -- $txt {
	   @* {
		    switch {} [set oi [lindex $fg 1]] {} default {set img $oi}
	       }
	   +* {
		    switch {} [set oi [lindex $fg 2]] {} default {set img $oi}
	       }
	   }
	   set txt $img
	}
    }
    menubutton $wnk -$top $txt -highlightthickness 0 -borderwidth 2 -pady 1 -padx 2
    switch {} $wd {} default { $wnk configure -width $wd }
    $wnk configure -menu [makeUserMenu $chan $wnk.menu $usr]
    if {$srt} {
	set y 1
	while {![catch {set x [$win window cget 1.$y -window]}]} {
	    switch {} $x {} default {
	    	set u2 [winfo name $x]
		switch [$fn1 $chan $op $spk $u2] {
		0 { }
		1 {
		    if {[string compare $un [$u2 $fn]] < 0} {
		        $win window create [$win index $x] -window $wnk -padx 0
		        return
		    }
		  }
		2 {
		    $win window create [$win index $x] -window $wnk -padx 0
		    return
		   }
		}
	    }
	    incr y
	}
    }
    $win window create end -window $wnk -padx 0
}
#
proc channel_addUser {this usr op sp} {
    upvar #0 $this cdata
    set marking {}
    lclTrace $this Spk,$usr
    if {[set cdata(Spk,$usr) $sp]} { set marking speaker }
    lclTrace $this Op,$usr
    if {[set cdata(Op,$usr) $op]} { set marking operator }
    set net $cdata(net)
    set w $cdata(window)
    set un [$usr name]
    set jn 1
    listincl cdata(users) $usr
    if {[winfo exists [set win [$this ufrm].userBtn]]} {
	set wnk $win.$usr
	if {![winfo exists $wnk]} {
	    wsortIns $this $win $un $usr
	} {
	    set jn 0
	}
	$wnk configure -state normal
	markButton $net $wnk $marking
    }
    if {[winfo exists [set w $w.users.menu]]} {
	if {[$net me $usr]} {
	    if {![winfo exists $w.$usr]} {
		$w insert 3 cascade -label $un -menu $w.$usr
		makeUserMenu $this $w.$usr $usr
	    }
	    set x 3
	} \
	elseif {[set x [indexHack $w $un 4]] < 0} {
	    set x [sortIns $this $un $usr $w]
	} {
	    $w entryconfigure $x -state normal
	}
	markEntry $w $x $marking
    }
    $this makeTag $usr
    if {$jn} { $usr join $this }
}
#
proc channel_ircOp {this state} {
    switch {} [set w [$this window]] {} default {
	catch {
	    foreach name [winfo children [$this ufrm].userBtn] {
		setState $name.menu ircop $state
	    }
	}
	catch {
	    foreach name [winfo children $w.users.menu] {
		setState $name ircop $state
	    }
	}
    }
}
#
proc channel_userMode {this usr mode} {
    upvar #0 $this cdata
    switch -- $mode o {set typ Op} v {set typ Spk}
    set val $cdata(lcl$typ,$usr)
    set cdata(lcl$typ,$usr) [expr {!$val}]
    [$this net] MODE [$this name] [expr {$val ? {+} : {-}}]$mode [$usr name]
}
#
proc channel_setOps {this prefix usr} {
    upvar #0 MkOp[$this net] MkOp
    foreach n [$this ops] {
	if {[regexp -nocase -- $n $prefix]} {
	    lappend MkOp($this) [$usr ref]
	    break
	}
    }
}
#
proc channel_mode {this vals prefix} {
    upvar #0 $this cdata
    set net $cdata(net)
    set nxt {}
    set flag {}
    foreach par $vals {
	switch {} $nxt {
	    set nxt {}
	    foreach m [split $par {}] {
		switch -- $m {
		- - + { set flag $m }
		l {
			switch + $flag {append nxt $flag$m} default {
			    set cdata(limit) {}
			    $this showInfo limit {}
			    handleOn $net MODE [list [$this name] -$m {} $prefix]
			}
		    }
		b - e - k - o - v { append nxt $flag$m }
		a {
			set w $cdata(window)
			if {[set cdata(a) [string match + $flag]]} {
			    $w.users configure -state disabled
			    set cdata(anonpos) [grid info [$this ufrm]]
			    catch {grid forget [$this ufrm]}
			} {
			    $w.users configure -state normal
			    if {![winfo ismapped [$this ufrm]]} {
				eval grid [$this ufrm] $cdata(anonpos)
			    }
			    $net q1Send "NAMES :[$this name]"
			}
			# set cdata(lcla) $cdata(a)
			handleOn $net MODE [list [$this name] ${flag}a {} $prefix]
		    }
		m {
			if {[set cdata(m) [string match + $flag]]} {
			   set mt {Moderated}
			} {
			   set mt {}
			} 
			# set cdata(lclm) $cdata(m)
			$this cmdState
			$this showFlags mod $mt
			handleOn $net MODE [list [$this name] ${flag}m {} $prefix]
		    }
		p - s - i - n {
			set cdata($m) [string match + $flag]
			# set cdata(lcl$m) $cdata($m)
			handleOn $net MODE [list [$this name] $flag$m {} $prefix]
		    }
		t   {
			set x [string match + $flag]
			set cdata(t) $x
			# set cdata(lclt) $x
			if {![$this operator]} {
			    set st [expr {$x ? {disabled} : {normal}}]
			    switch {} [set w $cdata(window)] {} default {
				$this topicState $st
			    }
			}
			handleOn $net MODE [list [$this name] ${flag}t {} $prefix]
		    }
		}
	    }
	} default {
	    set flag [string index $nxt 0]
	    switch -- [set m [string index $nxt 1]] {
	    o   {
		    switch + $flag {set op markOp} default {set op unmarkOp}
		    $this $op [set who [User :: make $cdata(net) $par]]
		    $who protect $this $prefix ${flag}o
		}
	    v   {
		    switch + $flag {set op markV} default {set op unmarkV}
		    $this $op [set who [User :: make $cdata(net) $par]]
		    $who protect $this $prefix ${flag}v
		}
	    k   {
		    switch + $flag {set cdata(key) $par} default {set cdata(key) {}}
		    $this showFlags key $cdata(key)
		}
	    l	{
		    set cdata(limit) $par
		    $this showInfo limit "Limit: $par"
		}
	    b   {
		    banProtect $this $prefix $flag $par
		}
	    e   {
	    	    # No idea what to do with this one
		    # if anything needs to be done that is.
	        }
	    }
	    set nxt [string range $nxt 2 end]
	    handleOn $net MODE [list [$this name] $flag$m $par $prefix]
	}
    }
    if {$cdata(modewait)} {
	set cdata(modewait) 0
	if {!$cdata(t)} { $this topicState normal }
    }
}
#
proc channel_opItems {this state} {
    set win [$this window]
    if {[winfo exists [set uf [$this ufrm].userBtn]]} {
	foreach name [winfo children $uf] {
	    setState $name.menu chanop $state
	}
    }
    if {[winfo exists $win.users.menu]} {
	foreach name [winfo children $win.users.menu] {
	    setState $name chanop $state
	}
    }
    if {[$this isa Channel] && [winfo exists [set mn $win.mode.menu]]} {
	set vl [$mn index [trans ban]]
	incr vl
	set last [$mn index last]
	while {$vl <= $last} {
	    $mn entryconfigure $vl -state $state
	    incr vl
	}
	upvar #0 $this cdata
	if {![string compare $state normal] || $cdata(t)} {
	    $this topicState $state
	}
	$this cmdState
    }
}
#
proc Channel_make {net name} {
    upvar #0 CTO$net CTO
    set ln [string tolower $name]
    if {[info exists CTO($ln)]} { return $CTO($ln) }
    return [$net eval "Channel [list $name]"]
}
#
proc Channel_find {name net} {
    upvar #0 CTO$net CTO
    set ln [string tolower $name]
    return [expr {[info exists CTO($ln)] ? $CTO($ln) : {nil}}]
}
#
# proc to determine if a message is wanted
#
proc channel_wantMessage {this msg} {
    set cm [$this msg]
    return [expr {[lsearch $cm "!$msg"] >= 0 || [lsearch $cm $msg] < 0}]
}
#
proc typeConv {value name class} {
    upvar #0 Configure.$class ConfData
    catch {switch $ConfData($name) {
    mseconds { return " -$name [list [expr {$value / 1000}]]" }
    }}
    return " -$name [list $value]"
}
#
proc valCmp {this op ln def dc} {
    global OType
    set v [$this $op]
    switch $this $def {
	global zDefs
	switch $this $dc {
	    if {![info exists zDefs] || [string compare $v $zDefs($op)]} {
		append ln [typeConv $v $op $OType($this)]
	    }
	} default {
	    switch -- $v [$dc $op] {} default {
		append ln [typeConv $v $op $OType($this)]
	    }
	}
	return $ln
    }
    switch -- $v [$def $op] {} default { append ln [typeConv $v $op $OType($this)] }
    return $ln
}
#
proc mncSave {desc this defA TVar} {
    global defaultNet Channel
    upvar #0 $TVar config
    array set cvals $Channel
    array set cvals $config
    set def [getDefault $defA [$this net]]
    set dc [getDefault $defA $defaultNet]
    set typ [$this type]
    set ln {}
    upvar #0 $this cdata
    foreach x [array names cvals] {
	switch {} $cdata($x) {} default {
	    set ln [valCmp $this $x $ln $def $dc]
	}
    }
    switch {} $ln {
        puts $desc "$typ [list [$this name]]"
    } default {
	puts $desc "$typ [list [$this name]] $ln"
    }
}
#
proc channel_save {this desc} {
    mncSave $desc $this defChan Channel
    foreach b [$this bindings] {
	puts $desc "zbind [$this name] [lindex $b 0] [list [lindex $b 1]]"
    }
}
#
proc defSave {desc arry net TVar} {
    global zDefs defaultNet Channel
    upvar #0 $TVar conf $arry defObj
    array set zDefs $Channel
    array set zDefs $conf
    set adf $defObj($defaultNet)
    set def $defObj($net)
    switch -- $def $adf {
	foreach x [array names zDefs] {
	    switch -- $zDefs($x) [$def $x] {} default {
	    	$def save $desc
		return
	    }
	}
    } default {
	foreach x [array names zDefs] {
	    switch -- [$adf $x] [$def $x] {} default {
	    	$def save $desc
		return
	    }
	}
    }
    unset zDefs
}
#
proc Channel_save {desc net} {
    global defChan
    defSave $desc defChan $net Channel
    foreach ch [$net channels] {
	switch -- $ch $defChan($net) continue
	if {![$ch sys] && [$ch keep]} {$ch save $desc}
    }
}
#
proc Channel_pack {net} { foreach ch [$net channels] { $ch pack new }}
#
proc channel_pack {this where} {
    if {![$this isa Channel]} return
    upvar #0 $where$this to $this from
    # FRINK : array to from
    array set to [array get from]
}
#
proc channel_unpack {this where} {
    upvar #0 $where$this new Configure.Channel config
    foreach prop [array names config] {
	$this configure -$prop $new($prop)
    }
    unset new
}
#
proc Channel_cleanup {net where} {
    foreach ch [$net channels] { safeUnset ${where}$ch }
}
#
proc indexHack {w nk start} {
#
# Do non-pattern based index matching....
#
    if {[winfo exists $w]} {
	set x [$w index end]
	set i $start
	while {$i <= $x} {
	    switch -- [$w entrycget $i -label] $nk - @$nk - +$nk - @+$nk {return $i}
	    incr i
	}
    }
    return -1

}
#
proc channel_showInfo {this lb txt} {
    upvar #0 $this cdata
    if {[winfo exists [set w $cdata(window).cinfo]]} {
	switch {} $txt {
	    grid forget $w.$lb
	} default {
	    if {![winfo ismapped $w.$lb]} {
		grid $w.$lb -row 0 -column [lindex [grid size $w] 0]
	    }
	    $w.$lb configure -text $txt -relief raised
	}
    }
}
#
proc channel_showFlags {this lb txt} {
    global zKeyGif zSpkGif zNSpkGif zLog
    upvar #0 $this cdata
    if {[winfo exists [set w $cdata(window).cflags]]} {
	switch {} $txt {
	    catch {grid forget $w.$lb}
	} default {
	    if {![winfo ismapped $w.$lb]} {
		grid $w.$lb -row 0 -column [lindex [grid size $w] 0]
	    }
	    switch $lb {
	    key {$w.key configure -image $zKeyGif}
	    log {$w.log configure -image $zLog}
	    mod {
		    if {![$this operator] && ![$this speaker]} {
			$w.mod configure -image $zNSpkGif
		    } {
			$w.mod configure -image $zSpkGif
		    }
		}
	    }
	}
    }
}
#
proc channel_search {this} {
    global Search zircon
    if {[winfo exists [set w .@srch$this]]} { popup $w ; return }
    makeToplevel $w "Search [$this name]" {} {}
    labelEntry 0 $w.search {-text [list [trans {search string}]]} {} "$this doSearch $w"
    $w.search.entry configure -textvariable Search($this,term)
    grid $w.search - -row 0 -sticky ew
    if {![info exists Search($this,case)]} { set Search($this,case) 0 }
    grid [checkbutton $w.case -text [trans {case insensitive}] -variable Search($this,case)] \
      - -row 1 -column 0 -sticky ew
    if {![info exists Search($this,ex)]} { set Search($this,ex) 1 }
    grid [radiobutton $w.exact -text [trans exact] -variable Search($this,ex) -value 1] \
      -row 2 -column 0 -sticky w
    grid [radiobutton $w.reg -text [trans regexp] -variable Search($this,ex) -value 0] \
      -row 2 -column 1 -sticky w
    if {![info exists Search($this,fwds)]} { set Search($this,fwds) 1 }
    grid [radiobutton $w.fwds -text [trans forwards] -variable Search($this,fwds) -value 1] \
      -row 3 -column 0 -sticky w
    grid [radiobutton $w.bwds -text [trans backwards] -variable Search($this,fwds) -value 0] \
      -row 3 -column 1 -sticky w
    grid [frame $w.line -bg $zircon(sepColor) -borderwidth 2] - -row 4 -sticky ew -pady 4
    grid [button $w.do -text [trans search] -command "$this doSearch $w"] \
      -row 5 -column 0 -sticky ew
    grid [button $w.ok -text [trans dismiss] -command "destroy $w"] \
      -row 5 -column 1 -sticky ew
}
#
proc channel_doSearch {this unused args} {
    global Search
    set cmd {}
    if {$Search($this,case)} { lappend cmd -nocase }
    if {!$Search($this,fwds)} { lappend cmd -backwards }
    if {!$Search($this,ex)} { lappend cmd -regexp }
    set res [eval [$this text] search $cmd -- \$Search($this,term) [[$this text] index current]]
    switch {} $res {bell} default {[$this text] see $res}
}
#
proc channel_highlight {this} {
    tkwait window [mkEntryBox {} {Text Highlight} \
      {Enter the text you wish highlighted:} \
      [list [list Text {}]] [list ok "$this doHi"] [list cancel {}]]
}
#
proc channel_doHi {this txt} {
    switch {} $txt return
    set t [$this text]
    $t tag delete @hilite@
    set res 1.0
    while {1} { switch {} [set res [$t search -- $txt $res]] break }
}
#
proc checkJoinTime {this timev} {
    if {$timev == {}} { return 1 }
    upvar #0 $this cdata
    if {[catch {set tt [clock scan $timev]}]} {
	tellError {} {JoinAt Error} "Bad time specification in -joinat\
for channel [$this name] - \"$timev\""
	return 0
    }
    set now [clock seconds]
    if {$now >= $tt} { incr tt [expr {24*60*60}] }
    if {$now >= $tt} {
	tellError {} {JoinAt Error} "Past time in -joinat for channel [$this name] - \"$timev\""
	return 0
    }
    after [expr {($tt - $now) * 1000}] "$this sendJoin"
    set cdata(joinat) $timev
    return 1
}
#
proc channel_doNickAction {this usr action} {
    regsub -all %n $action [$usr name] action
    regsub -all %c $action [$this name] action
    regsub -all %m $action [[[$this net] myid] name] action
    $this action $action
}
