<!DOCTYPE article
 PUBLIC "-//Mark Wroth//DTD DocBook V4.1-Based Extension Literate Programming 1.1//EN">
<!-- # $Id: JadeTCL.sgm,v 1.3 2001/04/29 20:54:53 Mark Exp Mark $ -->
<article id="JadeTCL">
  <articleinfo>
    <author>
      <firstname>Mark</firstname>
      <surname>Wroth</surname>
    </author>
    <title>A Tcl/Tk Front End for the Jade DSSSL Processor</title>
    <revhistory>
      <revision>
	<revnumber>2</revnumber>
	<date>29 April 01</date>
	<revdescription>
	  <para>This revision makes a major shift in how the action
	  buttons are constructed, from individually programmed and
	  enumerated buttons to buttons constructed from an array
	  defined in the setup of the program.</para>
	  <para>This change allows a configuration file to define an
	  arbitrary number of scripts to be executed.  Each script is
	  identified by a checkbox, and all of the checkboxes selected
	  will be executed sequentially.</para>
	</revdescription>
      </revision>
      <revision>
	<revnumber>1</revnumber>
	<date>28 April 2001</date>
	<revremark>Initial implementation.</revremark>
      </revision>
    </revhistory>
  </articleinfo>
  <section>
    <title>Background</title>
    <para>This <application>Tcl/Tk</application> script provides a
    simple front end for <application>Jade</application>.  The purpose
    is to provide a somewhat configurable graphical front end for
    repetitive invocation of the program, for situations where the
    full setup of something like <application>Pomade</application>
    doesn't seem warranted. </para>

    <para>Users who are considering adopting this script should also
    consider <application>Pomade</application>, which can be found at
    <ulink
    url="http://www.saremba.de/pomade">http://www.saremba.de/pomade/</ulink>.
    This script is designed to be much easier to configure for a new
    set of scripts than <application>Pomade</application>.  The
    tradeoff is that this application is much less flexible than
    <application>Pomade</application>.</para>

    <para>While this application shares no code with
    <application>Pomade</application>, the author is indebted to
    Andreas Saremba, <application>Pomade</application>'s author for
    the inspiration for this program and an introduction to
    <application>Tcl/Tk</application> in general.</para>
  </section>
  <section>
    <title>Downloading and Installation</title> 

    <para>This application is created using the <ulink
    url="http://www.west-point.org/users/usma1978/36200/LitProg/SGMLWEB/index.htm"><application>DBLP</application>
    literate programming system</ulink>.  The primary file is <ulink
    url="http://www.west-point.org/users/usma1978/36200/TCLScripts/JadeTCL/JadeTCL.sgm"><filename>JadeTCL.sgm</filename></ulink>.
    The actual <application>Tcl/Tk</application> script is <ulink
    url="http://www.west-point.org/users/usma1978/36200/TCLScripts/JadeTCL/Jade.tcl"><filename>Jade.tcl</filename></ulink>,
    which is written by the literate programming system from the basic
    file.</para> <para>All that is necessary to run the application is
    a functioning <application>Tcl/Tk</application>.  However, to be
    useful, you will also probably want to create a file
    <filename>JadeRC.TCL</filename>. This file will be sourced after
    the default initialization, and is intended to contain definitions
    of additional (or substitute) <acronym>DSSSL</acronym> script
    definitions. This is how the system is customized to a particular
    environment. See <xref linkend="initialize"> for more discussion
    on how this set of arrays are defined.</para>
  </section>
  <section>
    <title>Implementation</title>
    <section>
      <title>Pack the Primary Window</title>

      <para>The top level display of the <acronym>GUI</acronym> is a
      simple layout with the menu at the top, a display of the target
      file name and location below the menu, a column of checkboxes
      for the various scripts, and finally a row of buttons to start
      the various actions.</para>
      <para>The bulk of the script is devoted to defining the various
      buttons that are packed in this display.</para>
<programlisting 
 id="displaygui"
 xreflabel="Display the GUI"
>
pack .menubar -side top -fill x
pack .pathname .browse  -side top -fill x
foreach script [array names scripts] {
  pack .$script -side top -fill x
}
pack .go .showerr .exit -side left -expand 1 -fill x
</programlisting>
    </section>
    <section>
      <title>Organization</title>
      <para>Notice that the actual packing of the
      <acronym>GUI</acronym> can't take place until the various button
      structures have been defined.</para>
<programlisting
 file="Jade.tcl"
 id  = "MainProgram"
>
<xref linkend="initialization">
<xref linkend="browsebutton">
<xref linkend="menus">
<xref linkend="actions">
<xref linkend="displaygui">
<xref linkend="auxiliaryprocs">
</programlisting>
    </section>
    <section id="initialize">
      <title>Initialization</title>
      <para>The basic concept of the initialization is to define a
      minimum set of variables in the main script, and then to source
      another file which can define (or redefine) the variables that
      will actually be used in setting up the application.</para>

      <para>To add a <acronym>DSSSL</acronym> script to the
      application, edit the configuration file to add the new script.
      Each script needs three strings: an id (which is never seen in
      the <acronym>GUI</acronym>, but is used internally in the
      script; a <acronym>DSSSL</acronym> script name, including (if
      needed) the path to it; and a type, which is the
      <application>Jade</application> output type.  The
      <acronym>DSSSL</acronym> script name and the output type are
      stored in associative arrays, indexed by the id.</para>
       
      <para>So to add a new script, you will choose a new
      <replaceable>script-id</replaceable>, and set two variables:
      <literal>scripts(<replaceable>script-id</replaceable>)</literal>,
      which is set to the name of the <acronym>DSSSL</acronym> script
      (including, if necessary, the path to the script), and
      <literal>type(<replaceable>script-id</replaceable>)</literal>,
      which is set to the <application>Jade</application> output type
      (the <literal>-t</literal> option).</para>

      <para>To change a default script, adopt the same process,
      except that the <replaceable>script-id</replaceable> is that of
      the script you are replacing.</para>

      <para>The other configuration variables can also be reset in the
      configuration file, although this should usually not be
      necessary. The main options would appear to be the name (and
      path to) the <application>Jade</application> processor (in
      <literal>jade_name</literal>), and the catalog file name (in
      <literal>catalog_name</literal>). </para>
<programlisting
 id="initialization"
 xreflabel="Initialization and Setup"
>
set config_filename "JadeRC.TCL"
set jade_name "jade"
set catalog_name "C:/catalog"
set debug 0
set scripts(htm) "not set"
set type(htm) "sgml"
set scripts(rtf) "c:/usr/dsssl/docbook/print/docbook.dsl"
set type(rtf) "rtf"
set scripts(tangle) "c:/usr/dtd/dblp/SGMLTangle.dsl"
set type(tangle) "sgml"

if [file exists $config_filename] {
 source $config_filename
 }
  
set target [tk_getOpenFile \
    -title "Set Target File Name" \
    -filetypes {
     {{SGML files} {.sgm}}
     {{XML files} {.xml}}
     {{All files} {".*"}}
     }]
set target_path [file dirname $target]
set target_name [file tail $target]
eval cd \"$target_path\"
</programlisting>
    </section>
    <section>
      <title>Define the Configuration Button</title>
<programlisting
  id="browsebutton"
  xreflabel="Define the BROWSE button"
>
button .browse -textvariable target_name -command { 
set target [tk_getOpenFile \
     -title "Select New Target File Name" \
     -filetypes {
      {{SGML files} {.sgm}}
      {{XML files} {.xml}}
      {{All files} {".*"}}
      }]
  set target_path [file dirname $target]
  set target_name [file tail $target]
  eval cd \"$target_path\"
  
} 
label .pathname -textvariable target_path
</programlisting>
    </section>
    <section>
      <title>Define the Action Buttons</title>
      <para>The most complicated set of action buttons are the check
      buttons that decide which scripts will be executed.  In addition
      to actually defining the buttons, this part of the script also
      defines the command actions.  This could, in principle, be
      separated into its own loop.</para>
<programlisting 
 id          = "actions"
 xreflabel   = "Define the action buttons"
 continuedin = "actions-01"
>
foreach script [array names scripts] {
  <xref linkend="definecmd">
  checkbutton .$script \
    -textvariable scripts($script) \
    -variable do($script) \
    -onvalue  1 \
    -offvalue 0 \
    -anchor w 
}
</programlisting>
      <para>The actual definition of the command is a simple assembly
      from the various pieces already defined.</para>
<programlisting
 id        = "definecmd"
 xreflabel = "Define the cmd array"
>
  set outfile($script) ""
  append outfile($script) [file rootname $target_name] "." $script
  set cmd($script) ""
  append cmd($script) "\"$jade_name\" " 
  append cmd($script) " " -c " " \"$catalog_name\" " "
  append cmd($script) " " -d " \"" $scripts($script) "\"" 
  append cmd($script) " " -t " " $type($script)
  append cmd($script) " " \"$target_name\" 
  append cmd($script) " " 2&greaterthan;&greaterthan; jadetcl.err 
  append cmd($script) " " &greaterthan; $outfile($script) 

</programlisting>
      <para>The <guilabel>Go</guilabel> button is the key button of
      the <acronym>GUI</acronym>, at least in the sense that it is the
      one that actually executes the actions that are the whole
      purpose of the program.</para>
      <para>The basic approach is to loop over all of the array
      indicies, and, if the relevant checkbox is checked, executing
      the command defined while we were doing the setup of the
      program. </para>
<programlisting 
 id            = "actions-01"
 continuedfrom = "actions"
 continuedin   = "actions-02"
>

button .go -text "Go" -command {
  if [file exists jadetcl.err] {file delete jadetcl.err}
  foreach script [array names scripts] {
    if $do($script) {
      if $debug {tk_messageBox -message $cmd($script)}
      catch {eval exec $cmd($script)}
      catch {
        if [expr [file size jadetcl.err] &greaterthan; 0] {
          showFile jadetcl.err
          } else {
          file delete jadetcl.err
          }
        }
      } else {}
  }
}

</programlisting>
      <para>The remaining action buttons are straightforward, and
      define commands to view the error log and exit from the
      application. The <function>showFile</function> procedure is
      defined in <xref linkend="auxiliary">. </para>
<programlisting 
 id            = "actions-02"
 continuedfrom = "actions-01"
>

button .showerr -text "Show Err" -command {
  showFile jadetcl.err
}

button .exit -text "EXIT" -command {exit}
</programlisting>
    </section>
    <section>
      <title>Define the Menus</title>
      <para>More as a programming exercise than anything else, menus
      were added to the ancestor of this program.  However, they
      provide a convenient way to give the user access to functions
      that are not needed very often, so we retain and extend their
      use in this application.</para>
      <para>In addition to infrequently used configurations, we also
      include some obvious functions, such as changing the target
      file, even if they are also accessible from
      <acronym>GUI</acronym> buttons.</para>
<programlisting
 id="menus"
 xreflabel="Define the menu system"
 continuedin="menus-01"
>
frame .menubar -relief groove -borderwidth 4 

</programlisting>
      <para>The first menu we set up is the <guimenu>File</guimenu>
      menu.  This allows the user to change the target file, read a
      new configuration file, or exit the program.</para>
<programlisting
 id            = "menus-01"
 continuedfrom = "menus"
 continuedin   = "menus-02"
>

menubutton .menubar.file   -text "File" \
  -direction below -menu .menubar.file.menu 
menu .menubar.file.menu\
  -tearoff 0
.menubar.file.menu add command -label "Set target" -command {
  set target [tk_getOpenFile -title "File" -filetypes {
      {{SGML files} {.sgm}}
      {{XML files} {.xml}}
      {{All files} {".*"}}
      }]
  set target_path [file dirname $target]
  set target_name [file tail $target]
  eval cd \"$target_path\"
  }
.menubar.file.menu add command -label "Read config" \
  -command {
   source  [tk_getOpenFile -filetypes {
      {{TCL Files} {.tcl}}
      {{All files} {".*"}}
  }]
  foreach script [array names scripts] {
  <xref linkend="definecmd">
  }}
.menubar.file.menu add command -label "Exit" -command {
  exit}

</programlisting>
      <para>The <guimenu>Config</guimenu> menu allows the user to
      reconfigure the program to use, for example, a different set of
      <acronym>DSSSL</acronym> files, or a different
      <application>Jade</application> executable.</para>
<programlisting
 id =    "menus-02"
 continuedfrom = "menus-01"
 continuedin   = "menus-03"
>

menubutton .menubar.config -text "Config" \
  -direction below -menu .menubar.config.menu
menu .menubar.config.menu \
  -tearoff 0 
.menubar.config.menu add command -label "Jade" -command {
  set jade [tk_getOpenFile -filetypes {
      {{Executables} {.exe}}
      {{All files} {".*"}}
  }]
  } 
.menubar.config.menu add command -label "Catalog File" -command {
  set catalog_name [tk_getOpenFile -title "catalog" -filetypes {
      {{All files} {".*"}}
  }]
  } 

</programlisting>
      <para>The <guimenu>Help</guimenu> menu provides some very
      sketchy help functions, and the <quote>About</quote> information
      on the program.</para>
<programlisting
 id            = "menus-03"
 continuedfrom = "menus-02"
 continuedin   = "menus-04"
>

menubutton .menubar.help   -text "Help" \
  -direction below -menu .menubar.help.menu
menu .menubar.help.menu \
  -tearoff 0 
.menubar.help.menu add command -label "Help" \
        -command {showHelp}
.menubar.help.menu add command -label "About" -command {
  tk_messageBox -type ok -message \
  "This is Jade.tcl, a TCL/Tk application providing a 
   graphical front end for Jade.\n\n
   Copyright (c) Mark Wroth &lt;mark@astrid.upland.ca.us&gt; 2001\n\n
   Free distribution under the terms of the Gnu Public License
   is authorized."
  }

</programlisting>
      <para>Finally, we pack the menus into the menubar, which will
      itself be packed into the main window later.</para>
<programlisting
 id            = "menus-04"
 continuedfrom = "menus-03"
>

pack .menubar.file .menubar.config  -side left -fill x
pack .menubar.help -side right -fill x
</programlisting>
    </section>
    <section id="auxiliary">
      <title>Auxiliary Procedures</title>
      <para>Several auxiliary procedures are needed at various places
      in the overall script.</para>
      <section>
        <title>Help Display</title>
<programlisting
 id          = "auxiliaryprocs"
 xreflabel   = "Define the Auxiliary procedures"
 continuedin = "aux-01"
>
proc showHelp {} {
     set helptext "\
                   OVERVIEW
                   This is a Tcl/Tk front end for the Jade \
                   processor. It takes a single target file and (as \
                   part of its initialization) an array of DSSSL \
                   scripts and output types.  When commanded, it \
                   runs Jade on each script in turn.
                   
                   TARGET FILE
                   The name of the target file is displayed in a text button near \
                   the top of the application window, with the path to that file \
                   displayed above it.  The jade process will execute in this \
                   directory, which means that all relative path names defined \
                   in the target file will be interpreted relative to this directory.
                   \tTo change the target file, click on the text button containing \
                   the target file name.  A file requester will appear; navigate to and \
                   select the new target file using the requester.  By default, the \
                   requester will show only files ending in \".sgm\", since this is the \
                   usual extension given to SGML target files.
                   \tThe target file requester can also be summoned via the \"File\" \
                   menu. \n

                   GO\n
                   To execute Jade with the currently selected target file and options, click \
                   on the button labeled \"Go\".
                   CONFIGURATION\n
                   The application assumes that the Jade executable is found \
                   somewhere in the operating system path.  If this is not the case, the path \
                   to the nuweb executable can be set using the \"Config\" menu \"Jade\" \
                   option.
                   EXITING FROM THE APPLICATION\n
                   There are three different ways to exit from the Nuweb Tcl application:
                   \t - Click on the button marked \"Exit\"
                   \t - Select the \"Exit\" item on the \"File\" menu
                   \t - Click on the \"Close Window\" button provided by the operating \
                   system's window manager.
                   "
     set w .text
     catch {destroy $w}
     toplevel $w
     wm title $w "Jade TCL/Tk Help"
     wm iconname $w "text"

     frame $w.buttons 
     pack $w.buttons -side bottom -fill x -pady 2m
     button $w.buttons.dismiss -text Dismiss -command "destroy $w"
     pack $w.buttons.dismiss  -side bottom -expand 1

     text $w.text -relief sunken -bd 2 -yscrollcommand "$w.scroll set" \
        -setgrid 1 -height 30 \
        -tabs {1c 2c 3c} \
        -wrap word  
     scrollbar $w.scroll -command "$w.text yview"
     pack $w.scroll -side right -fill y
     pack $w.text -expand yes -fill both
     $w.text insert end $helptext
     $w.text mark set insert 0.0 } 

</programlisting>
      </section>
      <section>
        <title>File Display</title>
<programlisting
 id            = "aux-01"
 continuedfrom = "auxiliaryprocs"
 continuedin   = "aux-02"
>

proc showFile {thefile} {
     set w .text
     catch {destroy $w}
     toplevel $w
     wm title $w "$thefile"
     wm iconname $w "text"

     frame $w.buttons
     pack $w.buttons -side bottom -fill x -pady 2m
     button $w.buttons.dismiss -text Dismiss -command "destroy $w"
     pack $w.buttons.dismiss  -side bottom -expand 1

     text $w.text -relief sunken -bd 2 -yscrollcommand "$w.scroll set" \
        -setgrid 1 -height 30
     scrollbar $w.scroll -command "$w.text yview"
     pack $w.scroll -side right -fill y
     pack $w.text -expand yes -fill both
     textLoadFile $w.text $thefile
     $w.text mark set insert 0.0 } 

</programlisting>
      </section>
      <section>
        <title>Load a Text File</title>
<programlisting
 id            = "aux-02"
 continuedfrom = "aux-01"
>

proc textLoadFile {w file} {
    if [file exists $file] {
      set f [open $file]
      $w delete 1.0 end
      while {![eof $f]} {
         $w insert end [read $f 10000]
      }
      close $f
  }
}
</programlisting>
      </section>
    </section>
  </section>
</article>