linuxcnc/tcl/ngcgui.tcl
Dewey Garrett 3b26624ce9 ngcgui-gcmc: examples: option for mm or inch
also:
add cpocket.gcmc
ensure_units: debug prints iff verbose exists
ngcgui.tcl fix max_len_msg
2014-03-08 22:45:29 -07:00

4162 lines
146 KiB
Tcl
Executable file

#!/usr/bin/wish
#-----------------------------------------------------------------------
# ngcgui.tcl is a front-end gui that reads one or more single function
# gcode subroutine files, provides user prompts for parameters for an
# arbitrary number of invocations, and creates a single output file
# of gcode.
# ngcgui can be run as a standalone application or its functionality
# can be embedded in a parent tcl application including the axis gui.
# Example standalone Usage, create link:
# $ ln -s somewhere/ngcgui.tcl directory_in_your_PATH/ngcgui
#
# Usage:
# ngcgui --help | -?
# ngcgui [Options] -D nc_files_directory_name
# ngcgui [Options] -i LinuxCNC_inifile_name
# ngcgui [Options]
#
# Options:
# [-S subroutine_file]
# [-p preamble_file]
# [-P postamble_file]
# [-o output_file]
# [-a autosend_file] (autosend to axis default:auto.ngc)
# [--noauto] (no autosend to axis)
# [-N | --nom2] (no m2 terminator (use %))
# [--font [big|small|fontspec]] (default: "Helvetica -10 bold")
# [--horiz|--vert] (default: --horiz)
# [--cwidth comment_width] (width of comment field)
# [--vwidth varname_width] (width of varname field)
# [--quiet] (fewer comments in outfile)
# [--noiframe] (default: frame displays image)
#
#-----------------------------------------------------------------------
# ngcgui was first developed on git-master version 2.4.0-pre
# named "O" words available since: LinuxCNC 2.3.0, April 19, 2009
#-----------------------------------------------------------------------
# Copyright: 2010-2013
# Author: Dewey Garrett <dgarrett@panix.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#-----------------------------------------------------------------------
# ngcgui allows a user to write subroutine files that contain
# a single subroutine as described in
# 3.7 Calling Files of the LinuxCNC ________ manual
# and then use or test them with a gui frontend that simplifies
# user entry of calling arguments (positional parameters #1,#2,...)
# If the subroutine includes lines to equate positional parameters
# (#n) to named parameters (#<parmname>) on special association lines like:
#
# #<parmname> = #n (optional_comment_text)
#
# then the positional parameter will be supplemented with the more
# descriptive #<parmname> in the gui entry box and any optional_comment_text
# will be included in the gui. Use of the descriptive #<parmname> in
# the body of the subroutine will make it more readable but is not
# mandatory.
#
# When this format is used, the order of appearance of the positional
# parameters must be monotonically increasing with no omissions. This
# helps to prevent user errors in assignment of parmnames to parameters.
#
# A default value can also be specified on the special association line like:
# #<parmname> = #n (=dvalue)
# or
# #<parmname> = #n (=dvalue optional_comment_text)
# All positional parameters used in the body of the subroutine must be
# entered -- an error occurs if an item entry is missing when a feature
# is made with "Create Feature"
# The linuxcnc gcode language does not provide a mechanism for returning
# results so subroutines must set global parameters for results.
# Within ngcgui, _globals with names that contain a colon (:) character
# are ignored in the creation of entry boxes.
# For example, a subroutine called from a Subfile named o<line> returns
# results in globals like: #<_line:theta>, $<_line:length>, etc.
# This feature can be used to hide globals from entry boxes for any purpose
# or for communication between routines
# Workflow (for standalone usage):
# 1) The directory location for ngc gcode files used in linuxcnc is specified
# in the ini file by: [DISPLAY]PROGRAM_PREFIX.
# In linuxcnc2.5, multiple directories can be specified using
# [RS274NGC]SUBROUTINE_PATH if
# 2) Candidate subroutine files for use with this utility should contain
# a single subroutine as described in:
# 3.7 Calling Files of the LinuxCNC ________ manual
# 3) Optionally, user supplies a Preamble file of gcode
# No substitutions are performed on this file
# 4) User specifies a subroutine file (Subfile).
# Entry boxes are created for each positional parameter
# 5) Optionally, user supplies a Postamble file of gcode
# No substitutions are performed on this file
# 6) "Create Feature" Button adds feature to queue for output file.
# The gui will verify that all positional parameters are not
# null but makes no checks on values.
# 7) "Finalize" button prompts for filename, and writes output file
# for all features and adds a terminating m2
# 8) After finalizing the file, the user may send the file to
# the axis gui with the SendFileToAxis button. If axis is not running,
# an error is displayed. User should verify axis state before
# sending. Errors detected by axis are shown within the axis
# application.
# 9) To create a file with multiple sections from one or more
# subroutine files:
# a) enter values for Preamble, Subfile, Postamble
# b) fill in positional parameters
# d) "Create Feature" number_1
# e) If this this the only feature, select "Finalize" to write
# the file. Then select "SendFileToAxis" to send the file to axis
# or "Create Feature" to start a new file
# f) For multiple features, continue:
# enter different parameter values
# or
# specify new values for Preamble, Subfile, Postamble
# and fill in the new entry box values
# g) "Create Feature" number_2
# h) Repeat f),g) for all features
# i) "Finalize" the file (as above)
# The Preamble and Postamble files are optional, for example one
# might specify the Preamble only for the first subroutine and the
# Postamble only for the last subroutine in making a output file
# for a set of features with common parameters specified in a
# single preamble file of features.
# Options:
# "Retain values on Subfile read"
# After opening a Subfile (and creating an output file) a second
# Subfile (third,fourth, ...) may be opened while retaining values
# for positional parameters where the names are
# _matched_ in the subsequent file. This is useful when
# testing new subroutines and may be useful when combining multiple
# feature routines if they share parameters with common names like
# "#<zsafe>", "#<zstart>", etc.
# Values for _numbered_ positional parameters (#n) without a name
# association are never retained.
# "Expand subroutine"
# When checked, subroutines are expanded in the
# output file. This allows the axis_gui to highlight
# gcode lines in the text window when paths are left-clicked in
# the 3D window (and vice-vera) when subroutines are used.
# In expanding subroutines, labels within are made unique
# to avoid name collision with labels in other expansions or
# other included subroutines. Only one level of subroutine
# expansion is performed. If the interpreter detects an error, it
# is sometimes unclear where it occurs when subroutines are called.
# Expanding the Subfile and rerunning often gives a line number
# as an aid in finding the problem.
#
# When not checked, subroutines are called and not expanded.
# Button Shortcut bindings:
# Preamble, Subfile, Postamble buttons
# Instead of using the button and file selection dialog, enter
# a new file name in the associated entry and <Return> to open
# and read different file. When the filename differs from the
# currently laoded file, the filename text changes color.
# This shortcut is useful when you are debugging/editing one of the
# input files -- enter a <Return> in the corresponding entry item
# for the filename to reload the file.
# Notes:
# 0. configuring ngcgui is simplified with linuxcnc2.5; support for
# linuxcnc2.4 will cease when linuxcnc2.5 is released
# 1. ngcgui supports subroutine files that contain a _single_
# subroutine in a file where the name of the subroutine
# is the same as the name of the file.
# ex:
# $ cat rect.ngc
# o<rect> sub
# ...
# o<rect> endsub
# Only comments and empty lines may appear before sub or after endsub
# 2. The parameters passed to a subroutine (Postional parameters)
# are identified as "Numbered parameters" #1,#2,...,#n with
# n <= 30
# ngcgui finds any instances of #1,...,#30 and identifies
# each as a positional parameter for invocation of the subroutine.
# So, if you have a subroutine with 3 parameters (#1,#2,#3),
# it is not a good idea to use parameters like #4 or #30 in the
# body of the routine since they will increase the number of
# entry-box items in the ngcgui front-end and cause great confusion.
#
# In the manual:
# "O- call takes up to 30 optional arguments, which are passed
# to the subroutine as #1, #2, ..., #N. Parameters from #N+1 to
# #30 have the same value as in the calling context."
# 3. LinuxCNC gcode supports labels for conditional blocks and subroutines
# in both "Numbered" (ex: o100) and "Named" (ex: o<l101>) forms.
# Support for the "Numbered" label format is included, but
# it would be clearer to limit ngcgui support to:
# Positional Parametrs --> #1, ..., #n 1<=n<=30
# Named Labels --> o<label_name>
# This seems consistent with the trajectory of LinuxCNC gcode and
# accomodation of earlier styles (numbered labels like
# #n+1 to #30) is a small matter of editing:).
# 4. removed
# 5. If a file (subfile,preamble,postamble) is removed or modified by
# another application (like an editor), the color for its name will
# change to notify the ngcgui user that it should probably be reloaded.
# 6. The preamble file is provided to support simple setup actions
# like g20/g21,g40 etc. Similarly, the postamble file supports
# terminating actions as required like m5.
# The preamble and postamble file can be more complex even
# including subroutines. Such inclusion requires care
# by the user if multiple files are used to make a single output
# file with ngcgui because if a file containing subroutines
# is included more than once, a multiple definition error is
# flagged. The user can avoid this by carefully selecting/deselecting
# preamble/postamble files but a better course is to avoid
# subroutines in these files and rely on a library of "subroutine-only"
# files in the [DISPLAY]PROGRAM_PREFIX directory.
# 7. ngcgui inserts a special global variable named #<_feature:> that begins
# with a value of 0 and is incremented for each added feature. This
# _global can be tested in subroutines; no entry box is created for it.
# 8. entry boxes for positional parameters include key bindings
# for keys x,y,z,a,b,c,u,v,w, and d. When embedded in axis, typing these keys
# cause the current value (emc_rel_act_pos) to be entered into the
# entry box. This function makes it simple to enter current coordinate
# values. The d key will enter the 2*x for the diameter on a lathe)
#
# (If there is a tcl global ::entrykeybinding proc, it will
# be used instead for these key bindings so that other embedding
# applications can handle these keys -- see the source for the parameters
# passed to the proc.)
# 9. lines before the o<>sub line and after the o<>endsub line must
# be comments (enclosed in parentheses) or begun with a semicolon (;)
# 10. each time an output file is finished, ngcgui saves a copy in
# /tmp/ngcgui_bak/ just in case you want to see it or reuse it later
# The /tmp directory is normally purged at restart or after
# a number of days determined by the variable TMPTIME in
# the system file /etc/default/rcS (ubuntu for example)
# 11. key bindings
# Escape return to Preview page (only if embed_in_axis)
# Ctrl-a Toggle autosend
# Ctrl-c Clear entries
# Ctrl-d Set entries to default values
# Ctrl-e Open editor specified by $VISUAL on last outfile
# Ctrl-f Create feature
# Ctrl-F Finalize
# Ctrl-k Show key bindings
# Ctrl-n Restart (cancel pending)
# Ctrl-p (re)Read Preamble
# Ctrl-P (re)Read Postamble
# Ctrl-r (re)Read Subfile
# Ctrl-s Show status
# Ctrl-S Show full status (debug info)
# Ctrl-u Open editor specified by $VISUAL on current subfile
# Ctrl-U Open editor specified by $VISUAL on current preamble
# 12. All entry boxes are checked for valid numbers and the entry is
# turned red if invalid.
# 13. Emc gcode (2.3 19apr09) allows a single semicolon use for comments.
# This gui supports semicolon comments but the syntax for special
# association lines requires the () form:
#
# for positional parameters 1<=n<=30:
# #<parmname> = #n (=defaultvalue comment_text)
# 14. Features requiring linuxcnc-2.4pre (that I can remember):
# a) error detection when sending file to axis
# 15. Helper subroutine files that are included in the
# [DISPLAY]PROGRAM_PREFIX (or the[RS274NGC]SUBROUTINE_PATH)
# directory may not be suitable for use as a subfile.
# To indicate this to a user, include a special comment line:
# (not_a_subfile)
# Alternatively, these files can be placed in a different
# directory specified in the ini file [WIZARD]WIZARD_ROOT
# 16. Using a launcher (like ubuntu gnome destop launcher) doesn't
# make it easy to pass in environmental variables like VISUAL.
# This works for a launcher: put ngcgui.tcl in a directory
# such as /home/yourname/bin and create script such as
# $ cat /home/yourname/bin/launch_ngc
# #!/bin/sh
# export VISUAL=gedit ;# your favorite editor
# /home/yourname/bin/ngcgui.tcl -a auto -i your inifile
#
# make it executable:
# $ chmod 755 /home/yourname/bin/launch_ngc
# configure the launcher so the command is:
# Command: /home/yourname/bin/launch_ngc
#
# 17. obsolete: xembed support removed, internal embedding works better
#
# 18. If --vwidth 0 is used and a parameter has no comment, the variable
# name is placed in the comment field
#
# 19. For linuxcnc 2.4, the tcl proc embed_in_axis_tab will embed directly
# in an axis tab using [DISPLAY]USER_COMMAND_FILE (or ~/.axisrc)
# example:
# w = widgets.right.insert("end", 'ngcgui', text='Ngcgui')
# w.configure(borderwidth=1, highlightthickness=0)
# f = Tkinter.Frame(w, container=0, borderwidth=0, highlightthickness=0)
# f.pack(fill="both", expand=1, anchor="nw",side="top")
# root_window.tk.call("source","somepath/ngcgui.tcl")
# root_window.tk.call("::ngcgui::embed_in_axis_tab",f,"nameof_ngcgui_subfile")
#
# 20. The Preamble and Postamble entry fields may be used to insert
# immediate gcode commands instead of reading files. The immediate
# syntax is signaled by a leading colon (:), commands are separated by
# semicolons (;). Example:
# :t0m6;(debug, pausing);m0 (pause)
# The commands are not validated by ngcgui but are added to the
# output gcode file
#
# 21. When embedding in axis directly, multiple tabpages can be specified. Each
# can be used independently to add multiple features from the initial or
# newly selected subfiles. If multiple tabpages have created features, the
# Finalize action will offer to finalize all tabpages in left-to-right order.
# Beware of this ordering. If the order is incorrect, cancel and then
# rearrange page order before finalizing.
#
# 22. Subfiles can optionally include a special comment:
# (info: info_text)
# The info text will be displayed (embed_in_axis only)
#
# 23. An optional image file (.png,.gif,.jpg,.pgm) can accompany a subfile.
# The image file can help clarify the parameters; a window displaying
# the image is popped up when the subfile is read. The image file
# should be in the same directory as the subfile and have the same
# name with an appropriate image suffix, e.g. the subfile iquad.ngc
# should be accompanied by an image file iquad.png
#
# 24. When ngcgui pages are embedded in the axis gui, options can
# be specified:
# NGCGUI_OPTIONS = opt1 opt2 ...
# opt items:
# nonew -- disallow making new tab page
# noremove -- disallow removing any tab page
# noauto -- noautosend (makeFile, then manually send)
# noiframe -- put image inside a toplevel instead of a frame
# so all controls are available
# nom2 -- no m2 terminator (use %)
#
# 25. When ngcgui pages are embedded in the axis gui and the user
# is allowed to open new subroutines, the initial starting directroy
# for subfiles is:
# the first directory in [RS274NGC]SUBROUTINE_PATH if
# [RS274NGC]SUBROUTINE_PATH is specified
# or
# the dir specified by [DISPLAY]PROGRAM_PREFIX if
# [DISPLAY]PROGRAM_PREFIX is specified
# otherwise
# "."
# 26. removed
# 27. Ngcgui supports .gcmc files (for gcmc the G-Code Meta Compiler)
# http://www.vagrearg.org/content/gcmc
# Special tags in the .gcmc file are used to:
# 1) specify the info text for the tab page (optional)
# 1) specify variable names requiring an ngcgui entry box
# 2) specify gcmc options (optional)
#
# When creating a feature from a gcmc file, the gcmc program
# is run with the variable values from the entry boxes and the gcmc
# options specified.
#
#-----------------------------------------------------------------------
namespace eval ::ngcgui {
namespace export ngcgui ;# public interface
}
#-----------------------------------------------------------------------
# Internationalization
# use the tcl-package named Emc to set up I18n support
if [catch {package require Linuxcnc} msg] {
# if user is trying to use as standalone in an unconfigured (non-Emc)
# environment, just continue without internationalization
puts stdout "Internationalization not available: <$msg>"
}
# use a command or proc named "_" for ::msgcat::mc
# when embedded in axis, a command named "_" is predefined,
# since "_" is not defined for standalone usage, make a proc named "_"
if {"" == [info command "_"]} {
package require msgcat
proc _ {s} {return [::msgcat::mc $s]}
}
#-----------------------------------------------------------------------
proc ::ngcgui::parse_ngc {hdl ay_name filename args} {
# return 1 for ok
# return 0 for error and lappend to (parse,msg)
upvar $ay_name ay
set ay($hdl,parse,msg) ""
# default info, supersede expected:
set ay($hdl,info) "[_ "Current subfile: $filename"]"
if {"$filename" == ""} {
lappend ay($hdl,parse,msg) "[_ "Need non-null file name"]"
return 0
}
if [catch {set fd [open $filename r]} msg] {
lappend ay($hdl,parse,msg) $msg
return 0
}
set basename [file tail $filename]
set idx [string last . $basename]
set ay($hdl,subroutine,name) [string replace $basename $idx end]
new_image $hdl $filename
retain_or_unset $hdl $ay_name
set min_num 999999; set max_num -1
set last_num 0
set ay($hdl,label_maxwidth) 0
set lct 0
set lno 1
catch {
foreach n [array names ::ngc_sub $hdl,*] {
unset ::ngc_sub($n)
}
}
while {![eof $fd]} {
gets $fd theline
incr lno
#remove blanks and tabs, use lower case (ngc rs274 format):
set line [string map {" " "" " " ""} $theline] ;#sp,tab to ""
set line [string tolower $line]
# theline: original line, may have whitespace, caps, etc.
# line: collapsed whitespace, lowercase
set line_end [expr -1 + [string len $line]] ;# last index
if {"$line" == ""} continue ;# discard empty lines
set iscomment 0
if { ([string first ( $line] == 0 && [string last ) $line] == $line_end)\
|| [string first \; $line] == 0 } {
set iscomment 1
# match to theline for caps to find spaceFEATUREspace on a comment line
if [string match "*\[ \]FEATURE\[ \]*" $theline] {
lappend emsg "[_ "Disallowed use of ngcgui generated file as Subfile"]"
set ay($hdl,parse,msg) $emsg
catch {unset ay($hdl,argct)} ;# make parmcheck fail
return 0
}
if [string match "(not_a_subfile)" $theline] {
lappend emsg "[_ "File"] <$filename> [_ "marked (not_a_subfile)\nNot intended for use as a subfile"]"
catch {unset ay($hdl,argct)} ;# make parmcheck fail
set ay($hdl,parse,msg) $emsg
return 0
}
if {[string first "(info:" $theline] >= 0} {
set idx [string first : $theline]
set info [string range $theline [expr $idx +1] end]
set ay($hdl,info) [string trim $info " )"]
}
}
# disallow embedded numbered subroutines within a single-file subroutine
if {[regexp -nocase "^o\[0-9\]*sub" $line]} {
puts stdout "[_ "bogus"]:$lno<$theline>"
lappend emsg \
"[_ "can not include subroutines within ngcgui subfile"]:$theline"
set ay($hdl,parse,msg) $emsg
return 0
}
# find subroutine start:
if [string match o<*>sub* $line] {
if [info exists found_sub_end] {
lappend emsg "[_ "Multiple subroutines in file not allowed"]"
set ay($hdl,parse,msg) $emsg
return 0
}
set found_sub_start 1
set i1 [string first < $theline]
set i2 [string first > $theline]
set label [string range $theline [expr $i1 + 1] [expr $i2 -1]]
if {"$label" != "$ay($hdl,subroutine,name)"} {
puts stdout "[_ "bogus"]:$lno<$theline>"
lappend emsg \
"[_ "sub label"]: o<$label> [_ "does not match subroutine file name"]"
}
continue ;# the sub line itself is not saved
}
if {[info exists found_sub_end]} {
# allow null lines and comments after endsub
if $iscomment {
set ::ngc_sub($hdl,$lct) $theline
incr lct
continue
} else {
# sometimes there is an m2 after endsub, ignore it
if {[string first m2 [string trim [string tolower $theline]]] == 0} {
set ::ngc_sub($hdl,$lct) \
"($::ngc(any,app): [_ "ignoring M2 after endsub"]: <$theline>)"
puts stdout "[_ "ignoring M2 after endsub"] <$theline>"
incr lct
continue
} else {
puts stdout "[_ "bogus"]:$lno<$theline>"
lappend emsg "[_ "file contains lines after subend"]"
}
}
}
if {![info exists found_sub_start]} {
# allow null lines and comments before sub
if $iscomment {
set ::ngc_sub($hdl,$lct) $theline
incr lct
continue
} else {
puts stdout "[_ "bogus"]:$lno<$theline>"
lappend emsg "[_ "file contains lines before sub"]"
}
}
if {$iscomment} {
set ::ngc_sub($hdl,$lct) $theline
incr lct
continue
}
# processing below for non-comments only
# find subroutine end:
if { [info exists found_sub_start] \
&& [string match o<*>endsub* $line] } {
set found_sub_end 1
set i1 [string first < $theline]
set i2 [string first > $theline]
set label [string range $theline [expr $i1 + 1] [expr $i2 -1]]
if {"$label" != "$ay($hdl,subroutine,name)"} {
puts stdout "[_ "bogus"]:$lno<$theline>"
lappend emsg \
"[_ "endsub label"]: o<$label> [_ "does not match subroutine file name"]"
}
continue ;# the endsub line is not saved
}
# find and save labels for name mangling when expanding
if { [info exists found_sub_start] \
&& ![info exists found_sub_end]} {
if {$lct >= 0} {
# save label identifiers so they can be made unique when expanding
# multiple subroutines
# but do not include labels for calls:
# match to line but use theline for label to preserve user case
if { [string match *o<* $line] \
&& ![string match *o<*>*call* $line]} {
set i1 [string first < $theline]
set i2 [string first > $theline]
set label [string range $theline [expr $i1 + 1] [expr $i2 -1]]
set ::ngc_sub($hdl,$lct,label) $label
set txt [string range $theline [expr $i2+1] end]
set ::ngc_sub($hdl,$lct) $txt
} elseif { [string match o\[0-9\]* $line] } {
set tline [string trimleft $theline]
if [regexp -nocase "(^o\[0-9\]*)(.*)" $tline v label txt] {
set ::ngc_sub($hdl,$lct,label) $label
set ::ngc_sub($hdl,$lct) $txt
}
} else {
set ::ngc_sub($hdl,$lct) $theline
set label ""
}
if {[string length $label] > $ay($hdl,label_maxwidth)} {
set ay($hdl,label_maxwidth) [string length $label]
}
}
incr lct
}
# find numbered parameters #1--#30 inclusive
# in order to identify the biggest one since all
# in this range are considered to be positional parameters
# even if some in the range are not explicitly used
set l $line
while 1 {
set i1 [string first # $l]
if {$i1 < 0} {break}
set i2 [expr 1 + $i1]
set i3 [expr 2 + $i1]
set i4 [expr 3 + $i1]
set char2 [string range $l $i2 $i2]
set char3 [string range $l $i3 $i3]
set v $char2$char3[string range $l $i4 $i4]
if { [is_int $v] \
&& ($v > 30) } {
break ;# ignore #nnn...
}
if {[is_int $char2] && ![is_int $char3]} {
set num_var $char2
if {$num_var < $min_num} {set min_num $num_var}
if {$num_var > $max_num} {set max_num $num_var}
set l [string range $l $i3 end]
continue
}
if {[is_int $char2] && [is_int $char3]} {
set num_var $char2$char3
if { 0 < $num_var & $num_var <= 30} {
if {$num_var < $min_num} {set min_num $num_var}
if {$num_var > $max_num} {set max_num $num_var}
set l [string range $l [expr 1+$i3] end]
continue
}
}
set l [string range $l $i2 end]
}
# find special association lines that match:
# for positional parameters, special line is
# #<parmname>=#n where 0 <= n <= 30
# or #<parmname>=#n (=defaultvalue comment_text)
if { [string match *#<*>=#\[1-9\]* $line] \
|| [string match *#<*>=#\[1-2\]\[0-9\]* $line] \
|| [string match *#<*>=#30* $line] } {
if { [string match *#<*>=#\[3-9\]\[1-9\]* $line] } {
# exclude #31-#99
} elseif {[string match *#<*>=#\[1-9\]\[0-9\]\[0-9\]* $line] } {
# exclude #nnn... (3 or more digit numbers)
} else {
set i1 [string first >=# $line]
set parmname [string range $line 2 [expr -1+$i1]]
set num [string range $line [expr 3+ $i1] end]
# remove trailing comment:
set i1 [string first ( $num]
if {$i1 >= 0} {
set num [string range $num 0 [expr -1 +$i1]]
}
set num02 [format %02d $num]
set ay($hdl,arg,name,$num02) $parmname
set expect_num [expr $last_num +1]
# enforce these to appear in order to help prevent user errors
if {$num != $expect_num && $num <= 30} {
puts stdout "[_ "bogus"]:$lno<$theline>"
lappend emsg \
"[_ "out of sequence positional parameter"] $num [_ "expected"]: $expect_num "
} else {
set last_num $num
}
set i1 [string first ( $theline]
set i2 [string last ) $theline]
if { $i1 >0 && $i2 > $i1} {
set cmt [string range $theline [expr 1 + $i1] [expr -1 + $i2]]
if [regexp -nocase "= *(\\+*-*\[0-9.\]*)(.*)" \
$cmt V(match) V(dvalue) V(comment)] {
set ay($hdl,arg,dvalue,$num02) $V(dvalue)
set ay($hdl,arg,comment,$num02) [string trim $V(comment)]
} else {
set ay($hdl,arg,comment,$num02) $cmt
}
}
# for --vwidth 0, make sure something exists for comment
if { $ay(any,width,varname) == 0 \
&& ( ![info exists ay($hdl,arg,comment,$num02)] \
|| "$ay($hdl,arg,comment,$num02)" == "")
} {
set ay($hdl,arg,comment,$num02) $ay($hdl,arg,name,$num02)
}
}
}
} ;# while !eof
set ay($hdl,sublines) $lct
close $fd
# for args without a special name association, use #n for name
for {set i 1} {$i <= $max_num} {incr i} {
set num02 [format %02d $i]
if ![info exists ay($hdl,arg,name,$num02)] {
set ay($hdl,arg,name,$num02) #$i ;# ensure all intervening parms
}
}
set ay($hdl,argct) $max_num
# remove any notused retained items
for {set i [expr $max_num +1]} {$i <= 30} {incr i} {
set num02 [format %02d $i]
catch {unset ay($hdl,arg,name,$num02)}
catch {unset ay($hdl,arg,comment,$num02)}
}
# error checks
if {![info exists found_sub_start]} {
lappend emsg "[_ "no sub found in file"]"
}
if {[info exists found_sub_start] && ![info exists found_sub_end]} {
lappend emsg "[_ "no endsub found in file"]"
}
if [info exists emsg] {
set ay($hdl,parse,msg) $emsg
return 0
}
return 1 ;# ok
} ;# parse
proc retain_or_unset {hdl ay_name} {
upvar $ay_name ay
if {$ay($hdl,retainvalues)} {
# positional parameters: retain some
foreach n [array names ay $hdl,arg,name,*] {
# example:
# exists arg,name,03 == xloc
# arg,value,03 == 999
# set arg,byname,xloc == 999
set num [string range $n [expr 1+[string last , $n]] end]
set name $ay($n)
if ![info exists ay($hdl,arg,value,$num)] continue
if {[string first # $name] != 0} {
set ay($hdl,arg,byname,$name) $ay($hdl,arg,value,$num)
}
}
} else {
# retaining none
foreach n [array names ay $hdl,arg,value*] {unset ay($n)}
foreach n [array names ay $hdl,arg,byname,*] {unset ay($n)}
}
# always unset these
foreach n [array names ay $hdl,arg,name,*] {unset ay($n)}
foreach n [array names ay $hdl,arg,comment,*] {unset ay($n)}
foreach n [array names ay $hdl,arg,value,*] {unset ay($n)}
foreach n [array names ay $hdl,arg,dvalue,*] {unset ay($n)}
foreach n [array names ay $hdl,arg,entrywidget,*] {unset ay($n)}
catch {
foreach n [array names ::ngc_sub $hdl,*] {
unset ::ngc_sub($n)
}
}
} ;# retain_or_unset
#-----------------------------------------------------------------------
proc ::ngcgui::find_gcmc {} {
if [catch {set found [exec which gcmc]} msg] {
puts stdout "find_gcmc:NOTfound:<$msg>"
return ""
} else {
#puts stdout "find_gcmc:found:$found"
}
return $found
} ;# find_gcmc
proc ::ngcgui::parse_gcmc {hdl ay_name filename args} {
# return 1 for ok
# return 0 for error and lappend to (parse,msg)
upvar $ay_name ay
set ay($hdl,parse,msg) ""
if ![info exists ::ngc(any,gcmc,executable)] {
set result [find_gcmc]
if {"$result" == ""} {
lappend ay($hdl,parse,msg) "[_ "Cannot find gcmc executable"]"
lappend ay($hdl,parse,msg) "[_ "Please Install in path"]"
return 0
} else {
set ::ngc(any,gcmc,executable) [find_gcmc]
# outdir has to be in path
# use first dir in path as dir for temporary ofile
if ![info exists ::ngc(any,paths)] {
set ::ngc(any,paths) [file normalize [file dirname $filename]]
puts "\nngcgui: [_ "not embedded, deriving outdir from:"] $filename\n"
}
set ::ngc(any,gcmc,outdir) [file normalize [lindex $::ngc(any,paths) 0]]
set ::ngc(any,gcmc,funcname) tmpgcmc ;# append session id and suffix
# clean up prior runs by moving to tmp
if ![catch {set flist [glob [file join $::ngc(any,gcmc,outdir) \
$::ngc(any,gcmc,funcname)]*] } msg] {
file mkdir /tmp/oldgcmc
foreach f $flist {
#puts " file rename $f /tmp/[file tail $f]"
file rename -force $f [file join /tmp/oldgcmc [file tail $f]]
}
}
}
set ct 1
# catch: early versions of gcmc returns $?=1
if [catch {set ans [exec $::ngc(any,gcmc,executable) --version]
} msg ] {
puts stdout "parse_gcmc: unexpected version:<$msg>"
} else {
foreach line [split $ans \n] {
set ::ngc(any,gcmc,version,line$ct) $line
incr ct
}
puts stdout "gcmc path: $::ngc(any,gcmc,executable)"
puts stdout "gcmc version: $::ngc(any,gcmc,version,line1)"
}
}
# default info, supersede expected:
set ay($hdl,info) "[_ "Current subfile: $filename"]"
catch {unset ::ngc($hdl,gcmc,opts)} ;# no retain on reread
if {"$filename" == ""} {
lappend ay($hdl,parse,msg) "[_ "Need non-null file name"]"
return 0
}
if [catch {set fd [open $filename r]} msg] {
lappend ay($hdl,parse,msg) $msg
return 0
}
set basename [file tail $filename]
set idx [string last . $basename]
set ay($hdl,subroutine,name) [string replace $basename $idx end]
new_image $hdl $filename
retain_or_unset $hdl $ay_name
set min_num 999999; set max_num -1
set ay($hdl,label_maxwidth) 0
set lno 1
set num 1
set num02 [format %02d $num]
set names {}
while {![eof $fd]} {
gets $fd theline
incr lno
#remove blanks and tabs
set theline [string trim $theline]
# consider // comments only
if {[string first "//" $theline] != 0} continue
# The '*', '+', and '?' qualifiers are all greedy.
# Greedy <.*> matches all of <H1>title</H1>
# NonGreedy <.*?> matches the only first <H1>
# // ngcgui : info: describing text
set einfo "^ *\\/\\/ *ngcgui *: *info: *\(.*?\)"
if {[regexp $einfo $theline match info]} {
set ay($hdl,info) $info
continue
}
set eopt "^ *\\/\\/ *ngcgui *: *\(-.*\)$"
if {[regexp $eopt $theline match opt]} {
# remove a trailing comment:
set idx [string first '//' $opt]
if {$idx >= 0} { set opt [string replace $opt $idx end] }
set idx [string first \; $opt]
if {$idx >= 0} { set opt [string replace $opt $idx end] }
set opt [string trim $opt]
lappend ::ngc($hdl,gcmc,opts) $opt
continue
}
catch {unset name dvalue comment}
# // ngcgui : name [= value [,comment]]
set e1 "^ *\\/\\/ *ngcgui *: *\(.*?\) *= *\(.*?\) *\, *\(.*?\) *$"
set e2 "^ *\\/\\/ *ngcgui *: *\(.*?\) *= *\(.*?\) *$"
set e3 "^ *\\/\\/ *ngcgui *: *\(.*?\) *$"
if {[regexp $e1 $theline match name dvalue comment]} {
#puts "1_____<$name>,<$dvalue>,<$comment>"
} elseif {[regexp $e2 $theline match name dvalue]} {
#puts "2_____<$name>,<$dvalue>"
} elseif {[regexp $e3 $theline match name]} {
#puts "3_____<$name>"
} else {
continue
}
if {[lsearch $names $name] >= 0} {
puts "duplicate name, first one wins <$name>"
# could be an error:
# lappend emsg "[_ "duplicate name <$name>"]"
continue
}
lappend names $name
set ay($hdl,arg,name,$num02) $name
if [info exists dvalue] {
# this is a convenience to make it simple to edit to
# add a var without removing the semicolon
# xstart = 10;
# //ngcgui: xstart = 10;
set dvalue [lindex [split $dvalue ";"] 0] ;# strip after a ";"
set ay($hdl,arg,dvalue,$num02) $dvalue
}
if [info exists comment] {
set ay($hdl,arg,comment,$num02) $comment
} else {
set ay($hdl,arg,comment,$num02) $name
}
incr num
set num02 [format %02d $num]
} ;# while !eof
close $fd
set ay($hdl,argct) [llength $names]
# gcmc files with no args are allowed
# if {$ay($hdl,argct) <= 0} {
# lappend emsg "[_ "gcmc file with no args"]"
# }
if {$ay($hdl,argct) > 30} {
lappend emsg "[_ "gcmc file with too many args <$::ay($hdl,argct)"]"
}
# error checks
if [info exists emsg] {
set ay($hdl,parse,msg) $emsg
return 0
}
return 1 ;# ok
} ;# parse_gcmc
proc ::ngcgui::dt {} {
return [clock format [clock seconds] -format %y%m%d:%H.%M.%S]
} ;# dt
proc ::ngcgui::is_int {v} {
if [catch {format %d $v}] { return 0 }
return 1
} ;# is_int
proc ::ngcgui::trimprefix {s {pfx opt,} } {
set idx [string first $pfx $s]
if {$idx != 0} {return $s}
return [string range $s [string length $pfx] end]
} ;# trimprefix
proc ::ngcgui::trimsuffix {s {sfx .ngc} } {
set idx [string last $sfx $s]
if {$idx <0} {return $s}
return [string range $s 0 [expr -1 + $idx]]
} ;# trimsuffix
proc ::ngcgui::qid {} {
# unique identifier
if ![info exists ::ngc(any,qid)] { set ::ngc(any,qid) 0 }
return [incr ::ngc(any,qid)]
} ;# qid
proc ::ngcgui::initgui {hdl} {
if ![info exists ::ngc(embed,hdl)] {set ::ngc(embed,hdl) 0}
if [info exists ::ngcgui($hdl,afterid)] { return ;# already done }
# fixed initializations
set ::ngc(any,pentries) 10 ;# number of entries in positional frame
;# 30 max positional parameters
;# 3 frames max so must have pentries >=10
set ::ngc(any,pollms) 2000
set ::ngc(any,color,black) black
set ::ngc(any,color,stdbg) #dcdad5 ;# default gray color set
set ::ngc(any,color,title) lightsteelblue2
set ::ngc(any,color,vdefault) darkseagreen2 ;# value defaults
set ::ngc(any,color,readonly) gray
set ::ngc(any,color,ok) green4
set ::ngc(any,color,single) palegreen
set ::ngc(any,color,multiple) cyan
set ::ngc(any,color,feature) lightslategray
set ::ngc(any,color,prompt) blue3
set ::ngc(any,color,warn) darkorange
set ::ngc(any,color,notice) lightgoldenrodyellow
set ::ngc(any,color,override) blue3
set ::ngc(any,color,error) red
set ::ngc(any,color,filegone) maroon
set ::ngc(any,color,filenew) darkorange
set ::ngc(any,color,filemod) purple
set ::ngc(any,color,custom) ivory2
set ::ngc(any,color,default) blue4
set ::ngc(any,max_msg_len) 500 ;# limit popup msg len (gcmc)
set ::ngc($hdl,afterid) ""
statemap $hdl ;# set up state transitions
} ;# initgui
proc ::ngcgui::preset {hdl ay_name} {
# using apps call this to populate ay_name,
# superseded items as reqd
# all required items with defaults:
upvar $ay_name ay
# per-instance items:
set ay($hdl,fname,subfile) ""
set ay($hdl,fname,preamble) ""
set ay($hdl,fname,postamble) ""
set ay($hdl,fname,outfile) ""
set ay($hdl,auto) 1
set ay($hdl,fname,autosend) "auto.ngc"
set ay($hdl,dir) ""
set ay($hdl,retainvalues) 1
set ay($hdl,expandsubroutine) 0
set ay($hdl,verbose) 1
set ay($hdl,chooser) 0
set ay($hdl,info) "[_ "Choose Files"]"
set ay($hdl,standalone) 0
# common to any instance items:
set ay(any,app) ngcgui
set ay(any,entrykeys,special) {x X y Y z Z a A b B c C u U v V w W d D}
set ay(any,dir,just) "/tmp/ngcgui_bak" ;# set to "" to disable
set ay(any,aspect) horiz
set ay(any,font) {Helvetica -10 normal}
set ay(any,width,comment) 12
set ay(any,width,varname) 12
set ay(any,img,width,max) 320 ;# subsample image to this max size
set ay(any,img,height,max) 240 ;# subsample image to this max size
# options currently available with embed_in_axis only
set ::ngc(opt,nonew) 0 ;# default allows new
set ::ngc(opt,noremove) 0 ;# default allows remove
set ::ngc(opt,noauto) 0 ;# default is autosend
set ::ngc(opt,noinput) 0 ;# default is to show an input frame
set ::ngc(opt,noiframe) 0 ;# default uses a separate toplevel for img
set ::ngc(opt,nom2) 0 ;# default use % at start and end
# instead of m2 at 3end
} ;# preset
proc ::ngcgui::gui {hdl mode args} {
# use ::ngcgui::preset for required ::ngc($hdl,) items and defaults
# standalone invoke: ::ngcgui::gui $hdl standalone wframe
# embedded invoke: ::ngcgui::gui $hdl create wframe
switch $mode {
standalone {
set ::ngc($hdl,standalone) 1
set w [::ngcgui::gui $hdl create $args]
return $w
}
create {
if {"$hdl" == ""} {return -code error "hdl is null"}
# mandatory arg for mode==create is a frame
# caller packs/unpacks wframe which must be a valid name
# but not exist yet
set wframe [lindex $args 0]
initgui $hdl
set ::ngc($hdl,l,width) 10 ;# min lside width, see also tw
if {"$::ngc(any,dir,just)" == ""} {
unset ::ngc(any,dir,just) ;# disable feature:
} else {
if { [file isdirectory $::ngc(any,dir,just)] \
&& [file writable $::ngc(any,dir,just)] \
} {
# ok
} else {
if [catch {file mkdir $::ngc(any,dir,just)} msg] {
puts stdout $msg ;# no such dir for example
return "" ;# something bad happened
}
}
}
if {"$wframe" == ""} {
return -code error "gui:create no arg for wframe"
}
set wframe [frame $wframe] ;# wframe specifies name, create it here
pack $wframe -anchor nw -fill none -expand 0 ;# NB
set ::ngc($hdl,top) [winfo toplevel $wframe]
set ::ngc($hdl,topf) $wframe ;# ok for embed_in_axis, ok standalone
if {"$::ngc($hdl,dir)" == ""} {set ::ngc($hdl,dir) .}
# defaults:
set ::ngc($hdl,id) 0
set ::ngc($hdl,savect) 0
conf $hdl restart,widget state disabled
set ::ngc($hdl,ftypes,subfile) { {{GCODE,GCMC} {.ngc .gcmc}} }
set ::ngc($hdl,ftypes,other) { {{NGC} {.ngc}} }
# initializations:
set ::ngc($hdl,data,preamble) ""
set ::ngc($hdl,data,postamble) ""
# special frame for embed,axis
set removable 0; set newable 0
if {[info exists ::ngc(embed,axis)] } {
if !$::ngc($hdl,standalone) {
if {!$::ngc(opt,noremove) || $::ngc($hdl,chooser)} {
set removable 1
}
if {!$::ngc(opt,nonew) || $::ngc($hdl,chooser)} {
set newable 1
}
}
tabmanage $::ngc($hdl,axis,page) $wframe \
"$::ngc(any,app)-$hdl" \
::ngc($hdl,info) \
$removable $newable
}
set wframe [frame $wframe.[qid]]
set bw 8
set tw 10 ;# min text width (default is 20) see also l,width
switch $::ngc(any,aspect) {
vert {
set wI [frame $wframe.input -bd 1 -relief sunken] ;# input frame
set wO [frame $wframe.output -bd 1 -relief sunken] ;# output frame
set wV [frame $wframe.var] ;# variable frame
set wC [frame $wframe.create -bd 1 -relief sunken] ;# create frame
set wE [frame $wframe.exit -bd 1 -relief sunken] ;# exit frame
pack $wI -side top -fill x -expand 1 -anchor n
pack $wE -side bottom -fill x -expand 1 -anchor s
pack $wO -side bottom -fill x -expand 1 -anchor s
pack $wC -side bottom -fill x -expand 1 -anchor n
pack $wV -side top -fill x -expand 1 -anchor n
set ::ngc($hdl,pack,positional) top
}
horiz {
set wL [frame $wframe.left -bd 2 -relief ridge] ;# left frame
set wI [frame $wL.input -bd 0 -relief sunken] ;# input frame
set wO [frame $wL.output -bd 0 -relief sunken] ;# output frame
set wC [frame $wL.create -bd 0 -relief sunken] ;# create frame
set wE [frame $wL.exit -bd 0 -relief sunken] ;# exit frame
set wV [frame $wframe.var -bd 0 -relief flat] ;# variable frame
pack $wL -side left -fill x -expand 1 -anchor nw
pack $wI -side top -fill x -expand 1 -anchor n
pack $wO -side top -fill x -expand 1 -anchor n
pack $wE -side bottom -fill x -expand 1 -anchor s
pack $wC -side bottom -fill x -expand 1 -anchor s
pack $wV -side left -fill x -expand 1 -anchor n
set ::ngc($hdl,pack,positional) left
$wframe config -relief ridge -bd 2
}
default {return -code error ngc::gui:aspect <$aspect>}
}
set ::ngc($hdl,varframe) $wV
set ::ngc($hdl,iframe) $wI
image_init $hdl
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
#pack [label $w.[qid] -anchor w -text "Input Files" \
# -width $::ngc($hdl,l,width)\
# -bg $::ngc(any,color,title) -relief groove] -fill x -expand 1
pack [label $w.[qid] -anchor w -text "[_ "Controls"]" \
-width $::ngc($hdl,l,width)\
-bg $::ngc(any,color,title) -relief groove] -fill x -expand 1
# wI inputs
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [button $w.[qid] -font $::ngc(any,font) \
-pady 0 -width $bw -text "[_ "Preamble"]" \
-command "::ngcgui::gui $hdl getpreamble"]
set ::ngc($hdl,begin,widget) $b
pack $b -side left -expand 0
set e [entry $w.e -width $tw -font $::ngc(any,font) \
-textvariable ::ngc($hdl,dname,preamble)]
bind $e <Return> [list ::ngcgui::readfile $hdl preamble]
pack $e -side left -fill x -expand 1
set ::ngc($hdl,preamble,widget) $e
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [button $w.[qid] -font $::ngc(any,font) \
-pady 0 -width $bw -text "[_ "Subfile"]" \
-command "::ngcgui::gui $hdl getsubfile"]
pack $b -side left -expand 0
set e [entry $w.e -width $tw -font $::ngc(any,font) \
-textvariable ::ngc($hdl,dname,subfile)]
bind $e <Return> [list ::ngcgui::readfile $hdl subfile]
pack $e -side left -fill x -expand 1
set ::ngc($hdl,subfile,widget) $e
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [button $w.[qid] -font $::ngc(any,font) \
-pady 0 -width $bw -text "[_ "Postamble"]" \
-command "::ngcgui::gui $hdl getpostamble"]
pack $b -side left -expand 0
set e [entry $w.e -width $tw -font $::ngc(any,font) \
-textvariable ::ngc($hdl,dname,postamble)]
bind $e <Return> [list ::ngcgui::readfile $hdl postamble]
pack $e -side left -fill x -expand 1
set ::ngc($hdl,postamble,widget) $e
# set w [frame $wI.[qid]]
# pack $w -fill x -expand 1
# pack [label $w.[qid] -anchor w -text "Options" \
# -bg $::ngc(any,color,title) -relief groove] -fill x -expand 1
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [checkbutton $w.[qid] -anchor w -font $::ngc(any,font) \
-text "[_ "Retain values on Subfile read"]" \
-command [list ::ngcgui::aftertoggle $hdl retainvalues] \
-variable ::ngc($hdl,retainvalues)]
pack $b -side left -fill x -expand 1
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [checkbutton $w.[qid] -anchor w -font $::ngc(any,font) \
-text "[_ "Expand subroutine"]" \
-command [list ::ngcgui::aftertoggle $hdl expandsubroutine] \
-variable ::ngc($hdl,expandsubroutine)]
pack $b -side left -fill x -expand 1
set ::ngc($hdl,expandsubroutine,widget) $b
if {1} {
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [checkbutton $w.[qid] -anchor w -font $::ngc(any,font) \
-text "[_ "Autosend"]" \
-command [list ::ngcgui::aftertoggle $hdl auto] \
-variable ::ngc($hdl,auto)]
pack $b -side left -fill x -expand 1
}
if {0} {
# take up too much room
set w [frame $wI.[qid]]
pack $w -fill x -expand 1
set b [checkbutton $w.[qid] -anchor w -font $::ngc(any,font) \
-text "[_ "Verbose ngcfile"]" \
-command [list ::ngcgui::aftertoggle $hdl verbose] \
-variable ::ngc($hdl,verbose)]
pack $b -side left -fill x -expand 1
}
# wC create frame
# used fixed widths so buttons stay same when text is changed
set w [frame $wC.[qid]]
pack $w -side top -fill x -expand 1
set b [button $w.[qid] -text "[_ "Create Feature"]" -font $::ngc(any,font) \
-width 14 -padx 1\
-command "::ngcgui::gui $hdl savesection"]
pack $b -side left -fill x -expand 1
set ::ngc($hdl,save,widget) $b
set text "[_ "MakeFile"]"
if $::ngc($hdl,auto) {set text "[_ "Finalize"]"}
set b [button $w.[qid] -state disabled -font $::ngc(any,font) \
-fg $::ngc(any,color,prompt) \
-width 8 -padx 1\
-text "$text" -command "::ngcgui::gui $hdl finalize"]
pack $b -side left -fill x -expand 1
set ::ngc($hdl,finalize,widget) $b
set w [frame $wC.[qid]]
pack $w -fill x -expand 1
pack [label $w.[qid] -width 0 -font $::ngc(any,font) \
-pady 0 -relief flat \
-textvariable ::ngc($hdl,savect)] -side left -fill x -expand 0
if {!$::ngc(opt,noinput) || $::ngc($hdl,chooser)} {
# reread notapplicable with no controls
set b [button $w.[qid] -width 2 -font $::ngc(any,font) \
-padx 0 -pady 0 -text "[_ "Reread"]" \
-state disabled \
-command [list ::ngcgui::reread $hdl] \
]
pack $b -side left -fill x -expand 1
set ::ngc($hdl,reread,widget) $b
}
set b [button $w.[qid] -width 2 -font $::ngc(any,font) \
-padx 0 -pady 0 -text "[_ "Restart"]" \
-state disabled \
-command [list ::ngcgui::message $hdl restart] \
]
pack $b -side left -fill x -expand 1
set ::ngc($hdl,restart,widget) $b
# sendfile,widget button is forgettable
# use wC frame avoids problems with ctrl-a resizing app
set b [button $wC.[qid] -state disabled -font $::ngc(any,font) \
-pady 1 \
-text "[_ "SendFileToAxis"]" \
-command [list ::ngcgui::sendfile $hdl]]
pack $b -side bottom -fill x -expand 1
set ::ngc($hdl,sendfile,widget) $b
if $::ngc($hdl,auto) {
pack forget $::ngc($hdl,sendfile,widget)
$::ngc($hdl,finalize,widget) conf -fg $::ngc(any,color,prompt)
}
if $::ngc($hdl,standalone) {
set b [button $w.[qid] -takefocus 0 -font $::ngc(any,font) \
-pady 0 -text "[_ "Exit"]" \
-command [list ::ngcgui::bye $hdl]]
pack $b -side left -fill none -expand 0
}
# wO output frame
set w [frame $wO.[qid] -bd 2]
pack $w -side top -fill x -expand 1
set ::ngc($hdl,msg,widget) [label $wE.[qid] \
-width 20\
-relief sunken \
-anchor w] ;# update with config
pack $::ngc($hdl,msg,widget) -side left -fill x -expand 1
# wE exit frame obsoleted
#------------------------------------------------------------------------------
if {"$::ngc($hdl,fname,preamble)" != ""} {
set ::ngc($hdl,fname,preamble) [string trim $::ngc($hdl,fname,preamble)]
::ngcgui::gui $hdl readpreamble
}
if {"$::ngc($hdl,fname,subfile)" != ""} {
set ::ngc($hdl,fname,subfile) [string trim $::ngc($hdl,fname,subfile)]
::ngcgui::gui $hdl readsubfile
}
if {"$::ngc($hdl,fname,postamble)" != ""} {
set ::ngc($hdl,fname,postamble) \
[string trim $::ngc($hdl,fname,postamble)]
::ngcgui::gui $hdl readpostamble
}
if [info exists ::ngc($hdl,fail)] {
puts stdout "\n$::ngc(any,app):[_ "Unrecoverable problem"]:\n<$hdl>$::ngc($hdl,fail)"
::ngcgui::deletepage $::ngc($hdl,axis,page)
return
}
update ;# ensure entry variables are updated before starting checks
periodic_checks $hdl
bindings $hdl init
if ![info exists ::ngc(embed,axis)] [list updownkeys $::ngc($hdl,top)]
after 2000 [list ::ngcgui::showmessage $hdl startup]
return $wframe
# ::ngcgui::gui-create-end
}
getpreamble {
if {$::ngc($hdl,fname,preamble) == ""} {
set idir $::ngc($hdl,dir)
} else {
set idir [file dirname $::ngc($hdl,fname,preamble)]
}
set filename [tk_getOpenFile \
-title "$::ngc(any,app) Preamble file" \
-defaultextension .ngc \
-initialfile [file tail $::ngc($hdl,fname,preamble)] \
-initialdir $idir \
-filetypes $::ngc($hdl,ftypes,other) \
]
set filename [string trim $filename]
if {"$filename" == ""} return
check_path $filename
set ::ngc($hdl,fname,preamble) $filename
::ngcgui::gui $hdl readpreamble
return
}
readpreamble {
if { ![string match *.ngc $::ngc($hdl,fname,preamble)]\
&& [file readable "$::ngc($hdl,fname,preamble).ngc"]} {
set ::ngc($hdl,fname,preamble) "$::ngc($hdl,fname,preamble).ngc"
}
set ::ngc($hdl,data,preamble) ""
if {"$::ngc($hdl,fname,preamble)" == ""} {
# message $hdl nullpreamble
return
} else {
if [catch {set fpre [open $::ngc($hdl,fname,preamble) r]} msg] {
lappend emsg $msg
showerr $emsg
message $hdl preambleerror
if {$::ngc(opt,noinput) && !$::ngc($hdl,chooser)} {
set ::ngc($hdl,fail) "preamble:$msg" ;# unrecoverable
}
return
}
set ::ngc($hdl,dname,preamble) [file tail $::ngc($hdl,fname,preamble)]
lappend ::ngc($hdl,data,preamble) \
"($::ngc(any,app): preamble file: $::ngc($hdl,fname,preamble))"
# dont copy some items to preamble
while {![eof $fpre]} {
gets $fpre line
set l [string map {" " "" " " ""} $line] ;#sp,tab to ""
if {"$l" == ""} continue
if ![string match "(not_a_subfile)" $line] {
lappend ::ngc($hdl,data,preamble) $line
}
}
close $fpre
set ::ngc($hdl,fname,preamble,time) \
[file mtime $::ngc($hdl,fname,preamble)]
}
message $hdl readpreamble
return
}
getpostamble {
if {$::ngc($hdl,fname,postamble) == ""} {
set idir $::ngc($hdl,dir)
} else {
set idir [file dirname $::ngc($hdl,fname,postamble)]
}
set filename [tk_getOpenFile \
-title "$::ngc(any,app) [_ "Postamble file"]" \
-defaultextension .ngc \
-initialfile [file tail $::ngc($hdl,fname,postamble)] \
-initialdir $idir \
-filetypes $::ngc($hdl,ftypes,other) \
]
set filename [string trim $filename]
if {"$filename" == ""} return
check_path $filename
set ::ngc($hdl,fname,postamble) $filename
::ngcgui::gui $hdl readpostamble
return
}
readpostamble {
if { ![string match *.ngc $::ngc($hdl,fname,postamble)]\
&& [file readable "$::ngc($hdl,fname,postamble).ngc"]} {
set ::ngc($hdl,fname,postamble) "$::ngc($hdl,fname,postamble).ngc"
}
set ::ngc($hdl,data,postamble) ""
if {"$::ngc($hdl,fname,postamble)" == ""} {
# message $hdl nullpostamble
return
} else {
if [catch {set fpost [open $::ngc($hdl,fname,postamble) r]} msg] {
lappend emsg $msg
showerr $emsg
message $hdl postambleerror
return
}
set ::ngc($hdl,dname,postamble) [file tail $::ngc($hdl,fname,postamble)]
lappend ::ngc($hdl,data,postamble) \
"($::ngc(any,app): postamble file: $::ngc($hdl,fname,postamble))"
while {![eof $fpost]} {
gets $fpost line
lappend ::ngc($hdl,data,postamble) "$line"
}
close $fpost
set ::ngc($hdl,fname,postamble,time) \
[file mtime $::ngc($hdl,fname,postamble)]
}
message $hdl readpostamble
return
}
getsubfile {
if {$::ngc($hdl,fname,subfile) == ""} {
set idir $::ngc($hdl,dir)
} else {
set idir [file dirname $::ngc($hdl,fname,subfile)]
}
set filename [tk_getOpenFile \
-title "$::ngc(any,app) [_ "Subroutine file"]" \
-defaultextension .ngc \
-initialfile [file tail $::ngc($hdl,fname,subfile)] \
-initialdir $idir \
-filetypes $::ngc($hdl,ftypes,subfile) \
]
set filename [string trim $filename]
if {"$filename" == ""} return
check_path $filename
set ::ngc($hdl,fname,subfile) $filename
::ngcgui::gui $hdl readsubfile
return
}
readsubfile {
set parsecmd ::ngcgui::parse_ngc
if {[string match *.gcmc $::ngc($hdl,fname,subfile)] } {
set parsecmd ::ngcgui::parse_gcmc
set ::ngc($hdl,gcmc,file) $::ngc($hdl,fname,subfile)
$::ngc($hdl,expandsubroutine,widget) configure -state disable
} else {
# in case earlier an earlier find for gcmc failed;
catch {unset ::ngc($hdl,gcmc,file)}
$::ngc($hdl,expandsubroutine,widget) configure -state normal
}
if { ![string match *.ngc $::ngc($hdl,fname,subfile)] \
&& ![string match *.gcmc $::ngc($hdl,fname,subfile)] \
} {
set ::ngc($hdl,fname,subfile) "$::ngc($hdl,fname,subfile).ngc"
}
# uses two pack/unpack frames wP
set ew 6; set bw 9
# wP positional parameters
set wP $::ngc($hdl,varframe).positional ;# variable frame positional parms
if [winfo exists $wP] {destroy $wP}
set wP [frame $wP -bd 2 -relief ridge]
pack $wP -side $::ngc($hdl,pack,positional) -fill x -expand 1 -anchor n
if { ![string match *.ngc $::ngc($hdl,fname,subfile)]\
&& [file readable "$::ngc($hdl,fname,subfile).ngc"]} {
set ::ngc($hdl,fname,subfile) "$::ngc($hdl,fname,subfile).ngc"
}
# read and parse the file
set ::ngc($hdl,dname,subfile) [file tail $::ngc($hdl,fname,subfile)]
if ![$parsecmd $hdl ::ngc $::ngc($hdl,fname,subfile)] {
# case where user can't recover
if {$::ngc(opt,noinput) && !$::ngc($hdl,chooser)} {
set ::ngc($hdl,fail) "subfile:$::ngc($hdl,parse,msg)";# unrecoverable
}
showerr $::ngc($hdl,parse,msg)
# try to display name of failed file:
message $hdl parseerror
# 101024:09.13 leave them alone
# set ::ngc($hdl,fname,subfile) "" ;# prevents color change
# set ::ngc($hdl,dname,subfile) "" ;# in periodic_checks
catch {pack forget $wP}
return
}
set ::ngc($hdl,fname,subfile,time) \
[file mtime $::ngc($hdl,fname,subfile)]
set w [frame $wP.[qid]]
pack $w -side top -fill x -expand 1
pack [label $w.[qid] -text "[_ "Positional Parameters"]" \
-bg $::ngc(any,color,title) -anchor w -relief groove] \
-side top -fill x -expand 1
# Positional parameters
# find retained values for numbered parms (#n) with
# a byname association
foreach n [array names ::ngc $hdl,arg,name,*] {
# example:
# if ::ngc($hdl,arg,name,04) == xloc
# and ::ngc($hdl,arg,byname,xloc) == 33
# then set ::ngc($hdl,arg,value,04) 33
# else set ::ngc($hdl,arg,value,04) ""
set name $::ngc($n)
set num [string range $n [expr 1 + [string last , $n]] end]
if {[info exists ::ngc($hdl,arg,byname,$name)]} {
set ::ngc($hdl,arg,value,$num) $::ngc($hdl,arg,byname,$name)
} else {
# use default value if available
if [info exists ::ngc($hdl,arg,dvalue,$num)] {
set ::ngc($hdl,arg,value,$num) $::ngc($hdl,arg,dvalue,$num)
} else {
set ::ngc($hdl,arg,value,$num) ""
}
}
}
# Positional parameters entries, provide two frames
set pnamelist [lsort [array names ::ngc $hdl,arg,name,*]]
set wP1 [frame $wP.[qid] -relief flat]
set wP2 [frame $wP.[qid] -relief flat]
set wP3 [frame $wP.[qid] -relief flat]
set npos [llength $pnamelist]
pack $wP1 -side left -anchor n -fill x -expand 1
# a weird space is left if you dont do these separately:
if {$npos > $::ngc(any,pentries)} {
pack $wP2 -side left -anchor n -fill x -expand 1
if {$npos > [expr 2*$::ngc(any,pentries)]} {
pack $wP3 -side left -anchor n -fill x -expand 1
}
}
set ct 0
foreach v $pnamelist {
incr ct
if {$ct <= $::ngc(any,pentries)} {
set fdata [frame $wP1.[qid]]
} elseif {$ct <= [expr 2* $::ngc(any,pentries)]} {
set fdata [frame $wP2.[qid]]
} else {
set fdata [frame $wP3.[qid]]
}
pack $fdata -side top -fill x -expand 1
set i1 [string last , $v]
set num [string range $v [expr 1+$i1] end]
if [info exists ::ngc($hdl,arg,name,$num)] {
set name $::ngc($hdl,arg,name,$num)
} else {
set name [format %d $num]
}
scan $num %d onum ;# ==>onum avoid octalinterpretation of 08,09
set num02 [format %02d $onum]
set l [label $fdata.[qid] -text [format %#2d $onum] -anchor e \
-takefocus 0 -relief ridge -width 2]
pack $l -side left -fill x -expand 0
# use entry since it can be expanded by user to see overfill
if {$::ngc(any,width,varname) != 0} {
set l [entry $fdata.[qid] -state readonly -font $::ngc(any,font) \
-textvariable ::ngc($hdl,arg,name,$num) \
-takefocus 0 -justify right -relief groove \
-width $::ngc(any,width,varname)]
pack $l -side left -fill x -expand 0
}
set tvar ::ngc($hdl,arg,value,$num)
set e [entry $fdata.[qid] \
-width $ew \
-font $::ngc(any,font) \
-textvariable $tvar\
-validate all\
-validatecommand \
[list ::ngcgui::validateNumber $hdl $tvar %W %s %P]]
foreach k $::ngc(any,entrykeys,special) {
bind $e <Key-$k> \
[list ::ngcgui::entrykeybinding %K %W ::ngc($hdl,arg,value,$num)]
}
if [info exists ::ngc(embed,axis)] [list updownkeys $e]
set ::ngc($hdl,arg,entrywidget,$num02) $e
pack $e -side left
set l [entry $fdata.[qid] -state readonly -font $::ngc(any,font) \
-textvariable ::ngc($hdl,arg,comment,$num02) \
-takefocus 0 -relief groove \
-width $::ngc(any,width,comment)\
]
pack $l -side left -fill x -expand 1
}
dcheck $hdl
set ::ngc($hdl,dir) [file dirname $::ngc($hdl,fname,subfile)]
message $hdl readsubfile
if [info exists ::ngc(embed,axis)] {
set tabname $::ngc($hdl,dname,subfile)
if {[string match *.ngc $tabname] } {
set idx [string last .ngc $tabname]
set tabname [string replace $tabname $idx end ""]
} elseif {[string match *.gcmc $tabname] } {
set idx [string last .gcmc $tabname]
set tabname [string replace $tabname $idx end ""]
}
# show last subfile used as page name
$::ngc(any,axis,parent) itemconfigure $::ngc($hdl,axis,page) \
-createcmd "::ngcgui::pagecreate $hdl"\
-raisecmd "::ngcgui::pageraise $hdl"\
-leavecmd "::ngcgui::pageleave $hdl"\
-text "$tabname"
# current tab names for other hdls
set names ""
for {set i 0} {$i <= $::ngc(embed,hdl)} {incr i} {
if {$i == $hdl} continue ;# exclude name for this hdl
if [info exists ::ngc($i,axis,page)] {
lappend names [$::ngc(any,axis,parent) \
itemcget $::ngc($i,axis,page) -text]
}
}
if {[lsearch $names "$tabname"] >= 0} {
# name exists, make unique name for page
set ct 1
while 1 {
set tryname ${tabname}-$ct
if {[lsearch $names "$tryname"] < 0} break
incr ct
if {$ct>100} {return -code error "readsubfile:problem<$trytabname>"}
}
$::ngc(any,axis,parent) itemconfigure $::ngc($hdl,axis,page) \
-text "$tryname"
}
}
return ;# readsubfile
}
parmcheck {
if ![info exists ::ngc($hdl,argct)] {
if {"$::ngc($hdl,fname,subfile)" == ""} {
lappend err "[_ "No Subfile specified"]"
}
lappend err "[_ "No parameters yet"]"
} else {
for {set i 1} {$i <= $::ngc($hdl,argct)} {incr i} {
set num02 [format %02d $i]
set token $::ngc($hdl,arg,name,$num02)
# nuisance spaces cause problems:
set ::ngc($hdl,arg,value,$num02) \
[string trim $::ngc($hdl,arg,value,$num02)]
if {"$::ngc($hdl,arg,value,$num02)" == ""} {
lappend err "[_ "Missing value for parm"] #$i ($token)"
}
}
}
if [info exists err] {
showerr $err
message $hdl parmerr
return 0 ;# error
}
return 1 ;# ok
}
setoutfile {
if {$::ngc($hdl,fname,outfile) == ""} {
set idir $::ngc($hdl,dir)
} else {
set idir [file dirname $::ngc($hdl,fname,outfile)]
}
if {"$::ngc($hdl,fname,outfile)" == "" } {
set ::ngc($hdl,fname,outfile) tmp
}
set filename [tk_getSaveFile \
-title "$::ngc(any,app) [_ "Output file"]" \
-defaultextension .ngc \
-initialfile [file tail $::ngc($hdl,fname,outfile)] \
-initialdir $idir \
-filetypes $::ngc($hdl,ftypes,subfile) \
]
set filename [string trim $filename]
# sometimes leading blanks get in
set filename [string map {" " "" " " ""} $filename] ;#sp,tab to ""
if {$filename == ""} {
set ::ngc($hdl,fname,outfile) "" ;# canceled
return
}
set ::ngc($hdl,fname,outfile) $filename
message $hdl setoutfile
return
}
savesection {
::ngcgui::readfile $hdl preamble
::ngcgui::readfile $hdl postamble
# save,widget has multiple presentations to steer user
if ![::ngcgui::gui $hdl parmcheck] {
return
}
if $::ngc($hdl,verbose) {
lappend ::ngc($hdl,data,section) \
"($::ngc(any,app): files: <$::ngc($hdl,fname,preamble) $::ngc($hdl,fname,subfile) $::ngc($hdl,fname,postamble)>)"
}
# note: this line will be replaced on file output with a count
# that can include multiple tab pages
lappend ::ngc($hdl,data,section) "#<_feature:> = $::ngc($hdl,savect)"
if {"$::ngc($hdl,fname,preamble)" == "IMMEDIATE"} {
# indicates preamble is interpreted as
# immediate commands separated by semicolons
# example ":t1m6;m1"
set ::ngc($hdl,immediate,preamble) [string range \
$::ngc($hdl,dname,preamble) 1 end]
if $::ngc($hdl,verbose) {
lappend ::ngc($hdl,data,section) \
"($::ngc(any,app): IMMEDIATE preamble:)"
}
foreach line [split $::ngc($hdl,immediate,preamble) \;] {
lappend ::ngc($hdl,data,section) [string trim $line]
}
unset ::ngc($hdl,immediate,preamble)
} else {
for {set i 0} {$i < [llength $::ngc($hdl,data,preamble)]} {incr i} {
lappend ::ngc($hdl,data,section) \
[lindex $::ngc($hdl,data,preamble) $i]
}
}
if [info exists ::ngc($hdl,gcmc,file)] {
if ![savesection_gcmc $hdl] {return} ;# .gcmc file
} else {
if ![savesection_ngc $hdl] {return} ;# conventional .ngc file
}
if {"$::ngc($hdl,fname,postamble)" == "IMMEDIATE"} {
# indicates postamble is interpreted as
# immediate commands separated by semicolons
# example ":t1m6;m1"
set ::ngc($hdl,immediate,postamble) [string range \
$::ngc($hdl,dname,postamble) 1 end]
if $::ngc($hdl,verbose) {
lappend ::ngc($hdl,data,section) \
"($::ngc(any,app): IMMEDIATE postamble:)"
}
foreach line [split $::ngc($hdl,immediate,postamble) \;] {
lappend ::ngc($hdl,data,section) [string trim $line]
}
unset ::ngc($hdl,immediate,postamble)
} else {
for {set i 0} {$i < [llength $::ngc($hdl,data,postamble)]} {incr i} {
lappend ::ngc($hdl,data,section) \
[lindex $::ngc($hdl,data,postamble) $i]
}
}
message $hdl savesection
return
}
finalize {
if {$::ngc($hdl,savect) == 0} {
return ;# silently (may be bound to key)
}
set doall 1 ;# default
if {![info exists ::ngc(embed,axis)]} {
set hdllist $hdl
} else {
# find all tabpages with saved features
# order of tabpage names determines execution order
set tnames ""
foreach p [$::ngc(any,axis,parent) pages] {
set h [pagetohdl $p]
if {$h >= 0} {
if {$::ngc($h,savect) == 0} {continue}
lappend hdllist $h
if [info exists ::ngc($h,axis,page)] {
lappend tnames [$::ngc(any,axis,parent) \
itemcget $::ngc($h,axis,page) -text]
}
}
}
set thisone [$::ngc(any,axis,parent) \
itemcget $::ngc($hdl,axis,page) -text]
if {[llength $hdllist] > 1} {
set ans [tk_dialog .foo \
"[_ "Multiple Tabs with Features"]" \
"[_ "Finalize all Tabs?"]\n [_ "Order"]:<$tnames>" \
questhead 0 \
"[_ "No, just this page"] <$thisone>" Yes Cancel\
]
switch $ans {
0 { set hdllist $hdl; set doall 0; #NO}
1 {}
2 {showmessage $hdl cancel; return}
}
}
}
set endhdl [lindex $hdllist end]
if {$::ngc($hdl,auto) && ![sendaxis $hdl ping]} {
set ::ngc($hdl,auto) 0
$::ngc($hdl,finalize,widget) conf -fg $::ngc(any,color,prompt)
lappend msg "[_ "Axis is not responding"]"
lappend msg "[_ "Error: "]$::ngc($hdl,axis,error)"
lappend msg ""
lappend msg "[_ "Autosend disabled, Ctrl-A toggles autosend"]"
lappend msg ""
lappend msg "[_ "File saving enabled -- Finalize to save"]"
showerr $msg nosort
message $hdl senderror
return
}
if $::ngc($hdl,auto) {
set ::ngc($hdl,fname,outfile) $::ngc($hdl,fname,autosend)
} else {
# open and write fname,outfile
title $::ngc($hdl,top) "$::ngc(any,app) <>"
::ngcgui::gui $hdl setoutfile
if {"$::ngc($hdl,fname,outfile)" == ""} {
message $hdl usercancel
return
}
if {![string match *.ngc $::ngc($hdl,fname,outfile)]} {
lappend msg "[_ "Require .ngc suffix for filename"]"
showerr $msg
message $hdl writeerror
return
}
if { "$::ngc($hdl,fname,outfile)" == "$::ngc($hdl,fname,subfile)" \
|| "$::ngc($hdl,fname,outfile)" == "$::ngc($hdl,fname,preamble)" \
|| "$::ngc($hdl,fname,outfile)" == "$::ngc($hdl,fname,postamble)" \
} {
set msg ""
lappend msg "[_ "Disallowed overwrite of"] $::ngc($hdl,fname,outfile)"
showerr $msg
message $hdl writeerror
return
}
}
if [catch {set fout [open $::ngc($hdl,fname,outfile) w]} msg] {
lappend emsg $msg
showerr $emsg
message $hdl writeerror
return
}
if {$::ngc(opt,nom2) || [info exists ::ngcgui::control(any,nom2)]} {
puts $fout "%"
puts $fout "($::ngc(any,app): nom2 option)"
}
set featurect 0;
set date [dt]
foreach thdl $hdllist {
# the string FEATURE is used so files generated by ngcgui can
# be detected and excluded as subfile candidates
puts $fout "($::ngc(any,app): [_ "FEATURE"] $date)"
for {set i 0} {$i < [llength $::ngc($thdl,data,section)]} {incr i} {
set line [lindex $::ngc($thdl,data,section) $i]
if {[string first "#<_feature:>" $line] >= 0} {
# instead of current $line, output feature count (zero referenced)
puts $fout \
"($::ngc(any,app): [_ "feature line added"]) #<_feature:> = $featurect"
incr featurect 1
} else {
puts $fout $line
}
}
} ;# for hdllist
if {$::ngc(opt,nom2) || [info exists ::ngcgui::control(any,nom2)]} {
puts $fout "%"
} else {
if $::ngc($endhdl,verbose) {
puts $fout "($::ngc(any,app): m2 [_ "line added"]) m2 (g54 [_ "activated"])"
} else {
puts $fout "m2 (m2 [_ "restores"] g54)"
}
}
close $fout
set ::ngc(any,gcmc,id) 0 ;# restart after finalize
set ::ngc($hdl,last,outfile) $::ngc($hdl,fname,outfile)
# just in case you need it later, save a dated copy in /tmp
if [info exists ::ngc(any,dir,just)] {
set base [file tail $::ngc($hdl,fname,outfile)]
set savename [file join $::ngc(any,dir,just) [dt].${base}]
if [catch {file copy $::ngc($hdl,fname,outfile) $savename} msg] {
lappend emsg "<$hdl>$msg"
showerr $emsg
message $hdl writeerror
return
}
}
if {$::ngc($hdl,auto)} {
if ![::ngcgui::sendfile $hdl] {
return ;# send failed, user can start axis or Ctrl-a
}
}
foreach thdl $hdllist {
set ::ngc($thdl,savect) 0
conf $hdl restart,widget state disabled
set ::ngc($thdl,data,section) ""
message $thdl finalize
} ;# for
title $::ngc($thdl,top) "$::ngc(any,app) \
<[file tail $::ngc($thdl,fname,outfile)]>"
return
}
default {return -code error "::ngcgui::gui: unknown mode <$mode>"}
}
puts stdout "[_ "NOTREACHED mode"]=<$mode>"
} ;# gui
proc ::ngcgui::savesection_ngc {hdl} {
# could check for number here using %f
set pfmt "%12s = %s" ;# positional
set cfmt "(%11s = %12s = %12s)" ;# positional comment form
if {$::ngc($hdl,expandsubroutine)} {
# id for unique label when expanding multiple sub files
set id $::ngc($hdl,id)
set uwidth 3 ;# extra width for unique label 000-999
# $uwdith characters in unique ids
set id [format %0${uwidth}d $::ngc($hdl,id)]
incr ::ngc($hdl,id)
lappend ::ngc($hdl,data,section) \
"([_ "Positional parameters for"] $::ngc($hdl,fname,subfile):)"
for {set i 1} {$i <= $::ngc($hdl,argct)} {incr i} {
set num02 [format %02d $i]
set name $::ngc($hdl,arg,value,$num02)
lappend ::ngc($hdl,data,section) [format $pfmt #$i $name ]
}
# expand the subroutine in place
lappend ::ngc($hdl,data,section) \
"([_ "expanded file"]: $::ngc($hdl,fname,subfile))"
for {set i 0} {$i < $::ngc($hdl,sublines)} {incr i} {
if [info exists ::ngc_sub($hdl,$i,label)] {
lappend ::ngc($hdl,data,section) \
"o<$id$::ngc_sub($hdl,$i,label)> $::ngc_sub($hdl,$i)"
} else {
lappend ::ngc($hdl,data,section) \
[format %${uwidth}s%s "" " $::ngc_sub($hdl,$i)"]
}
}
} else {
# insert the subroutine call
if $::ngc($hdl,verbose) {
lappend ::ngc($hdl,data,section) \
"($::ngc(any,app): [_ "call subroutine file"]: $::ngc($hdl,fname,subfile))"
lappend ::ngc($hdl,data,section) "($::ngc(any,app): positional parameters:)"
}
set cline "o<$::ngc($hdl,subroutine,name)> call "
for {set i 1} {$i <= $::ngc($hdl,argct)} {incr i} {
set num02 [format %02d $i]
set name $::ngc($hdl,arg,name,$num02)
if {[string first # $name] == 0} {set name "?"}
# documenting comment
if $::ngc($hdl,verbose) {
lappend ::ngc($hdl,data,section) \
[format $cfmt #$i $name $::ngc($hdl,arg,value,$num02)]
}
set cline "$cline\[$::ngc($hdl,arg,value,$num02)\]"
}
lappend ::ngc($hdl,data,section) "$cline"
}
return 1 ;# ok
} ;# savesection_ngc
proc ::ngcgui::savesection_gcmc {hdl} {
#puts =====================================
#parray ::ngc $hdl,arg,*
#parray ::ngc $hdl,gcmc,*
#parray ::ngc any,gcmc,*
#parray ::ngc $hdl,argct
#puts =====================================
# could check for number here using %f
set cfmt "(%12s = %12s)" ;# positional comment form
# maybe implement later, expand after calling gcmc below
if {$::ngc($hdl,expandsubroutine)} {
set answer [tk_dialog .notdoneyet \
"Not done yet"\
"Expand subroutine not supported for gcmc files - continuing"\
warning -1 \
"OK"]
}
if ![info exists ::ngc(any,gcmc,id)] {
set ::ngc(any,gcmc,id) 0
}
incr ::ngc(any,gcmc,id) ;# id for any hdl
set funcname $::ngc(any,gcmc,funcname)
# gcmc chars: (allowed: [a-z0-9_-])
set funcname ${funcname}-[format %02d $::ngc(any,gcmc,id)]
# use first one found in searchpath:
set ifile [file normalize \
[pathto [file tail $::ngc($hdl,gcmc,file)]]]
if {"$ifile" == ""} {
return 0 ;# fail
}
set ::ngc($hdl,gcmc,realfile) $ifile
set ofile [file join $::ngc(any,gcmc,outdir) $funcname.ngc]
set cmd $::ngc(any,gcmc,executable)
set opts ""
if [info exists ::ngc(any,gcmc_include_path)] {
foreach dir [split $::ngc(any,gcmc_include_path) ":"] {
set opts "$opts --include $dir"
}
}
# note: gcmc adds the current directory
# to the search path as last entry.
# maybe also ?: set opts "$opts --include [file dirname $ifile]"
set opts "$opts --output $ofile"
set opts "$opts --gcode-function $funcname"
if [info exists ::ngc($hdl,gcmc,opts)] {
foreach opt $::ngc($hdl,gcmc,opts) {
set opts "$opts $opt"
}
}
if {$::ngc($hdl,argct) > 0} {
for {set i 1} {$i <= $::ngc($hdl,argct)} {incr i} {
set idx [format %02d $i]
# make all entry box values explicitly floating point
if [catch {set floatvalue [expr 1.0 * $::ngc($hdl,arg,value,$idx)]} msg] {
set answer [tk_dialog .gcmcerror \
"gcmc input ERROR" \
"<$::ngc($hdl,arg,value,$idx)> must be a number" \
error -1 \
"OK"]
return 0 ;# fail
}
set opts "$opts --define=$::ngc($hdl,arg,name,$idx)=$floatvalue"
}
}
# puts stdout " cmd=$cmd"
# puts stdout " opts=$opts"
# puts stdout " ifile=$ifile"
# puts stdout "funcname=$funcname"
# puts stdout " pwd=[pwd]"
# puts stdout " exists=[file exists $ifile]"
set eline "$cmd $opts $ifile"
if $::ngc($hdl,verbose) {
puts stdout "eline=$eline"
}
#tclsh considers any output on stderr as an error
# -ignorestderr lets it pass so that --precision 2
# would not cause an error but then there are no
# error messages even for hard ($? !=0) errors, just
# "child process exited abnormally"
# so warnings ($?=0) cause abort even though file created
# partial file may be left on error so you cant tell by existence
# so, parse each warning message
# parse messages on stderr from gcmc
set e_message ".*Runtime message\\(\\): *\(.*\)"
set e_warning ".*Runtime warning\\(\\): *\(.*\)"
set e_error ".*Runtime error\\(\\): *\(.*\)"
set m_txt ""; set w_txt ""; set e_txt ""; set compile_txt ""
if [catch {set result [eval exec $eline]} msg] {
if {[string length $msg] > $::ngc(any,max_msg_len)} {
set msg [string range $msg 0 $::ngc(any,max_msg_len)]
set msg "$msg ..."
}
set lmsg [split $msg \n]
foreach line $lmsg {
#puts l=$line
if {[regexp $e_message $line match txt]} {
set m_txt "$m_txt\n$txt"
} elseif { [regexp $e_warning $line match txt]} {
set w_txt "$w_txt\n$txt"
} elseif { [regexp $e_error $line match txt]} {
set e_txt "$e_txt\n$txt"
} else {
if {"$line" != ""} {
set compile_txt "$compile_txt\n$line"
}
}
}
if {"$m_txt" != ""} {
set answer [tk_dialog .gcmcinfor \
"gcmc INFO"\
"gcmc file:\n$ifile\n\n$m_txt"\
info -1 \
"OK"]
}
if {"$w_txt" != ""} {
set answer [tk_dialog .gcmcwarning \
"gcmc WARNING"\
"gcmc file:\n$ifile\n\n$w_txt"\
warning -1 \
"OK"]
}
if {"$e_txt" != ""} {
set answer [tk_dialog .gcmcerror \
"gcmc ERROR"\
"gcmc file:\n$ifile\n\n$e_txt"\
error -1 \
"OK"]
}
if {"$compile_txt" != ""} {
set answer [tk_dialog .gcmcerror \
"gcmc compile ERROR"\
"gcmc file:$compile_txt"\
error -1 \
"OK"]
}
if {"$e_txt" != ""} {
return 0 ;# fail
}
} else {
#puts "savesection_gcmc OK<$result>"
}
# insert the subroutine call
lappend ::ngc($hdl,data,section) \
"\n(NOTE: $funcname is provided by a one-time, gcmc-created file:)"
lappend ::ngc($hdl,data,section) \
"( $ofile)"
lappend ::ngc($hdl,data,section) \
"(gcmc: File: $::ngc($hdl,gcmc,realfile))"
lappend ::ngc($hdl,data,section) \
"(gcmc: Options: )"
if [info exists ::ngc($hdl,gcmc,opts)] {
foreach opt $::ngc($hdl,gcmc,opts) {
lappend ::ngc($hdl,data,section) \
"( $opt)"
}
}
lappend ::ngc($hdl,data,section) \
"(gcmc: Variable substitions:)"
for {set i 1} {$i <= $::ngc($hdl,argct)} {incr i} {
set num02 [format %02d $i]
set name $::ngc($hdl,arg,name,$num02)
lappend ::ngc($hdl,data,section) \
[format $cfmt $name $::ngc($hdl,arg,value,$num02)]
}
lappend ::ngc($hdl,data,section) "o<$funcname> call "
return 1 ;# ok
} ;# savesection_gcmc
proc ::ngcgui::conf {hdl wsuffix item value} {
set w $hdl,$wsuffix
if ![info exists ::ngc($w)] return
$::ngc($w) conf -$item $value
} ;# conf
proc ::ngcgui::reread {hdl} {
::ngcgui::gui $hdl readpreamble
::ngcgui::gui $hdl readsubfile
::ngcgui::gui $hdl readpostamble
} ;# reread
proc ::ngcgui::sendfile {hdl} {
if ![sendaxis $hdl ping] {
showerr $::ngc($hdl,axis,error) nosort
message $hdl senderror
return 0 ;# err
}
if ![sendaxis $hdl file] {
showerr $::ngc($hdl,axis,error) nosort
message $hdl senderror
return 0 ;# err
}
$::ngc($hdl,sendfile,widget) conf -state disabled
message $hdl sendfile
return 1 ;# ok
} ;# sendfile
proc ::ngcgui::readfile {hdl item} {
# update fname,$item and readfile
if { ("$item" == "preamble" || "$item" == "postamble") \
&& [string first : $::ngc($hdl,dname,$item)] == 0} {
set ::ngc($hdl,fname,$item) "IMMEDIATE"
set ::ngc($hdl,immediate,$item) [string range \
$::ngc($hdl,fname,$item) 1 end]
return
}
if {"$::ngc($hdl,dname,$item)" != ""} {
set ptype [file pathtype $::ngc($hdl,dname,$item)]
switch $ptype {
relative {
set fdir [file dirname $::ngc($hdl,fname,$item)]
if {"$fdir" == "." } {
set fdir $::ngc($hdl,dir) ;# -D wins for this case
}
set ::ngc($hdl,fname,$item) [file normalize \
[file join $fdir $::ngc($hdl,dname,$item)]]
}
absolute {set ::ngc($hdl,fname,$item) \
[file normalize $::ngc($hdl,dname,$item)]
}
default {return -code error "::ngcgui::readfile <$hdl $ptype>"}
}
# simplify dname,$item to just filename
set ::ngc($hdl,dname,$item) [file tail $::ngc($hdl,fname,$item)]
} else {
#note: ngc(dname,$item) is "", each readproc must init appropriately
set ::ngc($hdl,fname,$item) ""
}
switch $item {
preamble {::ngcgui::gui $hdl readpreamble }
subfile {::ngcgui::gui $hdl readsubfile }
postamble {::ngcgui::gui $hdl readpostamble }
}
} ;# readfile
proc ::ngcgui::debug {hdl} {
set t .debug-$hdl
catch {destroy $t}
set t [toplevel $t]
set lw 20;set ew 12
# hdl,$i
foreach i {standalone auto state lastevent \
savect dir afterid img,orig,size img,sampled,size} {
set f [frame $t.[qid] ]
pack [label $f.[qid] -relief ridge -anchor e -width $lw\
-text "$i" \
-font $::ngc(any,font)\
] -fill x -expand 0 -side left
pack [entry $f.[qid] -state readonly -relief ridge -width $ew \
-textvariable ::ngc($hdl,$i) \
-font $::ngc(any,font)\
] -fill x -expand 1 -side left
pack $f -side top -fill x -expand 1
}
# any,$i
foreach i {any,font any,width,comment any,width,varname any,pollms\
embed,axis embed,hdl} {
set f [frame $t.[qid] ]
pack [label $f.[qid] -relief ridge -anchor e -width $lw\
-text "$i" \
-font $::ngc(any,font)\
] -fill x -expand 0 -side left
pack [entry $f.[qid] -state readonly -relief ridge -width $ew \
-textvariable ::ngc($i) \
-font $::ngc(any,font)\
] -fill x -expand 1 -side left
pack $f -side top -fill x -expand 1
}
wm resizable $t 1 0
} ;# debug
proc ::ngcgui::statemap {hdl} {
# form: (next,state:mode,event) --> nextstate
set ::ngc(any,next,reset:auto,savesection) start
set ::ngc(any,next,reset:noauto,savesection) start
set ::ngc(any,next,reset:auto,restart) reset
set ::ngc(any,next,reset:noauto,restart) reset
set ::ngc(any,next,start:auto,immediate) avail
set ::ngc(any,next,start:noauto,immediate) avail
# have one or more features available:
set ::ngc(any,next,avail:auto,savesection) avail
set ::ngc(any,next,avail:noauto,savesection) avail
set ::ngc(any,next,avail:auto,restart) reset
set ::ngc(any,next,avail:noauto,restart) reset
set ::ngc(any,next,avail:auto,finalize) reset
set ::ngc(any,next,avail:noauto,finalize) reset2
set ::ngc(any,next,reset2:auto,immediate) reset
set ::ngc(any,next,reset2:noauto,immediate) reset
set ::ngc($hdl,state) reset
set ::ngc($hdl,lastevent) notsetyet
} ;# statemap
proc ::ngcgui::message {hdl event} {
# statemachine events (and messages)
# ::ngc(any,next,currentstateandmode,event) specifies next state for event
switch $::ngc($hdl,auto) {
0 {set statemode $::ngc($hdl,state):noauto}
1 {set statemode $::ngc($hdl,state):auto}
}
if ![info exists ::ngc(any,next,$statemode,$event)] {
showmessage $hdl $event
#puts "NOEVENT $::ngc($hdl,state) $event"
return
}
set ::ngc($hdl,lastevent) $event
set ::ngc($hdl,state) $::ngc(any,next,$statemode,$event)
#puts "$event: $statemode ------>$::ngc($hdl,state)"
set mw $::ngc($hdl,msg,widget)
# entry-to-state actions:
# note: execute switch even if state unchanged to update gui
switch $::ngc($hdl,state) {
reset {
if {"$event" == "finalize"} {
showmessage $hdl finalize
update idletasks
if $::ngc($hdl,standalone) {
after 500 ;#pause to see messages
}
}
set ::ngc($hdl,savect) 0
conf $hdl restart,widget state disabled
set ::ngc($hdl,data,section) ""
if [info exists ::ngc(embed,axis)] {
set bcolor $::ngc(any,color,stdbg)
if $::ngc($hdl,chooser) {
set bcolor $::ngc(any,color,custom)
}
$::ngc(any,axis,parent) itemconfigure $::ngc($hdl,axis,page) \
-foreground $::ngc(any,color,black) \
-background $bcolor
}
title $::ngc($hdl,top) "$::ngc(any,app)"
walktree $::ngc($hdl,varframe) normal
walktree $::ngc($hdl,iframe) normal
# 101024:19.49 this is better:
focus $::ngc($hdl,topf)
# note: dont disable sendfile,widget (wanted if noauto)
$::ngc($hdl,finalize,widget) conf -state disabled
$::ngc($hdl,save,widget) conf -text "[_ "Create Feature"]"
$mw conf -text "[_ "Enter parms for 1st feature"]" \
-fg $::ngc(any,color,prompt)
}
uwait {
# alternate behavior: user must select "New Outfile"
walktree $::ngc($hdl,varframe) disabled
walktree $::ngc($hdl,iframe) disabled
$::ngc($hdl,save,widget) conf -text "[_ "New Outfile"]"
$::ngc($hdl,finalize,widget) conf -state disabled
$mw conf -text "[_ "Ready to make New Outfile"]" \
-fg $::ngc(any,color,prompt)
}
reset2 - uwait2 {
# just make sure sendfile is made available, then go next state
$::ngc($hdl,sendfile,widget) conf -state normal
after 0 [list ::ngcgui::message $hdl immediate]
}
start {
walktree $::ngc($hdl,varframe) normal
walktree $::ngc($hdl,iframe) normal
focus $::ngc($hdl,begin,widget)
$::ngc($hdl,save,widget) conf -text "[_ "Create Feature"]"
$::ngc($hdl,sendfile,widget) conf -state disabled
$::ngc($hdl,finalize,widget) conf -state normal
$mw conf -text "[_ "Enter parms for feature "][expr 1 + $::ngc($hdl,savect)]" \
-fg $::ngc(any,color,prompt)
after 0 [list ::ngcgui::message $hdl immediate]
}
avail {
incr ::ngc($hdl,savect)
conf $hdl restart,widget state active
if [info exists ::ngc(embed,axis)] {
if {$::ngc($hdl,savect) > 1} {
$::ngc(any,axis,parent) itemconfigure $::ngc($hdl,axis,page) \
-foreground $::ngc(any,color,multiple) \
-background $::ngc(any,color,feature)
} else {
$::ngc(any,axis,parent) itemconfigure $::ngc($hdl,axis,page) \
-foreground $::ngc(any,color,single) \
-background $::ngc(any,color,feature)
}
}
set t "$::ngc(any,app) $::ngc($hdl,savect) [_ "feature"]"
if {$::ngc($hdl,savect) > 1} { set t ${t}s}
title $::ngc($hdl,top) "$t" ;# plural
$::ngc($hdl,finalize,widget) conf -state normal
if {$::ngc($hdl,savect) > 0} {
$::ngc($hdl,save,widget) conf -text "[_ "Create Next"]"
} else {
$::ngc($hdl,save,widget) conf -text "[_ "Create Feature"]"
}
$::ngc($hdl,sendfile,widget) conf -state disabled
$mw conf -text "[_ "Created feature "]$::ngc($hdl,savect)" \
-fg $::ngc(any,color,ok)
after 500 [list $::ngc($hdl,msg,widget) conf \
-text "[_ "Enter parms for feature "][expr 1 + $::ngc($hdl,savect)]" \
-fg $::ngc(any,color,prompt)
]
}
}
} ;# message
proc ::ngcgui::title {t txt} {
if ![info exists ::ngc(embed,axis)] {
wm title $t $txt
}
} ;# title
proc ::ngcgui::showmessage {hdl type} {
# if $hdl==opt then just show $type in *,msg,widget
# if no $hdl,msg,widget then do nothing
# if known type then update widgets per $type
# else then just show type in *,msg,widget
if {"$hdl" == "opt"} {
# no message widget since opt is for all instances
foreach w [array names ::ngc *,msg,widget] {
$::ngc($w) conf -text "[_ "option"] :$type $::ngc($hdl,$type)" \
-fg $::ngc(any,color,ok)
}
return
}
if ![info exists ::ngc($hdl,msg,widget)] return
set ::ngc($hdl,dname,outfile) [file tail $::ngc($hdl,fname,outfile)] ;#shorten
set mw $::ngc($hdl,msg,widget)
switch $type {
parmerr {
$mw conf -text "[_ "Missing parameters"]" \
-fg $::ngc(any,color,error)
}
parseerror {
$mw conf -text "[_ "Parse Error"]: $::ngc($hdl,dname,subfile)" \
-fg $::ngc(any,color,error)
$::ngc($hdl,finalize,widget) conf -state disabled
$::ngc($hdl,save,widget) conf -state disabled
}
nullpreamble {
periodic_checks $hdl ;# resync
$mw conf -text "[_ "Null Preamble"]" \
-fg $::ngc(any,color,ok)
}
readpreamble {
periodic_checks $hdl ;# resync
$mw conf -text "[_ "Read Preamble"]: $::ngc($hdl,dname,preamble)" \
-fg $::ngc(any,color,ok)
}
preambleerror {
$mw conf -text "[_ "Preamble Error"]: $::ngc($hdl,dname,preamble)" \
-fg $::ngc(any,color,error)
}
nullpostamble {
periodic_checks $hdl ;# resync
$mw conf -text "[_ "Null Postamble"]" \
-fg $::ngc(any,color,ok)
}
readpostamble {
periodic_checks $hdl ;# resync
$mw conf -text "[_ "Read Postamble"]: $::ngc($hdl,dname,postamble)" \
-fg $::ngc(any,color,ok)
}
postambleerror {
$mw conf -text "[_ "Postamble Error"]: $::ngc($hdl,dname,postamble)" \
-fg $::ngc(any,color,error)
}
readsubfile {
periodic_checks $hdl ;# resync
$mw conf -text "[_ "Read Subfile"]: $::ngc($hdl,dname,subfile)" \
-fg $::ngc(any,color,ok)
$::ngc($hdl,save,widget) conf -state normal ;# restore after parseerror
}
writeerror {
$mw conf -text "[_ "Write Error"]: $::ngc($hdl,dname,outfile)" \
-fg $::ngc(any,color,error)
}
setoutfile {
$mw conf -text "[_ "Outfile set"]: $::ngc($hdl,dname,outfile)" \
-fg $::ngc(any,color,ok)
}
finalize {
$mw conf -text \
"[_ "Finished"]: ($::ngc($hdl,savect)): $::ngc($hdl,dname,outfile)"\
-fg $::ngc(any,color,ok)
}
usercancel {
# user canceled output file spec
$mw conf -text "[_ "Canceled"]: $::ngc($hdl,savect) pending "\
-fg $::ngc(any,color,warn)
walktree $::ngc($hdl,varframe) normal
walktree $::ngc($hdl,iframe) normal
}
sendfile {
$mw conf -text "[_ "Sent"]: $::ngc($hdl,dname,outfile)" \
-fg $::ngc(any,color,ok)
}
senderror {
$mw conf -text "[_ "SendFileToAxis failed"]" \
-fg $::ngc(any,color,error)
}
startup {
$mw conf -text "[_ "Ctrl-k for Key bindings"]" \
-fg $::ngc(any,color,ok)
}
expandsubroutine {
$mw conf -text "[_ "Expand sub"] $::ngc($hdl,expandsubroutine)" \
-fg $::ngc(any,color,ok)
}
retainvalues {
$mw conf -text "[_ "Retain values"] $::ngc($hdl,retainvalues)" \
-fg $::ngc(any,color,ok)
}
verbose {
$mw conf -text "[_ "Verbose"] $::ngc($hdl,verbose)" -fg $::ngc(any,color,ok)
}
auto {
$mw conf -text "[_ "Autosend"] $::ngc($hdl,auto)" -fg $::ngc(any,color,ok)
}
cancel {
$mw conf -text "[_ "Finalize Canceled"]" \
-fg $::ngc(any,color,ok)
}
default {
$mw conf -text "$type" -fg $::ngc(any,color,default)
}
}
} ;# showmessage
proc ::ngcgui::periodic_checks {hdl} {
after cancel $::ngc($hdl,afterid)
if { [info exists ::ngc(embed,axis)] \
&& ([$::ngc(any,axis,parent) raise] != "$::ngc($hdl,axis,page)") } {
# not raised, skip tests
set ::ngc($hdl,afterid) [after $::ngc(any,pollms) \
[list ::ngcgui::periodic_checks $hdl]] ;#reschedule
return
}
# notify for modified files
foreach i {subfile preamble postamble} {
set f $::ngc($hdl,fname,$i)
if {"$f" == ""} continue
# check for widget because it can go away
if { [info exists ::ngc($hdl,$i,widget)] \
&& [winfo exists $::ngc($hdl,$i,widget)]} {
# check for change in entry widget
if {[file tail $f] != "$::ngc($hdl,dname,$i)"} {
# new file specified in entry box
$::ngc($hdl,$i,widget) conf -fg $::ngc(any,color,filenew)
} else {
$::ngc($hdl,$i,widget) conf -fg $::ngc(any,color,ok)
catch {unset ::ngc($hdl,$i,reread,pending)}
}
# check for file removal
if ![file readable $f] {
# file gone/perm changed notification:
$::ngc($hdl,$i,widget) conf -fg $::ngc(any,color,filegone)
continue
}
set t [file mtime $f]
if { [info exists ::ngc($hdl,fname,$i,time)] \
&& $t > $::ngc($hdl,fname,$i,time)\
} {
# file modified notification:
conf $hdl $i,widget fg $::ngc(any,color,filemod)
conf $hdl reread,widget state normal
conf $hdl reread,widget fg $::ngc(any,color,filemod)
set ::ngc($hdl,$i,reread,pending) 1
}
}
}
if {[array names ::ngc $hdl,*,reread,pending] == ""} {
conf $hdl reread,widget fg $::ngc(any,color,black)
conf $hdl reread,widget state disabled
}
::ngcgui::dcheck $hdl
set ::ngc($hdl,afterid) [after $::ngc(any,pollms) \
[list ::ngcgui::periodic_checks $hdl]] ;#reschedule
return
} ;# periodic_checks
proc ::ngcgui::dcheck {hdl} {
# check display of default values for positional parameters
foreach n [array names ::ngc $hdl,arg,entrywidget,*] {
set i1 [string last , $n]
set num02 [string range $n [expr 1 + $i1] end]
# under some contitions, this entrywidget may be done:
if ![winfo exists $::ngc($hdl,arg,entrywidget,$num02)] continue
if { [info exists ::ngc($hdl,arg,dvalue,$num02)] \
&& "$::ngc($hdl,arg,dvalue,$num02)" \
== "$::ngc($hdl,arg,value,$num02)"} {
$::ngc($hdl,arg,entrywidget,$num02) conf -bg $::ngc(any,color,vdefault)
} else {
$::ngc($hdl,arg,entrywidget,$num02) conf \
-bg $::ngc(any,color,stdbg);# restore default
}
}
} ;# dcheck
proc ::ngcgui::updownkeys {w} {
# not compatible with axis key bindings
# make up-arrow, down-arrow behave like tab,shift-tab navigation
bind $w <Key-Down> [bind all <Key-Tab>]
bind $w <Key-Up> [bind all <<PrevWindow>>]
# recursion:
foreach child [winfo children $w] {
if {$child == ""} continue
updownkeys $child
}
} ;# updownkeys
proc ::ngcgui::walktree {w mode} {
# mode == normal|disabled
# puts "w=$w mode=$mode"
switch [winfo class $w] {
Button -
Checkbutton -
Radiobutton -
Entry {
if {[$w cget -state] == "readonly"} {
# skip
} else {
$w config -state $mode
}
}
Toplevel -
Frame {
# recursion:
foreach child [winfo children $w] {
if {$child == ""} continue
walktree $child $mode
}
}
}
} ;# walktree
proc ::ngcgui::showerr {msg "opt sort" "maxerr 10"} {
# msg is a list; default: sort msg
set w .showerr
catch {destroy $w}
set w [toplevel $w]
set l [label $w.l -justify left]
set text ""
if {"$opt" == "sort"} {set msg [lsort $msg]}
set ct 0
foreach item $msg {
if {$ct > $maxerr} {
set text "$text\n..."
break ;# avoid showing too many
} else {
set text "$text\n$item"
}
incr ct
}
$l configure -text $text
pack $l -side top
set b [button $w.b -text "[_ "Dismiss"]" \
-command "destroy $w"]
pack $b -side top
focus $b
wm withdraw $w
wm title $w "[_ "ngcgui Error"]"
update idletasks
set x [expr [winfo screenwidth $w]/2 \
- [winfo reqwidth $w]/2 - [winfo vrootx [winfo parent $w]]]
set y [expr [winfo screenheight $w]/2 \
- [winfo reqheight $w]/2 - [winfo vrooty [winfo parent $w]]]
wm geom $w +$x+$y
wm deiconify $w
} ;# showerr
proc ::ngcgui::bye {hdl} {
after cancel $::ngc($hdl,afterid)
catch {destroy $::ngc($hdl,top)} ;# for embedded usage
set ::ngcgui::finis 1 ;# for standalone usage
} ;# bye
proc ::ngcgui::sendaxis {hdl cmd} {
# return 1==>ok
switch $cmd {
ping {
if ![catch {send axis pwd} msg] {return 1 ;#ok}
# tk8.5 send misfeature
if {[string first "X server insecure" $msg] >= 0} {
puts stdout "[_ "Declining support for tk send bug in ngcgui"]"
puts stdout "[_ "You should upgrade linuxcnc to >= linuxcnc2.5"]"
eval exec xhost - SI:localuser:gdm
eval exec xhost - SI:localuser:root
# test if that worked:
if [::ngcgui::sendaxis $hdl ping2] {return 1 ;# ok}
}
}
ping2 {
if ![catch {send axis pwd} msg] {return 1 ;#ok}
}
file {
set f [file normalize $::ngc($hdl,fname,outfile)]
if ![catch {send axis "remote open_file_name $f"} msg] {
if {"$msg" == ""} {
#puts sendaxis:file:ok:<$f>msg=$msg
if [info exists ::ngc(embed,axis)] {
$::ngc(any,axis,parent) raise preview
focus -force .
}
return 1 ;# ok
} else {
# nonnull msg means axis-remote cmd failed, see msg
}
} else {
# axis-ui-remote command not available pre2.4
# try method that may work for axis in linuxcnc2.3.x
return [pre2.4_send_file_to_axis $hdl $f]
}
}
default {return -code error "sendaxis: unknown cmd <$cmd>"}
}
set ::ngc($hdl,axis,error) \{$msg\} ;# brackets needed here
lappend ::ngc($hdl,axis,error) {Note: Ctrl-A toggles autosend}
return 0 ;# fail
} ;# sendaxis
proc ::ngcgui::pre2.4_send_file_to_axis {hdl f} {
# errors may be shown on axisui but NOT detected here with pre2.4
if ![catch {send axis open_file_name $f} msg] {
return 1 ;# ok (expect "None")
} else {
# notreached i suspect
puts "[_ "pre2.4_send_file_to_axis:error"]<$msg>"
set ::ngc($hdl,axis,error) [list $msg]
return 0 ;# error
}
} ;# pre2.4_send_file_to_axis
proc ::ngcgui::entrykeybinding {ax w v} {
# if a global ::entrykeybinding proc exists, use it only:
if {[info proc ::entrykeybinding] != ""} {
after 0 [list ::entrykeybinding $ax $w $v]
return
}
set axis [string toupper $ax]
# these coord values may not work for some configurations:
switch $axis {
X {set coord 0}
Y {set coord 1}
Z {set coord 2}
A {set coord 3}
B {set coord 4}
C {set coord 5}
U {set coord 6}
V {set coord 7}
W {set coord 8}
D {set coord 0;# for diameter}
}
if {![info exists coord]} return ;# silently
# ignore errors (standalone for example)
if [catch {
set value [emc_rel_act_pos $coord]
switch $axis {
D {set value [expr 2.0*$value] ;# diameter}
default {}
}
set value [format %.4f $value]
after 0 [list set $v $value]
after 0 [list $w configure -fg $::ngc(any,color,override)]
} msg] {
# silently ignore, emc_rel_act_pos will fail in standalone
# puts stdout "entrykeybinding:<$msg>"
}
} ;# entrykeybinding
proc ::ngcgui::text_width_and_length {text wname lname} {
upvar $wname maxwidth ;#pass by ref
upvar $lname lines ;#pass by ref
set linelimit 80 ;# some lines can be real long, ex ::env(LS_COLORS)
set start 0; set end 0; set len 0
set maxwidth 0
set lines 0
while {$end >= 0} {
set end [string first \n $text $start]
set len [expr $end - $start]
#puts "$len $start $end [string range $text $start $end]"
set start [expr $end +1]
if {$len > $maxwidth} {
# dont use len of very long lines
if {$len < $linelimit} {
set maxwidth $len
}
}
incr lines
}
return
} ;# text_width_and_length
proc ::ngcgui::simple_text {top text {title ""} } {
#note: on first cany, top should not exist
set maxheight 20
set tf $top.f
set t $tf.txt
set ysb $tf.ysb
if {![winfo exists $top]} {
toplevel $top
pack [frame $tf] -fill both -expand 1
text_width_and_length "$text" twidth theight
if {$theight > $maxheight} {set theight $maxheight}
set t [text $t \
-width $twidth -height $theight\
-yscrollcommand "$ysb set" \
]
set ysb [scrollbar $ysb -command "$t yview" -relief sunken]
set db [button $top.b -pady 1 -text "[_ "Dismiss"]" \
-command "destroy $top"]
focus $db
pack $t -side left -fill both -expand 0
pack $ysb -side right -fill y
pack $db -side top -fill x -expand 0
# fall-thru to insert
} else {
wm deiconify $top
}
if {"$title" != ""} { wm title $top "$title" }
#update
#set geo [wm geometry $top]
#set w [string range $geo 0 [expr [string first x $geo] -1]]
#set h [string range $geo [expr [string first x $geo +1]]\
# [expr [string first + $geo] -1]]
$t configure -state normal ;# to delete/insert
$t delete 0.0 end
$t insert end $text
$t configure -state disabled ;# leave disabled: insert
wm resizable $top 0 1
wmcenter $top
return $top
} ;# simple_text
proc ::ngcgui::wmcenter w {
# Withdraw the window, then update all the geometry information
# so we know how big it wants to be, then center the window in the
# display and de-iconify it.
wm withdraw $w
update idletasks
set x [expr [winfo screenwidth $w]/2 \
- [winfo reqwidth $w]/2 - [winfo vrootx [winfo parent $w]]]
set y [expr [winfo screenheight $w]/2 \
- [winfo reqheight $w]/2 - [winfo vrooty [winfo parent $w]]]
wm geom $w +$x+$y
wm deiconify $w
} ;# wmcenter
proc ::ngcgui::entry_mend {w} {
# note: entry_mend is callable by others (ttt)
# axis creates jog bindings for the toplevel (.==dot):
# for <KeyPress-minus> <KeyRelease-minus> <KeyPress-equal> <KeyRelease-equal>
# eg: bind . <KeyPress-minus> ==> {stuff}
# thus, for entries, bindtags are: {$e Entry . all} <-- the . is a problem
# so, limit the bindtags for entries
if {[winfo class $w] == "Entry"} {
bindtags $w [list $w Entry all] ;# remove the . bindtag
bind_for_axis $w
}
foreach child [winfo children $w] {
if {$child == ""} continue
::ngcgui::entry_mend $child
}
} ;# entry_mend
proc ::ngcgui::recursive_bind_controlkeys {hdl w} {
bind_controlkeys $hdl $w
foreach child [winfo children $w] {
if {$child == ""} continue
::ngcgui::recursive_bind_controlkeys $hdl $child
}
} ;# recursive_bind_controlkeys
proc ::ngcgui::bind_controlkeys {hdl w} {
set ::ngc(any,kbindlist) {a c d e E f F k n p P r R s S x v t U u}
bind $w <Control-Key-a> [list ::ngcgui::toggle $hdl auto]
bind $w <Control-Key-c> [list ::ngcgui::setentries $hdl clear]
bind $w <Control-Key-d> [list ::ngcgui::setentries $hdl defaults]
bind $w <Control-Key-D> [list ::ngcgui::debug $hdl]
bind $w <Control-Key-e> [list ::ngcgui::editfile $hdl last]
bind $w <Control-Key-E> [list ::ngcgui::toggle $hdl expandsubroutine]
bind $w <Control-Key-f> [list ::ngcgui::gui $hdl savesection]
bind $w <Control-Key-F> [list ::ngcgui::gui $hdl finalize]
bind $w <Control-Key-k> [list ::ngcgui::bindings $hdl show]
bind $w <Control-Key-n> [list ::ngcgui::message $hdl restart]
bind $w <Control-Key-p> [list ::ngcgui::gui $hdl readpreamble]
bind $w <Control-Key-P> [list ::ngcgui::gui $hdl readpostamble]
bind $w <Control-Key-r> [list ::ngcgui::gui $hdl readsubfile]
bind $w <Control-Key-R> [list ::ngcgui::toggle $hdl retainvalues]
bind $w <Control-Key-q> [list ::ngcgui::toggle $hdl verbose]
bind $w <Control-Key-s> [list ::ngcgui::status $hdl]
bind $w <Control-Key-S> [list ::ngcgui::status $hdl full]
bind $w <Control-Key-u> [list ::ngcgui::editfile $hdl source]
bind $w <Control-Key-U> [list ::ngcgui::editfile $hdl preamble]
# for debugging:
bind $w <Control-Key-x> [list parray ::ngc]
bind $w <Control-Key-v> [list parray ::env]
bind $w <Control-Key-t> [list ::ngcgui::test]
} ;# bind_controlkeys
proc ::ngcgui::bind_for_axis {w} {
# Escape and other special bindings for axis embedding
bind $w <Key-Escape> "$::ngc(any,axis,parent) raise preview" ;# allow Escape too
# axis omits return break in estopped_clicked for F1
bind $w <Key-F1> "[bind all <Key-F1>];break"
# Fn keys
foreach i {2 3 4 5 6 7 8 9 10 11 12} {
bind $w <Key-F$i> "[bind . <Key-F$i>];break"
}
} ;# bind_for_axis
proc ::ngcgui::bindings {hdl mode} {
set mode [string tolower $mode] ;# -nocase doesnt work tcl8.4
switch $mode {
show {
set atxt "[_ "OFF"]"
if {$::ngc($hdl,auto)} {set atxt "[_ "ON"]"}
set msg "\
Ctrl-a [_ "Toggle autosend"]\n\
Ctrl-c [_ "Clear entries"]\n\
Ctrl-d [_ "Set entries to default values"]\n\
Ctrl-e [_ "Open editor specified by"] \$VISUAL\n\
[_ "on last outfile"]\n\
Ctrl-E [_ "toggle expand subroutines"]\n\
Ctrl-f [_ "Create feature"]\n\
Ctrl-F [_ "Finalize (AUTO send is"] $atxt)\n\
Ctrl-k [_ "Show key bindings"]\n\
Ctrl-n [_ "Restart (cancel pending)"]\n\
Ctrl-p [_ "(re)Read Preamble"]\n\
Ctrl-P [_ "(re)Read Postamble"]\n\
Ctrl-r [_ "(re)Read Subfile"]\n\
Ctrl-R [_ "toggle retain values"]\n\
Ctrl-q [_ "toggle output file verbosity"]\n\
Ctrl-s [_ "Show status"]\n\
Ctrl-S [_ "Show full status (debug info)"]\n\
Ctrl-u [_ "Open editor specified by"] \$VISUAL\n\
[_ "on current subfile"]\n\
Ctrl-U [_ "Open editor specified by"] \$VISUAL\n\
[_ "on current preamble"]\
"
if [info exists ::ngc(embed,axis)] {
set msg "[_ " Escape Return to Preview page"]\n$msg"
}
# puts $msg
::ngcgui::simple_text .ngcguikeys $msg "$::ngc(any,app)-$hdl-keys"
}
init {
# coordinate with bind_controlkeys (x,v,t for debugging)
if [info exists ::ngc(embed,axis)] {
::ngcgui::bind_for_axis $::ngc($hdl,topf)
}
if [info exists ::ngc(embed,axis)] {
entry_mend $::ngc($hdl,topf)
}
recursive_bind_controlkeys $hdl $::ngc($hdl,topf)
bind $::ngc($hdl,topf) <Enter> [list ::ngcgui::bindings $hdl enter]
bind $::ngc($hdl,topf) <Leave> [list ::ngcgui::bindings $hdl leave]
set ::ngc($hdl,restore,bindtags) [bindtags $::ngc($hdl,topf)]
set ::ngc($hdl,restore,focus) [focus -lastfor $::ngc($hdl,topf)]
}
enter {
set ::ngc($hdl,restore,bindtags) [bindtags $::ngc($hdl,topf)]
bindtags $::ngc($hdl,topf) $::ngc($hdl,topf)
if [info exists ::ngc(embed,axis)] {
entry_mend $::ngc($hdl,topf)
}
recursive_bind_controlkeys $hdl $::ngc($hdl,topf)
set ::ngc($hdl,restore,focus) [focus -lastfor $::ngc($hdl,topf)]
focus $::ngc($hdl,topf)
return
}
leave {
bindtags $::ngc($hdl,topf) $::ngc($hdl,restore,bindtags)
focus -force $::ngc($hdl,restore,focus)
# this seems to be necesarry with notebook pages
foreach key $::ngc(any,kbindlist) {
bind $::ngc($hdl,topf) <Control-Key-$key> {}
}
}
}
} ;# bindings
proc ::ngcgui::aftertoggle {hdl x} {
# hdl: handle (note: opt may be used too)
switch $x {
auto {
if $::ngc($hdl,auto) {
pack forget $::ngc($hdl,sendfile,widget)
$::ngc($hdl,sendfile,widget) conf -state normal
$::ngc($hdl,finalize,widget) config -text "[_ "Finalize"]"
} else {
pack $::ngc($hdl,sendfile,widget) -fill x
$::ngc($hdl,finalize,widget) config -text "[_ "MakeFile"]"
}
}
}
::ngcgui::showmessage $hdl $x
} ;# aftertoggle
proc ::ngcgui::toggle {hdl x} {
# hdl: handle (note: opt may be used too)
set ::ngc($hdl,$x) [expr $::ngc($hdl,$x)?0:1]
::ngcgui::aftertoggle $hdl $x
} ;# toggle
proc ::ngcgui::test {} {
set text "Environmental Variables:\n"
foreach v [lsort [array names ::env]] {
set text "$text $v [set ::env($v)]\n"
}
simple_text .test $text
} ;# test
proc ::ngcgui::editfile {hdl {mode last} } {
if ![info exists ::env(VISUAL)] {
simple_text .problem "\n[_ "Editing requires setting for environmental variable VISUAL"] \n
[_ "Trying gedit"]\n"\
"$::ngc(any,app)-$hdl-problem"
set ::env(VISUAL) gedit
update
after 5000 {destroy .problem}
}
# note: normalize filename to honor tilde (~)
switch $mode {
last {
if { [info exists ::ngc($hdl,last,outfile)] \
&& "$::ngc($hdl,last,outfile)" != ""} {
eval exec $::env(VISUAL) [file normalize $::ngc($hdl,last,outfile)] &
} else {
simple_text .problem "[_ "No file available for editing yet"]\n"\
"$::ngc(any,app)-$hdl-problem"
return
}
}
source {
if {"$::ngc($hdl,fname,subfile)" != ""} {
eval exec $::env(VISUAL) [file normalize $::ngc($hdl,fname,subfile)] &
} else {
simple_text .problem "[_ "No file available for editing"]\n"\
"$::ngc(any,app)-$hdl-problem"
return
}
}
preamble {
if {"$::ngc($hdl,fname,preamble)" != ""} {
eval exec $::env(VISUAL) [file normalize $::ngc($hdl,fname,preamble)] &
} else {
simple_text .problem "[_ "No file available for editing"]\n"\
"$::ngc(any,app)-$hdl-problem"
return
}
}
}
} ;# editfile
proc ::ngcgui::status {hdl args} {
set items {fname,preamble fname,subfile fname,postamble\
fname,outfile fname,autosend\
auto dir savect font aspect retainvalues\
expandsubroutine chooser\
}
set optitems {noauto nonew noremove noiframe noinput nom2}
set anyitems {app pollms aspect width,comment width,varname qid}
set text "[_ "Status items"]:"
if {"$args" == "full"} {
#parray ::ngc;return
set bitems [lsort [array names ::ngc $hdl,*]]
foreach i $bitems {lappend items [string trim $i $hdl,]}
set text "Status items(all):"
}
set fmt "%s: %s"
foreach i $items {
# catch in case item gets unset
if [catch { set line [format "$fmt" $i $::ngc($hdl,$i)]}] continue
set text "$text\n$line"
}
set text "$text\n\n[_ "All-page opt items"]:"
foreach i $optitems {
# catch in case item gets unset
if [catch { set line [format "$fmt" $i $::ngc(opt,$i)]}] continue
set text "$text\n$line"
}
set text "$text\n\n[_ "any-items"]:"
foreach i $anyitems {
# catch in case item gets unset
if [catch { set line [format "$fmt" $i $::ngc(any,$i)]}] continue
set text "$text\n$line"
}
simple_text .status $text "$::ngc(any,app)-$hdl-status"
focus .status
bind .status <Control-Key-s> [list ::ngcgui::status $hdl $args]
bind .status <Control-Key-S> [list ::ngcgui::status $hdl $args]
} ;# status
proc ::ngcgui::validateNumber {hdl varname widget current new} {
# all entries must be numbers
if ![info exists $varname] {return 1}
if [catch {format %g $new} ] {
$widget configure -fg $::ngc(any,color,error)
return 1 ;# problem but return ok (just change color)
} else {
if {"$current" != "$new"} {}
$widget configure -fg $::ngc(any,color,black)
return 1 ;# 1==>ok
}
} ;# validateNumber
proc ::ngcgui::setentries {hdl opt} {
# set entries per opt == defaults | clear
switch $opt {
defaults {
foreach n [array names ::ngc $hdl,arg,dvalue,*] {
set num02 [string range $n [expr 1+[string last , $n]] end]
set ::ngc($hdl,arg,value,$num02) $::ngc($n)
}
::ngcgui::showmessage $hdl "[_ "Set defaults"]"
}
clear {
foreach n [array names ::ngc $hdl,arg,value,*] {
set num02 [string range $n [expr 1+[string last , $n]] end]
set ::ngc($hdl,arg,value,$num02) ""
}
::ngcgui::showmessage $hdl "[_ "Clear entries"]"
}
}
::ngcgui::dcheck $hdl
} ;# setentries
proc ::ngcgui::wgui {dir} {
# for embedded applications, this proc makes a separate-window gui
# in the current process
# this proc is useful for testing with tkcon:
# to debug using tkcon: source this file then % ::ngcgui::wgui dirname
# to run ngcgui in a frame, use ::ngcgui::gui hdl create frame
# multiple intantiations of ngcgui within the same prcess are not supported
package require Tk
set hdl 0
catch {unset ::ngc}
::ngcgui::preset $hdl control ;# setup control() with defaults
set control(any,aspect) horiz
set control(any,font) {Helvetica -10 bold}
# set control(any,app) [file tail $::argv0]
set control(any,app) ::ngcgui::wgui ;# with tkcon argv0 not available
set control($hdl,auto) 1 ;# autosend with finalize
set control($hdl,dir) $dir
set control($hdl,topname) .ngcgui
eval ::ngcgui::top $hdl control
wm withdraw .
} ;# wgui
proc ::ngcgui::findkeybinding {w {key k} } {
# utility
set b [bind $w <Control-Key-$key>]
if {"$b" != ""} {
puts "w=$w key=$key binding=<$b>"
}
foreach child [winfo children $w] {
if {$child == ""} continue
find $child $key
}
} ;# findkeybinding
proc ::ngcgui::top {hdl ay_name} {
# make a standalone toplevel
upvar $ay_name ay
foreach n [array names ay $hdl,*] { set ::ngc($n) $ay($n) }
foreach n [array names ay any,*] { set ::ngc($n) $ay($n) }
if ![info exists ::ngc($hdl,topname)] {
set ::ngc($hdl,topname) .
focus $::ngc($hdl,topname)
} else {
catch {destroy $::ngc($hdl,topname)}
toplevel $::ngc($hdl,topname)
}
wm protocol $::ngc($hdl,topname) WM_DELETE_WINDOW [list ::ngcgui::bye $hdl]
# if autosend, make sure file is writable
if $::ngc($hdl,auto) {
if {"$::ngc($hdl,fname,autosend)" == ""} {
set ::ngc($hdl,fname,autosend) auto.ngc
}
if ![string match *.ngc $::ngc($hdl,fname,autosend)] {
set ::ngc($hdl,fname,autosend) $::ngc($hdl,fname,autosend).ngc
}
set fname $::ngc($hdl,fname,autosend)
if [file writable $fname] {
# ok
} else {
if [file exists $fname] {
puts stdout "$fname [_ "not writable"]"
exit 1
} else {
if [catch {set fd [open $fname w]} msg] {
puts stdout $msg
exit 1
} else {
close $fd
file delete $fname
}
}
}
}
if {"$::ngc($hdl,topname)" == "."} {
set w [::ngcgui::gui $hdl standalone .w]
} else {
set w [::ngcgui::gui $hdl standalone $::ngc($hdl,topname).w]
}
if {"$w" == ""} {exit 1} ;# "" indicates something went wrong
pack $w -expand 0
switch $::ngc(any,aspect) {
vert {wm resizable $::ngc($hdl,top) 0 1}
horiz {wm resizable $::ngc($hdl,top) 1 0}
}
} ;# top
proc ::ngcgui::usage {hdl ay_name} {
upvar $ay_name ay
set prog [file tail $::argv0]
set dfont "\"$ay(any,font)\"" ;# avoid messing up vim colors
set aname $ay($hdl,fname,autosend)
puts stdout "Usage:
$prog --help | -?
$prog \[Options\] -D nc_files_directory_name
$prog \[Options\] -i LinuxCNC_inifile_name
$prog \[Options\]
Options:
\[-S subroutine_file\]
\[-p preamble_file\]
\[-P postamble_file\]
\[-o output_file\]
\[-a autosend_file]\ (autosend to axis default:$aname)
\[--noauto]\ (no autosend to axis)
\[-N | --nom2]\ (no m2 terminator (use %))
\[--font \[big|small|fontspec\]\] (default: $dfont)
\[--horiz|--vert\] (default: --horiz)
\[--cwidth comment_width]\ (width of comment field)
\[--vwidth varname_width]\ (width of varname field)
\[--quiet]\ (fewer comments in outfile)
\[--noiframe]\ (default: frame displays image)
"
exit 0
} ;# usage
proc ::ngcgui::inifind {filename stanza item} {
# find [STANZA]ITEM value from an ini file
set fd [open $filename r]
set state find_stanza
while {![eof $fd]} {
gets $fd theline
# remove blanks and tabs, use lower case
set line [string map {" " "" " " ""} $theline] ;#sp,tab to ""
# remove trailing comment
set i1 [string first # $line]
if {$i1 > 0} {
set line [string range $line 0 [expr $i1 -1]]
}
switch $state {
find_stanza {
if [regexp -nocase "^\\\[$stanza\\\]$" $line] { set state find_item }
}
find_item {
if [regexp -nocase "^\\\[.*" $line] {
break ;# new stanza found before item
}
if [regexp -nocase "^$item=(.*)" $line match value] {
set thevalue $value
# if more than one line like item=value, take the last line
}
}
}
}
close $fd
if [info exists thevalue] {
return $value
}
return ""
} ;# inifind
proc ::ngcgui::movepage {parent lr} {
set pages [$parent pages]
set page [$parent raise]
set idx [lsearch $pages $page]
switch $lr {
left {
if {$idx <= $::ngc(any,axis,min,idx)} {
return
}
incr idx -1
}
right { incr idx +1 }
}
$parent move $page $idx
updatepage
} ;# movepage
proc ::ngcgui::newpage {creatinghdl} {
set subfile "" ;# newpage: user must open file
if $::ngc(opt,noinput) {
# there is no wI input frame, just use current file
# file tail needed to use search path
set subfile [file tail $::ngc($creatinghdl,fname,subfile)]
if {"$subfile" == ""} {
set ::ngc(opt,noinput) 0 ;# need input if no subfile to open page
}
}
if $::ngc($creatinghdl,chooser) {
set subfile "\"\"" ;# chooser starts with no specifed subfile
}
set prefile ""
set postfile ""
if {"$::ngc($creatinghdl,dname,preamble)" != ""} {
# file tail needed to use search path
set prefile [file tail $::ngc($creatinghdl,fname,preamble)]
}
if {"$::ngc($creatinghdl,dname,postamble)" != ""} {
# file tail needed to use search path
set postfile [file tail $::ngc($creatinghdl,fname,postamble)]
}
set pageid ngcgui[qid]
set w [$::ngc(any,axis,parent) insert end "$pageid" \
-text "[_ "new"]"
]
$w config -borderwidth 0 ;# not sure why this needs to be by itself
set f [frame $w.[qid] -borderwidth 0 -highlightthickness 0]
pack $f -fill both -expand 1 -anchor nw -side top
# note: express font as list here is important fore embedded spaces
set newhdl [embed_in_axis_tab $f \
subfile=$subfile \
preamble=$prefile \
postamble=$postfile \
font=$::ngc(any,font) \
options=$::ngc(input,options) \
gcmc_include_path=$::ngc(input,gcmc_include_path) \
]
$::ngc(any,axis,parent) itemconfigure $pageid \
-createcmd "::ngcgui::pagecreate $newhdl"\
-raisecmd "::ngcgui::pageraise $newhdl"\
-leavecmd "::ngcgui::pageleave $newhdl"
# use directory from creating page
set ::ngc($newhdl,dir) [file dir $::ngc($creatinghdl,fname,subfile)]
$::ngc(any,axis,parent) raise $::ngc($newhdl,axis,page)
if {$::ngc(opt,noinput) && ("$::ngc($newhdl,dname,subfile)" != "")} {
set ::ngc($newhdl,info) "$::ngc($newhdl,dname,subfile)"
} else {
set ::ngc($newhdl,info) "[_ "Open a new Subfile"]"
}
updatepage
} ;# newpage
proc ::ngcgui::nextpage {pagename lr} {
# next page to use after this page is deleted
set hdl [pagetohdl $pagename]
if {$hdl <0} {return -code error \
"nextpage:unexpected pagename <$pagename>"
}
set page $::ngc($hdl,axis,page)
set pages [$::ngc(any,axis,parent) pages]
set lastidx [expr -1 + [llength $pages]]
set idx [lsearch $pages $page]
switch $lr {
left {
if {$idx <= $::ngc(any,axis,min,idx)} {
incr idx +1 ;# since idx page will be deleted
} else {
incr idx -1
}
}
right {
if {$idx >= $lastidx} {
incr idx -1 ;# since idx page will be deleted
} else {
incr idx +1
}
}
}
set newpage [lindex $pages $idx]
return $newpage
} ;# nextpage
proc ::ngcgui::pageexists {hdl} {
if [info exists ::ngc($hdl,axis,page)] {return 1}
return 0
} ;# pageexists
proc ::ngcgui::deletepage {pagename} {
set hdl [pagetohdl $pagename]
if {$hdl <0} {return -code error \
"deletepage:unexpected pagename <$pagename>"
}
set newpage [nextpage $pagename left]
after cancel $::ngc($hdl,afterid)
$::ngc(any,axis,parent) delete $::ngc($hdl,axis,page)
wm protocol $::ngc($hdl,img,top) WM_DELETE_WINDOW {}
destroy $::ngc($hdl,img,top)
foreach n [array names ::ngc $hdl,*] {
unset ::ngc($n)
}
set idx [lsearch $::ngc(embed,pages) $pagename]
set ::ngc(embed,pages) [lreplace $::ngc(embed,pages) $idx $idx]
$::ngc(any,axis,parent) raise $newpage
updatepage
} ;# deletepage
proc ::ngcgui::updatepage {} {
set parent $::ngc(any,axis,parent)
set allpages [$parent pages] ;# these are in tab order
foreach page [$parent pages] {
if {[lsearch $::ngc(embed,pages) $page] < 0} continue
lappend orderedpages $page
}
if ![info exists orderedpages] return ;# can occur at start
if {[llength $orderedpages] == 1} {
set p $orderedpages
foreach w {,move,l,widget move,r,widget ,remove,widget} {
if [info exists ::ngc($p$w)] {
$::ngc($p$w) config -state disabled
}
}
return
}
foreach p $orderedpages {
set idx [lsearch $orderedpages $p]
if {$idx == 0} {
$::ngc($p,move,l,widget) config -state disabled
$::ngc($p,move,r,widget) config -state active
} elseif {$idx == [expr -1 +[llength $orderedpages]]} {
$::ngc($p,move,l,widget) config -state active
$::ngc($p,move,r,widget) config -state disabled
} else {
$::ngc($p,move,l,widget) config -state active
$::ngc($p,move,r,widget) config -state active
}
# remove,widget not always present
if [info exists ::ngc($p,remove,widget)] {
$::ngc($p,remove,widget) config -state active}
}
# if choosers exist, do not allow removal of last one
set ct 0
foreach name [array names ::ngc *,chooser] {
if $::ngc($name) {
incr ct
lappend chdls [trimsuffix $name ,chooser]
}
}
if {$ct == 1} {
set chdl $chdls
set page $::ngc($chdl,axis,page)
$::ngc($page,remove,widget) configure -state disabled
} elseif {$ct > 1} {
foreach chdl $chdls {
set page $::ngc($chdl,axis,page)
$::ngc($page,remove,widget) configure -state active
}
}
} ;# updatepage
proc ::ngcgui::pagetohdl {pagename} {
foreach name [array names ::ngc *,axis,page] {
if {"$::ngc($name)" == "$pagename"} {
return [trimsuffix $name ,axis,page]
break
}
}
return -1
} ;# pagetohdl
proc ::ngcgui::tabmanage {pagename wframe ident infovar \
{removable 0} {newable 0} } {
# filler frame to put space below page tabs
pack [frame $wframe.[qid] -relief flat -height 1m\
] -anchor n -fill both -expand 0
set af [frame $wframe.[qid] -relief ridge -bd 2]
pack $af -fill x -expand 0 -anchor n ;# always pack to hold space
# another filler frame to put space below page tabs
pack [frame $wframe.[qid] -relief flat -height 1m\
] -anchor n -fill both -expand 0
pack [label $wframe.[qid] -relief flat -anchor w \
-textvariable $infovar \
-fg $::ngc(any,color,prompt)\
] -anchor ne -fill both -expand 0
if $removable {
set hdl [pagetohdl $pagename]
set b [button $af.[qid] -text "[_ "remove"]" \
-padx 2 -pady 1]
$b configure -command [list ::ngcgui::deletepage $pagename]
pack $b -side left -fill none -expand 0
set ::ngc($pagename,remove,widget) $b
}
if $newable {
set hdl [pagetohdl $pagename]
set b [button $af.[qid] -text "[_ "new"]" \
-padx 2 -pady 1]
$b configure -command [list ::ngcgui::newpage $hdl]
pack $b -side left -fill none -expand 0
}
set l [label $af.[qid] \
-text "$ident" \
-padx 2 -pady 1 -relief ridge\
]
pack $l -side left -fill x -expand 1
set parent $::ngc(any,axis,parent)
set b [button $af.[qid] -text "[_ "move"]-->" \
-padx 2 -pady 1]
$b configure -command [list ::ngcgui::movepage $parent right]
pack $b -side right -fill none -expand 0
set ::ngc($pagename,move,r,widget) $b
set b [button $af.[qid] -text "<--[_ "move"]" \
-padx 2 -pady 1]
$b configure -command [list ::ngcgui::movepage $parent left]
pack $b -side right -fill none -expand 0
set ::ngc($pagename,move,l,widget) $b
updatepage
} ;# tabmanage
proc ::ngcgui::parent {} {return $::ngc(any,axis,parent)}
proc ::ngcgui::getngcgui_frame {name} {
# utility for applications managed by ngcgui
set wtab [dynamic_tab $name $name] ;# axis function
set w [frame $wtab.[qid] -container 0 -borderwidth 0 -highlightthickness 0]
pack $w -side top -fill both -expand 1 -anchor nw
lappend ::ngc(embed,pages) $name
return $w
} ;# getngcgui_frame
proc ::ngcgui::embed_in_axis_tab {f args} {
# f: frame
# args: "item=value item=value ..."
if ![info exists ::ngc(embed,hdl)] {
set ::ngc(embed,axis) 1
set ::ngc(embed,hdl) 0
set ::ngc(embed,pages) ""
set ::ngc(any,axis,parent) [winfo parent [winfo parent $f]]
# dont allow movement of tab to left of original location:
set idx [lsearch [$::ngc(any,axis,parent) pages] \
[$::ngc(any,axis,parent) pages end]]
if {$idx < 0} {
set ::ngc(any,axis,min,idx) 10000
} else {
set ::ngc(any,axis,min,idx) $idx
}
} else {
incr ::ngc(embed,hdl)
}
set hdl $::ngc(embed,hdl) ;# local
initgui $hdl
::ngcgui::preset $hdl ::ngc ;# setup defaults
set equalitems {subfile preamble postamble \
font \
startdir \
gcmc_include_path \
options \
}
foreach item $equalitems {set ::ngc(input,$item) ""}
foreach input $args {
set pair [split $input =]
set ::ngc(input,[lindex $pair 0]) [lindex $pair 1]
# ex: input,subfile
}
foreach item $equalitems {set $item $::ngc(input,$item)}
if [info exists ::ngc(input,gcmc_include_path)] {
set ::ngc(any,gcmc_include_path) $::ngc(input,gcmc_include_path)
}
set ::ngc($hdl,dir) $::ngc(input,startdir)
if {[lsearch $options nonew ] >=0} {set ::ngc(opt,nonew) 1}
if {[lsearch $options noremove ] >=0} {set ::ngc(opt,noremove) 1}
if {[lsearch $options noauto ] >=0} {set ::ngc(opt,noauto) 1}
if {[lsearch $options noinput ] >=0} {set ::ngc(opt,noinput) 1}
if {[lsearch $options noiframe ] >=0} {set ::ngc(opt,noiframe) 1}
if {[lsearch $options nom2 ] >=0} {set ::ngc(opt,nom2) 1}
if {[lsearch $options expandsub ] >=0} {set ::ngc($hdl,expandsubroutine) 1}
# special options
if {[lsearch $options nopathcheck ] >=0} {set ::ngc($hdl,nopathcheck) 1}
if $::ngc(opt,noauto) {
set ::ngc($hdl,auto) 0
} else {
set ::ngc($hdl,auto) 1
}
# with image in frame there is not enough room so force noinput
if !$::ngc(opt,noiframe) {set ::ngc(opt,noinput) 1}
set ::ngc(any,width,comment) 0 ;# field can be as long as reqd
set ::ngc($hdl,axis,page) [$::ngc(any,axis,parent) pages end]
set page $::ngc($hdl,axis,page) ;# local
# if font has leading/trailing literal quotes, remove them
if { [string first \" $font] == 0 \
&& [string last \" $font] == [expr [string len $font] -1]} {
set font [string range $font 1 [expr [string len $font] -2]]
}
if {"$font" != ""} {set ::ngc(any,font) $font}
# specific settings for embedding in axis tab:
set ::ngc(any,aspect) horiz
set ::ngc(any,width,varname) 0
if {"$subfile" != ""} {
# detect ini file specified as ""
# this is a chooser page -- user can open new files
if {"$subfile" == "\"\""} {
set ::ngc($hdl,chooser) 1
set ::ngc($hdl,fname,subfile) ""
$::ngc(any,axis,parent) itemconfigure $page \
-text "[_ "Custom"]" \
-background $::ngc(any,color,custom)
} else {
if [info exists ::ngc($hdl,nopathcheck)] {
# subfile must be a valid absolute path for this option
# example: ttt uses /tmp directory specified with full path
# to avoid creating persistent files
# relying on purging of /tmp
set ::ngc($hdl,fname,subfile) $subfile
set ::ngc($hdl,dir) [file dirname $subfile]
} else {
set ::ngc($hdl,fname,subfile) [::ngcgui::pathto $subfile]
set ::ngc($hdl,dir) [file dirname $::ngc($hdl,fname,subfile)]
}
}
}
if {"$preamble" != ""} {
set ::ngc($hdl,fname,preamble) [::ngcgui::pathto $preamble]
}
if {"$postamble" != ""} {
set ::ngc($hdl,fname,postamble) [::ngcgui::pathto $postamble]
}
set w [::ngcgui::gui $hdl create $f.ngc_gui]
if {"$w" == ""} {
puts stdout "[_ "Problem creating page"] <$hdl> <$f>"
} else {
pack $w -side top -fill none -expand 1 -anchor nw
}
# package require Linuxcnc ;# needs linuxcnc v2.5.x, segfaults linuxcnc v2.4.x
# just invoking emc_init works with v2.4 and v2.5
if [catch {emc_init} msg] {
puts "embed_in_axis_tab: [_ "entrykeybindings not available"] <$msg>"
}
lappend ::ngc(embed,pages) $page
updatepage
return $hdl
} ;# embed_in_axis_tab
proc ::ngcgui::set_path {} {
# set ::ngc(any,paths) on first use:
if ![info exists ::ngc(any,paths)] {
# expect single item, so take end item in list:
set ::ngc(any,paths) [file normalize \
[lindex [inifindall DISPLAY PROGRAM_PREFIX] end]]
set tmp [lindex [inifindall RS274NGC SUBROUTINE_PATH] end]
foreach p [split $tmp ":"] {lappend ::ngc(any,paths) "$p"}
}
} ;# get_path
proc ::ngcgui::pathto {fname {mode info}} {
# for embedded usage, find configuration file using a search path
set fname [string trim $fname]
if {"$fname" == ""} {return ""}
set_path ;# if not set, will set
if { [string first "/" $fname] == 0
|| [string first "~" $fname] == 0
|| [string first "." $fname] == 0
} {
if [file exists $fname] {
# expected usage: spcecify search path [RS274NGC]SUBROUTINE_PATH
# and: specify [DISPLAY]NGCGUI_SUBFILE as a file name only
#
# future: maybe it should be an error to use an absolute path
# since the interpreter may not find the file
# for now: only use a file if it is in search path
set foundabsolute "$fname"
set fname [file tail $fname] ;# to test if it is in search path
}
}
foreach path $::ngc(any,paths) {
set f [file join $path $fname]
if {[info exists foundinpath] && [file exists $f]} {
puts stdout "::ngcgui::pathto: [_ "Found multiple matches for"] <$fname>"
puts stdout "[_ "using path"]: $::ngc(any,paths)"
}
if {![info exists foundinpath] && [file exists $f]} {set foundinpath $f}
}
if [info exists foundinpath] {
if { [info exists foundabsolute] \
&& [file normalize $foundinpath] != [file normalize $foundabsolute] } {
puts "\nngcgui [_ "Warning"]:"
puts "[_ "File absolute path specifier conflicts with searchpath result"]"
puts " [_ "Absolute Specifier"]: $foundabsolute"
puts " [_ "Using Search Result"]: $foundinpath"
puts ""
}
return "$foundinpath"
} else {
set title "[_ "File not in Search Path"]"
set msg "<$fname> [_ "Must be in search path"]\n"
if {[info exists foundabsolute]} {
set msg "$msg\n[_ "(File found -- not in search path)"]"
}
set msg "$msg\n[_ "Current directory"]:\n[pwd]"
set msg "$msg\n\n[_ "Search path"]:\n"
set i 1
foreach p $::ngc(any,paths) {
set msg "$msg\n$i $p"
set fullp [file normalize $p]
if {"$p" != "$fullp"} {
set msg "$msg\n== $fullp"
}
incr i
}
set msg "$msg\n\n[_ "Check setting for"]: \[RS274NGC\]SUBROUTINE_PATH"
set msg "$msg\n[_ "in ini file"]:\n$::emcini"
set msg "$msg\n\n[_ "(Restart required after fixing ini file)"]"
switch $mode {
info {
set answer [tk_dialog .notfound \
"$title"\
"$msg"\
warning -1 \
"OK"]
set answer 0 ;# continue with warning
}
default {
set answer [tk_dialog .notfound \
"$title"\
"$msg" \
error 0 \
"[_ "Try to Continue"]" "[_ "Exit"]"
]
}
}
if $answer {return \
-code error "[_ "Ngcgui Configuration File Not Found"] <$fname>"
}
if ![info exists foundabsolute] {set foundabsolute ""}
return "$foundabsolute" ;# try to continue
}
} ;# pathto
proc ::ngcgui::check_path filename {
if [info exists ::ngc(embed,axis)] {
pathto [file tail $filename] info
}
return
} ;# check_path
proc ::ngcgui::raiselastpage {} {
$::ngc(any,axis,parent) raise $::ngc($::ngc(embed,hdl),axis,page)
} ;# raiselastpage
proc ::ngcgui::position {top} {
set geo [wm geometry $top]
return [string range $geo [string first + $geo] end]
} ;# position
proc ::ngcgui::pagecreate {hdl} {
#puts "n:pagecreate-$hdl"
return 1
} ;# pagecreate
proc ::ngcgui::pageraise {hdl} {
#puts "n:pageraise-$hdl"
set ::ngc($hdl,img,status) raised
if {"$::ngc($hdl,fname,subfile)" != ""} {
new_image $hdl $::ngc($hdl,fname,subfile)
}
return 1
} ;# pageraise
proc ::ngcgui::pageleave {hdl} {
#puts "n:pageleave-$hdl"
set ::ngc($hdl,img,position) [position $::ngc($hdl,img,top)]
wm withdraw $::ngc($hdl,img,top)
return 1 ;# important: permission to leave
} ;# pageleave
proc ::ngcgui::image_init {hdl} {
set ::ngc($hdl,img,status) new
if [info exists ::ngc(embed,axis)] {
set ::ngc($hdl,img,top) .$::ngc(any,app)-$hdl
} else {
set ::ngc($hdl,img,top) .$::ngc(any,app)
}
if [winfo exists $::ngc($hdl,img,top)] return
wm withdraw [toplevel $::ngc($hdl,img,top)]
wm protocol $::ngc($hdl,img,top) WM_DELETE_WINDOW \
[list wm withdraw $::ngc($hdl,img,top)]
if {$::ngc(opt,noinput) && !$::ngc($hdl,chooser)} {
pack forget $::ngc($hdl,iframe) ;# wI remove the Input frame
}
if { (!$::ngc(opt,noiframe) && !$::ngc($hdl,chooser) )\
|| (!$::ngc(opt,noiframe) && $::ngc($hdl,standalone) )\
} {
# use a frame for image
set p [winfo parent $::ngc($hdl,iframe)]
set w $p.[qid] ;# name of frame
set ::ngc($hdl,img,widget) [image_widget $hdl $w]
set ::ngc($hdl,img,type) frame
} else {
# use a toplevel for image
set ::ngc($hdl,img,widget) [image_widget $hdl $::ngc($hdl,img,top).i]
set ::ngc($hdl,img,type) toplevel
}
# note: new_image packs $::ngc($hdl,img,widget)
} ;# image_init
proc ::ngcgui::image_widget {hdl f} {
# f is name of a frame, it should not exist at call, caller packs
# png, pgm,ppm etc support
if [catch {package require Img} msg] {
tk_dialog .img \
"[_ "Missing Tcl Package Img"] " \
"[_ "Please install Img"]:\n $ sudo apt-get install libtk-img" \
"" 0 \
"ok"
exit
}
if {[winfo exists $f]} {return -code error "image_widget <$w> exists"}
frame $f ;# caller packs
set fimg [frame $f.fimg -relief groove -borderwidth 2]
pack $fimg -side top -expand 1 -fill both
set ::ngc($hdl,img,canvas) [canvas $fimg.canvas -bg darkgray ]
pack $::ngc($hdl,img,canvas) -side left -expand 1 -fill both
return $f
} ;# image_widget
proc ::ngcgui::new_image {hdl ngcfilename} {
set idx [string first .ngc $ngcfilename]
if {$idx < 0} { set idx [string first .gcmc $ngcfilename]}
if {$idx < 0} { return -code error \
"new_image: unexpected filename: <$ngcfilename>"}
set filestart [string range $ngcfilename 0 $idx]
foreach suffix {png gif jpg pgm} {
set f ${filestart}$suffix
if [file readable $f] {
set ifilename $f
break
}
}
if ![info exists ifilename] {
catch {unset ::ngc($hdl,img,filename)}
catch {pack forget $::ngc($hdl,img,widget)} ;# standalone
catch {wm withdraw $::ngc($hdl,img,top)} ;# needed for standalone
return ;# silently continue
}
set doimage 0
if ![info exists ::ngc($hdl,img,filename)] {
set ::ngc($hdl,img,status) first
set doimage 1
} else {
if {"$::ngc($hdl,img,filename)" != "$ifilename"} {
set ::ngc($hdl,img,position) [position $::ngc($hdl,img,top)]
set ::ngc($hdl,img,status) new
set doimage 1
}
}
if {$doimage} {
# first time for this file for this hdl
set ::ngc($hdl,img,filename) $ifilename
pack forget $::ngc($hdl,img,widget)
set tmpimage [image create photo -file $ifilename]
set ct 0
set sw [expr [image width $tmpimage] / $::ngc(any,img,width,max) + 1]
set sh [expr [image height $tmpimage] / $::ngc(any,img,height,max) + 1]
set subsample $sw
if {$sh > $sw} {set subsample $sh}
set ::ngc($hdl,img,image) [image create photo]
$::ngc($hdl,img,image) copy $tmpimage -subsample $subsample -shrink
set width [image width $::ngc($hdl,img,image)]
set height [image height $::ngc($hdl,img,image)]
# convenience only:
set ::ngc($hdl,img,orig,size) [image width $tmpimage]x[image height $tmpimage]
set ::ngc($hdl,img,sampled,size) ${width}x${height}
$::ngc($hdl,img,canvas) delete all
$::ngc($hdl,img,canvas) configure -width $width -height $height
$::ngc($hdl,img,canvas) create image [expr $width/2] [expr $height/2]\
-anchor center \
-image $::ngc($hdl,img,image)
recursive_bind_controlkeys $hdl $::ngc($hdl,img,top)
pack $::ngc($hdl,img,widget)
}
# restore the image widget toplevel if applicable
if {"$::ngc($hdl,img,type)" == "toplevel"} {
switch $::ngc($hdl,img,status) {
first {
if [info exists ::ngc($hdl,img,position)] {
wmrestore $hdl
} else {
wmcenter $::ngc($hdl,img,top)
}
if { ![info exists ::ngc(embed,axis)] \
|| [$::ngc(any,axis,parent) raise] == $::ngc($hdl,axis,page)} {
set ::ngc($hdl,img,status) raised ;# need for standalone
} else {
wm withdraw $::ngc($hdl,img,top)
}
}
new -
raised { wmrestore $hdl }
}
wm resizable $::ngc($hdl,img,top) 0 0
wm title $::ngc($hdl,img,top) [trimsuffix $::ngc($hdl,dname,subfile)]
}
} ;# new_image
proc ::ngcgui::wmrestore {hdl} {
set w $::ngc($hdl,img,top)
wm deiconify $w
if [catch {
if [info exists ::ngc($hdl,img,position)] {
wm geometry $w $::ngc($hdl,img,position)
}
} msg] {
puts stdout "wmrestore: unexpected<$msg>"
}
} ;# wmrestore
# configure standalone usage:
proc ::ngcgui::standalone_ngcgui {args} {
# setup ::ngcgui::control() with defaults
set hdl 0
::ngcgui::preset $hdl ::ngcgui::control
package require Tk
# configure for standalone usage
# map dot (.) to underline (_) to preclude window naming errors:
set ::ngcgui::control(any,app) [string map {. _} [file tail $::argv0]]
while {[llength $::argv] >0} {
# beware wish handling of reserved cmdline arguments
# to use -h: use -- -h,
# lreplace shifts argv for no. of items for each iteration
switch -- [lindex $::argv 0] {
--noiframe {set ::ngc(opt,noiframe) 1
set ::argv [lreplace $::argv 0 0]
}
-h - -? -
--help {::ngcgui::usage $hdl ::ngcgui::control;exit 0}
--horiz -
-horiz {set ::ngcgui::control(any,aspect) horiz
set ::argv [lreplace $::argv 0 0]
}
--vert -
-vert {set ::ngcgui::control(any,aspect) vert
set ::argv [lreplace $::argv 0 0]
}
-q -
--quiet {
set ::ngcgui::control($hdl,verbose) 0
set ::argv [lreplace $::argv 0 0]
}
--font -
-font {set ::ngcgui::control(any,font) [lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
--vwidth {set ::ngcgui::control(any,width,varname) [lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
--cwidth {set ::ngcgui::control(any,width,comment) [lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
-N -
--nom2 {set ::ngcgui::control(any,nom2) 0
set ::argv [lreplace $::argv 0 0]
}
-S -
--subfile {set ::ngcgui::control($hdl,fname,subfile) [lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
-p -
--preamble {set ::ngcgui::control($hdl,fname,preamble) \
[lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
-P -
--postamble {set ::ngcgui::control($hdl,fname,postamble) \
[lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
-o -
--output {set ::ngcgui::control($hdl,fname,outfile) [lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
-D -
--dir {
# -D allows dir specification with no filenames
set ans [lindex $::argv 1]
if [file isdirectory $ans] {
set ::ngcgui::control($hdl,dir) $ans
} else {
set ::ngcgui::control($hdl,dir) [file dirname $ans]
}
set ::argv [lreplace $::argv 0 1]
}
-a -
--autosend {set ::ngcgui::control($hdl,auto) 1
set ::ngcgui::control($hdl,fname,autosend) \
[lindex $::argv 1]
set ::argv [lreplace $::argv 0 1]
}
--noautosend -
--noauto {set ::ngcgui::control($hdl,auto) 0
set ::argv [lreplace $::argv 0 0]
}
-i -
--ini* {
set filename [lindex $::argv 1]
if ![file readable $filename] {
puts "[_ "ini file"]: <$filename> not readable"
exit 1
}
set ::argv [lreplace $::argv 0 1]
set dir [file normalize [file dirname $filename]]
set pdir [::ngcgui::inifind $filename \
DISPLAY PROGRAM_PREFIX]
set pdir [file normalize $pdir]
if {"$pdir" == ""} {
puts "\[DISPLAY\]PROGRAM_PREFIX [_ "not found"] <$filename>"
exit 1
}
set ptype [file pathtype $pdir]
switch $ptype {
relative {set inidir [file join $dir $pdir]}
absolute {set inidir [file normalize $pdir]}
default {puts "unhandled pathtype for $pdir <$ptype>"
exit 1
}
}
set ::ngcgui::control($hdl,dir) $inidir
}
default {break}
}
}
if {"$::ngcgui::control(any,font)" == ""} {
set ::ngcgui::control(any,font) small
}
switch -- $::ngcgui::control(any,font) {
small {set ::ngcgui::control(any,font) {Helvetica -10 bold}}
big {set ::ngcgui::control(any,font) {Helvetica -16 bold}}
default {}
}
# ::ngcgui::control() specifies args
eval ::ngcgui::top $hdl ::ngcgui::control
tkwait variable ::ngcgui::finis
exit 0
} ;# standalone_ngcgui
if {[info exists ::argv0] && [info script] == $::argv0} ::ngcgui::standalone_ngcgui