# OpenVerse Base Functions
# 
# This file contains some basic functions for 
# OpenVerse
#
# Module Name		- Main Module
# Sourced By		- Main Module
#
# Copyright (C) 1999 David Gale <cruise@openverse.com>
# For more information visit http://www.openverse.com/
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
# USA.

# NOTES: 
# Now using curleybraces around functions for speed

# OpenVerse images, chat module
foreach name {"sing" "love" "steam" "idea" "smile" "frown" "wink" "jump" "send"
		"shiver" "push" "quit" "hug" "kiss" "connect_create" "aved"
		"connect_no" "connect_yes" "whois" "setup" "text_chat" "lock"
		"unlock" "log" "erase"} {
	image create photo "img::chat::$name" -file "$MV(icondir)/$name.gif"
}
image create bitmap img::chat::vhandle -file "$MV(icondir)/vhandle.xbm"\
		-maskfile "$MV(icondir)/vhandle.mask" -background gray90
image create bitmap img::chat::hhandle -file "$MV(icondir)/hhandle.xbm"\
		-maskfile "$MV(icondir)/hhandle.mask" -background gray90

# This proc calls every registered PanelEvent handler, using the same args.
# Syntax:
# PanelEvent <ev> [-emote {em}]
# Where ev is one of:
#  aved-open, aved-close, setup-open, setup-close, textchat-open,
#  textchat-close, connect, disconnect, connecting, lock-on, lock-off, log-on,
#  log-off, emote
# emote is one of:
#  none, wink, smile, frown, idea, love, sing, steam, hug, kiss
proc PanelEvent {args} {
	global MV
	foreach handler $MV(panel_event) {eval [list $handler] $args}
}

# Translator - This is an attempt to make a portable translator.
# It used text files to load it's phrases from. If there is no translation
# for the word or phrase, it will just display the english form. If there
# is no english form, it will improvise.
proc Trns {what} {
	global MV

	if ![info exists MV(words.$what)] {
		DebugIt "(Translator) No translation exists for $what" other
		return [string_totitle [string_map {"_" " "} $what]]
	} else {
		return $MV(words.$what)
	}
}

# Fill the names button with people who are in the room
proc DoNames {} {
	global MV

	destroy $MV(names_menu).m
	menu $MV(names_menu).m -tearoff $MV(tearoff_menus)

	$MV(names_menu).m add command -label [Trns whois_everyone]\
			-command "SendToServer \"WHOIS *\""

	# Process plugins
	foreach plugin $MV(plugin.traps.Names) {
		if ![$MV(plugin.traps.Names.$plugin)] return
	}
	$MV(names_menu).m add separator

	set people 0
	foreach person $MV(people) {
		set MV(whois.$people) $person
		$MV(names_menu).m add cascade -menu $MV(names_menu).m.$people \
				-label "$person"
		if !$MV(use_windowmanager_colors) {
			menu $MV(names_menu).m.$people -bg $MV(colors.name.bg)\
				-fg $MV(colors.name.fg)\
				-activebackground $MV(colors.name.abg)\
				-activeforeground $MV(colors.name.afg)\
				-tearoff $MV(tearoff_menus)
		} else {
			menu $MV(names_menu).m.$people\
					-tearoff $MV(tearoff_menus)
		}
		NameMenu "$MV(names_menu).m.$people" $people $person
		incr people
	}
	ColorNames
}

# Do a single name menu entry in $win for user with given index and nick
proc NameMenu {win idx nick} {
	global MV MVS

	# Do commands
	set types {"whois" "WhoisCMD" "send_message" "MessageCMD" "send_url"
			"URLCMD" "send_file" "SendFileFromMenu" "call"
			"CallUser" "request_personal_info" "RequestPersonal"}
	if $MVS(serving) {
		lappend types "announce_server"
		lappend types "AnnounceServerToUser"
	}
	foreach {msg cmd} $types {
		$win add command -label [Trns $msg] -command "$cmd $idx"
	}
	$win add cascade -label [Trns ignore] -menu $win.m

	# Make ignore/unignore menu
	if !$MV(use_windowmanager_colors) {
		menu $win.m -bg $MV(colors.name.bg)\
				-fg $MV(colors.name.fg)\
				-activebackground $MV(colors.name.abg)\
				-activeforeground $MV(colors.name.afg)\
				-tearoff $MV(tearoff_menus)
	} else {
		menu $win.m -tearoff $MV(tearoff_menus)
	}

	# Do ignore/unignore
	set types {"avatar" "effect" "move" "chat" "sub" "url"}
	foreach type $types {
		if [lcontains $MV(ignore.$type) $nick] {
			set un "un"
		} else {
			set un ""
		}
		$win.m add command -label [Trns "${un}ignore_$type"]\
				-command "IgnoreUser $idx $type -${un}ignore"
	}

	# Do ignore and unignore all
	set ignore 0
	set unignore 0
	foreach type $types {
		if [lcontains $MV(ignore.$type) $nick] {incr ignore}
		if ![lcontains $MV(ignore.$type) $nick] {incr unignore}
	}
	foreach which {"ignore" "unignore"} {
		if {[set $which] < [llength $types]} {
			$win.m add command -label [Trns "${which}_all"]\
					-command "IgnoreUser $idx all -$which"
		}
	}

	# Process plugins
	foreach plugin $MV(plugin.traps.DoNames) {
		if ![$MV(plugin.traps.DoNames.$plugin) $win $nick $idx] {
			return
		}
	}
}

proc StartLog {} {
	global MV

	set MV(log) 1
	set MV(logfile) [open "$MV(homedir)/LogFile.txt" "a+"]
	puts $MV(logfile) "- Log Started - [clock format [clock seconds]]"
	InsertIntoChat 0 "\0030,04 - Log Started - \0030,12 [clock format [clock seconds]] \00312 ($MV(homedir)/LogFile.txt)\n"
}

proc LogThis {what} {
	global MV
	puts -nonewline $MV(logfile) "$what"
}

proc StopLog {} {
	global MV

	set MV(log) 0
	puts $MV(logfile) "- Log Stopped - [clock format [clock seconds]]"
	InsertIntoChat 0 "\0030,04 - Log Stopped - \0030,12 [clock format [clock seconds]] \00312 ($MV(homedir)/LogFile.txt)\n"
	close $MV(logfile)
	set MV(logfile) "-1"
}

proc NickComplete {} {
	global MV

	DebugIt "-- In NickComplete" other
        set stuff [$MV(chat_entry) get]
	if {$stuff == ""} return
	set wrds [split $stuff " "]
	set lastwrd [string tolower [lindex $wrds [expr [llength $wrds] -1]]]
	set len [expr [string length $lastwrd] - 1]
	foreach person $MV(people) {
		if [string_equal -length $len $person $lastwrd] {
			$MV(chat_entry) insert end [string range $person [expr $len + 1] end]
			return
		}
	}
}

proc SetRoom {what size} {
	global MV

	if {[file exists "$MV(roomdir)/$what"]} {
		if {[file size "$MV(roomdir)/$what"] != $size} {
			DebugIt "size mismatch for $what" other
			catch {file delete "$MV(roomdir)/$what"}
			if {[lsearch -exact $MV(roomloads) $what] == -1} {
				image create photo OpenVerse_Image_room -file "$MV(homedir)/icons/loading.gif"
				SendToServer "DCCSENDROOM $what"
			}
		} else {
			image create photo OpenVerse_Image_room -file "$MV(roomdir)/$what"
		}
	} else {
		if {[lsearch -exact $MV(roomloads) $what] == -1} {
			image create photo OpenVerse_Image_room -file "$MV(homedir)/icons/loading.gif"
			SendToServer "DCCSENDROOM $what"
		}
	}
}


proc WhoisCMD {person} {
	global MV

	SendToServer "WHOIS $MV(whois.$person)"
}

proc WHOISUser {who hostmask} {
	global MV
	if {[lsearch -exact $MV(people) $who] == -1} {return}
	ProcChat $who "\00303[Trns host_info] \00312$hostmask" 0 3 $MV(colors.whois.baloon)
}

proc GetChatRows {text} {
	global MV
	set words [split $text]
	set word_ptr 0
	set last_word [llength $words]
	if {$last_word == 1} {return 1}
	set rows 1
	set line ""
	set last_word_ptr -1
	while {$word_ptr <= $last_word} {
		update idletasks
		append line [lindex $words $word_ptr]
		set width [font measure "$MV(font.balloon.$MV(font.balloon).$MV(font.balloon.style)) -size $MV(font.balloon.size)" $line]
		if {$width >= 240} {
			incr rows
			set incremented 1
			set line ""
			if {$word_ptr == $last_word_ptr} {
				incr word_ptr
			} else {
				set last_word_ptr $word_ptr
			}
		} else {
			set incremented 0
			incr word_ptr
		}
	}
	if $incremented {
		return $rows
	} else {
		return [expr $rows + 1]
	}
}

#
# StripColorCodes "Text To Be Stripped"
#
# Takes out all the color codes from some text
# and returns a cleaner string.
#
proc StripColorCodes {text} {
	while {[set tb [string first \x03 $text]] != -1} {
		set string [string range $text 0 [expr $tb -1]]
		set text [string range $text [expr $tb +1] end]
		if {[regexp {^([0-9][0-9]?)(,([0-9][0-9]?))?(.*)} $text m fg bl bg text]} {
			# hehe stripped!
			set color 1
		}
		set text "$string$text"
	}
	return $text
}

proc ProcChat {who msg que type balloon args} {
	global MV

	if {$who == $MV(nick)} {
		set MV($MV(nick).x) $MV(x)
		set MV($MV(nick).y) $MV(y)
		set MV($MV(nick).baloon_x) $MV(anim.baloon_x)
		set MV($MV(nick).baloon_y) $MV(anim.baloon_y)
	} elseif ![lcontains $MV(people) $who] {
		return
	}

	set log 1
	set icon ""
	switch -- $type {
	0 {	;# Normal chat
		set textcolor $MV(colors.chat.text)
		set chat "<$who> $msg\n"
	} 1 {	;# System message
		set textcolor $MV(colors.system.text)
		set log 0
		set chat "\0030,4[Trns system]\00399,99 \00304$msg\n"
	} 2 {	;# Whois message
		set textcolor $MV(colors.privmsg.text)
		set chat "<!>$who<!> \00310$msg\n"
	} 3 {	;# Private message
		set textcolor $MV(colors.whois.text)
		set chat "<?>$who<?> $msg\n"
	} 4 {	;# Singing
		set textcolor $MV(colors.sing.text)
		set chat "<§>$who<§> $msg\n"
		set icon "img::chat::sing"
	} 5 {	;# Love
		set textcolor $MV(colors.love.text)
		set chat "<¤>$who<¤> $msg\n"
		set icon "img::chat::love"
	} 6 {	;# Anger
		set textcolor $MV(colors.steam.text)
		set chat "<¥>$who<¥> $msg\n"
		set icon "img::chat::steam"
	} 7 {	;# Idea
		set textcolor $MV(colors.idea.text)
		set chat "<¿>$who<¿> $msg\n"
		set icon "img::chat::idea"
	} 8 {	;# Smile
		set textcolor $MV(colors.smile.text)
		set chat "<:)>$who<(:> $msg\n"
		set icon "img::chat::smile"
	} 9 {	;# Frown
		set textcolor $MV(colors.frown.text)
		set chat "<:(>$who<):> $msg\n"
		set icon "img::chat::frown"
	} 10 {	;# Wink
		set textcolor $MV(colors.wink.text)
		set chat "<;)>$who<(;> $msg\n"
		set icon "img::chat::wink"
	} 11 {	;# Hug
		set textcolor $MV(colors.hug.text)
		set chat "<*hug*>$who<*hug*> $msg\n"
		set icon "img::chat::hug"
	} 12 {	;# Kiss
		set textcolor $MV(colors.kiss.text)
		set chat "<*kiss*>$who<*kiss*> $msg\n"
		set icon "img::chat::kiss"
	} default {
		set textcolor "black"
		set chat "<$who> $msg\n"
	}}

	# Insert into text chat
	if {!$MV(chatsync) && ![llength $args]} {
		InsertIntoChat $log $chat
		if $MV(bell) bell
	}

	# Queue if necessary
	# TODO: add to existing balloon
	if {$MV($who.haschat) && !$que} {
		lappend MV($who.chatque) [list $type $balloon $msg]
		return
	}
	set MV($who.chattime) [clock seconds]
	set MV($who.lastsaid) $msg

	# Process plugins
        foreach plugin $MV(plugin.traps.ProcChat.pre) {
		if ![$MV(plugin.traps.ProcChat.pre.$plugin) $who $msg $type\
				$balloon] return
	}
	
	# Make balloon
	set MV($who.haschat) 1
	set fnt $MV(font.balloon.$MV(font.balloon).$MV(font.balloon.style))
	append fnt " -size $MV(font.balloon.size)"
	set MV($who.balloon) [tcl_escape "balloon-[unique_id]"]
	balloon::create [expr $MV($who.x) + $MV($who.baloon_x) - 20]\
			[expr $MV($who.y) + $MV($who.baloon_y)] .top.c\
			"$MV($who.balloon)" -text [StripColorCodes $msg]\
			-fill $balloon -outline "black" -foreground $textcolor\
			-shadow "black" -opacity 0.3 -icon $icon -font $fnt
	after [expr $MV(staytime) * 1000] "KillBalloon [list $who]"

	# Insert into text chat
	if $MV(chatsync) {
		InsertIntoChat $log $chat
		if $MV(bell) bell
	}

	# Process plugins
        foreach plugin $MV(plugin.traps.ProcChat.post) {
                if ![$MV(plugin.traps.ProcChat.post.$plugin) $who $msg $type\
				$balloon] return
        }
	update idletasks
}

proc KillBalloon {who} {
	global MV

	if ![info exists MV($who.balloon)] return
	if ![llength [.top.c find withtag $MV($who.balloon)]] return
	.top.c delete $MV($who.balloon)
	unset MV($who.balloon)

	if [llength $MV($who.chatque)] {
		set q [lindex $MV($who.chatque) 0]
		ProcChat $who [lindex $q 2] 1 [lindex $q 0] [lindex $q 1] -queue
		set MV($who.chatque) [lreplace $MV($who.chatque) 0 0]
	} else {
		set MV($who.haschat) 0
	}
}

proc NewPerson {who x y image xx yy size bx by} {
	global MV

	if {$who == $MV(nick)} return
	if {[lsearch -exact $MV(people) $who] != -1} {
		DebugIt "New User $who is already logged on" other
		return
	}
	if {[TestPosNum $x] || [TestPosNum $y] || [TestNum $xx] || \
	[TestNum $yy] || [TestPosNum $size] || [TestNum $bx] || \
	[TestNum $by]} {
		DebugIt "New User $who has invalid numerics (ignoring)" other
		return
	}
	lappend MV(people) $who
	set MV($who.name) $who
	set MV($who.downloading) 0
	set MV($who.moving) 0
	set MV($who.avatar) $image
	set MV($who.x) $x
	set MV($who.y) $y
	set MV($who.lastsaid) ""
	set MV($who.name_x_offset) $xx
	set MV($who.name_y_offset) $yy
	set MV($who.baloon_x) $bx
	set MV($who.baloon_y) $by
	set MV($who.haschat) 0
	set MV($who.nomoremove) 0
	set MV($who.chatque) {}
	set MV($who.moves) {}

	# Process plugins
        foreach plugin $MV(plugin.traps.NewPerson) {
                if ![$MV(plugin.traps.NewPerson.$plugin) $who $x $y $image $xx $yy $size $bx $by] {return}
        }
	
	# Dark green text by djaman
	InsertIntoChat 1 "<*>$who<*> \00303[Trns entered_the_room]\n"
	if {$image == "default.gif"} {
		image create photo OpenVerse_User_Image_$who -file "$MV(images)/default.gif"
		set MV($who.icon) [.top.c create image $x $y -image OpenVerse_User_Image_$who]
		if $MV(names) {ShowName $who}
		DoNames
		return
	}
	if ![file exists "$MV(rem_images)/$image"] {
		image create photo OpenVerse_User_Image_$who -file "$MV(images)/default.gif"
		set MV($who.icon) [.top.c create image $x $y -image OpenVerse_User_Image_$who]
		if {[lsearch -exact $MV(downloads) $image] == -1} {
			DebugIt "I do not have $image" other
			if $MV(download_avatars) {
				SendToServer "DCCSENDAV $image"
				set MV($who.downloading) 1
			}
		}
	} else {
		if {[file size "$MV(rem_images)/$image"] != $size} {
			image create photo OpenVerse_User_Image_$who -file "$MV(images)/default.gif"
			set MV($who.icon) [.top.c create image $x $y -image OpenVerse_User_Image_$who]
			if {[lsearch -exact $MV(downloads) $image] == -1} {
				DebugIt "size mismatch for $image" other
				if $MV(download_avatars) {
					SendToServer "DCCSENDAV $image"
					set MV($who.downloading) 1
				}
			}
		} else {
			if [catch {image create photo OpenVerse_User_Image_$who -file "$MV(rem_images)/$image"}] {
				image create photo OpenVerse_User_Image_$who -file "$MV(images)/default.gif"
			}
			set MV($who.icon) [.top.c create image $x $y -image OpenVerse_User_Image_$who]
		}
	}
	if $MV(names) {ShowName $who}
	DoNames
}

proc PersonLeft {who} {
	global MV

	if {[lsearch -exact $MV(people) $who] == -1} return

	set MV($who.chatque) ""
	KillBalloon $who

	set MV(people) [ldifference $MV(people) $who]

	# Process plugins
        foreach plugin $MV(plugin.traps.PersonLeft) {
                if ![$MV(plugin.traps.PersonLeft.$plugin) $who] return
        }

	.top.c delete $MV($who.icon)
	InsertIntoChat 1 "<*>$who<*> \00303[Trns left_the_room]\n"
	if $MV(names) {KillName $who}

	# Clean up the memory this user was using!
	image delete OpenVerse_User_Image_$who
	array_unset MV "$who.*"
	DoNames
}

proc SanityCheck {what} {
        if {[string first "../" $what] != -1} {return 0}
        if {[string first "//" $what] != -1} {return 0}
        if {[string first "~/" $what] != -1} {return 0}
        if [string_equal -length 1 $what "/"] {return 0}
	return 1
}

#
# checks the length of stuff typed into the chat windows.
#
proc CheckLen {win} {
        if {[string length [$win get]] > 256} {
                $win delete 256 end
                bell
        }
}


# Checks to be sure a nick is within preset limits.
# The maximum nick length is currently 12 characters.
proc CheckThatNick {nick} {
        global MV
        
	set nick [string range [string_map {" " ""} $nick] 0 11]
        if [lcontains $MV(people) $nick] {
                tk_dialog .nickInUse [Trns openverse_error]\
				[Trns nick_in_use] "" 0 [Trns ok]
                return ""
        }
        return $nick
}

#
proc ShowPrev {win} {
        global MV

        if ![llength $MV(prev)] {return}
        set top [expr [llength $MV(prev)] -1]
        if {$MV(curprev) <= 0} {
                set MV(curprev) $top
        } else {
                incr MV(curprev) -1
        }
        $win delete 0 end
        $win insert end [lindex $MV(prev) $MV(curprev)]
}

proc ShowNext {win} {
        global MV

        if ![llength $MV(prev)] {return}
        set top [expr [llength $MV(prev)] -1]
        if {$MV(curprev) >= $top} {
                set MV(curprev) 0
        } else {
                incr MV(curprev)
        }
        $win delete 0 end
        $win insert end [lindex $MV(prev) $MV(curprev)]
}

proc CheckNick {} {
        global MV
        
        set max 0
        set newnck {}
        set nck [split [.setup.na.l.t.nick.entry get] ""]
        foreach letter $nck {
                incr max
                if {$max <= 12} {
                        switch -- $letter {
                                " " {
                                        bell
                                        # No spaces allowed :)
                                }
                                default {
                                        lappend newnck $letter
                                }
                        }
                } else {
                        bell
                }
        }
        .setup.na.l.t.nick.entry delete 0 end
        .setup.na.l.t.nick.entry insert end [join $newnck ""]
}

proc MessageCMD {which} {
	global MV

	if {[winfo exists .msg]} {destroy .msg}
	toplevel .msg
	wm title .msg "[Trns messaging] $MV(whois.$which)"
	button .msg.send -text [Trns send] -command "SendPrivMsg $which"
	entry .msg.entry -width 60
	pack .msg.send .msg.entry -side left -fill both -expand y
	bind .msg <Return> "SendPrivMsg $which"
}

proc SendPrivMsg {which} {
	global MV

	SendToServer "PRIVMSG $MV(whois.$which) [.msg.entry get]"
	ProcChat $MV(nick) "[.msg.entry get]" 0 2 $MV(colors.privmsg.baloon)
	destroy .msg
}

proc URLCMD {which} {
	global MV

	if {[winfo exists .url]} {destroy .url}
	toplevel .url
	wm title .url "[Trns send_url] $MV(whois.$which)"

	button .url.send -text [Trns send] -command "SendPrivURL $which"
	entry .url.entry -width 60
	pack .url.send .url.entry -side left -fill both -expand y
	bind .url <Return> "SendPrivURL $which"
}

proc SendPrivURL {which} {
	global MV

	SendToServer "URL $MV(whois.$which) [.url.entry get]"
	ProcURL $MV(nick) "[.url.entry get]"
	destroy .url
}

#
# Test a variable to see if it is a number. It will return 0 if it passes
# the test, it will return 1 if it fails the test. Negative numbers are
# considered to be valid numbers in this test.
#
proc TestNum {number} {
        if [string length [string trim $number -0123456789]] {
                return 1
        } else {
                return 0
        }
}

#
# Test a variable to see if it is a number. It will return 0 if it passes
# the test, it will return 1 if it fails the test. Negative numbers are
# considered invalid numbers in this test.
#
proc TestPosNum {number} {
        if [string length [string trim $number 0123456789]] {
                return 1
        } else {
                return 0                  
        }
}

#
# Change the user's nick and provide a place for plugin's to link to.
#
proc ChangeNick {newnick} {
	global MV

	set MV($MV(nick).moves) {}
	set MV($MV(nick).moving) 0

	set MV(nick) "$newnick"

	set MV($MV(nick).moving) 0
	set MV($MV(nick).moves) {}

        #
        # Process Plugins!
        #
        foreach plugin $MV(plugin.traps.ChangeNick) {
                if ![$MV(plugin.traps.ChangeNick.$plugin) $newnick] {return}
        }
}

proc ColorNames {} {
	global MV

	if $MV(use_windowmanager_colors) {return}

	$MV(names_menu).m configure -bg $MV(colors.name.bg) \
		-fg $MV(colors.name.fg) -activebackground $MV(colors.name.abg) \
		-activeforeground $MV(colors.name.afg)

}

# Ignore or unignore user with index $who; use -ignore or -unignore for $ignore
proc IgnoreUser {who how args} {
	global MV
	
	set who $MV(whois.$who)
	if ![llength $args] {
		set ignore 1
	} else {
		switch -- $args {
		"-ignore" {set ignore 1}
		"-unignore" {set ignore 0}
		}
	}
	if $ignore {
		set prot "IGNORE"
		DebugIt "Ignoring $who ($how)" other
	} else {
		set prot "UNIGNORE"
		DebugIt "UnIgnoring $who ($how)" other
	}

	set all [string_equal -nocase $how "all"]
	foreach type {"avatar" "effect" "move" "chat" "sub" "url"} {
		if {!($all || [string_equal -nocase $type $how])} continue
		if {($ignore ^ [lcontains $MV(ignore.$type) $who]) || $all} {
			if $ignore {
				lappend MV(ignore.$type) $who
			} else {
				set MV(ignore.$type) [ldifference\
						$MV(ignore.$type) $who]
			}
			set type [string toupper $type]
			SendToServer "$prot $type $who"
		}
		if !$all break
	}
	if $all {
		if $ignore {
			lappend MV(ignore.all) $who
		} else {
			set MV(ignore.all) [ldifference $MV(ignore.all) $who]
		}
	}
	DoNames
}

proc RightClick {x y xx yy} {
	global MV

	# Process Plugins!
	#
	foreach plugin $MV(plugin.traps.RightClick) {
		if ![$MV(plugin.traps.RightClick.$plugin) $x $y] {return}
	}
	if $MV(names) {
		set people 0
		foreach who $MV(people) {
			if {$x >= $MV($who.name_tl_x) &&
				$x <= $MV($who.name_br_x) &&
				$y >= $MV($who.name_tl_y) &&
				$y <= $MV($who.name_br_y)} {
					if {$x < 320 && $y < 240} {set where nw;set dir left}
					if {$x < 320 && $y > 240} {set where sw;set dir left}
					if {$x < 320 && $y == 240} {set where w;set dir left}
					if {$x > 320 && $y == 240} {set where e;set dir right}
					if {$x > 320 && $y < 240} {set where ne;set dir right}
					if {$x > 320 && $y > 240} {set where se;set dir right}
					if {$x == 320 && $y > 240} {set where s;set dir left}
					if {$x == 320 && $y < 240} {set where n;set dir left}
					if {$x == 320 && $y == 240} {set where center;set dir left}
					if {[winfo exists .top.c.name]} {destroy .top.c.name}
					if $MV(use_windowmanager_colors) {
						menu .top.c.name\
						-tearoff $MV(tearoff_menus)
					} else {
						menu .top.c.name \
							-bg $MV(colors.buttons.names.bg) \
							-fg $MV(colors.buttons.names.fg) \
							-activebackground $MV(colors.buttons.names.abg) \
							-activeforeground $MV(colors.buttons.names.afg)\
						-tearoff $MV(tearoff_menus)
					}
					NameMenu .top.c.name $people $who
					tk_popup .top.c.name $xx $yy
					return
			}
			incr people
		}
	}

}

proc SendFileFromMenu {person} {
	global MV

	set file [tk_getOpenFile]
	if {$file != ""} {DCCSend $MV(whois.$person) "$file"}
}

proc CallUser {person} {
	global MV

	SendToServer "SUB $MV(whois.$person) CALL [Trns call_msg]"
}


#
# CRUISE - 11/03/2001 - Fixed a bug here. It will  add this uer to the list
# of people we requested info from. This prevents them sending
# it to us without us wanting it.
#
proc RequestPersonal {person} {
	global MV

	#
	# We only allow one copy here.
	#
	if {[lsearch -exact $MV(personals.wanted) $MV(whois.$person)] == -1} {
		lappend MV(personals.wanted) $MV(whois.$person)
	}
	SendToServer "SUB $MV(whois.$person) GIVEPERSONAL"
}


#
# CRUISE - 10/24/2001 - Madede this work on the honor call
#
proc ProcCall {who} {
	global MV

	if $MV(honor_call) {
		InsertIntoChat 0 "\00300,04[Trns system]\00399,99 \00312$who \00304[Trns call_notice]"
		ProcChat $who [Trns call_msg] 0 2 $MV(colors.privmsg.baloon)
		update idletasks
		for {set d 0} {$d < 75} {incr d} {
			bell
			after 2
		}
	}
}

#
# This function will "encrypt" text using the self inversing rot13
# encryption technique. While not secure, it is still useful.
#
proc Rot13 {what} {
	set rot_what ""
	foreach letter [split $what ""] {
		switch -regexp -- $letter {
			[a-m] {set dir "+"}
			[A-M] {set dir "+"}
			[n-z] {set dir "-"}
			[N-Z] {set dir "-"}
			default {set dir 0}
		}
		if {$dir != 0} {
			binary scan $letter c* int_let
			append rot_what [format "%c" [expr $int_let $dir 13]]
		} else {append rot_what $letter}
	}
	return $rot_what
}

proc GivePersonal {who} {
	global MV
	switch -exact -- $MV(personal.availability) {
		Never {set go 0}
		Ask {
			incr MV(popup_window)
			set go [tk_dialog .personal_ask_$MV(popup_window) \
				"[Trns send_personal_info_to] $who?" \
				"$who [Trns personal_info_question]" \
				"" 0 [Trns cancel] [Trns send]]
		}
		Always {set go 1}
		default {set go 0}
	}
	if $go {
		SendToServer "SUB $who PERSONAL 1 $MV(personal.realname)"
		SendToServer "SUB $who PERSONAL 2 $MV(personal.email)"
		SendToServer "SUB $who PERSONAL 3 $MV(personal.homepage)"
		SendToServer "SUB $who PERSONAL 4 $MV(personal.sex)"
		SendToServer "SUB $who PERSONAL 5 $MV(personal.image)"
		SendToServer "SUB $who PERSONAL 6 [clock format [clock seconds] -format \"%T\"]"
		SendToServer "SUB $who PERSONAL 7 [clock format [clock seconds] -format \"%D\"]"
		set c 99
		foreach chunk [split $MV(personal.info) "\n"] {
			SendToServer "SUB $who PERSONAL $c $chunk"
			incr c
		}
		SendToServer "SUB $who PERSONAL 999 END OF INFO"
		InsertIntoChat 1 "\0030,4[Trns system]\00399,99 \00312$who\00399,99 \00304[Trns personal_info_sent]\n"
	}
}


#
# CRUISE 10/24/2001 - Added a scrollbar for sketch.
# CRUISE 11/03/2001 - will only build windows for people we requested
# information from.
#
proc BuildPersonalInfo {who} {
	global MV


	if {[lsearch -exact $MV(personals.wanted) $MV($who.name)] == -1} {
		DebugIt "Unwanted personals information!" other
		return
	}
	set idx [lsearch -exact $MV(personals.wanted) $MV($who.name)]
	set MV(personals.wanted) [lreplace $MV(personals.wanted) $idx $idx]


	incr MV(popup_window)
	set win ".pu$MV(popup_window)"
	toplevel $win
	wm title $win "[Trns personal_info_for] $who"
	
	image create photo PERSONAL_IMAGE -file [file nativename "$MV(personal.image)"]

	frame $win.info -relief sunken -borderwidth 2
	label $win.info.txt -relief raised -text "[Trns personal_info_for] $who" \
		-borderwidth 2
	frame $win.info.meat
	frame $win.info.meat.img
	label $win.info.meat.img.img -image PERSONAL_IMAGE \
		-relief raised -borderwidth 2
	
	frame $win.info.meat.inf
	frame $win.info.meat.inf.a
	frame $win.info.meat.inf.b
	frame $win.info.meat.inf.c
	frame $win.info.meat.inf.d
	frame $win.info.meat.inf.e
	label $win.info.meat.inf.a.l -text [Trns real_name] -width 15
	label $win.info.meat.inf.b.l -text [Trns email_address] -width 15
	label $win.info.meat.inf.c.l -text [Trns home_page] -width 15
	label $win.info.meat.inf.d.l -text [Trns sex] -width 15
	label $win.info.meat.inf.e.l -text [Trns local_time]
	label $win.info.meat.inf.e.ll -text [Trns date]

	entry $win.info.meat.inf.a.e -textvariable MV($who.personal.name)
	entry $win.info.meat.inf.b.e -textvariable MV($who.personal.email)
	entry $win.info.meat.inf.c.e -textvariable MV($who.personal.webpage)
	entry $win.info.meat.inf.e.e -textvariable MV($who.personal.time)
	entry $win.info.meat.inf.e.ee -textvariable MV($who.personal.date)

	radiobutton $win.info.meat.inf.d.oa -text [Trns male] \
		-variable MV($who.personal.sex) -value [Trns male]
	radiobutton $win.info.meat.inf.d.ob -text [Trns female] \
		-variable MV($who.personal.sex) -value [Trns female]
	radiobutton $win.info.meat.inf.d.oc -text [Trns other] \
		-variable MV($who.personal.sex) -value [Trns other]
	DebugIt "(PersonalSex) - |$MV($who.personal.sex)|" other

	frame $win.info.meat.txt
	text $win.info.meat.txt.t -height 10 -width 65 -wrap word -yscrollcommand "$win.info.meat.txt.scrolly set"
	scrollbar $win.info.meat.txt.scrolly -command "$win.info.meat.txt.t yview"


	pack $win.info -side left -fill both -expand y
	pack $win.info.txt -side top -fill x -expand y
	pack $win.info.meat -side bottom -fill both -expand y
	pack $win.info.meat.txt -fill both -expand y -side bottom
	pack $win.info.meat.img -side left
	pack $win.info.meat.inf -side right -fill both -expand y
	pack $win.info.meat.img.img -side top
	pack $win.info.meat.inf.a -fill x -expand y
	pack $win.info.meat.inf.b -fill x -expand y
	pack $win.info.meat.inf.c -fill x -expand y
	pack $win.info.meat.inf.d -fill x -expand y
	pack $win.info.meat.inf.e -fill x -expand y
	pack $win.info.meat.inf.a.l -side left
	pack $win.info.meat.inf.a.e -side left -fill x -expand y
	pack $win.info.meat.inf.b.l -side left
	pack $win.info.meat.inf.b.e -side left -fill x -expand y
	pack $win.info.meat.inf.c.l -side left
	pack $win.info.meat.inf.c.e -side left -fill x -expand y
	pack $win.info.meat.inf.d.l -side left
	pack $win.info.meat.inf.d.oa $win.info.meat.inf.d.ob \
		$win.info.meat.inf.d.oc -side left -fill x -expand y
	pack $win.info.meat.inf.e.l -side left
	pack $win.info.meat.inf.e.e -side left -fill x -expand y
	pack $win.info.meat.inf.e.ll -side left
	pack $win.info.meat.inf.e.ee -side left -fill x -expand y
	pack $win.info.meat.txt.t -side left -fill both -expand y
	pack $win.info.meat.txt.scrolly -side left -fill y
	$win.info.meat.txt.t insert end $MV($who.personal.text)	
}


#
# ThrowError
#
# Usage: ThrowError "stuff"
#
# Will pop up a dialog window with an error in it giving the user only ONE
# button.... "OK" as an option, which will close the window.
#
proc ThrowError {stuff} {
	global MV
	incr MV(popup_window)
	tk_dialog .openverse_error_$MV(popup_window) \
		[Trns openverse_error] \
		"$stuff" "" 0 [Trns ok]
}

# List procs
foreach {name var} {"Avatars" "anims" "Plugins" "plugindir" "Images" "images"
		"Rimages" "rem_images" "Panels" "paneldir"
		"Languages" "languagedir"} {
	proc List_$name {} "
		global MV
		return \[ldifference \[lsort \[glob -nocomplain\
				\"\$MV($var)/*\"\]\] \[file nativename\
				\"\$MV($var)/CVS\"\]\]
	"
}

