# main-window.tcl -- 
#
#	The main window.
#

namespace eval NSMainWindow {

variable Priv
variable Progress

variable tracking 0
variable trackId 0
variable trackStepping 0
variable trackX
variable trackY

# NSMainWindow::InitModule --
#
#	One-time-only-ever initialization.
#
# Arguments:
#
# Results:
#	What happened.

proc InitModule {} {

	global Angband
	global Display
	global PYPX

	set PYPX "0 0"

	InitImageIfNeeded Image_ButtonOptions button-options.gif
	InitImageIfNeeded Image_ButtonHelp button-help.gif

	# Keep track of active window (inventory, book, etc)
	set Display(window) none

	# Create the main window
	NSObject::New NSMainWindow
}

# NSMainWindow::NSMainWindow --
#
#	Object constructor called by NSObject::New().
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc NSMainWindow oop {

	global Windows

	InitWindow $oop

	# use_sound
	angband setting use_sound [Value use_sound]
	Info $oop useSound [Value use_sound]

	# Set ANGBAND_DIR_XTRA_SOUND
	set path [eval file join [Value sound,directory]]
	if {[file exists $path] && [file isdirectory $path]} {
		angband game directory ANGBAND_DIR_XTRA_SOUND $path
	}
	
	# ambient_delay
	NSValueManager::AddClient ambient_delay {
		angband sound setting ambient_delay [Value ambient_delay]
	}
	angband sound setting ambient_delay [Value ambient_delay]

	# monster_delay
	NSValueManager::AddClient monster_delay {
		angband sound setting monster_delay [Value monster_delay]
	}
	angband sound setting monster_delay [Value monster_delay]

	#
	# Global list of application windows
	#

	set Windows(main,win) [Info $oop win]
	set Windows(main,class) NSMainWindow
	set Windows(main,oop) $oop
}

# NSMainWindow::Info --
#
#	Query and modify info.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Info {oop info args} {

	global NSMainWindow

	# Set info
	if {[llength $args]} {
		switch -- $info {
			default {
				set NSMainWindow($oop,$info) [lindex $args 0]
			}
		}

	# Get info
	} else {
		switch -- $info {
			default {
				return $NSMainWindow($oop,$info)
			}
		}
	}
}

# NSMainWindow::InitWindow --
#
#	Creates the Main Window.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc InitWindow oop {

	global PYPX

	set win .main$oop
	toplevel $win
	wm title $win Main

	# Do stuff when window closes
	wm protocol $win WM_DELETE_WINDOW "NSMainWindow::Close $oop"

	# This window plays sound
	SoundWindow $win

	# Start out withdrawn (hidden)
	wm withdraw $win

	Info $oop win $win

	InitMenus $oop

	set frame $win.divider2
	frame $frame -height 2 -borderwidth 1 -relief groove

	#
	# Statusbar
	#

	frame $win.statusBar \
		-borderwidth 0
	label $win.statusBar.label \
		-anchor w -text "Hello world!" -relief sunken -padx 2 \
		-foreground #80FFFF -background Black
	pack $win.statusBar.label \
		-side left -expand yes -fill both

	label $win.statusBar.depth \
		-relief sunken -width 12 -padx 2 \
		-foreground White -background Black
	pack $win.statusBar.depth \
		-side right

	label $win.statusBar.center \
		-text "C" -relief sunken -width 2 -padx 0 -foreground White \
		-background Black
	pack $win.statusBar.center \
		-side right

	# The small "C" in the status bar can be clicked to recenter the
	# Main widget and Micro Map widget on the character's location.
	# This is only need when the "scroll_follow" option is 0.
	bind $win.statusBar.center <ButtonPress-1> {
		WidgetCenter [Global main,widget]
		Global main,widget,center [[Global main,widget] center]
		WidgetCenter [Global micromap,widget]
	}
	bind $win.statusBar.center <Enter> "
		%W configure -foreground gray60
		NSMainWindow::StatusText $oop {Click to recenter the display.}
	"
	bind $win.statusBar.center <Leave> "
		%W configure -foreground White
		NSMainWindow::StatusText $oop {}
	"

	bind $win.statusBar.depth <ButtonPress-1> {
		angband setting depth_in_feet [expr ![lindex [angband setting depth_in_feet] 0]]
	}
	bind $win.statusBar.depth <Enter> "
		%W configure -foreground gray60
		NSMainWindow::StatusText $oop {Click to toggle depth_in_feet.}
	"
	bind $win.statusBar.depth <Leave> "
		%W configure -foreground White
		NSMainWindow::StatusText $oop {}
	"
	qebind $win.statusBar.depth <Py-depth> {
		NSMainWindow::DisplayDepth %W %c
	}
	qebind $win.statusBar.depth <Setting-depth_in_feet> {
		NSMainWindow::DisplayDepth %W [angband player depth]
	}

	#
	# Main widget
	#

	frame $win.mainframe \
		-borderwidth 1 -relief sunken

	set gsize [icon size]
	if {[winfo screenwidth .] >= 800} {
		if {$gsize == 16} {
			set width 30
			set height 22
		} else {
			set width 15
			set height 11
		}
	} else {
		if {$gsize == 16} {
			set width 26
			set height 18
		} else {
			set width 13
			set height 9
		}
	}

	set widgetId [NSObject::New NSWidget $win.mainframe \
		$width $height $gsize $gsize]
	NSWidget::Info $widgetId examineCmd NSMainWindow::ExamineLocation
	set widget [NSWidget::Info $widgetId widget]

	bind $widget <ButtonPress-1> "NSMainWindow::TrackPress $oop %x %y"
	bind $widget <Button1-Motion> "NSMainWindow::TrackMotion $oop %x %y"
	bind $widget <ButtonRelease-1> "NSMainWindow::TrackRelease $oop"

	bind $widget <Control-ButtonPress-1> "NSMainWindow::MouseCommand $oop %x %y +"
	bind $widget <Shift-ButtonPress-1> "NSMainWindow::MouseCommand $oop %x %y ."

	bind $widget <ButtonPress-3> "NSMainWindow::ButtonPress3 $oop %x %y %X %Y"
	bind $widget <Control-ButtonPress-3> "NSInventory::PopupSelect_Use $win.context %X %Y"

	bind $widget <Leave> "+NSMainWindow::StatusText $oop {}"

	qebind $widget <Position> {NSMainWindow::PositionChanged %W %y %x}

	Global main,widget,center [angband player position]

	qebind $widget <Dungeon-enter> {
		WidgetCenter %W
		Global main,widget,center [%W center]
	}

	qebind $widget <Inkey> "NSMainWindow::TrackOnce $oop"

	qebind $widget <Track-health> "NSMainWindow::UpdateHealthWho $oop %w %f"

	# The "big map", the map of the entire cave with scroll bars.
	# The user can change the scale via a popup menu, so we save
	# the desired scale.
	set scale [Value bigmap,scale]
	set width [expr [NSWidget::Info $widgetId width] - 16]
	set height [expr [NSWidget::Info $widgetId height] - 16]
#	set mapId [NSObject::New NSMap $win.mainframe $width $height $scale $scale]
	set mapId [NSObject::New NSMap $widget $width $height $scale $scale]

	set widget2 [NSMap::Info $mapId widget]

	NSMap::Info $mapId scaleCmd \
		"Value bigmap,scale \[NSWidget::Info [NSMap::Info $mapId widgetId] scale]"

	NSWidget::Info [NSMap::Info $mapId widgetId] examineCmd BigMapExamine

	bind $widget2 <Leave> {
		[Global mapdetail,widget] center -100 -100
	}

	bind $widget2 <ButtonPress-1> {
		angband keypress \033
	}

	# Each NSMap widget has Left/Right etc bindings. Need this to
	# hide the map.
	bind $widget2 <KeyPress-Escape> {
		angband keypress \033
	}

	# Global access
	Global main,widgetId $widgetId
	Global main,widget $widget
	Global bigmap,mapId $mapId
	Global bigmap,widgetId [NSMap::Info $mapId widgetId]
	Global bigmap,widget [NSMap::Info $mapId widget]

	# This binding is called whenever the Main Window is resized
	# by the user.
	bindtags $win.mainframe [concat [bindtags $win.mainframe] MainFrameBindTag]
	bind MainFrameBindTag <Configure> \
		"NSMainWindow::Configure $oop %w %h"

	grid $widget -row 1 -column 0 -rowspan 1 -columnspan 1

	# Gridded geometry. The main widget uses gridded geometry, so
	# that the Main Window is always an exact number of grids in
	# size.
	$widget configure -setgrid yes
	wm minsize $win [expr 32 * 7 / $gsize] [expr 32 * 7 / $gsize]

	#
	# Geometry
	#

	grid rowconfigure $win 0 -weight 0
	grid rowconfigure $win 1 -weight 1
	grid rowconfigure $win 2 -weight 0
	grid columnconfigure $win 0 -weight 1

	grid $win.divider2 \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew
	grid $win.mainframe \
		-row 1 -column 0 -rowspan 1 -columnspan 1 -sticky news
	grid $win.statusBar \
		-row 2 -column 0 -rowspan 1 -columnspan 1 -sticky ew

	#
	# Context menu
	#

	menu $win.context -tearoff 0

	#
	# Feed Term when keys pressed
	#

	Term_KeyPress_Bind $win

	# Hack -- Visual feedback of whether the target is set or not.
	TargetSetup $oop

	# The monster health bar
	ProgressSetup $widget

if 0 {
	# This creates a text item that displays the character's current
	# location as "y:YYY x:XXX"
	set itemId [$widget create text -x 0 \
		-y [expr [$widget cget -height] * [$widget cget -gheight] - 1] \
		-anchor sw -clipx yes -width 100 -height 16 -bevel yes \
		-fill [Value statusText] \
		-fill2 [Value statusText2] \
		-background [Value statusBG] \
		-background2 [Value statusBG2] \
		-bevellight [Value statusBL] \
		-bevellight2 [Value statusBL2] \
		-beveldark [Value statusBD] \
		-beveldark2 [Value statusBD2]]
	qebind $widget <Position> "+
		$widget itemconfigure $itemId -text {y:%y x:%x}
	"
}

if 0 {
	set height 18

	# This creates a text item that displays the character's hitpoints
	set itemId [$widget create text -x 4 \
		-y [expr [$widget cget -height] * [$widget cget -gheight] - 5 - $height] \
		-anchor sw -clipx yes -width 48 -height $height -bevel no \
		-font {Courier 10} \
		-fill [Value statusText] \
		-fill2 [Value statusText2] \
		-background 35 \
		-background2 [Value statusBG2] \
		-bevellight [Value statusBL] \
		-bevellight2 [Value statusBL2] \
		-beveldark [Value statusBD] \
		-beveldark2 [Value statusBD2]]
	qebind $widget <Py-hitpoints> "NSMainWindow::DisplayPoints $oop $itemId %c"

	# This creates a text item that displays the character's mana
	set itemId [$widget create text -x 4 \
		-y [expr [$widget cget -height] * [$widget cget -gheight] - 5] \
		-anchor sw -clipx yes -width 48 -height $height -bevel no \
		-font {Courier 10} \
		-fill [Value statusText] \
		-fill2 [Value statusText2] \
		-background 235 \
		-background2 [Value statusBG2] \
		-bevellight [Value statusBL] \
		-bevellight2 [Value statusBL2] \
		-beveldark [Value statusBD] \
		-beveldark2 [Value statusBD2]]
	qebind $widget <Py-mana> "NSMainWindow::DisplayPoints $oop $itemId %c"
}

}

# NSMainWindow::Configure --
#
#	Called when the Main Window changes size. The main widget is
#	resized, and all its items are repositioned. The Big Map is
#	also resized.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc Configure {oop width height} {

	variable Progress

	set win [Info $oop win]
	set widgetId [Global main,widgetId]
	set widget [Global main,widget]

	# Resize the main widget
	set iconSize [icon size]
	set cols [expr int($width / $iconSize)]
	set rows [expr int($height / $iconSize)]
	if {[NSWidget::Resize $widgetId $cols $rows]} {

		# Move the Monster Health Bar
		set x [expr $cols * $iconSize / 2]
		set y [expr $rows * $iconSize - 8]
		$widget itemconfigure $Progress(barId) -x $x -y $y
		$widget itemconfigure $Progress(textId) -x $x -y [expr $y - 6]

		# Move the target 'T' item
		set x [expr $cols * $iconSize - 6]
		set y [expr $rows * $iconSize - 6]
		$widget itemconfigure [Global target,itemId] -x $x -y $y

		# Arrange "status" items
		NSStatus::Configure
	}
}

proc DisplayPoints {oop itemId value} {

	set widget [Global main,widget]
	$widget itemconfigure $itemId -text $value
}

# NSMainWindow::TargetSetup --
#
#	One-time initialization. When the target is set, display an
#	icon in the lower-left corner. Remove the icon when the target
#	is unset.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc TargetSetup oop {

	global Windows

	set win [Info $oop win]

	set widget [Global main,widget]
	set cols [$widget cget -width]
	set rows [$widget cget -height]
	set gwidth [$widget cget -gwidth]
	set gheight [$widget cget -gheight]
	set x [expr $cols * $gwidth - 6]
	set y [expr $rows * $gheight - 6]

	set itemId [$widget create text -x $x -y $y -visible no \
		-anchor se -text T -clipx no -width 20 -height 20 -bevel yes \
		-font {Times 12 bold}  -justify center \
		-fill [Value statusText] \
		-fill2 [Value statusText2] \
		-background [Value statusBG] \
		-background2 [Value statusBG2] \
		-bevellight [Value statusBL] \
		-bevellight2 [Value statusBL2] \
		-beveldark [Value statusBD] \
		-beveldark2 [Value statusBD2]]

	qebind $widget <Target-set> "
		$widget itemconfigure $itemId -visible yes
	"
	qebind $widget <Target-unset> "
		$widget itemconfigure $itemId -visible no
	"

	Global target,itemId $itemId

	# This "cursor" is displayed during targetting/looking
	set itemId [$widget create cursor -color yellow -linewidth 2 -visible no]

	qebind $widget <Term-fresh> "NSMainWindow::Fresh_Cursor $oop"
	qeconfigure $widget <Term-fresh> -active no

	if $::DEBUG {
		set ::debug_cursor 0
	}

	qebind $widget <Cursor-show> {
		Global cursor,x %x
		Global cursor,y %y
		qeconfigure %W <Term-fresh> -active yes
		if $::DEBUG {
			set ::debug_cursor 1
		}
	}

	qebind $widget <Cursor-hide> "
		if \[Global cursor,visible] {
			NSMainWindow::DisplayCursor $oop 0 -1 -1
		}
		qeconfigure [Global main,widget] <Term-fresh> -active no
		if $::DEBUG {
			set ::debug_cursor 0
		}
	"

	Global cursor,itemId $itemId
	Global cursor,visible 0
}

# NSMainWindow::ProgressSetup --
#
#	Creates a Widget text item (for the monster name) and progress item
#	(for the monster hit points) in the Main Window.
#
# Arguments:
#	widget					The Widget to create the monster bar in.
#
# Results:
#	What happened.

proc ProgressSetup widget {

	variable Progress

	# XXX Mega-Hack XXX
	# The monster health bar is displayed differently for friendly versus
	# non-friendly monsters. This is done by using a different set of
	# colors for each state. We don't want the progress item to repeatedly
	# allocate and deallocate the colors it uses, so we call the
	# "$widget coloralloc" command to preallocate each color used by
	# the progress item.
	#
	# When the user chooses new progress item colors via the Color
	# Preferences Window, we must deallocate those colors previously
	# pre-allocated, then pre-allocate the new colors.

	foreach name {BarDone BarToDo BarBL BarBD} {

		set color [Value health$name]
		set opacity [Value health${name}2]
		$widget coloralloc $color $opacity
		set Progress(alloc,health$name) [list $color $opacity]

		set color [Value friend$name]
		set opacity [Value friend${name}2]
		$widget coloralloc $color $opacity
		set Progress(alloc,friend$name) [list $color $opacity]
	}

	set cols [$widget cget -width]
	set rows [$widget cget -height]
	set gwidth [$widget cget -gwidth]
	set gheight [$widget cget -gheight]
	set x [expr $cols * $gwidth / 2]
	set y [expr $rows * $gheight - 8]

	set data {
		healthBarDone done
		healthBarToDo todo
		healthBarBL bevellight
		healthBarBD beveldark
		healthBarDone2 done2
		healthBarToDo2 todo2
		healthBarBL2 bevellight2
		healthBarBD2 beveldark2
	}
	foreach {name varname} $data {
		set $varname [Value $name]
	}

	set Progress(barId) \
		[$widget create progressbar -x $x -y $y -width 150 -height 6 \
		-anchor s -done $done -done2 $done2 -todo $todo -todo2 $todo2 \
		-bevellight $bevellight -bevellight2 $bevellight2 \
		-beveldark $beveldark -beveldark2 $beveldark2 \
		-visible no]

	set data {
		healthNameText fill
		healthNameBG background
		healthNameBL bevellight
		healthNameBD beveldark
		healthNameText2 fill2
		healthNameBG2 background2
		healthNameBL2 bevellight2
		healthNameBD2 beveldark2
	}
	foreach {name varname} $data {
		set $varname [Value $name]
	}

	set maxWidth 240

	# Calculate the maximum required width of the health bar text
	if {$::DEBUG} {
		set maxWidth2 0
		set max_r_idx [angband r_info max]
		for {set r_idx 1} {$r_idx < $max_r_idx} {incr r_idx} {
			angband r_info info $r_idx attrib
			set width [font measure {{MS Sans Serif} 8} $attrib(name)]
			if {$width > $maxWidth2} {
				set maxWidth2 $width
			}
		}
		incr maxWidth2 4

		if {$maxWidth2 > $maxWidth} {
			error "progress text width should be >= $maxWidth2"
		}
	}

	set Progress(textId) [$widget create text -x $x -y [expr $y - 6] \
		-visible no -anchor s -fill $fill -fill2 $fill2 \
		-background $background -background2 $background2 \
		-bevellight $bevellight -bevellight2 $bevellight2 \
		-beveldark $beveldark -beveldark2 $beveldark2 \
		-clipx yes -width $maxWidth -height 15 -bevel yes]

	set data {
		healthBarDone -done
		healthBarToDo -todo
		healthBarBL -bevellight
		healthBarBD -beveldark
		healthBarDone2 -done2
		healthBarToDo2 -todo2
		healthBarBL2 -bevellight2
		healthBarBD2 -beveldark2

		friendBarDone -done
		friendBarToDo -todo
		friendBarBL -bevellight
		friendBarBD -beveldark
		friendBarDone2 -done2
		friendBarToDo2 -todo2
		friendBarBL2 -bevellight2
		friendBarBD2 -beveldark2

		healthNameText -fill
		healthNameBG -background
		healthNameBL -bevellight
		healthNameBD -beveldark
		healthNameText2 -fill2
		healthNameBG2 -background2
		healthNameBL2 -bevellight2
		healthNameBD2 -beveldark2
	}
	foreach {name option} $data {
		NSValueManager::AddClient $name \
			"NSMainWindow::ProgressSynch $name $option"
	}

	set Progress(visible) 0
	set Progress(current) 0
	set Progress(r_idx) 0
	set Progress(friend) 0
}

# NSMainWindow::ProgressSynch --
#
#	Called by NSValueManager when any of the healthName*, healthBar* or
#	friendBar* values change. Configures the monster health bar colors
#	as appropriate. Note the ugly song-and-dance number done to control
#	which colors are pre-allocated.
#
# Arguments:
#	name					The name of the value.
#	option					Configuration option for the widget item.
#
# Results:
#	What happened.

proc ProgressSynch {name option} {

	global Windows
	variable Progress

	set widget [Global main,widget]
	set value [Value $name]

	switch -glob $name {
		healthName* {
			$widget itemconfigure $Progress(textId) $option $value
		}
		healthBar*2 {
			set name2 [string trimright $name 2]
			eval $widget colorderef $Progress(alloc,$name2)
			set color [lindex $Progress(alloc,$name2) 0]
			set Progress(alloc,$name2) [list $color $value]
			eval $widget coloralloc $color $value
			if !$Progress(friend) {
				$widget itemconfigure $Progress(barId) $option $value
			}
		}
		healthBar* {
			eval $widget colorderef $Progress(alloc,$name)
			set opacity [lindex $Progress(alloc,$name) 1]
			set Progress(alloc,$name) [list $value $opacity]
			eval $widget coloralloc $value $opacity
			if !$Progress(friend) {
				$widget itemconfigure $Progress(barId) $option $value
			}
		}
		friendBar*2 {
			set name2 [string trimright $name 2]
			eval $widget colorderef $Progress(alloc,$name2)
			set color [lindex $Progress(alloc,$name2) 0]
			set Progress(alloc,$name2) [list $color $value]
			eval $widget coloralloc $color $value
			if $Progress(friend) {
				$widget itemconfigure $Progress(barId) $option $value
			}
		}
		friendBar* {
			eval $widget colorderef $Progress(alloc,$name)
			set opacity [lindex $Progress(alloc,$name) 1]
			set Progress(alloc,$name) [list $value $opacity]
			eval $widget coloralloc $value $opacity
			if $Progress(friend) {
				$widget itemconfigure $Progress(barId) $option $value
			}
		}
	}
}

# NSMainWindow::DisplayCursor --
#
#	Shows the cursor in the main widget at the given cave
#	location. If the location is outside the visible area of the
#	widget, then the widget is centered on the location.
#
#	When hiding the cursor the widget is centered on the character
#	location if required.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	show					1 to show the cursor, 0 to hide it.
#	x, y					Cave location to place cursor at.
#
# Results:
#	What happened.

proc DisplayCursor {oop show x y} {

	global PYPX

	set widget [Global main,widget]

	# Show the cursor
	if $show {

		$widget itemconfigure [Global cursor,itemId] \
			-visible yes -x $x -y $y

		set center [$widget center]

		set centerY [lindex $center 0]
		set height [$widget cget -height]
		set minY [expr $centerY - $height / 2]
		set maxY [expr $minY + $height - 1]
	
		set centerX [lindex $center 1]
		set width [$widget cget -width]
		set minX [expr $centerX - $width / 2]
		set maxX [expr $minX + $width - 1]
	
		if {$y < $minY || $y > $maxY || $x < $minX || $x > $maxX} {
			$widget center $y $x
		}

		Global cursor,visible 1

		# Need "$widget itemcget" like canvas
		Global cursor,y $y
		Global cursor,x $x

	# Hide the cursor
	} else {

		$widget itemconfigure [Global cursor,itemId] \
			-visible no

		if {[string compare [$widget center] [Global main,widget,center]]} {
			eval $widget center [Global main,widget,center]
		}

		Global cursor,visible 0
	}
}

# NSRecall::Fresh_Cursor --
#
#	Called as a <Term-fresh> quasi-event script.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Fresh_Cursor oop {

	ASSERT {$::debug_cursor == 1} \
		"Fresh_Cursor called with debug_cursor=0!"

	DisplayCursor $oop 1 [Global cursor,x] [Global cursor,y]
}

# NSMainWindow::InitMenus --
#
#	Initialize the menus for the Main Window.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc InitMenus oop {

	global Angband
	global NSMainWindow
	global Windows

	set win [Info $oop win]

	set menuDef "-tearoff 0 -postcommand \"NSMainWindow::SetupMenus $oop\" -identifier MENUBAR"
	set mbarId [NSObject::New NSMenu $win $menuDef]

	Info $oop mbarId $mbarId

	#
	# File Menu
	#

	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_FILE}
	NSMenu::MenuInsertEntry $mbarId -end MENUBAR {-type cascade -menu MENU_FILE -label "File" -underline 0 -identifier M_FILE}

	set entries {}
	lappend entries "-type command -label \"Save\" -command \"DoUnderlyingCommand ^s\" -identifier E_GAME_SAVE"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Quit With Save\" -command \"DoUnderlyingCommand ^x\" -identifier E_GAME_EXIT"
	lappend entries "-type command -label \"Quit\" -command \"QuitNoSave\" -identifier E_GAME_ABORT"

	NSMenu::MenuInsertEntries $mbarId -end MENU_FILE $entries

	#
	# Inven Menu
	#

	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_INVEN}
	NSMenu::MenuInsertEntry $mbarId -end MENUBAR {-type cascade -menu MENU_INVEN -label "Inven" -underline 0 -identifier M_INVEN}

	# Magic Menu
	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_MAGIC}
	set entries {}
	lappend entries "-type command -label \"Activate\" -command \"DoUnderlyingCommand A\" -identifier E_MAGIC_ACTIVATE"
	lappend entries "-type command -label \"Aim Wand\" -command \"DoUnderlyingCommand a\" -identifier E_MAGIC_WAND"
	lappend entries "-type command -label \"Drink Potion\" -command \"DoUnderlyingCommand q\" -identifier E_MAGIC_POTION"
	lappend entries "-type command -label \"Read Scroll\" -command \"DoUnderlyingCommand r\" -identifier E_MAGIC_SCROLL"
	lappend entries "-type command -label \"Use Staff\" -command \"DoUnderlyingCommand u\" -identifier E_MAGIC_STAFF"
	lappend entries "-type command -label \"Zap Rod\" -command \"DoUnderlyingCommand z\" -identifier E_MAGIC_ROD"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Browse\" -command \"DoUnderlyingCommand b\" -identifier E_MAGIC_BROWSE"
	lappend entries "-type command -label \"Study\" -command \"DoUnderlyingCommand G\" -identifier E_MAGIC_STUDY"
	NSMenu::MenuInsertEntries $mbarId -end MENU_MAGIC $entries

	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_USE}
	set entries {}
	lappend entries "-type command -label \"Destroy\" -command \"DoUnderlyingCommand k\" -identifier E_USE_DESTROY"
	lappend entries "-type command -label \"Drop\" -command \"DoUnderlyingCommand d\" -identifier E_USE_DROP"
	lappend entries "-type command -label \"Pick Up\" -command \"DoUnderlyingCommand g\" -identifier E_USE_PICKUP"
	lappend entries "-type command -label \"Take Off\" -command \"DoUnderlyingCommand t\" -identifier E_USE_TAKEOFF"
	lappend entries "-type command -label \"Wear/Wield\" -command \"DoUnderlyingCommand w\" -identifier E_USE_WIELD"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Eat Food\" -command \"DoUnderlyingCommand E\" -identifier E_USE_FOOD"
	lappend entries "-type command -label \"Fire Missle\" -command \"DoUnderlyingCommand f\" -identifier E_USE_MISSILE"
	lappend entries "-type command -label \"Fuel Light\" -command \"DoUnderlyingCommand F\" -identifier E_USE_FUEL"
	lappend entries "-type command -label \"Jam Spike\" -command \"DoUnderlyingCommand j\" -identifier E_USE_SPIKE"
	lappend entries "-type command -label \"Throw\" -command \"DoUnderlyingCommand v\" -identifier E_USE_THROW"
	NSMenu::MenuInsertEntries $mbarId -end MENU_USE $entries

	set entries {}
	lappend entries "-type command -label \"Equipment\" -command \"DoUnderlyingCommand e\" -identifier E_INVEN_EQUIPMENT"
	lappend entries "-type command -label \"Inventory\" -command \"DoUnderlyingCommand i\" -identifier E_INVEN_INVENTORY"
	lappend entries "-type separator"
 	lappend entries "-type cascade -menu MENU_MAGIC -label \"Magic\" -identifier M_MAGIC"
	lappend entries "-type cascade -menu MENU_USE -label \"Use\" -identifier M_USE"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Inscribe\" -command \"DoUnderlyingCommand \{\" -identifier E_INVEN_INSCRIBE"
	lappend entries "-type command -label \"Uninscribe\" -command \"DoUnderlyingCommand \}\" -identifier E_INVEN_UNINSCRIBE"

	NSMenu::MenuInsertEntries $mbarId -end MENU_INVEN $entries

	#
	# Book Menu -- Hey, Steve!
	#

	if {[angband player spell_book] != ""} {
		NSObject::New NSBookMenu $mbarId
	}

	#
	# Mindcraft Menu -- For Mindcrafter class only
	#

	if {[angband player class] == "Mindcrafter"} {
		NSObject::New NSMindcraftMenu $mbarId
	}

	#
	# Other Menu
	#

	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_OTHER}
	NSMenu::MenuInsertEntry $mbarId -end MENUBAR {-type cascade -menu MENU_OTHER -label "Other" -underline 0 -identifier M_OTHER}

	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_PREFERENCES}
	set entries {}
	lappend entries "-type command -label \"Alternate\" \
		-command \"NSModule::LoadIfNeeded NSAlternate ; \
		NSWindowManager::Display alternate\" \
		-identifier E_PREF_ALTERNATE"
	lappend entries "-type command -label \"Assign\" \
		-command \"NSModule::LoadIfNeeded NSAssign ; \
		NSWindowManager::Display assign\" \
		-identifier E_PREF_ASSIGN"
	lappend entries "-type command -label \"Color\" \
		-command \"NSModule::LoadIfNeeded NSColorPreferences ; \
		NSWindowManager::Display color\" \
		-identifier E_PREF_COLOR"
	lappend entries "-type command -label \"Keymap\" \
		-command \"NSModule::LoadIfNeeded NSKeymap ; \
		NSWindowManager::Display keymap\" \
		-identifier E_PREF_KEYMAP"
	lappend entries "-type command -label \"Macros\" \
		-command \"DoUnderlyingCommand @\" -identifier E_PREF_MACROS"
	lappend entries "-type command -label \"Options\" \
		-command \"DoUnderlyingCommand =\" -identifier E_PREF_OPTIONS"
	lappend entries "-type command -label \"Sound\" \
		-command \"NSModule::LoadIfNeeded NSSound ; \
		NSWindowManager::Display sound\" \
		-identifier E_PREF_SOUND"
	lappend entries "-type command -label \"Sprite\" \
		-command \"NSModule::LoadIfNeeded NSSprite ; \
		NSWindowManager::Display sprite\" \
		-identifier E_PREF_SPRITE"
	NSMenu::MenuInsertEntries $mbarId -end MENU_PREFERENCES $entries

	set entries {}
	lappend entries "-type command -label \"Character Info\" -command \"DoCommandIfAllowed C\" -identifier E_OTHER_INFO"
	lappend entries "-type command -label \"File Character\" -command \"FileCharacter $win\" -identifier E_OTHER_FILE"
	lappend entries "-type command -label \"High Scores\" \
		-command \"NSModule::LoadIfNeeded NSHighScore ; \
		NSHighScore::Info \$Windows(highscore,oop) interactive 0 ; \
		NSWindowManager::Display highscore\" \
		-identifier E_OTHER_SCORE"
	lappend entries "-type command -label \"Knowledge\" -command \"DoCommandIfAllowed ~\" -identifier E_OTHER_KNOWLEDGE"
	lappend entries "-type command -label \"Message History\" -command \"DoCommandIfAllowed ^p\" -identifier E_OTHER_MESSAGES"
	lappend entries "-type command -label \"Photo\" \
		-command \"NSModule::LoadIfNeeded NSPhoto ; \
		NSObject::New NSPhoto \[Global main,widget\]\" -identifier E_OTHER_PHOTO"
	lappend entries "-type command -label \"Quest Status\" -command \"DoUnderlyingCommand ^Q\" -identifier E_OTHER_QUEST"
	lappend entries "-type command -label \"Time Of Day\" -command \"DoUnderlyingCommand ^T\" -identifier E_OTHER_TIME"
	lappend entries "-type cascade -menu MENU_PREFERENCES -label \"Preferences\" -identifier M_PREFERENCES"
	lappend entries "-type separator"
	lappend entries "-type checkbutton -label \"Use Sound\" \
		-command \"NSMainWindow::ToggleSound $oop\" \
		-variable NSMainWindow($oop,useSound) -identifier E_OTHER_SOUND"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Arrange Windows\" -command \"HardcodeGeometry\" -identifier E_OTHER_DEFPOS"
	lappend entries "-type command -label \"Save Window Positions\" -command \"WriteGeometryFile\" -identifier E_OTHER_SAVEPOS"

	NSMenu::MenuInsertEntries $mbarId -end MENU_OTHER $entries

	#
	# Help Menu
	#

	NSObject::New NSMenu $mbarId {-tearoff 0 -identifier MENU_HELP}
	NSMenu::MenuInsertEntry $mbarId -end MENUBAR {-type cascade \
		-menu MENU_HELP -label "Help" -underline 0 -identifier M_HELP}

	set entries {}
	lappend entries "-type command -label \"Help\" \
		-command \"DoCommandIfAllowed ?\" -identifier E_HELP"
	lappend entries "-type command -label \"Tips\" \
		-command \"NSModule::LoadIfNeeded NSTips ; \
		WindowBringToFront \$Windows(tip,win)\" \
		-identifier E_TIPS"
	lappend entries "-type separator"
	lappend entries "-type command -label \"About $Angband(name)...\" \
		-command AboutApplication -identifier E_ABOUT"

	NSMenu::MenuInsertEntries $mbarId -end MENU_HELP $entries
	
	# Hack -- Accelerators depend on current keymap!
	SynchMenuAccel $oop 1
	qebind $win <Setting-rogue_like_commands> \
		"NSMainWindow::SynchMenuAccel $oop 0"
	qebind $win <Keymap> \
		"NSMainWindow::SynchMenuAccel $oop 0"
}

# NSMainWindow::SetupMenus --
#
#	Called by NSMenus::_MenuPostCommand() to enable menu items before
#	posting a menu.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	mbarId					OOP ID of NSMenu object (the menubar).
#
# Results:
#	What happened.

proc SetupMenus {oop mbarId} {

	lappend identList E_OTHER_SOUND E_OTHER_SAVEPOS E_OTHER_DEFPOS \
		E_ABOUT E_OTHER_PHOTO E_TIPS
	lappend identList M_PREFERENCES E_PREF_ASSIGN E_PREF_COLOR \
		E_PREF_KEYMAP E_PREF_SOUND E_PREF_SPRITE E_PREF_ALTERNATE

	if {![string compare [angband inkey_flags] INKEY_CMD]} {
		lappend identList E_GAME_SAVE E_GAME_EXIT E_OTHER_INFO \
			E_OTHER_FILE E_OTHER_KNOWLEDGE E_PREF_MACROS E_OTHER_MESSAGES \
			E_OTHER_QUEST E_OTHER_TIME E_PREF_OPTIONS E_OTHER_SCORE E_HELP

		lappend identList E_INVEN_INVENTORY E_INVEN_EQUIPMENT \
			E_INVEN_INSCRIBE E_INVEN_UNINSCRIBE

		lappend identList M_MAGIC E_MAGIC_ACTIVATE E_MAGIC_WAND \
			E_MAGIC_POTION E_MAGIC_SCROLL E_MAGIC_STAFF E_MAGIC_ROD \
			E_MAGIC_BROWSE E_MAGIC_STUDY

		lappend identList M_USE E_USE_DESTROY E_USE_DROP E_USE_PICKUP \
			E_USE_TAKEOFF E_USE_WIELD E_USE_FOOD E_USE_MISSILE E_USE_FUEL \
			E_USE_SPIKE E_USE_THROW
	}

	lappend identList E_GAME_ABORT

	NSMenu::MenuEnable $mbarId $identList
}

# NSMainWindow::Close --
#
#	Called when the user attempts to close the window.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc Close oop {

	global Angband

	# Check if game is waiting for a command. If not, it isn't a
	# good time to quit.
	if {[string compare [angband inkey_flags] INKEY_CMD]} {
		bell
		return
	}

	# Ask the user to confirm quit with save
	set answer [tk_messageBox -icon question -type yesno \
		-title "Quit $Angband(name)" -message "Do you really want to\
		save the game and quit?"]
	if {$answer == "no"} return

	# Save and quit
	DoCommandIfAllowed ^x
}

# NSMainWindow::SynchMenuAccel --
#
#	Sets the accelerator option for certain menu entries depending on
#	the current keymap.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc SynchMenuAccel {oop force} {

	global NSMenu
	variable Priv
	
	# Since many keymaps may change when a pref file is read in, delay
	# configuring the menu accelerators until idle time.
	if {!$force} {
	
		if {($Priv(keymap,afterId) == "")} {
			set Priv(keymap,afterId) \
				[after idle NSMainWindow::SynchMenuAccel $oop 1]
		}

		# The idle task was scheduled by a previous call, but this
		# call isn't from the idle task.
		return
	}

	# Important: clear the after id.
	set Priv(keymap,afterId) ""

	set mbarId [Info $oop mbarId]

	lappend data E_GAME_SAVE ^S
	lappend data E_GAME_EXIT ^X

	lappend data E_MAGIC_ACTIVATE A
	lappend data E_MAGIC_WAND a
	lappend data E_MAGIC_POTION q
	lappend data E_MAGIC_SCROLL r
	lappend data E_MAGIC_STAFF u
	lappend data E_MAGIC_ROD z
	lappend data E_MAGIC_BROWSE b
	lappend data E_MAGIC_STUDY G

	lappend data E_USE_DESTROY k
	lappend data E_USE_DROP d
	lappend data E_USE_PICKUP g
	lappend data E_USE_TAKEOFF t
	lappend data E_USE_WIELD w
	lappend data E_USE_FOOD E
	lappend data E_USE_MISSILE f
	lappend data E_USE_FUEL F
	lappend data E_USE_SPIKE j
	lappend data E_USE_THROW v

	lappend data E_INVEN_EQUIPMENT e
	lappend data E_INVEN_INVENTORY i
	lappend data E_INVEN_INSCRIBE \{
	lappend data E_INVEN_UNINSCRIBE \}

	lappend data E_HELP ?
	lappend data E_OTHER_INFO C
	lappend data E_OTHER_KNOWLEDGE ~
	lappend data E_OTHER_MESSAGES ^P

	lappend data E_PREF_MACROS @
	lappend data E_PREF_OPTIONS =
	
	foreach {ident key} $data {
		set entry [NSMenu::_MenuFindEntry $mbarId $ident]
		set menuId [lindex $entry 0]
		set index [lindex $entry 1]
		set menu $NSMenu($menuId,menu)
		set string [angband keymap find $key]
		regsub {\^} $string Ctrl+ string
		$menu entryconfigure $index -accelerator $string
	}
}

# NSMainWindow::MouseCmd --
#
#	Use to execute commands when a mouse button is pressed. The direction
#	is determined from the given widget coordinates.
#	Calls "angband keypress CMD DIR".
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	x					x coordinate in Widget (as returned by event)
#	y					y coordinate in Widget (as returned by event)
#	cmd					Command to invoke.
#
# Results:
#	What happened.

proc MouseCommand {oop x y cmd} {

	set widgetId [Global main,widgetId]

	set coords [NSWidget::PointToCave $widgetId $x $y]
	set dirInfo [CaveToDirection [lindex $coords 0] [lindex $coords 1]]

	angband keypress \\$cmd[lindex $dirInfo 0]
}

# NSMainWindow::TrackPress --
#
#	Set up mouse tracking when <ButtonPress-1> occurs. See TrackMotion()
#	and TrackOnce() as well.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	x					x coordinate in Widget (as returned by event)
#	y					y coordinate in Widget (as returned by event)
#
# Results:
#	What happened.

proc TrackPress {oop x y} {

	variable tracking
	variable track1st
	variable trackStepping
	variable trackX
	variable trackY

	set tracking 1
	set track1st 1
	set trackX $x
	set trackY $y

	TrackOnce $oop

	set track1st 0

	set trackStepping 1
	after 200 set NSMainWindow::trackStepping 0
}

# NSMainWindow::TrackMotion --
#
#	Called to remember the cursor position when <Button1-Motion> occurs.
#	See TrackOnce() below as well.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	x					x coordinate in Widget (as returned by event)
#	y					y coordinate in Widget (as returned by event)
#
# Results:
#	What happened.

proc TrackMotion {oop x y} {

	variable trackX
	variable trackY

	set trackX $x
	set trackY $y
}

# NSMainWindow::TrackOnce --
#
#	This command examines the result of "angband inkey_flags" and
#	takes some action depending on the value. During INKEY_MORE and
#	INKEY_DISTURB it calls "angband keypress" with a single space
#	character. During INKEY_DIR it calls "angband keypress" with the
#	corresponding direction character (0-9).
#
#	During INKEY_CMD it calls "angband keypress" with a direction
#	key (to move the character), but only if the grid is not blocked.
#
#	This command is usually called when the <Inkey> binding is invoked,
#	but if the character is unable to move it calls itself again as
#	an "after" command.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc TrackOnce oop {

	variable tracking
	variable track1st
	variable trackX
	variable trackY
	variable trackId
	variable trackStepping

	# If the mouse isn't down, then do nothing. This command gets
	# called whenever the <Inkey> event is generated.
	if !$tracking return

	# It is important to delay after taking the first step, otherwise
	# the character won't be able to navigate cleanly, and -more-
	# messages may go zipping by.
	if {$trackStepping} {
		set trackId [after 1 NSMainWindow::TrackOnce $oop]
		return
	}

	# Get the inkey_flags
	set flags [angband inkey_flags]

	# If the game is displaying the "-more-" message, feed the Term
	# with a single space character. This only works if the "quick_messages"
	# option is set.
	if {![string compare $flags INKEY_MORE]} {
		angband keypress " "
		return
	}

	# If a repeated command is in progress, a mouse-click will disturb
	if {![string compare $flags INKEY_DISTURB]} {
		angband keypress " "
		return
	}

	set widgetId [Global main,widgetId]

	set coords [NSWidget::PointToCave $widgetId $trackX $trackY]
	set dirInfo [CaveToDirection [lindex $coords 0] [lindex $coords 1]]
	set dirKey [lindex $dirInfo 0]
	set y [lindex $dirInfo 1]
	set x [lindex $dirInfo 2]

	# If the game is waiting for the user to enter a direction, then
	# feed the direction key into the Term.
	if {![string compare $flags INKEY_DIR]} {
		angband keypress $dirKey
		return
	}

	# If the game is NOT asking for a command, then do nothing
	if {[string compare $flags INKEY_CMD]} {
		return
	}

	if {[angband cave blocked $y $x]} {
		set xdiff [expr $trackX - [NSWidget::Info $widgetId width] / 2]
		set ydiff [expr $trackY - [NSWidget::Info $widgetId height] / 2]
		set xdiff [expr abs($xdiff)]
		set ydiff [expr abs($ydiff)]
		switch $dirKey {
			1 {
				if {$xdiff > $ydiff} {
					incr y -1
					set dirKey 4
				} else {
					incr x
					set dirKey 2
				}
			}
			3 {
				if {$xdiff > $ydiff} {
					incr y -1
					set dirKey 6
				} else {
					incr x -1
					set dirKey 2
				}
			}
			7 {
				if {$xdiff > $ydiff} {
					incr y 1
					set dirKey 4
				} else {
					incr x
					set dirKey 8
				}
			}
			9 {
				if {$xdiff > $ydiff} {
					incr y 1
					set dirKey 6
				} else {
					incr x -1
					set dirKey 8
				}
			}
		}
		if {[angband cave blocked $y $x]} {
			set trackId [after 1 NSMainWindow::TrackOnce $oop]
			return
		}
	}

	# If the mouse is over the player grid, only move if this is
	# the initial mouse click. Otherwise the user may accidentally
	# "run on the spot".
	if {!$track1st && ($dirKey == 5)} {
		set trackId [after 10 NSMainWindow::TrackOnce $oop]
		return
	}

	# Move the character.
	angband keypress $dirKey
}

# NSMainWindow::TrackRelease --
#
#	Cancels mouse tracking when the mouse button is released.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc TrackRelease oop {

	variable trackId
	variable tracking
	variable trackStepping

	set tracking 0
	set trackStepping 0
	after cancel trackId
}

# NSMainWindow::ExamineLocation --
#
#	Display a description of what the character sees at a given cave
#	location. Called as NSWidget(OOP,examineCmd).
#
# Arguments:
#	oop					OOP ID NSWidget.
#	y					y cave location.
#	x					x cave location.
#
# Results:
#	What happened.

proc ExamineLocation {oop y x} {

	global Windows

	StatusText $Windows(main,oop) [angband cave examine $y $x]
}

# NSMainWindow::CaveToDirection --
#
#	Given cave location y,x, determine the direction key relative
#	to the player location.
#
# Arguments:
#	y					y cave location.
#	x					x cave location.
#
# Results:
#	Return "dir y x", where dir is key to move, y/x is adjacent cave location
#	character would move to.

proc CaveToDirection {y x} {

	global PYPX

	set py [lindex $PYPX 0]
	set px [lindex $PYPX 1]

	if {$y < $py} {
		set yyy 789
		incr py -1
	} elseif {$y > $py} {
		set yyy 123
		incr py
	} else {
		set yyy 456
	}

	if {$x < $px} {
		set dirKey [string index $yyy 0]
		incr px -1
	} elseif {$x > $px} {
		set dirKey [string index $yyy 2]
		incr px
	} else {
		set dirKey [string index $yyy 1]
	}

	return "$dirKey $py $px"
}

# NSMainWindow::StatusText --
#
#	Displays text in the status bar.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc StatusText {oop text} {

	set win [Info $oop win]
	$win.statusBar.label configure -text $text
}

# NSMainWindow::DisplayPYPX --
#
#	This is a "trace variable" command. Displays the character location
#	in the Main Window status bar.
#
#	NOT USED (see DisplayDepth() below).
#
# Arguments:
#	label					The label widget to display the depth in.
#	name1					See "trace" manual entry.
#	name2					See "trace" manual entry.
#	op					See "trace" manual entry.
#
# Results:
#	What happened.

proc DisplayPYPX {label name1 name1 op} {

	global PYPX

	$label configure -text "Y:[lindex $PYPX 0] X:[lindex $PYPX 1]"
}

# NSMainWindow::DisplayDepth --
#
#	Displays the dungeon level in the Main Window's status bar.
#
# Arguments:
#	label					The label widget to display the depth in.
#	depth					Current depth.
#
# Results:
#	What happened.

proc DisplayDepth {label depth} {

	if {[angband player inside_arena]} {
		set depthStr Arena
	} elseif {[angband player inside_quest]} {
		set depthStr Quest
	} elseif {$depth == 0} {
		set depthStr [angband cave wild_name]
	} else {
		if {[lindex [angband setting depth_in_feet] 0]} {
			set depthStr "[expr $depth * 50] feet"
		} else {
			set depthStr "Level $depth"
		}
	}

	$label configure -text $depthStr
}

# NSMainWindow::ButtonPress3 --
#
#	Do something when Button 3 is pressed in the main widget.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	x y					Coords in Widget (as returned by event).
#	X Y					Global coords (as returned by event).
#
# Results:
#	What happened.

proc ButtonPress3 {oop x y X Y} {

	set win [Info $oop win]

	set flags [angband inkey_flags]

	# Popup item list
	if {[lsearch $flags INKEY_ITEM] != -1} {
		NSInventory::PopupSelect $win.context $X $Y

	# Popup spell list
	} elseif {[lsearch $flags INKEY_SPELL] != -1} {
		NSBookMenu::PopupSelect $win.context $X $Y

	# Popup Mindcraft power list
	} elseif {[lsearch $flags INKEY_MINDCRAFT] != -1} {
		NSMindcraftMenu::PopupSelect $win.context $X $Y

	# Popup racial/mutation power list
	} elseif {[lsearch $flags INKEY_POWER] != -1} {
		NSPower::PopupSelect $win.context $X $Y

	# Run
	} elseif {[lsearch $flags INKEY_CMD] != -1} {
		MouseCommand $oop $x $y .
	}
}

# NSMainWindow::UpdateHealthWho --
#
#	Called as a qebind <Track-health> script. Hides/shows/updates the
#	Monster Health Bar.
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#	m_idx					m_list[] index of the tracked monster.
#	friend					1 if m_idx if a pet.
#
# Results:
#	What happened.

proc UpdateHealthWho {oop m_idx friend} {

	global NSProgress
	variable Progress

	set widget [Global main,widget]
	
	# Hide the bar if visible and not tracking
	if {$m_idx == 0} {
		if $Progress(visible) {
			$widget itemconfigure $Progress(barId) -visible no
			$widget itemconfigure $Progress(textId) -visible no
			set Progress(visible) 0
		}
		return
	}

	# Show the bar if hidden
	if !$Progress(visible) {
		$widget itemconfigure $Progress(barId) -visible yes
		$widget itemconfigure $Progress(textId) -visible yes
		set Progress(visible) 1
	}

	# Change colors depending on friend status
	if {$friend != $Progress(friend)} {
		SetProgressColors $friend
		set Progress(friend) $friend
	}

	# Set the progress
	array set attrib [debug dumptype monster_type $m_idx]
	if {$attrib(ml) == "TRUE"} {
		set curhp [expr ($attrib(hp) > 0) ? $attrib(hp) : 0]
		set current [expr int(($curhp.0 / $attrib(maxhp)) * 100)]
	} else {
		set current 0
	}
	if {$current != $Progress(current)} {
		$widget itemconfigure $Progress(barId) \
			-current $current -maximum 100
		set Progress(current) $current
	}

	# Set the name
	if {$attrib(r_idx) != $Progress(r_idx)} {
		angband r_info info $attrib(r_idx) attrib2
		$widget itemconfigure $Progress(textId) -text $attrib2(name)
		set Progress(r_idx) $attrib(r_idx)
	}
}

# NSMainWindow::SetProgressColors --
#
#	Called when the monster health bar goes from friend to non-friend mode.
#	Updates the monster health bar colors as appropriate.
#
# Arguments:
#	name1					See "trace" manual entry.
#	name2					See "trace" manual entry.
#	op					See "trace" manual entry.
#
# Results:
#	What happened.

proc SetProgressColors friend {

	variable Progress

	set widget [Global main,widget]

	if $friend {
		set data {
			friendBarDone done
			friendBarToDo todo
			friendBarBL bevellight
			friendBarBD beveldark
			friendBarDone2 done2
			friendBarToDo2 todo2
			friendBarBL2 bevellight2
			friendBarBD2 beveldark2
		}
	} else {
		set data {
			healthBarDone done
			healthBarToDo todo
			healthBarBL bevellight
			healthBarBD beveldark
			healthBarDone2 done2
			healthBarToDo2 todo2
			healthBarBL2 bevellight2
			healthBarBD2 beveldark2
		}
	}

	foreach {name varname} $data {
		set $varname [Value $name]
	}

	$widget itemconfigure $Progress(barId) \
		-done $done -done2 $done2 -todo $todo -todo2 $todo2 \
		-bevellight $bevellight -bevellight2 $bevellight2 \
		-beveldark $beveldark -beveldark2 $beveldark2
}

# NSMainWindow::SelectWindow --
#
#	Make a window the frontmost active window.
#
# Arguments:
#	window					Index into Windows[] (inventory, book, etc)
#
# Results:
#	What happened.

proc SelectWindow window {

	global Windows

	if {[info exists NSWindowManager::Priv($window,win)]} {
		NSWindowManager::Display $window
		return
	}

	WindowBringToFront $Windows($window,win)
}

# NSMainWindow::WithdrawWindow --
#
#	Withdraw a window.
#
# Arguments:
#	window					Index into Windows[] (inventory, book, etc)
#
# Results:
#	What happened.

proc WithdrawWindow window {

	global Windows

	wm withdraw $Windows($window,win)
}

# NSMainWindow::Display --
#
#	Remove current window (if any), and select given window.
#
# Arguments:
#	window					Index into Windows[] (inventory, book, etc)
#
# Results:
#	What happened.

proc Display window {

	global Display
	global Windows

	if {$Display(window) != "none" && $Display(window) != $window} {
		WithdrawWindow $Display(window)
	}

	SelectWindow $window

	set Display(window) $window
}

# NSMainWindow::ToggleSound --
#
#	Calls "angband setting use_sound" with the value of the
#	NSMainWindow(OOP-ID,useSound) variable (which is the variable for
#	a menu checkbutton entry).
#
# Arguments:
#	oop					OOP ID of NSMainWindow object.
#
# Results:
#	What happened.

proc ToggleSound oop {

	angband setting use_sound [Info $oop useSound]

	Value use_sound [Info $oop useSound]
}

# PositionChanged --
#
#	Called as a qebind <Position> script. Update the Main Window
#	when the character's position changes. Handles the "scroll_follow"
#	and "disturb_panel" options.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc PositionChanged {widget y x} {

	global PYPX

	# Option: Keep character centered in the display
	if {[Value scroll_follow]} {
		$widget center $y $x
		Global main,widget,center "$y $x"

	# Scroll when character crosses the edges of the display
	} else {
		scan [$widget center] "%d %d" oy ox

		set ny $y
		set yscroll [ClipCenter ny $oy [angband cave height] [$widget cget -height]]

		set nx $x
		set xscroll [ClipCenter nx $ox [angband cave width] [$widget cget -width]]

		# Center the widget if needed
		if {$xscroll || $yscroll} {
			scan $PYPX "%d %d" opy opx
			if {abs($y - $opy) > 1 || abs($x - $opx) > 1} {
				set ny $y
				set nx $x
			}
			if {[lindex [angband setting disturb_panel] 0]} {
				angband player disturb
			}
		}

		$widget center $ny $nx
		Global main,widget,center "$ny $nx"
	}

	# This global is read in various places
	set PYPX "$y $x"
}

# namespace eval NSMainWindow
}

# cnv_stat --
#
#	Converts a raw stat value into a human-readable value. Values from
#	3 to 18 are returned unchanged.
#		ex. "118" becomes "18/100"
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc cnv_stat val {

	# Above 18
	if {$val > 18} {

		set bonus [expr $val - 18];
		if {$bonus >= 10} {
			return 18/$bonus
		}
		return 18/0$bonus

	# From 3 to 18
	} else {

		return $val
	}
}

# cnv_stat_disp --
#
#	Same as cnv_stat(), but any bonus greater than 220 is displayed as
#	"***".
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc cnv_stat_disp val {

	# Above 18
	if {$val > 18} {

		set bonus [expr $val - 18];
		if {$bonus >= 220} {
			return 18/***
		} elseif {$bonus >= 10} {
			return 18/$bonus
		}
		return 18/0$bonus

	# From 3 to 18
	} else {

		return $val
	}
}

# FlashCanvasText --
#
#	Configure the fill color of a canvas item, then do it again later.
#
# Arguments:
#	canvas					Canvas widget the item is in.
#	tagOrId					The canvas item ID to manipulate.
#	color					The fill color.
#	num					Number of times to flash it.
#
# Results:
#	What happened.

variable FlashCanvas

proc FlashCanvasTextAux {canvas tagOrId color num} {

	variable FlashCanvas

	set nextColor [$canvas itemcget $tagOrId -fill]

	$canvas itemconfigure $tagOrId -fill $color

	incr num -1

	if $num {
		set id [after 250 "FlashCanvasTextAux $canvas $tagOrId $nextColor $num"]
		set FlashCanvas($canvas,$tagOrId) $id
	} else {
		unset FlashCanvas($canvas,$tagOrId)
	}
}

proc FlashCanvasText {canvas tagOrId color num} {

	variable FlashCanvas

	# Never set more than one "after" command for an item
	if {[info exists FlashCanvas($canvas,$tagOrId)]} {
		set id $FlashCanvas($canvas,$tagOrId)
		set script [lindex [after info $id] 0]
		if {$color == [$canvas itemcget $tagOrId -fill]} {
			set color [lindex $script 3]
			incr num
		}
		after cancel $id
	}

	FlashCanvasTextAux $canvas $tagOrId $color $num
}

# DoCommandIfAllowed --
#
#	Feeds a string of bytes to the Term, but only if INKEY_CMD is set.
#
# Arguments:
#	string					String argument to "angband keypress"
#
# Results:
#	What happened.

proc DoCommandIfAllowed string {

	# Check if game is waiting for a command
	if {[string compare [angband inkey_flags] INKEY_CMD]} return

	# Feed the Term
	angband keypress $string
}

# DoUnderlyingCommand --
#
#	Feeds the string to "angband keypress", but prepends a slash
#	to bypass keymaps. I use this because I couldn't properly get a
#	slash in the menu entry string.
#
# Arguments:
#	string					String argument to "angband keypress"
#
# Results:
#	What happened.

proc DoUnderlyingCommand string {

	# Check if game is waiting for a command
	if {[string compare [angband inkey_flags] INKEY_CMD]} return

	# Feed the Term
	angband keypress \\$string
}

# Note: Setting a delay of 0 results in running after the mouse is
# released; setting a delay of 1 or more prevents this
proc ConfigureMouse {} {

	set win .mouse
	toplevel $win
	wm title $win "Mouse Settings"

	set scale $win.speed
	scale $scale \
		-orient horizontal -label "Tracking Delay" \
		-width 15 -sliderlength 20 -length 200 -from 0 -to 200 \
		-command "set ::trackDelay"

	$scale set $::trackDelay

	pack $scale


	set clicks [clock clicks]
	set text [time {after 1} 100]
	set diff [expr [clock clicks] - $clicks]

	Debug $text
	Debug "1 ms = [expr $diff / 100] clicks"
}

proc TestRedrawSpeed {} {
	set widget [Global main,widget]
	set clicks [clock clicks]
	set text [time {$widget wipe ; update idletasks} 100]
	set diff [expr [clock clicks] - $clicks]
	Debug "TestRedrawSpeed: 100 redraws in $diff clicks"
}

# BigMapExamine --
#
#	Called as NSWidget(OOP,examineCmd). Display a description of what the
#	character sees at a given cave location. Center the Widget in the
#	MicroMap window at the location also.
#
# Arguments:
#	oop					OOP ID of NSWidget.
#	y					y cave location.
#	x					x cave location.
#
# Results:
#	What happened.

proc BigMapExamine {oop y x} {

	global Windows

	[Global mapdetail,widget] center $y $x
	[Global mapdetail,widget] itemconfigure [Global mapdetail,cursor] -y $y -x $x
	NSMainWindow::StatusText $Windows(main,oop) [angband cave examine $y $x]
}
