1# File: recall.tcl
2
3# Purpose: the Recall Window and related commands
4
5#
6# Copyright (c) 1997-2001 Tim Baker
7#
8# This software may be copied and distributed for educational, research, and
9# not for profit purposes provided that this copyright and statement are
10# included in all such copies.
11#
12
13namespace eval NSRecall {
14
15	variable Priv
16
17# namespace eval NSRecall
18}
19
20# NSRecall::InitModule --
21#
22#	One-time-only-ever initialization.
23#
24# Arguments:
25#	arg1					about arg1
26#
27# Results:
28#	What happened.
29
30proc NSRecall::InitModule {} {
31
32	variable Priv
33
34	set Priv(icon,valid) 0
35	set Priv(icon,known) 0
36
37	set oop [NSObject::New NSRecall]
38
39	# Update ourself when the font for the Recall Window changes
40	NSValueManager::AddClient font,recall \
41		"NSRecall::ValueChanged_font_recall $oop"
42
43	return
44}
45
46# NSRecall::NSRecall --
47#
48#	Object constructor called by NSObject::New().
49#
50# Arguments:
51#	oop					OOP ID. See above.
52#
53# Results:
54#	What happened.
55
56proc NSRecall::NSRecall {oop} {
57
58	Info $oop showIcon [Value recall,showicon]
59
60	InitWindow $oop
61
62	set win [Info $oop win]
63
64	NSWindowManager::RegisterWindow recall $win \
65		"NSRecall::GeometryCmd $oop" \
66		"" \
67		"NSRecall::DisplayCmd $oop"
68
69	# If the Choice Window is displayed, we don't show choices in
70	# the Recall Window.
71	Info $oop clientId,choicewindow \
72		[NSValueManager::AddClient choicewindow,show {
73			NSRecall::SetHook [Global recall,oop] ""
74		}]
75
76	if {$::DEBUG} {
77		set ::debug_display 0
78	}
79
80	Info $oop hook ""
81	Info $oop busy 0
82	Info $oop expanded 0
83	Info $oop current ""
84	Info $oop inConfigure 0
85
86	# Kind of information currently displayed
87	Info $oop display ""
88
89Info $oop monsterMem ""
90
91	#
92	# Global list of application windows
93	#
94
95	Global recall,oop $oop
96	Window recall $win
97
98	return
99}
100
101# NSRecall::Info --
102#
103#	Query and modify info.
104#
105# Arguments:
106#	arg1					about arg1
107#
108# Results:
109#	What happened.
110
111proc NSRecall::Info {oop info args} {
112
113	global NSRecall
114
115	# Verify the object
116	NSObject::CheckObject NSRecall $oop
117
118	# Set info
119	if {[llength $args]} {
120		switch -- $info {
121			default {
122				set NSRecall($oop,$info) [lindex $args 0]
123			}
124		}
125
126	# Get info
127	} else {
128		switch -- $info {
129			default {
130				return $NSRecall($oop,$info)
131			}
132		}
133	}
134
135	return
136}
137
138# NSRecall::InitWindow --
139#
140#	Create a recall window.
141#
142# Arguments:
143#	oop					OOP ID. See above.
144#
145# Results:
146#	What happened.
147
148proc NSRecall::InitWindow {oop} {
149
150	set win .recall$oop
151	toplevel $win
152	wm title $win "Recall"
153
154	wm transient $win [Window main]
155
156	# Feed the Term when keys are pressed
157	Term_KeyPress_Bind $win
158
159	# Do stuff when window closes
160	wm protocol $win WM_DELETE_WINDOW "NSRecall::Close $oop"
161
162	# Start out withdrawn (hidden)
163	wm withdraw $win
164
165	# Turn off geometry propagation for the window
166	pack propagate $win no
167
168	# Set instance variables
169	Info $oop win $win
170
171	set frame $win.frame
172	frame $frame -relief sunken -borderwidth 1 -background Black
173
174	# Canvas to display icon
175	set iconSize [expr {[icon size] + 8}]
176	set canvas $frame.icon
177	canvas $canvas \
178		-borderwidth 0 -width $iconSize -height $iconSize -background Black \
179		-highlightthickness 0
180if 0 {
181	$canvas create widget \
182		6 6 -tags icon
183}
184	$canvas create rectangle \
185		4 4 [expr {6 + [icon size] + 1}] [expr {6 + [icon size] + 1}] \
186		-outline Black -tags focus
187if 0 {
188# Problems with highlight when Knowledge window appears, so skip it
189	$canvas bind icon <Enter> {
190#		%W itemconfigure focus -outline gray60
191	}
192	$canvas bind icon <Leave> {
193		%W itemconfigure focus -outline Black
194	}
195	$canvas bind icon <ButtonPress-1> "
196		$canvas move icon 1 1
197		set CanvasButtonDown 1
198	"
199	$canvas bind icon <Button1-Enter> "
200		$canvas move icon 1 1
201		set CanvasButtonDown 1
202	"
203	$canvas bind icon <Button1-Leave> "
204		$canvas move icon -1 -1
205		set CanvasButtonDown 0
206	"
207	$canvas bind icon <ButtonRelease-1> "
208		if {\$CanvasButtonDown} {
209			$canvas move icon -1 -1
210			update idletasks
211			NSRecall::DisplayKnowledge $oop
212		}
213	"
214}
215	# Create an arrow which appears when there is content out of site
216	set x [expr {$iconSize / 2}]
217	$canvas create polygon [expr {$x - 3}] 46 [expr {$x + 3}] 46 \
218		$x 49 -fill Red -outline Red -tags arrow
219
220	set wrap word
221	text $frame.text \
222		-wrap $wrap -width 1 -height 1 -font [Value font,recall] \
223		-borderwidth 0 -setgrid no -highlightthickness 0 \
224		-padx 4 -pady 2 -background Black -foreground White -cursor ""
225	bindtags $frame.text [list $frame.text $win all]
226
227	pack $frame \
228		-expand yes -fill both
229
230	grid rowconfig $frame 0 -weight 1
231	grid columnconfig $frame 0 -weight 0
232	grid columnconfig $frame 1 -weight 1
233
234	grid $frame.icon -in $frame \
235		-row 0 -column 0 -rowspan 1 -columnspan 1 -sticky ns
236	grid $frame.text -in $frame \
237		-row 0 -column 1 -rowspan 1 -columnspan 1 -sticky news
238
239	if {![Info $oop showIcon]} {
240		grid remove $frame.icon
241	}
242
243	# Set instance variables
244	Info $oop icon $frame.icon
245	Info $oop text $frame.text
246
247	# Window expands and contracts as the mouse enters and leaves it
248	bindtags $win [concat [bindtags $win] RecallBindTag]
249	bind RecallBindTag <Enter> "NSRecall::Expand $oop"
250	bind RecallBindTag <Leave> "NSRecall::Contract $oop"
251
252	# When the window changes size, reposition the indicator arrow
253	bind $frame.text <Configure> \
254		"NSRecall::Configure $oop"
255
256	# Fiddle with the selection for list behaviour
257	$frame.text tag configure HOT -foreground White \
258		-background [Value listHilite]
259
260	$frame.text tag bind HOT <ButtonPress-1> \
261		"NSRecall::Invoke $oop \[$frame.text index {@%x,%y linestart}]"
262	$frame.text tag bind TEXT <Motion> \
263		"NSRecall::Motion $oop \[$frame.text index {@%x,%y linestart}]"
264	$frame.text tag bind HOT <Leave> \
265		"NSRecall::Motion $oop {}"
266
267	if {[Platform unix]} {
268
269		# When the inactive window is clicked, I get a <Leave> event
270		# followed by an <Enter> event. The <Leave> Contract()'s the window
271		# and removes the highlight (if any).
272
273		bind RecallWindowBindTag <Leave> "
274			if {!\[NSRecall::HasCursor $oop]} {
275				NSRecall::Contract $oop
276			}
277		"
278		$frame.text tag bind HOT <Leave> "
279			if {!\[NSRecall::CursorHot $oop %x %y]} {
280				NSRecall::Motion $oop {}
281			}
282		"
283		bind $win <Leave> "
284			if {!\[NSRecall::HasCursor $oop]} {
285				NSRecall::Motion $oop {}
286			}
287		"
288
289		proc CursorHot {oop x y} {
290			set text [Info $oop text]
291			if {![llength [$text tag ranges HOT]]} {return 0}
292			set index [$text index @$x,$y]
293			if {[$text compare $index < HOT.first] ||
294				[$text compare $index > HOT.last]} {return 0}
295			return 1
296		}
297	}
298
299	#
300	# Context Menu
301	#
302
303	set menu $win.context
304	menu $menu -tearoff 0
305	bind $frame.icon <ButtonPress-3> \
306		"NSRecall::ContextMenu $oop $menu %X %Y"
307	bind $frame.text <ButtonPress-3> \
308		"NSRecall::ContextMenu $oop $menu %X %Y"
309
310	return
311}
312
313# NSRecall::DisplayCmd --
314#
315#	Called by NSWindowManager::Display().
316#
317# Arguments:
318#	arg1					about arg1
319#
320# Results:
321#	What happened.
322
323proc NSRecall::DisplayCmd {oop message first args} {
324
325	switch -- $message {
326		preDisplay {
327		}
328		postDisplay {
329			Value recall,show 1
330		}
331		postWithdraw {
332			SetHook $oop ""
333			Value recall,show 0
334		}
335	}
336
337	return
338}
339
340# NSRecall::GeometryCmd --
341#
342#	Called by NSWindowManager::Setup(). Returns the desired (default)
343#	geometry for the window.
344#
345# Arguments:
346#	arg1					about arg1
347#
348# Results:
349#	What happened.
350
351proc NSRecall::GeometryCmd {oop} {
352
353	set win [Info $oop win]
354	set winMain [Window main]
355	set spacing 0
356
357	set left 0
358	set top 0
359	set right [winfo screenwidth .]
360	set bottom [winfo screenheight .]
361
362	set x [NSToplevel::FrameLeft $winMain]
363	if {[Value choicewindow,show]} {
364		set width [NSToplevel::ContentWidth $win \
365			[expr {[NSToplevel::TotalWidth $winMain] / 2}]]
366	} else {
367		set width [NSToplevel::TotalWidth $winMain]
368		if {$width > 400} {
369			set width 400
370		}
371		set width [NSToplevel::ContentWidth $win $width]
372	}
373
374	set y [expr {[NSToplevel::FrameBottom $winMain] + $spacing}]
375	if {$bottom - $y < 100} {
376		set y [expr {$bottom - 100}]
377		set height [NSToplevel::ContentHeight $win 100]
378	} elseif {($y + [NSToplevel::TotalHeight $win]) < $bottom} {
379		set height [winfo height $win]
380	} else {
381		set height [expr {$bottom - [NSToplevel::FrameBottom $winMain]}]
382		set height [NSToplevel::ContentHeight $win $height]
383	}
384
385	return ${width}x$height+$x+$y
386}
387
388# NSRecall::Close --
389#
390#	Description.
391#
392# Arguments:
393#	oop					OOP ID. See above.
394#
395# Results:
396#	What happened.
397
398proc NSRecall::Close {oop} {
399
400	NSWindowManager::Undisplay recall
401
402	return
403}
404
405
406
407# NSRecall::RecallSpell --
408#
409#	Show info about a spell.
410#
411# Arguments:
412#	arg1					about arg1
413#
414# Results:
415#	What happened.
416
417proc NSRecall::RecallSpell {bookNum index} {
418
419	variable Priv
420
421	if {![Value recall,show]} return
422
423	# Hack -- Get the object id
424	set oop [Global recall,oop]
425
426	# If we are in "list mode", don't clobber the text
427	if {[string length [Info $oop hook]]} return
428
429	# Get information about the spell
430	angband spell info $bookNum $index attrib
431
432	# Get the book icon
433	set icon [angband k_info info $bookNum icon]
434
435	# Color
436	switch -- $attrib(info) {
437		unknown {
438			set color gray70
439
440			# The character can learn this spell
441			if {[angband player new_spells] &&
442				($attrib(level) <= [angband player level])} {
443				set color [Value TERM_L_GREEN]
444			}
445		}
446		untried {
447			set color [Value TERM_L_BLUE]
448		}
449		default {
450			set color White
451		}
452	}
453
454	# Get the name
455	set name $attrib(name):
456
457	# Get the memory
458	set memory [angband spell memory $bookNum $index]
459
460	# Extra info
461	if {[string length $memory]} {
462		append memory \n
463	}
464	append memory "Level $attrib(level)  Mana $attrib(mana) \
465		Fail $attrib(chance)%"
466	if {[string length $attrib(info)]} {
467		append memory "\n$attrib(info)"
468	}
469
470	# Set the text
471	SetText $oop $icon $color $name $memory
472
473	return
474}
475
476
477# NSRecall::SetText --
478#
479#	Description.
480#
481# Arguments:
482#	oop					OOP ID. See above.
483#
484# Results:
485#	What happened.
486
487proc NSRecall::SetText {oop icon color title text} {
488
489	global NSRecall
490	variable Priv
491
492	set win [Info $oop win]
493	set textBox [Info $oop text]
494
495	# If we are in "list mode", then do not set the text. This may
496	# happen if we are waiting for an object to be chosen, and the
497	# user highlights an object in the Inventory Window, which would
498	# ordinarily display the object memory.
499	if {[string length [Info $oop hook]]} {
500		return
501	}
502
503	# Display the icon
504	[Info $oop icon] itemconfigure icon -assign $icon
505
506	# Delete
507	$textBox delete 1.0 end
508
509	# Insert title if any
510	if {[string length $title]} {
511
512		# Title (color?)
513		$textBox insert end $title\n
514		$textBox tag add TAG_STYLE 1.0 {end -1 chars}
515		$textBox tag configure TAG_STYLE -foreground $color
516	}
517
518	# Insert text if any
519	set text [string trim $text]
520	if {[string length $text]} {
521
522		# Text
523		$textBox insert end $text
524	}
525
526	set Priv(icon,valid) 0
527
528	# Synchronize the indicator arrow
529	ContentChanged $oop
530
531	return
532}
533
534# NSRecall::IconChanged --
535#
536#	The icon of the recalled monster/object is displayed in the
537#	Recall Window. If that monster or object is assigned a different
538#	icon, we want to update the display. This is called as a
539#	qebind command on the "Assign" quasi-event.
540#
541# Arguments:
542#	oop					OOP ID. See above.
543#
544# Results:
545#	What happened.
546
547proc NSRecall::IconChanged {oop to toindex assign} {
548
549	variable Priv
550
551	if {!$Priv(icon,valid)} return
552	if {[string equal $to $Priv(icon,to)] && ($toindex == $Priv(icon,toindex))} {
553		[Info $oop icon] itemconfigure icon -assign $assign
554	}
555
556	return
557}
558
559
560# NSRecall::DisplayKnowledge --
561#
562#	Display the Knowledge Window for the displayed monster or object.
563#
564# Arguments:
565#	arg1					about arg1
566#
567# Results:
568#	What happened.
569
570proc NSRecall::DisplayKnowledge {oop} {
571
572	variable Priv
573
574	if {!$Priv(icon,valid)} return
575
576	# This can't work when an unknown flavored object is displayed
577	if {!$Priv(icon,known)} return
578
579	if {[string compare $Priv(icon,to) monster] &&
580		[string compare $Priv(icon,to) object]} {
581		return
582	}
583
584	angband_display knowledge show $Priv(icon,to) $Priv(icon,toindex)
585
586	return
587}
588
589
590# NSRecall::SetHook --
591#
592#	Set the hook.
593#
594# Arguments:
595#	arg1					about arg1
596#
597# Results:
598#	What happened.
599
600proc NSRecall::SetHook {oop hook} {
601
602	if {[string length $hook]} {
603		Info $oop hook NSRecall::$hook
604		CallHook $oop open
605		if {$::DEBUG} {
606			set ::debug_display 1
607		}
608	} elseif {[string length [Info $oop hook]]} {
609		Info $oop hook ""
610		Restore $oop
611		if {$::DEBUG} {
612			set ::debug_display 0
613		}
614	}
615
616	return
617}
618
619# NSRecall::CallHook --
620#
621#	Call the hook.
622#
623# Arguments:
624#	arg1					about arg1
625#
626# Results:
627#	What happened.
628
629proc NSRecall::CallHook {oop message args} {
630
631	return [uplevel #0 [Info $oop hook] $oop $message $args]
632}
633
634# NSRecall::Fresh_Display --
635#
636#	Calls the hook to set the list, if required. Called as a command
637#	on the "Term-fresh" quasi-event.
638#
639# Arguments:
640#	arg1					about arg1
641#
642# Results:
643#	What happened.
644
645proc NSRecall::Fresh_Display {oop} {
646
647	ASSERT {$::debug_display == 1} \
648		"Fresh_Display called with debug_display=0!"
649
650	CallHook $oop fresh
651
652	# If the cursor is inside the Recall Window, we will attempt to
653	# expand it.
654	set pointerx [winfo pointerx .]
655	set pointery [winfo pointery .]
656	set toplevel [winfo containing $pointerx $pointery]
657	if {[string length $toplevel] && \
658		[string equal [winfo toplevel $toplevel] [Info $oop win]]} {
659		Expand $oop
660	}
661
662	return
663}
664
665# NSRecall::SetList --
666#
667#	Clears the recall text, sets the icon to "none 0" and calls the
668#	hook to set the text.
669#
670# Arguments:
671#	arg1					about arg1
672#
673# Results:
674#	What happened.
675
676proc NSRecall::SetList {oop} {
677
678	set win [Info $oop win]
679	set textBox [Info $oop text]
680
681	# Clear the text
682	$textBox delete 1.0 end
683
684	# Clear the icon
685	[Info $oop icon] itemconfigure icon -assign {icon none 0}
686
687	# Call the hook to set the list
688	CallHook $oop set_list
689
690	# Something is displayed
691	Info $oop display something
692
693	# No item is highlighted
694	Info $oop current ""
695
696	# Synchronize the indicator arrow
697	ContentChanged $oop
698
699	return
700}
701
702# NSRecall::Invoke --
703#
704#	Called when a list item is clicked.
705#
706# Arguments:
707#	arg1					about arg1
708#
709# Results:
710#	What happened.
711
712proc NSRecall::Invoke {oop index} {
713
714	set textBox [Info $oop text]
715set index [Info $oop current]
716	set row [expr {[lindex [split $index .] 0] - 1}]
717
718	CallHook $oop invoke $row
719
720	return
721}
722
723# NSRecall::Motion --
724#
725#	Called when the mouse moves in a list item.
726#
727# Arguments:
728#	arg1					about arg1
729#
730# Results:
731#	What happened.
732
733proc NSRecall::Motion {oop index} {
734
735	# If you invoke an item, hold down the mouse, and drag...
736	if {![string length [Info $oop hook]]} return
737
738	# No tracking while menu is up
739	if {[Info $oop busy]} return
740
741	# See if the item has changed
742	if {$index == [Info $oop current]} return
743
744	# An item is highlighted
745	if {[string length [Info $oop current]]} {
746
747		# Remove highlighting
748		UnhighlightItem $oop [Info $oop current]
749	}
750
751	# An item is under the pointer
752	if {[string length $index]} {
753
754		# Highlight the item
755		HighlightItem $oop $index
756	}
757
758	# Remember which item is highlighted
759	Info $oop current $index
760
761	return
762}
763
764# NSRecall::HighlightItem --
765#
766#	Highlights a list item.
767#
768# Arguments:
769#	arg1					about arg1
770#
771# Results:
772#	What happened.
773
774proc NSRecall::HighlightItem {oop index} {
775
776	set textBox [Info $oop text]
777	set row [expr {[lindex [split $index .] 0] - 1}]
778
779	# Highlight the item
780	$textBox tag add HOT $index "$index lineend"
781	$textBox tag raise HOT
782
783	# Call the hook (to set the icon, for example)
784	CallHook $oop highlight $row
785
786	return
787}
788
789# NSRecall::UnhighlightItem --
790#
791#	Removes highlighting from a list item.
792#
793# Arguments:
794#	arg1					about arg1
795#
796# Results:
797#	What happened.
798
799proc NSRecall::UnhighlightItem {oop index} {
800
801	set win [Info $oop win]
802	set textBox [Info $oop text]
803
804	# Unhighlight the item
805	$textBox tag remove HOT 1.0 end
806
807	# Clear the icon
808	[Info $oop icon] itemconfigure icon -assign {icon none 0}
809
810	return
811}
812
813# NSRecall::HasCursor --
814#
815#	See if the cursor is over the window.
816#
817# Arguments:
818#	arg1					about arg1
819#
820# Results:
821#	What happened.
822
823proc NSRecall::HasCursor {oop} {
824
825	set pointerx [winfo pointerx .]
826	set pointery [winfo pointery .]
827	set window [winfo containing $pointerx $pointery]
828	if {![string length $window]} {
829		return 0
830	}
831	if {[string compare [winfo toplevel $window] [Info $oop win]]} {
832		return 0
833	}
834	return 1
835}
836
837# NSRecall::Expand --
838#
839#	Resizes the Recall Window to display all of the information in it.
840#	Does nothing if the window is already expanded.
841#
842# Arguments:
843#	arg1					about arg1
844#
845# Results:
846#	What happened.
847
848proc NSRecall::Expand {oop} {
849
850	variable Priv
851
852#	if {![string length [Info $oop hook]]} return
853	if {[Info $oop busy]} return
854	if {[Info $oop expanded]} return
855
856	set win [Info $oop win]
857	set textBox [Info $oop text]
858
859	set textHeight [winfo height $textBox]
860	set lineHeight [font metrics [Value font,recall] -linespace]
861
862	# Hack -- In order to find out how much space is taken up by the
863	# text in the text widget, I create a canvas text item with the
864	# proper attributes and calculate its size. The width is width-8
865	# and height-4 because of the internal padding of the text
866	# widget. I added 2 to each adjustment as a hack.
867	set padx [$textBox cget -padx]
868	set pady [$textBox cget -pady]
869	set itemId [[Info $oop icon] create text 1 1 -font [Value font,recall] \
870		-width [expr {[winfo width $textBox] - $padx * 2 - 1}] -anchor nw \
871		-text [$textBox get 1.0 end]]
872	set bbox [[Info $oop icon] bbox $itemId]
873	set height [expr {[lindex $bbox 3] - [lindex $bbox 1] + $pady * 2 + 2}]
874
875	# Hmmm... Is there a trailing newline, or what?
876	incr height -$lineHeight
877
878	# Delete the temp canvas item
879	[Info $oop icon] delete $itemId
880
881	set winHeight [winfo height $win]
882	set winWidth [winfo width $win]
883
884	if {$height <= $winHeight} return
885
886	# If the window is closer to the top of the screen, then
887	# expand downwards, otherwise expand upwards.
888	set top [NSToplevel::FrameTop $win]
889	set topDist $top
890	if {$topDist < 0} {set topDist 0}
891	set bottom [NSToplevel::FrameBottom $win]
892	set bottomDist [expr {[winfo screenheight $win] - $bottom}]
893	if {$bottomDist < 0} {set bottomDist 0}
894	if {$topDist < $bottomDist} {
895		set expandUp 0
896	} else {
897		set expandUp 1
898	}
899
900	# Save the current window geometry
901	Info $oop geometry [wm geometry $win]
902
903	Info $oop busy 1
904
905	raise $win
906	set x [NSToplevel::FrameLeft $win]
907	if {$expandUp} {
908		set y [expr {[NSToplevel::FrameTop $win] - ($height - $winHeight)}]
909	} else {
910		set y [NSToplevel::FrameTop $win]
911	}
912	wm geometry $win ${winWidth}x$height+$x+$y
913	update
914
915	Info $oop expanded 1
916	Info $oop busy 0
917
918	# If the cursor moved outside the Recall Window, collapse it
919	if {![HasCursor $oop]} {
920		Contract $oop
921	}
922
923	return
924}
925
926# NSRecall::Contract --
927#
928#	Restores the window geometry to the size it was before it was
929#	expanded. Does nothing if the window is not expanded.
930#
931# Arguments:
932#	arg1					about arg1
933#
934# Results:
935#	What happened.
936
937proc NSRecall::Contract {oop} {
938
939	if {[Info $oop busy]} return
940	if {![Info $oop expanded]} return
941
942	Info $oop busy 1
943
944	set win [Info $oop win]
945	wm geometry $win [Info $oop geometry]
946	update
947
948	Info $oop expanded 0
949	Info $oop busy 0
950
951	return
952}
953
954# NSRecall::Restore --
955#
956#	Description.
957#
958# Arguments:
959#	arg1					about arg1
960#
961# Results:
962#	What happened.
963
964proc NSRecall::Restore {oop} {
965
966	if {![string length [Info $oop display]]} return
967	SetText $oop {icon none 0} {} {} {}
968	Contract $oop
969	Info $oop display ""
970
971	return
972}
973
974# NSRecall::ContextMenu --
975#
976#	When the window is right-clicked, pop up a menu of options.
977#
978# Arguments:
979#	arg1					about arg1
980#
981# Results:
982#	What happened.
983
984proc NSRecall::ContextMenu {oop menu x y} {
985
986	set text [Info $oop text]
987
988	$menu delete 0 end
989
990	$menu add command -label "Set Font" \
991		-command "NSModule::LoadIfNeeded NSFont ; NSWindowManager::Display font recall"
992	$menu add checkbutton -label "Show Icon" \
993		-variable ::NSRecall($oop,showIcon) \
994		-command "NSRecall::OptionChanged $oop showIcon showicon"
995	$menu add separator
996	$menu add command -label "Cancel"
997
998	# Hack -- Try to prevent collapsing while popup is visible.
999	# It would be nice if "winfo ismapped $menu" worked
1000	Info $oop busy 1
1001
1002	# Pop up the menu
1003	tk_popup $menu $x $y
1004
1005	if {[Platform unix]} {
1006		tkwait variable ::tkPriv(popup)
1007	}
1008
1009	Info $oop busy 0
1010
1011	set index ""
1012	if {[NSUtils::HasCursor $text]} {
1013		set x [expr {[winfo pointerx $text] - [winfo rootx $text]}]
1014		set y [expr {[winfo pointery $text] - [winfo rooty $text]}]
1015		set index2 [$text index @$x,$y]
1016		foreach tag [$text tag names $index2] {
1017			if {[string equal $tag TEXT]} {
1018				set index "$index2 linestart"
1019				break
1020			}
1021		}
1022	}
1023	Motion $oop $index
1024
1025	return
1026}
1027
1028# NSRecall::OptionChanged --
1029#
1030#	Description.
1031#
1032# Arguments:
1033#	arg1					about arg1
1034#
1035# Results:
1036#	What happened.
1037
1038proc NSRecall::OptionChanged {oop info keyword} {
1039
1040	set setting [Info $oop $info]
1041	Value recall,$keyword $setting
1042	switch -- $keyword {
1043		showicon {
1044			if {$setting} {
1045				grid [Info $oop icon]
1046			} else {
1047				grid remove [Info $oop icon]
1048			}
1049		}
1050	}
1051
1052	return
1053}
1054
1055# NSRecall::Configure --
1056#
1057#	Called as a <Configure> event script. Positions the indicator
1058#	arrow (the one which tells us if there is more information out
1059#	of site) near the bottom of the window.
1060#
1061# Arguments:
1062#	arg1					about arg1
1063#
1064# Results:
1065#	What happened.
1066
1067proc NSRecall::Configure {oop} {
1068
1069	set win [Info $oop win]
1070	set canvas [Info $oop icon]
1071	set text [Info $oop text]
1072
1073	scan [$canvas bbox arrow] "%s %s %s %s" left top right bottom
1074	set height [winfo height $text]
1075	$canvas move arrow 0 [expr {$height - $bottom - 4}]
1076
1077	ContentChanged $oop
1078
1079	return
1080}
1081
1082# NSRecall::ContentChanged --
1083#
1084#	Called when the information displayed has changed.
1085#
1086# Arguments:
1087#	arg1					about arg1
1088#
1089# Results:
1090#	What happened.
1091
1092proc NSRecall::ContentChanged {oop} {
1093
1094	set win [Info $oop win]
1095	set canvas [Info $oop icon]
1096	set text [Info $oop text]
1097
1098	scan [$text yview] "%f %f" top bottom
1099	if {$bottom < 1} {
1100		set fill Red
1101	} else {
1102		set fill [$canvas cget -background]
1103	}
1104
1105	$canvas itemconfigure arrow -fill $fill -outline $fill
1106
1107	return
1108}
1109
1110# NSRecall::Choose --
1111#
1112#	Handle <Choose> quasi-event.
1113#
1114# Arguments:
1115#	arg1					about arg1
1116#
1117# Results:
1118#	What happened.
1119
1120proc NSRecall::Choose {oop what show args} {
1121
1122	if {[lsearch -exact [list cmd_pet ele_attack item] \
1123		$what] == -1} return
1124
1125	if {!$show} {
1126		SetHook $oop {}
1127		return
1128	}
1129
1130	switch -- $what {
1131		cmd_pet {
1132			SetHook $oop hook_cmd_pet
1133		}
1134		ele_attack {
1135			SetHook $oop hook_ele_attack
1136		}
1137	}
1138
1139	return
1140}
1141
1142
1143proc NSRecall::MenuSelect {menu hook} {
1144
1145	set index [$menu index active]
1146	eval $hook [Global recall,oop] menu_select $menu $index
1147
1148	return
1149}
1150
1151
1152proc NSRecall::PetCmdInfo {_mode} {
1153
1154	upvar $_mode mode
1155
1156	set letters abcdefgh
1157	set index -1
1158
1159	if {[llength [angband player pets]]} {
1160		set char [string index $letters [incr index]]
1161		lappend data $char "Dismiss pets"
1162	}
1163
1164#	set dist [struct set player_type 0 pet_follow_distance]
1165	set mode ""
1166
1167	set char [string index $letters [incr index]]
1168	lappend data $char "Stay close"
1169	if {$dist == [const PET_CLOSE_DIST]} {
1170		set mode $char
1171	}
1172	set char [string index $letters [incr index]]
1173	lappend data $char "Follow me"
1174	if {$dist == [const PET_FOLLOW_DIST]} {
1175		set mode $char
1176	}
1177	set char [string index $letters [incr index]]
1178	lappend data $char "Seek and destroy"
1179	if {$dist == [const PET_DESTROY_DIST]} {
1180		set mode $char
1181	}
1182	set char [string index $letters [incr index]]
1183	lappend data $char "Give me space"
1184	if {$dist == [const PET_SPACE_DIST]} {
1185		set mode $char
1186	}
1187	set char [string index $letters [incr index]]
1188	lappend data $char "Stay away"]	if {$dist == [const PET_AWAY_DIST]} {
1189		set mode $char
1190	}
1191
1192	set char [string index $letters [incr index]]
1193	lappend data $char "Allow open doors"
1194
1195	set char [string index $letters [incr index]]
1196	lappend data $char "Allow pickup items"
1197
1198	return $data
1199}
1200
1201proc NSRecall::hook_cmd_pet {oop message args} {
1202
1203	switch -- $message {
1204
1205		open {
1206		}
1207
1208		fresh {
1209			SetList $oop
1210		}
1211
1212		close {
1213		}
1214
1215		set_list {
1216
1217			set textBox [Info $oop text]
1218
1219			# Keep a list of invoke chars
1220			set match {}
1221
1222			# Process each command
1223			foreach {char label} [PetCmdInfo mode] {
1224
1225				if {[string equal $char $mode]} {
1226					set color [Value TERM_L_BLUE]
1227				} else {
1228					set color White
1229				}
1230
1231				# Append the character and description
1232				$textBox insert end "$char\) " TEXT $label \
1233					[list ITEM_$char TEXT] "\n"
1234				$textBox tag configure ITEM_$char -foreground $color
1235
1236				# Keep a list of chars and colors
1237				lappend match $char
1238				lappend colors $color
1239			}
1240
1241			# Delete trailing newline
1242			$textBox delete "end - 1 chars"
1243
1244			# Keep a list of chars and colors
1245			Info $oop match $match
1246			Info $oop color $colors
1247		}
1248
1249		get_color {
1250			set row [lindex $args 0]
1251			return [lindex [Info $oop color] $row]
1252		}
1253
1254		invoke {
1255			set row [lindex $args 0]
1256			set char [lindex [Info $oop match] $row]
1257			angband keypress $char
1258		}
1259
1260		highlight {
1261		}
1262	}
1263
1264	return
1265}
1266
1267# NSRecall::PopupSelect_CmdPet --
1268#
1269#	Show a pop-up menu of pet commands.
1270#
1271# Arguments:
1272#	arg1					about arg1
1273#
1274# Results:
1275#	What happened.
1276
1277proc NSRecall::PopupSelect_CmdPet {menu x y} {
1278
1279	global PopupResult
1280
1281	set PopupResult 0
1282
1283	# Clear the menu
1284	$menu delete 0 end
1285
1286	set num 0
1287	foreach {char name} [PetCmdInfo mode] {
1288
1289		if {[string equal $char $mode]} {
1290			set ::PopupCheck 1
1291			$menu add checkbutton -label "$char $name" \
1292				-command "angband keypress $char ; set PopupResult 1" \
1293				-underline 0 -variable ::PopupCheck
1294		} else {
1295			$menu add command -label "$char $name" \
1296				-command "angband keypress $char ; set PopupResult 1" \
1297				-underline 0
1298		}
1299
1300		incr num
1301	}
1302
1303	$menu add separator
1304	$menu add command -label "Cancel"
1305
1306	# Pressing and holding Button-3, popping up, then letting go selects
1307	# an item, so wait a bit if it was a quick press-release
1308	after 100
1309
1310	tk_popup $menu $x $y [expr {$num / 2}]
1311
1312	if {[Platform unix]} {
1313		tkwait variable ::tkPriv(popup)
1314	}
1315
1316	# If the user unposts the menu without choosing an entry, then
1317	# I want to feed Escape into the Term. I tried binding to the <Unmap>
1318	# event but it isn't called on Windows(TM).
1319	after idle {
1320		if {!$PopupResult} {
1321			angband keypress \033
1322		}
1323	}
1324
1325	return
1326}
1327
1328
1329proc NSRecall::hook_xxx {oop message args} {
1330
1331	switch -- $message {
1332
1333		set_list {
1334		}
1335
1336		get_color {
1337		}
1338	}
1339
1340	return
1341}
1342
1343# NSRecall::ValueChanged_font_recall --
1344#
1345#	Called when the font,recall value changes.
1346#	Updates the Recall Window.
1347#
1348# Arguments:
1349#	arg1					about arg1
1350#
1351# Results:
1352#	What happened.
1353
1354proc NSRecall::ValueChanged_font_recall {oop} {
1355
1356	[Info $oop text] configure -font [Value font,recall]
1357
1358	return
1359}
1360