1#----------------------------------------------------------------------------
2# PLPLOT TK/TCL graphics renderer support procs
3# Maurice LeBrun
4# 1-Jul-1993
5# IFS, University of Texas at Austin
6#
7# Includes code borrowed from the TCL/TK widget demo.
8#----------------------------------------------------------------------------
9
10#----------------------------------------------------------------------------
11# plstdwin
12#
13# Does "standard" startup for a plframe-containing main window.
14# Use it or roll your own, but note: this may change in future versions.
15#----------------------------------------------------------------------------
16
17proc plstdwin {w} {
18    global plstdwin_skip_startup
19
20# Only do global initialization once.
21
22    if { ! [info exists plstdwin_skip_startup]} {
23
24    # Set up configuration options.
25    # The first is to hold default values of everything, the second is for
26    # user customization.  See pldefaults.tcl for more info.
27
28	pldefaults
29	plconfig
30
31	set plstdwin_skip_startup 1
32    }
33
34# Set min/max window sizes.
35
36    set root_width  [winfo vrootwidth .]
37    set root_height [winfo vrootheight .]
38
39    wm minsize $w 300 240
40    wm maxsize $w [expr "$root_width/64*63"] [expr "$root_height/64*62"]
41
42# Set window geometry defaults.  Try to get value from:
43#  - option database, from app-defaults file
44#  - global geometry var, from plconfig.tcl (legacy way)
45#  - automatic: specified fraction of root window
46#
47# Typically we depart from the usual 4/3 ratio somewhat to account for the
48# menu bar.
49
50    global geometry
51    if [info exists geometry] {
52	set w_geom $geometry
53    } else {
54	set w_geom [option get $w geometry {}]
55	if { $w_geom == "auto" } {
56	    set width  [expr "$root_width / 16 * 10"]
57	    set height [expr "$root_height / 16 * 11"]
58	    set w_geom ${width}x${height}
59	}
60    }
61    if { $w_geom != "" } {
62	wm geometry $w $w_geom
63    }
64}
65
66#----------------------------------------------------------------------------
67# null_command
68#
69# Invokes a dialog explaining that the real binding isn't written yet.
70#----------------------------------------------------------------------------
71
72proc null_command {cmd_name} {
73    set dialog_args "-text {Command \"$cmd_name\" not yet implemented.} \
74		     -aspect 500 -justify left"
75    mkDialog .null $dialog_args {OK {}}
76    tkwait visibility .null
77    grab .null
78    tkwait window .null
79}
80
81#----------------------------------------------------------------------------
82# bogue_out
83#
84# Invokes a dialog explaining that the user bogued out (messed up, blew
85# it, puked on the system console, etc).
86#----------------------------------------------------------------------------
87
88proc bogue_out {msg} {
89    set dialog_args "-text \"$msg\" -aspect 800 -justify left"
90    mkDialog .bogus $dialog_args {OK {}}
91    tkwait visibility .bogus
92    grab .bogus
93    focus .bogus
94    tkwait window .bogus
95}
96
97#----------------------------------------------------------------------------
98# dpos w
99#
100# Position a dialog box at a reasonable place on the screen.
101#----------------------------------------------------------------------------
102
103proc dpos w {
104    set offx [expr "[winfo rootx .]+100"]
105    set offy [expr "[winfo rooty .]+100"]
106    wm geometry $w +$offx+$offy
107}
108
109#----------------------------------------------------------------------------
110# normal_text_setup
111#
112# Sets up text widgets the way I like them.
113#----------------------------------------------------------------------------
114
115proc normal_text_setup {w {width 60} {height 30}} {
116    global dialog_font dialog_bold_font
117
118    button $w.ok -text OK -command "destroy $w"
119    text $w.t -relief raised -bd 2 -yscrollcommand "$w.s set" -setgrid true \
120	    -width $width -height $height
121    scrollbar $w.s -relief flat -command "text_scroll $w.t"
122    pack append $w $w.ok {bottom fillx} $w.s {right filly} $w.t {expand fill}
123    focus $w.t
124
125# Set up display styles
126
127    $w.t tag configure normal -font $dialog_font
128    $w.t tag configure bold -font $dialog_bold_font
129
130    if {[winfo depth $w] == 1} {
131	$w.t tag configure color1 -background black -foreground white
132	$w.t tag configure color2 -background black -foreground white
133	$w.t tag configure raised -background white -relief raised \
134		-borderwidth 1
135	$w.t tag configure sunken -background white -relief sunken \
136		-borderwidth 1
137    } else {
138	$w.t tag configure color1 -background "#eed5b7"
139	$w.t tag configure color2 -foreground red
140	$w.t tag configure raised -background "#eed5b7" -relief raised \
141		-borderwidth 1
142	$w.t tag configure sunken -background "#eed5b7" -relief sunken \
143		-borderwidth 1
144    }
145    $w.t tag configure bgstipple -background black -borderwidth 0 \
146	    -bgstipple gray25
147    $w.t tag configure fgstipple -fgstipple gray50
148    $w.t tag configure underline -underline on
149
150# Set up bindings to be as useful as possible.
151
152    bind $w <Any-Enter>	  "focus $w.t"
153
154    bind $w.t <Return>	  "destroy $w"
155
156    bind $w.t <Down>	  "text_scroll_by_line $w.t + 1"
157    bind $w.t <Up>	  "text_scroll_by_line $w.t - 1"
158
159    bind $w.t <Next>	  "text_scroll_by_page $w.t + 1"
160    bind $w.t <space>	  "text_scroll_by_page $w.t + 1"
161
162    bind $w.t <Prior>	  "text_scroll_by_page $w.t - 1"
163    bind $w.t <BackSpace> "text_scroll_by_page $w.t - 1"
164    bind $w.t <Delete>	  "text_scroll_by_page $w.t - 1"
165}
166
167#----------------------------------------------------------------------------
168# text_scroll
169#
170# Scrolls text widget vertically, updating various things
171#----------------------------------------------------------------------------
172
173proc text_scroll {w line args} {
174    eval [list $w yview $line] $args
175    $w mark set insert [$w index @0,0]
176}
177
178#----------------------------------------------------------------------------
179# text_scroll_by_line
180#
181# Scrolls text widget vertically by the given number of lines.
182#----------------------------------------------------------------------------
183
184proc text_scroll_by_line {w sign delta} {
185    text_scroll $w [$w index "@0,0 $sign $delta lines"]
186}
187
188#----------------------------------------------------------------------------
189# text_scroll_by_page
190#
191# Scrolls text widget vertically by the given number of pages (almost).
192#----------------------------------------------------------------------------
193
194proc text_scroll_by_page {w sign delta} {
195    set height [lindex [$w config -height] 4]
196    set delta [expr $delta*($height-2)]
197    text_scroll $w [$w index "@0,0 $sign $delta lines"]
198}
199
200#----------------------------------------------------------------------------
201# The procedure below inserts text into a given text widget and
202# applies one or more tags to that text.  The arguments are:
203#
204# w		Window in which to insert
205# text		Text to insert (it's inserted at the "insert" mark)
206# args		One or more tags to apply to text.  If this is empty
207#		then all tags are removed from the text.
208#----------------------------------------------------------------------------
209
210proc insertWithTags {w text args} {
211    set start [$w index insert]
212    $w insert insert $text
213    foreach tag [$w tag names $start] {
214	$w tag remove $tag $start insert
215    }
216    foreach i $args {
217	$w tag add $i $start insert
218    }
219}
220
221#----------------------------------------------------------------------------
222# Numeric utility procs:
223#
224#    min	returns minimum argument
225#    max	returns maximum argument
226#
227# Taken from utils.tcl by Tom Phelps (phelps@cs.Berkeley.EDU)
228#----------------------------------------------------------------------------
229
230proc min {args} {
231   set x [lindex $args 0]
232   foreach i $args {
233      if {$i<$x} {set x $i}
234   }
235   return $x
236}
237
238proc max {args} {
239   set x [lindex $args 0]
240   foreach i $args {
241      if {$i>$x} {set x $i}
242   }
243   return $x
244}
245
246#----------------------------------------------------------------------------
247# fileSelect
248#
249# Puts up a file selector.  Uses iWidgets 3.0 File selector if available,
250# otherwise just getItem.
251#
252# I have to go through a bit of trickery to get "~" expanded, since the
253# Tcl glob doesn't expand it if the file doesn't already exist.
254#----------------------------------------------------------------------------
255
256proc fileSelect {{filter {}}} {
257    global pl_iwidgets_package_name
258
259    # Use the Iwidgets file selector if available
260    if ![catch {eval package require $pl_iwidgets_package_name}] {
261	if {![winfo exist .fs]} {
262	    iwidgets::fileselectiondialog .fs -modality application
263	}
264
265	if {$filter > ""} {
266	    .fs configure -mask $filter
267	    .fs filter
268	}
269
270	if {[.fs activate]} {
271	    set file [.fs get]
272	} else {
273	    set file ""
274	}
275
276	.fs deactivate
277
278    } else {
279	set file [getItem "Enter file name"]
280    }
281
282    if { [string index $file 0] == "~" } {
283	set file [glob ~][string trimleft $file ~]
284    }
285
286    return $file
287}
288
289#----------------------------------------------------------------------------
290# getSaveFile
291#
292# Puts up a file selector for save file.
293#----------------------------------------------------------------------------
294
295proc getSaveFile {devkey} {
296
297    set filter "*"
298
299    # Map device name to filter suffix.
300    # Add to this as desired.
301    switch "$devkey" \
302	"ps"		"set filter *.ps" \
303	"psc"		"set filter *.ps" \
304	"plmeta"	"set filter *.plm" \
305	"xfig"		"set filter *.fig"
306
307    return [fileSelect $filter]
308}
309
310#----------------------------------------------------------------------------
311# getPaletteFile
312#
313# Puts up a file selector for a palette file.
314#----------------------------------------------------------------------------
315
316proc getPaletteFile {} {
317
318    return [fileSelect *.pal]
319}
320
321#----------------------------------------------------------------------------
322# getItem
323#
324# Asks user to input something, returning the result.
325# Selecting "Cancel" returns the empty string.
326#----------------------------------------------------------------------------
327
328proc getItem {item} {
329    global dialog_font dialog_bold_font
330    global itemval
331
332    set w .entry
333    set itemval ""
334
335    catch {destroy $w}
336    toplevel $w
337    dpos $w
338    wm title $w "Entry"
339    wm iconname $w "Entry"
340    message $w.msg -font $dialog_font -aspect 800 -text $item
341
342    frame $w.frame -borderwidth 10
343    pack append $w.frame \
344	[entry $w.frame.e1 -relief sunken] {top pady 10 fillx}
345
346    button $w.ok -text OK -command \
347	"set itemval \[$w.frame.e1 get\]; destroy $w"
348    button $w.cancel -text Cancel -command "destroy $w"
349
350    bind $w.frame.e1 <Return> \
351	"set itemval \[$w.frame.e1 get\]; destroy $w"
352
353    pack append $w $w.msg {top fill} $w.frame {top expand fill} \
354	$w.ok {left expand fill} $w.cancel {left expand fill}
355
356    tkwait visibility $w
357    grab $w
358    focus $w.frame.e1
359    tkwait window $w
360    return $itemval
361}
362
363#----------------------------------------------------------------------------
364# confirm
365#
366# Sure about that, buddy?
367#----------------------------------------------------------------------------
368
369proc confirm {msg} {
370    global confirm_flag
371    set dialog_args "-text {$msg} \
372		     -aspect 500 -justify left"
373    mkDialog .confirm $dialog_args \
374	"OK {set confirm_flag 1}" "Cancel {set confirm_flag 0}"
375    tkwait visibility .confirm
376    grab .confirm
377    focus .confirm
378    tkwait window .confirm
379    return $confirm_flag
380}
381
382#----------------------------------------------------------------------------
383# mkDialog w msgArgs list list ...
384#
385# Create a dialog box with a message and any number of buttons at
386# the bottom.
387#
388# Arguments:
389#    w -	Name to use for new top-level window.
390#    msgArgs -	List of arguments to use when creating the message of the
391#		dialog box (e.g. text, justifcation, etc.)
392#    list -	A two-element list that describes one of the buttons that
393#		will appear at the bottom of the dialog.  The first element
394#		gives the text to be displayed in the button and the second
395#		gives the command to be invoked when the button is invoked.
396#----------------------------------------------------------------------------
397
398proc mkDialog {w msgArgs args} {
399    catch {destroy $w}
400    toplevel $w -class Dialog
401    dpos $w
402    wm title $w "Dialog box"
403    wm iconname $w "Dialog"
404
405# Create two frames in the main window. The top frame will hold the message
406# and the bottom one will hold the buttons.  Arrange them one above the
407# other, with any extra vertical space split between them.
408
409    frame $w.top -relief raised -border 1
410    frame $w.bot -relief raised -border 1
411    pack append $w $w.top {top fill expand} $w.bot {top fill expand}
412
413# Create the message widget and arrange for it to be centered in the top
414# frame.
415
416    eval message $w.top.msg -justify center $msgArgs
417    pack append $w.top $w.top.msg {top expand padx 10 pady 10}
418
419# Create as many buttons as needed and arrange them from left to right in
420# the bottom frame.  Embed the left button in an additional sunken frame to
421# indicate that it is the default button, and arrange for that button to be
422# invoked as the default action for clicks and returns in the dialog.
423
424    if {[llength $args] > 0} {
425	set arg [lindex $args 0]
426	frame $w.bot.0 -relief sunken -border 1
427	pack append $w.bot $w.bot.0 {left expand padx 20 pady 20}
428	button $w.bot.0.button -text [lindex $arg 0] \
429		-command "[lindex $arg 1]; destroy $w"
430	pack append $w.bot.0 $w.bot.0.button {expand padx 12 pady 12}
431	bind $w <Return> "[lindex $arg 1]; destroy $w"
432	focus $w
433
434	set i 1
435	foreach arg [lrange $args 1 end] {
436	    button $w.bot.$i -text [lindex $arg 0] \
437		    -command "[lindex $arg 1]; destroy $w"
438	    pack append $w.bot $w.bot.$i {left expand padx 20}
439	    set i [expr $i+1]
440	}
441    }
442    bind $w <Any-Enter> [list focus $w]
443    focus $w
444}
445
446#----------------------------------------------------------------------------
447# Form2d
448#
449# Create a top-level window that displays a bunch of entries used for
450# entering window coordinates.
451#
452# Arguments:
453#    w		Name of top level window
454#    desc	Short description of coordinates to be entered.
455#
456# Global variables referenced:
457#    fv00	fn00
458#    fv01	fn01
459#    fv10	fn10
460#    fv11	fn11
461#
462# The global variables are modified by the entry widgets and may be
463# overwritten at any time so the caller must wait for the dialog to be
464# destroyed and then use them immediately.
465#----------------------------------------------------------------------------
466
467proc Form2d {w desc} {
468    global dialog_font dialog_bold_font
469    global tabList
470    global fv00 fv01 fv10 fv11
471    global fn00 fn01 fn10 fn11
472
473    catch {destroy $w}
474    toplevel $w
475    dpos $w
476
477    wm title $w "Entry window"
478    wm iconname $w "Entry"
479
480    message $w.msg \
481	-font $dialog_font \
482	-aspect 700 \
483	-text "$desc  Click \"OK\" button when finished."
484
485    pack append $w \
486	$w.msg {top fill}
487
488    set rows {0 1}
489    set cols {0 1}
490    set tabList ""
491
492    foreach i $rows {
493	frame $w.$i
494
495	foreach j $cols {
496            set name [set fn$i$j]
497            set value [set fv$i$j]
498	    frame $w.$i.$j -bd 1m
499
500	    entry $w.$i.$j.entry -relief sunken -width 10
501	    $w.$i.$j.entry insert 0 $value
502	    bind $w.$i.$j.entry <Tab> "Form2d_tab \$tabList"
503	    bind $w.$i.$j.entry <Return> "Form2d_destroy $w"
504            set tabList [concat $tabList $w.$i.$j.entry]
505
506	    label $w.$i.$j.label -width 10
507	    $w.$i.$j.label config -text "$name:"
508
509	    pack append $w.$i.$j \
510		$w.$i.$j.entry right \
511		$w.$i.$j.label left
512
513	    pack append $w.$i \
514		$w.$i.$j {left fillx}
515	}
516
517	pack append $w \
518	    $w.$i {top fillx}
519    }
520
521    button $w.ok -text OK -command "Form2d_destroy $w"
522    pack append $w \
523	$w.ok {bottom fill}
524
525    tkwait visibility $w
526    grab $w
527    focus $w.0.0.entry
528}
529
530# This procedure is invoked when the top level entry dialog is destroyed.
531# It updates the global vars used to communicate the entry values then
532# destroys the window.
533
534proc Form2d_destroy {w} {
535    global fv00 fv01 fv10 fv11
536
537    set fv00 [$w.0.0.entry get]
538    set fv01 [$w.0.1.entry get]
539    set fv10 [$w.1.0.entry get]
540    set fv11 [$w.1.1.entry get]
541
542    destroy $w
543}
544
545# The procedure below is invoked in response to tabs in the entry
546# windows.  It moves the focus to the next window in the tab list.
547# Arguments:
548#
549# list -	Ordered list of windows to receive focus
550
551proc Form2d_tab {list} {
552    set i [lsearch $list [focus]]
553    if {$i < 0} {
554	set i 0
555    } else {
556	incr i
557	if {$i >= [llength $list]} {
558	    set i 0
559	}
560    }
561    focus [lindex $list $i]
562}
563
564#----------------------------------------------------------------------------
565# evalCmd w
566#
567# Create a top-level window containing a text widget that allows you
568# to enter a TCL command and have it executed.
569#
570# Arguments:
571#    w -	Name to use for new top-level window.
572#----------------------------------------------------------------------------
573
574proc evalCmd {{w .eval}} {
575    catch {destroy $w}
576# -geometry unknown in 7.6 toplevels: toplevel $w -geometry 400x300
577    toplevel $w
578    wm geometry $w 400x300
579    dpos $w
580    wm title $w "Interpret command"
581    wm iconname $w "Interpret"
582
583    frame $w.cmd
584    label $w.cmd.label -text "Command:" -width 13 -anchor w
585    entry $w.cmd.entry -width 40 -relief sunken -bd 2 -textvariable command
586    button $w.cmd.button -text "Execute" \
587	    -command "eval \$command"
588    pack append $w.cmd $w.cmd.label left $w.cmd.entry left \
589	    $w.cmd.button {left pady 10 padx 20}
590    bind $w.cmd.entry <Return> "eval \$command"
591
592    text $w.t -relief raised -bd 2 -setgrid true
593    $w.t insert 0.0 {\
594Type TCL command to be executed in the window above, then type <Return>
595or click on "Execute".
596}
597    $w.t mark set insert 0.0
598    bind $w <Any-Enter> "focus $w.cmd.entry"
599
600    button $w.ok -text OK -command "destroy $w"
601
602    pack append $w $w.cmd {top fill} \
603	    $w.ok {bottom fillx} $w.t {expand fill}
604}
605
606#----------------------------------------------------------------------------
607# Used to get rid of sections of code during development.
608#----------------------------------------------------------------------------
609
610proc ignore { args } {}
611
612#------------------------------------------------------------------------------
613# Proc to set up debug bindings.
614#------------------------------------------------------------------------------
615
616proc dbug_bind {w} {
617
618bind $w <Any-ButtonPress>	{puts stderr "Widget event: ButtonPress"}
619bind $w <Any-ButtonRelease>	{puts stderr "Widget event: ButtonRelease"}
620bind $w <Any-Circulate>		{puts stderr "Widget event: Circulate"}
621bind $w <Any-CirculateRequest>	{puts stderr "Widget event: CirculateRequest"}
622bind $w <Any-Colormap>		{puts stderr "Widget event: Colormap"}
623bind $w <Any-Configure>		{puts stderr "Widget event: Configure"}
624bind $w <Any-ConfigureRequest>	{puts stderr "Widget event: ConfigureRequest"}
625bind $w <Any-Destroy>		{puts stderr "Widget event: Destroy"}
626bind $w <Any-Enter>		{puts stderr "Widget event: Enter"}
627bind $w <Any-Expose> 		{puts stderr "Widget event: Expose"}
628bind $w <Any-FocusIn>		{puts stderr "Widget event: FocusIn"}
629bind $w <Any-FocusOut>		{puts stderr "Widget event: FocusOut"}
630bind $w <Any-Gravity>		{puts stderr "Widget event: Gravity"}
631bind $w <Any-Keymap>		{puts stderr "Widget event: Keymap"}
632bind $w <Any-KeyPress>		{puts stderr "Widget event: KeyPress"}
633bind $w <Any-KeyRelease>	{puts stderr "Widget event: KeyRelease"}
634bind $w <Any-Leave>		{puts stderr "Widget event: Leave"}
635bind $w <Any-Map>		{puts stderr "Widget event: Map"}
636bind $w <Any-MapRequest>	{puts stderr "Widget event: MapRequest"}
637#bind $w <Any-Motion>		{puts stderr "Widget event: Motion"}
638bind $w <Any-Property>		{puts stderr "Widget event: Property"}
639bind $w <Any-Reparent>		{puts stderr "Widget event: Reparent"}
640bind $w <Any-ResizeRequest>	{puts stderr "Widget event: ResizeRequest"}
641bind $w <Any-Unmap>		{puts stderr "Widget event: Unmap"}
642bind $w <Any-Visibility>	{puts stderr "Widget event: Visibility"}
643
644}
645