# This is the "Magic wrapper".
# Its main purpose is to redefine the "openwindow" command in magic so that
# opening a new window creates a window wrapped by a GUI interface.
#
# Written by Tim Edwards, August 23, 2002.

# revision A: proof-of-concept.  Add whatever you want to this basic wrapper.
# revision B: Adds Tk scrollbars and caption
# revision C: Adds a layer manager toolbar on the left side
# revision D: Adds a menubar on top with cell and tech manager tools

global lwindow
global tk_version
global Glyph
global Opts
global Winopts

set tk_version $::tk_version
# Simple console commands (like TkCon, but much simpler)

if {[lsearch [namespace children] ::tkshell] < 0} {
   catch {source ${CAD_ROOT}/magic/tcl/tkshell.tcl}
}

proc magic::makeglyphimages {} {
    global Opts
    global Glyph
    global CAD_ROOT

    # Check if glyphs exist---don't need to make them more than once
    if {![catch {set Glyph(up)}]} {return}

    # Glyph images

    set gsize [expr {int($Opts(scale) * 13)}]
    set gscale [expr {int($Opts(scale))}]

    image create photo stdglyph -file ${CAD_ROOT}/magic/tcl/bitmaps/up.gif
    image create photo Glyph(up) -width $gsize -height $gsize
    Glyph(up) copy stdglyph -zoom $gscale

    image create photo stdglyph -file ${CAD_ROOT}/magic/tcl/bitmaps/down.gif
    image create photo Glyph(down) -width $gsize -height $gsize
    Glyph(down) copy stdglyph -zoom $gscale

    image create photo stdglyph -file ${CAD_ROOT}/magic/tcl/bitmaps/left.gif
    image create photo Glyph(left) -width $gsize -height $gsize
    Glyph(left) copy stdglyph -zoom $gscale

    image create photo stdglyph -file ${CAD_ROOT}/magic/tcl/bitmaps/right.gif
    image create photo Glyph(right) -width $gsize -height $gsize
    Glyph(right) copy stdglyph -zoom $gscale

    image create photo stdglyph -file ${CAD_ROOT}/magic/tcl/bitmaps/zoom.gif
    image create photo Glyph(zoom) -width $gsize -height $gsize
    Glyph(zoom) copy stdglyph -zoom $gscale

    image create bitmap Glyph(lock) \
	-file ${CAD_ROOT}/magic/tcl/bitmaps/lock.xbm \
	-background gray80 -foreground steelblue4]
}

# Menu button callback functions

proc magic::promptload {type} {
   global CAD_ROOT

   switch $type {
      cif { set Layoutfilename [ tk_getOpenFile -filetypes \
		{{CIF {.cif {.cif}}} {"All files" {*}}}]
	    if {$Layoutfilename != ""} {
		set cifname [file tail [file root $Layoutfilename]]
		magic::cellname create cif_temp
		magic::load cif_temp
		magic::cif read [file root $Layoutfilename]
		set childcells [magic::cellname list children cif_temp]
		magic::load [lindex $childcells 0]
		magic::cellname delete cif_temp -noprompt
		if {[llength $childcells] > 1} {
		   puts stdout "Cells read from GDS file: $childcells"
		}
	     }
	  }
      gds { set Layoutfilename [ tk_getOpenFile -filetypes \
		{{GDS {.gds .strm .cal {.gds .strm .cal}}} {"All files" {*}}}]
	    if {$Layoutfilename != ""} {
		set origlist [magic::cellname list top]
		magic::gds read [file root $Layoutfilename]
		set newlist [magic::cellname list top]

		# Find entries in newlist that are not in origlist.
		# If there's only one, load it into the window.

		set newtopcells {}
		foreach n $newlist {
		   if {[lsearch $origlist $n] < 0} {
		      lappend newtopcells $n
		   }
		}
		if {[llength $newtopcells] == 1} {
		   magic::load [lindex $newtopcells 0]
		} elseif {[llength $newtopcells] != 0} {
		   puts stdout "Top-level cells read from GDS file: $newtopcells"
		}
	    }
	  }
      magic { set Layoutfilename [ tk_getOpenFile -filetypes \
		{{Magic {.mag {.mag}}} {"All files" {*}}}]
	    if {$Layoutfilename != ""} {
		magic::load [file root $Layoutfilename]
	    }
	  }

      getcell { set Layoutfilename [ tk_getOpenFile -filetypes \
		{{Magic {.mag {.mag}}} {"All files" {*}}}]
	    if {$Layoutfilename != ""} {
		set fdir [file dirname $Layoutfilename]
		set lidx [lsearch [path search] $fdir]
		if {$lidx < 0} {path search +$fdir}
		magic::getcell [file tail $Layoutfilename]

		# Append path to cell search path if it's not there already

		if {[string index $Layoutfilename 0] != "/"} {
		   set $Layoutfilename "./$Layoutfilename"
		}
		set sidx [string last "/" $Layoutfilename]
		if {$sidx > 0} {
		   set cellpath [string range $Layoutfilename 0 $sidx]
		   magic::path cell +$cellpath
		}
	    }
	  }
   }
}

proc magic::promptsave {type} {
   global CAD_ROOT

   switch $type {
      cif { set Layoutfilename [ tk_getSaveFile -filetypes \
		{{CIF {.cif {.cif}}} {"All files" {*}}}]
	    if {$Layoutfilename != ""} {
	       magic::cif write $Layoutfilename
	    }
	  }
      gds { set Layoutfilename [ tk_getSaveFile -filetypes \
		{{GDS {.gds .strm .cal {.gds .strm .cal}}} {"All files" {*}}}]
	    if {$Layoutfilename != ""} {
		magic::gds write $Layoutfilename
	    }
	  }
      force -
      magic {
	    set CellList [ magic::cellname list window ]
	    if {[lsearch $CellList "(UNNAMED)"] >= 0} {
	       set Layoutfilename [ tk_getSaveFile -filetypes \
		   {{Magic {.mag {.mag}}} {"All files" {*}}} \
		   -title "Save cell (UNNAMED) as:" ]
	       if {$Layoutfilename != ""} {
		   set cellpath [file dirname $Layoutfilename]
		   if {$cellpath == [pwd]} {
		      set Layoutfilename [file tail $Layoutfilename]
		   } else {
		      magic::path cell +$cellpath
		   }
		   magic::save $Layoutfilename
	       }
	    }
	    if {$type == "force"} {
	       magic::writeall force
	    } else {
	       magic::writeall
	    }
	  }
   }
}

# Window to prompt for a new cell

proc magic::prompt_dialog { type } {
   global Opts

   if {[catch {toplevel .dialog}]} {
      foreach child [winfo children .dialog] {
         destroy $child
      }
   }

   frame .dialog.titlebar
   frame .dialog.text
   frame .dialog.bbar

   switch $type {
      new {
         label .dialog.titlebar.title -text "Create new cell" -foreground blue
         button .dialog.bbar.okay -text "Okay" -command {load $Opts(cell_name); \
		lower .dialog}
         set Opts(cell_name) "(UNNAMED)"
      }
      save {
         label .dialog.titlebar.title -text "Save cell as..." -foreground blue
         button .dialog.bbar.okay -text "Okay" -command {save $Opts(cell_name); \
		lower .dialog}
         set Opts(cell_name) [cellname list window]
      }
   }
   pack .dialog.titlebar.title

   label .dialog.text.tlabel -text "Cell name:" -foreground brown
   entry .dialog.text.tentry -background white -textvariable Opts(cell_name)

   pack .dialog.text.tlabel -side left
   pack .dialog.text.tentry -side left

   button .dialog.bbar.cancel -text "Cancel" -command "lower .dialog"

   pack .dialog.bbar.okay -side left
   pack .dialog.bbar.cancel -side right

   pack .dialog.titlebar -side top -ipadx 2 -ipady 2
   pack .dialog.text -side top -fill both -expand true
   pack .dialog.bbar -side top -fill x -ipadx 5

   bind .dialog.text.tentry <Return> {.dialog.bbar.okay invoke}

   raise .dialog
}

# Callback functions used by the DRC.

proc magic::drcupdate { option } {
   global Opts
   if {[info level] <= 1} {
      switch $option {
         on {set Opts(drc) 1}
         off {set Opts(drc) 0}
      }
   }
}

proc magic::drcstate { status } {
   set winlist [*bypass windownames layout]
   foreach lwin $winlist {
      set framename [winfo parent $lwin]
      if {$framename == "."} {return}
      switch $status {
         idle {
		set dct [*bypass drc list count total]
		if {$dct > 0} {
	           ${framename}.titlebar.drcbutton configure -selectcolor red
		} else {
	           ${framename}.titlebar.drcbutton configure -selectcolor green
		}
	        ${framename}.titlebar.drcbutton configure -text "DRC=$dct"
	      }
         busy { ${framename}.titlebar.drcbutton configure -selectcolor yellow }
      }
   }
}

# Create the menu of windows.  This is kept separate from the cell manager,
# and linked into it by the "clone" command.

menu .winmenu -tearoff 0

proc magic::setgrid {gridsize} {
   set techlambda [magic::tech lambda]
   set tech1 [lindex $techlambda 1]
   set tech0 [lindex $techlambda 0]
   set tscale [expr {$tech1 / $tech0}]
   set lambdaout [expr {[magic::cif scale output] * $tscale}]
   set gridlambda [expr {$gridsize/$lambdaout}]
   magic::grid ${gridlambda}l
   magic::snap on
}

# Technology manager callback functions

proc magic::techparseunits {} {
   set techlambda [magic::tech lambda]
   set tech1 [lindex $techlambda 1]
   set tech0 [lindex $techlambda 0]

   set target0 [.techmgr.lambda1.lval0 get]
   set target1 [.techmgr.lambda1.lval1 get]

   set newval0 [expr {$target0 * $tech0}]
   set newval1 [expr {$target1 * $tech1}]

   magic::scalegrid $newval1 $newval0
   magic::techmanager update
}

# The technology manager

proc magic::maketechmanager { mgrpath } {
   toplevel $mgrpath
   wm withdraw $mgrpath

   frame ${mgrpath}.title
   label ${mgrpath}.title.tlab -text "Technology: "
   menubutton ${mgrpath}.title.tname -text "(none)" -foreground red3 \
	-menu ${mgrpath}.title.tname.menu
   label ${mgrpath}.title.tvers -text "" -foreground blue
   label ${mgrpath}.subtitle -text "" -foreground sienna4

   frame ${mgrpath}.lambda0
   label ${mgrpath}.lambda0.llab -text "Microns per lambda (CIF): "
   label ${mgrpath}.lambda0.lval -text "1" -foreground blue

   frame ${mgrpath}.lambda1
   label ${mgrpath}.lambda1.llab -text "Internal units per lambda: "
   entry ${mgrpath}.lambda1.lval0 -foreground red3 -background white -width 3
   label ${mgrpath}.lambda1.ldiv -text " / "
   entry ${mgrpath}.lambda1.lval1 -foreground red3 -background white -width 3

   frame ${mgrpath}.cif0
   label ${mgrpath}.cif0.llab -text "CIF input style: "
   menubutton ${mgrpath}.cif0.lstyle -text "" -foreground blue \
	-menu ${mgrpath}.cif0.lstyle.menu
   label ${mgrpath}.cif0.llab2 -text " Microns/lambda="
   label ${mgrpath}.cif0.llambda -text "" -foreground red3

   frame ${mgrpath}.cif1
   label ${mgrpath}.cif1.llab -text "CIF output style: "
   menubutton ${mgrpath}.cif1.lstyle -text "" -foreground blue \
	-menu ${mgrpath}.cif1.lstyle.menu
   label ${mgrpath}.cif1.llab2 -text " Microns/lambda="
   label ${mgrpath}.cif1.llambda -text "" -foreground red3

   frame ${mgrpath}.extract
   label ${mgrpath}.extract.llab -text "Extract style: "
   menubutton ${mgrpath}.extract.lstyle -text "" -foreground blue \
	-menu ${mgrpath}.extract.lstyle.menu

   frame ${mgrpath}.drc
   label ${mgrpath}.drc.llab -text "DRC style: "
   menubutton ${mgrpath}.drc.lstyle -text "" -foreground blue \
	-menu ${mgrpath}.drc.lstyle.menu

   pack ${mgrpath}.title.tlab -side left
   pack ${mgrpath}.title.tname -side left
   pack ${mgrpath}.title.tvers -side left
   pack ${mgrpath}.lambda0.llab -side left
   pack ${mgrpath}.lambda0.lval -side left
   pack ${mgrpath}.lambda1.llab -side left
   pack ${mgrpath}.lambda1.lval0 -side left
   pack ${mgrpath}.lambda1.ldiv -side left
   pack ${mgrpath}.lambda1.lval1 -side left
   pack ${mgrpath}.cif0.llab -side left
   pack ${mgrpath}.cif0.lstyle -side left
   pack ${mgrpath}.cif0.llab2 -side left
   pack ${mgrpath}.cif0.llambda -side left
   pack ${mgrpath}.cif1.llab -side left
   pack ${mgrpath}.cif1.lstyle -side left
   pack ${mgrpath}.cif1.llab2 -side left
   pack ${mgrpath}.cif1.llambda -side left
   pack ${mgrpath}.extract.llab -side left
   pack ${mgrpath}.extract.lstyle -side left
   pack ${mgrpath}.drc.llab -side left
   pack ${mgrpath}.drc.lstyle -side left

   pack ${mgrpath}.title -side top -fill x
   pack ${mgrpath}.subtitle -side top -fill x
   pack ${mgrpath}.lambda0 -side top -fill x
   pack ${mgrpath}.lambda1 -side top -fill x
   pack ${mgrpath}.cif0 -side top -fill x
   pack ${mgrpath}.cif1 -side top -fill x
   pack ${mgrpath}.extract -side top -fill x

   bind ${mgrpath}.lambda1.lval0 <Return> magic::techparseunits
   bind ${mgrpath}.lambda1.lval1 <Return> magic::techparseunits

   #Withdraw the window when the close button is pressed
   wm protocol ${mgrpath} WM_DELETE_WINDOW  "set Opts(techmgr) 0 ; wm withdraw ${mgrpath}"
}

# Generate the cell manager

catch {source ${CAD_ROOT}/magic/tcl/cellmgr.tcl}

# Generate the library manager

catch {source ${CAD_ROOT}/magic/tcl/libmgr.tcl}

# Generate the DRC manager

catch {source ${CAD_ROOT}/magic/tcl/drcmgr.tcl}

# Generate the text helper

catch {source ${CAD_ROOT}/magic/tcl/texthelper.tcl}

# Create or redisplay the technology manager

proc magic::techmanager {{option "update"}} {
   global CAD_ROOT

   if {[catch {wm state .techmgr}]} {
      if {$option == "create"} {
	 magic::maketechmanager .techmgr
      } else {
	 return
      }
   } elseif { $option == "create"} {
      return
   }

   if {$option == "create"} {
      menu .techmgr.title.tname.menu -tearoff 0
      menu .techmgr.cif0.lstyle.menu -tearoff 0
      menu .techmgr.cif1.lstyle.menu -tearoff 0
      menu .techmgr.extract.lstyle.menu -tearoff 0
      menu .techmgr.drc.lstyle.menu -tearoff 0

   }

   if {$option == "init"} {
	.techmgr.title.tname.menu delete 0 end
	.techmgr.cif0.lstyle.menu delete 0 end
	.techmgr.cif1.lstyle.menu delete 0 end
	.techmgr.extract.lstyle.menu delete 0 end
	.techmgr.drc.lstyle.menu delete 0 end
   }

   if {$option == "init" || $option == "create"} {
      set tlist [magic::cif listall istyle]
      foreach i $tlist {
	 .techmgr.cif0.lstyle.menu add command -label $i -command \
		"magic::cif istyle $i ; \
		 magic::techmanager update"
      }

      set tlist [magic::cif listall ostyle]
      foreach i $tlist {
	 .techmgr.cif1.lstyle.menu add command -label $i -command \
		"magic::cif ostyle $i ; \
		 magic::techmanager update"
      }

      set tlist [magic::extract listall style]
      foreach i $tlist {
	 .techmgr.extract.lstyle.menu add command -label $i -command \
		"magic::extract style $i ; \
		 magic::techmanager update"
      }

      set tlist [magic::drc listall style]
      foreach i $tlist {
	 .techmgr.drc.lstyle.menu add command -label $i -command \
		"magic::drc style $i ; \
		 magic::techmanager update"
      }

      set dirlist [subst [magic::path sys]]
      set tlist {}
      foreach i $dirlist {
	 lappend tlist [glob -nocomplain ${i}/*.tech]
	 lappend tlist [glob -nocomplain ${i}/*.tech27]
      }
      foreach i [join $tlist] {
	 set j [file tail [file rootname ${i}]]
	 .techmgr.title.tname.menu add command -label $j -command \
		"magic::tech load $j ; \
		 magic::techmanager update"
      }
   }

   set techlambda [magic::tech lambda]
   set tech1 [lindex $techlambda 1]
   set tech0 [lindex $techlambda 0]
   set tscale [expr {$tech1 / $tech0}]

   .techmgr.title.tname configure -text [magic::tech name]
   set techstuff [magic::tech version]
   .techmgr.title.tvers configure -text "(version [lindex $techstuff 0])"
   .techmgr.subtitle configure -text [lindex $techstuff 1]
   set lotext [format "%g" [expr {[magic::cif scale output] * $tscale}]]
   .techmgr.lambda0.lval configure -text $lotext
   .techmgr.cif0.lstyle configure -text [magic::cif list istyle]
   set litext [format "%g" [expr {[magic::cif scale input] * $tscale}]]
   .techmgr.cif0.llambda configure -text $litext
   .techmgr.cif1.lstyle configure -text [magic::cif list ostyle]
   .techmgr.cif1.llambda configure -text $lotext
   .techmgr.extract.lstyle configure -text [magic::extract list style]
   .techmgr.drc.lstyle configure -text [magic::drc list style]

   .techmgr.lambda1.lval0 delete 0 end
   .techmgr.lambda1.lval1 delete 0 end
   .techmgr.lambda1.lval0 insert end $tech1
   .techmgr.lambda1.lval1 insert end $tech0
}

proc magic::captions {{subcommand {}}} {
   global Opts

   if {$subcommand != {} && $subcommand != "writeable" && $subcommand != "load"} {
      return
   }
   set winlist [magic::windownames layout]
   foreach winpath $winlist {
      set framename [winfo parent $winpath]
      set caption [$winpath windowcaption]
      set subcaption1 [lindex $caption 0]
      set techname [tech name]
      if {[catch {set Opts(tool)}]} {
         set Opts(tool) unknown
      }
      if {[lindex $caption 1] == "EDITING"} {
         set subcaption2 [lindex $caption 2]
      } else {
         # set subcaption2 [join [lrange $caption 1 end]]
         set subcaption2 $caption
      }
      ${framename}.titlebar.caption configure -text \
	   "Loaded: ${subcaption1} Editing: ${subcaption2} Tool: $Opts(tool) \
	   Technology: ${techname}"
   }
}

# Allow captioning in the title window by tagging the "load" and "edit" commands
# Note that the "box" tag doesn't apply to mouse-button events, so this function
# is duplicated by Tk binding of mouse events in the layout window.

magic::tag load "[magic::tag load]; magic::captions"
magic::tag edit "magic::captions"
magic::tag save "magic::captions"
magic::tag down "magic::captions"
magic::tag box "magic::boxview %W %1"
magic::tag move "magic::boxview %W"
magic::tag scroll "magic::scrollupdate %W"
magic::tag view "magic::scrollupdate %W"
magic::tag zoom "magic::scrollupdate %W"
magic::tag findbox "magic::scrollupdate %W"
magic::tag see "magic::toolupdate %W %1 %2"
magic::tag tech "magic::techrebuild %W %1; magic::captions %1"
magic::tag drc "magic::drcupdate %1"
magic::tag path "[magic::tag path]; magic::techmanager update"
magic::tag cellname "magic::mgrupdate %W %1"
magic::tag cif      "magic::mgrupdate %W %1"
magic::tag gds      "magic::mgrupdate %W %1"

# This should be a list. . . do be done later
set lwindow 0
set owindow 0

set Opts(techmgr) 0
set Opts(target)  default
set Opts(netlist) 0
set Opts(colormap) 0
set Opts(wind3d) 0
set Opts(crosshair) 0
set Opts(hidelocked) 0
set Opts(hidespecial) 0
set Opts(toolbar) 0
set Opts(scale) 1.0
set Opts(toolscale) 1.0
set Opts(drc) 1
set Opts(autobuttontext) 1

# Update cell and tech managers in response to a cif or gds read command

proc magic::mgrupdate {win {cmdstr ""}} {
   if {${cmdstr} == "read"} {
      catch {magic::cellmanager}
      magic::captions
      magic::techmanager update
   } elseif {${cmdstr} == "delete" || ${cmdstr} == "rename"} {
      catch {magic::cellmanager}
      magic::captions
   } elseif {${cmdstr} == "writeable"} {
      magic::captions
   }
}

# Set default width and height to be 3/4 of the screen size.
set Opts(geometry) \
"[expr {3 * [winfo screenwidth .] / 4}]x[expr {3 * [winfo screenheight .] \
/ 4}]+100+100"

# Procedures for the layout scrollbars, which are made from canvas
# objects to avoid the problems associated with Tk's stupid scrollbar
# implementation.

# Repainting function for scrollbars, title, etc., to match the magic
# Change the colormap (most useful in 8-bit PseudoColor)

proc magic::repaintwrapper { win } {
   set bgcolor [magic::magiccolor -]
   ${win}.xscroll configure -background $bgcolor
   ${win}.xscroll configure -highlightbackground $bgcolor
   ${win}.xscroll configure -highlightcolor [magic::magiccolor K]

   ${win}.yscroll configure -background $bgcolor
   ${win}.yscroll configure -highlightbackground $bgcolor
   ${win}.yscroll configure -highlightcolor [magic::magiccolor K]

   ${win}.titlebar.caption configure -background [magic::magiccolor w]
   ${win}.titlebar.caption configure -foreground [magic::magiccolor c]

   ${win}.titlebar.message configure -background [magic::magiccolor w]
   ${win}.titlebar.message configure -foreground [magic::magiccolor c]

   ${win}.titlebar.pos configure -background [magic::magiccolor w]
   ${win}.titlebar.pos configure -foreground [magic::magiccolor c]

}

# Coordinate display callback function
# Because "box" calls "box", use the "info level" command to avoid
# infinite recursion.

proc magic::boxview {win {cmdstr ""}} {
   if {${cmdstr} == "exists" || ${cmdstr} == "help" || ${cmdstr} == ""} {
      # do nothing. . . informational only, no change to the box
   } elseif {${cmdstr} == "remove"} {
      set framename [winfo parent $win]
      if {$framename == "."} {return}
      ${framename}.titlebar.pos configure -text "no box"
   } elseif {[info level] <= 1} {
      # For NULL window, find all layout windows and apply update to each.
      if {$win == {}} {
         set winlist [magic::windownames layout]
         foreach lwin $winlist {
	    magic::boxview $lwin
         }
         return
      }

      set framename [winfo parent $win]
      if {$framename == "."} {return}
      if {[catch {set cr [cif scale out]}]} {return}
      set bval [${win} box values]
      set bllx [expr {[lindex $bval 0] * $cr }]
      set blly [expr {[lindex $bval 1] * $cr }]
      set burx [expr {[lindex $bval 2] * $cr }]
      set bury [expr {[lindex $bval 3] * $cr }]
      if {[expr {$bllx == int($bllx)}]} {set bllx [expr {int($bllx)}]}
      if {[expr {$blly == int($blly)}]} {set blly [expr {int($blly)}]}
      if {[expr {$burx == int($burx)}]} {set burx [expr {int($burx)}]}
      if {[expr {$bury == int($bury)}]} {set bury [expr {int($bury)}]}
      set titletext [format "box (%+g %+g) to (%+g %+g) microns" \
			$bllx $blly $burx $bury]
      ${framename}.titlebar.pos configure -text $titletext
   }
}

proc magic::cursorview {win} {
   global Opts
   if {$win == {}} {
      return
   }
   set framename [winfo parent $win]
   if {[catch {set cr [*bypass cif scale out]}]} {return}
   if {$cr == 0} {return}
   set olst [${win} cursor internal]

   set olstx [lindex $olst 0]
   set olsty [lindex $olst 1]

   if {$Opts(crosshair)} {
      *bypass crosshair ${olstx}i ${olsty}i
   }

   # Use catch, because occasionally this fails on startup
   if {[catch {
      set olstx [expr {$olstx * $cr}]
      set olsty [expr {$olsty * $cr}]
   }]} {return}

   if {[${win} box exists]} {
      set dlst [${win} box position]
      set dx [expr {$olstx - ([lindex $dlst 0]) * $cr }]
      set dy [expr {$olsty - ([lindex $dlst 1]) * $cr }]
      if {[expr {$dx == int($dx)}]} {set dx [expr {int($dx)}]}
      if {[expr {$dy == int($dy)}]} {set dy [expr {int($dy)}]}
      set titletext [format "(%+g %+g) %+g %+g microns" $olstx $olsty $dx $dy]
      ${framename}.titlebar.pos configure -text $titletext
   } else {
      set titletext [format "(%+g %+g) microns" $olstx $olsty]
      ${framename}.titlebar.pos configure -text $titletext
   }
}

proc magic::toolupdate {win {yesno "yes"} {layerlist "none"}} {
   global Winopts

   if {$win == {}} {
      set win [magic::windownames]
   }

   # Wind3d has a "see" function, so make sure this is not a 3d window
   if {$win == [magic::windownames wind3d]} {
      return
   }

   set topname [winfo toplevel $win]
   set framename [winfo parent $win]

   # Don't do anything if toolbar is not present
   if { $Winopts(${topname},toolbar) == 0 } { return }

   if {$layerlist == "none"} {
	set layerlist $yesno
	set yesno "yes"
   }
   if {$layerlist == "*"} {
      set layerlist [magic::tech layer "*"]
   }

   # Change from comma-separated list to Tcl list
   set layerlist [string map {, " "} $layerlist]

   foreach layer $layerlist {
      switch $layer {
	 none {set canon ""}
	 allSame {set canon ""}
	 "*" {set canon ""}
	 "$" {set canon ""}
	 connect {set canon ""}
	 errors {set canon $layer}
	 subcell {set canon $layer}
	 labels {set canon $layer}
	 default {set canon [magic::tech layer $layer]}
      }

      # Layers may not be in the toolbar if "hidelocked" or
      # "hidespecial" is used, so catch each configure command.

      if {$canon != ""} {
         if {$yesno == "yes"} {
	    catch {${framename}.toolbar.b$canon configure -image img_$canon}
	    catch {${framename}.toolbar.p$canon configure -image pale_$canon}
         } else {
	    catch {${framename}.toolbar.b$canon configure -image img_space}
	    catch {${framename}.toolbar.p$canon configure -image img_space}
	 }
      }
   }
}

# Generate the toolbar images for a technology

proc magic::maketoolimages {} {
   global Opts

   # Tool size expands with the GUI scale but can also be expanded independently.
   set tsize [expr {int($Opts(scale) * $Opts(toolscale) * 16)}]

   # Generate a layer image for "space" that will be used when layers are
   # invisible.

   image create layer img_space -name none -width $tsize -height $tsize

   set all_layers [concat {errors labels subcell} [magic::tech layer "*"]]

   foreach layername $all_layers {
      image create layer img_$layername -name $layername -width $tsize -height $tsize
      image create layer pale_$layername -name $layername \
		-disabled true -icon 23 -width $tsize -height $tsize
    }
}

# Generate the toolbar for the wrapper

proc magic::maketoolbar { framename } {
   global Opts
   global Winopts

   # Don't do anything if in suspend mode
   set topname [winfo toplevel $framename]
   if {[info exists Winopts(${topname},suspend)]} {
      if { $Winopts(${topname},suspend) > 0} { return }
   }

   if {$Opts(toolbar) == 0} {
      magic::maketoolimages
      set Opts(toolbar) 1
   }

   # Destroy any existing toolbar before starting
   set alltools [winfo children ${framename}.toolbar]
   foreach i $alltools {
      destroy $i
   }

   # All toolbar commands will be passed to the appropriate window
   set win ${framename}.magic

   # Generate layer images and buttons for toolbar
   if {$Opts(hidespecial) == 0} {
       set special_layers {errors labels subcell}
   } else {
       set special_layers {}
   }

   if {$Opts(hidelocked) == 0} {
       set all_layers [concat $special_layers [magic::tech layer "*"]]
   } else {
       set all_layers [concat $special_layers [magic::tech unlocked]]
   }
   foreach layername $all_layers {
      button ${framename}.toolbar.b$layername -image img_$layername -command \
		"$win see $layername"

      # Bindings:  Entering the button puts the canonical layer name in the
      # message window.
      bind ${framename}.toolbar.b$layername <Enter> \
		[subst {focus %W ; ${framename}.titlebar.message configure \
		 -text "$layername"}]
      bind ${framename}.toolbar.b$layername <Leave> \
		[subst {${framename}.titlebar.message configure -text ""}]

      # 3rd mouse button makes layer invisible; 1st mouse button restores it.
      # 2nd mouse button paints the layer color.  Key "p" also does paint, esp.
      # for users with 2-button mice.  Key "e" erases, as does Shift-Button-2.

      bind ${framename}.toolbar.b$layername <ButtonPress-2> \
		"$win paint $layername"
      bind ${framename}.toolbar.b$layername <KeyPress-p> \
		"$win paint $layername"
      bind ${framename}.toolbar.b$layername <Shift-ButtonPress-2> \
		"$win erase $layername"
      bind ${framename}.toolbar.b$layername <KeyPress-e> \
		"$win erase $layername"
      bind ${framename}.toolbar.b$layername <ButtonPress-3> \
		"$win see no $layername"
      bind ${framename}.toolbar.b$layername <KeyPress-s> \
		"$win select more area $layername"
      bind ${framename}.toolbar.b$layername <KeyPress-S> \
		"$win select less area $layername"
   }

   # Create an additional set of layers and buttons in the "disabled" style
   # These buttons can be swapped in place of the regular buttons when the
   # layer is locked.  They define no bindings except "u" for "unlock",
   # and the button bindings (see, see no)

   foreach layername $all_layers {
      button ${framename}.toolbar.p$layername -image pale_$layername -command \
		"$win see $layername"
      bind ${framename}.toolbar.p$layername <ButtonPress-3> \
		"$win see no $layername"
      bind ${framename}.toolbar.p$layername <Enter> \
		[subst {focus %W ; ${framename}.titlebar.message configure \
		 -text "$layername (locked)"}]
      bind ${framename}.toolbar.p$layername <Leave> \
		[subst {${framename}.titlebar.message configure -text ""}]
   }

   # Figure out how many columns we need to fit all the layer buttons inside
   # the toolbar without going outside the window area.

   set locklist [tech locked]
   set ncols 0
   while {1} {
      incr ncols
      set i 0
      set j 0
      foreach layername $all_layers {
	 if {[lsearch $locklist $layername] >= 0} {
            grid ${framename}.toolbar.p$layername -row $i -column $j -sticky news
	 } else {
            grid ${framename}.toolbar.b$layername -row $i -column $j -sticky news
	 }
	 bind ${framename}.toolbar.p$layername <KeyPress-u> \
		"$win tech unlock $layername ; \
		grid forget ${framename}.toolbar.p$layername ; \
		grid ${framename}.toolbar.b$layername \
		-row $i -column $j -sticky news"
	 bind ${framename}.toolbar.b$layername <KeyPress-l> \
		"$win tech lock $layername ; \
		grid forget ${framename}.toolbar.b$layername ; \
		grid ${framename}.toolbar.p$layername \
		-row $i -column $j -sticky news"
         incr j
         if {$j == $ncols} {
	    set j 0
	    incr i
         }
      }

      # Make sure that window has been created so we will get the correct
      # height value.

      update idletasks
      set winheight [expr {[winfo height ${framename}] - \
		[winfo height ${framename}.titlebar]}]
      set toolheight [lindex [grid bbox ${framename}.toolbar] 3]
      if {$toolheight <= $winheight} {break}
   }
}

# Delete and rebuild the toolbar buttons in response to a "tech load"
# command.

proc magic::techrebuild {winpath {cmdstr ""}} {
   global Opts

   # For NULL window, find all layout windows and apply update to each.
   if {$winpath == {}} {
      set winlist [magic::windownames layout]
      foreach lwin $winlist {
	 magic::techrebuild $lwin $cmdstr
      }
      return
   }

   set framename [winfo parent $winpath]
   if {${cmdstr} == "load"} {
      set Opts(toolbar) 0
      maketoolbar ${framename}
      magic::techmanager init
   } elseif {${cmdstr} == "lock" || ${cmdstr} == "unlock" || ${cmdstr} == "revert"} {
      maketoolbar ${framename}
   }
}

# Scrollbar callback procs

# Procedure to return the effective X and Y scrollbar bounds for the
# current view in magic (in pixels)

proc magic::setscrollvalues {win} {
   global Opts

   set svalues [${win} view get]
   set bvalues [${win} view bbox]

   set framename [winfo parent ${win}]
   if {$framename == "."} {return}

   set bwidth [expr {[lindex $bvalues 2] - [lindex $bvalues 0]}]
   set bheight [expr {[lindex $bvalues 3] - [lindex $bvalues 1]}]

   set wwidth [winfo width ${framename}.xscroll.bar]  ;# horizontal scrollbar
   set wheight [winfo height ${framename}.yscroll.bar]  ;# vertical scrollbar

   # Note that adding 0.0 here forces floating-point

   set xscale [expr {(0.0 + $wwidth) / $bwidth}]
   set yscale [expr {(0.0 + $wheight) / $bheight}]

   set xa [expr {$xscale * ([lindex $svalues 0] - [lindex $bvalues 0]) }]
   set xb [expr {$xscale * ([lindex $svalues 2] - [lindex $bvalues 0]) }]
   set ya [expr {$yscale * ([lindex $svalues 1] - [lindex $bvalues 1]) }]
   set yb [expr {$yscale * ([lindex $svalues 3] - [lindex $bvalues 1]) }]

   # Magic's Y axis is inverted with respect to X11 window coordinates
   set ya [expr { $wheight - $ya }]
   set yb [expr { $wheight - $yb }]

   set swidth [expr {int($Opts(scale) * 13)}]
   set slength [expr {$swidth + 2}]

   ${framename}.xscroll.bar coords slider $xa 2 $xb $slength
   ${framename}.yscroll.bar coords slider 2 $ya $slength $yb

   set xb [expr { 1 + ($xa + $xb) / 2 }]
   set xa [expr { $xb - 2 }]
   ${framename}.xscroll.bar coords centre $xa 4 $xb $swidth

   set yb [expr { 1 + ($ya + $yb) / 2 }]
   set ya [expr { $yb - 2 }]
   ${framename}.yscroll.bar coords centre 4 $ya $swidth $yb
}

# Procedure to update scrollbars in response to an internal command
# "view" calls "view", so avoid infinite recursion.

proc magic::scrollupdate {win} {

   if {[info level] <= 1} {

      # For NULL window, find current window
      if {$win == {}} {
	 set win [magic::windownames]
      }

      # Make sure we're not a 3D window, which doesn't take window commands
      # This is only necessary because the 3D window declares a "view"
      # command, too.

      if {$win != [magic::windownames wind3d]} {
 	 magic::setscrollvalues $win
      }
   }
}

# scrollview:  update the magic display to match the
# scrollbar positions.

proc magic::scrollview { w win orient } {
   global scale
   set v1 $scale($orient,origin)
   set v2 $scale($orient,update)
   set delta [expr {$v2 - $v1}]

   set bvalues [${win} view bbox]
   set wvalues [${win} windowpositions]

   # Note that adding 0.000 in expression forces floating-point

   if {"$orient" == "x"} {

      set bwidth [expr {[lindex $bvalues 2] - [lindex $bvalues 0]}]
      set wwidth [expr {0.000 + [lindex $wvalues 2] - [lindex $wvalues 0]}]
      set xscale [expr {$bwidth / $wwidth}]
      ${win} scroll e [expr {$delta * $xscale}]i

   } else {

      set bheight [expr {[lindex $bvalues 3] - [lindex $bvalues 1]}]
      set wheight [expr {0.000 + [lindex $wvalues 3] - [lindex $wvalues 1]}]
      set yscale [expr {$bheight / $wheight}]
      ${win} scroll s [expr {$delta * $yscale}]i
   }
}

# setscroll: get the current cursor position and save it as a
# reference point.

proc magic::setscroll { w v orient } {
   global scale
   set scale($orient,origin) $v
   set scale($orient,update) $v
}

proc magic::dragscroll { w v orient } {
   global scale
   set v1 $scale($orient,update)
   set scale($orient,update) $v
   set delta [expr {$v - $v1}]

   if { "$orient" == "x" } {
      $w move slider $delta 0
      $w move centre $delta 0
   } else {
      $w move slider 0 $delta
      $w move centre 0 $delta
   }
}

# Scrollbar generator for the wrapper window

proc magic::makescrollbar { fname orient win } {
   global scale
   global Glyph
   global Opts

   set swidth [expr {int($Opts(scale) * 13)}]

   set scale($orient,update) 0
   set scale($orient,origin) 0

   # To be done:  add glyphs for the arrows

   if { "$orient" == "x" } {
      canvas ${fname}.bar -height $swidth -relief sunken -borderwidth 1
      button ${fname}.lb -image Glyph(left) -borderwidth 1 \
		-command "${win} scroll left .1 w"
      button ${fname}.ub -image Glyph(right) -borderwidth 1 \
		-command "${win} scroll right .1 w"
      pack ${fname}.lb -side left
      pack ${fname}.bar -fill $orient -expand true -side left
      pack ${fname}.ub -side right
   } else {
      canvas ${fname}.bar -width $swidth -relief sunken -borderwidth 1
      button ${fname}.lb -image Glyph(down) -borderwidth 1 \
		-command "${win} scroll down .1 w"
      button ${fname}.ub -image Glyph(up) -borderwidth 1 \
		-command "${win} scroll up .1 w"
      pack ${fname}.ub
      pack ${fname}.bar -fill $orient -expand true
      pack ${fname}.lb
   }

   # Create the bar which controls the scrolling and bind actions to it
   ${fname}.bar create rect 2 2 15 15 -fill steelblue -width 0 -tag slider
   ${fname}.bar bind slider <Button-1> "magic::setscroll %W %$orient $orient"
   ${fname}.bar bind slider <ButtonRelease-1> "magic::scrollview %W $win $orient"
   ${fname}.bar bind slider <B1-Motion> "magic::dragscroll %W %$orient $orient"

   # Create a small mark in the center of the scrolling rectangle which aids
   # in determining how much the window is being scrolled when the full
   # scrollbar extends past the window edges.
   ${fname}.bar create rect 4 4 $swidth $swidth -fill black -width 0 -tag centre
   ${fname}.bar bind centre <Button-1> "magic::setscroll %W %$orient $orient"
   ${fname}.bar bind centre <ButtonRelease-1> "magic::scrollview %W $win $orient"
   ${fname}.bar bind centre <B1-Motion> "magic::dragscroll %W %$orient $orient"
}

# Save all and quit.  If something bad happens like an attempt to
# write cells into an unwriteable directory, then "cellname list modified"
# will contain a list of cells, so prompt to quit with the option to cancel.
# If there are no remaining modified and unsaved cells, then just exit.
# Because cell "(UNNAMED)" is not written by "writeall force", if that is
# the only modified cell, then prompt to change its name and save; then quit.

proc magic::saveallandquit {} {
   magic::promptsave force
   set modlist [magic::cellname list modified]
   if {$modlist == {}} {
      magic::quit -noprompt
   } else {
      magic::quit
   }
}

# Create the wrapper and open up a layout window in it.

proc magic::openwrapper {{cell ""} {framename ""}} {
   global lwindow
   global owindow
   global tk_version
   global Glyph
   global Opts
   global Winopts

   # Disallow scrollbars and title caption on windows---we'll do these ourselves

   if {$lwindow == 0} {
      windowcaption off
      windowscrollbars off
      windowborder off
   }

   if {$framename == ""} {
      incr lwindow
      set framename .layout${lwindow}
   }
   set winname ${framename}.pane.top.magic

   toplevel $framename
   tkwait visibility $framename

   # Resize the window
   if {[catch {wm geometry ${framename} $Winopts(${framename},geometry)}]} {
      catch {wm geometry ${framename} $Opts(geometry)}
   }

   # Create a paned window top--bottom inside the top level window to accomodate
   # a resizable command entry window at the bottom.  Sashwidth is zero by default
   # but is resized by enabling the command entry window.

   panedwindow ${framename}.pane -orient vertical -sashrelief groove -sashwidth 6

   frame ${framename}.pane.top
   frame ${framename}.pane.bot

   set layoutframe ${framename}.pane.top

   ${framename}.pane add ${framename}.pane.top
   ${framename}.pane add ${framename}.pane.bot
   ${framename}.pane paneconfigure ${framename}.pane.top -stretch always
   ${framename}.pane paneconfigure ${framename}.pane.bot -hide true

   pack ${framename}.pane -side top -fill both -expand true

   set swidth [expr {int($Opts(scale) * 13)}]
   frame ${layoutframe}.xscroll -height $swidth
   frame ${layoutframe}.yscroll -width $swidth

   magic::makeglyphimages
   magic::makescrollbar ${layoutframe}.xscroll x ${winname}
   magic::makescrollbar ${layoutframe}.yscroll y ${winname}
   button ${layoutframe}.zb -image Glyph(zoom) -borderwidth 1 -command "${winname} zoom 2"

   # Add bindings for mouse buttons 2 and 3 to the zoom button
   bind ${layoutframe}.zb <Button-3> "${winname} zoom 0.5"
   bind ${layoutframe}.zb <Button-2> "${winname} view"

   frame ${layoutframe}.titlebar
   label ${layoutframe}.titlebar.caption -text "Loaded: none Editing: none Tool: box" \
	-foreground white -background sienna4 -anchor w -padx 15
   label ${layoutframe}.titlebar.message -text "" -foreground white \
	-background sienna4 -anchor w -padx 5
   label ${layoutframe}.titlebar.pos -text "" -foreground white \
	-background sienna4 -anchor w -padx 5

   # Menu buttons
   frame ${layoutframe}.titlebar.mbuttons

# File
   menubutton ${layoutframe}.titlebar.mbuttons.file -text File -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.file.toolmenu -borderwidth 2
# Edit
   menubutton ${layoutframe}.titlebar.mbuttons.edit -text Edit -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.edit.toolmenu -borderwidth 2
# Cell
   menubutton ${layoutframe}.titlebar.mbuttons.cell -text Cell -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.cell.toolmenu -borderwidth 2
# Window
   menubutton ${layoutframe}.titlebar.mbuttons.win -text Window -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.win.toolmenu -borderwidth 2
# Layers
   menubutton ${layoutframe}.titlebar.mbuttons.layers -text Layers -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.layers.toolmenu -borderwidth 2
# DRC
   menubutton ${layoutframe}.titlebar.mbuttons.drc -text Drc -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.drc.toolmenu -borderwidth 2
# Netlist
#   menubutton ${layoutframe}.titlebar.mbuttons.netlist -text Neltist -relief raised \
#		-menu ${layoutframe}.titlebar.mbuttons.netlist.netlistmenu -borderwidth 2
# Help
#   menubutton ${layoutframe}.titlebar.mbuttons.help -text Help -relief raised \
#		-menu ${layoutframe}.titlebar.mbuttons.help.helpmenu -borderwidth 2
# Options
   menubutton ${layoutframe}.titlebar.mbuttons.opts -text Options -relief raised \
		-menu ${layoutframe}.titlebar.mbuttons.opts.toolmenu -borderwidth 2
   pack ${layoutframe}.titlebar.mbuttons.file   -side left
   pack ${layoutframe}.titlebar.mbuttons.edit   -side left
   pack ${layoutframe}.titlebar.mbuttons.cell   -side left
   pack ${layoutframe}.titlebar.mbuttons.win    -side left
   pack ${layoutframe}.titlebar.mbuttons.layers -side left
   pack ${layoutframe}.titlebar.mbuttons.drc    -side left
#   pack ${layoutframe}.titlebar.mbuttons.netlist -side left
#   pack ${layoutframe}.titlebar.mbuttons.help    -side left
   pack ${layoutframe}.titlebar.mbuttons.opts    -side left

   # DRC status button
   checkbutton ${layoutframe}.titlebar.drcbutton -text "DRC" -anchor w \
	-borderwidth 2 -variable Opts(drc) \
	-foreground white -background sienna4 -selectcolor green \
	-command [subst { if { \$Opts(drc) } { drc on } else { drc off } }]

   magic::openwindow $cell $winname

   # Create toolbar frame.  Make sure it has the same visual and depth as
   # the layout window, so there will be no problem using the GCs from the
   # layout window to paint into the toolbar.
   frame ${layoutframe}.toolbar \
	-visual "[winfo visual ${winname}] [winfo depth ${winname}]"

   # Repaint to magic colors
   magic::repaintwrapper ${layoutframe}

   grid ${layoutframe}.titlebar -row 0 -column 0 -columnspan 3 -sticky news
   grid ${layoutframe}.yscroll -row 1 -column 0 -sticky ns
   grid $winname -row 1 -column 1 -sticky news
   grid ${layoutframe}.zb -row 2 -column 0
   grid ${layoutframe}.xscroll -row 2 -column 1 -sticky ew
   # The toolbar is not attached by default

   grid rowconfigure ${layoutframe} 1 -weight 1
   grid columnconfigure ${layoutframe} 1 -weight 1

   grid ${layoutframe}.titlebar.mbuttons -row 0 -column 0 -sticky news
   grid ${layoutframe}.titlebar.drcbutton -row 0 -column 1 -sticky news
   grid ${layoutframe}.titlebar.caption -row 0 -column 2 -sticky news
   grid ${layoutframe}.titlebar.message -row 0 -column 3 -sticky news
   grid ${layoutframe}.titlebar.pos -row 0 -column 4 -sticky news
   grid columnconfigure ${layoutframe}.titlebar 2 -weight 1

   bind $winname <Enter> "focus %W ; set Opts(focus) $framename"

   # Note: Tk binding bypasses the event proc, so it is important to
   # set the current point;  otherwise, the cursor will report the
   # wrong position and/or the wrong window.  HOWEVER we should wrap
   # this command with the "bypass" command such that it does not
   # reset any current input redirection to the terminal.

   bind ${winname} <Motion> "*bypass setpoint %x %y ${winname}; \
	magic::cursorview ${winname}"

   set Winopts(${framename},toolbar) 1
   set Winopts(${framename},cmdentry) 0

# #################################
# File
# #################################
   set m [menu ${layoutframe}.titlebar.mbuttons.file.toolmenu -tearoff 0]
   $m add command -label "Open..."      -command {magic::promptload magic}
   # $m add command -label "Save"       -command {magic::save }
   $m add command -label "Save..."      -command {magic::promptsave magic}
   # $m add command -label "Save as..." -command {echo "not implemented"}
   # $m add command -label "Save selection..." -command {echo "not implemented"}
   $m add separator
   $m add command -label "Flush changes" -command {magic::flush}
   $m add separator
   # $m add command -label "Read CIF" -command {magic::promptload cif}
   $m add command -label "Read GDS"    -command {magic::promptload gds}
   # $m add separator
   # $m add command -label "Write CIF" -command {magic::promptsave cif}
   $m add command -label "Write GDS"   -command {magic::promptsave gds}
   # $m add separator
   # $m add command -label "Print..."  -command {echo "not implemented"}
   $m add separator
   $m add command -label "Save All and Quit" -command {magic::saveallandquit}
   $m add command -label "Quit"              -command {magic::quit}

# #################################
# Edit
# #################################

   set m [menu ${layoutframe}.titlebar.mbuttons.edit.toolmenu -tearoff 0]
   # $m add command -label "Cut" -command {echo "not implemented"}
   # $m add command -label "Copy" -command {echo "not implemented"}
   # $m add command -label "Paste" -command {echo "not implemented"}
   $m add command -label "Delete" -command {delete}
   $m add separator
   $m add command -label "Select Area" -command {select area}
   $m add command -label "Select Clear" -command {select clear}
   $m add separator
   $m add command -label "Undo" -command {magic::undo}
   $m add command -label "Redo" -command {magic::redo}
   # $m add command -label "Repeat Last" -command {echo "not implemented"}
   $m add separator
   $m add command -label "Rotate 90 degree" -command {magic::clock}
   $m add command -label "Mirror   Up/Down"  -command {magic::upsidedown}
   $m add command -label "Mirror Left/Right" -command {magic::sideways}
   $m add separator
   $m add command -label "Move Right" -command {move right 1}
   $m add command -label "Move Left" -command {move left 1}
   $m add command -label "Move Up" -command {move up 1}
   $m add command -label "Move Down" -command {move down 1}
   $m add separator
   $m add command -label "Stretch Right" -command {stretch right 1}
   $m add command -label "Stretch Left" -command {stretch left 1}
   $m add command -label "Stretch Up" -command {stretch up 1}
   $m add command -label "Stretch Down" -command {stretch down 1}
   $m add separator
   $m add command -label "Text ..." \
		-command [subst {magic::update_texthelper; \
		wm deiconify .texthelper ; raise .texthelper}]

# #################################
# Cell
# #################################
   set m [menu ${layoutframe}.titlebar.mbuttons.cell.toolmenu -tearoff 0]
   $m add command -label "New..." -command {magic::prompt_dialog new}
   $m add command -label "Save as..." -command {magic::prompt_dialog save}
   $m add command -label "Select" -command {magic::select cell}
   $m add command -label "Place Instance" -command {magic::promptload getcell}
   # $m add command -label "Rename" -command {echo "not implemented"}
   $m add separator
   $m add command -label "Down hierarchy" -command {magic::pushstack}
   $m add command -label "Up   hierarchy" -command {magic::popstack}
   $m add separator
   $m add command -label "Edit" -command {magic::edit}
   $m add separator
   $m add command -label "Delete" -command \
		{magic::cellname delete [magic::cellname list window]}
   $m add separator
   $m add command -label "Expand Toggle" -command {magic::expand toggle}
   $m add command -label "Expand" -command {magic::expand}
   $m add command -label "Unexpand" -command {magic::unexpand}
   $m add separator
   $m add command -label "Lock   Cell" -command {magic::instance lock}
   $m add command -label "Unlock Cell" -command {magic::instance unlock}

# #################################
# Window
# #################################
   set m [menu ${layoutframe}.titlebar.mbuttons.win.toolmenu -tearoff 0]
   $m add command -label "Clone" -command \
		{magic::openwrapper [magic::cellname list window]}
   $m add command -label "New" -command "magic::openwrapper"
   $m add command -label "Set Editable" -command \
		"pushbox ; select top cell ; edit ; popbox"
   $m add command -label "Close" -command "closewrapper ${framename}"
   $m add separator
   $m add command -label "Full View" -command {magic::view}
   $m add command -label "Redraw" -command {magic::redraw}
   $m add command -label "Zoom Out" -command {magic::zoom 2}
   $m add command -label "Zoom In" -command {magic::zoom 0.5}
   $m add command -label "Zoom Box" -command {magic::findbox zoom}
   $m add separator
   $m add command -label "Grid on" -command {magic::grid on}
   $m add command -label "Grid off" -command {magic::grid off}
   $m add command -label "Snap-to-grid on" -command {magic::snap on}
   $m add command -label "Snap-to-grid off" -command {magic::snap off}
   $m add command -label "Measure box" -command {magic::box }
   $m add separator
   $m add command -label "Set grid 0.05um" -command {magic::grid 0.05um}
   $m add command -label "Set grid 0.10um" -command {magic::grid 0.10um}
   $m add command -label "Set grid 0.50um" -command {magic::grid 0.50um}
   $m add command -label "Set grid 1.00um" -command {magic::grid 1.00um}
   $m add command -label "Set grid 5.00um" -command {magic::grid 5.00um}
   $m add command -label "Set grid 10.0um" -command {magic::grid 10.0um}
   # $m add command -label "Set grid ..." -command {echo "not implemented"}

# #################################
# Layers
# #################################
   set m [menu ${layoutframe}.titlebar.mbuttons.layers.toolmenu -tearoff 0]
   $m add command -label "Protect Base Layers" -command {magic::tech revert}
   $m add command -label "Unlock  Base Layers" -command {magic::tech unlock *}
   $m add separator
   $m add command -label "Clear Feedback" -command {magic::feedback clear}
   $m add separator

# #################################
# DRC
# #################################
   set m [menu ${layoutframe}.titlebar.mbuttons.drc.toolmenu -tearoff 0]
   # $m add command -label "DRC On" -command {drc on}
   # $m add command -label "DRC Off" -command {drc off}
   # $m add separator
   $m add command -label "DRC update" -command {drc check; drc why}
   $m add command -label "DRC report" -command {drc why}
   $m add command -label "DRC Find next error" -command {drc find; findbox zoom}
   $m add separator
   $m add command -label "DRC Fast"     -command {drc style drc(fast)}
   $m add command -label "DRC Complete" -command {drc style drc(full)}

   set m [menu ${layoutframe}.titlebar.mbuttons.opts.toolmenu -tearoff 0]
   $m add check -label "Toolbar" -variable Winopts(${framename},toolbar) \
	-command [subst {if { \$Winopts(${framename},toolbar) } { \
		magic::maketoolbar ${layoutframe} ; \
		grid ${layoutframe}.toolbar -row 1 -column 2 -rowspan 2 -sticky new ; \
		} else { \
		grid forget ${layoutframe}.toolbar } }]

   $m add check -label "Toolbar Hide Locked" \
	-variable Opts(hidelocked) \
	-command "magic::maketoolbar ${layoutframe}"

   .winmenu add radio -label ${framename} -variable Opts(target) -value ${winname}
   if {$tk_version >= 8.5} {
     $m add check -label "Cell Manager" -variable Opts(cellmgr) \
	-command [subst { magic::cellmanager create; \
	if { \$Opts(cellmgr) } { \
	   wm deiconify .cellmgr ; raise .cellmgr \
	} else { \
	   wm withdraw .cellmgr } }]
      .winmenu entryconfigure last -command ".cellmgr.target.list configure \
         -text ${framename}"
   }
   if {$tk_version >= 8.5} {
     $m add check -label "Library Manager" -variable Opts(libmgr) \
	-command [subst { magic::libmanager create; \
	if { \$Opts(libmgr) } { \
	   wm deiconify .libmgr ; raise .libmgr \
	} else { \
	   wm withdraw .libmgr } }]
      .winmenu entryconfigure last -command ".libmgr.target.list configure \
         -text ${framename}"
   }
   $m add check -label "Tech Manager" -variable Opts(techmgr) \
	-command [subst { magic::techmanager create; \
		if { \$Opts(techmgr) } { \
		wm deiconify .techmgr ; raise .techmgr \
		} else { \
		wm withdraw .techmgr } }]

   $m add check -label "DRC Manager" -variable Opts(drcmgr) \
	-command [subst { magic::drcmanager create; \
		if { \$Opts(drcmgr) } { \
		wm deiconify .drcmgr ; raise .drcmgr \
		} else { \
		wm withdraw .drcmgr } }]

   $m add check -label "Netlist Window" -variable Opts(netlist) \
	-command [subst { if { \[windownames netlist\] != {}} { \
		   set Opts(netlist) 0 ; closewindow \[windownames netlist\] \
		} else { \
		   set Opts(netlist) 1 ; specialopen netlist \
		} }]

   $m add check -label "Colormap Window" -variable Opts(colormap) \
	-command [subst { if { \[windownames color\] != {}} { \
		   set Opts(colormap) 0 ; closewindow \[windownames color\] \
		} else { \
		   set Opts(colormap) 1 ; specialopen color \
		} }]

   $m add check -label "3D Display" -variable Opts(wind3d) \
	-command [subst { if { \[windownames wind3d\] != {}} { \
		   set Opts(wind3d) 0 ; .render.magic closewindow ; \
		   destroy .render \
		} else { \
		   set Opts(wind3d) 1 ; \
		   magic::render3d \[${winname} cellname list window\] \
		} }]

   $m add check -label "Window Command Entry" \
	-variable Winopts(${framename},cmdentry) \
	-command [subst { if { \$Winopts(${framename},cmdentry) } { \
		addcommandentry $framename \
		} else { \
		deletecommandentry $framename } }]

   $m add check -label "Crosshair" \
	-variable Opts(crosshair) \
	-command {if {$Opts(crosshair) == 0} {crosshair off}}

   catch {addmazehelper $m}

   # Set the default view

   update idletasks
   ${winname} magic::view

   magic::captions

   # If the toolbar is turned on, invoke the toolbar button
   if { $Winopts(${framename},toolbar) == 1} {
      magic::maketoolbar ${layoutframe}
      grid ${layoutframe}.toolbar -row 1 -column 2 -rowspan 2 -sticky new
   }

   # Remove "open" and "close" macros so they don't generate non-GUI
   # windows or (worse) blow away the window inside the GUI frame

   if {[magic::macro list o] == "openwindow"} {
      magic::macro o \
	   "incr owindow ;\
	   set rpt \[cursor screen\] ;\
	   set rptx \[lindex \$rpt 0\] ;\
	   set rpty \[lindex \$rpt 1\] ;\
	   set Winopts(.owindow\$owindow,geometry) 500x500+\$rptx+\$rpty ;\
	   openwrapper \[\$Opts(focus)*.magic cellname list window\] \
	   .owindow\$owindow ;\
	   .owindow\$owindow*.magic view \[box values\]"
   }
   if {[magic::macro list O] == "closewindow"} {
      magic::macro O "closewrapper \$Opts(focus)"
   }

   # Make sure that closing from the window manager is equivalent to
   # the command "closewrapper"
   wm protocol ${framename} WM_DELETE_WINDOW "closewrapper ${framename}"

   # If the variable $Opts(callback) is defined, then attempt to execute it.
   catch {eval $Opts(callback)}

   # If the variable $Winopts(callback) is defined, then attempt to execute it.
   catch {eval $Winopts(${framename}, callback)}

   # Since one purpose of the window callback is to customize the menus,
   # run the automatic generation of accelerator key text at the end.
   # This can be subverted by setting Opts(autobuttontext) to 0, e.g.,
   # to put it at the top of the Winopts callback and then generate
   # override values for specific buttons.
   if {$Opts(autobuttontext)} {
      catch {magic::button_auto_bind_text $layoutframe}
   }
   return ${winname}
}

# Delete the wrapper and the layout window in it.

proc magic::closewrapper { framename } {
   global tk_version
   global Opts

   # Remove this window from the target list in .winmenu
   # (used by, e.g., cellmanager)

   set layoutframe ${framename}.pane.top
   if { $Opts(target) == "${layoutframe}.magic" } {
      set Opts(target) "default"
      if {$tk_version >= 8.5} {
	 if {![catch {wm state .cellmgr}]} {
	    .cellmgr.target.list configure -text "default"
         }
      }
   }

   set idx [.winmenu index $framename]
   .winmenu delete $idx

   ${layoutframe}.magic magic::closewindow
   destroy $framename
}

# This procedure adds a command-line entry window to the bottom of
# a wrapper window (rudimentary functionality---incomplete)

proc magic::addcommandentry { framename } {
   set commandframe ${framename}.pane.bot
   if {![winfo exists ${commandframe}.eval]} {
      tkshell::YScrolled_Text ${commandframe}.eval -height 5
      tkshell::MakeEvaluator ${commandframe}.eval.text \
		"${framename}>" ${framename}.pane.top.magic
      pack ${commandframe}.eval -side top -fill both -expand true
      ${framename}.pane paneconfigure ${framename}.pane.bot -stretch never
      ${framename}.pane paneconfigure ${framename}.pane.bot -minsize 50
   }
   set entercmd [bind ${framename}.pane.top.magic <Enter>]
   set bindstr "$entercmd ; macro XK_colon \"set Opts(redirect) 1;\
		focus ${commandframe}.eval.text\";\
		alias puts tkshell::PutsTkShell"
   bind ${commandframe}.eval <Enter> \
	"focus ${commandframe}.eval.text ; set Opts(focus) $framename ;\
	catch {unset Opts(redirect)}"
   bind ${framename}.pane.top.magic <Enter> $bindstr
   # Make command entry window visible
   ${framename}.pane paneconfigure ${framename}.pane.bot -hide false
}

# Remove the command entry window from the bottom of a frame.

proc magic::deletecommandentry { framename } {
   set commandframe ${framename}.pane.bot
   ::grid forget ${commandframe}.eval
   # Remove the last bindings for <Enter>
   set bindstr [bind ${framename}.pane.top.magic <Enter>]
   set i [string first "; macro" $bindstr]
   set bindstr [string range $bindstr 0 $i-1]
   bind ${framename}.pane.top.magic <Enter> $bindstr
   # Restore the keybinding for colon
   imacro XK_colon ":"
   # Restore the alias for "puts"
   alias puts ::tkcon_puts
   # Make command entry window invisible
   ${framename}.pane paneconfigure ${framename}.pane.bot -hide true
}

namespace import magic::openwrapper
puts "Use openwrapper to create a new GUI-based layout window"
namespace import magic::closewrapper
puts "Use closewrapper to remove a new GUI-based layout window"

# Create a simple wrapper for the 3D window. . . this can be
# greatly expanded upon.

proc magic::render3d {{cell ""}} {
   global Opts

   toplevel .render
   tkwait visibility .render
   magic::specialopen wind3d $cell .render.magic
   .render.magic cutbox box
   set Opts(cutbox) 1
   wm protocol .render WM_DELETE_WINDOW "set Opts(wind3d) 0 ; \
		.render.magic closewindow ; destroy .render"

   frame .render.title
   pack .render.title -expand true -fill x -side top
   checkbutton .render.title.cutbox -text "Cutbox" -variable Opts(cutbox) \
	-foreground white -background sienna4 -anchor w -padx 15 \
	-command [subst { if { \$Opts(cutbox) } { .render.magic cutbox box \
	} else { \
	.render.magic cutbox none } }]

   if {$cell == ""} {set cell default}
   label .render.title.msg -text "3D Rendering window  Cell: $cell" \
	-foreground white -background sienna4 -anchor w -padx 15
   pack .render.title.cutbox -side left
   pack .render.title.msg -side right -fill x -expand true
   pack .render.magic -expand true -fill both -side bottom
   bind .render.magic <Enter> {focus %W}
}

