# options.tcl --
#
#	Implements the Options Window and related commands.
#

namespace eval NSOptions {

variable Priv

# NSOptions::InitModule --
#
#	One-time-only-ever initialization.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitModule {} {

	global Angband
	global Windows
	variable Priv

	InitImageIfNeeded Image_Checked checked.gif
	InitImageIfNeeded Image_Unchecked unchecked.gif

	# If the tk/doc/options-index file does not exist, then create it.
	# Otherwise, recreate the file if it is older than tk/doc/options.txt.
	set file1 [file join $Angband(dirTK) doc options.txt]
	set file2 [file join $Angband(dirTK) doc options-index]
	if {![file exists $file2]} {
		GenOptionsIndex
	} else {
		set mtime1 [file mtime $file1]
		set mtime2 [file mtime $file2]
		if {$mtime1 > $mtime2} {
			GenOptionsIndex
		}
	}
	ReadOptionsIndex

	set Priv(page) {}
	lappend Priv(page) UserInterface "User Interface"
	lappend Priv(page) Disturbance "Disturbance"
	lappend Priv(page) GamePlay "Game-Play"
	lappend Priv(page) Efficiency "Efficiency"
	lappend Priv(page) Cheating "Cheating"
	lappend Priv(page) Other "Other"
	lappend Priv(page) ZAngband "Z"

	# Hack -- Verify we don't have any bad options
	foreach {page label} $Priv(page) {
		foreach option [GetPage $page] {
			Setting $option
		}
	}

	set Priv(showUnused) [Value settings,showUnused]

	# Create the Options Window
	NSObject::New NSOptions
}

# NSOptions::NSOptions --
#
#	Object constructor called by NSObject::New().
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc NSOptions oop {

	global Angband
	global NSOptions
	global Windows

	Info $oop ignoreChange 0
	Info $oop current -1
	Info $oop current2 -1

	trace variable NSOptions($oop,scale,value) w \
		"NSOptions::EntryTextVarProc $oop"

	InitWindow $oop

	Info $oop page none
	Info $oop tempWidget {}

	# Set the list
	Info $oop page UserInterface

	NSWindowManager::RegisterWindow options [Info $oop win] \
		"NSOptions::GeometryCmd $oop" "" "NSOptions::DisplayCmd $oop"

	#
	# Global list of application windows
	#

	set Windows(options,win) [Info $oop win]
	set Windows(options,class) NSOptions
	set Windows(options,oop) $oop
}

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

proc Info {oop info args} {

	global NSOptions

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

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

# NSOptions::InitWindow --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitWindow oop {

	global Windows
	variable Priv

	set win .options$oop
	toplevel $win
	wm title $win Options

	# Transient to main window
	wm transient $win $Windows(main,win)

	# Start out withdrawn (hidden)
	wm withdraw $win

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

	Info $oop win $win

	InitMenus $oop

	frame $win.divider1 \
		-borderwidth 1 -relief sunken -height 2

	# Tabs!
	set tabsId [NSObject::New NSTabs $win]
	foreach {page label} $Priv(page) {
		NSTabs::Add $tabsId $label
	}
	NSTabs::Info $tabsId invokeCmd "NSOptions::InvokeTab $oop"
	NSTabs::Info $tabsId active 1
	Info $oop tabsId $tabsId

	frame $win.frame \
		-borderwidth 1 -relief sunken
	set canvistId [NSObject::New NSCanvist $win.frame 20 400 200 \
		"NSOptions::NewItemCmd $oop" "NSOptions::HighlightItemCmd $oop"]
	set canvas [NSCanvist::Info $canvistId canvas]
	$canvas configure -background [NSColorPreferences::Get listBG]
	$canvas configure -yscrollcommand "$win.frame.scroll set"
	scrollbar $win.frame.scroll \
		-borderwidth 0 -command "$canvas yview" -orient vertical
	bind $win.frame.scroll <Map> \
		"eval %W set \[$canvas yview]"

	NSCanvist::Info $canvistId selectionCmd \
		"NSOptions::SelectionChanged $oop"

	# This call updates the list background color whenever the
	# global list background color changes
	Info $oop list,clientId \
		[NSValueManager::AddClient listBG "ListBackgroundChanged $canvas"]

	bind $canvas <Configure> \
		"NSOptions::Configure $oop $canvas"

	Info $oop canvistId $canvistId

	#
	# Help + divider
	#

	frame $win.frameHelp \
		-borderwidth 0
	frame $win.frameHelp.divider2 \
		-borderwidth 1 -height 2 -relief groove
	text $win.frameHelp.text \
		-width 40 -height 5 -background [NSColorPreferences::Get listBG] \
		-foreground White -cursor {} -wrap word \
		-font [Global font,fixed,normal] -takefocus 0 \
		-yscrollcommand "$win.frameHelp.yscroll set"
	scrollbar $win.frameHelp.yscroll \
		-borderwidth 0 -orient vertical -command "$win.frameHelp.text yview"
	bindtags $win.frameHelp.text [list $win.frameHelp.text $win all]

	# This call updates the list background color whenever the
	# global list background color changes
	Info $oop help,clientId \
		[NSValueManager::AddClient listBG "$win.frameHelp.text configure \
			-background \[NSColorPreferences::Get listBG]"]

	#
	# Statusbar
	#

	frame $win.statusBar -relief flat -borderwidth 0
	label $win.statusBar.label -anchor w -relief sunken -padx 2
	label $win.statusBar.label2 -anchor w -relief sunken -padx 2 -width 20
	pack $win.statusBar.label -side left -expand yes -fill both
	pack $win.statusBar.label2 -side right -expand no

	#
	# Geometry
	#

	grid rowconfigure $win.frame 0 -weight 1
	grid rowconfigure $win.frame 1 -weight 0
	grid columnconfigure $win.frame 0 -weight 1
	grid columnconfigure $win.frame 1 -weight 0

	grid $canvas \
		-row 0 -column 0 -rowspan 2 -columnspan 1 -sticky news
	grid $win.frame.scroll \
		-row 0 -column 1 -rowspan 2 -columnspan 1 -sticky ns

	grid rowconfigure $win.frameHelp 0 -weight 0
	grid rowconfigure $win.frameHelp 1 -weight 1
	grid columnconfigure $win.frameHelp 0 -weight 1
	grid columnconfigure $win.frameHelp 1 -weight 0

	grid $win.frameHelp.divider2 \
		-row 0 -column 0 -rowspan 1 -columnspan 2 -sticky ew \
		-pady 2
	grid $win.frameHelp.text \
		-row 1 -column 0 -rowspan 2 -columnspan 1 -sticky news
	grid $win.frameHelp.yscroll \
		-row 1 -column 1 -rowspan 2 -columnspan 1 -sticky ns

	grid rowconfigure $win 0 -weight 0 -minsize 0
	grid rowconfigure $win 1 -weight 0 -minsize 0
	grid rowconfigure $win 2 -weight 1 -minsize 0
	grid rowconfigure $win 3 -weight 0 -minsize 0
	grid rowconfigure $win 4 -weight 0 -minsize 0
	grid columnconfigure $win 0 -weight 1 -minsize 0

	grid $win.divider1 \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ew -pady 2
	grid [NSTabs::Info $tabsId canvas] \
		-row 1 -column 0 -rowspan 1 -columnspan 1 -sticky ew
	grid $win.frame \
		-row 2 -column 0 -rowspan 1 -columnspan 1 -sticky news
	grid $win.frameHelp \
		-row 3 -column 0 -rowspan 1 -columnspan 1 -sticky ew
	grid $win.statusBar \
		-row 4 -column 0 -rowspan 1 -columnspan 1 -sticky ew

	bind $win <KeyPress-Escape> "NSOptions::Close $oop"
	bind $win <Control-KeyPress-w> "NSOptions::Close $oop"
}

# NSOptions::InitMenus --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InitMenus oop {

	global NSOptions
	variable Priv

	set win [Info $oop win]
	set mod "Ctrl"

	#
	# Menu bar
	#

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

	#
	# Options Menu
	#

	NSObject::New NSMenu $mbar {-tearoff 0 -identifier MENU_OPTIONS}
	NSMenu::MenuInsertEntry $mbar -end MENUBAR {-type cascade -menu MENU_OPTIONS -label "Options" -underline 0 -identifier M_OPTIONS}

	set entries {}
	set i 1
	foreach {page label} $Priv(page) {
		lappend entries "-type radiobutton -label \"$label\" \
			-command \"NSOptions::SetOptions $oop $page\" \
			-variable NSOptions($oop,radio) -value $page \
			-accelerator $i -identifier E_PAGE_$i"
		bind $win <KeyPress-$i> "NSOptions::SetOptions $oop $page"
		incr i
	}
	lappend entries "-type separator"
	lappend entries "-type command -label \"Save As Default\" \
		-command \"NSOptions::WriteSettings $oop\" -underline 0 \
		-identifier E_WRITE_DEFAULT"
	lappend entries "-type command -label \"Load Default Settings\" \
		-command \"NSOptions::ReadSettings $oop\" -underline 0 \
		-identifier E_READ_DEFAULT"
	lappend entries "-type separator"
	lappend entries "-type checkbutton -label \"Show Unused Options\" \
		-variable NSOptions::Priv(showUnused) \
		-command \"NSOptions::ToggleShowUnused $oop\" \
		-identifier E_SHOW_UNUSED"
	lappend entries "-type separator"
	lappend entries "-type command -label \"Close\" \
		-command \"NSOptions::Close $oop\" -underline 0 \
		-accelerator $mod+W -identifier E_CLOSE"
	
	NSMenu::MenuInsertEntries $mbar -end MENU_OPTIONS $entries
}

# NSOptions::SetupMenus --
#
#	Description
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetupMenus {oop mbarID} {

	variable Priv

	set i 0
	foreach {page label} $Priv(page) {
		lappend identList E_PAGE_[incr i]
	}

	lappend identList E_WRITE_DEFAULT E_READ_DEFAULT E_SHOW_UNUSED E_CLOSE

	NSMenu::MenuEnable $mbarID $identList
}

# NSOptions::DisplayCmd --
#
#	Called by NSWindowManager::Display().
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc DisplayCmd {oop message first} {

	set canvistId [Info $oop canvistId]
	set canvas [NSCanvist::Info $canvistId canvas]

	switch -- $message {
		preDisplay {
			SetOptions $oop [Info $oop page]

			# Hack -- Restore the selection
			set current [Info $oop current2]
			if {$current != -1} {
				NSCanvist::UpdateSelection $canvistId $current {}
				NSCanvist::See $canvistId $current
			}
		}
		postDisplay {
		}
	}
}

# NSOptions::GeometryCmd --
#
#	Called by NSWindowManager::Setup(). Returns the desired (default)
#	geometry for the window.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc GeometryCmd oop {

	global Windows

	set win [Info $oop win]
	set winMain $Windows(main,win)
	set x [winfo x $winMain]
	set y [winfo y $winMain]
	set width [NSToplevel::ContentWidth $win [expr [NSToplevel::EdgeRight $winMain] - $x]]
	set height [NSToplevel::ContentHeight $win [expr [NSToplevel::EdgeBottom $winMain] - $y]]
	return ${width}x$height+$x+$y
}

# NSOptions::Close --
#
#	Called by WM_DELETE_WINDOW protocol handler.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	Instead of destroying the window, just withdraw (hide) it.

proc Close oop {

	Info $oop current2 [Info $oop current]
	angband keypress \033
}

# NSOptions::StatusBar --
#
#	Display text in the status bar, perhaps clearing it later.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc StatusBar {oop text zap} {

	set win [Info $oop win]
	set label $win.statusBar.label
	$label configure -text $text
	if $zap {
		NSUtils::ZapLabel $label
	}
}

# NSOptions::Toggle --
#
#	Toggles the value of an option.
#
# Arguments:
#	oop					OOP ID. See above.
#	tagOrId					canvas id for checkbox image.
#
# Results:
#	What happened.

proc Toggle {oop tagOrId} {

	set canvistId [Info $oop canvistId]
	set canvas [NSCanvist::Info $canvistId canvas]

	set row [NSCanvist::ItemRow $canvistId $tagOrId]
	set option [lindex [Info $oop settings] $row]
	set setting [Setting $option]

	# Hack -- If this is the first-ever setting of any cheating
	# option, including wizard mode or debug commands, then remind
	# the user about losing the high score.
	if {[string match cheat_* $option]} {
		angband player cheat cheat
		foreach {name boolean} [array get cheat] {
			if {$boolean} break
		}
		if {!$boolean} {
			set answer [tk_messageBox -icon warning -title "Cheat Warning" \
				-parent [Info $oop win] -message "Your character has never\
				cheated before!\n\nCheating means your character will not\
				get a\npermanent entry in the High Score list!\n\nAre you sure\
				you want to set this cheating option?" -type yesno]
			if {$answer == "no"} return
		}
	}

	if {[lindex $setting 0]} {
		Setting $option no
		set image Image_Unchecked
	} else {
		Setting $option yes
		set image Image_Checked
	}

	$canvas itemconfigure $tagOrId -image $image
}

# NSOptions::SetOptions --
#
#	Display options on given "page". Usually this is list of
#	on/off options handled by our NSCanvist list. But sometimes
#	the page does not use a list, instead using slider controls.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetOptions {oop page} {

	global NSOptions
	variable Priv

	set win [Info $oop win]
	set canvistId [Info $oop canvistId]
	set canvas [NSCanvist::Info $canvistId canvas]

	foreach widget [Info $oop tempWidget] {
		destroy $widget
	}
	Info $oop tempWidget {}

	eval grid forget [winfo children $win.frame]
	grid $canvas \
		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky news
	grid $win.frame.scroll \
		-row 0 -column 1 -rowspan 1 -columnspan 1 -sticky ns

	switch -- $page {

		UserInterface -
		Disturbance -
		GamePlay -
		Efficiency -
		Cheating -
		ZAngband {
			SetList $oop $page
		}

		Other {
			grid $canvas -rowspan 1
			grid $win.frame.scroll -rowspan 1

			set data {
				delay_factor 0 9 1
				hitpoint_warn 0 9 1
				autosave_freq 0 25000 50
				ambient_delay 1 1000 5
				monster_delay 1 1000 5
			}
			NSCanvist::Delete $canvistId 0 end
			set settings {}
			set scaleInfo {}
			foreach {setting from to resolution} $data {
				set value [lindex [Setting $setting] 0]
				set label [lindex [Setting $setting] 1]
				NSCanvist::Insert $canvistId end {} $label $value
				lappend settings $setting
				lappend scaleInfo [list $from $to $resolution]
			}
			Info $oop settings $settings
			Info $oop scaleInfo $scaleInfo

			set frame $win.frame.scale
			frame $frame \
				-borderwidth 0
			scale $frame.scale \
				-orient horizontal -label "(Nothing selected)" \
				-width 8 -sliderlength 12 -length 350 -from 1 -to 100 \
				-showvalue no -command "" -state disabled
			entry $frame.entry \
				-width 5 -state disabled -textvariable NSOptions($oop,scale,value)
			bind $frame.entry <FocusIn> {%W selection range 0 end}
			bind $frame.entry <KeyPress-Escape> "NSOptions::Close $oop"
			bindtags $frame.entry [list $frame.entry Entry all]
			$frame.scale set 50
			pack $frame.scale -side left
			pack $frame.entry -side left
			grid $frame -row 1 -column 0 -rowspan 1 -columnspan 1
			lappend NSOptions($oop,tempWidget) $frame
		}
	}

	wm title $win "Options ($page)"

	Info $oop page $page
	Info $oop radio $page

	set tabsId [Info $oop tabsId]
	set index [expr [lsearch -exact $Priv(page) $page] / 2]
	if {$index != [NSTabs::Info $tabsId current]} {
		NSTabs::Smaller $tabsId [NSTabs::Info $tabsId current]
		NSTabs::Bigger $tabsId $index
		NSTabs::Info $tabsId current $index
	}
}

# NSOptions::SetList --
#
#	Description.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SetList {oop page} {

	set canvistId [Info $oop canvistId]
	set canvas [NSCanvist::Info $canvistId canvas]

	NSCanvist::Delete $canvistId 0 end

	set showUnused [Value settings,showUnused]

	set settings {}
	foreach keyword [GetPage $page] {
		set unused [IsUnused $oop $keyword]
		if {!$showUnused && $unused} continue
		set setting [Setting $keyword]
		if {[lindex $setting 0]} {
			set image Image_Checked
		} else {
			set image Image_Unchecked
		}
		set desc [lindex $setting 1]
		if $unused {
			append desc " (unused)"
		}
		NSCanvist::Insert $canvistId end $image $desc ""

		lappend settings $keyword
	}
	Info $oop settings $settings
}

# NSOptions::GetPage --
#
#	Return a list of option-variable keywords.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc GetPage page {

	switch -- $page {

		UserInterface {
			return {
				rogue_like_commands
				quick_messages
				other_query_flag
				carry_query_flag
				use_old_target
				always_pickup
				always_repeat
				depth_in_feet
				stack_force_notes
				stack_force_costs
				show_labels
				show_weights
				show_choices
				show_details
				ring_bell
				use_color
				show_icons
				easy_floor
			}
		}
	
		Disturbance {
			return {
				find_ignore_stairs
				find_ignore_doors
				find_cut
				find_examine
				disturb_move
				disturb_near
				disturb_panel
				disturb_state
				disturb_minor
				alert_hitpoint
				alert_failure
			}
		}
	
		GamePlay {
			return {
				auto_haggle
				auto_scum
				stack_allow_items
				stack_allow_wands
				testing_stack
				testing_carry
				expand_look
				expand_list
				view_perma_grids
				view_torch_grids
				dungeon_align
				dungeon_stair
				flow_by_sound
				flow_by_smell
				smart_learn
				smart_cheat
				easy_open
				easy_disarm
				remember_recall
			}
		}
	
		Efficiency {
			return {
				view_reduce_lite
				view_reduce_view
				avoid_abort
				avoid_other
				flush_failure
				flush_disturb
				fresh_before
				fresh_after
				fresh_message
				compress_savefile
				hilite_player
				view_yellow_lite
				view_bright_lite
				view_granite_lite
				view_special_lite
				scroll_follow
			}
		}

		Cheating {
			return {
				cheat_peek
				cheat_hear
				cheat_room
				cheat_xtra
				cheat_know
				cheat_live
			}
		}

		Other {
			return {
				delay_factor
				hitpoint_warn
				autosave_freq
				ambient_delay
				monster_delay
			}
		}

		ZAngband {
			return {
				disturb_other
				last_words
				speak_unique
				small_levels
				always_small_levels
				empty_levels
				player_symbols
				equippy_chars
				skip_mutations
				plain_descriptions
				stupid_monsters
				auto_destroy
				confirm_wear
				confirm_stairs
				disturb_pets
				autosave_l
				autosave_t
			}
		}
	}
}

# NSOptions::SelectionChanged --
#
#	Called when the list selection changes.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc SelectionChanged {oop canvistId select deselect} {

	set win [Info $oop win]

	Info $oop ignoreChange 1

$win.frameHelp.text delete 1.0 end

	if {[llength $select]} {
		set row [lindex $select 0]
		Info $oop current $row

		set keyword [lindex [Info $oop settings] $row] 
		$win.statusBar.label2 configure -text $keyword

DisplaySettingHelp $oop $keyword

		if {[winfo exists $win.frame.scale]} {
			$win.frame.scale.entry configure -state normal
			set scaleInfo [lindex [Info $oop scaleInfo] $row]
			$win.frame.scale.scale configure \
				-from [lindex $scaleInfo 0] -to [lindex $scaleInfo 1] \
				-resolution [lindex $scaleInfo 2] \
				-label [lindex [Setting $keyword] 1] -state normal \
				-command "NSOptions::ScaleCmd $oop $keyword"
			Info $oop scale,value [lindex [Setting $keyword] 0]
			$win.frame.scale.scale set [lindex [Setting $keyword] 0]
		}

	} else {
		Info $oop current -1

		$win.statusBar.label2 configure -text ""

		if {[winfo exists $win.frame.scale]} {
			$win.frame.scale.entry configure -state disabled
			Info $oop scale,value 1
			$win.frame.scale.scale set 1
			$win.frame.scale.scale configure \
				-from 1 -to 100 \
				-label "(Nothing selected)" -state disabled \
				-command ""
		}
	}

	# Must update now, because scale's command is called at
	# idle time!
	update idletasks

	Info $oop ignoreChange 0
}

# NSOptions::NewItemCmd --
#
#	Called by NSCanvist::InsertItem() to create a list row.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc NewItemCmd {oop canvistId y image text value} {

	set c [NSCanvist::Info $canvistId canvas]
	set lineHeight [NSCanvist::Info $canvistId rowHgt]

	set font [Global font,fixed,normal]
	set fw [font measure $font $text]
	set fh [font metrics $font -linespace]
	set diff [expr int([expr ($lineHeight - $fh) / 2])]

	# Selection rectangle inside row
	lappend itemIdList [$c create rectangle 2 [expr $y + 2] \
		[expr [winfo width $c] - 3] [expr $y + $lineHeight - 2] \
		-fill "" -outline "" -tags {enabled selrect} -width 2.0]

	#
	# Checkbox
	#

	if {$image != {}} {
		set iw [image width $image]
		set ih [image height $image]
		set idiff [expr int([expr (20 - $iw) / 2])]
		set ydiff [expr int([expr ($lineHeight - $ih) / 2])]
		lappend itemIdList [$c create image $idiff [expr $y + $ydiff] \
			-image $image -anchor nw -tags enabled]

		set id [lindex $itemIdList end]
		$c bind $id <Button-1> "::NSOptions::Toggle $oop $id ; break"
	}

	#
	# Text
	#

	if {[string length $image]} {
		set textLeft 21
	} else {
		set textLeft 4
	}
	lappend itemIdList [$c create text $textLeft [expr $y + $diff] \
		-text $text -anchor nw -font $font -fill White -tags enabled]

	#
	# Value
	#

	if {$value != ""} {
		lappend itemIdList [$c create text [expr [winfo width $c] - 4] \
			[expr $y + $diff] -text $value -anchor ne -font $font \
			-fill White -tags value]
	}

	return $itemIdList
}

# NSOptions::HighlightItemCmd --
#
#	Called by NSCanvist::Select() to highlight a row.
#
# Arguments:
#	oop					OOP ID. See above.
#	canvistId					OOP ID of NSCanvist object.
#	state					1 or 0 highlight state.
#	args					List of canvas item ids
#
# Results:
#	What happened.

proc HighlightItemCmd {oop canvistId state args} {

	set canvas [NSCanvist::Info $canvistId canvas]
	set itemIdList $args

	set idRect [lindex $itemIdList 0]

	if {[NSUtils::HasFocus $canvas]} {
		set fill [NSColorPreferences::Get listHilite]
	} else {
		set fill [NSColorPreferences::Get listInactive]
	}

	if $state {
		$canvas itemconfigure $idRect -outline $fill

	} else {
		$canvas itemconfigure $idRect -outline {}
	}
}

# NSOptions::Configure --
#
#	Called as a <Configure> event script. Resizes the selection rectangles
#	so they fit properly.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc Configure {oop canvas} {

	foreach itemId [$canvas find withtag selrect] {
		set coords [$canvas coords $itemId]
		set right [expr [winfo width $canvas] - 3]
		set coords [lreplace $coords 2 2 $right]
		eval $canvas coords $itemId $coords
	}

	foreach itemId [$canvas find withtag value] {
		set coords [$canvas coords $itemId]
		set right [expr [winfo width $canvas] - 4]
		set coords [lreplace $coords 0 0 $right]
		eval $canvas coords $itemId $coords
	}
}

# NSOptions::InvokeTab --
#
#	Called when a tab is clicked.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc InvokeTab {oop tabsId tabId} {

	variable Priv

	set index [lsearch -exact [NSTabs::Info $tabsId id] $tabId]
	SetOptions $oop [lindex $Priv(page) [expr $index * 2]]
}

# NSOptions::ScaleCmd --
#
#	Called when the value of a scale changes.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ScaleCmd {oop keyword value} {

	if {[Info $oop ignoreChange]} return

	# Update the game setting
	Setting $keyword $value

	# Update the list
	UpdateList $oop

	# Update the entry
	Info $oop scale,value $value
}

# NSStore::EntryTextVarProc --
#
#	Trace variable callback on NSOptions($oop,scale,value).
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc EntryTextVarProc {oop name1 name2 op} {

	if {[Info $oop ignoreChange]} return

	set value [Info $oop scale,value]
	set scaleInfo [lindex [Info $oop scaleInfo] [Info $oop current]]
	set from [lindex $scaleInfo 0]
	set to [lindex $scaleInfo 1]

	regsub -all \[^0-9] $value "" value
	if {$value != ""} {
		if {$value < $from} {set value $from}
		if {$value > $to} {set value $to}
		[Info $oop win].frame.scale.scale set $value
	}

	Info $oop scale,value $value
}

# NSOptions::UpdateList --
#
#	Configure the Widget canvas item on the row of the list.
#	When the scale value changes, we want to update the list.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc UpdateList oop {

	set canvistId [Info $oop canvistId]
	set canvas [NSCanvist::Info $canvistId canvas]
	set row [Info $oop current]

	set keyword [lindex [Info $oop settings] $row]
	set rowTag [lindex [NSCanvist::Info $canvistId rowTags] $row]
	set itemIdList [$canvas find withtag $rowTag]
	set itemId [FindItemByTag $canvas $itemIdList value]
	$canvas itemconfigure $itemId -text [lindex [Setting $keyword] 0]
}

# NSOptions::DisplaySettingHelp --
#
#	Display the help text for the given setting keyword.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc DisplaySettingHelp {oop keyword} {

	set text [Info $oop win].frameHelp.text

	set setting [Setting $keyword]
	set desc [lindex $setting 1]
	if {[IsUnused $oop $keyword]} {
		append desc " (unused)"
	}
	set help [GetSettingHelp $oop $keyword]

	$text insert end $desc\n
	$text insert end $help tag1
	$text tag configure tag1 \
		-lmargin1 19 -lmargin2 19 -rmargin 0
}

# NSOptions::GetSettingHelp --
#
#	Scan the options.txt file for the description of a setting.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc GetSettingHelp {oop keyword} {

	global Angband
	variable Priv

	set index [lsearch -exact $Priv(optionsIndex) $keyword]
	if {$index == -1} {
		return ""
	}
	set lineNo [lindex $Priv(optionsIndex) [expr $index + 1]]

	# Open the tk/doc/options.txt file. Read and discard lines
	# up to and including the line determined above. Then read until
	# a blank line (or end-of-file).

	if {[catch {open [file join $Angband(dirTK) doc options.txt]} fileId]} {
		return ""
	}
	catch {
		set result ""
		for {set i 1} {$i <= $lineNo} {incr i} {
			gets $fileId
		}
		while 1 {
			set count [gets $fileId lineBuf]
			if {$count <= 0} break
			append result "[string trim $lineBuf] "
		}
	}
	close $fileId

	return $result
}

# NSOptions::GenOptionsIndex --
#
#	Reads the tk/doc/options.txt file, and for each keyword writes a
#	line to tk/doc/options-index. Each line consists of two elements:
#	the setting keyword and the line it was found on.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc GenOptionsIndex {} {

	global Angband

	if {[catch {open [file join $Angband(dirTK) doc options.txt]} readId]} {
		return ""
	}
	if {[catch {open [file join $Angband(dirTK) doc options-index] \
		[list CREAT WRONLY TRUNC]} writeId]} {
		close $readId
		return
	}

	catch {
		set lineNo 1
		set saw_blank 1
		while 1 {
			set count [gets $readId lineBuf]
			if {$count < 0} break
			if {$count == 0} {
				set saw_blank 1
			} else {
				if $saw_blank {
					set lineBuf [string trim $lineBuf]
					puts $writeId "$lineBuf $lineNo"
				}
				set saw_blank 0
			}
			incr lineNo
		}
	}

	close $writeId
	close $readId
}

# NSOptions::ReadOptionsIndex --
#
#	Reads the tk/doc/options-index file.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ReadOptionsIndex {} {

	global Angband
	variable Priv

	set Priv(optionsIndex) {}

	if {[catch {open [file join $Angband(dirTK) doc options-index]} fileId]} {
		return
	}
	catch {
		set lineNo 0
		while 1 {
			set count [gets $fileId lineBuf]
			if {$count <= 0} break
			lappend Priv(optionsIndex) [lindex $lineBuf 0] [lindex $lineBuf 1]
		}
	}
	close $fileId
}

# NSOptions::WriteSettings --
#
#	Save the current settings to the tk/config/setting file.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc WriteSettings oop {

	global Angband
	variable Priv

	set answer [tk_messageBox -title "Write Settings" -icon question \
		-parent [Info $oop win] \
		-type yesno -message "Really make these the default settings?"]
	if {$answer == "no"} return

	if {[catch {open [file join $Angband(dirTK) config setting] w} fileId]} {
		set msg "The following error occurred while attempting to open "
		append msg "the \"setting\" file for writing:\n\n$fileId"
		tk_messageBox -title Oops -parent [Info $oop win] -message $msg
		return
	}

	StatusBar $oop "Writing settings..." 0

	foreach {page label} $Priv(page) {
		if {$page == "Cheating"} continue
		foreach keyword [GetPage $page] {
			lappend data $keyword
		}
	}

	puts $fileId "# Automatically generated. Do not edit.\n"

	foreach keyword $data {
		set setting [Setting $keyword]
		set value [lindex $setting 0]
		puts $fileId "setg::one $keyword \"$value\""
	}

	close $fileId

	StatusBar $oop "Done." 1
}

# NSOptions::ReadSettings --
#
#	Override current settings with those in the tk/config/setting file.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ReadSettings oop {

	global Angband

	set fileName [file join $Angband(dirTK) config setting]
	if {![file exists $fileName]} {
		tk_messageBox -title "Read Settings" -icon info -type ok \
			-parent [Info $oop win] \
			-message "There are no default settings to restore."
		return
	}

	set answer [tk_messageBox -title "Read Settings" -icon question \
		-type yesno -parent [Info $oop win] \
		-message "Really read the saved settings?"]
	if {$answer == "no"} return

	uplevel #0 source $fileName

	SetOptions $oop [Info $oop page]

	StatusBar $oop "Done." 1
}

# NSOptions::ToggleShowUnused --
#
#	Hide or show unused settings.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc ToggleShowUnused oop {

	variable Priv

	Value settings,showUnused $Priv(showUnused)
	SetOptions $oop [Info $oop page]
}

# NSOptions::IsUnused --
#
#	Return 1 if the given setting is an unused setting.
#
# Arguments:
#	arg1					about arg1
#
# Results:
#	What happened.

proc IsUnused {oop keyword} {

	switch -- $keyword {
		avoid_other -
		equippy_chars -
		hidden_player -
		hilite_player -
		player_symbols -
		show_choices -
		show_details -
		skip_mutations -
		use_color {
			return 1
		}
	}

	# This is a useful setting
	return 0
}

# namespace eval NSOptions
}
