#
#  File: xschem.tcl
#
#  This file is part of XSCHEM,
#  a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
#  simulation.
#  Copyright (C) 1998-2024 Stefan Frederik Schippers
#
#  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

### INUTILE integration (spice stimuli generator from a higher level description language)
proc inutile_line {txtlabel} {
   toplevel .inutile_line -class Dialog
   set X [expr {[winfo pointerx .inutile_line] - 60}]
   set Y [expr {[winfo pointery .inutile_line] - 35}]
   wm geometry .inutile_line "+$X+$Y"
   wm transient .inutile_line [xschem get topwindow]
   label .inutile_line.l1  -text $txtlabel
   entry .inutile_line.e1   -width 60
   entry_replace_selection .inutile_line.e1
   .inutile_line.e1 delete 0 end
   .inutile_line.e1 insert 0 $tctx::retval
   button .inutile_line.b1 -text "OK" -takefocus 0 -command \
   {
     set tctx::retval [.inutile_line.e1 get ]
     destroy .inutile_line
   }
   bind .inutile_line <Return> {
     set tctx::retval [.inutile_line.e1 get ]
     destroy .inutile_line
   }
   pack .inutile_line.l1 -side top -fill x
   pack .inutile_line.e1  -side top -fill both -expand yes
   pack .inutile_line.b1 -side top -fill x
   grab set .inutile_line
   focus .inutile_line.e1
   tkwait window .inutile_line
   return $tctx::retval
}

proc inutile_write_data {w f} {
 set fid [open $f w]
 set t [$w get  0.0 {end - 1 chars}]
 puts  -nonewline $fid $t
 close $fid
}

proc inutile_read_data {w f} {
 set fid [open $f r]
 set t [read $fid]
 $w delete 0.0 end
 $w insert 0.0 $t
 close $fid
}

proc inutile_template {w f} {
 set fid [open $f r]
 set t [read $fid]
 $w insert 0.0 $t
 close $fid
}

proc inutile_get_time {} {
 global netlist_dir
 regsub {/$} $netlist_dir {} netlist_dir
 set fileid [open "$netlist_dir/inutile.simulationtime"  "RDONLY"]
 .inutile.buttons.time delete 0 end
 .inutile.buttons.time insert 0 [read -nonewline $fileid]
 close $fileid
 file delete "$netlist_dir/inutile.simulationtime"
}

proc inutile_alias_window {w filename} {
 catch {destroy $w}
 toplevel $w -class Dialog
 wm title $w "(IN)UTILE ALIAS FILE: $filename"
 wm iconname $w "ALIAS"
 # wm transient $w [xschem get topwindow]

 set fileid [open $filename "RDONLY CREAT"]
 set testo [read $fileid]
 close $fileid
 frame $w.buttons -takefocus 0
 pack $w.buttons -side bottom -fill x -pady 2m
 text $w.text -undo 1 -relief sunken -bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 \
	 -height 30
 scrollbar $w.scroll -command "$w.text yview"
 button $w.buttons.dismiss -text Dismiss -command "destroy $w" -takefocus 0
 button $w.buttons.save -text Save -command "inutile_write_data $w.text \"$filename\"" -takefocus 0
 button $w.buttons.load -text Reload -command "inutile_read_data $w.text \"$filename\"" -takefocus 0
 pack $w.buttons.dismiss $w.buttons.save $w.buttons.load -side left -expand 1

 pack $w.scroll -side right -fill y
 pack $w.text -expand yes -fill both
 $w.text insert 0.0 $testo
}

proc inutile_help_window {w filename} {
 catch {destroy $w}
 toplevel $w -class Dialog
 wm title $w "(IN)UTILE ALIAS FILE"
 wm iconname $w "ALIAS"
 # wm transient $w [xschem get topwindow]

 frame $w.buttons -takefocus 0
 pack $w.buttons -side bottom -fill x -pady 2m
 button $w.buttons.dismiss -text Dismiss -command "destroy $w" -takefocus 0
 button $w.buttons.save -text Save -command "inutile_write_data $w.text \"$filename\"" -takefocus 0
 pack $w.buttons.dismiss  $w.buttons.save -side left -expand 1

 text $w.text -undo 1 -relief sunken -bd 2 -yscrollcommand "$w.scroll set" -setgrid 1 \
	 -height 30 -width 90
 scrollbar $w.scroll -command "$w.text yview" -takefocus 0
 pack $w.scroll -side right -fill y
 pack $w.text -expand yes -fill both
 set fileid [open $filename "RDONLY CREAT"]
 $w.text insert 0.0 [read $fileid]
 close $fileid
}

proc inutile_translate {f} {
  global XSCHEM_SHAREDIR netlist_dir
  regsub {/$} $netlist_dir {} netlist_dir
  set p $XSCHEM_SHAREDIR/utile
  set savedir [pwd]
  cd $netlist_dir
  eval exec awk -f $p/preprocess.awk \"$f\" | awk -f $p/expand_alias.awk | awk -f $p/param.awk | awk -f $p/clock.awk | awk -f $p/stimuli.awk
  cd $savedir
}

proc inutile { {filename {}} {wait {}} } {
  global XSCHEM_SHAREDIR netlist_dir

  if {$wait ne {}} {
    xschem set semaphore [expr {[xschem get semaphore] +1}]
  }
  if { ![string compare $filename  ""]  } then {
   tk_messageBox -type ok -message "Please give a file name as argument"
   return
  }
  toplevel .inutile -class Dialog
  wm title .inutile "(IN)UTILE (Stefan Schippers, sschippe)"
  wm iconname .inutile "(IN)UTILE"
  # wm transient .inutile [xschem get topwindow]
  set utile_path $XSCHEM_SHAREDIR/utile
  set tctx::retval {}
  frame .inutile.buttons -takefocus 0
  pack .inutile.buttons -side bottom -fill x -pady 2m
  button .inutile.buttons.translate -takefocus 0 -text Translate -command "
    inutile_write_data .inutile.text \"$filename\"
    inutile_translate \"$filename\"
    inutile_get_time"
  button .inutile.buttons.dismiss -takefocus 0 -text Dismiss -command "destroy .inutile"
  button .inutile.buttons.code -takefocus 0 -text "Help" \
     -command "inutile_help_window .inutile.help $utile_path/utile.txt"
  text .inutile.text -undo 1 -relief sunken -bd 2 -yscrollcommand ".inutile.scroll set" -setgrid 1 -height 30
  scrollbar .inutile.scroll -command {.inutile.text yview}
  button .inutile.buttons.save -takefocus 0 -text Save -command "
    set tctx::retval \"$filename\"
    set filename \[inutile_line {Filename}\]
    inutile_write_data .inutile.text \"$filename\""
  button .inutile.buttons.load -takefocus 0 -text Reload -command "
    set tctx::retval \"$filename\"
    set filename \[inutile_line {Filename}\]
    inutile_read_data .inutile.text \"$filename\""
  button .inutile.buttons.send -takefocus 0 -text "Template" -command "
    if { !\[string compare \[.inutile.text get 0.0 {end - 1 chars}\]  {}\]} {
      inutile_template  .inutile.text  $utile_path/template.stimuli}"
  label .inutile.buttons.timelab -text "time:"
  entry .inutile.buttons.time  -width  11
  entry_replace_selection .inutile.buttons.time
  pack .inutile.buttons.dismiss .inutile.buttons.code \
       .inutile.buttons.load .inutile.buttons.save .inutile.buttons.translate \
       .inutile.buttons.send .inutile.buttons.timelab \
       .inutile.buttons.time  -side left -expand 1
  pack .inutile.scroll -side right -fill y
  pack .inutile.text -expand yes -fill both
  if { [file exists $filename] }  {
    set fileid [open $filename "RDONLY"]
    .inutile.text insert 0.0 [read $fileid]
    close $fileid
  }
  set tmp [.inutile.text index end]
  regsub {\..*$} $tmp {} lines
  for {set i 1} {$i <= $lines} {incr i} {
   set tmp [.inutile.text get $i.0 "$i.0 lineend"]
   if [regexp {^(include)|(\.include)} $tmp  ] {
    inutile_alias_window .inutile.tw$i [lindex $tmp 1]
   }
  }
  if {$wait ne {}} {
    tkwait window .inutile
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
}

### End INUTILE integration

### for tclreadline: disable customcompleters
proc completer { text start end line } { return {}}

# set 'var' with '$val' if 'var' not existing
proc set_ne { var val } {
    upvar #0 $var v
    if { ![info exists v ] } {
      set v $val
    }
}


# $execute(id) is an integer identifying the last running pipeline
#       set id $execute(id) to get the current subprocess id in variable 'id'
# $execute(status,$id) contains the status argument as given in proc execute
# $execute(pipe,$id) contains the channel descriptor to read or write as specified in status
# $execute(data,$id) contains the stdout of the pipeline (output data)
# $execute(cmd,$id) contains the pipeline command
# $execute(win_path,$id) contains the xctx->current_win_path that started the command
# $execute(exitcode,id) contains the exit code. This variable will not be deleted at the end
# when subprocess ends all execute(...,$id) data is cleared (except execute(exitcode,id)
#
# The following post-mortem data is available for last finished process:
#   execute(cmd,last)     : the command
#   execute(win_path,last): the xctx->current_win_path that started the last command
#   execute(data,last)    : the data
#   execute(error,last)   : the errors (stderr)
#   execute(status,last)  : the status argument as was given when calling proc execute
#   execute(exitcode,last): exit code of last finished process
#
# execute service function
proc execute_fileevent {id} {
  global execute OS has_x errorCode

  append execute(data,$id) [read $execute(pipe,$id) 1024]
  if { ![regexp -nocase {windows} $OS] && ![regexp -nocase {cygwin} $OS]} {
    set eof [eof $execute(pipe,$id)]
    # handle processes that close stdout. Read pipe will go into eof condition
    # but process is still running. Doing a close operation in blocking mode
    # will block execution until process exits.
    # In this situation we avoid setting pipe to blocking mode and do an
    # asynchronous close. We lose exit status and stderr though, but
    # avoid the program to freeze waiting for process to exit.
    set lastproc [lindex [pid $execute(pipe,$id)] end]
    set ps_status [exec ps -o state= -p $lastproc]
    set finished [regexp Z $ps_status] ;# if zombie consider process to be finished.
    if { $eof && !$finished} {
      after 2000 "execute_fileevent $id"
      fileevent $execute(pipe,$id) readable ""
      # puts "rescheduling $id"
      return
    }
  } else {
    set eof [eof $execute(pipe,$id)]
    set finished 1
  }
  if {$eof} {
      set report [regexp {1} $execute(status,$id)]
      fileevent $execute(pipe,$id) readable ""
      # setting pipe to blocking before closing allows to get pipeline exit status
      # do not ask status for processes that close stdout/stderr, as eof might
      # occur before process ends and following close blocks until process terminates.
      if {$finished} {fconfigure $execute(pipe,$id) -blocking 1}
      set exit_status 0
      set catch_return [eval catch [list {close $execute(pipe,$id)} err] ]
      # puts "catch_return=$catch_return"
      if {$catch_return} {
        # puts "errorCode=$errorCode"
        if {"NONE" == [lindex $errorCode 0]} {
          set exit_status 0
        } elseif {"CHILDSTATUS" == [lindex $errorCode 0]} {
          set exit_status [lindex $errorCode 2]
          # puts "exit_status=$exit_status"
        } else {
          set exit_status  $catch_return
        }
        if {$exit_status != 0} {
          if {$report} {viewdata "Failed: $execute(cmd,$id)\nstderr:\n$err\ndata:\n$execute(data,$id)"}
        } else {
          if {$report} {viewdata "Completed: $execute(cmd,$id)\nstderr:\n$err\ndata:\n$execute(data,$id)"}
        }
      } else {
        if {$report} {viewdata "Completed: $execute(cmd,$id)\ndata:\n$execute(data,$id)"}
      }
      set execute(exitcode,last) $exit_status
      set execute(exitcode,$id) $exit_status
      set execute(cmd,last) $execute(cmd,$id)
      set execute(win_path,last) $execute(win_path,$id)
      set execute(data,last) $execute(data,$id)
      if { ![info exists err] } { set err {} }
      set execute(error,last) $err
      set execute(status,last) $execute(status,$id)

      if { [info exists tctx::$execute(win_path,$id)_simulate_id] } {
        if { [set tctx::$execute(win_path,$id)_simulate_id] eq $id } {
          unset tctx::$execute(win_path,$id)_simulate_id
        }
      }

      if {[info exists execute(callback,$id)] && $execute(callback,$id) ne {}} {
        # puts  $execute(callback,$id)
        # puts $execute(win_path,$id)
        eval uplevel #0 [list $execute(callback,$id)]
      }
      catch {unset execute(callback,$id)}

      if { ![info exists exit_status] } { set exit_status 0 }
      unset execute(pipe,$id)
      unset execute(data,$id)
      unset execute(status,$id)
      unset execute(cmd,$id)
      unset execute(win_path,$id)
      # apply a delay, process does not disappear immediately.
      if {[info exists has_x] && [winfo exists .processlist]} { after 250 {insert_running_cmds .processlist.f2.lb}}
  }
}

proc execute_wait {status args} {
  global execute
  set id [eval execute $status $args]
  if {$id == -1} {
    return -1
  }
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  vwait execute(pipe,$id)
  xschem set semaphore [expr {[xschem get semaphore] -1}]
  return $id
}

# equivalent to the 'exec' tcl function but keeps the event loop
# responding, so widgets get updated properly
# while waiting for process to end.
# status:
#   rw     open pipe in 'r+' (read write) mode instead of 'r'
#   line   set line buffering mode of channel
#   none   set no channel buffering.
#   1      get status report at process end
#   0      no status report
#
# These options can be combined as in '1rwline' of '1rnone'
#
proc execute {status args} {
  global execute has_x
  if {![info exists execute(id)]} {
      set execute(id) 0
  } else {
      incr execute(id)
  }
  set id $execute(id)
  if { [info exists execute(callback)] } {
    set execute(callback,$id) $execute(callback)
    unset execute(callback)
  }
  set mode r
  if {[regexp {rw} $status]} {
    set mode r+
  }
  if { [catch {open "|$args" $mode} err] } {
    puts stderr "Proc execute error: $err"
    if { [info exists has_x]} {
        tk_messageBox -message  \
          "Can not execute '$args': ensure it is available on the system. Error: $err" \
           -icon error -parent [xschem get topwindow] -type ok
    }
    return -1
  } else {
    set pipe $err
  }
  set execute(status,$id) $status
  set execute(pipe,$id) $pipe
  set execute(cmd,$id) $args
  set execute(win_path,$id) [xschem get current_win_path]
  set execute(data,$id) ""

  # Apply a delay to catch the new process.
  if {[info exists has_x] && [winfo exists .processlist]} { after 250 {insert_running_cmds .processlist.f2.lb}}
  fconfigure $pipe -blocking 0
  if {[regexp {line} $status]} {
    fconfigure $pipe -buffering line
  }
  if {[regexp {none} $status]} {
    fconfigure $pipe -buffering none
  }
  fileevent $pipe readable "execute_fileevent $id"
  return $id
}

# kill selected sub-processes by looking up their command strings
# into all running sub-processes, killing the matching ones
# with the supplied 'sig'.
proc kill_running_cmds {{lb {}} sig} {
  global execute
  if { [regexp {^[0-9]+$} $lb] && $lb >= 0 } {
    set id $lb
    if { [info exists execute(pipe,$id)] } {
      set pid [pid $execute(pipe,$id)]
      exec kill $sig $pid
    } else {
      return 0
    }
  } else {
    set selected [$lb curselection]
    foreach idx $selected {
      set cmd1 [$lb get $idx]
      foreach  {id pid cmd2} [get_running_cmds] {
        # puts "$cmd1\n$cmd2 \n$pid"
        if { $cmd1 eq $cmd2 } {
          exec kill $sig $pid
          break
        }
      }
    }
    after 250 insert_running_cmds $lb
  }
  # apply a delay, after a kill command process does not disappear
  # immediately.
  return 1
}

# refresh list of running commands in dialog box
proc insert_running_cmds {lb} {
  $lb delete 0 end
  foreach {id pid cmd} [get_running_cmds] {
    # puts "inserting $cmd"
    $lb insert end $cmd
  }
}

# periodically update status
proc update_process_status {lb} {
  global execute has_x
  set exists 0
  if { ![info exists has_x]} {return}
  if {[winfo exists .processlist] && [winfo exists .pstat] } {
    set selected [$lb curselection]
    set pos [lindex [.pstat.text yview] 1]
    if {$pos == 1} {
      if { $selected ne {} && [llength $selected] == 1} {
        .pstat.text delete 1.0 end
        set idx $selected
        set cmd1 [$lb get $idx]
        foreach  {id pid cmd2} [get_running_cmds] {
          if { $cmd1 eq $cmd2 } {
            if {[catch { set t $execute(data,$id) } err]} {
              set t $err
            }
            .pstat.text insert 1.0 $t
            .pstat.text yview moveto 1
            break
          }
        }
      }
    }
    after 1000 "update_process_status $lb"
  } else {
   after cancel "update_process_status $lb"
  }
}


# display stdout of selected sub-process
proc view_process_status {lb} {
  global execute has_x
  set exists 0
  if {![info exists has_x]} {return}
  if {[winfo exists .pstat] } {
    .pstat.text delete 1.0 end
    set exists 1
  }
  set selected [$lb curselection]
  if {$selected ne {} && [llength $selected] == 1} {
    set idx $selected
    set cmd1 [$lb get $idx]
    foreach  {id pid cmd2} [get_running_cmds] {
      if { $cmd1 eq $cmd2 } {
        if {[catch { set t $execute(data,$id) } err]} {
          set t $err
        }
        if {$exists == 0} {
          viewdata $t ro .pstat
        } else {
         .pstat.text insert 1.0 $t
        }
        .pstat.text yview moveto 1
        break
      }
    }
  }
  after 1000 "update_process_status $lb"
}

proc list_running_cmds_title {} {
  if {[winfo exists .processlist]} {
    wm title .processlist "List of running commands - [xschem get current_name]"
    after 1000 "list_running_cmds_title"
  }
}

# top level dialog displaying running sub-processes
proc list_running_cmds {} {
  global has_x
  set top .processlist
  if {![info exists has_x]} {return}
  if {[winfo exists $top]} {raise $top; return}
  toplevel $top -class Dialog
  list_running_cmds_title
  # wm transient $top [xschem get topwindow]
  set frame1 $top.f1
  set frame2 $top.f2
  set frame3 $top.f3
  frame $frame1 -takefocus 0
  frame $frame2 -takefocus 0
  frame $frame3 -takefocus 0
  set lb $frame2.lb
  listbox $lb -width 70 -height 8 -selectmode extended \
     -yscrollcommand "$frame2.yscroll set" \
     -xscrollcommand "$frame2.xscroll set"
  scrollbar $frame2.yscroll -command "$lb yview"
  scrollbar $frame2.xscroll -orient horiz -command "$lb xview"
  pack  $frame2.yscroll -side right -fill y
  pack  $frame2.xscroll -side bottom -fill x
  pack  $lb -side bottom  -fill both -expand true

  bind $lb <Double-Button-1> [list $frame3.b3 invoke]
  button $frame3.b1 -width 16 -takefocus 0 -text {Terminate selected} -command "kill_running_cmds $lb -15" \
    -fg black -background yellow
  button $frame3.b2 -width 16 -takefocus 0 -text {Kill selected} -command "kill_running_cmds $lb -9" \
    -fg black -background red
  button $frame3.b3 -width 16 -takefocus 0 -text {View status} -command "view_process_status $lb" \
    -fg black -background PaleGreen
  button $frame3.b4 -width 16 -takefocus 0 -text {Dismiss} \
    -fg black -background PaleGreen -command "
    if {\[winfo exists .pstat\]} {
      after cancel [list update_process_status $lb]
      destroy .pstat
    }
    destroy $top

  "
  wm protocol $top WM_DELETE_WINDOW " $frame3.b4 invoke "
  pack $frame3.b1 $frame3.b2 $frame3.b3 $frame3.b4 -side left -fill x -expand 1
  pack $frame1 -fill x -expand 0
  pack $frame2 -fill both -expand 1
  pack $frame3 -fill x -expand 0

  insert_running_cmds $lb
}

# for each running sub-process return a list of three elements per process:
# the integer id, the process PID, the command string.
proc get_running_cmds {} {
  global execute
  set ret {}
  foreach i [array names execute *pipe*] {
    set id [lindex [split $i ,] 1]
    lappend ret $id  [pid $execute($i)] $execute(cmd,$id)
  }
  return $ret
}

# pause for $del_ms milliseconds, keep event loop responsive
proc delay {del_ms} {
  after $del_ms {set tctx::delay_flag 1}
  vwait tctx::delay_flag
  unset tctx::delay_flag
}

#### Scrollable frame
proc sframeyview {container args} {
  global ${container}_vpos ;# global to remember scrollbar position
  set_ne ${container}_vpos 0
  if {[lindex $args 0] eq {place}} {
    place ${container}.f.scrl -in $container.f -x 0 -y 0 -relwidth 1
    update ;# without this vpos of scrollbar will not be remembered when reopening toplevel
  }
  set ht [winfo height $container.f]
  set hs [winfo height $container.f.scrl]
  set frac [expr {double($ht)/$hs}]
  if { [lindex $args 0] eq {scroll}} { ;# mouse wheel
    set ${container}_vpos [expr {[set ${container}_vpos] + [lindex $args 1] *(1.0/$frac)/5}]
  } elseif { [lindex $args 0] eq {moveto}} { ;# scrollbar slider
    set ${container}_vpos [lindex $args 1]
  }
  if { [set ${container}_vpos] < 0.0 } {set ${container}_vpos 0.0}
  if { [set ${container}_vpos] > 1.0 - $frac } {set ${container}_vpos [expr {1.0 - $frac}]}
  $container.vs set [set ${container}_vpos] [expr {[set ${container}_vpos] + $frac}]
  place $container.f.scrl -in $container.f -x 0 -y [expr {-$hs * [set ${container}_vpos]}] -relwidth 1.0
}

# scrollable frame constructor
proc sframe {container} {
  frame $container.f -takefocus 0
  scrollbar $container.vs -command "sframeyview $container" ;# sframeyview moveto commands
  frame $container.f.scrl -takefocus 0
  pack $container.f -expand yes -fill both -side left
  pack $container.vs -expand yes -fill y
  return $container.f.scrl
}
#### /Scrollable frame

## convert engineering form to number
proc from_eng {i} {
  set str {}
  scan $i "%g%s" n str
  set str [string tolower $str]
  if { [regexp {^meg} $str] } { set suffix {meg} } else {
    set suffix [string index $str 0]
  }
  set mult [switch $suffix {
    a         { expr {1e-18}}
    f         { expr {1e-15}}
    p         { expr {1e-12}}
    n         { expr { 1e-9}}
    u         { expr {1e-6}}
    m         { expr {1e-3}}
    k         { expr {1e3}}
    x         { expr {1e6}}
    meg       { expr {1e6}}
    g         { expr {1e9}}
    t         { expr {1e12}}
    default   { expr {1.0}}
  }]
  return [expr {$n * $mult}]
}

## convert number to engineering form
proc to_eng {args} {
  global ev_precision
  if {[info exists ev_precision]} {set pr $ev_precision} else {set pr 4}

  set suffix {}

  if {[ catch {uplevel #0 expr [join $args]} i]} {
    return [join $args]
  }
  set i [uplevel #0 expr [join $args]]
  set absi [expr {abs($i)}]

  if       {$absi == 0.0}  { set mult 1    ; set suffix {}
  } elseif {$absi >=1e12}  { set mult 1e-12; set suffix T
  } elseif {$absi >=1e9}   { set mult 1e-9 ; set suffix G
  } elseif {$absi >=1e6}   { set mult 1e-6 ; set suffix M
  } elseif {$absi >=1e3}   { set mult 1e-3 ; set suffix k
  } elseif {$absi >=0.1}   { set mult 1    ; set suffix {}
  } elseif {$absi >=1e-3}  { set mult 1e3  ; set suffix m
  } elseif {$absi >=1e-6}  { set mult 1e6  ; set suffix u
  } elseif {$absi >=1e-9}  { set mult 1e9  ; set suffix n
  } elseif {$absi >=1e-12} { set mult 1e12 ; set suffix p
  } elseif {$absi >=1e-15} { set mult 1e15 ; set suffix f
  } else                   { set mult 1e18 ; set suffix a}
  if {$suffix ne {}} {
    set i [expr {$i * $mult}]
    set s [format %.${pr}g%s $i $suffix]
  } else {
    set s [format %.${pr}g $i]
  }
  return $s
}

proc = {args} {
  set r [ev $args]
  puts $r
}

## evaluate expression with 7 significant digits.
## if expression has errors or does not evaluate return expression as is
proc ev7 {args} {
  set i [join $args]
  if {![catch {uplevel #0 expr $i} res]} {
    return [format %.7g $res]
  } else {
    # puts stderr "proc ev: $res"
    return $args
  }
}

## evaluate expression. if expression has errors or does not evaluate return expression as is
proc ev {args} {
  global ev_precision
  if {[info exists ev_precision]} {set pr $ev_precision} else {set pr 4}
  set i [join $args]
  if {![catch {uplevel #0 expr $i} res]} {
    return [format %.${pr}g $res]
  } else {
    # puts stderr "proc ev: $res"
    return $args
  }
}

## evaluate expression. if expression has errors or does not evaluate return 0
proc ev0 {args} {
  global ev_precision
  if {[info exists ev_precision]} {set pr $ev_precision} else {set pr 4}
  set i [join $args]
  if {![catch {uplevel #0 expr $i} res]} {
    return [format %.${pr}g $res]
  } else {
    # puts stderr "proc ev0: $res"
    return 0
  }
}

# create the 'lassign' command if tcl does not have it (pre 8.5)
if {[info commands lassign] eq {}} {
  proc lassign { list args } {
    set n 0
    foreach i $args {
      upvar $i v
      set v [lindex $list $n]
      incr n
    }
    return [lrange $list $n end]
  }
}

# return "$n * $indent" spaces
proc spaces {n {indent 4} {char { }}} {
  set n [expr {$n * $indent}]
  # return [format %${n}s {}]
  return [string repeat $char $n]
}

# complex number operators
# a + b
proc cadd {a b} {
  lassign $a ra ia
  lassign $b rb ib
  set c [list [expr {$ra + $rb}] [expr {$ia + $ib}]]
  return $c
}

# a - b
proc csub {a b} {
  lassign $a ra ia
  lassign $b rb ib
  set c [list [expr {$ra - $rb}] [expr {$ia - $ib}]]
  return $c
}

# a * b
proc cmul {a b} {
  lassign $a ra ia
  lassign $b rb ib
  set c [list [expr {$ra * $rb - $ia * $ib}] [expr {$ra * $ib + $rb * $ia}]]
  return $c
}

# a / b
proc cdiv {a b} {
  lassign $a ra ia
  lassign $b rb ib
  set ra [expr {double($ra)}]
  set ia [expr {double($ia)}]
  set rb [expr {double($rb)}]
  set ib [expr {double($ib)}]
  set m [expr {$rb * $rb + $ib * $ib}]
  set c [list [expr {($ra * $rb + $ia * $ib) / $m}] [expr {($rb * $ia - $ra * $ib) / $m}]]
  return $c
}

# 1/b
proc cinv {b} {

  lassign $b rb ib
  set rb [expr {double($rb)}]
  set ib [expr {double($ib)}]
  set m [expr {$rb * $rb + $ib * $ib}]
  set c [list [expr {$rb / $m}] [expr {-$ib / $m}]]
  return $c
}

# return real component
proc creal {a} {
  lassign $a ra ia
  return $ra
}

# return imaginary component
proc cimag {a} {
  lassign $a ra ia
  return $ia
}

# return resulting impedance of parallel connected impedances a and b
proc cparallel {a b} {
  return [cdiv [cmul $a $b] [cadd $a $b]]
}

# wraps provided table formatted text into a nice looking bordered table
# sep is the list of characters used as separators, default are { }, {,}, {\t}
# if you want to tabulate data with spaces use only {,} as separator or any other character.
proc tabulate {text {sep ",\t "}} {
  # define table characters
  set top  {┌  ─  ┬ ┐}
  set row  {│ { } │ │}
  set mid  {├  ─  ┼ ┤}
  set head {╞  ═  ╪ ╡}
  set bot  {└  ─  ┴ ┘}

  # used_sep (first character of sep) will be used as separator after text cleanup
  set used_sep [string range $sep 0 0]
  set sep "\[$sep\]"
  set maxcols 0 ;# max number of columns
  set nlines 0  ;# number of data lines
  set chopped_text {}
  set found_data 0
  foreach line [split $text \n] {

    # skip completely empty lines
    if {[regexp {^$} $line]} { continue}
    # skip leading lines with no data (only separators)
    if {!$found_data && [regexp ^${sep}*\$ $line]} {
      continue
    }
    set found_data 1

    # change separators to { }
    regsub -all $sep $line $used_sep line

    # transform resulting line into a proper list
    set line [split $line $used_sep]
    incr nlines
    set ncols 0
    # calculate max field width and number of columns
    foreach field $line {
      incr ncols
      if {![info exists maxlen($ncols)]} {set maxlen($ncols) 0}
      if {$ncols > $maxcols} {set maxcols $ncols}
      set len [string length $field]
      if {$len > $maxlen($ncols)} { set maxlen($ncols) $len}
    }
    if { $chopped_text ne {}} {append chopped_text \n}
    append chopped_text $line
  }

  set table {}
  set l 0
  foreach line [split $chopped_text \n] {
    incr l

    # top table border
    set rowsep {}
    if {$l == 1} {
      append rowsep [lindex $top 0]
      set c 0
      for {set i 0} {$i < $maxcols} { incr i} {
        set field [lindex $line $i]
        incr c
        append rowsep [string repeat [lindex $top 1] $maxlen($c)]
        if { $c == $ncols} {
          append rowsep [lindex $top 3]
        } else {
          append rowsep [lindex $top 2]
        }
      }
      append table $rowsep \n
    } else {
      append table \n
    }
    set rowsep {}
    append table [lindex $row 0]
    if { $l == $nlines} {
      append rowsep [lindex $bot 0]
    } elseif {$l == 1} {
      append rowsep [lindex $head 0]
    } else {
      append rowsep [lindex $mid 0]
    }
    set c 0
    for {set i 0} {$i < $maxcols} { incr i} {
      set field [lindex $line $i]
      incr c
      set pad [expr {$maxlen($c) - [string length $field]}]
      append table $field [string repeat { } $pad]
      if { $l == $nlines} {
        append rowsep [string repeat [lindex $bot 1] $maxlen($c)]
      } elseif {$l == 1}  {
        append rowsep [string repeat [lindex $head 1] $maxlen($c)]
      } else {
        append rowsep [string repeat [lindex $mid 1] $maxlen($c)]
      }
      if { $c == $ncols} {
        append table [lindex $row 3]
        if { $l == $nlines} {
          append rowsep [lindex $bot 3]
        } elseif {$l == 1} {
          append rowsep [lindex $head 3]
        } else {
          append rowsep [lindex $mid 3]
        }
      } else {
        append table [lindex $row 2]
        if { $l == $nlines} {
          append rowsep [lindex $bot 2]
        } elseif { $l == 1}  {
          append rowsep [lindex $head 2]
        } else {
          append rowsep [lindex $mid 2]
        }
      }
    }
    append table \n $rowsep
  }
  return $table
}

# get pin ordering from included subcircuit
# return empty string if not found.
proc has_included_subcircuit {symname spice_sym_def no_of_pins} {
  global has_x
  set include_found 0
  regsub -all {\n\+} $spice_sym_def { } spice_sym_def
  # puts "has_included_subcircuit: spice_sym_def=$spice_sym_def"
  set pinlist {}
  # .include? get the file ...
  if {[regexp -nocase {^[ \t\n]*\.include +} $spice_sym_def]} {
    regsub -nocase {^[ \t\n]*\.include +} $spice_sym_def {} filename
    regsub -all {^"|"$} $filename {} filename ;# remove double quotes at beginning and end
    regsub -all {^\n*|\n*$} $filename {} filename ;# remove newlines at beginning and end
    set filename [abs_sym_path $filename]
    # puts "filename=$filename"
    set res [catch {open $filename r} fd]
    if { $res } {
      puts "has_included_subcircuit: error opening file $filename: $fd"
      if { [info exists has_x] } {alert_ "has_included_subcircuit: error opening file $filename: $fd"}
      return $pinlist
    }
    set spice_sym_def [read -nonewline $fd]
    close $fd
    regsub -all {\n\+} $spice_sym_def { } spice_sym_def
  }
  # ... or use the attribute value as *the* subcircuit

  # split lines
  set spice_sym_def [split $spice_sym_def \n]
  foreach line $spice_sym_def {
    # get 1st line with a matching .subckt. This is our subcircuit definition, get the pin order
    if {[string tolower [lindex $line 0]] eq {.subckt}  &&
        [string tolower [lindex $line 1]] eq $symname} {
     set include_found 1
     regsub -all { *= *} $line {=} line
     # pin list ends where parameter assignment begins (param=value)
     set last [lsearch -regexp [string tolower $line] {=|param:}]
     if {$last == -1} {
       set last [expr {[llength $line] -1}]
     } else {
       set last [expr {$last -1}]
     }
     # if spice_sym_def has more ports than symbol (no_of_pins) assume these are extra ports
     # assigned via attributes ('extra' symbol attribute, usually power rails), these extra
     # ports are not returned as @pinlist and correspondence with symbol format string
     # is not checked. user must ensure these additional ports are in the right order.
     if { $last >= $no_of_pins + 1 } {
       set last [expr {$no_of_pins + 1}]
     }
     set pinlist [lrange $line 2 $last]
     break
    }
  }
  # return pinlist as found in the .subckt line or empty string if not found
  if {!$include_found} {
    puts "has_included_subcircuit: $symname: no matching .subckt found in spice_sym_def. Ignore"
    if { [info exists has_x] } {
      alert_ "has_included_subcircuit: $symname: no matching .subckt found in spice_sym_def. Ignore"
    }
  }
  # puts $pinlist
  return [string tolower [join $pinlist]]
}

# should not be called directly by user
# does netlist post processing, called from global_(spice|vhdl|verilog)_netlist()
proc netlist {source_file show netlist_file} {
 global XSCHEM_SHAREDIR flat_netlist netlist_dir simulate_bg
 global verilog_2001 debug_var OS has_x verilog_bitblast

 regsub {/$} $netlist_dir {} netlist_dir
 # if waves are loaded turn Waves button to orange to indicate old waves are shown
 set win_path [xschem get current_win_path]
 set top_path [xschem get top_path]
 set waves_var tctx::${win_path}_waves
 if {[info exists has_x] && $waves_var ne $simulate_bg} {
   set $waves_var orange
   catch {$top_path.menubar entryconfigure Waves -background orange}
 }

 set netlist_type [xschem get netlist_type]
 if {$debug_var <= -1} { puts "netlist: source_file=$source_file, netlist_type=$netlist_type" }
 set dest $netlist_dir/$netlist_file
 if {$netlist_type eq {spice}} {
   if { [sim_is_xyce] } {
     set xyce  {-xyce}
   } else {
     set xyce  {}
   }
   set cmd  ${XSCHEM_SHAREDIR}/spice.awk
   set brk ${XSCHEM_SHAREDIR}/break.awk
   set flatten ${XSCHEM_SHAREDIR}/flatten.awk
   if {$flat_netlist==0} {
     eval exec {awk -f $cmd -- $xyce $source_file | awk -f $brk > $dest}
   } else {
     eval exec {awk -f $cmd -- $xyce $source_file | awk -f $flatten | awk -f $brk > $dest}
   }
   if ![string compare $show "show"] {
      textwindow $dest
   }
 }

 if {$netlist_type eq {spectre}} {
   set cmd  ${XSCHEM_SHAREDIR}/spectre.awk
     eval exec {awk -f $cmd -- $source_file > $dest}
   if ![string compare $show "show"] {
      textwindow $dest
   }
 }

 if {$netlist_type eq {vhdl}} {
   set cmd $XSCHEM_SHAREDIR/vhdl.awk
   eval exec {awk -f $cmd $source_file > $dest}
   if ![string compare $show "show"] {
     textwindow $dest
   }
 }
 if {$netlist_type eq {tedax}} {
    set cmd1  $XSCHEM_SHAREDIR/tedax.awk
    set cmd2 $XSCHEM_SHAREDIR/flatten_tedax.awk
    if {[catch {eval exec {awk -f $cmd1 $source_file | awk -f $cmd2 > $dest} } err] } {
     puts stderr "tEDAx errors: $err"
   }
   if ![string compare $show "show"] {
     textwindow $dest
   }
 }
 if {$netlist_type eq {verilog}} {
   set cmd  ${XSCHEM_SHAREDIR}/verilog.awk
   if { $verilog_bitblast == 1 } {
     eval exec {awk -f $cmd -- -bitblast $source_file > $dest}
   } else {
     eval exec {awk -f $cmd $source_file > $dest}
   }
   if { $verilog_2001==1 } {
     set cmd ${XSCHEM_SHAREDIR}/convert_to_verilog2001.awk
     set interm ${dest}[pid]
     eval exec {awk -f $cmd $dest > $interm}
     file rename -force $interm $dest
   }
   if ![string compare $show "show"] {
     textwindow "$dest"
   }
 }
 return {}
}

# 20161121
proc convert_to_pdf {filename dest} {
  global to_pdf OS execute
  if { [regexp -nocase {\.pdf$} $dest] } {
    set pdffile [file rootname $filename].pdf
    # puts "--->  cmd=$to_pdf  filename=$filename  pdffile=$pdffile  dest=$dest"
    if {$OS == "Windows"} {
      set cmd "exec $to_pdf \$pdffile \$filename"
      if { ![catch {eval $cmd} msg] } {
        file rename -force $pdffile $dest
        # ps2pdf succeeded, so remove original .ps file
        if { ![xschem get debug_var] } {
          file delete $filename
        }
      } else {
        puts stderr "problems converting postscript to pdf: $msg"
      }
    } else { ;# OS == unix
      eval execute_wait 0 $to_pdf [list $filename $pdffile]
      if {$execute(status,last) == 0} {
        file rename -force $pdffile $dest
        if { ![xschem get debug_var] } {
          file delete $filename
        }
      }
    }
  } else {
    # puts "rename  $filename $dest"
    file rename -force $filename $dest
  }
}

# 20161121
proc convert_to_png {filename dest} {
  global to_png debug_var OS
  # puts "---> $to_png $filename $dest"
  set cmd "exec $to_png \$filename png:\$dest"
    if {$OS == "Windows"} {
      set cmd "exec $to_png \$dest \$filename"
    }
  if { ![catch {eval $cmd} msg] } {
    # conversion succeeded, so remove original .xpm file
    if { ![xschem get debug_var] } {
      file delete $filename
    }
  } else {
    puts stderr "problems converting xpm to png: $msg"
  }
}

# always specify Shift- modifier for capital letters
# see tk 'man keysyms' for key names
# example format for s, d: Control-Alt-Key-asterisk
#                          Control-Shift-Key-A
#                          Alt-Key-c
#                          ButtonPress-4
#
proc key_binding {  s  d { win_path {.drw} } } {
  regsub {.*-} $d {} key


  switch $key {
     Insert { set keysym 65379 }
     Escape { set keysym 65307 }
     Return { set keysym 65293 }
     Delete { set keysym 65535 }
     F1 { set keysym  65470 }
     F2 { set keysym  65471 }
     F3 { set keysym  65472 }
     F4 { set keysym  65473 }
     F5 { set keysym  65474 }
     F6 { set keysym  65475 }
     F7 { set keysym  65476 }
     F8 { set keysym  65477 }
     F9 { set keysym  65478 }
     F10 { set keysym  65479 }
     F11 { set keysym  65480 }
     F12 { set keysym  65481 }
     BackSpace { set keysym 65288 }
     default { set keysym [scan "$key" %c] }
  }
  set state 0
  # not found any portable way to get modifier constants ...
  if { [regexp {(Mod1|Alt)-} $d] } { set state [expr {$state +8}] }
  if { [regexp Control- $d] } { set state [expr {$state +4}] }
  if { [regexp Shift- $d] } { set state [expr {$state +1}] }
  if { [regexp ButtonPress-1 $d] } { set state [expr {$state +0x100}] }
  if { [regexp ButtonPress-2 $d] } { set state [expr {$state +0x200}] }
  if { [regexp ButtonPress-3 $d] } { set state [expr {$state +0x400}] }
  if { [regexp ButtonPress-4 $d] } { set state [expr {$state +0x800}] }
  if { [regexp ButtonPress-5 $d] } { set state [expr {$state +0x1000}] }
  # puts "$state $key <${s}>"
  if {[regexp ButtonPress- $d]} {
    # puts "bind $win_path <${s}> xschem callback %W %T %x %y 0 $key 0 $state"
    bind $win_path "<${s}>" "xschem callback %W %T %x %y 0 $key 0 $state"
  } else {
    if {![string compare $d {} ] } {
      # puts  "bind $win_path  <${s}> {}"
      bind $win_path "<${s}>" {}
    } else {
      # puts  "bind $win_path  <${s}> xschem callback %W %T %x %y $keysym 0 0 $state"
      bind $win_path  "<${s}>" "xschem callback %W %T %x %y $keysym 0 0 $state"
    }
  }

}

proc edit_file {filename} {

 global editor
 # since $editor can be an executable with options (gvim -f) I *need* to use eval
 eval execute 0  $editor  $filename
 return {}
}

# ============================================================
#      SIMULATION CONTROL
# ============================================================

# ============================================================
#      SIMCONF
# ============================================================

## $N : netlist file full path (/home/schippes/simulations/opamp.spice)
## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp)
## $s : schematic name (opamp)
## $d : netlist directory
##
## Other global vars:
## netlist_dir
## terminal
## netlist_type can be obtained with [xschem get netlist_type]
proc save_sim_defaults {f} {
  global sim netlist_dir terminal

  regsub {/$} $netlist_dir {} netlist_dir
  set a [catch {open $f w} fd]
  if { $a } {
    puts "save_sim_defaults: error opening file $f: $fd"
    return
  }
  puts $fd {# set the list of tools known to xschem}
  puts $fd {# Note that no spaces are allowed around commas in array keys}
  puts $fd "set sim(tool_list) {$sim(tool_list)}"
  puts $fd {}
  foreach tool $sim(tool_list) {
    puts $fd "#Specify the number of configured $tool tools."
    puts $fd "set sim($tool,n) $sim($tool,n) ;# number of configured $tool tools"
    puts $fd "# Specify the default $tool tool to use (first=0)"
    puts $fd "set sim($tool,default) $sim($tool,default) ;# default $tool tool to launch"
    puts $fd {}
    for {set i 0} {$i < $sim($tool,n)} { incr i} {
      puts $fd "# specify tool command (cmd), name (name), if tool must run\
                in foreground and if exit status must be reported"
      puts $fd "set sim($tool,$i,cmd) {$sim($tool,$i,cmd)}"
      puts $fd "set sim($tool,$i,name) {$sim($tool,$i,name)}"
      puts $fd "set sim($tool,$i,fg) $sim($tool,$i,fg)"
      puts $fd "set sim($tool,$i,st) $sim($tool,$i,st)"
      puts $fd {}
    }
    puts $fd {}
  }
  close $fd
}

proc load_recent_file {} {
  global USER_CONF_DIR has_x
  # recent files
  set tctx::recentfile {}
  if { [file exists $USER_CONF_DIR/recent_files] } {
    if {[catch { source $USER_CONF_DIR/recent_files } err] } {
      puts "Problems opening recent_files: $err"
      if {[info exists has_x]} {
        tk_messageBox -message  "Problems opening recent_files: $err" \
            -icon warning -parent . -type ok
      }
    }
    foreach i [info vars c_toolbar::c_t_*] {
      if {[set ${i}(w)] != $c_toolbar::c_t(w) ||
          [set ${i}(n)] != $c_toolbar::c_t(n)} {
         array unset $i
      }
    }
    set hash $c_toolbar::c_t(hash)
    if { [info exists c_toolbar::c_t_$hash]} {
        array set c_toolbar::c_t [array get c_toolbar::c_t_$hash]
    }
  }
}

proc update_recent_file {f {topwin {} } } {
  global has_x
  # puts "update recent file, f=$f, topwin=$topwin"
  set old $tctx::recentfile
  set tctx::recentfile {}
  lappend tctx::recentfile $f
  foreach i $old {
    if {[abs_sym_path $i] ne [abs_sym_path $f]} {
      lappend tctx::recentfile [abs_sym_path $i]
    }
  }
  # tcl8.4 errors if using lreplace past the last element
  if { [llength $tctx::recentfile] > 10  } {
    set tctx::recentfile [lreplace $tctx::recentfile 10 end]
  }
  write_recent_file
  if { [info exists has_x] } {setup_recent_menu $topwin}
}

proc write_recent_file {} {
  global USER_CONF_DIR

  # puts "write recent file tctx::recentfile=$tctx::recentfile"
  set a [catch {open $USER_CONF_DIR/recent_files w} fd]
  if { $a } {
    puts "write_recent_file: error opening file $f: $fd"
    return
  }
  puts $fd "set tctx::recentfile {$tctx::recentfile}"

  if {[info exists c_toolbar::c_t]} {
    set hash $c_toolbar::c_t(hash)
    set c_toolbar::c_t(time) [clock seconds]
    array set c_toolbar::c_t_$hash [array get c_toolbar::c_t]
    foreach i [info vars c_toolbar::c_t_*] {
      ## old (~3 months) recent components will expire
      if { [info exists [subst $i](time)]} {
        if { [clock seconds] -  [set [subst $i](time)] < 7500000} {
          puts $fd "array set $i {[array get $i]}"
        }
        # puts "Age: [expr {[clock seconds] -  [set [subst $i](time)]}]"
      }
    }
  }
  close $fd
}

proc setup_recent_menu { { topwin {} } } {
  $topwin.menubar.file.recent delete 0 9
  set i 0
  if { [info exists tctx::recentfile] } {
    foreach i $tctx::recentfile {
      $topwin.menubar.file.recent add command \
        -command "xschem load -gui {$i}" \
        -label [file tail $i]
    }
  }
}



## ngspice:: raw file access functions
namespace eval ngspice {
  # Create a variable inside the namespace
  variable ngspice_data
  variable op_point_read
}

proc ngspice::get_current {n} {
  set raw_level [xschem get raw_level]
  set path [string range [xschem get sch_path] 1 end]
  # skip hierarchy components above the level where raw file has been loaded.
  # node path names to look up in raw file begin from there.
  if { [xschem get currsch] < $raw_level} { return {}}
  set skip 0
  while { $skip < $raw_level } {
    regsub {^[^.]*\.} $path {} path
    incr skip
  }
  set n [string tolower $n]
  set prefix $n
  # if xm1.rd is given get prefix (r) by removing path components (xm1.)
  regsub {.*\.} $prefix {} prefix
  set prefix [string range $prefix 0 0]
  # puts "ngspice::get_current: path=$path n=$n prefix=$prefix"
  set n $path$n
  set currname i
  if { ![sim_is_xyce] } { ;# ngspice
    if {$path ne {} } {
      set n $prefix.$n
    }
    if { ![regexp $prefix {[ve]}] } {
      set n @$n
    }
    set n i($n)
  } else { ;# xyce
    if { [regexp {\[i[bcedgsb]\]$} $n] } {
      regexp {\[(i[bcesdgb])\]$} $n curr1 currname
      if { $prefix == {d} } {set currname i}
      regsub {\[(i[bcesdgb])\]$} $n {} n
    }
    regsub {\[i\]} $n {} n
    set n $currname\($n\)
  }
  # puts "ngspice::get_current --> $n"
  set err [catch {set ngspice::ngspice_data($n)} res]
  if { $err } {
    set res {?}
  }
  # puts "$n --> $res"
  return $res
}

proc ngspice::get_diff_voltage {n m} {
  set raw_level [xschem get raw_level]
  set path [string range [xschem get sch_path] 1 end]
  if { [xschem get currsch] < $raw_level} { return {}}
  # skip hierarchy components above the level where raw file has been loaded.
  # node path names to look up in raw file begin from there.
  set skip 0
  while { $skip < $raw_level } {
    regsub {^[^.]*\.} $path {} path
    incr skip
  }
  set n [string tolower $n]
  set m [string tolower $m]
  set nn $path$n
  set mm $path$m
  set errn [catch {set ngspice::ngspice_data($nn)} resn]
  if {$errn} {
    set nn v(${path}${n})
    set errn [catch {set ngspice::ngspice_data($nn)} resn]
  }
  set errm [catch {set ngspice::ngspice_data($mm)} resm]
  if {$errm} {
    set mm v(${path}${m})
    set errm [catch {set ngspice::ngspice_data($mm)} resm]
  }
  if { $errn  || $errm} {
    set res {?}
  }
  return $res
}


proc ngspice::get_voltage {n} {
  set raw_level [xschem get raw_level]
  set path [string range [xschem get sch_path] 1 end]
  if { [xschem get currsch] < $raw_level} { return {}}
  # skip hierarchy components above the level where raw file has been loaded.
  # node path names to look up in raw file begin from there.
  set skip 0
  while { $skip < $raw_level } {
    regsub {^[^.]*\.} $path {} path
    incr skip
  }
  set n [string tolower $n]
  # puts "ngspice::get_voltage: path=$path n=$n"
  set node $path$n
  # puts "ngspice::get_voltage: trying $node"
  set err [catch {set ngspice::ngspice_data($node)} res]
  if {$err} {
    set node v(${path}${n})
    # puts "ngspice::get_voltage: trying $node"
    set err [catch {set ngspice::ngspice_data($node)} res]
  }
  if { $err } {
    set res {?}
  }
  return $res
}

proc update_schematic_header {} {
  set tctx::retval [xschem get header_text]
  text_line {Header/License text:} 0 header
  if { $tctx::rcode ne {}} {
    xschem set header_text $tctx::retval
  }
}

proc ngspice::get_node {n} {
  set raw_level [xschem get raw_level]
  set path [string range [xschem get sch_path] 1 end]
  if { [xschem get currsch] < $raw_level} { return {}}
  # skip hierarchy components above the level where raw file has been loaded.
  # node path names to look up in raw file begin from there.
  set skip 0
  while { $skip < $raw_level } {
    regsub {^[^.]*\.} $path {} path
    incr skip
  }
  set n [string tolower $n]
  # n may contain $path, so substitute its value
  set n [ subst -nocommand $n ]
  set err [catch {set ngspice::ngspice_data($n)} res]
  if { $err } {
    set res {?}
  }
  return $res
}

## end ngspice:: functions

# test if currently set simulator is ngspice
proc sim_is_ngspice {} {
  global sim netlist_type

  set_sim_defaults

  if {$netlist_type eq {spice} && [info exists sim(spice,default)] } {
    set idx $sim(spice,default)
    if { [regexp {ngspice} $sim(spice,$idx,cmd)] } {
      return 1
    }
  }
  return 0
}

# test if currently set simulator is Xyce
proc sim_is_Xyce {} {
  return [sim_is_xyce]
}

proc sim_is_xyce {} {
  global sim netlist_type

  set_sim_defaults

  if {$netlist_type eq {spice} && [info exists sim(spice,default)] } {
    set idx $sim(spice,default)
    if { [regexp {[xX]yce} $sim(spice,$idx,cmd)] } {
      return 1
    }
  }
  return 0
}

proc sim_is_vacask {} {
  global sim netlist_type

  set_sim_defaults

  if {$netlist_type eq {spectre} && [info exists sim(spectre,default)] } {
    set idx $sim(spectre,default)
    if { [regexp {vacask} $sim(spectre,$idx,cmd)] } {
      return 1
    }
  }
  return 0
}

# tests if file f exists. One level of global scope 'subst' is done on f
# to expand global variables / commands catching errors.
# example:
#    % set b {$env(HOME)/.bashrc}
#    $env(HOME)/.bashrc
#    % file_exists $b
#    1
#    %
#    %
#    % set b {$env(HOMExx)/.bashrc}
#    $env(HOMExx)/.bashrc
#    % file_exists $b
#    0
#    %
proc file_exists {f} {
  set ret 0
  set r [catch "uplevel #0 {subst $f}" res]
  if {$r == 0} {
    if {[file exists $res]} { set ret 1}
  }
  return $ret
}

# wrapper to "xschem list_tokens" comand to handle non list results
# usually as a result of malformed input strings
proc list_tokens {s} {
  set res [xschem list_tokens $s 0]


  if { [info tclversion] > 8.4 } {
    if { [string is list $res]} {
      return $res
    } else {
      return [split $res]
    }
  } else {
    if {![catch {llength $res}]} {
      return $res
    } else {
      return [split $res]
    }
  }
}

# generates a proper list, trimming multiple separators
proc tolist {s} {
  set s [string trim $s]
  regsub -all {[\t\n ]+} $s { } s


  if { [info tclversion] > 8.4 } {
    if { [string is list $s]} {
      return $s
    } else {
      return [split $s]
    }
  } else {
    if {![catch {llength $s}]} {
      return $s
    } else {
      return [split $s]
    }
  }
}

# where:
#   begin: remove $needle if it is at the beginning of $haystack
#   end  : remove $needle if it is at the end of $haystack
#   any  : remove first found $needle anywhere inside $haystack
proc substring_remove {needle haystack {where {begin}}} {
  if {$where eq {begin}} {
    set needle_length [string length $needle]
    if {[string compare -length $needle_length $needle $haystack] == 0} {
     return [string range $haystack $needle_length end]
    }
    return $haystack
  } elseif {$where eq {end}} {
    set needle_length [string length $needle]
    set pos [expr {[string length $haystack] -$needle_length}]
    set haystack_end [string range $haystack $pos end]
    if {[string compare $needle $haystack_end] == 0} {
      incr pos -1
      return [string range $haystack 0 $pos]
    }
    return $haystack
  } elseif {$where eq {any}} {
    set needle_length [string length $needle]
    set pos [string first $needle $haystack]
    if {$pos >= 0} {
      set before [expr {$pos - 1}]
      set after [expr {$pos + $needle_length}]
      return [string range $haystack 0 $before][string range $haystack $after end]
    }
    return $haystack
  }
}


# Initialize the tcl sim array variable (if not already set)
# setting up simulator / wave viewer commands
proc set_sim_defaults {{reset {}}} {
  global sim terminal USER_CONF_DIR has_x bespice_listen_port env OS
  if {$reset eq {reset} } { file delete ${USER_CONF_DIR}/simrc }
  if { $reset eq {} } {
    set failure 0
    if { [info exists has_x] && [winfo exists .sim] } {
      foreach tool $sim(tool_list) {
        for {set i 0} {$i < $sim($tool,n)} { incr i} {
          set sim($tool,$i,cmd) [.sim.topf.f.scrl.center.$tool.r.$i.cmd get 1.0 {end - 1 chars}]
        }
      }
    }
    if { ![info exists sim] } {
      if { [file exists ${USER_CONF_DIR}/simrc] } {
        # get conf from simrc
        if { [catch {source ${USER_CONF_DIR}/simrc} err]} {
          puts "Problems opening simrc file: $err"
          if {[info exists has_x]} {
            tk_messageBox -message  "Problems opening simrc file: $err" -icon warning \
               -parent [xschem get topwindow] -type ok
          }
          set failure 1
        }
      }
    }
  }
  if {( $reset eq {reset} ) || ![info exists sim] || $failure} {
    if {[info exists sim]} {unset sim}
    # no simrc, set a reasonable default
    set sim(tool_list) {spice spicewave spectre verilog verilogwave vhdl vhdlwave}
    if {$OS == "Windows"} {
      set_ne sim(spice,0,cmd) {ngspice -i "$N" -a}
    } else {
      set_ne sim(spice,0,cmd) {$terminal -e {ngspice -i "$N" -a || sh}}
    }
    # can not use set_ne as variables bound to entry widgets always exist if widget exists
    set sim(spice,0,name) {Ngspice interactive}
    set_ne sim(spice,0,fg) 0
    set_ne sim(spice,0,st) 0


    set_ne sim(spice,1,cmd) {ngspice "$N" -a}
    set sim(spice,1,name) {Ngspice Control mode}
    set_ne sim(spice,1,fg) 0
    set_ne sim(spice,1,st) 1


    set_ne sim(spice,2,cmd) {ngspice -b -r "$n.raw" "$N"}
    set sim(spice,2,name) {Ngspice batch}
    set_ne sim(spice,2,fg) 0
    set_ne sim(spice,2,st) 1

    set_ne sim(spice,3,cmd) {Xyce "$N"}
    set sim(spice,3,name) {Xyce batch}
    set_ne sim(spice,3,fg) 0
    set_ne sim(spice,3,st) 1

    set_ne sim(spice,4,cmd) {mpirun /path/to/parallel/Xyce "$N"}
    set sim(spice,4,name) {Xyce parallel batch}
    set_ne sim(spice,4,fg) 0
    set_ne sim(spice,4,st) 1

    # number of configured spice simulators, and default one
    set_ne sim(spice,n) 5
    set_ne sim(spice,default) 0

    ### spice wave view
    set_ne sim(spicewave,0,cmd) {gaw "$n.raw" }
    set sim(spicewave,0,name) {Gaw viewer}
    set_ne sim(spicewave,0,fg) 0
    set_ne sim(spicewave,0,st) 0

    set_ne sim(spicewave,1,cmd) {$terminal -e ngspice}
    set sim(spicewave,1,name) {Ngspice Viewer}
    set_ne sim(spicewave,1,fg) 0
    set_ne sim(spicewave,1,st) 0

    set_ne sim(spicewave,2,cmd) {rawtovcd -v 1.5 "$n.raw" > "$n.vcd" && gtkwave "$n.vcd" "$n.sav" 2>/dev/null}
    set sim(spicewave,2,name) {Rawtovcd}
    set_ne sim(spicewave,2,fg) 0
    set_ne sim(spicewave,2,st) 0

    # A server communicating with bespice wave was set up in the function setup_tcp_bespice().
    # This server is listening on port $bespice_listen_port.
    set_ne sim(spicewave,3,cmd) {$env(HOME)/analog_flavor_eval/bin/bspwave --socket localhost $bespice_listen_port}
    set sim(spicewave,3,name) {Bespice wave}
    set_ne sim(spicewave,3,fg) 0
    set_ne sim(spicewave,3,st) 0
    # number of configured spice wave viewers, and default one
    set_ne sim(spicewave,n) 4
    set_ne sim(spicewave,default) 0

    ### spectre (vacask simulator)
    set_ne sim(spectre,0,cmd) {vacask "$N"}
    set sim(spectre,0,name) {Vacask}
    set_ne sim(spectre,0,fg) 0
    set_ne sim(spectre,0,st) 1

    # number of configured spectre simulators, and default one
    set_ne sim(spectre,n) 1
    set_ne sim(spectre,default) 0

    ### icarus verilog
    set_ne sim(verilog,0,cmd) {sh -c "iverilog -o .verilog_object -g2012 '$N' && vvp .verilog_object"}
    set sim(verilog,0,name) {Icarus verilog}
    set_ne sim(verilog,0,fg) 0
    set_ne sim(verilog,0,st) 1

    ### verilator
    set_ne sim(verilog,1,cmd) {sh -c "verilator -Wno-fatal --binary --timing --cc --Mdir obj_$s -o $s '$N' --trace \
                                     && ./obj_$s/$s"}
    set sim(verilog,1,name) {Verilator}
    set_ne sim(verilog,1,fg) 0
    set_ne sim(verilog,1,st) 1

    # number of configured verilog simulators, and default one
    set_ne sim(verilog,n) 2
    set_ne sim(verilog,default) 0

    ### verilog wave view
    set_ne sim(verilogwave,0,cmd) {gtkwave dumpfile.vcd "$N.sav" 2>/dev/null}
    set sim(verilogwave,0,name) {Gtkwave}
    set_ne sim(verilogwave,0,fg) 0
    set_ne sim(verilogwave,0,st) 0
    # number of configured verilog wave viewers, and default one
    set_ne sim(verilogwave,n) 1
    set_ne sim(verilogwave,default) 0

    ### vhdl
    set_ne sim(vhdl,0,cmd) {ghdl -c --ieee=synopsys -fexplicit "$N" -r "$s" "--wave=$n.ghw"}
    set sim(vhdl,0,name) {Ghdl}
    set_ne sim(vhdl,0,fg) 0
    set_ne sim(vhdl,0,st) 1
    # number of configured vhdl simulators, and default one
    set_ne sim(vhdl,n) 1
    set_ne sim(vhdl,default) 0

    ### vhdl wave view
    set_ne sim(vhdlwave,0,cmd) {gtkwave "$n.ghw" "$N.sav" 2>/dev/null}
    set sim(vhdlwave,0,name) {Gtkwave}
    set_ne sim(vhdlwave,0,fg) 0
    set_ne sim(vhdlwave,0,st) 0
    # number of configured vhdl wave viewers, and default one
    set_ne sim(vhdlwave,n) 1
    set_ne sim(vhdlwave,default) 0
  }
}

proc simconf_reset {} {
  global sim

  set answer [tk_messageBox -message  "Warning: delete simulation configuration file and reset to default?" \
            -icon warning -parent .sim  -type okcancel]
  if { $answer eq {ok}} {
    set_sim_defaults reset
    foreach tool $sim(tool_list) {
      for {set i 0} { $i < $sim($tool,n)} {incr i} {
        .sim.topf.f.scrl.center.$tool.r.$i.cmd delete 1.0 end
        .sim.topf.f.scrl.center.$tool.r.$i.cmd insert 1.0 $sim($tool,$i,cmd)
      }
    }
  }
}

proc simconf_saveconf {scrollframe} {
  global sim USER_CONF_DIR
  foreach tool $sim(tool_list) {
    for {set i 0} { $i < $sim($tool,n)} {incr i} {
      set sim($tool,$i,cmd) [${scrollframe}.center.$tool.r.$i.cmd get 1.0 {end - 1 chars}]
    }
  }
  # destroy .sim
  # xschem set semaphore [expr {[xschem get semaphore] -1}]
  save_sim_defaults ${USER_CONF_DIR}/simrc
  # puts "saving simrc"
}

proc simconf {} {
  global sim USER_CONF_DIR simconf_default_geometry

  if {[winfo exists .sim]} {
    destroy .sim
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  set_sim_defaults
  toplevel .sim -class Dialog
  wm title .sim {Simulation Configuration}
  wm geometry .sim 790x370
  # wm transient .sim [xschem get topwindow]
  frame .sim.topf
  set scrollframe [sframe .sim.topf]
  frame ${scrollframe}.center
  frame .sim.bottom
  pack ${scrollframe}.center -fill both -expand yes
  set bg(0) {#dddddd}
  set bg(1) {#aaaaaa}
  set toggle 0
  foreach tool $sim(tool_list) {
    frame ${scrollframe}.center.$tool
    label ${scrollframe}.center.$tool.l -width 12 -text $tool  -background $bg($toggle) -fg black
    frame ${scrollframe}.center.$tool.r
    pack ${scrollframe}.center.$tool -fill both -expand yes
    pack ${scrollframe}.center.$tool.l -fill y -side left
    pack ${scrollframe}.center.$tool.r -fill both -expand yes
    for {set i 0} { $i < $sim($tool,n)} {incr i} {
      frame ${scrollframe}.center.$tool.r.$i
      pack ${scrollframe}.center.$tool.r.$i -fill x -expand yes
      entry ${scrollframe}.center.$tool.r.$i.lab -textvariable sim($tool,$i,name) -width 18 \
         -background $bg($toggle) -fg black
      entry_replace_selection  ${scrollframe}.center.$tool.r.$i.lab
      radiobutton ${scrollframe}.center.$tool.r.$i.radio -background $bg($toggle) -fg black \
         -selectcolor white -variable sim($tool,default) -value $i
      text ${scrollframe}.center.$tool.r.$i.cmd -undo 1 -width 20 -height 3 -wrap none -background $bg($toggle) -fg black
      ${scrollframe}.center.$tool.r.$i.cmd insert 1.0 $sim($tool,$i,cmd)
      checkbutton ${scrollframe}.center.$tool.r.$i.fg -text Fg -variable sim($tool,$i,fg) \
        -selectcolor white -background $bg($toggle) -fg black
      checkbutton ${scrollframe}.center.$tool.r.$i.st -text Status -variable sim($tool,$i,st) \
        -selectcolor white -background $bg($toggle) -fg black
      pack ${scrollframe}.center.$tool.r.$i.lab -side left -fill y
      pack ${scrollframe}.center.$tool.r.$i.radio -side left -fill y
      pack ${scrollframe}.center.$tool.r.$i.cmd -side left -fill x -expand yes
      pack ${scrollframe}.center.$tool.r.$i.fg -side left -fill y
      pack ${scrollframe}.center.$tool.r.$i.st -side left -fill y
    }
    incr toggle
    set toggle [expr {$toggle %2}]
  }
  button .sim.bottom.cancel  -text Cancel -command {
    destroy .sim
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
  button .sim.bottom.help  -text Help -command {
    viewdata {In this dialog box you set the commands xschem uses to launch the
various external tools. The commands go through a tcl 'subst' round
 to do TCL variable and command substitution, so you may use the TCL
'{' and '}' characters for grouping arguments with spaces.
Xschem has 3 main netlisting modes (spice, verilog, vhdl) and for each
netlisting mode some simulators and some viewers can be defined.
The following variables are defined and will get substituted by
XSCHEM before sending commands to the shell:

 - N: complete filename of netlist for current netlisting mode
   (example: /home/schippes/.xschem/simulations/opamp.spice for spice)
   (example: /home/schippes/.xschem/simulations/opamp.v for verilog)
 - n: complete filename of netlist as above but without extension
   (example: /home/schippes/.xschem/simulations/opamp)
 - S: full pathname of schematic being used (example:
      /home/schippes/.xschem/xschem_library/opamp.sch)
 - s: name of schematic being used (example: opamp)
 - d: simulation directory (example: /home/schippes/.xschem/simulations)
 - terminal: terminal to be used for applications that need to be
   executed in terminal (example: $terminal -e ngspice -i "$N" -a)

If for a given tool there are multiple rows then the radiobutton
tells which one will be called by xschem.
Variables should be used with the usual substitution character $: $n, $N, etc.
Foreground (Fg) checkbutton tells xschem to wait for child process to finish.
Status checkbutton tells xschem to report a status dialog (stdout, stderr,
exit status) when process finishes.

If 'Accept, Save and Close' is pressed then changes made in the dialog box
entries will be saved in ~/.xschem/simrc, changes will be persistent.

If 'Accept, no Save and Close' is pressed then the changes are kept in
memory and dialog is closed without writing to a file,
if xschem is restarted changes will be lost.

If no ~/.xschem/simrc is present then a minimal default setup is presented.
To reset to default use the corresponding button or just delete the
~/.xschem/simrc file manually.
    } ro
  }
  button .sim.bottom.ok  -text {Accept, Save and Close} -command "
    set_sim_defaults
    simconf_saveconf $scrollframe
    destroy .sim
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  "
  button .sim.bottom.reset -text {Reset to default} -command {
    simconf_reset
  }
  button .sim.bottom.close -text {Accept, no Save and Close} -command {
    set_sim_defaults
    destroy .sim
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
  wm protocol .sim WM_DELETE_WINDOW {
    set_sim_defaults
    destroy .sim
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
  pack .sim.bottom.cancel -side left -anchor w
  pack .sim.bottom.help -side left
  #foreach tool $sim(tool_list) {
  #  button .sim.bottom.add${tool} -text +${tool} -command "
  #    simconf_add $tool
  #    destroy .sim
  #    xschem set semaphore [expr {[xschem get semaphore] -1}]
  #    save_sim_defaults ${USER_CONF_DIR}/simrc
  ##    simconf
  #  "
  #  pack .sim.bottom.add${tool} -side left
  #}
  pack .sim.bottom.ok -side right -anchor e
  pack .sim.bottom.close -side right
  pack .sim.bottom.reset -side right
  pack .sim.topf -fill both -expand yes
  pack .sim.bottom -fill x
  if { [info exists simconf_default_geometry]} {
     wm geometry .sim "${simconf_default_geometry}"
  }

  bind .sim.topf.f <Configure> {sframeyview .sim.topf}
  bind .sim <Configure> {
    set simconf_default_geometry [wm geometry .sim]
  }
  bind .sim <ButtonPress-4> { sframeyview .sim.topf scroll -0.2}
  bind .sim <ButtonPress-5> { sframeyview .sim.topf scroll 0.2}
  sframeyview .sim.topf place
  set maxsize [expr {[winfo height ${scrollframe}] + [winfo height .sim.bottom]}]
  wm maxsize .sim 9999 $maxsize
  # tkwait window .sim
}

proc simconf_add {tool} {
  global sim
  set n $sim($tool,n)
  set sim($tool,$n,cmd) {}
  set sim($tool,$n,name) {}
  set sim($tool,$n,fg) 0
  set sim($tool,$n,st) 0
  incr sim($tool,n)
}


############ cellview
# proc cellview prints symbol bindings (default binding or "schematic" attr in symbol)
# of all symbols used in current and sub schematics.
# the derived_symbols parameter of cellview should be either empty or 'derived_symbols'
proc cellview_setlabels {w symbol derived_symbol} {
  global dark_gui_colorscheme netlist_type
  if {$dark_gui_colorscheme} {
    set instfg orange1
    set symfg SeaGreen1
    set symbg SeaGreen4
    set missingbg IndianRed4
  } else {
    set instfg orange4
    set symfg SeaGreen4
    set symbg SeaGreen1
    set missingbg IndianRed1
  }
  set save_netlist_type [xschem get netlist_type]
  set current [xschem get current_name]
  set sym_spice_sym_def  [xschem getprop symbol $symbol spice_sym_def 2]
  set abs_sch [xschem get_sch_from_sym -1 $symbol]
  set sym_sch [rel_sym_path $abs_sch]
  set default_sch [add_ext $symbol .sch]
  set new_sch [$w get]
  $w configure -fg [option get . foreground {}]
  $w configure -bg [option get . background {}]
  if { $derived_symbol} {
    $w configure -fg $instfg
  } elseif {$sym_spice_sym_def ne {} } {
    $w configure -fg $symfg
  }
  if { $sym_spice_sym_def eq {}} {
    if { ![file exists [abs_sym_path [$w get]]] } {
      $w configure -bg $missingbg
    } elseif {$new_sch ne $default_sch } {
      $w configure -bg $symbg
    }
  }
  puts ===============
  if {$sym_sch ne $new_sch && $sym_spice_sym_def eq {}} {
    puts "Changing schematic attribute in symbol"
    xschem load -keep_symbols -nodraw -noundoreset $symbol
    set oldprop [xschem get schsymbolprop]
    if { $new_sch eq $default_sch } {
      set newprop [xschem subst_tok $oldprop schematic {}] ;# delete schematic attr in symbol
    } else {
      set newprop [xschem subst_tok $oldprop schematic $new_sch]
    }
    xschem set schsymbolprop $newprop
    xschem set_modify 3 ;# set only modified flag to force a save, do not update window/tab titles
    xschem save fast
    # no! cellview_setlabels called from cellview inside a "foreach {i symbol} $syms {...}"
    # xschem remove_symbols ;# purge all symbols to force a reload from disk
    xschem load -keep_symbols -nodraw -noundoreset $current
    set netlist_type $save_netlist_type
    xschem set netlist_type $netlist_type
    xschem netlist -keep_symbols -noalert;# traverse the hierarchy and retain all encountered symbols
    puts "get netlist"
  }
  puts sym_sch=$sym_sch
  puts default_sch=$default_sch
  puts new_sch=$new_sch
  puts symbol=$symbol
}

proc cellview_edit_item {symbol w} {
  global netlist_type
  set save_netlist_type [xschem get netlist_type]
  set sym_spice_sym_def  [xschem getprop symbol $symbol spice_sym_def 2]
  if {[xschem is_generator [$w get]]} {
    set f [$w get]
    regsub {\(.*} $f {} f
    textwindow [abs_sym_path $f]
  } elseif { $sym_spice_sym_def eq {}} {
    xschem load_new_window [$w get]
  } else {
    set current [xschem get current_name]
    set old_sym_def [xschem getprop symbol $symbol spice_sym_def 2]
    set new_sym_def [editdata $sym_spice_sym_def {Symbol spice_sym_def attribute}]
    if {$new_sym_def ne $old_sym_def} {
      xschem load -keep_symbols -nodraw -noundoreset $symbol
      set oldprop [xschem get schsymbolprop]
      set newprop [xschem subst_tok $oldprop spice_sym_def $new_sym_def]
      xschem set schsymbolprop $newprop
      xschem set_modify 3 ;# set only modified flag to force a save, do not update window/tab titles
      xschem save fast
      puts "$symbol: updated spice_sym_def attribute"
      xschem load -keep_symbols -nodraw -noundoreset $current
      set netlist_type $save_netlist_type
      xschem set netlist_type $netlist_type
      xschem reload_symbols ;# update in-memory symbol data
    }
  }
}

proc cellview_edit_sym {w} {
  set sym [$w cget -text]
  set res [catch {xschem symbol_base_name $sym} base_name]
  if {$res == 0} {
    if {$base_name ne {}} {
      set sym $base_name
    }
  }
  xschem load_new_window $sym
}

# derived_symbols: empty or 'derived_symbols'
# upd: never set by caller (used iinternally to update)
proc cellview { {derived_symbols {}} {upd 0}} {
  global nolist_libs dark_gui_colorscheme netlist_type

  set save_netlist_type [xschem get netlist_type]

  if {$dark_gui_colorscheme} {
    set instfg orange1
    set symfg SeaGreen1
    set symbg SeaGreen4
    set missingbg IndianRed4
  } else {
    set instfg orange4
    set symfg SeaGreen4
    set symbg SeaGreen1
    set missingbg IndianRed1
  }
  if {[info tclversion] >= 8.5} {
    set font {TkDefaultFont 10 bold} ;# Monospace
  } else {
    set font fixed
  }

  set netlist_type $save_netlist_type
  xschem set netlist_type $netlist_type
  xschem reload_symbols ;# purge unused symbols
  xschem netlist -keep_symbols -noalert;# traverse the hierarchy and retain all encountered symbols
  if {!$upd} {
    catch {destroy .cv}
    toplevel .cv
    wm geometry .cv 800x200
    update
    raise .cv
    frame .cv.top
    label .cv.top.sym -text {   SYMBOL} -width 30 -bg grey60 -anchor w -padx 4 -font $font
    label .cv.top.sch -text SCHEMATIC -width 45 -bg grey60 -anchor w -padx 4 -font $font
    label .cv.top.pad -text {      } -width 4 -bg grey60 -font $font
    pack .cv.top.sym .cv.top.sch -side left -fill x -expand 1
    pack .cv.top.pad -side left -fill x
    frame .cv.center
    set sf [sframe .cv.center]
  } else {
    set sf .cv.center.f.scrl
  }

  set syms [join [lsort -index 1 [xschem symbols $derived_symbols]]]
  # puts "syms=$syms"
  foreach {i symbol} $syms {
    if { [catch {set base_name [xschem symbol_base_name $symbol]}] } {
      set base_name $symbol
    }
    # puts "i=$i, symbol=$symbol"
    set derived_symbol 0
    if {$base_name ne {}} {
      set derived_symbol 1
    }
    if { [catch {xschem get_sch_from_sym -1 $symbol} abs_sch ]} {
      set abs_sch [abs_sym_path [add_ext $symbol .sch]]
    }
    if {$derived_symbol} {
      set abs_sym [abs_sym_path $base_name]
    } else {
      set abs_sym [abs_sym_path $symbol]
    }
    set skip 0
    foreach j $nolist_libs {
      if {[regexp $j $abs_sym]} {
        set skip 1
        break
      }
    }
    if {$skip} { continue }
    set sym_sch [rel_sym_path $abs_sch]
    if {[catch {xschem getprop symbol $symbol type} type]} {
      puts "error: $symbol not found: $type"
    }
    set type [xschem getprop symbol $symbol type]
    set sym_spice_sym_def [xschem getprop symbol $symbol spice_sym_def 2]
    if {$type eq {subcircuit}} {

      if {!$upd} {
        frame $sf.f$i
        pack $sf.f$i -side top -fill x
        label  $sf.f$i.l -text $symbol -width 30 -anchor w -padx 4 -borderwidth 1 \
          -relief sunken -pady 1 -font $font
        if {$derived_symbol} {
          $sf.f$i.l configure -fg $instfg
        }
        # puts $sf.f$i.s
        entry $sf.f$i.s -width 45 -borderwidth 1 -relief sunken -font $font
        button $sf.f$i.sym -text Sym -padx 4 -borderwidth 1 -pady 0 -font $font \
               -command "cellview_edit_sym $sf.f$i.l"
        button $sf.f$i.sch -text Sch -padx 4 -borderwidth 1 -pady 0 -font $font \
               -command "cellview_edit_item $symbol $sf.f$i.s"
        if {$sym_spice_sym_def eq {}} {
          $sf.f$i.s insert 0 $sym_sch
        } else {
          if {$derived_symbol} {
            $sf.f$i.s insert 0 {defined in instance spice_sym_def}
          } else {
            $sf.f$i.s insert 0 {defined in symbol spice_sym_def}
          }
        }
      }

      if {[xschem is_generator [ $sf.f$i.s get]]} {
        set f [ $sf.f$i.s get]
        regsub {\(.*} $f {} f
      } elseif { $sym_spice_sym_def eq {}} {
        set f [abs_sym_path [$sf.f$i.s get]]
      } else {
        set ff [split $sym_spice_sym_def \n]
        if {[llength $ff] > 5} {
          set ff [lrange $ff 0 4]
          lappend ff ...
        }
        set f [join $ff \n]
      }
      balloon $sf.f$i.s $f

      cellview_setlabels $sf.f$i.s $symbol $derived_symbol
      if {!$upd} {
        pack $sf.f$i.l $sf.f$i.s -side left -fill x -expand 1
        pack $sf.f$i.sch $sf.f$i.sym -side left
      }
    }
  }

  if {$upd} {return}

  frame .cv.bottom
  button .cv.bottom.update -text Update -command "cellview [list $derived_symbols] 1; xschem reload_symbols"
  pack .cv.bottom.update -side left
  label .cv.bottom.status -text {STATUS LINE}
  pack .cv.bottom.status -fill x -expand yes
  pack .cv.top -side top -fill x -expand no
  pack .cv.center -side top -fill both -expand yes
  pack .cv.bottom -side top -fill x -expand no
  sframeyview .cv.center place
  set maxsize [expr {[winfo height ${sf}] + [winfo height .cv.top] + [winfo height .cv.bottom]}]
  wm maxsize .cv 9999 $maxsize
  bind .cv.center.f <Configure> {sframeyview .cv.center}
  bind .cv <ButtonPress-4> { sframeyview .cv.center scroll -0.1}
  bind .cv <ButtonPress-5> { sframeyview .cv.center scroll 0.1}
  bind .cv <Escape> {destroy .cv}
}
############ /cellview

############ traversal
proc traversal_setlabels {w parent_sch instname inst_sch sym_sch default_sch
                          inst_spice_sym_def sym_spice_sym_def} {
  global traversal dark_gui_colorscheme netlist_type
  set sf .trav.center.f.scrl

  set save_netlist_type [xschem get netlist_type]
  # puts "traversal_setlabels: $w parent: |$parent_sch| inst: $instname def: $sym_sch $inst_sch --> [$w get]"
  # update schematic
  if {$parent_sch ne {} && $sym_spice_sym_def eq {} &&  $inst_spice_sym_def eq {} } {
    set current [xschem get current_name]
    if { $inst_sch ne [$w get] } {
      xschem load -undoreset -nodraw $parent_sch
      if { [$w get] eq  $sym_sch} {
        xschem setprop -fast instance $instname schematic  ;# remove schematic attr on instance
      } else {
        xschem setprop -fast instance $instname schematic [$w get]  ;# set schematic attr on instance
      }
      xschem set_modify 3 ;# set only modified flag to force a save, do not update window/tab titles
      xschem save fast
      set inst_sch [$w get]
      # puts "inst_sch set to: $inst_sch"
      xschem load -undoreset -nodraw $current
      set netlist_type $save_netlist_type
      xschem set netlist_type $netlist_type
    }
  }
  # /update schematic
  if {$dark_gui_colorscheme} {
    set instfg orange1
    set symfg SeaGreen1
    set instbg orange4
    set symbg SeaGreen4
    set missingbg IndianRed4
  } else {
    set instfg orange4
    set symfg SeaGreen4
    set instbg Orange1
    set symbg SeaGreen1
    set missingbg IndianRed1
  }
  $w configure -fg [option get . foreground {}]
  $w configure -bg [option get . background {}]
  if { $sym_spice_sym_def ne {}} {
    $w configure -fg $symfg
  } elseif {$inst_spice_sym_def ne {}} {
    $w configure -fg $instfg
  } elseif { ![file exists [abs_sym_path [$w get]]] } {
    $w configure -bg $missingbg
  } else {
    if {[$w get] eq $default_sch} {
      # ....
    } elseif {[$w get] eq $sym_sch} {
      $w configure -bg $symbg
    } elseif {[$w get] eq $inst_sch} {
      $w configure -bg $instbg
    }
  }
}

# This proc traverses the hierarchy and prints all instances in design.
proc traversal {{only_subckts 1} {all_hierarchy 1}} {
  global traversal keep_symbols
  set traversal(only_subckts) $only_subckts
  set traversal(all_hierarchy) $all_hierarchy
  set traversal(cnt) 0
  set save_keep $keep_symbols
  set keep_symbols 1
  xschem unselect_all
  xschem set no_draw 1 ;# disable screen update
  xschem set no_undo 1 ;# disable undo

  if {[info tclversion] >= 8.5} {
    set font {TkDefaultFont 10 bold} ;# Monospace
  } else {
    set font fixed
  }

  toplevel .trav
  frame .trav.top
  label .trav.top.inst -text {INSTANCE} -width 25 -bg grey60 -anchor w -padx 4 -font $font
  label .trav.top.sym  -text {SYMBOL} -width 30 -bg grey60 -anchor w -padx 4 -font $font
  label .trav.top.sch  -text SCHEMATIC -width 45 -bg grey60 -anchor w -padx 4 -font $font
  label .trav.top.pad  -text {        } -bg grey60 -font $font
  pack .trav.top.inst -side left -fill x -expand 1
  pack .trav.top.sym .trav.top.sch -side left -fill x
  pack .trav.top.pad -side left -fill x
  frame .trav.center
  set sf [sframe .trav.center]
  hier_traversal 0 $only_subckts $all_hierarchy
  xschem set no_draw 0
  xschem set no_undo 0
  set keep_symbols $save_keep

  frame .trav.bottom
  label .trav.bottom.status -text {STATUS LINE}
  pack .trav.bottom.status -fill x -expand yes
  pack .trav.top -side top -fill x -expand no
  pack .trav.center -side top -fill both -expand yes
  pack .trav.bottom -side top -fill x -expand no
  sframeyview .trav.center place
  set maxsize [expr {[winfo height ${sf}] + [winfo height .trav.top] + [winfo height .trav.bottom]}]
  wm maxsize .trav 9999 $maxsize
  bind .trav.center.f <Configure> {sframeyview .trav.center}
  bind .trav <ButtonPress-4> { sframeyview .trav.center scroll -0.1}
  bind .trav <ButtonPress-5> { sframeyview .trav.center scroll 0.1}
  bind .trav <Escape> "
    set traversal(geom) \[winfo geometry .trav\]
    destroy .trav
  "
  if {[info exists traversal(geom)]} {
    wm geometry .trav $traversal(geom)
  } else {
    wm geometry .trav 1200x400
  }
  update
  set traversal(geom) [winfo geometry .trav]
  return {}
}

# recursive procedure
proc hier_traversal {{level 0} {only_subckts 0} {all_hierarchy 1}} {
  global nolist_libs traversal

  if {[info tclversion] >= 8.5} {
    set font {TkDefaultFont 10 bold} ;# Monospace
  } else {
    set font fixed
  }
  set parent_sch [xschem get current_name]
  # puts $parent_sch
  set sf .trav.center.f.scrl
  set done_print 0
  set schpath [xschem get sch_path]
  set instances  [xschem get instances]
  set current_level [xschem get currsch]
  for {set i 0} { $i < $instances} { incr i} {
    # puts "hier_traversal: i=$i, current_level=$current_level, parent_sch=$parent_sch"
    set instname [xschem getprop instance $i name]
    set symbol [xschem getprop instance $i cell::name]
    set default_sch [add_ext $symbol .sch]
    set sym_sch [rel_sym_path [xschem get_sch_from_sym -1 $symbol]]
    set abs_symbol [abs_sym_path $symbol]
    set type [xschem getprop symbol $symbol type]
    set schematic [xschem get_sch_from_sym $i]
    set sch_exists [expr {[file exists $schematic] ? {} : {**missing**}}]
    set inst_sch [rel_sym_path $schematic]
    set sch_rootname [file tail [file rootname $inst_sch]]
    set inst_spice_sym_def [xschem getprop instance $i spice_sym_def]
    set sym_spice_sym_def [xschem getprop instance $i cell::spice_sym_def]
    if {$only_subckts && ($type ne {subcircuit})} { continue }

    set skip 0
    foreach j $nolist_libs {
      if {[regexp $j $abs_symbol]} {
        set skip 1
        break
      }
    }
    if {$skip} { continue }
    incr traversal(cnt)
    set cnt $traversal(cnt)

    # puts "building frame $sf.f$cnt"
    frame $sf.f$cnt
    pack $sf.f$cnt -side top -fill x
    label  $sf.f$cnt.i -text "[spaces $level 1 {* }]$instname" \
        -width 25 -anchor w -padx 4 -borderwidth 1 \
        -relief sunken -pady 1 -font $font
    label  $sf.f$cnt.l -text $symbol -width 30 -anchor w -padx 4 -borderwidth 1 \
        -relief sunken -pady 1 -font $font
    entry $sf.f$cnt.s -width 45 -borderwidth 1 -relief sunken -font $font
    if {$type eq {subcircuit}} {
      if {$inst_spice_sym_def ne {}} {
        $sf.f$cnt.s insert 0 "$sch_rootname defined in instance spice_sym_def"
      } elseif {$sym_spice_sym_def ne {}} {
        $sf.f$cnt.s insert 0 "$sch_rootname defined in symbol spice_sym_def"
      } else {
        $sf.f$cnt.s insert 0 "$inst_sch"
      }
    }
    button $sf.f$cnt.bsym -text {Sym} -padx 4 -borderwidth 1 -pady 0 -font $font \
           -command "
              xschem load_new_window \[$sf.f$cnt.l cget -text\]
            "
    button $sf.f$cnt.bsch -text {Sch} -padx 4 -borderwidth 1 -pady 0 -font $font \
         -command "
            if { [list $sym_spice_sym_def] eq {} && [list $inst_spice_sym_def] eq {}} {
              xschem load_new_window \[$sf.f$cnt.s get\]
            } elseif {[list $sym_spice_sym_def] ne {}} {
              editdata [list $sym_spice_sym_def] {Symbol spice_sym_def attribute}
            } else {
              editdata [list $inst_spice_sym_def] {Instance spice_sym_def attribute}
            }"
    button $sf.f$cnt.upd -text Upd  -padx 4 -borderwidth 1 -pady 0 -font $font \
      -command "
        traversal_setlabels $sf.f$cnt.s [list $parent_sch] [list $instname] [list $inst_sch] \
        [list $sym_sch] [list $default_sch] [list $inst_spice_sym_def] [list $sym_spice_sym_def]
        set traversal(geom) \[winfo geometry .trav\]
        destroy .trav
        traversal $traversal(only_subckts) $traversal(all_hierarchy)
      "

    traversal_setlabels $sf.f$cnt.s $parent_sch $instname $inst_sch $sym_sch \
                        $default_sch $inst_spice_sym_def $sym_spice_sym_def
    pack $sf.f$cnt.i -side left -fill x -expand 1
    pack $sf.f$cnt.l $sf.f$cnt.s -side left -fill x
    pack $sf.f$cnt.bsym $sf.f$cnt.bsch $sf.f$cnt.upd -side left
    set done_print 1
    if {$type eq {subcircuit} && $all_hierarchy} {
      xschem select instance $i fast nodraw
      set descended [xschem descend 1 6]
      if {$descended} {
        incr level
        set dp [hier_traversal $level $only_subckts 1]
        xschem go_back 2
        incr level -1
      } else { ;# descended into a blank schematic. Go back.
        xschem go_back 2
      }
    }
  }
  return $done_print
}
############ /traversal

proc bespice_getdata {sock} {
  global bespice_server_getdata
  if {[eof $sock] || [catch {gets $sock bespice_server_getdata(line,$sock)}]} {
    puts "Closing connection $sock to bespice : $bespice_server_getdata(addr,$sock)"
    unset bespice_server_getdata(addr,$sock)
    unset bespice_server_getdata(line,$sock)
    # we remove the closing socket from the list of clients
    set search_index [ lsearch $bespice_server_getdata(clients) $sock]
    # puts " search_index for $sock in $bespice_server_getdata(clients) ==> $search_index"
    if { $search_index >= 0 } {
      set bespice_server_getdata(clients) [ lreplace $bespice_server_getdata(clients) $search_index $search_index ]
    }
    set nb [ llength $bespice_server_getdata(clients) ]
    if { $nb == 0 } {
      # no more clients left => communication closed
      unset bespice_server_getdata(sock)
    } else {
       # we have another client connection => use this one
       set new_sock [lindex $bespice_server_getdata(clients) 0]
       puts "Communicating to bespice over socket $new_sock"
       set bespice_server_getdata(sock) [list $new_sock ]
    }
    close $sock

    # puts "Clients = $bespice_server_getdata(clients)"
    # set nb [ llength $bespice_server_getdata(clients) ]
    # puts "number of Clients = $nb"

  } else {
    puts "bespice --> $bespice_server_getdata(line,$sock)"
    set bespice_server_getdata(last) $bespice_server_getdata(line,$sock)
  }
}

proc xschem_getdata {sock} {
  global xschem_server_getdata tclcmd_puts debug_var

  while {1} {
    if {[gets $sock line] < 0} {
      break
    } else {
      append xschem_server_getdata(line,$sock) $line \n
    }
  }
  if {$debug_var<=-1} {puts "tcp<-- $xschem_server_getdata(line,$sock)"}
  # xschem command must be executed at global scope...
  redef_puts
  uplevel #0 [list catch $xschem_server_getdata(line,$sock) tclcmd_puts]
  rename puts {}
  rename ::tcl::puts puts
  if {$debug_var<=-1} {puts "tcp--> $tclcmd_puts"}
  set xschem_server_getdata(res,$sock) "$tclcmd_puts"
  puts -nonewline $sock "$xschem_server_getdata(res,$sock)"
  flush $sock
  close $sock ;# server closes
  if {$debug_var<=-1} {puts "Close $xschem_server_getdata(addr,$sock)"}
  unset xschem_server_getdata(addr,$sock)
  unset xschem_server_getdata(line,$sock)
  unset xschem_server_getdata(res,$sock)
}

# this function is called as soon as bespice wave connects to the communication server listening on $bespice_listen_port
# it makes sure the communication over the socket connection is possible
proc bespice_server {sock addr port} {
  global bespice_server_getdata
  if { ![info exists bespice_server_getdata(sock)] } {
    puts "Accepting bespice connection $sock from $addr port $port"
    set bespice_server_getdata(clients) [list $sock]
  } else {
    # we can' t handle more tha one socket at once
    #	however we put this socket to a list in case the 1st socket is closed.
    #	we can then use the 2nd socket
    puts "Can't handle bespice connection $sock from $addr port $port"
    lappend bespice_server_getdata(clients) [list $sock]
  }

  fconfigure $sock -buffering line
  set bespice_server_getdata(addr,$sock) [list $addr $port]
  set bespice_server_getdata(sock) [list $sock]
  fileevent $sock readable [list bespice_getdata $sock]
  # this informs bespice wave that it receives it's instructions from xschem. Some features will be adjusted for that.
  puts $bespice_server_getdata(sock) "set_customer_specialization xschem"

  # puts "Clients = $bespice_server_getdata(clients)"
  # set nb [ llength $bespice_server_getdata(clients) ]
  # puts "number of Clients = $nb"
}

proc xschem_server {sock addr port} {
  global xschem_server_getdata debug_var
  if {$debug_var<=-1} {puts "Accept $sock from $addr port $port"}
  fconfigure $sock -buffering line -blocking 0
  set xschem_server_getdata(addr,$sock) [list $addr $port]
  set xschem_server_getdata(line,$sock) {}
  fileevent $sock readable [list xschem_getdata $sock]
}

proc list_hierarchy {} {
  set s [xschem list_hierarchy]
  set r {}
  foreach {a b} [lsort -decreasing -dictionary -index 0 -stride 2 $s] {
    append r  $a {  } $b \n
  }
  return $r
}

## given a path (x1.x2.m4) descend into x1.x2 and return m4 whether m4 found or not
proc descend_hierarchy {path {redraw 1}} {
  xschem set no_draw 1
  # return to top level if not already there
  while { [xschem get currsch] } { xschem go_back }
  # recursively descend into sub-schematics
  while { [regexp {\.} $path] } {
    xschem unselect_all
    set inst $path
    regsub {\..*} $inst {} inst    ;# take 1st path component: xlev1[3].xlev2.m3 -> xlev1[3]
    regsub {[^.]+\.} $path {} path ;# take remaining path: xlev1[3].xlev2.m3 -> xlev2.m3
    xschem search exact 1 name $inst 1
    # handle vector instances: xlev1[3:0] -> xlev1[3],xlev1[2],xlev1[1],xlev1[0]
    # descend into the right one
    set inst_list [split [lindex [xschem expandlabel [lindex [xschem selected_set] 0 ] ] 0] {,}]
    set instnum [expr {[lsearch -exact  $inst_list $inst] + 1}]
    xschem descend $instnum
  }
  xschem set no_draw 0
  if {$redraw} {xschem redraw}
  return $path
}

## given a hierarchical instname name (x1.xamp.m1) go down in the hierarchy and
## select the specified instance (m1).
## this search assumes it is given from the top of hierarchy
proc select_inst {fullinst {redraw 1 } } {
  xschem set no_draw 1
  set inst [descend_hierarchy $fullinst 0]
  set res [xschem select instance $inst]
  # if nothing found return to top
  if {!$res} {
    while { [xschem get currsch] } { xschem go_back }
  }
  xschem set no_draw 0
  if {$redraw} {xschem redraw}
  if {$res} {return $inst} else { return {} }
}

proc pin_label {} {
  return [rel_sym_path [find_file_first lab_pin.sym]]
}

## given a hierarchical net name x1.xamp.netname go down in the hierarchy and
## highlight the specified net.
## this search assumes it is given from the top of hierarchy
proc probe_net {fullnet {redraw 1} } {
  xschem set no_draw 1
  set net [descend_hierarchy $fullnet 0]
  set res [xschem hilight_netname $net]
  if {$res==0  && [regexp {^net[0-9]+$} $net]} {
    set net \#$net
    set res [xschem hilight_netname $net]
  }
  if {!$res} {
    while { [xschem get currsch] } { xschem go_back }
  }
  xschem set no_draw 0
  if {$redraw} {xschem redraw}
  if {$res} {return $net} else { return {} }
}

# backannotate newnet to be connected to specified hierarchical instance name and pin.
# places a label close to the instance pin to be re-routed.
# actual reconnect is human assisted!
proc reroute_inst {fullinst pinattr pinval newnet} {
  if { [regexp {\.} $fullinst] } { set hier 1 } else { set hier 0 }
  set res [descend_hierarchy $fullinst 0]
  if {$res ne {} } {
    set coord [xschem instance_pin_coord $res $pinattr $pinval]
    if { $coord eq {} } {
      while { [xschem get currsch] } { xschem go_back }
      return 0
    }
    set pinname [lindex $coord 0]
    set x [expr {[lindex $coord 1] - 10} ]
    set y [expr {[lindex $coord 2] - 10} ]
    set oldnet [xschem instance_net $res $pinname]

    regsub {.*\.} $newnet {} newnet
    if { $oldnet eq $newnet } {
      while { [xschem get currsch] } { xschem go_back }
      puts "Warning: netlist patch already done? "
      return 0
    }

    xschem instance [pin_label] $x $y 0 0 [list name=l1 lab=$newnet]
    xschem hilight_netname $newnet
    xschem select instance $res
    xschem hilight_netname $oldnet
    if {$hier} { xschem save} ;# save so we can process other reroute_inst without beink asked to save.
    xschem redraw
    return 1
  }
  return 0
}

## put $new net labels close to pins on all elements connected to $old
proc reroute_net {old new} {
  xschem push_undo
  xschem set no_undo 1
  xschem unhilight
  probe_net $old
  set old_nopath [regsub {.*\.} $old {}]
  set new_nopath [regsub {.*\.} $new {}]
  set devlist [xschem instances_to_net $old_nopath]
  foreach i $devlist {
    set instname [lindex $i 0]
    set x  [expr {[lindex $i 2] - 10}]
    set y  [expr {[lindex $i 3] - 10}]
    xschem instance [pin_label] $x $y 0 0 [list name=l1 lab=$new_nopath]
    xschem select instance $instname
  }
  xschem hilight_netname $new_nopath
  xschem set no_undo 0
}

## $N : netlist file full path (/home/schippes/simulations/opamp.spice)
## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp)
## $s : schematic name (opamp) or netlist_name if given
## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch)
## $d : netlist directory
proc sim_cmd {cmd} {
  global  netlist_dir terminal
  regsub {/$} $netlist_dir {} netlist_dir
  set tool [xschem get netlist_type]
  set d ${netlist_dir}
  set S [xschem get schname]

  set custom_netlist_file [xschem get netlist_name]
  if {$custom_netlist_file ne {}} {
    set s [file rootname $custom_netlist_file]
  } else {
    set s [file tail [file rootname $S]]
  }
  set n ${netlist_dir}/${s}
  if {$tool eq {verilog}} {
    set N ${n}.v
  } else {
    set N ${n}.${tool}
  }
  # puts "N=$N\nn=$n\ns=$s\nS=$S\nd=$d"
  return [subst $cmd]
}

# wrapper to proc simulate, if called from button.
proc simulate_from_button {{callback {}}} {
   global simulate_bg
   set simvar tctx::[xschem get current_win_path]_simulate
   if {![info exists $simvar] || [set $simvar] ne {orange}} {
     simulate $callback
   } elseif {[info exists $simvar] && [set $simvar] eq {orange}} {
     set simulate_id tctx::[xschem get current_win_path]_simulate_id
     if { [info exists $simulate_id] } {
       set id [set $simulate_id]
       set res [kill_running_cmds $id -15]
       if { $res == 0} {
         # something went wrong. Forget about the process
         unset tctx::[xschem get current_win_path]_simulate_id
         set tctx::[xschem get current_win_path]_simulate $simulate_bg
         catch {[xschem get top_path].menubar entryconfigure Simulate -background $simulate_bg}
       }
     }
   }
}

## $N : netlist file full path (/home/schippes/simulations/opamp.spice)
## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp)
## $s : schematic name (opamp) or netlist_name if given
## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch)
## $d : netlist directory
proc simulate {{callback {}}} {

  global netlist_dir terminal sim env
  global execute XSCHEM_SHAREDIR has_x OS

  regsub {/$} $netlist_dir {} netlist_dir
  set_sim_defaults
  set netlist_type [xschem get netlist_type]
  if { [set_netlist_dir 0] ne {}} {
    set d ${netlist_dir}
    set tool $netlist_type
    set S [xschem get schname]
    set custom_netlist_file [xschem get netlist_name]
    if {$custom_netlist_file ne {}} {
      set s [file rootname $custom_netlist_file]
    } else {
      set s [file tail [file rootname $S]]
    }
    set n ${netlist_dir}/${s}
    if {$tool eq {verilog}} {
      set N ${n}.v
    } else {
      set N ${n}.${tool}
    }
    if { ![info exists  sim($tool,default)] } {
      if { [info exists has_x] } {alert_ "Warning: simulator for $tool is not configured"}
      puts "Warning: simulator for $tool is not configured"
      return -1
    }
    set def $sim($tool,default)
    set fg  $sim($tool,$def,fg)
    set st  $sim($tool,$def,st)
    if {$fg} {
      set fg {execute_wait}
    } else {
      set fg {execute}
    }
    set cmd [subst -nobackslashes $sim($tool,$def,cmd)]

    # window interface       tabbed interface
    # -----------------------------------------
    # top_path   win_path    top_path   win_path
    #  {}        .drw         {}        .drw
    #  .x1       .x1.drw      {}        .x1.drw
    #  .x2       .x2.drw      {}        .x2.drw
    set execute(callback) "
       set_simulate_button [list [xschem get top_path] [xschem get current_win_path]]
       $callback
    "
    if {$fg eq {execute_wait}} {xschem set semaphore [expr {[xschem get semaphore] +1}]}

    set save [pwd]
    cd $netlist_dir
    # Note: Windows $cmd cannot be surrounded by {} as exec will change forward slash to backward slash
    set id [eval execute $st $cmd]   ;# Start simulation process
    cd $save

    if {[info exists has_x] && $id >= 0 } {
      set tctx::[xschem get current_win_path]_simulate_id $id
      set button_path [xschem get top_path].menubar
      catch {$button_path entryconfigure Simulate -background orange}
      set tctx::[xschem get current_win_path]_simulate orange
    }

    puts "Simulation started: execution ID: $id"

    if {$fg eq {execute_wait}} {
      if {$id >= 0} {
        vwait execute(pipe,$id)
      }
      xschem set semaphore [expr {[xschem get semaphore] -1}]
    }
    return $id
  } else {
    return -1
  }
}

proc gaw_echoline {} {
  global gaw_fd
  gets $gaw_fd line
  if {[eof $gaw_fd]} {
     puts "finishing connection from gaw"
     close $gaw_fd
     unset gaw_fd
  } else {
    # generate a variable event we can vwait for
    set gaw_fd $gaw_fd
  }
  puts "gaw -> $line"
}

proc setup_tcp_gaw {} {
  global gaw_fd gaw_tcp_address netlist_dir has_x

  regsub {/$} $netlist_dir {} netlist_dir
  if { [info exists gaw_fd] } { return 1; }
  set custom_netlist_file [xschem get netlist_name]
  if {$custom_netlist_file ne {}} {
    set s [file rootname $custom_netlist_file]
  } else {
    set s [file tail [file rootname [xschem get schname 0]]]
  }
  if { ![info exists gaw_fd] && [catch {eval socket $gaw_tcp_address} gaw_fd] } {
    puts "Problems opening socket to gaw on address $gaw_tcp_address"
    unset gaw_fd
    if {[info exists has_x]} {
      tk_messageBox -type ok -title {Tcp socket error} \
       -message [concat "Problems opening socket to gaw on address $gaw_tcp_address. " \
         "Ensure the following line is present uncommented in ~/.gaw/gawrc: up_listenPort = 2020." \
         "If you recently closed gaw the port may be in a TIME_WAIT state for a minute or so ." \
         "Close gaw, Wait a minute or two, then send waves to gaw again."]
    }
    return 0
  }
  fconfigure $gaw_fd -blocking 1 -buffering line -encoding binary -translation binary
  fileevent $gaw_fd readable gaw_echoline
  puts $gaw_fd "table_set $s.raw"
  return 1
}

proc gaw_cmd {cmd} {
  global gaw_fd gaw_tcp_address netlist_dir has_x

  regsub {/$} $netlist_dir {} netlist_dir
  if { ![info exists gaw_fd] && [catch {eval socket $gaw_tcp_address} gaw_fd] } {
    puts "Problems opening socket to gaw on address $gaw_tcp_address"
    unset gaw_fd
    if {[info exists has_x]} {
      tk_messageBox -type ok -title {Tcp socket error} \
       -message [concat "Problems opening socket to gaw on address $gaw_tcp_address. " \
         "If you recently closed gaw the port may be in a TIME_WAIT state for a minute or so ." \
         "Close gaw, Wait a minute or two, then send waves to gaw again."]
    }
    return
  }
  fconfigure $gaw_fd -blocking 0 -buffering line -encoding binary -translation binary
  puts $gaw_fd "$cmd"
  set n [regexp -all \n $cmd]
  incr n
  puts "gaw command lines: $n"
  fileevent $gaw_fd readable gaw_echoline
  while { $n} {
    #timeout for abnormal deadlocks
    set wd [after 10000 set gaw_fd stalled]
    vwait gaw_fd
    if { $gaw_fd ne {stalled} } {
      after cancel $wd
    } else {
      puts "timeout waiting for gaw response.."
      break
    }
    incr n -1
  }
  close $gaw_fd
  unset gaw_fd
}

proc waves {{type {}}} {
  ## $N : netlist file full path (/home/schippes/simulations/opamp.spice)
  ## $n : netlist file full path with extension chopped (/home/schippes/simulations/opamp)
  ## $s : schematic name (opamp) or netlist_name if given
  ## $S : schematic name full path (/home/schippes/.xschem/xschem_library/opamp.sch)
  ## $d : netlist directory

  global netlist_dir terminal sim XSCHEM_SHAREDIR has_x
  global bespice_listen_port env simulate_bg execute

  regsub {/$} $netlist_dir {} netlist_dir
  if {$type ne {external} } {
    load_raw $type
    return
  }
  set netlist_type [xschem get netlist_type]
  set_sim_defaults
  if { [set_netlist_dir 0] ne {}} {
    set d ${netlist_dir}
    set tool ${netlist_type}
    set S [xschem get schname]

    set custom_netlist_file [xschem get netlist_name]
    if {$custom_netlist_file ne {}} {
      set s [file rootname $custom_netlist_file]
    } else {
      set s [file tail [file rootname $S]]
    }
    set n ${netlist_dir}/${s}
    if {$tool eq {verilog}} {
      set N ${n}.v
    } else {
      set N ${n}.${tool}
    }
    set tool ${tool}wave
    if {![info exists  sim($tool,default)] } {
      if { [info exists has_x] } {alert_ "Warning: viewer for $tool is not configured"}
      puts "Warning: viewer for $tool is not configured"
      return
    }
    set def $sim($tool,default)
    set fg  $sim($tool,$def,fg)
    set st  $sim($tool,$def,st)
    if {$fg} {
      set fg {execute_wait}
    } else {
      set fg {execute}
    }
    set cmd [subst -nobackslashes $sim($tool,$def,cmd)]

    set save [pwd]
    cd $netlist_dir
    set id [eval execute $st $cmd]
    cd $save

    if {$fg eq {execute_wait}} {
      if {$id >= 0} {
        xschem set semaphore [expr {[xschem get semaphore] +1}]
        vwait execute(pipe,$id)
        xschem set semaphore [expr {[xschem get semaphore] -1}]
      }
    }
  }
}
# ============================================================


proc graph_push_undo {} {
  global graph_change_done

  if {$graph_change_done == 0} {
    xschem push_undo
    set graph_change_done 1
  }
}

# allow change color (via graph_change_wave_color) of double clicked wave
proc graph_edit_wave {n n_wave} {
  global graph_sel_color graph_selected graph_sel_wave
  global graph_schname cadlayers
  if {[winfo exists .graphdialog]} {return}
  set graph_schname [xschem get schname]
  set_ne graph_sel_color 4
  set graph_selected $n
  set graph_sel_wave $n_wave
  set col  [xschem getprop rect 2 $graph_selected color]
  set node [xschem my_strtok_r [xschem getprop rect 2 $graph_selected node] \n \" 0]
  # add default colors if unspecified in col
  set i 0
  foreach graph_node $node {
    if {[lindex $col $i] eq {}} { lappend col $graph_sel_color}
    incr i
  }
  # remove excess colors
  set col [lrange $col 0 [expr {$i - 1}]]
  set graph_sel_color [lindex $col $graph_sel_wave]
  xschem setprop -fast rect 2 $graph_selected color $col
  xschem draw_graph  $graph_selected
  toplevel .graphdialog -class Dialog
  wm transient .graphdialog [xschem get topwindow]
  frame .graphdialog.f
  button .graphdialog.ok -text OK -command {
    destroy .graphdialog
  }
  button .graphdialog.cancel -text Cancel -command {destroy .graphdialog}
  for {set i 0} {$i < $cadlayers} {incr i} {
    radiobutton .graphdialog.f.r$i -value $i -background [lindex $tctx::colors $i] \
         -variable graph_sel_color -command {graph_change_wave_color $graph_sel_wave } \
         -selectcolor white -foreground black
    pack .graphdialog.f.r$i -side left -fill both -expand yes
  }
  grid .graphdialog.f  - -sticky nsew
  grid .graphdialog.ok .graphdialog.cancel -sticky ew
  grid rowconfig .graphdialog 0 -weight 1
  grid column .graphdialog 0 -weight 1
  grid column .graphdialog 1 -weight 1
  tkwait window .graphdialog
  set graph_schname {}
}


# get selected text from a text widget:
#
# .graphdialog.center.right.text1 get sel.first sel.last
#
# see if a selection is present:
# .graphdialog.center.right.text1 tag ranges sel
#
# replace selected text:
# .graphdialog.center.right.text1 replace sel.first sel.last BUS
#
# programmatically select text:
# .graphdialog.center.right.text1 tag add sel 1.0 {end - 1 chars}
# clear selection
# .graphdialog.center.right.text1 tag remove sel 1.0 end
# get position of cursor:
# .graphdialog.center.right.text1  index insert
# set cursor position:
# .graphdialog.center.right.text1 mark set insert 2.18


# add nodes from provided list of {node color} ....
# used in hilight_net()
proc graph_add_nodes_from_list {nodelist} {
  global graph_bus graph_selected graph_schname has_x
  if {$graph_bus} {
    set sep ,
  } else {
    set sep \n
  }
  if { [info exists has_x] && [winfo exists .graphdialog] } {
    set sel {}
    set current_node_list [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}]
    set col  [xschem getprop rect 2 $graph_selected color]
    if {[string length $current_node_list] > 0 && ![regexp "\n$" $current_node_list]} {
      .graphdialog.center.right.text1 insert end \n
    }
    set change_done 0
    set first 0
    foreach {i c} $nodelist {
      if {$sel ne {}} {append sel $sep}
      if {!$first  || !$graph_bus } {
        regsub {\[.*} $i {} busname
        lappend col $c
      }
      append sel $i
      set change_done 1
      set first 1
    }
    if {$change_done && $graph_bus} {
      set sel "[string toupper $busname];${sel}\n"
    } else {
      set sel "${sel}\n"
    }
    if {$change_done} {
      .graphdialog.center.right.text1 insert end $sel
      if { [xschem get schname] eq $graph_schname } {
        set node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
        xschem setprop -fastundo rect 2 $graph_selected color $col
        graph_update_nodelist
        regsub -all {[\\"]} $node {\\&} node_quoted
        xschem setprop -fast rect 2 $graph_selected node $node_quoted
        xschem draw_graph $graph_selected
      }
    }
  } else {
    set sel {}
    set change_done 0
    set first 0
    set col  [xschem getprop rect 2 [xschem get graph_lastsel] color]
    set nnn [xschem getprop rect 2 [xschem get graph_lastsel] node]
    foreach {i c} $nodelist {
      if {$sel ne {}} {append sel $sep}
      if {!$first  || !$graph_bus } {
        regsub {\[.*} $i {} busname
        lappend col $c
      }
      append sel $i
      set change_done 1
      set first 1
    }
    if {$change_done && $graph_bus} {
      set sel "[string toupper $busname];${sel}\n"
    } else {
      set sel "${sel}\n"
    }

    if {$change_done} {
      xschem setprop -fastundo rect 2 [xschem get graph_lastsel] color $col
      if {[string length $nnn] > 0 && ![regexp "\n$" $nnn]} {
        append nnn "\n"
      }
      append nnn $sel
      regsub -all {[\\"]} $nnn {\\&} node_quoted
      xschem setprop -fast rect 2 [xschem get graph_lastsel] node $node_quoted
      xschem draw_graph [xschem get graph_lastsel]
    }
  }
}

# add nodes from left listbox
proc graph_add_nodes {} {
  global graph_bus graph_sel_color graph_selected
  set sel_idx [.graphdialog.center.left.list1 curselection]
  set sel {}
  if {$graph_bus} {
    set sep ,
  } else {
    set sep \n
  }
  set current_node_list [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}]
  if {[string length $current_node_list] > 0 && ![regexp "\n$" $current_node_list]} {
    .graphdialog.center.right.text1 insert end \n
  }
  set change_done 0
  foreach i $sel_idx {
    set c [.graphdialog.center.left.list1 get $i]
    # escape [ and ] characters.
    set c [regsub -all {([][])}  $c {\\\1}]
    if { ![regexp "(^|\[ \t\n\])${c}($|\[ \t\n\])" $current_node_list]} {
      if {$sel ne {}} {append sel $sep}
      append sel [.graphdialog.center.left.list1 get $i]
      set change_done 1
    }
  }
  if {$change_done && $graph_bus} {
    set sel "BUS_NAME;${sel}\n"
  } else {
    set sel "${sel}\n"
  }
  if {$change_done} {
    set tag [.graphdialog.center.right.text1 tag names insert]
    if { $tag eq {}} {set tag [.graphdialog.center.right.text1 tag names {insert - 1 char}]}
    .graphdialog.center.right.text1 insert {insert lineend + 1 char} $sel
    # insert $graph_sel_color colors along with inserted nodes, so previous wave colors are preserved
    if { [regexp {^t} $tag]} {
      set index [string range $tag 1 end]
      incr index
      set col  [xschem getprop rect 2 $graph_selected color]
      set col [linsert $col $index $graph_sel_color]
      xschem setprop -fast rect 2 $graph_selected color $col
    }
  }
}

proc graph_delete_nodes {} {
  global graph_selected
  set tag [.graphdialog.center.right.text1 tag names insert]
  if { [regexp {^t} $tag]} {
    set change_done 1
    set index [string range $tag 1 end]
    set col  [xschem getprop rect 2 $graph_selected color]
    set node [xschem my_strtok_r [xschem getprop rect 2 $graph_selected node] \n \" 1]

    set col [lreplace $col $index $index]
    set node [lreplace $node $index $index]
    set node [join $node \n]
    graph_push_undo
    .graphdialog.center.right.text1 delete 1.0 end
    .graphdialog.center.right.text1 insert 1.0 $node
    xschem setprop -fast rect 2 $graph_selected color $col
  }
}

proc graph_get_signal_list {siglist pattern } {
  global graph_sort
  set siglist [split $siglist \n]
  set direction {-decreasing}
  if {$graph_sort} {set direction {-increasing}}
  set result {}
  set siglist [lsort $direction -dictionary $siglist]
  # just check if pattern is a valid regexp
  set err [catch {regexp $pattern {12345}} res]
  if {$err} {set pattern {}}
  foreach i $siglist {
    regsub {^v\((.*)\)$} $i {\1} i
    if {[regexp $pattern $i] } {
       lappend result $i
    }
  }
  return $result
}

proc touches {sel tag} {
  set res 0
  scan $sel {%d.%d %d.%d} sel_linestart sel_charstart sel_lineend sel_charend
  scan $tag {%d.%d %d.%d} tag_linestart tag_charstart tag_lineend tag_charend
  set selstart [expr {$sel_linestart * 1000000 + $sel_charstart}]
  set selend [expr {$sel_lineend * 1000000 + $sel_charend}]
  set tagstart [expr {$tag_linestart * 1000000 + $tag_charstart}]
  set tagend [expr {$tag_lineend * 1000000 + $tag_charend}]
  # puts "selstart: $selstart"
  # puts "selend: $selend"
  # puts "tagstart: $tagstart"
  # puts "tagend: $tagend"
  if { ($tagstart >= $selstart && $tagstart <= $selend) ||
       ($tagend >= $selstart && $tagend <= $selend) ||
       ($selstart >= $tagstart && $selstart <= $tagend) ||
       ($selend >= $tagstart && $selend <= $tagend)
     } {
    set res 1
  }
  # puts "touch: returning $res"
  return $res
}

proc set_graph_default_colors {} {
  global graph_selected graph_schname cadlayers
  if { [xschem get schname] ne $graph_schname } return
  set n_nodes [xschem count_items [xschem getprop rect 2 $graph_selected node] "\n" \"]
  set color {}
  for {set i 0} {$i < $n_nodes} { incr i} {
    append color [expr {$i%($cadlayers - 4) + 4}] { }
  }
  xschem setprop -fast rect 2 $graph_selected color $color
  graph_update_nodelist
  xschem draw_graph $graph_selected
}

# change color of selected wave in text widget and redraw graph
# OR
# change color attribute of wave given as parameter, redraw graph
proc graph_change_wave_color {{wave {}}} {
  global graph_sel_color graph_selected graph_schname

  if { [xschem get schname] ne $graph_schname } return
  graph_push_undo
  #  get tag the cursor is on:
  if { $wave eq {}} {
    set sel_range [.graphdialog.center.right.text1 tag ranges sel]
    if {$sel_range ne {}} {
      # puts "sel_range --> $sel_range"
      foreach tag [.graphdialog.center.right.text1 tag names] {
        if {$tag eq {sel}} {continue}
        set tag_range [.graphdialog.center.right.text1 tag ranges $tag]
        # puts "$tag --> $tag_range"

        if { [touches $sel_range $tag_range]} {
          set index [string range $tag 1 end]
          set col  [xschem getprop rect 2 $graph_selected color]
          set col [lreplace $col $index $index  $graph_sel_color]
          xschem setprop -fast rect 2 $graph_selected color $col
        }
      }
      graph_update_nodelist
      xschem draw_graph $graph_selected
    } else {
      set tag [.graphdialog.center.right.text1 tag names insert]
      # if { $tag eq {}} {set tag [.graphdialog.center.right.text1 tag names {insert - 1 char}]}
      if { [regexp {^t} $tag]} {
        set index [string range $tag 1 end]
        set col  [xschem getprop rect 2 $graph_selected color]
        set col [lreplace $col $index $index  $graph_sel_color]
        xschem setprop -fast rect 2 $graph_selected color $col
        graph_update_nodelist
        xschem draw_graph $graph_selected
      }
    }
  # wave to change provided as parameter
  } else {
    set col  [xschem getprop rect 2 $graph_selected color]
    set col [lreplace $col $wave $wave  $graph_sel_color]
    xschem setprop -fast rect 2 $graph_selected color $col
    xschem draw_graph $graph_selected
  }
}

# set txt [xschem getprop rect 2 $n node 2]
# set txt [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}]
# return first and last indexes of nodes in txt
proc graph_tag_nodes {txt} {
  global graph_selected graph_sel_color
  # delete old tags
  if { [winfo exists .graphdialog.center.right.text1] } {
    eval .graphdialog.center.right.text1 tag delete [ .graphdialog.center.right.text1 tag names]
    set col  [xschem getprop rect 2 $graph_selected color]
    set col [string trim $col " \n"]
  }


  set start 0
  # transform escaped newlines into two spaces, so tagging of multiline nodes is simpler
  regsub -all "\\\\\n" $txt {  } txt
  if {[regexp {^tcleval\(} $txt]} {
    set start 8
    regsub {\)[ \n]*$} $txt {} txt
  }
  set regx {("[^"]+")|([^\n]+)}

  set tt {}
  set cc {}
  while {[regexp -indices -start $start $regx $txt idxall]} {
    lappend tt [lindex $idxall 0]
    set ccc [lindex $idxall 1]
    lappend cc $ccc
    set start [expr {$ccc + 1}]
  }
  if { [winfo exists .graphdialog.center.right.text1] } {
    set n 0
    if { $tt ne {} } {
      foreach t $tt c $cc {
        set col_idx [lindex $col $n]
        # add missing colors
        if {$col_idx eq {}} {
          set col_idx $graph_sel_color
          lappend col $graph_sel_color
        }
        set b [lindex $tctx::colors $col_idx]
        .graphdialog.center.right.text1 tag add t$n "1.0 + $t chars" "1.1 + $c chars"
        if { [info tclversion] > 8.4} {
          .graphdialog.center.right.text1 tag configure t$n -background $b -selectbackground grey40
        } else {
          .graphdialog.center.right.text1 tag configure t$n -background $b
        }
        incr n
      }
      # remove excess colors
      set col [lrange $col 0 [expr {$n - 1}]]
    } else {
      set col {}
    }
    xschem setprop -fast rect 2 $graph_selected color $col
  }
  return [list $tt $cc]
}

# tag nodes in text widget with assigned colors
proc graph_update_nodelist {} {
  global graph_selected graph_sel_color graph_schname
  if { [xschem get schname] ne $graph_schname } return
  # tagging nodes in text widget:
  set txt [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}]
  graph_tag_nodes $txt
}

proc graph_fill_listbox {} {
  global graph_selected
  set pattern [.graphdialog.center.left.search get]
  set retv {}
  set autoload [uplevel #0 {subst [xschem getprop rect 2 $graph_selected autoload 2]}]
  set rawfile [xschem getprop rect 2 $graph_selected rawfile]
  if {$rawfile ne {}} {
    if {![catch {eval uplevel #0 {subst $rawfile}} res]} {
      set rawfile $res
    }
  }
  set sim_type [uplevel #0 {subst [xschem getprop rect 2 $graph_selected sim_type 2]}]
  if {$autoload ne {} && $autoload } { set autoload read} else {set autoload switch}
  # puts "graph_fill_listbox: $rawfile $sim_type"
  if {$rawfile ne {}} {
    if {$sim_type eq {table}} {
      set res [xschem raw table_read $rawfile $sim_type]
    } else {
      set res [xschem raw $autoload $rawfile $sim_type]
    }
    if {$res} {
      set retv [graph_get_signal_list [xschem raw_query list] $pattern]
      xschem raw switch_back
    }
    # puts "switch back"
  } elseif {[xschem raw loaded] != -1} {
    set retv [graph_get_signal_list [xschem raw_query list] $pattern]
  }
  .graphdialog.center.left.list1 delete 0 end
  eval .graphdialog.center.left.list1 insert 0 $retv

}

# called from event handlers (OK, KeyRelease, DoubleClick) in graph_edit_properties
proc graph_update_node {node} {
  global graph_selected
  graph_update_nodelist
  # add a backslash before " and \ characters
  # note the double escaping for regsub replace string
  regsub -all {[\\"]} $node {\\&} node_quoted
  graph_push_undo
  xschem setprop -fast rect 2 $graph_selected node $node_quoted
  xschem draw_graph $graph_selected
}

proc graph_update_div {graph_selected div} {
  set divis [.graphdialog.top2.$div get]
  if {[regexp {^[0-9]+$} $divis] && $divis < 1} {
    set divis 1
    .graphdialog.top2.$div delete 0 end
    .graphdialog.top2.$div insert 0 $divis
  }
  graph_push_undo
  xschem setprop rect 2 $graph_selected $div $divis
  xschem draw_graph $graph_selected
}

proc graph_set_linewidth {graph_sel} {
  global graph_linewidth_mult
  set custom_lw [.graphdialog.top.lwe get]
  if {[regexp {^[ \t]*$} $custom_lw]} {
    set custom_lw  $graph_linewidth_mult
  }
  graph_push_undo
  if {$custom_lw != $graph_linewidth_mult} {
    xschem setprop rect 2 $graph_sel linewidth_mult $custom_lw
  } else {
    xschem setprop rect 2 $graph_sel linewidth_mult ;# delete attribute since it is default
  }
}

proc raw_is_loaded {rawfile type} {
  set loaded 0

  set r [catch "uplevel #0 {subst $rawfile}" res]
  if {$r == 0} {
    set rawfile $res
  } else {
    return $loaded
  }
  set rawlist [lrange [xschem raw info] 2 end]
  foreach {n f t} $rawlist {
    if {$rawfile eq $f && $type eq $t} {
       set loaded 1
       break
    }
  }
  return $loaded
}
proc set_rect_flags {graph_selected} {
      global graph_private_cursor graph_unlocked

      if {$graph_private_cursor} {
        set private_cursor {,private_cursor}
      } else {
        set private_cursor {}
      }
      if {$graph_unlocked} {
        set unlocked {,unlocked}
      } else {
        set unlocked {}
      }
      xschem setprop -fast rect 2 $graph_selected flags "graph$unlocked$private_cursor"
  }

proc graph_set_raw_props {} {
  global graph_selected
  xschem setprop -fast rect 2 $graph_selected rawfile [.graphdialog.center.right.rawentry get]
  xschem setprop -fast rect 2 $graph_selected sim_type [.graphdialog.center.right.list get]
  graph_fill_listbox
}

proc graph_edit_properties {n} {
  global graph_bus graph_sort graph_digital graph_selected graph_sel_color graph_legend
  global graph_vlegend graph_unlocked graph_schname graph_logx graph_logy cadlayers graph_rainbow
  global graph_linewidth_mult graph_change_done has_x graph_dialog_default_geometry
  global graph_autoload graph_private_cursor

  if { ![info exists has_x]} {return}
  set graph_change_done 0
  if { [winfo exists .graphdialog]} {
    set graph_dialog_default_geometry [winfo geometry .graphdialog]
  }
  catch {destroy .graphdialog}
  toplevel .graphdialog -class Dialog ;# -width 1 -height 1
  wm withdraw .graphdialog
  wm transient .graphdialog [xschem get topwindow]
  update idletasks

  set graph_selected $n
  set graph_schname [xschem get schname]
  set_ne graph_sel_color 4
  set_ne graph_sort 0
  set graph_rainbow 0
  if {[xschem getprop rect 2 $n rainbow] == 1} {set graph_rainbow 1}
  set graph_logx 0
  if {[xschem getprop rect 2 $n logx] == 1} {set graph_logx 1}
  set graph_logy 0
  if {[xschem getprop rect 2 $n logy] == 1} {set graph_logy 1}
  set graph_legend 1
  if {[xschem getprop rect 2 $n legend] == 0} {set graph_legend 0}
  set graph_vlegend 0
  if {[xschem getprop rect 2 $n vlegend] == 1} {set graph_vlegend 1}
  set graph_digital 0
  if {[xschem getprop rect 2 $n digital] == 1} {set graph_digital 1}

  if {[regexp {private_cursor} [xschem getprop rect 2 $n flags]]} {
    set graph_private_cursor 1
  } else {
    set graph_private_cursor 0
  }

  if {[regexp {unlocked} [xschem getprop rect 2 $n flags]]} {
    set graph_unlocked 1
  } else {
    set graph_unlocked 0
  }

  set autoload [xschem getprop rect 2 $n autoload]
  if {$autoload ne {} && $autoload} {
    set graph_autoload 1
  } else {
    set graph_autoload 0
  }

  frame .graphdialog.top
  # another row of buttons
  frame .graphdialog.top2
  frame .graphdialog.top3
  frame .graphdialog.top4
  frame .graphdialog.top5
  panedwindow .graphdialog.center -orient horiz
  frame .graphdialog.bottom
  frame .graphdialog.center.left
  frame .graphdialog.center.right
  .graphdialog.center add .graphdialog.center.left .graphdialog.center.right
  pack .graphdialog.top -side top -fill x
  pack .graphdialog.top2 -side top -fill x
  pack .graphdialog.top3 -side top -fill x
  pack .graphdialog.top4 -side top -fill x
  pack .graphdialog.top5 -side top -fill x
  pack .graphdialog.center -side top -fill both -expand yes
  pack .graphdialog.bottom -side top -fill x
  # center-left frame
  label .graphdialog.center.left.labsearch -text Search:
  entry .graphdialog.center.left.search -width 10
  entry_replace_selection .graphdialog.center.left.search
  button .graphdialog.center.left.add -text Add -command {
    graph_add_nodes
    if { [xschem get schname] eq $graph_schname } {
      graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
    }
  }
  button .graphdialog.center.left.del -text Del -command {
    graph_delete_nodes
    if { [xschem get schname] eq $graph_schname } {
      graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
    }
  }
  listbox .graphdialog.center.left.list1 -width 20 -height 5 -selectmode extended \
     -yscrollcommand {.graphdialog.center.left.yscroll set} \
     -xscrollcommand {.graphdialog.center.left.xscroll set}
  scrollbar .graphdialog.center.left.yscroll -command {.graphdialog.center.left.list1 yview}
  scrollbar .graphdialog.center.left.xscroll -orient horiz -command {.graphdialog.center.left.list1 xview}

  grid configure .graphdialog.center.left.labsearch -row 0 -column 0
  grid configure .graphdialog.center.left.search -row 0 -column 1 -sticky ew
  grid configure .graphdialog.center.left.add -row 0 -column 2
  grid configure .graphdialog.center.left.del -row 0 -column 3
  grid .graphdialog.center.left.list1 - - - .graphdialog.center.left.yscroll -sticky nsew
  grid .graphdialog.center.left.xscroll - - - -sticky nsew
  grid rowconfig .graphdialog.center.left 0 -weight 0
  grid rowconfig .graphdialog.center.left 1 -weight 1 -minsize 2c
  # grid columnconfig .graphdialog.center.left 0 -weight 1
  grid columnconfig .graphdialog.center.left 1 -weight 1

  # center right frame
  checkbutton .graphdialog.center.right.autoload -text {Auto load}  -variable graph_autoload \
    -command {
      if {$graph_autoload} {
        xschem setprop -fast rect 2 $graph_selected autoload 1
      } else {
        xschem setprop -fast rect 2 $graph_selected autoload 0
      }
    }
  label .graphdialog.center.right.lab2 -text {    Sim type:}
  if { [info tclversion] > 8.4} {
    ttk::combobox .graphdialog.center.right.list \
      -values {dc ac tran op sp spectrum noise constants table}  -width 9
  } else {
    entry .graphdialog.center.right.list -width 4
    entry_replace_selection .graphdialog.center.right.list
  }
  if { [info tclversion] > 8.4} {
    bind .graphdialog.center.right.list <<ComboboxSelected>> {
      xschem setprop -fast rect 2 $graph_selected sim_type [.graphdialog.center.right.list get]
      graph_fill_listbox
    }
    if { [xschem getprop rect 2 $graph_selected sim_type 2] ne {}} {
      .graphdialog.center.right.list set [xschem getprop rect 2 $graph_selected sim_type 2]
    } else {
      .graphdialog.center.right.list set {}
    }
  } else {
    .graphdialog.center.right.list delete 0 end
    if { [xschem getprop rect 2 $graph_selected sim_type 2] ne {}} {
      .graphdialog.center.right.list insert 0 [xschem getprop rect 2 $graph_selected sim_type 2]
    } else {
      .graphdialog.center.right.list insert 0 {}
    }
  }

  bind .graphdialog.center.right.list <KeyRelease> {
    xschem setprop -fast rect 2 $graph_selected sim_type [.graphdialog.center.right.list get]
    graph_fill_listbox
  }


  entry .graphdialog.center.right.rawentry -width 20
  entry_replace_selection .graphdialog.center.right.rawentry
  button .graphdialog.center.right.rawbut -text {Raw file:} -command {
    regsub {/$} $netlist_dir {} netlist_dir
   .graphdialog.center.right.rawentry delete 0 end
   .graphdialog.center.right.rawentry insert 0 \
     [string map [list $netlist_dir {$netlist_dir}] [select_raw .graphdialog]]
    graph_set_raw_props
  }

  bind .graphdialog.center.right.rawentry <KeyRelease> graph_set_raw_props
  bind .graphdialog.center.right.rawentry <Leave> graph_set_raw_props

  .graphdialog.center.right.rawentry insert 0 [xschem getprop rect 2 $graph_selected rawfile 2]
  .graphdialog.center.right.rawentry xview moveto 1
  text .graphdialog.center.right.text1 -undo 1 -wrap none -width 50 -height 5 -background grey90 -fg black \
     -insertbackground grey40 -exportselection 1 \
     -yscrollcommand {.graphdialog.center.right.yscroll set} \
     -xscrollcommand {.graphdialog.center.right.xscroll set}
  scrollbar .graphdialog.center.right.yscroll -command {.graphdialog.center.right.text1 yview}
  scrollbar .graphdialog.center.right.xscroll -orient horiz -command {.graphdialog.center.right.text1 xview}

  grid .graphdialog.center.right.autoload \
       .graphdialog.center.right.lab2 .graphdialog.center.right.list \
       .graphdialog.center.right.rawbut  .graphdialog.center.right.rawentry -
  grid configure .graphdialog.center.right.rawentry -sticky ew
  grid .graphdialog.center.right.text1 - - - - .graphdialog.center.right.yscroll -sticky nsew
  grid .graphdialog.center.right.xscroll - - - - - -sticky ew
  grid rowconfig .graphdialog.center.right 0 -weight 0
  grid rowconfig .graphdialog.center.right 1 -weight 1 -minsize 3c
  grid rowconfig .graphdialog.center.right 2 -weight 0
  grid columnconfig .graphdialog.center.right 0 -weight 0
  grid columnconfig .graphdialog.center.right 1 -weight 0
  grid columnconfig .graphdialog.center.right 2 -weight 0
  grid columnconfig .graphdialog.center.right 3 -weight 0
  grid columnconfig .graphdialog.center.right 4 -weight 1
  grid columnconfig .graphdialog.center.right 5 -weight 0

  # bottom frame
  button .graphdialog.bottom.cancel -padx 1 -borderwidth 1 -pady 0 -text Cancel -command {
    set graph_dialog_default_geometry [winfo geometry .graphdialog]
    destroy .graphdialog
    set graph_selected {}
    set graph_schname {}
  }
  button .graphdialog.bottom.ok -padx 1 -borderwidth 1 -pady 0 -text OK -command {
    if { [xschem get schname] eq $graph_schname } {
      graph_push_undo
      graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
      xschem setprop -fast rect 2 $graph_selected x1 [.graphdialog.top3.xmin get]
      xschem setprop -fast rect 2 $graph_selected x2 [.graphdialog.top3.xmax get]
      xschem setprop -fast rect 2 $graph_selected y1 [.graphdialog.top3.ymin get]
      xschem setprop -fast rect 2 $graph_selected y2 [.graphdialog.top3.ymax get]
      set_rect_flags $graph_selected
    }
    set graph_dialog_default_geometry [winfo geometry .graphdialog]
    destroy .graphdialog
    set graph_selected {}
    set graph_schname {}
  }
  button .graphdialog.bottom.apply -padx 1 -borderwidth 1 -pady 0 -text Apply -command {
    if { [xschem get schname] eq $graph_schname } {
      graph_push_undo
      graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
      xschem setprop -fast rect 2 $graph_selected x1 [.graphdialog.top3.xmin get]
      xschem setprop -fast rect 2 $graph_selected x2 [.graphdialog.top3.xmax get]
      xschem setprop -fast rect 2 $graph_selected y1 [.graphdialog.top3.ymin get]
      xschem setprop -fast rect 2 $graph_selected y2 [.graphdialog.top3.ymax get]
      set_rect_flags $graph_selected
    }
  }

  # top packs
  pack .graphdialog.bottom.ok -side left
  pack .graphdialog.bottom.apply -side left
  pack .graphdialog.bottom.cancel -side left

  for {set i 0} {$i <= $cadlayers} {incr i} {
    if {$i == $cadlayers } {
      button .graphdialog.top4.r$i -padx 1 -borderwidth 1 -pady 0 \
        -command "set_graph_default_colors" \
        -text {AUTO SET}
    } else {
      radiobutton .graphdialog.top4.r$i -value $i -background [lindex $tctx::colors $i] \
        -variable graph_sel_color -command graph_change_wave_color \
        -selectcolor white -foreground black
    }
    pack .graphdialog.top4.r$i -side left
  }

  checkbutton .graphdialog.top5.legend -text {Legend} -variable graph_legend -indicatoron 1 \
    -command {
       if { [xschem get schname] eq $graph_schname } {
         graph_push_undo
         xschem setprop -fast rect 2 $graph_selected legend $graph_legend
         xschem draw_graph $graph_selected
       }
     }

  # top2 frame
  checkbutton .graphdialog.top5.vlegend -text {V. legend} -variable graph_vlegend -indicatoron 1 \
    -command { 
       if { [xschem get schname] eq $graph_schname } {
         graph_push_undo
         xschem setprop -fast rect 2 $graph_selected vlegend $graph_vlegend
         xschem draw_graph $graph_selected
       }
     } 

  label .graphdialog.top2.labunitx -text {X units}
  spinbox .graphdialog.top2.unitx -values {f p n u m 1 k M G T} -width 2 \
   -command {
      graph_push_undo
      xschem setprop rect 2 $graph_selected unitx [.graphdialog.top2.unitx get]
      xschem draw_graph $graph_selected
    }

  label .graphdialog.top2.labunity -text {  Y units}
  spinbox .graphdialog.top2.unity -values {f p n u m 1 k M G T} -width 2 \
   -command {
      graph_push_undo
      xschem setprop rect 2 $graph_selected unity [.graphdialog.top2.unity get]
      xschem draw_graph $graph_selected
    }

  label .graphdialog.top2.labdivx -text {  X div.}
  entry .graphdialog.top2.divx -width 2
  entry_replace_selection .graphdialog.top2.divx
  bind .graphdialog.top2.divx <KeyRelease> {
    graph_update_div $graph_selected divx
  }

  label .graphdialog.top2.labdivy -text {  Y div.}
  entry .graphdialog.top2.divy -width 2
  entry_replace_selection .graphdialog.top2.divy
  bind .graphdialog.top2.divy <KeyRelease> {
    graph_update_div $graph_selected divy
  }

  label .graphdialog.top2.labsubdivx -text {  X subdiv.}
  entry .graphdialog.top2.subdivx  -width 2
  entry_replace_selection .graphdialog.top2.subdivx
  bind .graphdialog.top2.subdivx <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected subdivx [.graphdialog.top2.subdivx get]
    xschem draw_graph $graph_selected
  }

  label .graphdialog.top2.labsubdivy -text {  Y subdiv.}
  entry .graphdialog.top2.subdivy -width 2
  entry_replace_selection .graphdialog.top2.subdivy
  bind .graphdialog.top2.subdivy <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected subdivy [.graphdialog.top2.subdivy get]
    xschem draw_graph $graph_selected
  }

  label .graphdialog.top2.labsweep -text {  Sweep}
  entry .graphdialog.top2.sweep -width 10
  entry_replace_selection .graphdialog.top2.sweep

  label .graphdialog.top2.labmode -text {Mode}
  if  { [info tclversion] > 8.4} {
    ttk::combobox .graphdialog.top2.mode -values {Line HistoV HistoH} -width 6
    bind .graphdialog.top2.mode <<ComboboxSelected>> {
      graph_push_undo
      xschem setprop rect 2 $graph_selected mode [.graphdialog.top2.mode get]
      xschem draw_graph $graph_selected
    }
  } else {
    spinbox .graphdialog.top2.mode -values {Line HistoV HistoH} -width 6 \
     -command {
        graph_push_undo
        xschem setprop rect 2 $graph_selected mode [.graphdialog.top2.mode get]
        xschem draw_graph $graph_selected
      }
  }

  # bind .graphdialog.top2.sweep <KeyRelease> {
  #   graph_push_undo
  #   xschem setprop rect 2 $graph_selected sweep [.graphdialog.top2.sweep get]
  #   xschem draw_graph $graph_selected
  # }

  bind .graphdialog.top2.sweep <Leave> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected sweep [.graphdialog.top2.sweep get]
    xschem draw_graph $graph_selected
  }

  .graphdialog.top2.sweep insert 0 [xschem getprop rect 2 $graph_selected sweep]

  set graph_divx [xschem getprop rect 2 $graph_selected divx]
  if {$graph_divx eq {}} { set graph_divx 5}
  .graphdialog.top2.divx insert 0 $graph_divx

  set graph_divy [xschem getprop rect 2 $graph_selected divy]
  if {$graph_divy eq {}} { set graph_divy 5}
  .graphdialog.top2.divy insert 0 $graph_divy

  set graph_subdivx [xschem getprop rect 2 $graph_selected subdivx]
  if {$graph_subdivx eq {}} { set graph_subdivx 1}
  .graphdialog.top2.subdivx insert 0 $graph_subdivx

  set graph_subdivy [xschem getprop rect 2 $graph_selected subdivy]
  if {$graph_subdivy eq {}} { set graph_subdivy 1}
  .graphdialog.top2.subdivy insert 0 $graph_subdivy

  set graph_unitx [xschem getprop rect 2 $graph_selected unitx]
  if {$graph_unitx eq {}} { set graph_unitx 1}
  .graphdialog.top2.unitx set $graph_unitx

  set graph_unity [xschem getprop rect 2 $graph_selected unity]
  if {$graph_unity eq {}} { set graph_unity 1}
  .graphdialog.top2.unity set $graph_unity

  set graph_mode [xschem getprop rect 2 $graph_selected mode]
  if {$graph_mode eq {}} { set graph_mode Line}
  .graphdialog.top2.mode set $graph_mode

  label .graphdialog.top5.legendlabmag -text { Legend mag:}
  entry .graphdialog.top5.legendmag -width 4
  entry_replace_selection .graphdialog.top5.legendmag
  bind .graphdialog.top5.legendmag <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected legendmag [.graphdialog.top5.legendmag get]
    xschem draw_graph $graph_selected
  }

  label .graphdialog.top5.xlabmag -text { X lab mag:}
  entry .graphdialog.top5.xmag -width 4
  entry_replace_selection .graphdialog.top5.xmag
  bind .graphdialog.top5.xmag <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected xlabmag [.graphdialog.top5.xmag get]
    xschem draw_graph $graph_selected
  } 

  label .graphdialog.top5.ylabmag -text { Y lab mag:}
  entry .graphdialog.top5.ymag -width 4
  entry_replace_selection .graphdialog.top5.ymag
  bind .graphdialog.top5.ymag <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected ylabmag [.graphdialog.top5.ymag get]
    xschem draw_graph $graph_selected
  }

  pack .graphdialog.top2.labunitx .graphdialog.top2.unitx \
       .graphdialog.top2.labunity .graphdialog.top2.unity -side left

  pack .graphdialog.top5.legend \
       .graphdialog.top5.vlegend .graphdialog.top5.legendlabmag .graphdialog.top5.legendmag \
       .graphdialog.top5.xlabmag .graphdialog.top5.xmag .graphdialog.top5.ylabmag .graphdialog.top5.ymag \
       -side left

  pack .graphdialog.top2.labdivx .graphdialog.top2.divx \
       .graphdialog.top2.labdivy .graphdialog.top2.divy \
       .graphdialog.top2.labsubdivx .graphdialog.top2.subdivx \
       .graphdialog.top2.labsubdivy .graphdialog.top2.subdivy -side left
  pack .graphdialog.top2.labmode .graphdialog.top2.mode .graphdialog.top2.labsweep -side left
  pack .graphdialog.top2.sweep -side left -fill x -expand yes

  # top frame
  checkbutton .graphdialog.top.bus -text Bus -padx 2 -variable graph_bus
  checkbutton .graphdialog.top.incr -text {Incr. sort} -variable graph_sort -indicatoron 1 \
    -command graph_fill_listbox
  checkbutton .graphdialog.top.rainbow -text {Rainbow col.} -variable graph_rainbow \
    -command {
       if { [xschem get schname] eq $graph_schname } {
         graph_push_undo
         xschem setprop -fast rect 2 $graph_selected rainbow $graph_rainbow
         xschem draw_graph $graph_selected
       }
     }
  label .graphdialog.top.lw -text "  Line width:"
  entry .graphdialog.top.lwe -width 4
  entry_replace_selection .graphdialog.top.lwe
  bind .graphdialog.top.lwe <KeyRelease> {
    graph_set_linewidth $graph_selected
    xschem draw_graph $graph_selected
  }
  set custom_lw [xschem getprop rect 2 $n linewidth_mult]
  if {[regexp {^[ \t]*$} $custom_lw]} {
    .graphdialog.top.lwe insert 0 $graph_linewidth_mult
  } else {
    .graphdialog.top.lwe insert 0 $custom_lw
  }

  label .graphdialog.top.labdset -text {  Dataset}
  entry .graphdialog.top.dset -width 4
  entry_replace_selection .graphdialog.top.dset
  bind .graphdialog.top.dset <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected dataset [.graphdialog.top.dset get]
    xschem draw_graph $graph_selected
  }
  .graphdialog.top.dset insert 0 [xschem getprop rect 2 $graph_selected dataset]

  checkbutton .graphdialog.top.priv_curs -text {Priv. Cursor} -variable graph_private_cursor \
  -command {set_rect_flags $graph_selected }

  checkbutton .graphdialog.top.unlocked -text {Unlock. X axis} -variable graph_unlocked \
  -command {set_rect_flags $graph_selected }

  checkbutton .graphdialog.top.dig -text {Digital} -variable graph_digital -indicatoron 1 \
    -command {
       if { [xschem get schname] eq $graph_schname } {
         graph_push_undo
         xschem setprop -fast rect 2 $graph_selected digital $graph_digital
         xschem draw_graph $graph_selected
       }
     }
  label .graphdialog.top3.xlabmin -text { X min:}
  entry .graphdialog.top3.xmin -width 7
  entry_replace_selection .graphdialog.top3.xmin
  bind .graphdialog.top3.xmin <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected x1 [.graphdialog.top3.xmin get]
    xschem draw_graph $graph_selected
  }
  label .graphdialog.top3.xlabmax -text { X max:}
  entry .graphdialog.top3.xmax -width 7
  entry_replace_selection .graphdialog.top3.xmax
  bind .graphdialog.top3.xmax <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected x2 [.graphdialog.top3.xmax get]
    xschem draw_graph $graph_selected
  }


  label .graphdialog.top3.ylabmin -text { Y min:}
  entry .graphdialog.top3.ymin -width 7
  entry_replace_selection .graphdialog.top3.ymin
  bind .graphdialog.top3.ymin <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected y1 [.graphdialog.top3.ymin get]
    xschem draw_graph $graph_selected
  }

  label .graphdialog.top3.ylabmax -text { Y max:}
  entry .graphdialog.top3.ymax -width 7
  entry_replace_selection .graphdialog.top3.ymax
  bind .graphdialog.top3.ymax <KeyRelease> {
    graph_push_undo
    xschem setprop rect 2 $graph_selected y2 [.graphdialog.top3.ymax get]
    xschem draw_graph $graph_selected
  }


  pack .graphdialog.top.incr -side left
  pack .graphdialog.top.bus -side left
  pack .graphdialog.top.priv_curs -side left
  pack .graphdialog.top.dig -side left
  pack .graphdialog.top.unlocked -side left
  pack .graphdialog.top.rainbow -side left
  pack .graphdialog.top.lw -side left
  pack .graphdialog.top.lwe -side left
  pack .graphdialog.top.labdset -side left
  pack .graphdialog.top.dset -side left
  .graphdialog.top3.ymin insert 0 [xschem getprop rect 2 $graph_selected y1]
  .graphdialog.top3.ymax insert 0 [xschem getprop rect 2 $graph_selected y2]
  .graphdialog.top3.xmin insert 0 [xschem getprop rect 2 $graph_selected x1]
  .graphdialog.top3.xmax insert 0 [xschem getprop rect 2 $graph_selected x2]

  set tmp [xschem getprop rect 2 $graph_selected xlabmag]
  if {$tmp eq {}} { set tmp 1.0}
  .graphdialog.top5.xmag insert 0 $tmp

  set tmp [xschem getprop rect 2 $graph_selected ylabmag]`
  if {$tmp eq {}} { set tmp 1.0}
  .graphdialog.top5.ymag insert 0 $tmp

  set tmp [xschem getprop rect 2 $graph_selected legendmag]
  if {$tmp eq {}} { set tmp 1.0}
  .graphdialog.top5.legendmag insert 0 $tmp

  # top3 frame
  set graph_rainbow [xschem getprop rect 2 $graph_selected rainbow]
  set graph_logx [xschem getprop rect 2 $graph_selected logx]
  set graph_logy [xschem getprop rect 2 $graph_selected logy]
  if { $graph_rainbow eq {} } { set graph_rainbow 0 }
  if { $graph_logx eq {} } { set graph_logx 0 }
  if { $graph_logy eq {} } { set graph_logy 0 }
  checkbutton .graphdialog.top3.logx -padx 2 -text {Log X} -variable graph_logx \
     -command {
       if { [xschem get schname] eq $graph_schname } {
         graph_push_undo
         xschem setprop -fast rect 2 $graph_selected logx $graph_logx
         if { $graph_logx eq 1} {
           graph_push_undo
           xschem setprop -fast rect 2 $graph_selected subdivx 8
           .graphdialog.top2.subdivx delete 0 end
           .graphdialog.top2.subdivx insert 0 8
           xschem setprop rect 2 $graph_selected fullxzoom
           xschem setprop rect 2 $graph_selected fullyzoom
         } else {
           graph_push_undo
           xschem setprop -fast rect 2 $graph_selected subdivx 4
           .graphdialog.top2.subdivx delete 0 end
           .graphdialog.top2.subdivx insert 0 4
           xschem setprop rect 2 $graph_selected fullxzoom
           xschem setprop rect 2 $graph_selected fullyzoom
         }
         xschem draw_graph $graph_selected
       }
     }

  checkbutton .graphdialog.top3.logy -text {Log Y} -variable graph_logy \
     -command {
       if { [xschem get schname] eq $graph_schname } {
         graph_push_undo
         xschem setprop -fast rect 2 $graph_selected logy $graph_logy
         if { $graph_logy eq 1} {
           graph_push_undo
           xschem setprop -fast rect 2 $graph_selected subdivy 8
           .graphdialog.top2.subdivy delete 0 end
           .graphdialog.top2.subdivy insert 0 8
           xschem setprop rect 2 $graph_selected fullyzoom
         } else {
           graph_push_undo
           xschem setprop -fast rect 2 $graph_selected subdivy 4
           .graphdialog.top2.subdivy delete 0 end
           .graphdialog.top2.subdivy insert 0 4
           xschem setprop rect 2 $graph_selected fullyzoom
         }
         xschem draw_graph $graph_selected
       }
     }
  pack .graphdialog.top3.logx .graphdialog.top3.logy \
   .graphdialog.top3.xlabmin .graphdialog.top3.xmin .graphdialog.top3.xlabmax .graphdialog.top3.xmax \
   .graphdialog.top3.ylabmin .graphdialog.top3.ymin .graphdialog.top3.ylabmax .graphdialog.top3.ymax \
   -fill x -expand yes -side left

  pack .graphdialog.top5.xlabmag .graphdialog.top5.xmag .graphdialog.top5.ylabmag .graphdialog.top5.ymag \
    -side left
  # binding
  bind .graphdialog.center.left.search <KeyRelease> {
    graph_fill_listbox
  }
  bind .graphdialog.center.left.list1 <Double-Button-1> {
    graph_add_nodes
    if { [xschem get schname] eq $graph_schname } {
      graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
    }
  }

  bind .graphdialog.center.right.text1 <KeyRelease> {
    if { [xschem get schname] eq $graph_schname } {
      graph_update_node [string trim [.graphdialog.center.right.text1 get 1.0 {end - 1 chars}] " \n"]
    }
  }
  bind .graphdialog <Control-Return> {
    .graphdialog.bottom.ok invoke
  }
  bind .graphdialog <Escape> {
    .graphdialog.bottom.cancel invoke
  }
  wm protocol .graphdialog  WM_DELETE_WINDOW {
    .graphdialog.bottom.cancel invoke
  }
  # fill data in left listbox
  graph_fill_listbox

  # fill data in right textbox
  set plotted_nodes [xschem getprop rect 2 $n node 2]
  if {[string length $plotted_nodes] > 0 && [string index $plotted_nodes end] ne "\n"} {append plotted_nodes \n}
  .graphdialog.center.right.text1 insert 1.0 $plotted_nodes
  graph_update_nodelist
  # add stuff in textbox at end of line + 1 char (after newline)
  # .graphdialog.center.right.text1 insert {insert lineend + 1 char} foo\n
  # tkwait window .graphdialog
  wm deiconify .graphdialog
  if {$graph_dialog_default_geometry ne {}} { wm geometry .graphdialog $graph_dialog_default_geometry}
}

proc graph_show_measure {{action show}} {
  global measure_id measure_text has_x

  if {![info exists has_x]} return;
  set_ne measure_text "y=\nx="
  if { [info exists measure_id] } {
    after cancel $measure_id
    unset measure_id
  }
  destroy .measure
  if {$action eq {stop}} { return }
  set measure_id [after 400 {
    unset measure_id
    toplevel .measure -background {}
    label .measure.lab -text $measure_text -background black -fg yellow -justify left
    pack .measure.lab
    wm overrideredirect .measure 1
    wm geometry .measure +[expr {[winfo pointerx .measure] +10}]+[expr {[winfo pointery .measure] -8}]
  }]
}

# launch a terminal shell, if 'curpath' is given set path to 'curpath'
proc get_shell { {curpath {}} } {
 global netlist_dir debug_var
 global  terminal

 regsub {/$} $netlist_dir {} netlist_dir
 set save [pwd]
 if { $curpath ne {} } {
   cd $curpath
   eval execute 0 $terminal
 } else {
   eval execute 0 $terminal
 }
 cd $save
}

proc edit_netlist {netlist } {
 global netlist_dir debug_var
 global editor terminal OS

 regsub {/$} $netlist_dir {} netlist_dir
 set netlist_type [xschem get netlist_type]

 if { [regexp vim $editor] } { set ftype "-c \":set filetype=$netlist_type\"" } else { set ftype {} }
 if { [set_netlist_dir 0] ne "" } {
   set save [pwd]
   cd $netlist_dir
   if {$OS == "Windows"} {
     set cmd "$editor \"${netlist}\""
     eval exec $cmd &
   } else {
     eval execute 0 $editor $ftype  \"${netlist}\"
   }
   cd $save
 }
 return {}
}


# 20180926
# global_initdir should be set to:
#   INITIALLOADDIR  for load
#   INITIALINSTDIR  for instance placement
# ext:  .sch or .sym or .sch.sym or .sym.sch
#
proc save_file_dialog { msg ext global_initdir {initialf {}} {overwrt 1} } {
  upvar #0 $global_initdir initdir
  set temp $initdir
  if { $initialf ne {}} {
    set initialdir [file dirname $initialf]
    set initialf [file tail $initialf]
  } else {
    set initialdir $initdir
    set initialf {}
  }
  set initdir $initialdir
  set r [load_file_dialog $msg $ext $global_initdir 0 $overwrt $initialf]
  set initdir $temp
  return $r
}

# opens indicated instance (or selected one) into a separate tab/window
# keeping the hierarchy path, as it was descended into (as with 'e' key).
proc open_sub_schematic {{inst {}} {inst_number 0}} {
  global search_schematic
  set rawfile {}
  set n_sel [xschem get lastsel]

  if { $inst eq {} && $n_sel == 0} {
    if {$search_schematic == 1} {
      set f [abs_sym_path [xschem get current_name] {.sch}]
    } else {
      set f [file rootname [xschem get schname]].sch
    }
    xschem new_schematic create {} $f
    return 1
  } elseif { $inst eq {} && $n_sel == 1} {
    set inst [lindex [xschem selected_set] 0]
    xschem unselect_all
  } else {
    set instlist {}
    # get list of instances (instance names only)
    foreach {i s t} [xschem instance_list] {lappend instlist $i}
    # if provided $inst is not in the list return 0
    if {[lsearch -exact $instlist $inst] == -1} {return 0}
  }
  # open a new top level in another window / tab
  if {[xschem raw loaded] >= 0} {
    set rawfile [xschem raw_query rawfile]
    set sim_type [xschem raw_query sim_type]
  }
  set res [xschem schematic_in_new_window force]
  # if successfull descend into indicated sub-schematic
  if {$res} {
    xschem copy_hilights
    xschem new_schematic switch [xschem get last_created_window]
    if { $rawfile ne {}} {
      if {$sim_type eq {op}} {
        xschem annotate_op $rawfile
      } else {
        xschem raw_read $rawfile $sim_type
      }
    }
    xschem select instance $inst fast
    xschem descend
    return 1
  }
  return 0
}


proc is_xschem_file {f} {
  # puts "is_xschem_file $f"
  regsub {\(.*} $f {} f ;# remove trailing generator args (gen.tcl(....))  if any
  if { ![file exists $f] } { return 0
  } elseif { [file isdirectory $f] } { return 0 }
  set a [catch {open "$f" r} fd]
  set ret 0
  set score 0
  set instances 0
  set symbol 0
  set nline 0
  set generator 0
  if {$a} {
    puts stderr "Can not open file $f"
  } else {
    fconfigure $fd -translation binary
    while { [gets $fd line] >=0 } {

      #### Can not use this. schematics may containg 8 bit extended characters
      # if {[regexp {[^[:print:][:space:]]} $line]} { ;# line contains non ascii chars
      #   close $fd
      #   return 0
      #  }

      # this is a script. not an xschem file
      if { $nline == 0 && [regexp {^#!} $line] } {
        #### too dangerous executing an arbitrary script...
        # close $fd
        # set fd [open "|$f"]
        set generator 1
        break
        # continue
      }
      if { [regexp {format *= *"@} $line] } { incr symbol; incr score}
      if { [regexp {template *= *"} $line] } { incr symbol; incr score}
      if { [regexp {^[TKFGVSE] \{} $line] } { incr score }
      if { [regexp {^[BL] +[0-9]+ +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +\{} $line] } {incr score}
      if { [regexp {^N +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +[-0-9.eE]+ +\{} $line] } {incr score}
      if { [regexp {^C +\{[^{}]+\} +[-0-9.eE]+ +[-0-9.eE]+ +[0-3]+ +[0-3]+ +\{} $line] } {
        incr instances
        incr score
        if {[regexp {ipin\.sym|opin\.sym|iopin\.sym} $line]} {set symbol 0}
      }

      if { [regexp "^v\[ \t\]+\{xschem\[ \t\]+version\[ \t\]*=.*\[ \t\]+file_version\[ \t\]*=" $line] } {
        set ret 1
      }
      if {$instances >= 4} {break}
      if {$score > 2000} {break}  ;# give up on extremely big files
      incr nline
    }
    if { $score > 4 }  { set ret 1} ;# Heuristic decision :-)
    if {$generator eq {1}} {
      set ret GENERATOR
    } elseif { $ret ne {0}} {
      if {$symbol && $instances < 4} {
        set ret SYMBOL
      } else {
        set ret SCHEMATIC
      }
    }
    close $fd
  }
  # puts "ret=$ret score=$score"
  return $ret
}

# "xschem hash_string" in scheduler.c is faster
proc hash_string {s} {
  set hash 5381
  set len [string length $s]
  for {set i 0} { $i < $len} { incr i} {
    set c [string index $s $i]
    set ascii [scan $c %c]
    set hash [expr {($hash + ($hash << 5) + $ascii)%4294967296} ]
  }
  return $hash
}

## Recent component toolbar
namespace eval c_toolbar {
  # Create a variable inside the namespace
  variable c_t
  variable i
  set c_t(w) .load.l.recent
  set c_t(hash) [xschem hash_string $::XSCHEM_LIBRARY_PATH]
  set c_t(n) 25
  set c_t(top) 0
  for {set i 0} {$i < $c_t(n)} {incr i} {
    set c_t($i,text) {}
    set c_t($i,file) {}
  }

  proc clear {} {
    variable c_t
    ## restore new recent component list...
    ### ... first delete...
    if {[info exists c_t(n)]} {
      set c_t(top) 0
      for {set i 0} {$i < $c_t(n)} {incr i} {
        set c_t($i,text) {}
        set c_t($i,file) {}
      }
    }
    # ... then reload.
    set c_t(hash) [xschem hash_string $::XSCHEM_LIBRARY_PATH]
  }

  proc cleanup {} {
      variable c_t
      if {![info exists c_t(n)]} return
      set j 0
      set n $c_t(n)
      set top $c_t(top)
      set i $top
      while {1} {
        set f [abs_sym_path $c_t($i,file)]
        if { $c_t($i,text) eq {} } {break}
        if { $j } {
          set k [expr {$i - $j}]
          if {$k < 0 } { set k [expr {$k + $n}]}
          set c_t($k,text) $c_t($i,text)
          set c_t($k,file) $c_t($i,file)
          set c_t($i,text) {}
          set c_t($i,file) {}
        }
        if {$f ne {} && ![file exists $f]} {
          incr j
        } elseif {[array names files -exact $f] ne {}} {
          incr j
        }
        set files($f) 1
        set i [expr {($i + 1) % $n} ]
        if {$i == $top} break
      }
  }

  proc command {i} {
    variable c_t
    set f $c_t($i,file)
    set file_dialog_retval {}
    if {[xschem get ui_state] & 8192 } { xschem abort_operation }
    file_dialog_display_preview $f
    xschem place_symbol $f
  }

  proc display {} {
    global has_x
    variable c_t

    if {![info exists has_x]} {return}

    if { [winfo exists $c_t(w)]} {
      set w $c_t(w)
      set n $c_t(n)
      cleanup
      destroy $w.title
      for {set i 0} {$i < $n} {incr i} {
        destroy $w.b$i
      }
      set i $c_t(top)
      button $w.title -text Recent -takefocus 0 -pady 0 -padx 0 -state disabled -disabledforeground black \
        -background grey60 -borderwidth 0 -font {TkDefaultFont 12 bold}
      pack $w.title -side top -fill x
      while {1} {
        button $w.b$i -text $c_t($i,text)  -pady 0 -padx 0 -command "c_toolbar::command $i" -takefocus 0
        pack $w.b$i -side top -fill x
        set i [expr {($i + 1) % $n}]
        if { $i == $c_t(top) } break
      }
    }
  }

  proc add {f} {
    variable c_t
    for {set i 0} {$i < $c_t(n)} {incr i} {
      if {  $c_t($i,file) eq $f } { return 0}
    }
    set ret 0
    set ret 1
    set i [expr { ($c_t(top)-1) % $c_t(n) } ];# last element
    set c_t($i,file) $f
    set c_t($i,text)  [file tail [file rootname $f]]
    set c_t(top) $i
    if {$ret} {write_recent_file}
    return $ret
  }
}
## end c_toolbar namespace

proc file_dialog_set_colors1 {} {
  global file_dialog_files1 dircolor
  for {set i 0} { $i< [.load.l.paneleft.list index end] } { incr i} {
    set maxlen 0
    set name "[lindex $file_dialog_files1 $i]"
    .load.l.paneleft.list itemconfigure $i -foreground black -selectforeground black
    foreach j [array names dircolor] {
      set pattern $j
      set color $dircolor($j)
      set len [string length [regexp -inline $pattern $name]]
      if { $len > $maxlen } {
        .load.l.paneleft.list itemconfigure $i -foreground $color -selectforeground $color
        set maxlen $len
      }
    }
  }
}

proc file_dialog_set_colors2 {} {
  global file_dialog_index1 file_dialog_files2 dircolor file_dialog_files1
  set dir1 [abs_sym_path [lindex $file_dialog_files1 $file_dialog_index1]]
  for {set i 0} { $i< [.load.l.paneright.f.list index end] } { incr i} {
    set maxlen 0
    set name "$dir1/[lindex $file_dialog_files2 $i]"
    if {[ file isdirectory $name]} {
      .load.l.paneright.f.list itemconfigure $i -foreground blue
      foreach j [array names dircolor] {
        set pattern $j
        set color $dircolor($j)
        set len [string length [regexp -inline $pattern $dir1]]
        # puts "len=$len\npattern=$pattern\nname=$name\n\n\n"
        if { $len > $maxlen } {
          .load.l.paneright.f.list itemconfigure $i -foreground $color -selectforeground $color
          set maxlen $len
        }
      }

    } else {
      .load.l.paneright.f.list itemconfigure $i -foreground black
    }
  }
}

proc file_dialog_set_names1 {} {
  global file_dialog_names1 file_dialog_files1 load_file_dialog_fullpath

  set file_dialog_names1 {}
  foreach i $file_dialog_files1 {
    if { $load_file_dialog_fullpath == 1} {
      set item $i
    } else {
      set item [get_cell $i 0]
    }
    lappend file_dialog_names1 $item
  }
}

proc file_dialog_set_home {dir} {
  global pathlist  file_dialog_files1 file_dialog_index1 file_dialog_names1

  set curr_dirname [xschem get current_dirname]
  .load.l.paneleft.list selection clear 0 end
  if { $dir eq {.}} { set dir $curr_dirname}
  # puts "set home: dir=$dir, pathlist=$pathlist"
  set pl {}
  foreach path_elem $pathlist {
    if { ![string compare $path_elem .]} {
      set path_elem $curr_dirname
    }
    lappend pl $path_elem
  }
  set i [lsearch -exact $pl $dir]
  if { $i>=0 } {
    set file_dialog_files1 $pathlist
    file_dialog_set_names1
    update
    file_dialog_set_colors1
    .load.l.paneleft.list xview moveto 1
    set file_dialog_index1 $i
    .load.l.paneleft.list selection set $file_dialog_index1
  } else {
    set file_dialog_files1 [list $dir]
    file_dialog_set_names1
    update
    file_dialog_set_colors1
    .load.l.paneleft.list xview moveto 1
    set file_dialog_index1 0
    .load.l.paneleft.list selection set 0
  }
}

proc setglob {dir} {
      global file_dialog_globfilter file_dialog_files2 OS
      # puts "setglob: $dir, filter=$file_dialog_globfilter"
      set file_dialog_files2 [lsort [glob -nocomplain -directory $dir -tails -type d .* *]]
      if { $file_dialog_globfilter eq {*}} {
        set file_dialog_files2 ${file_dialog_files2}\ [lsort [
           glob -nocomplain -directory $dir -tails -type {f} .* $file_dialog_globfilter]]
      } else {
        if {$OS == "Windows"} {
          regsub {:} $file_dialog_globfilter {\:} file_dialog_globfilter
        }

        if {![catch {glob -nocomplain -directory $dir -tails -type {f} $file_dialog_globfilter} res]} {
          set flist $res
        } else {
          set flist [glob -nocomplain -directory $dir -tails -type {f} {*}]
        }
        set file_dialog_files2 ${file_dialog_files2}\ [lsort $flist]
      }
}

proc load_file_dialog_mkdir {dir} {
  global file_dialog_dir1 has_x
  if { $dir ne {} } {
    if {[catch {file mkdir "${file_dialog_dir1}/$dir"} err]} {
      puts $err
      if {[info exists has_x]} {
        tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok
      }
    }
    setglob ${file_dialog_dir1}
    file_dialog_set_colors2
  }
}

proc load_file_dialog_up {dir} {
  global file_dialog_dir1
  bind .load.l.paneright.draw <Expose> {}
  bind .load.l.paneright.draw <Configure> {}
  .load.l.paneright.draw configure -background white
  set d [file dirname $dir]
  if { [file isdirectory $d]} {
    file_dialog_set_home $d
    setglob $d
    file_dialog_set_colors2
    set file_dialog_dir1 $d
  }
}


proc file_dialog_getresult {loadfile confirm_overwrt} {
  global file_dialog_dir1 file_dialog_retval file_dialog_ext has_x
  if { $file_dialog_retval ne {}} {
    if { [regexp {^https?://} $file_dialog_retval] } {
      set fname $file_dialog_retval
    } elseif { [regexp {^/} $file_dialog_retval]} {
      set fname $file_dialog_retval
    } else {
      regsub {/*$} $file_dialog_dir1 {/} file_dialog_dir1
      set fname "${file_dialog_dir1}${file_dialog_retval}"
    }
    if {![file exists "$fname"] } {
      return "$fname"
    }
    if { $loadfile == 0 } {
      if {[file exists "$fname"]} {
        if {$confirm_overwrt == 1 } {
          set answer [alert_ "Overwrite $fname?" {} 0 1]
        } else {
          set answer 1
        }
        if {$answer eq {1}} {
          return "$fname"
        } else {
          set file_dialog_retval {}
          return {}
        }
      }
    }
    set type [is_xschem_file "$fname"]
    if { $type eq {0}  || $type eq {GENERATOR} } {
      if { $type eq {0} } {
        set answer [alert_ "$fname does not seem to be an xschem file...\nContinue?" {} 0 1]
      } else { ;# $type == GENERATOR
        set answer 1
      }
      if { $answer eq {0}} {
        set file_dialog_retval {}
        return {}
      } else { ;# $answer == 1
        if { $type eq {GENERATOR} } {
          return "${fname}"
        }
        # $type == 0 but $answer==1 so return selected filename
        return "$fname"
      }
    # $type == SYMBOL or SCHEMATIC
    } elseif { $type ne {SYMBOL} && ($file_dialog_ext eq {*.sym}) } { ;# SCHEMATIC
      set answer [
        alert_ "$fname does not seem to be a SYMBOL file...\nContinue?" {} 0 1]
      if { $answer eq {0}} {
        set file_dialog_retval {}
        return {}
      } else {
        return "$fname"
      }
    } else { ;# SYMBOL
      return "$fname"
    }
  } else {
    return {}
  }
}

proc file_dialog_place_symbol {} {
  global file_dialog_retval

  set entry [.load.buttons_bot.entry get]
  # puts "entry=$entry"
  set file_dialog_retval  $entry
  set sym [file_dialog_getresult 2 0]
  # puts "sym=$sym"
  if { [xschem get ui_state] & 8192 } {
    xschem abort_operation
  }
  if {$sym ne {}} {
    xschem place_symbol "$sym"
  }
}

proc file_dialog_display_preview {f} {
  set type [is_xschem_file $f]
  if { $type ne {0} } {
    if { [winfo exists .load] } {
      .load.l.paneright.draw configure -background {}
      xschem preview_window draw .load.l.paneright.draw "$f"
      bind .load.l.paneright.draw <Expose> [subst {xschem preview_window draw .load.l.paneright.draw "$f"}]
      bind .load.l.paneright.draw <Configure> [subst {xschem preview_window draw .load.l.paneright.draw "$f"}]
    }
  } else {
    bind .load.l.paneright.draw <Expose> {}
    bind .load.l.paneright.draw <Configure> {}
    .load.l.paneright.draw configure -background white
  }
}

proc file_dialog_right_listboxselect {dirselect} {
    global file_dialog_yview file_dialog_dir1 file_dialog_dir2  file_dialog_retval file_dialog_sel
    global OS file_dialog_loadfile file_dialog_index1 file_dialog_files1 file_dialog_globfilter
    global file_dialog_others
    set file_dialog_yview [.load.l.paneright.f.list yview]
    set file_dialog_sel [lindex [.load.l.paneright.f.list curselection] 0]

    if { $file_dialog_sel ne {} } {
      set curr_dir [abs_sym_path [lindex $file_dialog_files1 $file_dialog_index1]]
      set curr_item [.load.l.paneright.f.list get $file_dialog_sel]

      if {$curr_item eq {..}} {
        set file_dialog_d [file dirname $curr_dir]
      } elseif {$curr_item eq {.} } {
        set file_dialog_d  $curr_dir
      } else {
        if {$OS == "Windows"} {
          if {[regexp {^[A-Za-z]\:/$} $curr_dir]} {
            set file_dialog_d "$curr_dir$curr_item"
          } else {
            set file_dialog_d "$curr_dir/$curr_item"
          }
        } else {
          if {$curr_dir eq "/"} {
            set file_dialog_d "$curr_dir$curr_item"
          } else {
            set file_dialog_d "$curr_dir/$curr_item"
          }
        }
      }

      if { !$dirselect && [file isdirectory $file_dialog_d] } {
        .load.buttons_bot.entry delete 0 end
        set file_dialog_retval  {   }
        return
      }

      set file_dialog_dir1 $curr_dir
      set file_dialog_dir2 $curr_item

      set file_dialog_others {}
      if {$file_dialog_loadfile == 1} {
        foreach i [lrange [.load.l.paneright.f.list curselection] 1 end] {
          set file_dialog_retval [.load.l.paneright.f.list get $i]
          lappend file_dialog_others [file_dialog_getresult 1 0]
        }
      }
      set file_dialog_retval {} ;# we used this variable above to communicate with file_dialog_getresult


      if { [file isdirectory $file_dialog_d]} {
        bind .load.l.paneright.draw <Expose> {}
        bind .load.l.paneright.draw <Configure> {}
        .load.l.paneright.draw configure -background white
        file_dialog_set_home $file_dialog_d
        setglob $file_dialog_d
        file_dialog_set_colors2
        set file_dialog_dir1 $file_dialog_d
      } else {
        .load.buttons_bot.entry delete 0 end
        .load.buttons_bot.entry insert 0 $file_dialog_dir2

        file_dialog_display_preview  $file_dialog_d
        # puts "xschem preview_window draw .load.l.paneright.draw \"$file_dialog_dir1/$file_dialog_dir2\""
      }
    }
    if {$file_dialog_loadfile == 2} {
      # set to something different to any file to force a new placement in file_dialog_place_symbol
      set file_dialog_retval  {   }
    }
}

proc load_additional_files {} {
  global file_dialog_others

  if {$file_dialog_others ne {} } {
    foreach i $file_dialog_others {
      xschem load_new_window $i
    }
  }
}

# global_initdir: name of global variable containing the initial directory
# loadfile: set to 0 if calling for saving instead of loading a file
#           set to 2 for non blocking operation (symbol insertion)
# confirm_overwrt: ask before overwriting an existing file
# initialf: fill the file entry box with this name (used when saving)
#
proc load_file_dialog {{msg {}} {ext {}} {global_initdir {INITIALINSTDIR}}
     {loadfile {1}} {confirm_overwrt {1}} {initialf {}}} {
  global file_dialog_index1 file_dialog_files2 file_dialog_files1 file_dialog_retval file_dialog_dir1 pathlist OS
  global file_dialog_default_geometry file_dialog_sp0 file_dialog_sp1 file_dialog_v_sp0 file_dialog_yview
  global file_dialog_names1 tcl_version file_dialog_globfilter file_dialog_dir2
  global file_dialog_save_initialfile file_dialog_loadfile file_dialog_ext

  if { [winfo exists .load] } {
    .load.buttons_bot.cancel invoke
  }
  set file_dialog_loadfile $loadfile
  if {$ext ne {}} {set file_dialog_ext $ext}
  set file_dialog_globfilter $file_dialog_ext

  set file_dialog_save_initialfile $initialf
  set file_dialog_retval {}
  upvar #0 $global_initdir initdir
  if { $loadfile != 2} {xschem set semaphore [expr {[xschem get semaphore] +1}]}
  toplevel .load -class Dialog
  wm title .load $msg
  # wm transient .load [xschem get topwindow]
  set_ne file_dialog_index1 0
  if { ![info exists file_dialog_files1]} {
    set file_dialog_files1 $pathlist
    file_dialog_set_names1
    update
    set file_dialog_index1 0
  }
  set_ne file_dialog_files2 {}
  panedwindow  .load.l -orient horizontal -height 8c
  if { $loadfile == 2} {frame .load.l.recent -takefocus 0}
  frame .load.l.paneleft -takefocus 0 -highlightcolor red -highlightthickness 2 -bg {grey90} \
    -highlightbackground [option get . background {}]
  eval [subst {listbox .load.l.paneleft.list -listvariable file_dialog_names1 -width 40 -height 12 \
    -fg black -background {grey90} -highlightthickness 0 -relief flat -borderwidth 0 \
    -yscrollcommand ".load.l.paneleft.yscroll set" -selectmode browse \
    -xscrollcommand ".load.l.paneleft.xscroll set" -exportselection 0}]
  if { ![catch {.load.l.paneleft.list cget -justify}]} {
    .load.l.paneleft.list configure -justify right
  }
  file_dialog_set_colors1
  scrollbar .load.l.paneleft.yscroll -command ".load.l.paneleft.list yview" -takefocus 0
  scrollbar .load.l.paneleft.xscroll -command ".load.l.paneleft.list xview" -orient horiz -takefocus 0
  bind .load.l.paneleft.list <<ListboxSelect>> {
    set file_dialog_sel [.load.l.paneleft.list curselection]
    if { $file_dialog_sel ne {} } {
      set file_dialog_dir1 [abs_sym_path [lindex $file_dialog_files1 $file_dialog_sel]]
      set file_dialog_index1 $file_dialog_sel

      set file_dialog_globfilter  *[.load.buttons_bot.src get]*
      if { $file_dialog_globfilter eq {**} } { set file_dialog_globfilter * }

      setglob $file_dialog_dir1
      file_dialog_set_colors2
    }
  }

  panedwindow .load.l.paneright -orient vertical
  frame .load.l.paneright.f -takefocus 0
  frame .load.l.paneright.draw -background white -height 3.8c -takefocus 0
  .load.l.paneright add .load.l.paneright.f
  .load.l.paneright add .load.l.paneright.draw -minsize 150

  if { ![catch {.load.l.paneright  panecget .load.l.paneright.f -stretch}]} {
    set optnever {-stretch never}
    set optalways {-stretch always}
  } else {
    set optnever {}
    set optalways {}
  }


  eval .load.l.paneright paneconfigure .load.l.paneright.f $optnever
  eval .load.l.paneright paneconfigure .load.l.paneright.draw $optalways

  if {$global_initdir eq {INITIALINSTDIR}} {
    set selmode browse
  } else {
    set selmode extended
  }

  listbox .load.l.paneright.f.list  -background {grey90} -listvariable file_dialog_files2 -width 20 -height 12\
    -fg black -highlightcolor red -highlightthickness 2 \
    -highlightbackground [option get . background {}] \
    -yscrollcommand ".load.l.paneright.f.yscroll set" -selectmode $selmode \
    -xscrollcommand ".load.l.paneright.f.xscroll set" -exportselection 0
  scrollbar .load.l.paneright.f.yscroll -command ".load.l.paneright.f.list yview" -takefocus 0
  scrollbar .load.l.paneright.f.xscroll -command ".load.l.paneright.f.list xview" -orient horiz -takefocus 0

  if { $loadfile == 2} {
    .load.l  add .load.l.recent
    c_toolbar::display
  }
  .load.l  add .load.l.paneleft -minsize 40
  .load.l  add .load.l.paneright -minsize 150
  eval .load.l paneconfigure .load.l.paneleft $optnever
  eval .load.l paneconfigure .load.l.paneright $optalways
  frame .load.buttons -takefocus 0
  frame .load.buttons_bot -takefocus 0
  button .load.buttons_bot.ok -width 5 -text OK -takefocus 0 -command "
    set file_dialog_retval \[.load.buttons_bot.entry get\]
    xschem preview_window destroy .load.l.paneright.draw {}
    destroy .load
    set $global_initdir \"\$file_dialog_dir1\"
  "
  button .load.buttons_bot.cancel -width 5 -text Cancel -takefocus 0 -command "
    set file_dialog_retval {}
    if {\$file_dialog_loadfile == 2} {xschem abort_operation}
    xschem preview_window destroy .load.l.paneright.draw {}
    destroy .load
    set $global_initdir \"\$file_dialog_dir1\"
  "
  wm protocol .load WM_DELETE_WINDOW {.load.buttons_bot.cancel invoke}
  button .load.buttons.home -width 5 -text {Home} -takefocus 0 -command {
    bind .load.l.paneright.draw <Expose> {}
    bind .load.l.paneright.draw <Configure> {}
    .load.l.paneright.draw configure -background white
    set file_dialog_files1 $pathlist
    file_dialog_set_names1
    update
    file_dialog_set_colors1
    .load.l.paneleft.list xview moveto 1
    set file_dialog_index1 0
    set file_dialog_dir1 [abs_sym_path [lindex $file_dialog_files1 $file_dialog_index1]]
    setglob $file_dialog_dir1
    file_dialog_set_colors2
    .load.l.paneleft.list selection clear 0 end
    .load.l.paneright.f.list selection clear 0 end
    .load.l.paneleft.list selection set $file_dialog_index1
  }
  label .load.buttons_bot.label  -text { File:}
  entry .load.buttons_bot.entry -highlightcolor red -highlightthickness 2 -takefocus 0 \
    -highlightbackground [option get . background {}]
  entry_replace_selection .load.buttons_bot.entry
  label .load.buttons_bot.srclab  -text { Search:}
  entry .load.buttons_bot.src -width 18 -highlightcolor red -highlightthickness 2 \
    -highlightbackground [option get . background {}]
  entry_replace_selection .load.buttons_bot.src
  .load.buttons_bot.src delete 0 end
  .load.buttons_bot.src insert 0 $file_dialog_globfilter
  if { $file_dialog_save_initialfile ne {} } {
    .load.buttons_bot.entry insert 0 $file_dialog_save_initialfile
  }
  bind .load.buttons_bot.src <KeyRelease> {
    if {$file_dialog_save_initialfile eq {} } {
      set file_dialog_globfilter  *[.load.buttons_bot.src get]*
      if { $file_dialog_globfilter eq {**} } { set file_dialog_globfilter * }
      setglob $file_dialog_dir1
      if {[.load.buttons_bot.entry get] ne {}} {
        .load.l.paneright.f.list yview moveto 1.0
      } else {
        if { [info exists file_dialog_yview]} {
         .load.l.paneright.f.list yview moveto  [lindex $file_dialog_yview 0]
        }
      }
    }
    # set to something different to any file to force a new placement in file_dialog_place_symbol
    set file_dialog_retval {   }
  }
  bind .load.buttons_bot.entry <ButtonPress> {
    # set to something different to any file to force a new placement in file_dialog_place_symbol
    set file_dialog_retval {   }
  }

  button .load.buttons.up -width 5 -text Up -command {load_file_dialog_up  $file_dialog_dir1} -takefocus 0
  label .load.buttons.mkdirlab -text { New dir: }
  entry .load.buttons.newdir -width 16 -takefocus 0
  entry_replace_selection .load.buttons.newdir
  button .load.buttons.mkdir -width 5 -text Create -takefocus 0 -command {
    load_file_dialog_mkdir [.load.buttons.newdir get]
  }
  button .load.buttons.rmdir -width 5 -text Delete -takefocus 0 -command {
    if { [.load.buttons.newdir get] ne {} } {
      file delete "${file_dialog_dir1}/[.load.buttons.newdir get]"
      setglob ${file_dialog_dir1}
      file_dialog_set_colors2
    }
  }
  button .load.buttons.pwd -text {Current dir} -takefocus 0 -command {load_file_dialog_up  [xschem get schname]}
  checkbutton .load.buttons.path -text {Library paths} -variable load_file_dialog_fullpath -takefocus 0 \
    -command {
      file_dialog_set_names1
      update
      file_dialog_set_colors1
      .load.l.paneleft.list xview moveto 1
    }

  pack .load.l -expand true -fill both
  pack  .load.l.paneleft.yscroll -side right -fill y
  pack  .load.l.paneleft.xscroll -side bottom -fill x
  pack  .load.l.paneleft.list -fill both -expand true -padx 12

  pack .load.buttons.home .load.buttons.up .load.buttons.pwd .load.buttons.path -side left
  pack .load.buttons.mkdirlab -side left
  pack .load.buttons.newdir -expand true -fill x -side left
  pack .load.buttons.rmdir .load.buttons.mkdir -side right
  pack .load.buttons_bot.srclab -side left
  pack .load.buttons_bot.src -side left
  pack .load.buttons_bot.label -side left
  pack .load.buttons_bot.entry -side left -fill x -expand true

  pack  .load.l.paneright.f.yscroll -side right -fill y
  pack  .load.l.paneright.f.xscroll -side bottom -fill x
  pack  .load.l.paneright.f.list -side bottom  -fill both -expand true

  pack .load.buttons_bot.cancel .load.buttons_bot.ok -side left
  pack .load.buttons_bot -side bottom -fill x
  pack .load.buttons -side bottom -fill x
  if { [info exists file_dialog_default_geometry]} {
     wm geometry .load "${file_dialog_default_geometry}"
  }
  file_dialog_set_home $initdir
  if { $loadfile != 2} {
    bind .load <Return> "
      set file_dialog_retval \[.load.buttons_bot.entry get\]
      if {\$file_dialog_retval ne {} && !\[file isdirectory \$file_dialog_retval\]} {
        bind .load.l.paneright.draw <Expose> {}
        bind .load.l.paneright.draw <Configure> {}
        xschem preview_window destroy .load.l.paneright.draw {}
        destroy .load
        set $global_initdir \"\$file_dialog_dir1\"
      }
    "
    bind .load.l.paneright.f.list <Double-Button-1> "
      set file_dialog_retval \[.load.buttons_bot.entry get\]
      if {\$file_dialog_retval ne {} && !\[file isdirectory \$file_dialog_retval\]} {
        bind .load.l.paneright.draw <Expose> {}
        bind .load.l.paneright.draw <Configure> {}
        xschem preview_window destroy .load.l.paneright.draw {}
        destroy .load
        set $global_initdir \"\$file_dialog_dir1\"
      }
    "
  }
  bind .load <Escape> "
    set file_dialog_retval {}
    if {\$file_dialog_loadfile == 2} {xschem abort_operation}
    xschem preview_window destroy .load.l.paneright.draw {}
    destroy .load
    set $global_initdir \"\$file_dialog_dir1\"
  "
  bind .load <KeyPress-H> {.load.buttons.home invoke }
  bind .load <KeyPress-U> {.load.buttons.up invoke }
  ### update

  if { [info exists file_dialog_v_sp0] } {
    eval .load.l.paneright sash mark 0 [.load.l.paneright sash coord 0]
    eval .load.l.paneright sash dragto 0 [subst {1 $file_dialog_v_sp0}]
  }

  if { [info exists file_dialog_sp0] } {
    if { $file_dialog_loadfile == 2} {
      eval .load.l sash mark 1 [.load.l sash coord 1]
      eval .load.l sash dragto 1 [subst {$file_dialog_sp0 1}]
    } else {
      eval .load.l sash mark 0 [.load.l sash coord 0]
      eval .load.l sash dragto 0 [subst {$file_dialog_sp0 1}]
    }
  }
  if { $file_dialog_loadfile == 2 && [info exists file_dialog_sp1] } {
    eval .load.l sash mark 0 [.load.l sash coord 0]
    eval .load.l sash dragto 0 [subst {$file_dialog_sp1 1}]
  }
  ### update
  .load.l.paneleft.list xview moveto 1

  bind .load <Configure> {
    set file_dialog_v_sp0 [lindex [.load.l.paneright sash coord 0] 1]
    if { $file_dialog_loadfile == 2} {
      set file_dialog_sp0 [lindex [.load.l sash coord 1] 0]
    } else {
      set file_dialog_sp0 [lindex [.load.l sash coord 0] 0]
    }
    if {$file_dialog_loadfile == 2} {
      set file_dialog_sp1 [lindex [.load.l sash coord 0] 0]
    }
    set file_dialog_default_geometry [wm geometry .load]
    .load.l.paneleft.list xview moveto 1
    # regsub {\+.*} $file_dialog_default_geometry {} file_dialog_default_geometry
  }

  bind .load.l.paneright.f.yscroll <Motion> {
    set file_dialog_yview [.load.l.paneright.f.list yview]
  }

  xschem preview_window create .load.l.paneright.draw {}
  set file_dialog_dir1 [abs_sym_path [lindex $file_dialog_files1 $file_dialog_index1]]
  setglob $file_dialog_dir1
  file_dialog_set_colors2


  if {$file_dialog_loadfile == 2} {
    bind .load <Leave> {
      if { {%W} eq {.load} && $file_dialog_retval ne {} && [.load.buttons_bot.entry get] ne $file_dialog_retval} {
        file_dialog_place_symbol
      }
    }
  }

  bind .load.l.paneright.f.list <KeyRelease-Return> {
    file_dialog_right_listboxselect 1
  }

  bind .load.l.paneright.f.list <ButtonRelease-1> {
    file_dialog_right_listboxselect 1
  }

  bind .load.l.paneright.f.list <KeyRelease-Up> {
    file_dialog_right_listboxselect 0
  }

  bind .load.l.paneright.f.list <KeyRelease-Down> {
    file_dialog_right_listboxselect 0
  }

  # bind .load.l.paneright.f.list <<ListboxSelect>> {
  #   file_dialog_right_listboxselect
  # }

  if { [info exists file_dialog_yview]} {
   .load.l.paneright.f.list yview moveto  [lindex $file_dialog_yview 0]
  }
  focus .load.buttons_bot.src
  .load.buttons_bot.src selection range 0 end
  if {$loadfile != 2} {
    tkwait window .load
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
  return [file_dialog_getresult $loadfile $confirm_overwrt]
}

# recursive procedure. Returns list of directories containing
# files by extension 'ext' from a list of supplied 'paths'
# if paths empty use XSCHEM_LIBRARY_PATH list.
# 'levels' is set to the number of levels to descend into.
# 'level' is used internally by the function and should not be set.
# incl_base_levels: if set to 1 always include level 0 dirs
proc get_list_of_dirs_with_files {{paths {}} {levels -1} \
                                  {ext {\.(sch|sym)$}}  \
                                  {level -1} \
                                  {incl_base_levels 1} \
                                 } {
  # puts "get_list_of_dirs_with_files paths=$paths"
  global pathlist
  set dir_with_symbols {}
  if {$level == -1} { set level 0}
  if {$levels >=0 && $level > $levels} {return {}}
  if {$paths eq {}} {set paths $pathlist}


  # if invalid pattern set to empty
  set err [catch {regexp $ext {12345}} res]
  if {$err} {set ext {}}
  foreach i $paths {
    # puts "dir:$i"
    set filelist [glob -nocomplain -directory $i -type f *]
    set there_are_symbols 0
    if {$incl_base_levels == 0 || $level > 0} {
      foreach f $filelist {
        if {[regexp $ext $f]} {
          # puts "match: $f"
          set there_are_symbols 1
          break
        }
      }
    }
    if {($incl_base_levels && $level == 0) || $there_are_symbols} {
      lappend dir_with_symbols $i
    }
    set dirlist [lsort -dictionary [glob -nocomplain -directory $i -type d *]]
    if {$level < $levels} {
      foreach d $dirlist {
        set dirs [get_list_of_dirs_with_files [list $d] $levels $ext [expr {$level + 1} ]]
        if { $dirs ne {}} {
          foreach dd $dirs {
            if {[lsearch $dir_with_symbols $dd] < 0} {
              lappend dir_with_symbols $dd
            }
          }
        }
      }
    }
  }
  return $dir_with_symbols
}

#######################################################################
##### new alternate file_chooser browser
#######################################################################

#### Display preview of selected symbol and start sym placement
proc file_chooser_draw_preview {f} {
  global file_chooser
  # puts "file_chooser_draw_preview"
  if {[winfo exists .ins]} {
    .ins.center.right configure -bg {}
    xschem preview_window create .ins.center.right {}
    xschem preview_window draw .ins.center.right "$f"


    ## preview window draw causes a save / restore context.
    ## restore_ctx writes XSCHEM_LIBRARY_PATH --> set_paths --> .ins.top4.upd invoke
    ## this rewrites the file_chooser(dirtails) list variable and list loses selection...
    # .ins.center.leftdir.l selection set  [.ins.center.leftdir.l index active]

    bind .ins.center.right <Expose> "xschem preview_window draw .ins.center.right {$f}"
    bind .ins.center.right <Configure> "xschem preview_window draw .ins.center.right {$f}"
    if {$file_chooser(action) eq {symbol}} {
      file_chooser_place symbol
    }
    if {$file_chooser(action) eq {symbol1}} {
      set file_chooser(action) {load}
      file_chooser_place symbol
    }
  }
}

proc file_chooser_preview {} {
  # puts "file_chooser_preview"
  global file_chooser
  if {[info exists file_chooser(f)]} {
    after cancel ".ins.center.right configure -bg white"
    after cancel "file_chooser_draw_preview {$file_chooser(f)}"
    unset file_chooser(f)
  }
  xschem preview_window close .ins.center.right {}
  bind .ins.center.right <Expose> {}
  bind .ins.center.right <Configure> {}
  set sel [lindex [.ins.center.left.l curselection] 0]
  if {$sel ne {} && [info exists file_chooser(fullpathlist)]} {
    # puts "set fileindex=$sel"
    .ins.center.left.l see $sel
    set f [lindex $file_chooser(fullpathlist) $sel]
    # puts "file_chooser_preview: f=$f"
    if {$f ne {}} {
      set file_chooser(f) $f
      set type [is_xschem_file $f]
      if {$type ne {0}} {
        set dir [rel_sym_path $f]
        set file_chooser(abs_filename) $f
        set file_chooser(rel_filename) $dir
        # global used to cancel delayed script
        after 200 "file_chooser_draw_preview {$f}"
      } else {
        after 200 {.ins.center.right configure -bg white}
      }
    }
  }
}

proc file_chooser_dirlist {} {
  # puts "file_chooser_dirlist [xschem get topwindow]"
  global file_chooser pathlist dark_gui_colorscheme new_file_browser_depth new_file_browser_ext
  if {$dark_gui_colorscheme} { set col {cyan} } else { set col {blue} }
  # regenerate list of dirs
  set file_chooser(dirs) [
    get_list_of_dirs_with_files {} $new_file_browser_depth $new_file_browser_ext
  ]
  set file_chooser(dirtails) {}
  set path_l  $pathlist
  foreach i $file_chooser(dirs) {
    set found 0
    foreach p $path_l {
      ## p = /home/schippes/.xschem/xschem_library
      ## i = /home/schippes/.xschem/xschem_library/matias_ota_5t
      ## --> t = xschem_library/matias_ota_5t
      if {[string first $p $i] == 0} {
        set pp [file dirname $p]/
        set t [substring_remove $pp $i begin]
        # puts "$p\n  $pp\n  $i\n  $t"
        if {$t ne $i} {
          lappend file_chooser(dirtails) $t
          set found 1
          break
        }
      }
    }
    if {$found == 0} {lappend file_chooser(dirtails) [file tail $i]}
  }
  set i 0
  foreach p $file_chooser(dirs) {
    # puts "--> $p"
    if {[lsearch -exact $path_l $p] != -1} {
      .ins.center.leftdir.l itemconfigure $i -foreground $col -selectforeground $col
    }
    incr i
  }
}

#### fill list of files matching pattern
proc file_chooser_filelist {} {
  global file_chooser new_file_browser_ext

  if {![info exists file_chooser(dirs)]} {return}
  set sel [lindex [.ins.center.leftdir.l curselection] 0]
  if {$sel eq {}} { return }
  set file_chooser(dirindex) $sel
  set path [lindex $file_chooser(dirs) $sel]
  set file_chooser(abs_filename) $path
  # check if regex is valid
  set regex $file_chooser(regex)
  set err [catch {regexp $regex {12345}} res]
  if {$err} {set regex {}}
  set f {}
  if {$path ne {} } {
    set f [match_file $regex [list $path] 0 $file_chooser(fullpath)]
  }
  set filelist {}
  set file_chooser(fullpathlist) {}
  set sel [lindex [.ins.center.left.l curselection] 0]
  if {$sel ne {}} {
    # puts "set fileindex=$sel"
  }
  if {$sel eq {}} { set sel 0}
  .ins.center.left.l selection clear 0 end
  .ins.center.left.l activate $sel
  .ins.center.left.l selection set $sel
  # just check if pattern is a valid regexp
  set nfbe $new_file_browser_ext
  set err [catch {regexp $nfbe {12345}} res1]
  if {$err} {set nfbe {}}
  foreach i $f {
    set fname [file tail $i]
    set err [catch {regexp $nfbe $fname} type]
    if {!$err && $type} {
      lappend filelist $fname
      lappend file_chooser(fullpathlist) $i
    }
  }
  set file_chooser(nitems) [llength $filelist]
  # assign listbox variable all at the end, it is faster...
  set file_chooser(files) $filelist
}

# called when double clicking a file item in the file_chooser file listbox
proc file_chooser_symbol_or_schematic {{shift 0}} {
  global file_chooser
  if {[xschem get semaphore] > 0} {return}
  set sel [.ins.center.left.l index active]
  if {$sel ne {}} {
    set f [lindex $file_chooser(fullpathlist) $sel]
    if {$f ne {}} {
      set type [is_xschem_file $f]
      if {$type eq {SCHEMATIC}} {
        if {$shift == 0} {
          file_chooser_place load
        } else {
          file_chooser_place load_new_win
        }
      } elseif {$type eq {SYMBOL}} {
        if {$shift == 0} {
          set file_chooser(action) {symbol1} ;# only one time insert symbol
          file_chooser_preview
        } else {
          file_chooser_place load_new_win
        }
      }
    }
  }
}

proc file_chooser_place {action} {
  # puts file_chooser_place
  global file_chooser open_in_new_window
  if {[xschem get semaphore] > 0} {return}
  set sel [.ins.center.left.l index active]
  if {$sel ne {}} {
    set f [lindex $file_chooser(fullpathlist) $sel]
    if {$f ne {}} {
      set type [is_xschem_file $f]
      # puts "file_chooser_place: file=$f, type=$type"
      if {$type ne {0}} {
        if { [xschem get ui_state] & 8192 } {
          xschem abort_operation
        }
        if {$action eq {symbol}} {
          xschem place_symbol $f
        } elseif {$action eq {load}} {
          if {$open_in_new_window} {
            xschem load_new_window $f
          } else {
            xschem load -gui $f
          }
        } elseif {$action eq {load_new_win}} {
         xschem load_new_window $f
        }
      }
    }
  }
}

proc file_chooser_select {f} {
  global file_chooser
  if {$f ne {} && [info exists file_chooser(dirs)] && [info exists file_chooser(files)]} {
    set dir [file dirname $f]
    set file [file tail $f]
    set dirtail [file tail $dir]
    # puts "file=$file"
    # puts "   dirtail=$dirtail"
    # puts "   dir=$dir"
    set dirindex [lsearch -exact  $file_chooser(dirs) $dir]
    if {$dirindex != -1} {
      # puts "dirindex=$dirindex"
      .ins.center.leftdir.l activate $dirindex
      .ins.center.leftdir.l selection clear 0 end
      .ins.center.leftdir.l selection set $dirindex
      .ins.center.leftdir.l see $dirindex
      file_chooser_filelist
      set fileindex [lsearch -exact  $file_chooser(files) $file]
      if {$fileindex != -1} {
        # puts "fileindex=$fileindex"
        .ins.center.left.l selection clear 0 end
        .ins.center.left.l activate $fileindex
        .ins.center.left.l selection set $fileindex
        .ins.center.left.l see $fileindex
        file_chooser_preview
      }
    }
  }
}

#### Display preview of selected symbol and start sym placement
proc file_chooser_search_all_draw_preview {f} {
  global file_chooser
  # puts "file_chooser_draw_preview"
  if {[winfo exists .ins]} {
    .ins.center.right configure -bg {}
    xschem preview_window create .ins.center.right {}
    xschem preview_window draw .ins.center.right "$f"
  

    ## preview window draw causes a save / restore context.
    ## restore_ctx writes XSCHEM_LIBRARY_PATH --> set_paths --> .ins.top4.upd invoke
    ## this rewrites the file_chooser(dirtails) list variable and list loses selection...
    # .ins.center.leftdir.l selection set  [.ins.center.leftdir.l index active]
  
    bind .ins.center.right <Expose> "xschem preview_window draw .ins.center.right {$f}"
    bind .ins.center.right <Configure> "xschem preview_window draw .ins.center.right {$f}"
    if {$file_chooser(action) eq {symbol}} {
      file_chooser_search_all_place symbol
    } 
    if {$file_chooser(action) eq {symbol1}} {
      set file_chooser(action) {load}
      file_chooser_search_all_place symbol
    }
  }
}

proc file_chooser_search_all_preview {} {
  # puts "file_chooser_preview"
  global file_chooser
  if {[info exists file_chooser(f)]} {
    after cancel ".ins.center.right configure -bg white"
    after cancel "file_chooser_draw_preview {$file_chooser(f)}"
    unset file_chooser(f)
  }
  xschem preview_window close .ins.center.right {}
  bind .ins.center.right <Expose> {}
  bind .ins.center.right <Configure> {}
  set sel [lindex [.searchall.c.lb curselection] 0]
  if {$sel ne {} && [info exists file_chooser(searchall)]} {
    # puts "set fileindex=$sel"
    set f [lindex $file_chooser(searchall) $sel]
    # puts "file_chooser_preview: f=$f" 
    if {$f ne {}} {
      set file_chooser(f) $f
      set type [is_xschem_file $f]
      if {$type ne {0}} {
        set dir [rel_sym_path $f]
        set file_chooser(abs_filename) $f
        set file_chooser(rel_filename) $dir
        # global used to cancel delayed script
        after 200 "file_chooser_search_all_draw_preview {$f}"
      } else {
        after 200 {.ins.center.right configure -bg white}
      }
    }
  }
}

proc file_chooser_search_all_place {action} {
  # puts file_chooser_place
  global file_chooser open_in_new_window
  if {[xschem get semaphore] > 0} {return}
  set sel [.searchall.c.lb index active]
  if {$sel ne {}} {
    set f [lindex $file_chooser(searchall) $sel]
    if {$f ne {}} {
      set type [is_xschem_file $f]
      # puts "file_chooser_place: file=$f, type=$type"
      if {$type ne {0}} {
        if { [xschem get ui_state] & 8192 } {
          xschem abort_operation
        }
        if {$action eq {symbol}} {
          xschem place_symbol $f
        } elseif {$action eq {load}} {
          if {$open_in_new_window} {
            xschem load_new_window $f
          } else {
            xschem load -gui $f
          }
        } elseif {$action eq {load_new_win}} {
         xschem load_new_window $f
        }
      }
    }
  }
}

# called when double clicking a file item in the file_chooser search all listbox
proc file_chooser_search_all_open {{shift 0}} { 
  global file_chooser
  if {[xschem get semaphore] > 0} {return}
  set sel [.searchall.c.lb index active]
  if {$sel ne {}} {
    set f [lindex $file_chooser(searchall) $sel]
    if {$f ne {}} {
      set type [is_xschem_file $f]
      if {$type eq {SCHEMATIC}} {
        if {$shift == 0} {
          file_chooser_search_all_place load
        } else {
          file_chooser_search_all_place load_new_win
        } 
      } elseif {$type eq {SYMBOL}} {
        if {$shift == 0} {
          set file_chooser(action) {symbol1} ;# only one time insert symbol
          file_chooser_search_all_preview
        } else {
          file_chooser_search_all_place load_new_win
        }
      }
    }
  }
} 

proc file_chooser_search_all {} {
  global file_chooser new_file_browser_depth new_file_browser_ext USER_CONF_DIR
  # just check if pattern is a valid regexp
  set file_chooser(searchall) {}
  set nfbe $new_file_browser_ext
  set err [catch {regexp $nfbe {12345}} res1]
  if {$err} {set nfbe {}}
  set regex $file_chooser(regex)
  # check if regex is valid
  set err [catch {regexp $regex {12345}} res1]
  if {$err} {set regex {}}
  set nth 0
  set f {}
  if {$file_chooser(dirs) ne {} } {
    set allfiles [
      match_file $regex {} $new_file_browser_depth $file_chooser(fullpath)
    ]
    puts ""
    foreach i $allfiles {
      set err [catch {regexp $nfbe $i} type]
      if {!$err && $type} {
        lappend file_chooser(searchall) $i
      }
    }
  }
  if {[winfo exists .searchall]} {return}
  toplevel .searchall
  frame .searchall.t
  frame .searchall.c
  frame .searchall.b
  listbox .searchall.c.lb -width 70 -height 28 -listvariable file_chooser(searchall) -selectmode single \
     -yscrollcommand ".searchall.c.yscroll set" \
     -xscrollcommand ".searchall.c.xscroll set"
  scrollbar .searchall.c.yscroll -command ".searchall.c.lb yview"
  scrollbar .searchall.c.xscroll -orient horiz -command ".searchall.c.lb xview"
  pack  .searchall.c.yscroll -side right -fill y
  pack  .searchall.c.xscroll -side bottom -fill x
  pack  .searchall.c.lb -side bottom  -fill both -expand true

  pack .searchall.t -fill x -expand true
  pack .searchall.c -fill both -expand true
  pack .searchall.b -fill x -expand true
  bind .searchall.c.lb <Double-Button-1> {file_chooser_search_all_open 0}
  bind .searchall.c.lb <Shift-Double-Button-1> {file_chooser_search_all_open r10}
  bind .searchall.c.lb <<ListboxSelect>> {
    if {[.searchall.c.lb curselection] ne {}} {
      if { [xschem get ui_state] & 8192 } {
        xschem abort_operation
      }
      file_chooser_search_all_preview
    }
  }

  if { [info exists file_chooser(search_all_geometry)]} {
    wm geometry .searchall "${file_chooser(search_all_geometry)}"
  } elseif {[file exists $USER_CONF_DIR/file_chooser_search_all_geometry]} {
      source $USER_CONF_DIR/file_chooser_search_all_geometry
      set valid 0
      set xmax [winfo screenwidth .]
      set ymax [winfo screenheight .]
      if {[info exists file_chooser(geometry)]} {
        set n [scan $file_chooser(search_all_geometry) {%dx%d+%d+%d} x y dx dy]
        if {$n == 4} {
          # puts "xmax=$xmax, ymax=$ymax, x=$x, y=$y dx=$dx dy=$dy"
          # off screen. do not use.
          set valid 1
          if { $dx > $xmax - 100 || $dy > $ymax - 100} {
            set valid 0
          }
        }
      }
      if {$valid} {wm geometry .searchall "${file_chooser(search_all_geometry)}"}
  } else {
    # wm geometry .searchall 800x300
  } 

  bind .searchall <Configure> {
    set file_chooser(search_all_geometry) [wm geometry .searchall]

    write_data [string cat \
      "set file_chooser(search_all_geometry) $file_chooser(search_all_geometry)\n" \
    ] $USER_CONF_DIR/file_chooser_search_all_geometry
  }



  button .searchall.b.dismiss -takefocus 0 -text Dismiss -command {
    if { [xschem get ui_state] & 8192 } {
      xschem abort_operation
    } else {
      destroy .searchall
    }
  }

  button .searchall.b.load -takefocus 0 -text {Open} -command {
    file_chooser_search_all_place load
  }
  
  button .searchall.b.load_new_win -takefocus 0 -text {Open in new Window / Tab} -command {
    file_chooser_search_all_place load_new_win 
  }
  
  button .searchall.b.sym -text {Place symbol} -takefocus 0 \
     -command {
       set file_chooser(action) {symbol1} ;# only one time insert symbol
       file_chooser_search_all_preview
     }

  pack .searchall.b.dismiss .searchall.b.load .searchall.b.load_new_win .searchall.b.sym -side left
  wm protocol .searchall WM_DELETE_WINDOW {.searchall.b.dismiss invoke}
  bind .searchall <KeyPress-Escape> {.searchall.b.dismiss invoke}
  return {}
}

proc file_chooser_search {{current {}}} {
  global file_chooser new_file_browser_depth new_file_browser_ext
  # just check if pattern is a valid regexp
  set nfbe $new_file_browser_ext
  set err [catch {regexp $nfbe {12345}} res1]
  if {$err} {set nfbe {}}
  set regex $file_chooser(regex)
  # check if regex is valid
  set err [catch {regexp $regex {12345}} res]
  if {$err} {set regex {}}
  set nth 0
  set f {}
  if {$file_chooser(dirs) ne {} } {
    set allfiles [
      match_file $regex {} $new_file_browser_depth $file_chooser(fullpath)
    ]
    foreach i $allfiles {
      set err [catch {regexp $nfbe $i} type]
      if {!$err && $type} {
        incr nth
        if {$current ne {}} {
          set dir1 [file dirname $i]
          set sel [.ins.center.leftdir.l index active]
          if {$sel ne {}} {
            set dir2 [lindex $file_chooser(dirs) $sel]
            # puts "  dir1=$dir1\n  dir2=$dir2\n  sel=$sel\n------\n"
            if {$dir2 ne {}} {
              if {$dir1 eq $dir2} {
                set f $i
                set file_chooser(nth) $nth
	        break
              }
            }
          }
        } else {
          if {$nth == $file_chooser(nth)} {
            set f $i
            break
          }
        }
      }
    }
  }
  if {$f ne {}} {
    file_chooser_select $f
  } else {
    if {$file_chooser(nth) > 0} {
      incr file_chooser(nth) -1
    } else {
      set file_chooser(nth) 1
    }
  }
}

proc file_chooser_browsedir {} {
  set pos [.editpaths.center.paths index insert]
  set row [regsub {\..*} $pos {}]
  set initialdir [.editpaths.center.paths get ${row}.0 "${row}.0 + 1 lines - 1 chars"]
  # puts $initialdir
  if {$row > 1} {
    incr row -1
  }
  # puts $row

  set dir [tk_chooseDirectory -initialdir $initialdir -parent .editpaths \
          -title {Directory to add to search paths}]
  # puts $dir

  if {$dir ne {}} {
    .editpaths.center.paths insert ${row}.0 ${dir}\n
  }
}

proc file_chooser_set_paths {} {
  global XSCHEM_LIBRARY_PATH
  if {$tctx::rcode == 1} {
    set paths $tctx::retval
    regsub -all -line {^[ \t]*\n} $paths {} paths ;# remove blank lines
    regsub -all \n $paths : paths
    set XSCHEM_LIBRARY_PATH $paths
  }
}

proc file_chooser_edit_paths {} {
  global pathlist XSCHEM_LIBRARY_PATH dark_gui_colorscheme file_chooser
  if {$dark_gui_colorscheme} { set col {cyan} } else { set col {blue} }
  set tctx::rcode 0
  if {[winfo exists .editpaths]} {
    raise .editpaths
    return
  }
  toplevel .editpaths
  frame .editpaths.top
  frame .editpaths.top2
  frame .editpaths.center
  frame .editpaths.bottom

  pack .editpaths.top -side top -fill x
  pack .editpaths.top2 -side top -fill x
  pack .editpaths.center -side top -expand 1 -fill both
  pack .editpaths.bottom -side top -fill x
  text .editpaths.center.paths -height 30 -width 80 -foreground $col
  pack .editpaths.center.paths

  button .editpaths.bottom.ok -text OK -command {
    set tctx::rcode 1
    set tctx::retval [.editpaths.center.paths get 1.0 {end - 1 chars}]
    file_chooser_set_paths
    .ins.top4.upd invoke
    destroy .editpaths
  }

  button .editpaths.bottom.apply -text Apply -command {
    set tctx::rcode 1
    set tctx::retval [.editpaths.center.paths get 1.0 {end - 1 chars}]
    file_chooser_set_paths
    .ins.top4.upd invoke
  }
  button .editpaths.bottom.dismiss -text Dismiss -command {
    destroy .editpaths
  }
  button .editpaths.bottom.browse -text {Browse directory} -command {
    file_chooser_browsedir
  }

  button .editpaths.bottom.show -text {Show resulting XSCHEM_LIBRARY_PATH} -command {
    set tctx::rcode 1
    set tctx::retval [.editpaths.center.paths get 1.0 {end - 1 chars}]
    file_chooser_set_paths
    .ins.top4.upd invoke
    if {[winfo exists .editdata]} {destroy .editdata}
    editdata "set XSCHEM_LIBRARY_PATH {$XSCHEM_LIBRARY_PATH}" {XSCHEM_LIBRARY_PATH} char 0
  }

  wm protocol .editpaths WM_DELETE_WINDOW {.editpaths.bottom.dismiss invoke}
  bind .editpaths  <KeyPress-Escape> {.editpaths.bottom.dismiss invoke}
  pack .editpaths.bottom.ok .editpaths.bottom.apply .editpaths.bottom.dismiss -side left
  pack .editpaths.bottom.browse .editpaths.bottom.show -side left

  set paths $XSCHEM_LIBRARY_PATH
  regsub {^[ :]+} $paths {} paths ;# remove leading : if present
  regsub {[ :]+$} $paths {} paths ;# remove trailing : if present
  regsub -all {:} $paths \n paths
  .editpaths.center.paths delete 1.0 end
  .editpaths.center.paths insert 1.0 $paths
}

proc file_chooser_saveas {} {
  global file_chooser

  set f $file_chooser(abs_filename)
  if {[file exists $f]} {
    if {[file isdirectory $f]} {return}
    if {[xschem get modified]} { ;# modified
      if {[xschem get schname] eq $f} { ;# file name not changed
        xschem saveas
        .ins.top2.save configure -bg [option get . background {}]
        file_chooser_filelist
      } else { ;# file name changed
        set answer [tk_messageBox -message "Warning: file $f already exists. Overwrite?" \
            -icon warning -parent .ins -type okcancel]
        if {$answer ne {ok}} { return }
        xschem saveas
        .ins.top2.save configure -bg [option get . background {}]
        file_chooser_filelist
      }
    } else { ;# not modified
      if {[xschem get schname] eq $f} { ;# file name not changed
        # ...
      } else { ;# file name changed
        set answer [tk_messageBox -message "Warning: file $f already exists. Overwrite?" \
            -icon warning -parent .ins -type okcancel]
        if {$answer ne {ok}} { return }
        xschem saveas
        .ins.top2.save configure -bg [option get . background {}]
        file_chooser_filelist
      }
    }
  } else { ;# file does not exist
    xschem saveas $f
    .ins.top2.save configure -bg [option get . background {}]
    file_chooser_filelist
  }
}

proc file_chooser_delete {} {
  global file_chooser

  set f $file_chooser(abs_filename)
  if {[file exists $f]} {
    if {[file isdirectory $f]} { return }
    set answer [tk_messageBox -message "Delete $f ?" \
        -icon warning -parent .ins -type okcancel]
    if {$answer eq {ok}} {
      file delete -force $f
      file_chooser_filelist
    }
  }
}

#### maxdepth: how many levels to descend for each $paths directory (-1: no limit)
proc file_chooser {} {
  global file_chooser new_file_browser_ext new_file_browser_depth USER_CONF_DIR
  set file_chooser(action) load
  set file_chooser(old_new_file_browser_ext) $new_file_browser_ext
  set file_chooser(old_new_file_browser_depth) $new_file_browser_depth
  set_ne file_chooser(fullpath) 0
  set_ne file_chooser(ontop) 0
  set file_chooser(enter) 1 ;# let file_chooser show current open schematic file name when mouse enters
  # xschem set semaphore [expr {[xschem get semaphore] +1}]
  if {[winfo exists .ins]} {
    raise .ins
    return
  }
  set_ne file_chooser(regex) {}
  set_ne file_chooser(nth) 1
  toplevel .ins
  wm title .ins "File Browser"
  if {$file_chooser(ontop)} {
    wm transient .ins [xschem get topwindow]
  } else {
    wm transient .ins {}
  }
  frame .ins.top -takefocus 0
  frame .ins.top2 -takefocus 0
  frame .ins.top3 -takefocus 0
  frame .ins.top4 -takefocus 0
  panedwindow  .ins.center -orient horizontal
  frame .ins.center.leftdir  -takefocus 0
  frame .ins.center.left  -takefocus 0
  frame .ins.center.right -width 50 -height 40 -bg white -takefocus 0
  .ins.center add .ins.center.leftdir
  .ins.center add .ins.center.left
  .ins.center add .ins.center.right
  frame .ins.bottom  -takefocus 0
  pack .ins.top -side top -fill x
  pack .ins.top2 -side top -fill x
  pack .ins.top3 -side top -fill x
  pack .ins.top4 -side top -fill x
  pack .ins.center -side top -expand 1 -fill both
  pack .ins.bottom -side top -fill x

  listbox .ins.center.leftdir.l -listvariable file_chooser(dirtails) -width 40 -height 5 \
    -yscrollcommand ".ins.center.leftdir.s set" -highlightcolor red -highlightthickness 2 \
    -activestyle underline -highlightbackground [option get . background {}] \
    -exportselection 0

  balloon .ins.center.leftdir.l [string cat \
    "The list of search paths is shown here\n" \
    "The directories provided in the\n" \
    "XSCHEM_LIBRARY_PATH are shown with a\n" \
    "different background.\n" \
    "Subdirectories as deep as specified in the\n" \
    "\"Levels down\" entry are shown after the\n" \
    "primary directory.\n" \
    "Only directories containing xschem files\n" \
    "matching the \"Ext\" pattern are shown."] 0 1 3000
  listbox .ins.center.left.l -listvariable file_chooser(files) -width 20 -height 5 \
    -yscrollcommand ".ins.center.left.s set" -highlightcolor red -highlightthickness 2 \
    -activestyle underline -highlightbackground [option get . background {}] \
    -exportselection 0

  balloon .ins.center.left.l [string cat \
    "List of symbols in selected directory\n" \
    "Double click on a schematic will load in current window\n" \
    "Shift-Double click on a schematic will load it in a new window\n" \
    "Double click on a symbol will place the symbol in current window\n" \
    "Shift-Double click on a symbol will load it in a new window"] 0 1 3000
  scrollbar .ins.center.leftdir.s -command ".ins.center.leftdir.l yview" -takefocus 0
  scrollbar .ins.center.left.s -command ".ins.center.left.l yview" -takefocus 0

  pack .ins.center.left.l -expand 1 -fill both -side left
  pack .ins.center.left.s -fill y -side left

  pack .ins.center.leftdir.l -expand 1 -fill both -side left
  pack .ins.center.leftdir.s -fill y -side left

  label .ins.top2.dir_l -text {Full path:} -width 12
  entry .ins.top2.dir_e -width 20 -state normal -textvariable file_chooser(abs_filename) \
    -readonlybackground [option get . background {}] -takefocus 0

  button .ins.top2.save  -takefocus 0 -text {Save current window} -command {
    set file_chooser(regex) {}
    file_chooser_select [xschem get schname]
    file_chooser_saveas
  }
  button .ins.top2.delete  -takefocus 0 -text {Delete} -command {
    file_chooser_delete
  }

  balloon .ins.top2.delete "Delete the shown file"
  label .ins.top3.pat_l -text Pattern: -width 12
  balloon .ins.top3.pat_l {Show files matching regular expression}
  entry .ins.top3.pat_e -width 30 -highlightcolor red -highlightthickness 2 \
     -textvariable file_chooser(regex) -highlightbackground [option get . background {}]
  balloon .ins.top3.pat_e {Show files matching regular expression}
  label .ins.top.dir_l -text { Symbol Reference in schematic: }
  balloon .ins.top.dir_l "This is the relative path of the symbol\nwhen instantiated in the schematic"
  entry .ins.top.dir_e -width 25 -state readonly -relief flat -textvariable file_chooser(rel_filename) \
    -readonlybackground [option get . background {}] -takefocus 0
  balloon .ins.top.dir_e "This is the relative path of the symbol\nwhen instantiated in the schematic"
  button .ins.top.editpaths -takefocus 0 -text {Edit library paths} -command {
    file_chooser_edit_paths
  }
  balloon .ins.top.editpaths [string cat \
    "Allows to edit XSCHEM_LIBRARY_PATH.\n" \
    "A hash (#) before a line will comment out\n" \
    "the path so it will not be used. You can enable it\n" \
    "later by removing the hash comment character"]
  checkbutton .ins.top.ontop  -takefocus 0 -text {Ontop   } -variable file_chooser(ontop) \
    -command {
      if { $file_chooser(ontop) } {
        wm transient .ins [xschem get topwindow]
      } else {
        wm transient .ins {}
      }
    }
  balloon .ins.top.ontop "Make the file browser window always\nshow above current xschem window"
  label .ins.top.lev_l -text {Levels down:}
  entry .ins.top.lev_e -width 3  -takefocus 0 -textvariable new_file_browser_depth
  label .ins.top3.ext_l -text {  Extension:}
  balloon .ins.top3.ext_l "Show only files matching the\nextension regular expression"
  entry .ins.top3.ext_e -width 30 -takefocus 0  -state normal -textvariable new_file_browser_ext
  balloon .ins.top3.ext_e "Show only files matching the\nextension regular expression"

  button .ins.top4.select_current -takefocus 0 -text {Select current} -command {
    set file_chooser(regex) {}
    file_chooser_select [xschem get schname]
  }
  balloon .ins.top4.select_current "Select directory and file name\nof current active xschem window"

  button .ins.top4.upd -takefocus 0 -text "Reload" -command {
    set file_chooser(abs_filename) {}
    set file_chooser(rel_filename) {}
    set file_chooser(fullpathlist) {}
    set file_chooser(files) {}
    .ins.center.left.l selection clear 0 end
    xschem preview_window close .ins.center.right {}
    .ins.center.right configure -bg white
    file_chooser_dirlist
    file_chooser_filelist
  }
  balloon .ins.top4.upd "Reset and reload list of files"

  button .ins.top4.search_curr -takefocus 0 -text {Search curr. dir.} -activebackground red -command {
    file_chooser_search current
  }
  balloon .ins.top4.search_curr "Show and select match\n in current directory"
  button .ins.top4.search_all -takefocus 0 -text {Search all} -activebackground red -command {
    file_chooser_search_all
  }
  balloon .ins.top4.search_all "Show all matches in all directories"
  button .ins.top4.search -takefocus 0 -text {Search first} -activebackground red -command {
    set file_chooser(nth) 1
    file_chooser_search
  }
  balloon .ins.top4.search "Show and select first match\nlooking in all directories"
  button .ins.top4.prev -takefocus 0 -text Prev -command {
    incr file_chooser(nth) -1
    file_chooser_search
  }
  balloon .ins.top4.prev {show and select previous match}
  button .ins.top4.next -takefocus 0 -text Next -command {
    incr file_chooser(nth)
    file_chooser_search
  }
  balloon .ins.top4.next {show and select next match}
  checkbutton .ins.top4.fullpath -takefocus 0 -variable file_chooser(fullpath) -text {match full path  }
  balloon .ins.top4.fullpath "Perform regular expression matching on\nfull path instead of only file name"
  button .ins.top4.clear -takefocus 0 -text Clear -command {
    .ins.top3.pat_e delete 0 end
    file_chooser_filelist
    file_chooser_preview
  }
  balloon .ins.top4.clear {Clear pattern entry box}
  # -command {
  #   file_chooser_search
  #   file_chooser_filelist
  #   file_chooser_preview
  # }
  bind .ins <KeyPress-Escape> {.ins.bottom.dismiss invoke}
  bind .ins <Enter> {
    if {{%W} eq {.ins} && $file_chooser(enter) } {
      # set file_chooser(regex) {}
      # file_chooser_select [xschem get schname]
      if {[xschem get modified]} {
        .ins.top2.save configure -bg red
      } else {
        .ins.top2.save configure -bg [option get . background {}]
      }
      set file_chooser(enter) 0 ;# do not reset shown filename to current xschem filename when entering again
    }
  }

  bind .ins.top3.pat_e <KeyRelease> {
    file_chooser_search current
  }
  bind .ins.center.leftdir.l <<ListboxSelect>> {
    listbox:select %W
    if {[.ins.center.leftdir.l curselection] ne {}} {
      set file_chooser(abs_filename) {}
      set file_chooser(rel_filename) {}
      set file_chooser(fullpathlist) {}
      set file_chooser(files) {}
      .ins.center.left.l selection clear 0 end
      xschem preview_window close .ins.center.right {}
      .ins.center.right configure -bg white
      file_chooser_filelist
    }
  }
  bind .ins.center.left.l <<ListboxSelect>> {
    listbox:select %W
    if {[.ins.center.left.l curselection] ne {}} {
      if { [xschem get ui_state] & 8192 } {
        xschem abort_operation
      }
      file_chooser_preview
    }
  }
  bind .ins.center.left.l <KeyPress-Return> {
    if {$file_chooser(action) eq {load}} {
      .ins.bottom.load invoke
    }
    xschem preview_window close .ins.center.right {}
    destroy .ins
  }
  label .ins.bottom.n -text { N. of items:}
  label .ins.bottom.nitems -textvariable file_chooser(nitems)
  button .ins.bottom.dismiss -takefocus 0 -text Dismiss -command {
    if {[winfo exists .searchall]} { .searchall.b.dismiss invoke }
    if {![winfo exists .editpaths]} {
      if { [xschem get ui_state] & 8192 } {
        xschem abort_operation
      } else {
        xschem preview_window close .ins.center.right {}  ;# check if regex is valid
        destroy .ins
      }
    }
  }
  button .ins.bottom.load -takefocus 0 -text {Open} -command {
    file_chooser_place load
  }

  button .ins.bottom.load_new_win -takefocus 0 -text {Open in new Window / Tab} -command {
    file_chooser_place load_new_win
  }

  button .ins.bottom.sym -text {Place symbol} -takefocus 0 \
     -command {
       set file_chooser(action) {symbol1} ;# only one time insert symbol
       file_chooser_preview
     }
  balloon .ins.bottom.sym {insert the currently selected symbol}

  checkbutton .ins.bottom.stickysym -text {Place symbol} -onvalue symbol -offvalue load -takefocus 0 \
     -variable file_chooser(action) \
     -command {
       if {$file_chooser(action) eq {symbol}} { ;# persistent insert symbol
         file_chooser_preview
       }
     }
  balloon .ins.bottom.stickysym {persistent insert symbol mode}

  bind .ins.center.left.l <Double-Button-1> {file_chooser_symbol_or_schematic 0}
  bind .ins.center.left.l <Shift-Double-Button-1> {file_chooser_symbol_or_schematic 1}

  wm protocol .ins WM_DELETE_WINDOW {.ins.bottom.dismiss invoke}
  pack .ins.bottom.dismiss -side left
  pack .ins.bottom.load -side left
  pack .ins.bottom.load_new_win -side left
  pack .ins.bottom.sym -side left
  pack .ins.bottom.stickysym -side left
  pack .ins.bottom.n -side left
  pack .ins.bottom.nitems -side left
  pack .ins.top2.dir_l -side left
  pack .ins.top2.dir_e -side left -fill x -expand 1
  pack .ins.top2.delete -side left
  pack .ins.top2.save -side left
  pack .ins.top4.select_current -side left
  pack .ins.top4.upd -side left
  pack .ins.top3.pat_l -side left
  pack .ins.top3.pat_e -side left
  pack .ins.top4.clear -side left
  pack .ins.top4.search_all -side left
  pack .ins.top4.search_curr -side left
  pack .ins.top4.search -side left
  pack .ins.top4.prev -side left
  pack .ins.top4.next -side left
  pack .ins.top4.fullpath -side left
  pack .ins.top.dir_l -side left
  pack .ins.top.dir_e -side left -fill x -expand 1
  pack .ins.top.ontop -side left
  pack .ins.top.lev_l -side left
  pack .ins.top.lev_e -side left
  pack .ins.top.editpaths -side left
  pack .ins.top3.ext_l -side left
  pack .ins.top3.ext_e -side left
  if { [info exists file_chooser(geometry)]} {
    wm geometry .ins "${file_chooser(geometry)}"
  } elseif {[file exists $USER_CONF_DIR/file_chooser_geometry]} {
      source $USER_CONF_DIR/file_chooser_geometry
      set valid 0
      set xmax [winfo screenwidth .]
      set ymax [winfo screenheight .]
      if {[info exists file_chooser(geometry)]} {
        set n [scan $file_chooser(geometry) {%dx%d+%d+%d} x y dx dy]
        if {$n == 4} {
          # puts "xmax=$xmax, ymax=$ymax, x=$x, y=$y dx=$dx dy=$dy"
          # off screen. do not use.
          set valid 1
          if { $dx > $xmax - 100 || $dy > $ymax - 100} {
            set valid 0
          }
        }
      }
      if {$valid} {wm geometry .ins "${file_chooser(geometry)}"}
  } else {
    # wm geometry .ins 800x300
  }
  .ins.center paneconfigure .ins.center.leftdir -minsize [winfo reqwidth .ins.center.leftdir]
  .ins.center paneconfigure .ins.center.left -minsize [winfo reqwidth .ins.center.left]
  update
  if { [info exists file_chooser(sp0)]} {
    .ins.center sash place 0 $file_chooser(sp0) 1
  }
  if { [info exists file_chooser(sp1)]} {
    .ins.center sash place 1 $file_chooser(sp1) 1
  }
  bind .ins <Configure> {
    # puts "sash0: [.ins.center sash coord 0]"
    # puts "sash1: [.ins.center sash coord 1]"
    set file_chooser(geometry) [wm geometry .ins]
    set file_chooser(sp0) [lindex [.ins.center sash coord 0] 0]
    set file_chooser(sp1) [lindex [.ins.center sash coord 1] 0]

    write_data [string cat \
      "set file_chooser(geometry) $file_chooser(geometry)\n" \
      "set file_chooser(sp0) $file_chooser(sp0)\n" \
      "set file_chooser(sp1) $file_chooser(sp1)\n" \
    ] $USER_CONF_DIR/file_chooser_geometry
  }
  file_chooser_dirlist
  file_chooser_filelist
  set file_chooser(old_dirs) $file_chooser(dirs)
  return {}
}
#######################################################################
##### /new alternate file_chooser browser
#######################################################################

# get last n path components: example , n=1 --> /aaa/bbb/ccc/ddd.sch -> ccc/ddd.sch
proc get_cell {s n } {
  set slist [file split $s]
  set l [llength $slist]
  if { $n >= $l } {set n [expr {$l - 1}]}
  set p {}
  for {set i [expr {$l-1-$n}]} {$i < $l} { incr i } {
    append p [lindex $slist $i]
    if {$i < $l - 1} {
      append p {/}
    }
  }
  return $p
}

# chop last n path components from s
proc path_head {s n } {
  set slist [file split $s]
  set l [llength $slist]
  if { $n < 0 } { set n 0 }
  set p {}
  for {set i 0} {$i < [expr {$l - $n}]} { incr i } {
    append p [lindex $slist $i]
    if {$i < $l -$n- 1 && ([lindex $slist $i] ne {/})} {
      append p {/}
    }
  }
  return $p
}


proc delete_files { dir } {
 if  { [info tclversion]  >=8.4} {
   set x [tk_getOpenFile -title "DELETE FILES" -multiple 1 -initialdir [file dirname $dir] ]
 } else {
   set x [tk_getOpenFile -title "DELETE FILES" -initialdir [file dirname $dir] ]
 }
 foreach i  $x {
   file delete $i
 }
}

proc create_user_xschemrc {} {
  global USER_CONF_DIR XSCHEM_SHAREDIR

  if {![file exists $USER_CONF_DIR/xschemrc]} {
    file copy $XSCHEM_SHAREDIR/xschemrc $USER_CONF_DIR/xschemrc
    puts stderr "copied system $XSCHEM_SHAREDIR/xschemrc to $USER_CONF_DIR/xschemrc"
    puts stderr "Please review the file and make your changes, then restart xschem"
  } else {
    puts stderr "$USER_CONF_DIR/xschemrc already exists, will not overwrite."
  }
}

proc create_pins {} {
  global env USER_CONF_DIR
  global filetmp

  set tctx::retval [ read_data_nonewline $filetmp ]
  regsub -all {<} $tctx::retval {[} tctx::retval
  regsub -all {>} $tctx::retval {]} tctx::retval
  set lines [split $tctx::retval \n]
  set dirprefix [file dirname [rel_sym_path [find_file_first ipin.sym]]]
  if {$dirprefix == {.}} { set dirprefix {}} else {append dirprefix {/}}

  # viewdata $tctx::retval
  set pcnt 0
  set y 0
  set fd [open $USER_CONF_DIR/.clipboard.sch w]
  foreach i $lines {
    puts $fd "C \{${dirprefix}[lindex $i 1].sym\} 0 [set y [expr {$y-20}]] \
                0 0 \{ name=p[incr pcnt] lab=[lindex $i 0] \}"
  }
  close $fd
  xschem merge $USER_CONF_DIR/.clipboard.sch
}

proc rectorder {x1 y1 x2 y2} {
  if {$x2 < $x1} {set tmp $x1; set x1 $x2; set x2 $tmp}
  if {$y2 < $y1} {set tmp $y1; set y1 $y2; set y2 $tmp}
  return [list $x1 $y1 $x2 $y2]
}

proc order {x1 y1 x2 y2} {
  if {$x2 < $x1} {set tmp $x1; set x1 $x2; set x2 $tmp; set tmp $y1; set y1 $y2; set y2 $tmp
  } elseif {$x2==$x1 && $y2<$y1} {set tmp $y1; set y1 $y2; set y2 $tmp}
  return [list $x1 $y1 $x2 $y2]
}

proc rotation {x0 y0 x y rot flip} {
  set tmp [expr {$flip? 2*$x0-$x : $x}]
  if {$rot==0} {set rx $tmp; set ry $y }
  if {$rot==1} {set rx [expr {$x0 - $y +$y0}]; set ry [expr {$y0+$tmp-$x0}]}
  if {$rot==2} {set rx [expr {2*$x0-$tmp}]; set ry [expr {2*$y0-$y}]}
  if {$rot==3} {set rx [expr {$x0+$y-$y0}]; set ry [expr {$y0-$tmp+$x0}]}
  return [list $rx $ry]
}

proc schpins_to_sympins {} {
  global env USER_CONF_DIR
  set pinhsize 2.5
  set first 1
  xschem copy
  set clipboard [read_data_nonewline $USER_CONF_DIR/.clipboard.sch]
  set lines [split $clipboard \n]
  set fd [open $USER_CONF_DIR/.clipboard.sch w]
  foreach i $lines {
    set ii [split [regexp -all -inline {\S+} $i]]
    if {[regexp {^C \{.*(i|o|io)pin} $i ]} {
      if {[regexp {ipin} [lindex $ii 1]]} { set dir in }
      if {[regexp {opin} [lindex $ii 1]]} { set dir out }
      if {[regexp {iopin} [lindex $ii 1]]} { set dir inout }
      set rot [lindex $ii 4]
      set flip [lindex $ii 5]
      while {1} {
        if { [regexp {lab=} $i] } {
          regsub {^.*lab=} $i {} lab
          regsub {[\} ].*} $lab {} lab
        }
        if { [regexp {\}} $i]} { break}
      }
      set x0 [lindex $ii 2]
      set y0 [lindex $ii 3]
      if {$first} {
        puts $fd "G {$x0 $y0 }"
        set first 0
      }
      set pinx1 [expr {$x0-$pinhsize}]
      set pinx2 [expr {$x0+$pinhsize}]
      set piny1 [expr {$y0-$pinhsize}]
      set piny2 [expr {$y0+$pinhsize}]
      if {![string compare $dir  "out"] || ![string compare $dir "inout"] } {
        set linex1 [expr {$x0-20}]
        set liney1 $y0
        set linex2 $x0
        set liney2 $y0
        set textx0 [expr {$x0-25}]
        set texty0 [expr {$y0-4}]
        set textflip [expr {!$flip}]
      } else {
        set linex1 [expr {$x0+20}]
        set liney1 $y0
        set linex2 $x0
        set liney2 $y0
        set textx0 [expr {$x0+25}]
        set texty0 [expr {$y0-4}]
        set textflip [expr {$flip}]
      }
      ## lassign not available pre-tck8.5
      lassign [rotation $x0 $y0 $linex1 $liney1 $rot $flip] linex1 liney1
      lassign [rotation $x0 $y0 $linex2 $liney2 $rot $flip] linex2 liney2
      lassign [order $linex1 $liney1 $linex2 $liney2] linex1 liney1 linex2 liney2
      lassign [rotation $x0 $y0 $textx0 $texty0 $rot $flip] textx0 texty0
      foreach {textx0 texty0} [rotation $x0 $y0 $textx0 $texty0 $rot $flip] break
      puts $fd "B 5 $pinx1 $piny1 $pinx2 $piny2 \{name=$lab dir=$dir\}"
      puts $fd "L 4 $linex1 $liney1 $linex2 $liney2 \{\}"
      puts $fd "T \{$lab\} $textx0 $texty0 $rot $textflip 0.2 0.2 \{\}"
    }
  }
  close $fd
  xschem paste
}


proc add_lab_no_prefix {} {
  global env USER_CONF_DIR
  global filetmp

  set dirprefix [file dirname [rel_sym_path [find_file_first ipin.sym]]]
  if {$dirprefix == {.}} { set dirprefix {}} else {append dirprefix {/}}
  set tctx::retval [ read_data_nonewline $filetmp ]
  regsub -all {<} $tctx::retval {[} tctx::retval
  regsub -all {>} $tctx::retval {]} tctx::retval
  set lines [split $tctx::retval \n]
  # viewdata $tctx::retval
  set pcnt 0
  set y 0
  set fd [open $USER_CONF_DIR/.clipboard.sch w]
  foreach i $lines {
    puts $fd "C \{${dirprefix}lab_pin.sym\} 0 [set y [expr {$y+20}]] \
               0 0 \{ name=p[incr pcnt] verilog_type=wire lab=[lindex $i 0] \}"
  }
  close $fd
  xschem merge $USER_CONF_DIR/.clipboard.sch
}

proc add_lab_prefix {} {
  global env USER_CONF_DIR
  global filetmp

  set dirprefix [file dirname [rel_sym_path [find_file_first ipin.sym]]]
  if {$dirprefix == {.}} { set dirprefix {}} else {append dirprefix {/}}
  set tctx::retval [ read_data_nonewline $filetmp ]
  regsub -all {<} $tctx::retval {[} tctx::retval
  regsub -all {>} $tctx::retval {]} tctx::retval
  set lines [split $tctx::retval \n]
  # viewdata $tctx::retval
  set pcnt 0
  set y 0
  set fd [open $USER_CONF_DIR/.clipboard.sch w]
  foreach i $lines {
    puts $fd "C \{${dirprefix}lab_pin.sym\} 0 [set y [expr {$y+20}]] \
               0 0 \{ name=p[incr pcnt] verilog_type=reg lab=i[lindex $i 0] \}"
  }
  close $fd
  xschem merge $USER_CONF_DIR/.clipboard.sch
}

# Create an xschem symbol from a list of in, out, inout pins.
# Example:
#   create_symbol test.sym {CLK RST D} {Q QB} {VCC VSS}
#
proc create_symbol {name {in {}} {out {}} {inout {}}} {
  set symname [file rootname $name].sym
  set res [catch {open $symname {WRONLY CREAT EXCL}} fd]
  if {$res} {puts $fd; return 0} ;# Error. Print reason and exit.
  puts $fd {v {xschem version=3.4.8RC file_version=1.3}}
  puts $fd {K {type=subcircuit format="@name @pinlist @symname" template="name=X1"}}
  set x -150
  set y 0
  foreach pin $in { ;# create all input pins on the left
    puts $fd "B 5 [expr {$x - 2.5}] [expr {$y - 2.5}] [expr {$x + 2.5}] [expr {$y + 2.5}] {name=$pin dir=in}"
    puts $fd "T {$pin} [expr {$x + 25}] [expr {$y - 6}] 0 0 0.2 0.2 {}"
    puts $fd "L 4 $x $y [expr {$x + 20}] $y {}"
    incr y 20
  }
  set x 150
  set y 0
  foreach pin $out { ;# create all out pins on the top right
    puts $fd "B 5 [expr {$x - 2.5}] [expr {$y - 2.5}] [expr {$x + 2.5}] [expr {$y + 2.5}] {name=$pin dir=out}"
    puts $fd "T {$pin} [expr {$x - 25}] [expr {$y - 6}] 0 1 0.2 0.2 {}"
    puts $fd "L 4 [expr {$x - 20}] $y $x $y {}"
    incr y 20
  }
  foreach pin $inout { ;# create all inout pins on the bottom right
    puts $fd "B 5 [expr {$x - 2.5}] [expr {$y - 2.5}] [expr {$x + 2.5}] [expr {$y + 2.5}] {name=$pin dir=inout}"
    puts $fd "T {$pin} [expr {$x - 25}] [expr {$y - 6}] 0 1 0.2 0.2 {}"
    puts $fd "L 7 [expr {$x - 20}] $y $x $y {}"
    incr y 20
  }
  puts $fd "B 4 -130 -10 130 [expr {$y - 10}] {fill=0}" ;# symbol box
  puts $fd "T {@symname} 0 [expr {($y - 20) / 2}] 0 0 0.3 0.3 {hcenter=1 vcenter=1}" ;#symbol name
  puts $fd "T {@name} 130 -10 2 1 0.2 0.2 {}" ;# instance name
  close $fd
  puts "Created symbol ${symname}"
  return 1
}

proc make_symbol {name {ask {no}} } {
  global XSCHEM_SHAREDIR symbol_width
  set name [abs_sym_path $name ]
  set symname [abs_sym_path $name .sym]
  if { $ask eq {no} && [file exists $symname] } {
    set answer [tk_messageBox -message "Warning: symbol $symname already exists. Overwrite?" \
        -icon warning -parent [xschem get topwindow] -type okcancel]
    if {$answer ne {ok}} { return {}}

  }
  # puts "make_symbol{}, executing: ${XSCHEM_SHAREDIR}/make_sym.awk $symbol_width ${name}"
  eval exec {awk -f ${XSCHEM_SHAREDIR}/make_sym.awk $symbol_width $name}
  return {}
}

proc make_symbol_lcc {name} {
  global XSCHEM_SHAREDIR
  set name [abs_sym_path $name]
  # puts "make_symbol{}, executing: ${XSCHEM_SHAREDIR}/make_sym_lcc.awk ${name}"
  eval exec {awk -f ${XSCHEM_SHAREDIR}/make_sym_lcc.awk $name}
  return {}
}

# point netlist_dir to  simulation dir 'simulation/' under current schematic directory
proc simuldir {} {
  global netlist_dir local_netlist_dir has_x
  if { $local_netlist_dir == 1 } {
    set simdir [xschem get current_dirname]/simulation
    set netlist_dir $simdir
    regsub {/$} $netlist_dir {} netlist_dir
    return $netlist_dir
  }
  if { $local_netlist_dir == 2 } {
    set simdir [xschem get current_dirname]/simulation/[get_cell [xschem get current_name] 0]
    set netlist_dir $simdir
    regsub {/$} $netlist_dir {} netlist_dir
    return $netlist_dir
  }
  return {}
}

#
# what==0: force creation of $netlist_dir (if netlist_dir variable not empty)
#           and return current setting.
#
# what==1: if no dir given prompt user
#           else set netlist_dir to dir
#
# what==2: just set netlist_dir according to local_netlist_dir setting
#
# Return current netlist directory
#
proc set_netlist_dir { what {dir {} }} {
  global netlist_dir env OS has_x local_netlist_dir USER_CONF_DIR

  if { ![info exists netlist_dir] } {
    set netlist_dir {}
  }
  #### set local-to-schematic-dir if local_netlist_dir tcl var is set
  simuldir
  regsub {/$} $netlist_dir {} netlist_dir
  if {$what == 2} { return $netlist_dir }

  #### what == 0
  if {$what == 0} {
    if {$netlist_dir eq {}} { set netlist_dir "$USER_CONF_DIR/simulations" }
    if {![file exist $netlist_dir]} {
      if {[catch {file mkdir "$netlist_dir"} err]} {
        puts stderr $err
        if {[info exists has_x]} {
          tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok
        }
      }
    }
  #### what == 1
  } else {
    # if local_netlist_dir is set can not provide a dir, set dir to netlist_dir as set by proc simuldir
    if {$local_netlist_dir != 0} {
      set dir $netlist_dir
    }
    if { $dir eq {} } {
      # set to default simulations/ directory.
      if {$local_netlist_dir == 0  && $netlist_dir eq {}} {
        set initdir "$USER_CONF_DIR/simulations"
      } elseif {$local_netlist_dir == 0 } {
        set initdir $netlist_dir
      } elseif { $netlist_dir ne {} }  {
        set initdir $netlist_dir
      } else {
        if {$OS == "Windows"} {
          set initdir  $env(windir)
        } else {
          set initdir  [pwd]
        }
      }
      # prompt user for a dir
      set new_dir [tk_chooseDirectory -initialdir $initdir \
         -parent [xschem get topwindow] -title {Select netlist DIR} -mustexist false]
    } else {
      # use provided dir argument
      set new_dir $dir
    }

    # create new dir if not already existing
    if {$new_dir ne {} } {
      if {![file exist $new_dir]} {
        if {[catch {file mkdir "$new_dir"} err]} {
          puts stderr $err
          if {[info exists has_x]} {
            tk_messageBox -message "$err" -icon error -parent [xschem get topwindow] -type ok
          }
        }

      }
      set netlist_dir $new_dir
    }
  } ;# what == 1
  regsub {^~/} $netlist_dir ${env(HOME)}/ netlist_dir
  regsub {/$} $netlist_dir {} netlist_dir
  # return $netlist_dir if valid and existing, else return empty string
  if {$netlist_dir ne {} && [file exists $netlist_dir]} {
    return $netlist_dir
  }
  return {}
}


proc enter_text {textlabel {preserve_disabled disabled}} {
  global has_cairo preserve_unchanged_attrs wm_fix props enter_text_default_geometry
  global text_tabs_setting tabstop
  set tctx::rcode {}
  toplevel .dialog -class Dialog
  wm title .dialog {Enter text}
  wm transient .dialog [xschem get topwindow]

  set X [expr {[winfo pointerx .dialog] - 30}]
  set Y [expr {[winfo pointery .dialog] - 25}]

  bind .dialog <Configure> {
    set enter_text_default_geometry [wm geometry .dialog]
    regsub {\+.*} $enter_text_default_geometry {} enter_text_default_geometry
  }

  # 20100203
  if { $wm_fix } { tkwait visibility .dialog }
  wm geometry .dialog "${enter_text_default_geometry}+$X+$Y"
  frame .dialog.f1
  frame .dialog.f2
  label .dialog.f1.txtlab -text $textlabel
  eval text .dialog.f2.txt -undo 1 -width 90 -height 9 $text_tabs_setting -yscrollcommand \".dialog.f2.yscroll set\"
  scrollbar .dialog.f2.yscroll -command  ".dialog.f2.txt yview"

  .dialog.f2.txt delete 1.0 end
  .dialog.f2.txt insert 1.0 $tctx::retval
  checkbutton .dialog.f1.l1 -text "preserve unchanged props" -variable preserve_unchanged_attrs \
     -state $preserve_disabled
  pack .dialog.f1 -side top -fill x ;# -expand yes
  pack .dialog.f2 -side top -fill both -expand yes
  pack .dialog.f1.l1 -side left
  pack .dialog.f1.txtlab -side left -expand yes -fill x

  pack .dialog.f2.txt -side left -fill both -expand yes
  pack .dialog.f2.yscroll -side left -expand no -fill y
  frame .dialog.edit
  frame .dialog.edit.hsize
  frame .dialog.edit.vsize
  frame .dialog.edit.props
  pack  .dialog.edit.hsize -side bottom -expand yes -fill x
  pack  .dialog.edit.vsize -side bottom -expand yes -fill x
  pack  .dialog.edit.props -side bottom -expand yes -fill x
  pack .dialog.edit  -side top  -fill x
  if {$has_cairo } {
    entry .dialog.edit.hsize.hsize -relief sunken -textvariable tctx::vsize -width 20
    entry_replace_selection .dialog.edit.hsize.hsize
  } else {
    entry .dialog.edit.hsize.hsize -relief sunken -textvariable tctx::hsize -width 20
    entry_replace_selection .dialog.edit.hsize.hsize
  }
  entry .dialog.edit.vsize.vsize -relief sunken -textvariable tctx::vsize -width 20
  entry_replace_selection .dialog.edit.vsize.vsize
  text .dialog.edit.props.props -undo 1 -width 70 -height 3
  .dialog.edit.props.props insert 1.0 $props
  label .dialog.edit.hsize.hlab -text "hsize:"
  label .dialog.edit.vsize.vlab -text "vsize:"
  label .dialog.edit.props.proplab -text "props:"
  pack .dialog.edit.hsize.hlab -side left
  pack .dialog.edit.hsize.hsize -side left -fill x -expand yes
  pack .dialog.edit.vsize.vlab  -side left
  pack .dialog.edit.vsize.vsize -side left -fill x -expand yes
  pack .dialog.edit.props.proplab -side left
  pack .dialog.edit.props.props -side left -fill x -expand yes
  frame .dialog.buttons
  button .dialog.buttons.ok -text "OK" -command  \
  {
   set props [.dialog.edit.props.props get 1.0 {end - 1 chars}]
   set tctx::retval [.dialog.f2.txt get 1.0 {end - 1 chars}]
   if {$has_cairo} {
     set tctx::hsize $tctx::vsize
   }
   set tctx::rcode {ok}
   destroy .dialog
  }
  button .dialog.buttons.cancel -text "Cancel" -command  \
  {
   set tctx::retval {}
   set tctx::rcode {}
   destroy .dialog
  }
  button .dialog.buttons.b3 -text "Load" -command \
  {
    global INITIALTEXTDIR
    if { ![info exists INITIALTEXTDIR] } { set INITIALTEXTDIR [xschem get current_dirname] }
    set a [tk_getOpenFile -parent .dialog -initialdir $INITIALTEXTDIR ]
    if [string compare $a ""] {
     set INITIALTEXTDIR [file dirname $a]
     read_data_window  .dialog.f2.txt  $a
    }
  }
  button .dialog.buttons.b4 -text "Del" -command \
  {
    .dialog.f2.txt delete 1.0 end
  }
  pack .dialog.buttons.ok  -side left -fill x -expand yes
  pack .dialog.buttons.cancel  -side left -fill x -expand yes
  pack .dialog.buttons.b3  -side left -fill x -expand yes
  pack .dialog.buttons.b4  -side left -fill x -expand yes
  pack .dialog.buttons -side bottom -fill x
  bind .dialog <Escape> {
    if ![string compare $tctx::retval [.dialog.f2.txt get 1.0 {end - 1 chars}]] {
      .dialog.buttons.cancel invoke
    }
  }
  bind .dialog.f2.txt <Shift-KeyRelease-Return> {return_release %W; .dialog.buttons.ok invoke}
  .dialog.f2.txt tag add sel 1.0 {end - 1 chars}
  .dialog.f2.txt mark set insert 1.0
  focus .dialog.f2.txt
  #grab set .dialog
  tkwait window .dialog
  return $tctx::retval
}

# will redefine puts to output into tclcmd_puts
proc redef_puts {} {
  global tclcmd_puts
  set tclcmd_puts {}
  if ![llength [info command ::tcl::puts]] {
    rename puts ::tcl::puts
    proc puts args {
      # ::tcl::puts "puts: args=$args"
      global tclcmd_puts
      set la [llength $args]
      if {$la<1 || $la>3} {
        error "usage: puts ?-nonewline? ?channel? string"
      }
      set nl \n
      if {[lindex $args 0]=="-nonewline"} {
        set nl ""
        set args [lrange $args 1 end]
      }
      if {[llength $args]==1} {
        set args [list stdout [join $args]] ;# (2)
      }
      foreach {channel s} $args break
      #set s [join $s] ;# (1) prevent braces at leading/tailing spaces
      if {$channel=="stdout" || $channel=="stderr"} {
        append tclcmd_puts $s$nl
      } else {
        set cmd ::tcl::puts
        if {$nl==""} {lappend cmd -nonewline}
        lappend cmd $channel $s
        eval $cmd
      }
      return {}
    };# puts
  }
  return {}
}

# return key release, used to remove last entered character
# when binding close text-widget window to Shift-return or Control-return.
proc return_release {window} {
  set curs [$window index insert]
  $window delete "$curs - 1 chars" $curs
}

proc tclcmd_ok_button {} {
  global tclcmd_txt tclcmd_puts

  set tclcmd_txt [.tclcmd.t.t get 1.0 end]
  regsub -all -line {^ *= *} $tclcmd_txt {= } tclcmd_txt
  redef_puts
  catch {uplevel #0 $tclcmd_txt} tclcmd_cmd
  rename puts {}
  rename ::tcl::puts puts
  if {$tclcmd_cmd ne {}} {
    if {$tclcmd_puts ne {}} {
      if {[string index $tclcmd_puts end] != "\n"} {
        append tclcmd_puts \n $tclcmd_cmd
      } else {
        append tclcmd_puts $tclcmd_cmd
      }
    } else {
      set tclcmd_puts $tclcmd_cmd
    }
  }
  if {$tclcmd_puts != {} && [string index $tclcmd_puts end] != "\n"} {
    append tclcmd_puts "\n"
  }
  .tclcmd.r.r insert end $tclcmd_puts
  .tclcmd.r.r yview moveto 1
}

# evaluate a tcl command from GUI
proc tclcmd {} {
  global tclcmd_txt tclcmd_default_geometry
  set_ne tclcmd_default_geometry {}
  if {[winfo exists .tclcmd]} {
    destroy .tclcmd
  }
  toplevel .tclcmd -class dialog
  label .tclcmd.txtlab -text {Enter TCL expression. Shift-Return will evaluate}
  panedwindow .tclcmd.p -orient vert

  bind .tclcmd <Configure> {
    set tclcmd_default_geometry [wm geometry .tclcmd]
  }

  frame .tclcmd.t
  text .tclcmd.t.t -width 60 -height 4 -font {Monospace 10} -yscrollcommand ".tclcmd.t.yscroll set"
  scrollbar .tclcmd.t.yscroll -command  ".tclcmd.t.t yview"

  frame .tclcmd.r
  text .tclcmd.r.r -width 60 -height 5 -font {Monospace 10} -yscrollcommand ".tclcmd.r.yscroll set"
  scrollbar .tclcmd.r.yscroll -command  ".tclcmd.r.r yview"

  .tclcmd.p add .tclcmd.t .tclcmd.r
  .tclcmd.t.t insert 1.0 $tclcmd_txt

  frame .tclcmd.b
  button .tclcmd.b.cleari -text {Clear Input} -command {
    .tclcmd.t.t delete 1.0 end
  }
  button .tclcmd.b.clear -text {Clear Results} -command {
    .tclcmd.r.r delete 1.0 end
  }
  button .tclcmd.b.close -text Close -command {
    set tclcmd_txt [.tclcmd.t.t get 1.0 {end - 1 chars}]
    destroy .tclcmd
  }
  button .tclcmd.b.ok -text Evaluate -command {tclcmd_ok_button}
  # bind .tclcmd.t <Shift-KeyPress-Return> { .tclcmd.b.ok invoke }
  bind .tclcmd.t.t <Shift-KeyRelease-Return> {return_release %W; .tclcmd.b.ok invoke }
  bind .tclcmd.t.t <Shift-Delete> {
    .tclcmd.r.r delete 1.0 end
  }
  bind .tclcmd <Escape> {.tclcmd.b.close invoke}
  pack .tclcmd.txtlab -side top -fill x
  pack .tclcmd.b -side bottom -fill x
  pack .tclcmd.p -side top -fill  both -expand yes
  pack .tclcmd.r.yscroll -side right -fill y
  pack .tclcmd.r.r -side top -fill  both -expand yes
  pack .tclcmd.t.yscroll -side right -fill y
  pack .tclcmd.t.t -side top -fill  both -expand yes
  pack .tclcmd.b.ok -side left -expand yes -fill x
  pack .tclcmd.b.clear -side left -expand yes -fill x
  pack .tclcmd.b.cleari -side left -expand yes -fill x
  pack .tclcmd.b.close -side left -expand yes -fill x
  if { $tclcmd_default_geometry ne {}} {wm geometry .tclcmd $tclcmd_default_geometry}
}

proc select_layers {} {
  global dark_colorscheme enable_layer
  # xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel .sl -class Dialog
  wm transient .sl [xschem get topwindow]
  if { $dark_colorscheme == 1 } {
    set txt_color black
  } else {
    set txt_color white
  }
  set j 0
  set f 0
  frame .sl.f0
  frame .sl.f1
  pack .sl.f0 .sl.f1 -side top -fill x
  button .sl.f1.ok -text OK -command { destroy .sl}
  pack .sl.f1.ok -side left -expand yes -fill x
  frame .sl.f0.f$f
  pack .sl.f0.f$f -side left -fill y
  foreach i $tctx::colors {
    if { $dark_colorscheme == 1 } {
      set ind_bg white
    } else {
      set ind_bg black
    }
    if {  $j == [xschem get pinlayer] } {
      set laylab [format %2d $j]-PIN
      set layfg $txt_color
    } elseif { $j == [xschem get wirelayer] } {
      set laylab [format %2d $j]-WIRE
      set layfg $txt_color
    } elseif { $j == [xschem get textlayer] } {
      set laylab [format %2d $j]-TEXT
      set layfg $txt_color
    } elseif { $j == [xschem get backlayer] } {
      set laylab [format %2d $j]-BG
      if { $dark_colorscheme == 1 } {
        set layfg white
        set ind_bg black
      } else {
        set layfg black
        set ind_bg white
      }
    } elseif { $j == [xschem get gridlayer] } {
      set laylab [format %2d $j]-GRID
      set layfg $txt_color
    } else {
      set laylab "[format %2d $j]        "
      set layfg $txt_color
    }

    checkbutton .sl.f0.f$f.cb$j -text $laylab  -variable enable_layer($j) -activeforeground $layfg \
       -selectcolor $ind_bg -anchor w -foreground $layfg -background $i -activebackground $i \
       -command {
           xschem enable_layers
           xschem redraw
        }
    pack .sl.f0.f$f.cb$j -side top -fill x
    incr j
    if { [expr {$j%10}] == 0 } {
      incr f
      frame .sl.f0.f$f
      pack .sl.f0.f$f -side left  -fill y
    }
  }
  # tkwait window .sl
  # xschem set semaphore [expr {[xschem get semaphore] -1}]
}

proc color_dim {} {
  global dim_bg dim_value enable_dim_bg
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel .dim -class Dialog
  wm title .dim {Dim colors}
  wm transient .dim [xschem get topwindow]
  checkbutton .dim.bg -text {Dim background} -variable enable_dim_bg
  # xschem color_dim <scale value> sets also dim_value variable
  scale .dim.scale -digits 2 -label {Dim factor} -length 256 \
     -showvalue 1 -command {xschem color_dim} -orient horizontal \
     -from -5 -to 5 -resolution 0.1
  button .dim.ok -text OK -command {destroy .dim}
  .dim.scale set $dim_value
  pack .dim.scale
  pack .dim.bg -side left
  pack .dim.ok -side right -anchor e
  tkwait window .dim
  xschem set semaphore [expr {[xschem get semaphore] -1}]
}

# show xschem about dialog
proc about {} {
  global OS running_in_src_dir
  if [winfo exists .about] {
    bind .about.link <Button-1> {}
    bind .about.link2 <Button-1> {}
    destroy .about
  }
  toplevel .about -class Dialog
  wm title .about {About XSCHEM}
  wm transient .about [xschem get topwindow]
  label .about.xschem -text "XSCHEM V[xschem get version]" -font {Sans 24 bold}
  label .about.descr -text "Schematic editor / netlister for VHDL, Verilog, SPICE, tEDAx"
  button .about.link -text {http://repo.hu/projects/xschem} -font Underline-Font -fg blue -relief flat
  button .about.link2 -text {https://github.com/StefanSchippers/xschem} -font Underline-Font -fg blue -relief flat
  button .about.link3 -text {Online XSCHEM Manual} -font Underline-Font -fg blue -relief flat
  button .about.link4 -text {Local XSCHEM Manual} -font Underline-Font -fg blue -relief flat
  label .about.copyright -text "\n Copyright (C) 1998-2024 Stefan Schippers (stefan.schippers@gmail.com) \n
 This is free software; see the source for copying conditions.  There is NO warranty;
 not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\n"
  button .about.close -text Close -command {destroy .about} -font {Sans 18}
  pack .about.xschem
  pack .about.link
  pack .about.link2
  pack .about.link3
  pack .about.link4
  pack .about.descr
  pack .about.copyright
  pack .about.close
  if {$OS == "Windows"} {
    bind .about.link <Button-1> {eval start http://repo.hu/projects/xschem}
    bind .about.link2 <Button-1> {eval start https://github.com/StefanSchippers/xschem}
    bind .about.link3 <Button-1> {eval start http://repo.hu/projects/xschem/index.html}
    bind .about.link4 <Button-1> {
      if {$running_in_src_dir} {
        eval start [pwd]/../doc/xschem_man/xschem_man.html
      } else {
        eval start $XSCHEM_SHAREDIR/../doc/xschem/xschem_man/xschem_man.html
      }
    }
  } else {
    bind .about.link <Button-1> {execute 0 xdg-open http://repo.hu/projects/xschem}
    bind .about.link2 <Button-1> {execute 0 xdg-open https://github.com/StefanSchippers/xschem}
    bind .about.link3 <Button-1> {execute 0 xdg-open http://repo.hu/projects/xschem/index.html}
    bind .about.link4 <Button-1> {
      if {$running_in_src_dir} {
        execute 0 xdg-open file://$XSCHEM_SHAREDIR/../doc/xschem_man/xschem_man.html
      } else {
        execute 0 xdg-open file://$XSCHEM_SHAREDIR/../doc/xschem/xschem_man/xschem_man.html
      }
    }
  }
}

proc property_search {} {
  global search_value search_found
  global search_exact search_case
  global search_select
  global custom_token OS

  set search_found 0
  while { !$search_found} {
    if { [winfo exists .dialog] } return
    xschem set semaphore [expr {[xschem get semaphore] +1}]
    toplevel .dialog -class Dialog
    wm title .dialog {Search}
    wm transient .dialog [xschem get topwindow]
    if { ![info exists X] } {
      set X [expr {[winfo pointerx .dialog] - 60}]
      set Y [expr {[winfo pointery .dialog] - 35}]
    }
    wm geometry .dialog "+$X+$Y"
    frame .dialog.custom
    label .dialog.custom.l -text "Token"
    entry .dialog.custom.e -width 32
    entry_replace_selection .dialog.custom.e
    .dialog.custom.e insert 0 $custom_token
    pack .dialog.custom.e .dialog.custom.l -side right
    frame .dialog.val
    label .dialog.val.l -text "Value"
    entry .dialog.val.e -width 32
    entry_replace_selection .dialog.val.e
    .dialog.val.e insert 0 $search_value
    pack .dialog.val.e .dialog.val.l -side right
    frame .dialog.but
    button .dialog.but.ok -text OK -command {
          set search_value [.dialog.val.e get]
          set custom_token [.dialog.custom.e get]
          if {$debug_var<=-1} { puts stderr "|$custom_token|" }
          if { $search_exact==1 } {
            set search_found [xschem searchmenu exact $search_select $custom_token $search_value $search_case]
          } else {
            set search_found [xschem searchmenu regex $search_select $custom_token $search_value $search_case]
          }
          destroy .dialog
    }
    button .dialog.but.cancel -text Cancel -command { set search_found 1; destroy .dialog }

    # Window doesn't support regular expression, has to be exact match for now
    checkbutton .dialog.but.sub -text {Exact search} -variable search_exact
    checkbutton .dialog.but.case -text {Match case} -variable search_case
    radiobutton .dialog.but.nosel -text {Highlight} -variable search_select -value 0
    radiobutton .dialog.but.sel -text {Select} -variable search_select -value 1
    # 20171211 added unselect
    radiobutton .dialog.but.unsel -text {Unselect} -variable search_select -value -1
    pack .dialog.but.ok  -anchor w -side left
    pack .dialog.but.sub  -side left
    pack .dialog.but.case  -side left
    pack .dialog.but.nosel  -side left
    pack .dialog.but.sel  -side left
    pack .dialog.but.unsel  -side left
    pack .dialog.but.cancel -anchor e
    pack .dialog.custom  -anchor e
    pack .dialog.val  -anchor e
    pack .dialog.but -expand yes -fill x
    focus  .dialog
    bind .dialog <Escape> {.dialog.but.cancel invoke}
    bind .dialog <Return> {.dialog.but.ok invoke}
    wm protocol .dialog WM_DELETE_WINDOW {.dialog.but.cancel invoke}
    # grab set .dialog
    tkwait window .dialog
    xschem set semaphore [expr {[xschem get semaphore] -1}]
  }
  return {}
}

#20171029
# allows to call TCL hooks from 'format' strings during netlisting
# example of symbol spice format definition:
# format="@name @pinlist @symname @tcleval(<script>) m=@m"
# NOTE: spaces and quotes in <script> must be escaped
# symname and instname are predefined variables in the <script> context
# they can be used together with TCL xschem command to query instance or symbol
# attributes.
#
proc tclpropeval {s instname symname} {
  # puts "tclpropeval: $s $instname $symname"
  global env debug_var
  regsub {^@tcleval\(} $s {} s
  regsub {\)([ \t\n]*)$} $s {\1} s
  # puts "tclpropeval: $s $instname $symname"
  if { [catch {subst $s} res] } {
    # puts stderr $res
    set res ?\n
  }
  return $res
}

# this hook is called in translate() if whole string is contained in a tcleval(...) construct
proc tclpropeval2 {s} {
  global debug_tcleval env path debug_var sch_basename

  set raw_level [xschem get raw_level]
  set netlist_type [xschem get netlist_type]
  # puts "tclpropeval2: s=|$s|"
  if {$debug_var <=-1} {puts "tclpropeval2: $s"}
  set path [string range [xschem get sch_path] 1 end]
  set sch_basename [file tail [file rootname [xschem get current_name]]]
  # skip hierarchy components above the level where raw file has been loaded.
  # node path names to look up in raw file begin from there.
  set skip 0
  while { $skip < $raw_level } {
    regsub {^[^.]*\.} $path {} path
    incr skip
  }

  if { $netlist_type eq {spice} } {
    # this is necessary if spiceprefix is being used in netlists
    regsub {^([^xX])} $path {x\1} path
    while { [regsub {\.([^xX])} $path {.x\1} path] } {}
  }

  ## no more necessary, ':' are converted to '.' when reading raw file */
  #  if { [sim_is_xyce]} {
  #    regsub -all {\.} [string toupper $path] {:} path
  #  }

  if { $debug_var<=-1 } { puts "---> path=$path" }
  regsub {^tcleval\(} $s {} s
  regsub {\)([ \n\t]*)$} $s {\1} s
  # puts "tclpropeval2: s=|$s|"
  # puts "tclpropeval2: subst $s=|[subst $s]|"
  if { [catch {uplevel #0 "subst \{$s\}"} res] } {
    if { $debug_tcleval > 0} { puts "tclpropeval2 warning: $s --> $res"}
    set res ?\n
  }
  # puts "tclpropeval2: res=|$res|"
  return $res
}

proc attach_labels_to_inst {} {
  global use_lab_wire use_label_prefix custom_label_prefix do_all_inst rotated_text

  set tctx::rcode {}
  if { [winfo exists .dialog] } return
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel .dialog -class Dialog
  wm title .dialog {Add labels to instances}
  wm transient .dialog [xschem get topwindow]

  # 20100408
  set X [expr {[winfo pointerx .dialog] - 60}]
  set Y [expr {[winfo pointery .dialog] - 35}]
  wm geometry .dialog "+$X+$Y"

  frame .dialog.custom
  label .dialog.custom.l -text "Prefix"
  entry .dialog.custom.e -width 32
  entry_replace_selection .dialog.custom.e
  .dialog.custom.e insert 0 $custom_label_prefix
  pack .dialog.custom.e .dialog.custom.l -side right


  frame .dialog.but
  button .dialog.but.ok -text OK -command {
        set custom_label_prefix [.dialog.custom.e get]
        #### put command here
        set tctx::rcode yes
        destroy .dialog
  }
  button .dialog.but.cancel -text Cancel -command { set tctx::rcode {}; destroy .dialog }
  checkbutton .dialog.but.wire -text {use wire labels} -variable use_lab_wire
  checkbutton .dialog.but.do_all -text {Do all} -variable do_all_inst
  label .dialog.but.rot -text {Rotated Text}
  entry .dialog.but.rotated -textvariable rotated_text -width 2
  entry_replace_selection .dialog.but.rotated
  checkbutton .dialog.but.prefix -text {use prefix} -variable use_label_prefix
  pack .dialog.but.ok  -anchor w -side left
  pack .dialog.but.prefix  -side left
  pack .dialog.but.wire  -side left
  pack .dialog.but.do_all  -side left
  pack .dialog.but.rotated  -side left
  pack .dialog.but.rot  -side left
  pack .dialog.but.cancel -anchor e

  pack .dialog.custom  -anchor e
  pack .dialog.but -expand yes -fill x

  focus  .dialog
  bind .dialog <Escape> {.dialog.but.cancel invoke}
  bind .dialog <Return> {.dialog.but.ok invoke}
  grab set .dialog
  tkwait window .dialog
  xschem set semaphore [expr {[xschem get semaphore] -1}]
  return {}
}

# if no_ask_save is set to 1 just return yes (always save)
proc ask_save_optional { {ask {save file?}} {cancel 1}} {
  global no_ask_save

  if { $no_ask_save } {
    return yes
  } else {
    return [ask_save $ask $cancel]
  }
}

proc ask_save { {ask {save file?}} {cancel 1}} {
  global wm_fix
  set tctx::rcode {}
  if { [winfo exists .dialog] } return
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel .dialog -class Dialog
  wm title .dialog {Ask Save}
  wm transient .dialog [xschem get topwindow]

  set X [expr {[winfo pointerx .dialog] - 60}]
  set Y [expr {[winfo pointery .dialog] - 35}]
  if { $wm_fix } { tkwait visibility .dialog }

  wm geometry .dialog "+$X+$Y"
  label .dialog.l1  -text $ask
  frame .dialog.f1
  button .dialog.f1.b1 -text {Yes} -command\
  {
   set tctx::rcode {yes}
   destroy .dialog
  }
  if {$cancel} {
    button .dialog.f1.b2 -text {Cancel} -command\
    {
     set tctx::rcode {}
     destroy .dialog
    }
  }
  button .dialog.f1.b3 -text {No} -command\
  {
   set tctx::rcode {no}
   destroy .dialog
  }
  pack .dialog.l1 .dialog.f1 -side top -fill x
  pack .dialog.f1.b1 -side left -fill x -expand yes
  bind .dialog <Return> {.dialog.f1.b1 invoke}
  bind .dialog <y> {.dialog.f1.b1 invoke}
  bind .dialog <n> {.dialog.f1.b3 invoke}
  if { $cancel} {
    pack .dialog.f1.b2 -side left -fill x -expand yes
  }
  pack .dialog.f1.b3 -side left -fill x -expand yes
  if {$cancel} {
    bind .dialog <Escape> {.dialog.f1.b2 invoke}
  }
  # needed, otherwise problems when descending with double clixk 23012004
  tkwait visibility .dialog
  raise .dialog
  grab set .dialog
  tkwait window .dialog
  xschem set semaphore [expr {[xschem get semaphore] -1}]
  return $tctx::rcode
}


proc edit_vi_prop {txtlabel} {
  global XSCHEM_TMP_DIR symbol prev_symbol debug_var editor
  global user_wants_copy_cell

  set netlist_type [xschem get netlist_type]
  set user_wants_copy_cell 0
  set tctx::rcode {}
  set filename .xschem_edit_file.[pid]
  if ![string compare $netlist_type "vhdl"] { set suffix vhd } else { set suffix v }
  set filename $filename.$suffix
  write_data $tctx::retval $XSCHEM_TMP_DIR/$filename
  # since $editor can be an executable with options (gvim -f) I *need* to use eval
  eval execute_wait 0 $editor $XSCHEM_TMP_DIR/$filename ;# 20161119
  if {$debug_var<=-1} {puts "edit_vi_prop{}:\n--------\nretval=$tctx::retval\n---------\n"}
  if {$debug_var<=-1} {puts "edit_vi_prop{}:\n--------\nsymbol=$symbol\n---------\n"}
  set tmp [read_data $XSCHEM_TMP_DIR/$filename]
  file delete $XSCHEM_TMP_DIR/$filename
  if {$debug_var<=-1} {puts "edit_vi_prop{}:\n--------\n$tmp\n---------\n"}
  if [string compare $tmp $tctx::retval] {
         set tctx::retval $tmp
         if {$debug_var<=-1} {puts "modified"}
         set tctx::rcode ok
         return  $tctx::rcode
  } else {
         set tctx::rcode {}
         return $tctx::rcode
  }
}

proc edit_vi_netlist_prop {txtlabel} {
  global XSCHEM_TMP_DIR debug_var editor
  global user_wants_copy_cell

  set netlist_type [xschem get netlist_type]
  set user_wants_copy_cell 0
  set filename .xschem_edit_file.[pid]
  if ![string compare $netlist_type "vhdl"] { set suffix vhd } else { set suffix v }
  set filename $filename.$suffix
  regsub -all {\\?"} $tctx::retval {"} tctx::retval
  regsub -all {\\?\\} $tctx::retval {\\} tctx::retval
  write_data $tctx::retval $XSCHEM_TMP_DIR/$filename
  if { [regexp vim $editor] } { set ftype "\{-c :set filetype=$netlist_type\}" } else { set ftype {} }
  # since $editor can be an executable with options (gvim -f) I *need* to use eval
  eval execute_wait 0 $editor  $ftype $XSCHEM_TMP_DIR/$filename
  if {$debug_var <= -1}  {puts "edit_vi_prop{}:\n--------\n$tctx::retval\n---------\n"}
  set tmp [read_data $XSCHEM_TMP_DIR/$filename]
  file delete $XSCHEM_TMP_DIR/$filename
  if {$debug_var <= -1}  {puts "edit_vi_prop{}:\n--------\n$tmp\n---------\n"}
  if [string compare $tmp $tctx::retval] {
         set tctx::retval $tmp
         regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
         set tctx::retval \"${tctx::retval}\"
         if {$debug_var <= -1}  {puts "modified"}
         set tctx::rcode ok
         return  $tctx::rcode
  } else {
         set tctx::rcode {}
         return $tctx::rcode
  }
}

proc reset_colors {ask} {
  global dark_colors light_colors dark_colorscheme USER_CONF_DIR svg_colors ps_colors
  global light_colors_save dark_colors_save

  if {$ask} {
    set answer [tk_messageBox -message  "Warning: delete 'colors' configuration file?" \
        -icon warning -parent [xschem get topwindow] -type okcancel]
    if {$answer ne {ok}} { return }
  }
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  set light_colors $light_colors_save
  set dark_colors $dark_colors_save
  if { $dark_colorscheme == 1 } {
    set tctx::colors $dark_colors
  } else {
    set tctx::colors $light_colors
  }
  regsub -all {"} $light_colors  {} ps_colors
  regsub -all {#} $ps_colors  {0x} ps_colors
  regsub -all {"} $tctx::colors {} svg_colors
  regsub -all {#} $svg_colors {0x} svg_colors
  file delete ${USER_CONF_DIR}/colors
  xschem build_colors
  xschem redraw
  xschem set semaphore [expr {[xschem get semaphore] -1}]
}

proc change_color {} {
  global dark_colors light_colors dark_colorscheme cadlayers USER_CONF_DIR svg_colors ps_colors

  set n [xschem get rectcolor]
  if { $n < 0 || $n >=$cadlayers} return
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  if { $dark_colorscheme == 1 } {
    set c $dark_colors
  } else {
    set c $light_colors
  }
  set initial_color [lindex $c $n]
  set value [tk_chooseColor -initialcolor $initial_color]
  if {[string compare $value {}] } {
    set cc [lreplace $c $n $n $value]
    set tctx::colors $cc
    if { $dark_colorscheme == 1 } {
      set dark_colors $cc
    } else {
      set light_colors $cc
      regsub -all {"} $cc  {} ps_colors
      regsub -all {#} $ps_colors  {0x} ps_colors
    }
    regsub -all {"} $tctx::colors {} svg_colors
    regsub -all {#} $svg_colors {0x} svg_colors

    xschem build_colors
    xschem redraw
    set    savedata "#### THIS FILE IS AUTOMATICALLY GENERATED BY XSCHEM, DO NOT EDIT.\n"
    append savedata "set cadlayers $cadlayers\n"
    append savedata "set light_colors {$light_colors}\n"
    append savedata "set dark_colors {$dark_colors}\n"
    write_data $savedata ${USER_CONF_DIR}/colors
  }
  xschem set semaphore [expr {[xschem get semaphore] -1}]
}

proc edit_prop {txtlabel} {
  global edit_prop_size infowindow_text edit_symbol_prop_new_sel edit_prop_pos
  global prev_symbol symbol no_change_attrs preserve_unchanged_attrs copy_cell debug_var
  global user_wants_copy_cell editprop_sympath text_tabs_setting
  global tabstop
  set user_wants_copy_cell 0
  set tctx::rcode {}
  set tctx::retval_orig $tctx::retval
  if {$debug_var <= -1}  {puts " edit_prop{}: tctx::retval=$tctx::retval"}
  if { [winfo exists .dialog] } return
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel .dialog  -class Dialog
  wm title .dialog {Edit Properties}
  wm transient .dialog [xschem get topwindow]
  set X [expr {[winfo pointerx .dialog] - 60}]
  set Y [expr {[winfo pointery .dialog] - 35}]

  # 20160325 change and remember widget size
  bind .dialog <Configure> {
    # puts [wm geometry .dialog]
    if { $edit_symbol_prop_new_sel != 1 } {
      set edit_prop_size [wm geometry .dialog]
      set edit_prop_pos $edit_prop_size
      regsub {\+.*} $edit_prop_size {} edit_prop_size
      regsub {[^+]*\+} $edit_prop_pos {+} edit_prop_pos
    }
  }
  wm geometry .dialog "${edit_prop_size}+$X+$Y"
  set prev_symbol $symbol
  set editprop_sympath [get_directory [abs_sym_path [tclpropeval2 $symbol]]]
  frame .dialog.f4
  label .dialog.f4.l1  -text $txtlabel
  label .dialog.f4.path  -text "Path:"
  entry .dialog.f4.symprop  -textvariable editprop_sympath -width 0 -state readonly
  entry_replace_selection .dialog.f4.symprop
  eval text .dialog.symprop -undo 1 -yscrollcommand \".dialog.yscroll set\" -setgrid 1 \
                  -xscrollcommand \".dialog.xscroll set\" -wrap none $text_tabs_setting
    .dialog.symprop delete 1.0 end
    .dialog.symprop insert 1.0 $tctx::retval
  scrollbar .dialog.yscroll -command  ".dialog.symprop yview"
  scrollbar .dialog.xscroll -command ".dialog.symprop xview" -orient horiz
  frame .dialog.f1
  frame .dialog.f2
  label .dialog.f1.l2 -text "Symbol"
  entry .dialog.f1.e2 -width 30
  entry_replace_selection .dialog.f1.e2
  .dialog.f1.e2 insert 0 $symbol
  button .dialog.f1.b5 -text "Browse" -command {
    set r [tk_getOpenFile -parent .dialog -initialdir $INITIALINSTDIR ]
    if {$r ne {} } {
      .dialog.f1.e2 delete 0 end
      .dialog.f1.e2 insert 0 $r
    }
    raise .dialog .drw
  }
  button .dialog.f1.b1 -text "OK" -command   {
    set tctx::retval [.dialog.symprop get 1.0 {end - 1 chars}]
    if { $tctx::selected_tok ne {<ALL>} } {
      regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
      set tctx::retval \"${tctx::retval}\"
      set tctx::retval [xschem subst_tok $tctx::retval_orig $tctx::selected_tok $tctx::retval]
      set tctx::selected_tok {<ALL>}
    }
    set symbol [.dialog.f1.e2 get]
    set abssymbol [abs_sym_path $symbol]
    set tctx::rcode {ok}
    set user_wants_copy_cell $copy_cell
    set prev_symbol [abs_sym_path $prev_symbol]
    if { ($abssymbol ne $prev_symbol) && $copy_cell } {
      if { ![regexp {^/} $symbol] && ![regexp {^[a-zA-Z]:} $symbol] } {
        set symlist [file split $symbol]
        set symlen [llength $symlist]
        set abssymbol "[path_head $prev_symbol $symlen]/$symbol"
      }
      # puts "$abssymbol   $prev_symbol"
      if { [file exists "[file rootname $prev_symbol].sch"] } {
        if { ! [file exists "[file rootname ${abssymbol}].sch"] } {
          file copy "[file rootname $prev_symbol].sch" "[file rootname $abssymbol].sch"
          # puts "file copy [file rootname $prev_symbol].sch [file rootname $abssymbol].sch"
        }
      }
      if { [file exists "$prev_symbol"] } {
        if { ! [file exists "$abssymbol"] } {
          file copy "$prev_symbol" "$abssymbol"
          # puts "file copy [file rootname $prev_symbol].sym [file rootname $abssymbol].sym"
        }
      }
    }
    #puts "symbol: $symbol , prev_symbol: $prev_symbol"
    set copy_cell 0 ;# 20120919
    destroy .dialog
  }
  button .dialog.f1.b2 -text "Cancel" -command  {
    set tctx::rcode {}
    set edit_symbol_prop_new_sel {}
    destroy .dialog
  }
  wm protocol .dialog  WM_DELETE_WINDOW {
    set tctx::rcode {}
    set edit_symbol_prop_new_sel {}
    destroy .dialog
  }
  button .dialog.f1.b3 -text "Load" -command {
    global INITIALPROPDIR
    set a [tk_getOpenFile -parent .dialog -initialdir $INITIALPROPDIR ]
    if [string compare $a ""] {
     set INITIALPROPDIR [file dirname $a]
     read_data_window  .dialog.symprop  $a
    }
  }
  button .dialog.f1.b4 -text "Del" -command {
    .dialog.symprop delete 1.0 end
  }
  checkbutton .dialog.f2.r1 -text "No change properties" -variable no_change_attrs -state normal
  checkbutton .dialog.f2.r2 -text "Preserve unchanged props" -variable preserve_unchanged_attrs -state normal
  checkbutton .dialog.f2.r3 -text "Copy cell" -variable copy_cell -state normal
  set tok_list "<ALL> [list_tokens $tctx::retval]"
  set tctx::selected_tok {<ALL>}
  set tctx::old_selected_tok {<ALL>}
  label .dialog.f2.r4 -text {   Edit Attr:}
  if  { [info tclversion] > 8.4} {
    ttk::combobox .dialog.f2.r5 -values $tok_list -textvariable tctx::selected_tok -width 14
  }
  pack .dialog.f1.l2 -side left
  pack .dialog.f1.e2 -side left -fill x -expand yes
  pack .dialog.f1.b1 -side left
  pack .dialog.f1.b2 -side left
  pack .dialog.f1.b3 -side left
  pack .dialog.f1.b4 .dialog.f1.b5 -side left
  pack .dialog.f4 -side top  -anchor nw
  #pack .dialog.f4.path .dialog.f4.symprop .dialog.f4.l1 -side left -fill x
  pack .dialog.f4.path -side left
  pack .dialog.f4.symprop -side left
  pack .dialog.f1 .dialog.f2 -side top -fill x
  pack .dialog.f2.r1 -side left
  pack .dialog.f2.r2 -side left
  pack .dialog.f2.r3 -side left
  if { [info tclversion] > 8.4 } {
    pack .dialog.f2.r4 -side left
    pack .dialog.f2.r5 -side left
  }
  pack .dialog.yscroll -side right -fill y
  pack .dialog.xscroll -side bottom -fill x
  pack .dialog.symprop  -fill both -expand yes
  bind .dialog.symprop <Shift-KeyRelease-Return> {return_release %W;.dialog.f1.b1 invoke}
  bind .dialog <Escape> {
    if { ![string compare $tctx::retval [.dialog.symprop get 1.0 {end - 1 chars}]] && \
         ![string compare $symbol [ .dialog.f1.e2 get]] } {
      .dialog.f1.b2 invoke
    }
  }
  if  { [info tclversion] > 8.4} {
    bind .dialog.f2.r5 <<ComboboxSelected>> {
      if {$tctx::old_selected_tok ne $tctx::selected_tok} {
        if { $tctx::old_selected_tok eq {<ALL>} } {
          set tctx::retval_orig [.dialog.symprop get 1.0 {end - 1 chars}]
        } else {
          set tctx::retval [.dialog.symprop get 1.0 {end - 1 chars}]
          regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;# vim syntax fix "
          set tctx::retval \"${tctx::retval}\"
          set tctx::retval_orig [xschem subst_tok $tctx::retval_orig $tctx::old_selected_tok $tctx::retval]
        }
      }
      if {$tctx::selected_tok eq {<ALL>} } {
        set tctx::retval $tctx::retval_orig
      } else {
        set tctx::retval [xschem get_tok $tctx::retval_orig $tctx::selected_tok 2]
        # regsub -all {\\?"} $tctx::retval {"} tctx::retval
      }
      .dialog.symprop delete 1.0 end
      .dialog.symprop insert 1.0 $tctx::retval
      set tctx::old_selected_tok $tctx::selected_tok
    }

    bind .dialog.f2.r5 <KeyRelease> {
      set tctx::selected_tok [.dialog.f2.r5 get]
      if { $tctx::old_selected_tok eq {<ALL>}} {
        set tctx::retval_orig [.dialog.symprop get 1.0 {end - 1 chars}]
      } else {
        set tctx::retval [.dialog.symprop get 1.0 {end - 1 chars}]
        if {$tctx::retval ne {}} {
          regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
          set tctx::retval \"${tctx::retval}\"
          set tctx::retval_orig [xschem subst_tok $tctx::retval_orig $tctx::old_selected_tok $tctx::retval]
        }
      }
      if {$tctx::selected_tok eq {<ALL>} } {
        set tctx::retval $tctx::retval_orig
      } else {
        set tctx::retval [xschem get_tok $tctx::retval_orig $tctx::selected_tok 2]
        # regsub -all {\\?"} $tctx::retval {"} tctx::retval
      }
      .dialog.symprop delete 1.0 end
      .dialog.symprop insert 1.0 $tctx::retval
      set tctx::old_selected_tok $tctx::selected_tok
    }
  }
  if {$edit_symbol_prop_new_sel == 1} {
    wm geometry .dialog $edit_prop_pos
  }
  set edit_symbol_prop_new_sel 0

  tkwait visibility .dialog
  # select text after value= or lab= and place cursor just before selection

  set sym_sel_attr [xschem get_tok $tctx::retval select] ;# get from instance
  if {$sym_sel_attr eq {}} {
    if {[catch {xschem getprop symbol $symbol select} sym_sel_attr]} {
      set sym_sel_attr {}
    }
  }
  if {$sym_sel_attr eq {*}} {
     .dialog.symprop tag add sel 1.0 {end - 1 chars}
     .dialog.symprop mark set insert 1.0
  } else {
    foreach attr [list $sym_sel_attr value lab name] {
      if {$attr eq {}} {continue}
      set regx {}
      set regx1 {}
      append regx $attr { *= *("([^"]|(\\"))+"|[^ \t\n"]+)} ;# vim syntax fix "
      append regx1 $attr { *= *[^ \n]}
      if  { [info tclversion] > 8.4} {
        set idx [.dialog.symprop search -regexp -nolinestop -count nchars $regx 1.0]
        .dialog.symprop search -regexp -nolinestop -count len $regx1 1.0
      } else {
        set idx [.dialog.symprop search -regexp -count nchars $regx 1.0]
        .dialog.symprop search -regexp -count len $regx1 1.0
      }
      if {$idx ne {}} break
    }
    if { $idx ne {} } {
      incr len -1
      .dialog.symprop tag add sel "$idx + $len chars" "$idx + $nchars chars"
      .dialog.symprop mark set insert "$idx + $len chars"
    }
  }
  focus .dialog.symprop
  tkwait window .dialog
  xschem set semaphore [expr {[xschem get semaphore] -1}]
  return $tctx::rcode
}

# reads metadata tokens from header component of the symbol inside `v {data}` element
# and returns dictionary in name of the token as key and value of the token as value in dictionary
# Courtesy @georgtree
proc symbolParse {file} {
    try {
        set file [open $file r]
    } on error {errmsg erropts} {
        puts stderr "Error while reading symbol file '$file': $errmsg"
        return
    }
    set data [read $file]
    close $file
    # pattern to find v {} and extract content inside brackets
    set pattern {(?:^|\n)v\s+\{((?:[^{}]|\\[{}])*)\}}
    if {![regexp $pattern $data -> content]} {
        return
    }
    # outer braces {...} are not part of content, so remove escaping of inner {, } and \ .
    set content [string map { \\\{ \{ \\\} \} \\\\ \\ } $content]
    # pattern to find: name="value" or name=value or name = "value" or name = value
    set pattern {(\w+)\s*=\s*(?:"((?:[^"]|\\["])*)"|(\S+))}
    set start 0
    # 'match' variable holds the complete matched pattern, used for searching next one
    while {[regexp -start $start $pattern $content -> name quotedValue unquotedValue]} {
        # Determine the value (quoted or unquoted)
        if {[string length $quotedValue] > 0} {
            set value $quotedValue
            # outer quotes are removed from value,
            # remove escaping of internal backslashes and doublequotes
            dict append tokens $name [string map {\\\\ \\ \\\" \" } $value]
        } else {
            set value $unquotedValue
            dict append tokens $name $value
        }
        # Update the start index to continue searching after this match
        set start [expr {[string first $value $content $start] + [string length $value]}]
        # Safety check to prevent infinite loops
        if {$start <= 0} {
            break
        }
    }
    return $tokens
}

proc read_data_nonewline {f} {
  set res [catch {open $f r} fid]
  if {$res} { puts "read_data_nonewline: $fid"; return {}}
  set data [read -nonewline $fid]
  close $fid
  return $data
}

proc read_data {f} {
  set res [catch {open $f r} fid]
  if {$res} { puts "read_data: $fid"; return {}}
  set data [read $fid]
  close $fid
  return $data
}

proc read_data_window {w f} {
  set res [catch {open $f r} fid]
  if {$res} { puts "read_data_window: $fid"; return {}}
  set t [read $fid]
  #  $w delete 0.0 end
  ## 20171103 insert text at cursor position instead of at beginning (insert index tag)
  $w insert insert $t
  close $fid
}

proc write_data {data f} {

  set res [catch {open $f w} fid]
  if {$res} { puts "write_data: $fid"; return {}}
  puts  -nonewline $fid $data
  close $fid
  return {}
}

proc text_line {txtlabel clear {preserve_disabled disabled} } {
  global text_line_default_geometry preserve_unchanged_attrs wm_fix tabstop
  global debug_var text_tabs_setting

  set buttonstate $preserve_disabled
  if {$preserve_disabled eq {header}} { set buttonstate {disabled}}

  if {$preserve_disabled eq {disabled}} {
    set tctx::selected_mode [xschem get netlist_type]
    set glob_attr [string map {
          spice     schprop
          vhdl      schvhdlprop
          verilog   schverilogprop
          tedax     schtedaxprop
          symbol    schsymbolprop
          spectre   schspectreprop
        } $tctx::selected_mode]
    set mode_list {Spice[S] Vhdl[G] Verilog[V] Tedax[E] Symbol[K] Spectre[F]}
    set tctx::selected_mode [string map {
         spice           Spice[S]
         vhdl            Vhdl[G]
         verilog         Verilog[V]
         tedax           Tedax[E]
         symbol          Symbol[K]
         spectre         Spectre[F]} $tctx::selected_mode]
    set tctx::old_selected_mode $tctx::selected_mode
    set tctx::retval [xschem get $glob_attr]
  }

  set tctx::retval_orig $tctx::retval
  if $clear==1 then {set tctx::retval ""}
  set tctx::rcode {}
  if { [winfo exists .dialog] } return
  toplevel .dialog  -class Dialog
  wm title .dialog {Text input}
  wm transient .dialog [xschem get topwindow]
  set X [expr {[winfo pointerx .dialog] - 60}]
  set Y [expr {[winfo pointery .dialog] - 35}]

  set tok_list "<ALL> [list_tokens $tctx::retval]"
  set tctx::selected_tok {<ALL>}
  set tctx::old_selected_tok {<ALL>}
  bind .dialog <Configure> {
    # puts [wm geometry .dialog]
    set text_line_default_geometry [wm geometry .dialog]
    regsub {\+.*} $text_line_default_geometry {} text_line_default_geometry
  }

  # 20100203
  if { $wm_fix } { tkwait visibility .dialog }
  wm geometry .dialog "${text_line_default_geometry}+$X+$Y"

  frame .dialog.f0
  frame .dialog.f1
  label .dialog.f0.l1  -text $txtlabel
  eval text .dialog.textinput -undo 1 -relief sunken -bd 2 -yscrollcommand \".dialog.yscroll set\" -setgrid 1 \
       -xscrollcommand \".dialog.xscroll set\" -wrap none -width 90 -height 40 $text_tabs_setting
  scrollbar .dialog.yscroll -command  ".dialog.textinput yview"
  scrollbar .dialog.xscroll -command ".dialog.textinput xview" -orient horiz
  .dialog.textinput delete 1.0 end
  .dialog.textinput insert 1.0 $tctx::retval
  button .dialog.f1.b1 -text "OK" -command  \
  {
    set tctx::retval [.dialog.textinput get 1.0 {end - 1 chars}]
    if { $tctx::selected_tok ne {<ALL>} } {
      regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
      set tctx::retval \"${tctx::retval}\"
      set tctx::retval [xschem subst_tok $tctx::retval_orig $tctx::selected_tok $tctx::retval]
      set tctx::selected_tok {<ALL>}
    }

    set tctx::rcode {ok}
    destroy .dialog
  }
  button .dialog.f1.b2 -text "Cancel" -command  \
  {
    set tctx::retval [.dialog.textinput get 1.0 {end - 1 chars}]
    set tctx::rcode {}
    destroy .dialog
  }
  button .dialog.f1.b3 -text "Load" -command \
  {
    global INITIALPROPDIR
    set a [tk_getOpenFile -parent .dialog -initialdir $INITIALPROPDIR ]
    if [string compare $a ""] {
     set INITIALPROPDIR [file dirname $a]
     read_data_window  .dialog.textinput  $a
    }
  }
  button .dialog.f1.b4 -text "Del" -command \
  {
    .dialog.textinput delete 1.0 end
  }

  if  { [info tclversion] > 8.4} {
    if {$preserve_disabled eq {disabled}} {
      label .dialog.f1.r6 -text {Mode:}
      ttk::combobox .dialog.f1.r7 -values $mode_list -textvariable tctx::selected_mode -width 14
    }
    if { $preserve_disabled ne {header} } {
      label .dialog.f1.r4 -text {   Edit Attr:}
      ttk::combobox .dialog.f1.r5 -values $tok_list -textvariable tctx::selected_tok -width 14
    }
  }

  checkbutton .dialog.f0.l2 -text "preserve unchanged props" -variable preserve_unchanged_attrs \
     -state $buttonstate
  pack .dialog.f0 -fill x
  pack .dialog.f0.l2 -side left
  pack .dialog.f0.l1 -side left -expand yes
  pack .dialog.f1  -fill x
  pack .dialog.f1.b1 -side left -fill x -expand yes
  pack .dialog.f1.b2 -side left -fill x -expand yes
  pack .dialog.f1.b3 -side left -fill x -expand yes
  pack .dialog.f1.b4 -side left -fill x -expand yes
  if  { [info tclversion] > 8.4} {
    if { $preserve_disabled ne {header} } {
      pack .dialog.f1.r4 -side left
      pack .dialog.f1.r5 -side left
    }
    if {$preserve_disabled eq {disabled}} {
      pack .dialog.f1.r6 -side left
      pack .dialog.f1.r7 -side left
    }
  }


  pack .dialog.yscroll -side right -fill y
  pack .dialog.xscroll -side bottom -fill x
  pack .dialog.textinput   -expand yes -fill both
  bind .dialog <Escape> {
    if ![string compare $tctx::retval [.dialog.textinput get 1.0 {end - 1 chars}]] {
      .dialog.f1.b2 invoke
    }
  }

  if  { $preserve_disabled ne {header} && [info tclversion] > 8.4} {
      bind .dialog.f1.r5 <<ComboboxSelected>> {
        if {$tctx::old_selected_tok ne $tctx::selected_tok} {
          if { $tctx::old_selected_tok eq {<ALL>} } {
            set tctx::retval_orig [.dialog.textinput get 1.0 {end - 1 chars}]
          } else {
            set tctx::retval [.dialog.textinput get 1.0 {end - 1 chars}]
            regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
            set tctx::retval \"${tctx::retval}\"
            set tctx::retval_orig [xschem subst_tok $tctx::retval_orig $tctx::old_selected_tok $tctx::retval]
          }
        }
        if {$tctx::selected_tok eq {<ALL>} } {
          set tctx::retval $tctx::retval_orig
        } else {
          set tctx::retval [xschem get_tok $tctx::retval_orig $tctx::selected_tok 2]
          # regsub -all {\\?"} $tctx::retval {"} tctx::retval
        }
        .dialog.textinput delete 1.0 end
        .dialog.textinput insert 1.0 $tctx::retval
        set tctx::old_selected_tok $tctx::selected_tok
      }
      bind .dialog.f1.r5 <KeyRelease> {
        set tctx::selected_tok [.dialog.f1.r5 get]
        if { $tctx::old_selected_tok eq {<ALL>}} {
          set tctx::retval_orig [.dialog.textinput get 1.0 {end - 1 chars}]
        } else {
          set tctx::retval [.dialog.textinput get 1.0 {end - 1 chars}]
          if {$tctx::retval ne {}} {
            regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
            set tctx::retval \"${tctx::retval}\"
            set tctx::retval_orig [xschem subst_tok $tctx::retval_orig $tctx::old_selected_tok $tctx::retval]
          }
        }
        if {$tctx::selected_tok eq {<ALL>} } {
          set tctx::retval $tctx::retval_orig
        } else {
          set tctx::retval [xschem get_tok $tctx::retval_orig $tctx::selected_tok 2]
          # regsub -all {\\?"} $tctx::retval {"} tctx::retval
        }
        .dialog.textinput delete 1.0 end
        .dialog.textinput insert 1.0 $tctx::retval
        set tctx::old_selected_tok $tctx::selected_tok
      }
  }

  if  { [info tclversion] > 8.4} {
    if {$preserve_disabled eq {disabled}} {
      bind .dialog.f1.r7 <<ComboboxSelected>> {
        proc set_global_mode {} {

          set tctx::retval [.dialog.textinput get 1.0 {end - 1 chars}]
          if { $tctx::selected_tok ne {<ALL>} } {
            regsub -all {(["\\])} $tctx::retval {\\\1} tctx::retval ;#"  editor is confused by the previous quote
            set tctx::retval \"${tctx::retval}\"
            set tctx::retval [xschem subst_tok $tctx::retval_orig $tctx::selected_tok $tctx::retval]
          }

          if {$tctx::retval ne $tctx::retval_orig} {
            xschem push_undo
            xschem set_modify 1
            set glob_attr [string map {
                Spice[S]    schprop
                Vhdl[G]     schvhdlprop
                Verilog[V]  schverilogprop
                Tedax[E]    schtedaxprop
                Symbol[K]   schsymbolprop
                Spectre[F]  schspectreprop
                } $tctx::old_selected_mode]
            xschem set $glob_attr $tctx::retval
          }
          set mode [string map  {
             Spice[S]     spice
             Vhdl[G]      vhdl
             Verilog[V]   verilog
             Tedax[E]     tedax
             Symbol[K]    symbol
             Spectre[F]   spectre} $tctx::selected_mode]

          set glob_attr [string map {
                spice     schprop
                vhdl      schvhdlprop
                verilog   schverilogprop
                tedax     schtedaxprop
                symbol    schsymbolprop
                spectre   schspectreprop
              } $mode]

          set tctx::old_selected_mode $tctx::selected_mode
          set $tctx::selected_tok {<ALL>}
          set tctx::retval [xschem get $glob_attr]
          set tctx::retval_orig $tctx::retval
          set tok_list "<ALL> [list_tokens $tctx::retval]"
          .dialog.f1.r5 configure -values $tok_list
          .dialog.textinput delete 1.0 end
          .dialog.textinput insert 1.0 $tctx::retval
        }
        set_global_mode
      }
    }
  }

  bind .dialog.textinput <Shift-KeyRelease-Return> {return_release %W; .dialog.f1.b1 invoke}
  #tkwait visibility .dialog
  #grab set .dialog
  #focus .dialog.textinput
  set tctx::rcode {}
  tkwait window .dialog

  if {$preserve_disabled eq {disabled}} {
    if {$tctx::retval ne $tctx::retval_orig} {
      xschem push_undo
      xschem set_modify 1
      set glob_attr [string map {
          Spice[S]    schprop
          Vhdl[G]     schvhdlprop
          Verilog[V]  schverilogprop
          Tedax[E]    schtedaxprop
          Symbol[K]   schsymbolprop
          Spectre[F]  schspectreprop
          } $tctx::selected_mode]
      xschem set $glob_attr $tctx::retval
    }
  }

  return $tctx::rcode
}

# alert_ text [position] [nowait] [yesno]
# show an alert dialog box and display 'text'.
# if 'position' is empty (example: alert_ {hello, world} {}) show at mouse coordinates
# otherwise use specified coordinates example: alert_ {hello, world} +300+400
# if nowait is 1 do not wait for user to close dialog box
# if yesnow is 1 show yes and no buttons and return user choice (1 / 0).
# (this works only if nowait is unset).
proc alert_ {txtlabel {position +200+300} {nowait {0}} {yesno 0}} {
  global has_x
  set tctx::rcode 1
  if {![info exists has_x] } {return}
  toplevel .alert -class Dialog
  wm title .alert {Alert}
  wm transient .alert [xschem get topwindow]
  set X [expr {[winfo pointerx .alert] - 70}]
  set Y [expr {[winfo pointery .alert] - 60}]
  if { [string compare $position ""] != 0 } {
    wm geometry .alert $position
  } else {
    wm geometry .alert "+$X+$Y"
  }
  label .alert.l1 -font {Sans 10 bold} -justify left \
       -text "                              \n${txtlabel}  \n" -wraplength 750
  if { $yesno} {
    set oktxt Yes
  } else {
    set oktxt OK
  }
  button .alert.b1 -text $oktxt -command  \
  {
    set tctx::rcode 1
    destroy .alert
  }
  if {$yesno} {
    button .alert.b2 -text "No" -command  \
    {
      set tctx::rcode 0
      destroy .alert
    }
  }

  pack .alert.l1 -side top -fill both -expand yes
  pack .alert.b1 -side left -fill x -expand yes
  if {$yesno} {pack .alert.b2 -side left -fill x -expand yes}
  tkwait visibility .alert
  # grab set .alert
  focus .alert.b1
  bind .alert <Return> { destroy .alert }
  bind .alert <Escape> { destroy .alert }
  bind .alert <Visibility> {
    if { [winfo exists .alert] && [winfo ismapped .alert] && [winfo ismapped .] && [wm stackorder .alert isbelow . ]} {
      if { [winfo exists .drw] } {
        raise .alert .drw
      } else {
        raise .alert
      }

    }
  }
  if {!$nowait} {tkwait window .alert}
  return $tctx::rcode
}

proc show_infotext {{err 0}} {
  global has_x infowindow_text show_infowindow_after_netlist
  set s $show_infowindow_after_netlist
  if {$s == 1} { set s always}
  if {$s == 0} { set s onerror}
  if {[info exists has_x]} {
    infowindow
  }
  if {($s eq {always}) || ($err != 0 && $s eq {onerror})} {
    if {[info exists has_x]} {
      wm deiconify .infotext
    } else {
      xschem log_write  [xschem get infowindow_text]
    }
  }
}

proc infowindow {} {
  global infowindow_text has_x
  set infowindow_text [xschem get infowindow_text]
  if {![info exists has_x]} { return }
  set z {.infotext}
  if ![string compare $infowindow_text ""] {
    if [winfo exists $z] {
      $z.f1.text delete 1.0 end
    }
  }
  if ![winfo exists $z] {
    toplevel $z
    wm title $z {Info window}
    # wm transient $z [xschem get topwindow]
    wm geometry $z 90x24+0+400 ;# gridded window: grid units, not pixels
    wm iconname $z {Info window}
    wm withdraw $z
    wm protocol .infotext WM_DELETE_WINDOW "wm withdraw $z; set show_infowindow 0"
    #  button $z.dismiss -text Dismiss -command  "destroy $z"
    frame $z.f1
    frame $z.f2
    text $z.f1.text -undo 1 -relief sunken -bd 2 -yscrollcommand "$z.f1.yscroll set" -setgrid 1 \
         -height 6 -width 50  -xscrollcommand "$z.f1.xscroll set" -wrap none
    scrollbar $z.f1.yscroll -command "$z.f1.text yview" -orient v
    scrollbar $z.f1.xscroll -command "$z.f1.text xview" -orient h
    grid $z.f1.text - $z.f1.yscroll -sticky nsew
    grid $z.f1.xscroll - -sticky ew
    # grid $z.dismiss - -
    grid rowconfig $z.f1 0 -weight 1
    grid columnconfig $z.f1 0 -weight 1
    pack $z.f1 -fill both -expand yes
    button $z.f2.dismiss -text Dismiss -command "wm withdraw $z; set show_infowindow 0"
    pack $z.f2.dismiss
    pack $z.f2 -fill x
    bind $z <Escape> "wm withdraw $z; set show_infowindow 0"
  }
  $z.f1.text delete 1.0 end
  $z.f1.text insert 1.0 $infowindow_text
  ## does not work in tcl/tk8.4
  # set lines [$z.f1.text count -displaylines 1.0 end]
  set lines [regexp -all \n [$z.f1.text get 1.0 end]]
  $z.f1.text see ${lines}.0
  return {}
}

proc editdata {{data {}} {title {Edit data}} {wrap {none}} {ro 1}} {
  global text_tabs_setting tabstop
  set window .editdata
  set tctx::retval $data
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel $window
  wm title $window $title
  wm iconname $window $title
  # wm transient $window [xschem get topwindow]
  frame $window.buttons
  pack $window.buttons -side bottom -fill x -pady 2m
  button $window.buttons.copy -text {Copy to clipboard} -command "
     clipboard clear
     clipboard append \[$window.text get 1.0 {end - 1 chars}\]
  "
  button $window.buttons.ok -text OK -command "
     set tctx::retval \[$window.text get 1.0 {end - 1 chars}\]; destroy $window
  "
  button $window.buttons.cancel -text Cancel -command "destroy $window"

  pack $window.buttons.ok -side left -expand 1
  pack $window.buttons.cancel -side left -expand 1
  pack $window.buttons.copy -side left -expand 1

  if { $ro ne {1} } {
    button $window.buttons.saveas -text {Save a copy} -command {
      proc editdata_save {} {
        global OS env

        if {$OS == "Windows"} {
          set editdata_filename [tk_getSaveFile -initialdir $env(windir) ]
        } else {
          set editdata_filename [tk_getSaveFile -initialdir [pwd] ]
        }
        if { $editdata_filename != "" } {
          if {[catch {open $editdata_filename w} editdata_fileid]} {
            puts "editdata: $editdata_fileid"
          } else {
            puts -nonewline $editdata_fileid [.editdata.text get 1.0 {end - 1 chars}]
            close $editdata_fileid
          }
        }
      }
      editdata_save
    }
    pack $window.buttons.saveas  -side left -expand 1
  }

  eval text $window.text -undo 1 -relief sunken -bd 2 -yscrollcommand \"$window.yscroll set\" -setgrid 1 \
       -xscrollcommand \"$window.xscroll set\" -wrap $wrap -height 30 $text_tabs_setting
  scrollbar $window.yscroll -command  "$window.text yview"
  scrollbar $window.xscroll -command "$window.text xview" -orient horiz
  pack $window.yscroll -side right -fill y
  pack $window.text -expand yes -fill both
  pack $window.xscroll -side bottom -fill x
  bind $window <Escape> "$window.buttons.cancel invoke"

  # 20171103 insert at insertion cursor(insert tag) instead of 0.0
  $window.text insert insert $data
  tkwait window $window
  xschem set semaphore [expr {[xschem get semaphore] -1}]
  return $tctx::retval
}

proc textwindow {filename {ro {}}} {
  global textwindow_wcounter
  global textwindow_w
  # set textwindow_w .win$textwindow_wcounter
  # catch {destroy $textwindow_w}
  set textwindow_wcounter [expr {$textwindow_wcounter+1}]
  set textwindow_w .win$textwindow_wcounter


  global textwindow_filename text_tabs_setting tabstop
  global textwindow_fileid
  set textwindow_filename $filename
  toplevel $textwindow_w
  wm title $textwindow_w $filename
  wm iconname $textwindow_w $filename
  # wm transient $textwindow_w [xschem get topwindow]
 frame $textwindow_w.buttons
  pack $textwindow_w.buttons -side bottom -fill x -pady 2m
  button $textwindow_w.buttons.dismiss -text Dismiss -command "destroy $textwindow_w"
  pack $textwindow_w.buttons.dismiss -side left -expand 1
  if { $ro eq {} } {
    button $textwindow_w.buttons.save -text "Save" -command \
     {
      if {[catch {open $textwindow_filename w} textwindow_fileid]} {
        puts "textwindow: $textwindow_fileid"
      } else {
        puts -nonewline $textwindow_fileid [$textwindow_w.text get 1.0 {end - 1 chars}]
        close $textwindow_fileid
      }
      destroy $textwindow_w
     }
    pack $textwindow_w.buttons.save  -side left -expand 1
  }

  eval text $textwindow_w.text -undo 1 -relief sunken -bd 2 -yscrollcommand \"$textwindow_w.yscroll set\" -setgrid 1 \
       -xscrollcommand \"$textwindow_w.xscroll set\" -wrap none -height 30 $text_tabs_setting
  scrollbar $textwindow_w.yscroll -command  "$textwindow_w.text yview"
  scrollbar $textwindow_w.xscroll -command "$textwindow_w.text xview" -orient horiz
  pack $textwindow_w.yscroll -side right -fill y
  pack $textwindow_w.text -expand yes -fill both
  pack $textwindow_w.xscroll -side bottom -fill x
  bind $textwindow_w <Escape> "$textwindow_w.buttons.dismiss invoke"
  set textwindow_fileid [open $filename r]
  if {[catch {open $filename r} textwindow_fileid]} {
    puts "textwindow: $textwindow_fileid"
  } else {
    $textwindow_w.text insert insert [read $textwindow_fileid]
    close $textwindow_fileid
  }
  return {}
}

proc viewdata {data {ro {}} {win .view} {wrap none}} {
  global viewdata_wcounter viewdata_filename
  global viewdata_w OS viewdata_fileid env has_x text_tabs_setting tabstop
  if {![info exists has_x]} {return}
  # set viewdata_w .view$viewdata_wcounter
  # catch {destroy $viewdata_w}
  if {$win eq {.view}} {
    set viewdata_wcounter [expr {$viewdata_wcounter+1}]
    set viewdata_w $win$viewdata_wcounter
  } else {
    set viewdata_w $win
  }
  set tctx::rcode {}
  toplevel $viewdata_w
  wm title $viewdata_w {View data}
  # wm transient $viewdata_w [xschem get topwindow]
  frame $viewdata_w.buttons
  pack $viewdata_w.buttons -side bottom -fill x -pady 2m

  button $viewdata_w.buttons.dismiss -text Dismiss -command  "destroy $viewdata_w"
  pack $viewdata_w.buttons.dismiss -side left -expand 1

  if { $ro eq {} } {
    button $viewdata_w.buttons.saveas -text {Save As} -command  {
      if {$OS == "Windows"} {
        set viewdata_filename [tk_getSaveFile -initialdir $env(windir) ]
      } else {
        set viewdata_filename [tk_getSaveFile -initialdir [pwd] ]
      }
      if { $viewdata_filename != "" } {
        if {[catch {open $viewdata_filename w} viewdata_fileid]} {
          puts "viewdata: $viewdata_fileid"
        } else {
          puts -nonewline $viewdata_fileid [$viewdata_w.text get 1.0 {end - 1 chars}]
          close $viewdata_fileid
        }
      }
    }
    pack $viewdata_w.buttons.saveas  -side left -expand 1
  }

  eval text $viewdata_w.text -undo 1 -relief sunken -bd 2 -yscrollcommand \"$viewdata_w.yscroll set\" -setgrid 1 \
       -xscrollcommand \"$viewdata_w.xscroll set\" -wrap $wrap -height 30 $text_tabs_setting
  scrollbar $viewdata_w.yscroll -command  "$viewdata_w.text yview"
  scrollbar $viewdata_w.xscroll -command "$viewdata_w.text xview" -orient horiz
  pack $viewdata_w.yscroll -side right -fill y
  pack $viewdata_w.text -expand yes -fill both
  pack $viewdata_w.xscroll -side bottom -fill x
  # 20171103 insert at insertion cursor(insert tag) instead of 0.0
  $viewdata_w.text insert insert $data
  bind $viewdata_w <Escape> "$viewdata_w.buttons.dismiss invoke"
  return $tctx::rcode
}

proc sub_match_file { f {paths {}} {maxdepth -1} {first 0} {fullpath 1}} {
  global pathlist match_file_dir_arr match_file_level nolist_libs
  set res {}
  if {$maxdepth eq {}} {set maxdepth 0}
  if {$paths eq {}} {set paths $pathlist}
  foreach i $paths {
    foreach j [concat \
                [lsort -dictionary [glob -nocomplain -type f -directory $i *]] \
                [lsort -dictionary [glob -nocomplain -type d -directory $i *]] \
              ] {
      set skip 0
      foreach k $nolist_libs {
        # just check if pattern is a valid regexp
        set err [catch {regexp $k {12345}} res1]
        if {$err} {continue}
        if {[regexp $k $j]} {
          set skip 1
          break
        }
      }
      if {$skip} { continue }

      if {[file isdirectory $j] && [file readable $j]} {
        if { $maxdepth == -1 || $match_file_level < $maxdepth} {
          set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
          # if {[array names match_file_dir_arr -exact $jj] == {}} { ... }
          if {![info exists match_file_dir_arr($jj)]} {
            set match_file_dir_arr($jj) 1
            incr match_file_level
            set sub_res [sub_match_file $f $j $maxdepth $first $fullpath] ;# recursive call
            incr match_file_level -1
            if {$sub_res != {} } {
              set res [concat $res $sub_res]
              if {$first} {return $res}
            }
          }
        }
      } else {
        if {![info exists match_file_dir_arr($j)]} {
         set match_file_dir_arr($j) 1
         if {$fullpath} { set file $j } else { set file [file tail $j]}
          if {[regexp $f $file]} {
            lappend res $j
            if {$first} {return $res}
          }
        }
      }
    }
  }
  return $res
}

# find files into $paths directories matching $f
# use $pathlist global search path if $paths empty
# recursively descend directories
proc match_file  { f {paths {}} {maxdepth -1} {fullpath 1} } {
  global match_file_dir_arr match_file_level
  set err [catch {regexp $f {12345}} res]
  if {$err} {return {}}
  set match_file_level 0
  catch {unset match_file_dir_arr}
  set res  [sub_match_file $f $paths $maxdepth 0 $fullpath]
  catch {unset match_file_dir_arr}
  # return [join $res \n]
  return $res
}

# find files into $paths directories matching $f
# use $pathlist global search path if $paths empty
# recursively descend directories
# only return FIRST FOUND
proc match_file_first  { f {paths {}} {maxdepth -1} {fullpath 1} } {
  global match_file_dir_arr match_file_level
  set err [catch {regexp $f {12345}} res]
  if {$err} {return {}}
  set match_file_level 0
  catch {unset match_file_dir_arr}
  set res  [sub_match_file $f $paths $maxdepth 1 $fullpath]
  catch {unset match_file_dir_arr}
  return $res
}


proc sub_find_file { f {paths {}} {maxdepth -1} {first 0}} {
  global pathlist match_file_dir_arr match_file_level nolist_libs
  set res {}
  if {$paths eq {}} {set paths $pathlist}
  foreach i $paths {
    foreach j [glob -nocomplain -directory $i *] {

      set skip 0
      foreach k $nolist_libs {
        # just check if pattern is a valid regexp
        set err [catch {regexp $k {12345}} res1]
        if {$err} {continue}
        if {[regexp $k $j]} {
          set skip 1
          break
        }
      }
      if {$skip} { continue }

      if {[file isdirectory $j]  && [file readable $j]} {
        if { $maxdepth == -1 || $match_file_level < $maxdepth} {
          set jj [regsub {/ $} [file normalize ${j}/\ ] {}]
          # if {[array names match_file_dir_arr -exact $jj] == {}} { ... }
          if {![info exists match_file_dir_arr($jj)]} {
            set match_file_dir_arr($jj) 1
            incr match_file_level
            set sub_res [sub_find_file $f $j $maxdepth $first] ;# recursive call
            incr match_file_level -1
            if {$sub_res != {} } {
              set res [concat $res $sub_res]
              if {$first} {return $res}
            }
          }
        }
      } else {
        set fname [file tail $j]
        if {![info exists match_file_dir_arr($j)]} {
          set match_file_dir_arr($j) 1
          if {$fname == $f} {
            lappend res $j
            if {$first} {return $res}
          }
        }
      }
    }
  }
  return $res
}

# find given file $f into $paths directories
# use $pathlist global search path if $paths empty
# recursively descend directories
proc find_file { f {paths {}} {maxdepth -1}} {
  global match_file_dir_arr match_file_level
  set match_file_level 0
  catch {unset match_file_dir_arr}
  set res  [sub_find_file $f $paths $maxdepth 0]
  catch {unset match_file_dir_arr}

  return $res
}

# find given file $f into $paths directories
# use $pathlist global search path if $paths empty
# recursively descend directories
# only return FIRST FOUND
proc find_file_first  { f {paths {}} {maxdepth -1}} {
  global match_file_dir_arr match_file_level
  set match_file_level 0
  catch {unset match_file_dir_arr}
  set res  [sub_find_file $f $paths $maxdepth 1]
  catch {unset match_file_dir_arr}
  return $res
}


# alternative implementation of "file dirname ... "
# that does not mess with http:// (file dirname removes double slashes)
proc get_directory {f} {
  if {![regexp {/} $f]} {
    set r .
  } else {
    set r [regsub {/[^/]*$} $f {}]
  }
  regsub {/\.$} $r {} r
  return $r
}

# set 'n' last directory components to every symbol
proc fix_symbols {n {pattern {}}} {
  xschem push_undo
  # just check if pattern is a valid regexp
  set err [catch {regexp $pattern {12345}} res1]
  if {$err} {return}
  foreach {i s t} [xschem instance_list] {
    if {$pattern ne {} && ![regexp $pattern $s]} {continue}
    if {![regexp {\(.*\)$} $s param]} { set param {}}
    regsub {\([^)]*\)$} $s {} s
    set sympath  [find_file_first [file tail $s]]
    if { $sympath  ne {}} {
      set new_sym_ref [get_cell $sympath $n]$param
      puts "Remapping $i:  $s$param --> $new_sym_ref"
      xschem reset_symbol $i $new_sym_ref
    }
  }
  xschem reload_symbols
  xschem set_modify 1
  xschem redraw
}

# fetch a remote url into ${XSCHEM_TMP_DIR}/xschem_web
proc download_url {url} {
  global XSCHEM_TMP_DIR download_url_helper OS has_x
  # puts "download_url: $url"
  # puts "xschem_web_dirname: $xschem_web_dirname"
  set xschem_web_dirname [xschem get xschem_web_dirname]
  if {![file exists $xschem_web_dirname]} {
    set msg {xschem_web_xxxxx directory was not created. Remote url download will not work}
    puts stderr $msg
    if {[info exists has_x]} {
      alert_ $msg {} 0 0
    }
    return 1 ;# fail
  }
  if {$OS eq "Windows"} {
    set cmd "cmd /c \"cd ${xschem_web_dirname} & $download_url_helper $url\""
    set r [catch {eval exec $cmd } res]
  } else {
    set r [catch {exec sh -c "cd ${xschem_web_dirname}; $download_url_helper $url"} res]
  }
  # puts "download_url: url=$url, exit code=$r, res=$res"
  return $r
}

# use some heuristic to find a sub sch/sym reference in the web repository.
proc try_download_url {dirname sch_or_sym} {
  set url $dirname/$sch_or_sym
  # puts "try_download_url: dirname=$dirname, sch_or_sym=$sch_or_sym"
  set r [download_url $url]
  if { $r!=0} {
    # count # of directories in sch/sym reference
    set nitems [regexp -all {/+} $sch_or_sym]
    # puts "try_download_url: dirname=$dirname, sch_or_sym=$sch_or_sym, nitems=$nitems"
    while { $nitems > 0} {
      # remove one path component from dirname and try to download URL
      set dirname [get_directory $dirname]
      incr nitems -1
      set url $dirname/$sch_or_sym
      set r [download_url $url]
      # done if url found
      if { $r == 0 } { break }
    }
  }
}

# Given an absolute path 'symbol' of a symbol/schematic remove the path prefix
# if file is in a library directory (a $pathlist dir)
# Example: rel_sym_path /home/schippes/share/xschem/xschem_library/devices/iopin.sym
#          devices/iopin.sym
proc rel_sym_path {symbol {paths {}} } {
  global OS pathlist env
  # puts "rel_sym_path: $symbol    $paths"

  if { $paths eq {}} {set paths $pathlist}
  regsub {^~/} $symbol ${env(HOME)}/ symbol
  # if {$OS eq "Windows"} {
  #   if {![regexp {^[A-Za-z]\:/} $symbol]} {
  #     set symbol [pwd]/$symbol
  #   }
  # } else {
  #   if {![regexp {^/} $symbol]} {
  #     set symbol [pwd]/$symbol
  #   }
  # }
  set curr_dirname [pwd]
  set name {}
  foreach path_elem $paths {
    if { ![string compare $path_elem .]  && [info exist curr_dirname]} {
      set path_elem $curr_dirname
    }
    regsub {/*$} $path_elem {/} path_elem
    set pl [string length $path_elem]
    if { [string equal -length $pl $path_elem $symbol] } {
      set name [string range $symbol [expr {$pl}] end]
      # When $path_elem is "C:/", string equal will match,
      # but $path_elem should not be removed from $name if $name
      # has more / for more directory levels.
      if {$OS eq "Windows" && [regexp {^[A-Za-z]\:/$} $path_elem ] && [regexp {/} $name]} {
           set name {}
           continue
      }
      break
    }
  }
  if {$name eq {} } {
    # no known lib, so return full path
    set name ${symbol}
    if {[file dirname $name] eq $curr_dirname} {
      # remove path if file is in pwd
      set name [file tail $name]
    }

  }

  return $name
}

# given a symbol reference 'sym' return its absolute path
# Example: % abs_sym_path devices/iopin.sch
#          /home/schippes/share/xschem/xschem_library/devices/iopin.sym
proc abs_sym_path {fname {ext {} } {paths {}}} {
  global pathlist OS

  if { $paths eq {}} {set paths $pathlist}
  set curr_dirname [pwd]
  ## empty: do nothing
  if {$fname eq {} } return {}
  ## add extension for 1.0 file format compatibility
  if { $ext ne {} } {
    set fname [file rootname $fname]$ext
  }
  # web url: return as is
  if { [regexp {^https?://} $fname]} {
     return "$fname"
  }
  if {$OS eq "Windows"} {
    ## absolute path: return as is
    if { [regexp {^[A-Za-z]\:/} $fname ] } {
      return "$fname"
    }
    # network drive, return as is
    if { [regexp {^/} $fname] } {
      return "$fname"
    }
  } else {
    ## absolute path: return as is
    if { [regexp {^/} $fname] } {
      return "$fname"
    }
  }
  ## replace all runs of multiple / with single / in fname
  regsub -all {/+} $fname {/} fname
  ## replace all '/./' with '/'
  while {[regsub {/\./} $fname {/} fname]} {}
  ## transform  a/b/../c to a/c or a/b/c/.. to a/b
  while {[regsub {([^/]*\.*[^./]+[^/]*)/\.\./?} $fname {} fname] } {}
  ## remove trailing '/'  or '/.'
  while {[regsub {/\.?$} $fname {} fname]} {}
  ## 'found' set to 1 if fname begins with './' or  '../'
  set found 0
  ## remove any leading './'
  while {[regsub {^\./} $fname {} fname]} {set found 1}
  ## if previous operation left fname empty set to '.'
  if { $fname eq {} } { set fname . }
  ## if fname is just "." return $curr_dirname
  if {[regexp {^\.$} $fname] } { return "$curr_dirname" }
  set tmpdirname $curr_dirname
  set tmpfname $fname
  ## if tmpfname begins with '../' remove this prefix and remove one path component from tmpdirname
  while { [regsub {^\.\./} $tmpfname {} tmpfname] } {
    set found 1
    set tmpdirname [file dirname $tmpdirname]
  }
  ## if tmpfname reduced to '..' return dirname of tmpdirname
  if { $tmpfname eq {..}} { return "[file dirname $tmpdirname]" }
  ## if given file begins with './' or '../' and dir or file exists relative to curr_dirname
  ## just return it.
  if {$found } {
    set tmpfname "${tmpdirname}/$tmpfname"
    if { [file exists "$tmpfname"] } { return "$tmpfname" }
    ## if file does not exists but directory does return anyway
    if { [file exists [file dirname "$tmpfname"]] } { return "$tmpfname" }
  }
  ## if fname is present in one of the paths paths get the absolute path
  set name {}
  foreach path_elem $paths {
    if { ![string compare $path_elem .]  && [info exist curr_dirname]} {
      set path_elem $curr_dirname
    }
    regsub {/*$} $path_elem {/} path_elem
    set fullpath "${path_elem}${fname}"
    if { [file exists $fullpath] } {
      set name $fullpath
      break
    }
  }
  ## nothing found -> use current schematic directory
  if {$name eq {} } {
    set name "$curr_dirname/$fname"
  }
  return "$name"
}

proc add_ext {fname ext} {
  return [file rootname $fname]$ext
}

proc swap_compare_schematics {} {
  global compare_sch XSCHEM_TMP_DIR
  set sch1 [xschem get schname]
  set sch2 [xschem get sch_to_compare]
  if { $sch1 eq $sch2} {
    set msg "Swapping with same schematic name on disk. Suggest to cancel to avoid losing/overwriting data"
    set answer [tk_messageBox -message  $msg \
       -icon warning -parent [xschem get topwindow]  -type okcancel]
    if { $answer ne {ok} } {return}
  }
  puts "swap_compare_schematics:\n  sch1=$sch1\n  sch2=$sch2"
  if {$sch2 ne {}} {
    xschem load -nofullzoom -gui $sch2
    set current  [xschem get schname]
    # Use "file tail" to handle equality of
    # https://raw.githubusercon...tb_reram.sch and /tmp/xschem_web/tb_reram.sch
    if {( [regexp "^${XSCHEM_TMP_DIR}/xschem_web_" $current] && [file tail $current] eq [file tail $sch2] ) ||
        ( $current eq $sch2 )} { ;# user did not cancel loading
      puts "swapping..."
      if {$compare_sch} {
        xschem compare_schematics $sch1
      } else {
        xschem set sch_to_compare $sch1
      }
    }
  }
}
proc input_line {txt {cmd {}} {preset {}}  {w 12}} {
  global wm_fix
  set tctx::retval $preset
  if { [winfo exists .dialog] } {return}
  xschem set semaphore [expr {[xschem get semaphore] +1}]
  toplevel .dialog -class Dialog
  wm title .dialog {Input number}
  wm transient .dialog [xschem get topwindow]
  set X [expr {[winfo pointerx .dialog] - 60}]
  set Y [expr {[winfo pointery .dialog] - 35}]
  # 20100203
  if { $wm_fix } { tkwait visibility .dialog }
  wm geometry .dialog "+$X+$Y"
  frame .dialog.f1
  label .dialog.f1.l -text $txt
  entry .dialog.f1.e -width $w
  entry_replace_selection .dialog.f1.e
  .dialog.f1.e insert 0 $preset
  .dialog.f1.e selection range 0 end

  pack .dialog.f1.l .dialog.f1.e -side left
  frame .dialog.f2

  button .dialog.f2.ok -text OK  -command  "
    if { {$cmd} ne {} } {
      eval $cmd \[.dialog.f1.e get\]
    }
    set tctx::retval \[.dialog.f1.e get\]
    destroy .dialog
  "
  button .dialog.f2.cancel -text Cancel -command {  set tctx::retval {}; destroy .dialog }
  pack .dialog.f2.ok  -anchor w -side left
  pack .dialog.f2.cancel -anchor e
  pack .dialog.f1
  pack .dialog.f2 -expand yes -fill x
  bind .dialog <Escape> {.dialog.f2.cancel invoke}
  bind .dialog <Return> {.dialog.f2.ok invoke}
  tkwait visibility .dialog
  grab set .dialog
  focus .dialog.f1.e
  tkwait window .dialog
  xschem set semaphore [expr {[xschem get semaphore] -1}]
  return $tctx::retval
}

proc launcher {launcher_var {launcher_program {} } } {
  # env, XSCHEM_SHAREDIR and netlist_dir not used directly but useful in paths passed thru launcher_var
  global launcher_default_program env XSCHEM_SHAREDIR netlist_dir OS

  regsub {/$} $netlist_dir {} netlist_dir
  if { ![string compare $launcher_program {}] } { set launcher_program $launcher_default_program}
  if {$OS == "Windows"} {
    eval exec $launcher_program \"\" {[subst $launcher_var]}
  } else {
    eval exec  [subst $launcher_program] {[subst $launcher_var]} &
  }
}

proc reconfigure_layers_button { { topwin {} } } {
   global dark_colorscheme
   set c [xschem get rectcolor]
   $topwin.menubar entryconfigure Layers -background [lindex $tctx::colors $c]
   if { $dark_colorscheme == 1 && $c == 0} {
     $topwin.menubar entryconfigure Layers -foreground white
   } else {
     $topwin.menubar entryconfigure Layers -foreground black
   }
}

proc reconfigure_layers_menu { {topwin {} } } {
   global dark_colorscheme
   set j 0
   foreach i $tctx::colors {
     set ind_bg white
     if {  $j == [xschem get backlayer] } {
        if { $dark_colorscheme == 1 } {
          set layfg white
        } else {
          set layfg black
        }
     } else {
        if { $dark_colorscheme == 1 } {
          set layfg black
        } else {
          set layfg white
        }
     }
     $topwin.menubar.layers entryconfigure $j -activebackground $i \
        -background $i -foreground $layfg -activeforeground $layfg
     incr j
   }
   reconfigure_layers_button $topwin
}

proc get_file_path {ff} {
  global env OS
  # Absolute path ? return as is.
  #        Linux                Windows
  if { [regexp {^/} $ff] || [regexp {^[a-zA-Z]:} $ff] } { return $ff }
  if {$OS == "Windows"} {
    set mylist [split $env(PATH) \;]
  } else {
    set mylist [split $env(PATH) :]
  }
  foreach i $mylist {
    set ii $i/$ff
    if { [file exists $ii]} {return $ii}
  }
  # nothing found, return $ff as is and hope for the best :-)
  return $ff
}

#
# Balloon help system, from https://wiki.tcl-lang.org/page/balloon+help
#
proc balloon {w help {pos 1} {motion_kill 0} {delay 1000}} {
    bind $w <Enter>     "after $delay   [list balloon_show %W [list $help] $pos]"
    bind $w <Leave>     "after cancel [list balloon_show %W [list $help] $pos]; destroy %W.balloon"
    if {$motion_kill} {
      bind $w <Motion> "
        if {\[winfo exists %W.balloon\]} {
          after cancel [list balloon_show %W [list $help] $pos]; destroy %W.balloon
        }"
    }
    bind $w <FocusOut>  "after cancel [list balloon_show %W [list $help] $pos]; destroy %W.balloon"
}

### pos:
## 0: set balloon close to mouse
## 1: set balloon below related widget
proc balloon_show {w arg pos} {
    if {[eval winfo containing  [winfo pointerxy .]]!=$w} {return}
    set top $w.balloon
    catch {destroy $top}
    toplevel $top -bd 1 -background black -takefocus 0
    wm overrideredirect $top 1
    if {[string equal [tk windowingsystem] aqua]}  {
        ::tk::unsupported::MacWindowStyle style $top help none
    }
    # pack [message $top.txt -aspect 10000 -takefocus 0 -fg black -background lightyellow \
    #     -font fixed -text $arg]
    pack [label $top.txt -justify left -takefocus 0 -fg black -background lightyellow \
        -font fixed -text $arg]
    if { $pos } {
      set wmx [winfo rootx $w]
      set wmy [expr {[winfo rooty $w]+[winfo height $w]}]
    } else {
      set wmx [expr {[winfo pointerx $w] + 20}]
      set wmy [winfo pointery $w]
    }
    wm geometry $top [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$wmx+$wmy
    raise $top
}

proc context_menu { } {

  set tctx::retval 0
  if {[info tclversion] >= 8.5} {
    set font TkDefaultFont
    # set font {Sans 8 bold}
  } else {
    set font fixed
  }
  set selection  [expr {[xschem get lastsel] > 0}]
  toplevel .ctxmenu
  wm overrideredirect .ctxmenu 1
  set x [expr {[winfo pointerx .ctxmenu] - 10}]
  set y [expr {[winfo pointery .ctxmenu] - 10}]
  if { !$selection} {
    button .ctxmenu.b9 -text {Open most recent} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuRecent -compound left \
      -font [subst $font] -command {set tctx::retval 9; destroy .ctxmenu}
  }
  button .ctxmenu.b10 -text {Edit attributes} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuEdit -compound left \
    -font [subst $font] -command {set tctx::retval 10; destroy .ctxmenu}
  button .ctxmenu.b11 -text {Edit attr in editor} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuEdit -compound left \
    -font [subst $font] -command {set tctx::retval 11; destroy .ctxmenu}
  if {$selection} {
    button .ctxmenu.b12 -text {Descend schematic} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuDown -compound left \
      -font [subst $font] -command {set tctx::retval 12; destroy .ctxmenu}
    button .ctxmenu.b13 -text {Descend symbol} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuDownSym -compound left \
      -font [subst $font] -command {set tctx::retval 13; destroy .ctxmenu}
    button .ctxmenu.b18 -text {Delete selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuDelete -compound left \
      -font [subst $font] -command {set tctx::retval 18; destroy .ctxmenu}
    button .ctxmenu.b7 -text {Cut selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuCut -compound left \
      -font [subst $font] -command {set tctx::retval 7; destroy .ctxmenu}
    button .ctxmenu.b15 -text {Copy selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuCopy -compound left \
      -font [subst $font] -command {set tctx::retval 15; destroy .ctxmenu}
    button .ctxmenu.b16 -text {Move selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuMove -compound left \
      -font [subst $font] -command {set tctx::retval 16; destroy .ctxmenu}
    button .ctxmenu.b17 -text {Duplicate selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuDuplicate -compound left \
      -font [subst $font] -command {set tctx::retval 17; destroy .ctxmenu}
    button .ctxmenu.b22 -text {Rotate selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuRotate -compound left \
      -font [subst $font] -command {xschem rotate; destroy .ctxmenu}
    button .ctxmenu.b23 -text {Flip selection} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuFlip -compound left \
      -font [subst $font] -command {xschem flip; destroy .ctxmenu}
    button .ctxmenu.b24 -text {Rotate in-place sel.} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuRotate -compound left \
      -font [subst $font] -command {xschem rotate_in_place; destroy .ctxmenu}
    button .ctxmenu.b25 -text {Flip in-place sel.} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuFlip -compound left \
      -font [subst $font] -command {xschem flip_in_place; destroy .ctxmenu}
  }
  if {!$selection} {
    button .ctxmenu.b14 -text {Go to upper level} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuUp -compound left \
      -font [subst $font] -command {set tctx::retval 14; destroy .ctxmenu}
    button .ctxmenu.b1 -text {Insert symbol} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuSymbol -compound left \
      -font [subst $font] -command {set tctx::retval 1; destroy .ctxmenu}
    button .ctxmenu.b2 -text {Insert wire} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuWire -compound left \
      -font [subst $font] -command {set tctx::retval 2; destroy .ctxmenu}
    button .ctxmenu.b3 -text {Insert line} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuLine -compound left \
      -font [subst $font] -command {set tctx::retval 3; destroy .ctxmenu}
    button .ctxmenu.b4 -text {Insert box} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuBox -compound left \
      -font [subst $font] -command {set tctx::retval 4; destroy .ctxmenu}
    button .ctxmenu.b5 -text {Insert polygon} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuPoly -compound left \
      -font [subst $font] -command {set tctx::retval 5; destroy .ctxmenu}
    button .ctxmenu.b19 -text {Insert arc} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuArc -compound left \
      -font [subst $font] -command {set tctx::retval 19; destroy .ctxmenu}
    button .ctxmenu.b20 -text {Insert circle} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuCircle -compound left \
      -font [subst $font] -command {set tctx::retval 20; destroy .ctxmenu}
    button .ctxmenu.b6 -text {Insert text} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuText -compound left \
      -font [subst $font] -command {set tctx::retval 6; destroy .ctxmenu}
    button .ctxmenu.b8 -text {Paste clipboard} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuPaste -compound left \
      -font [subst $font] -command {set tctx::retval 8; destroy .ctxmenu}
  }
  button .ctxmenu.b21 -text {Abort command} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuAbort -compound left \
    -font [subst $font] -command {set tctx::retval 21; destroy .ctxmenu}

  pack .ctxmenu.b21 -fill x -expand true
  if {!$selection} {
    pack .ctxmenu.b9  -fill x -expand true
  }
  pack .ctxmenu.b10 .ctxmenu.b11 -fill x -expand true
  if {$selection} {
    pack .ctxmenu.b12 .ctxmenu.b13 .ctxmenu.b18 -fill x -expand true
    pack .ctxmenu.b22 .ctxmenu.b23 -fill x -expand true
    pack .ctxmenu.b24 .ctxmenu.b25 -fill x -expand true
  }
  if {!$selection} {
    pack .ctxmenu.b14 -fill x -expand true
    pack .ctxmenu.b1 .ctxmenu.b2 .ctxmenu.b3 .ctxmenu.b4 .ctxmenu.b5 -fill x -expand true
    pack .ctxmenu.b19 .ctxmenu.b20 .ctxmenu.b6  -fill x -expand true
  }
  if {$selection} {
    pack .ctxmenu.b7 -fill x -expand true
    pack .ctxmenu.b15 .ctxmenu.b16 .ctxmenu.b17 -fill x -expand true
  }
  if {!$selection} {
    pack .ctxmenu.b8 -fill x -expand true
  }
  wm geometry .ctxmenu "+$x+$y"
  update
  # if window has been destroyed (by mouse pointer exiting) do nothing
  if { ![winfo exists .ctxmenu] } { return 0 }

  set wx [winfo width .ctxmenu]
  set wy [winfo height .ctxmenu]
  set sx [winfo screenwidth .]
  set sy [winfo screenheight .]
  if { $y + $wy > $sy } {
    set y [expr {$y - ( $y + $wy - $sy )} ]
  }
  if { $x + $wx > $sx } {
    set x [expr {$x - ( $x + $wx - $sx )} ]
  }
  wm geometry .ctxmenu "+$x+$y";# move away from screen edges
  bind .ctxmenu <Leave> {if { {%W} eq {.ctxmenu} } {destroy .ctxmenu}}
  tkwait window .ctxmenu
  # when context menu is destroyed an EnterNotify event is generated in the
  # main xschem window. We want to process this event before taking
  # actions based on $tctx::retval.
  update
  return $tctx::retval
}

proc tab_ctx_cmd {tab_but what} {
  global terminal editor netlist_dir OS has_x
  regsub {/$} $netlist_dir {} netlist_dir
  # get win_path from tab name
  set win_path [lindex [$tab_but cget -command] 3] ;# xschem new_schematic switch .x1.drw
  set tablist [xschem tab_list]
  set found 0
  foreach {tabname filename} $tablist {
    if {$win_path eq $tabname} {
      set found 1
      break
    }
  }
  if {!$found} { set filename {}}
  if { $filename ne {} } {
    if {$what eq {dir}} {
      if {$OS == "Windows" } {
        set dir [file dirname $filename]
        set command [list {*}[auto_execok start] {}]
        exec {*}$command $dir &
      } else {
        execute 0 xdg-open [file dirname $filename]
      }
    } elseif {$what eq {copy}} {
      clipboard clear
      clipboard append $filename
    } elseif {$what eq {term}} {
      if {$OS == "Windows" } {
        set save [pwd]
        set dir [file dirname $filename]
        cd $dir
        set command [list {*}[auto_execok start] {}]
        exec {*}$command &
        cd $save
      } else {
        set save [pwd]
        cd [file dirname $filename]
        execute 0 $terminal
        cd $save
      }
    } elseif {$what eq {simterm}} {
      if {$OS == "Windows" } {
        set save [pwd]
        cd $netlist_dir
        set command [list {*}[auto_execok start] {}]
        exec {*}$command &
        cd $save
      } else {
        set save [pwd]
        cd $netlist_dir
        execute 0 $terminal
        cd $save
      }
    } elseif {$what eq {edit}} {
      eval execute 0 $editor $filename
    } elseif {$what eq {netlist}} {
      set old [xschem get current_win_path]
      set save [pwd]
      xschem new_schematic switch $win_path {} 0 ;# no draw
      if {[file exists $netlist_dir] && [file exists "$netlist_dir/[xschem get netlist_name fallback]"]} {
        cd $netlist_dir
        eval execute 0 $editor \"[xschem get netlist_name fallback]\"
        cd $save
      } else {
        if {[info exists has_x]} {
          alert_ {Netlist not existing, not yet generated} {} 0 0
        } else {
          puts {Netlist not existing, not yet generated}
        }
      }
      xschem new_schematic switch $old {} 0 ;# no draw
    } elseif {$what eq {save}} {
      set old [xschem get current_win_path]
      xschem new_schematic switch $win_path {} 0 ;# no draw
      xschem save
      xschem new_schematic switch $old {} 0 ;# no draw
    } elseif {$what eq {close}} {
      set old [xschem get current_win_path]
      set ntabs [xschem get ntabs]
      xschem new_schematic switch $win_path {} 0 ;# no draw
      set remaining_tabs [xschem exit]
      if { $old != $win_path || $remaining_tabs == $ntabs} {
        # don't switch if we were on the tab that has been closed.
        xschem new_schematic switch $old {} 1 ;# draw
      }
    } elseif {[regexp {^open } $what]} {
      set counterpart [lindex $what 1]
      set filetype  [lindex $what 2]

      # see if $counterpart already open, in this case switch to it
      set found 0
      foreach {tabname filename} $tablist {
        if {$filename eq $counterpart} {
          set found 1
          break
        }
      }
      if {$found} {
        xschem new_schematic switch $tabname {} 1 ;# draw
      } else {
        xschem new_schematic create {1} $counterpart
        if {$filetype ne {symbol} } { xschem set netlist_type symbol}
      }
    }
  }
  # puts $filename
}

proc tab_context_menu {tab_but} {
  global search_schematic

  # find filename associated with tab button
  set win_path [lindex [$tab_but cget -command] 3] ;# xschem new_schematic switch .x1.drw
  set tablist [xschem tab_list] ;# .drw filename0 .x1.drw filename1 ...
  set found 0
  foreach {tabname filename} $tablist {
    if {$win_path eq $tabname} {
      set found 1
      break
    }
  }
  if {!$found} { set filename {}}
  set old [xschem get current_win_path]
  xschem new_schematic switch $win_path {} 0 ;# no draw
  set filetype [xschem get netlist_type] ;# symbol or spice or vhdl or tedax or verilog
  xschem new_schematic switch $old {} 0 ;# no draw

  if {$filetype ne {symbol}} {
    if {$search_schematic == 1} {
      set counterpart [abs_sym_path [xschem get current_name] {.sym}]
    } else {
      set counterpart [abs_sym_path $filename .sym]
    }
    set msg {Open symbol}
    set img CtxmenuSymbol
  } else {
    if {$search_schematic == 1} {
      set counterpart [abs_sym_path [xschem get current_name] {.sch}]
    } else {
      set counterpart [abs_sym_path $filename .sch]
    }
    set msg {Open schematic}
    set img CtxmenuSchematic
  }
  # puts $counterpart

  # puts $tab_but
  set tctx::retval 0
  if {[info tclversion] >= 8.5} {
    set font {Sans 8 bold}
  } else {
    set font fixed
  }
  toplevel .ctxmenu
  wm overrideredirect .ctxmenu 1
  set x [expr {[winfo pointerx .ctxmenu] - 10}]
  set y [expr {[winfo pointery .ctxmenu] - 10}]
  button .ctxmenu.b0 -text {Tab menu} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -fg black -background white -state disabled -disabledforeground black -font [subst $font]
  button .ctxmenu.b1 -text {Copy filename} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuCopy -compound left \
    -font [subst $font] -command "set tctx::retval 1; tab_ctx_cmd $tab_but copy; destroy .ctxmenu"
  button .ctxmenu.b2 -text {Open directory} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuOpendir -compound left \
    -font [subst $font] -command "set tctx::retval 2; tab_ctx_cmd $tab_but dir; destroy .ctxmenu"
  button .ctxmenu.b3 -text {Open circuit dir. term.} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuTerm -compound left \
    -font [subst $font] -command "set tctx::retval 3; tab_ctx_cmd $tab_but term; destroy .ctxmenu"
  button .ctxmenu.b4 -text {Open sim. dir. term.} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuTerm -compound left \
    -font [subst $font] -command "set tctx::retval 4; tab_ctx_cmd $tab_but simterm; destroy .ctxmenu"
  if {$counterpart ne {}} {
    button .ctxmenu.b6 -text $msg -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image $img -compound left \
      -font [subst $font] \
      -command "set tctx::retval 6; tab_ctx_cmd $tab_but {open {$counterpart} $filetype} ; destroy .ctxmenu"
  }
  if {$filetype ne {symbol}} {
    button .ctxmenu.b5 -text {Edit netlist} -padx 3 -pady 0 -anchor w -activebackground grey50 \
       -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuEdit -compound left \
      -font [subst $font] -command "set tctx::retval 5; tab_ctx_cmd $tab_but netlist; destroy .ctxmenu"
  }
  button .ctxmenu.b7 -text {Save} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuSave -compound left \
    -font [subst $font] -command "set tctx::retval 7; tab_ctx_cmd $tab_but save; destroy .ctxmenu"
  button .ctxmenu.b8 -text {Close tab} -padx 3 -pady 0 -anchor w -activebackground grey50 \
     -bg {#d9d9d9} -fg black -activeforeground black -image CtxmenuDelete -compound left \
    -font [subst $font] -command "set tctx::retval 8; tab_ctx_cmd $tab_but close; destroy .ctxmenu"

  pack .ctxmenu.b0 -fill x -expand true
  pack .ctxmenu.b1 -fill x -expand true
  pack .ctxmenu.b2 -fill x -expand true
  pack .ctxmenu.b3 -fill x -expand true
  pack .ctxmenu.b4 -fill x -expand true
  if {$counterpart ne {}} {
    pack .ctxmenu.b6 -fill x -expand true
  }
  if {$filetype ne {symbol}} {
    pack .ctxmenu.b5 -fill x -expand true
  }
  pack .ctxmenu.b7 -fill x -expand true
  pack .ctxmenu.b8 -fill x -expand true
  wm geometry .ctxmenu "+$x+$y"
  update
  # if window has been destroyed (by mouse pointer exiting) do nothing
  if { ![winfo exists .ctxmenu] } { return 0 }

  set wx [winfo width .ctxmenu]
  set wy [winfo height .ctxmenu]
  set sx [winfo screenwidth .]
  set sy [winfo screenheight .]
  if { $y + $wy > $sy } {
    set y [expr {$y - ( $y + $wy - $sy )} ]
  }
  if { $x + $wx > $sx } {
    set x [expr {$x - ( $x + $wx - $sx )} ]
  }
  wm geometry .ctxmenu "+$x+$y";# move away from screen edges
  bind .ctxmenu <Leave> {if { {%W} eq {.ctxmenu} } {destroy .ctxmenu}}
  tkwait window .ctxmenu
  return $tctx::retval
}

#
# toolbar: Public variables that we allow to be overridden
# Code contributed by Neil Johnson (github: nejohnson)
#
proc setup_toolbar {} {
  global toolbar_visible toolbar_horiz toolbar_list XSCHEM_SHAREDIR dark_gui_colorscheme
  global toolbar_icon_zoom ctxmenu_icon_zoom
  set_ne toolbar_visible 1
  set_ne toolbar_horiz   1
  set_ne toolbar_list {
    FileOpen
    FileSave
    FileReload
    ---
    EditUndo
    EditRedo
    EditCut
    EditCopy
    EditPaste
    EditDelete
    ---
    EditDuplicate
    EditMove
    ---
    EditPushSch
    EditPushSym
    EditPop
    ---
    ViewRedraw
    ViewToggleColors
    ---
    ToolInsertSymbol
    ToolInsertText
    ToolInsertWire
    ToolInsertLine
    ToolInsertRect
    ToolInsertPolygon
    ToolInsertArc
    ToolInsertCircle
    ---
    ToolSearch
    ---
    ToolJoinTrim
    ToolBreak
  }
  #
  # Pull in the toolbar graphics resources
  #
  source $XSCHEM_SHAREDIR/resources.tcl
  #
  # Separation bar counter
  #
}

#
# Create a tool button which may be displayed
#
proc toolbar_add {name cmd { help "" } {topwin {} } } {
    global dark_gui_colorscheme toolbar_icon_zoom

    set toolbar_icon_size [expr {$toolbar_icon_zoom * 24}]
    if { $dark_gui_colorscheme ==1} {
      set bg black
    } else {
      set bg white
    }
    if {![winfo exists  $topwin.toolbar]} {
       frame $topwin.toolbar -relief raised -bd 0 -background $bg -takefocus 0
    }
    if { ![winfo exists $topwin.toolbar.b$name]} {
      button $topwin.toolbar.b$name -image img$name -relief flat -bd 0 -takefocus 0 \
      -background $bg -fg $bg -height $toolbar_icon_size  -padx 0 -pady 0 -command $cmd
      if { $help == "" } { balloon $topwin.toolbar.b$name $name } else { balloon $topwin.toolbar.b$name $help }
    }
}

#
# Show the toolbar in horizontal or vertical position, parsing the toolbar list and
# adding any separators as needed.
#
proc toolbar_show { { topwin {} } } {
    global toolbar_horiz toolbar_list toolbar_visible tabbed_interface

    # puts "toolbar_show: $topwin"
    set toolbar_visible 1
    if {![winfo exists  $topwin.toolbar]} {
       frame $topwin.toolbar -relief raised -bd 0 -background white -takefocus 0
    }
    if {[winfo ismapped $topwin.toolbar]} {return}
    if { $toolbar_horiz } {
        if {$tabbed_interface} {
          pack $topwin.toolbar -fill x -before $topwin.tabs
        } else {
          pack $topwin.toolbar -fill x -before $topwin.drw
        }
    } else {
        pack $topwin.toolbar -side left -fill y -before $topwin.drw
    }
    set pos "top"
    if { $toolbar_horiz } { set pos "left" }
    set tlist [ winfo children $topwin.toolbar ]
    set toolbar_sepn 0
    foreach b $toolbar_list {
        if { $b == "---" } {
            if { $toolbar_horiz } {
                frame $topwin.toolbar.sep$toolbar_sepn -background lightgrey -width 2 -takefocus 0
                pack $topwin.toolbar.sep$toolbar_sepn -side $pos -padx 1 -pady 0 -fill y
            } else {
                frame $topwin.toolbar.sep$toolbar_sepn -background lightgrey -height 2 -takefocus 0
                pack $topwin.toolbar.sep$toolbar_sepn -side $pos -padx 0 -pady 1 -fill x
            }
            incr toolbar_sepn
        } else {
            if { [ lsearch -exact $tlist "$topwin.toolbar.b$b" ] != -1 } {
                pack $topwin.toolbar.b$b -side $pos
            } else {
                puts "Error: unknown toolbar item \"$b\""
            }
        }
    }
    set pos "bottom"
    if { $toolbar_horiz } { set pos "right" }
    foreach b { Waves Simulate Netlist } {
        pack $topwin.toolbar.b$b -side $pos
    }
}

#
# Hide the toolbar, unpack the buttons, and remove any separators
#
proc toolbar_hide { { topwin {} } } {
    global toolbar_visible
    set toolbar_visible 0
    if {![winfo exists $topwin.toolbar]} {return}
    if {![winfo ismapped $topwin.toolbar]} {return}
    set tlist [ winfo children $topwin.toolbar ]
    foreach b $tlist {
        pack forget $b
        if { [ string match "$topwin.toolbar.sep*" $b ] == 1 } {
            destroy $b
        }
    }
    pack forget $topwin.toolbar
    set toolbar_visible 0
}


proc add_toolbuttons {{topwin {}}} {
  # toolbar_add FileNew {xschem clear schematic} "New Schematic" $topwin
  # toolbar_add FileNewSym {xschem clear symbol} "New Symbol" $topwin
  toolbar_add FileOpen "xschem load" "Open File" $topwin
  toolbar_add FileSave "xschem save" "Save File" $topwin
  # toolbar_add FileMerge "xschem merge" "Merge File" $topwin
  toolbar_add FileReload {
      if {[alert_ "Are you sure you want to reload?" {} 0 1] == 1} {
        xschem reload
      }
    } "Reload File" $topwin
  toolbar_add EditUndo "xschem undo; xschem redraw" "Undo" $topwin
  toolbar_add EditRedo "xschem redo; xschem redraw" "Redo" $topwin
  toolbar_add EditCut "xschem cut" "Cut" $topwin
  toolbar_add EditCopy "xschem copy" "Copy" $topwin
  toolbar_add EditPaste "xschem paste" "Paste" $topwin
  toolbar_add EditDelete "xschem delete" "Delete" $topwin
  toolbar_add EditDuplicate "xschem copy_objects" "Duplicate objects" $topwin
  toolbar_add EditMove "xschem move_objects" "Move objects" $topwin
  toolbar_add EditPushSch "xschem descend" "Push schematic" $topwin
  toolbar_add EditPushSym "xschem descend_symbol" "Push symbol" $topwin
  toolbar_add EditPop "xschem go_back" "Pop" $topwin
  toolbar_add ViewRedraw "xschem redraw" "Redraw" $topwin
  # toolbar_add ViewZoomIn "xschem zoom_in" "Zoom In" $topwin
  # toolbar_add ViewZoomOut "xschem zoom_out" "Zoom Out" $topwin
  # toolbar_add ViewZoomBox "xschem zoom_box" "Zoom Box" $topwin
  toolbar_add ViewToggleColors {
     xschem toggle_colorscheme
  } "Toggle Color Scheme" $topwin
  toolbar_add ToolInsertSymbol "xschem place_symbol" "Insert Symbol" $topwin
  toolbar_add ToolInsertText "xschem place_text" "Insert Text" $topwin
  toolbar_add ToolInsertWire "xschem wire" "Insert Wire" $topwin
  toolbar_add ToolInsertLine "xschem line" "Insert Line" $topwin
  toolbar_add ToolInsertRect "xschem rect" "Insert Rectangle" $topwin
  toolbar_add ToolInsertPolygon "xschem polygon" "Insert Polygon" $topwin
  toolbar_add ToolInsertArc "xschem arc" "Insert Arc" $topwin
  toolbar_add ToolInsertCircle "xschem circle" "Insert Circle" $topwin
  toolbar_add ToolSearch property_search "Search" $topwin
  toolbar_add ToolJoinTrim "xschem trim_wires" "Join/Trim Wires" $topwin
  toolbar_add ToolBreak "xschem break_wires" "Break wires at selected\ninstance pin intersections" $topwin
  toolbar_add Netlist { xschem netlist -erc } "Create netlist" $topwin
  toolbar_add Simulate "simulate_from_button" "Run simulation" $topwin
  toolbar_add Waves { waves } "View results" $topwin
}

proc pack_tabs {} {
  global toolbar_horiz
  if {[winfo exists .toolbar] && [winfo ismapped .toolbar] } {
    if { $toolbar_horiz == 1 } {
      pack .tabs  -fill x -side top -after .toolbar
    } else {
      pack .tabs  -fill x -side top -before .toolbar
    }
  } else {
    pack .tabs  -fill x -side top -before .drw
  }
}

proc setup_tabbed_interface {} {
  global tabbed_interface toolbar_horiz has_x

  if { $tabbed_interface } {
    if { [info exists has_x] && ![winfo exists .tabs] } {
      frame .tabs -takefocus 0
      button .tabs.x0 -padx 2 -pady 0 -anchor nw -takefocus 0 \
          -text Main -command "xschem new_schematic switch .drw"
      bind .tabs.x0 <ButtonPress-3> {tab_context_menu %W}
      bind .tabs.x0 <ButtonPress-1> {swap_tabs %X %Y press}
      bind .tabs.x0 <ButtonRelease-1> {swap_tabs %X %Y release}
      button .tabs.add -padx 0 -pady 0  -takefocus 0 -text { + } \
          -command "xschem new_schematic create"
      pack .tabs.x0 .tabs.add -side left
      balloon .tabs.add {Create a new tab}
      pack_tabs
    }
  } else {
    destroy .tabs
  }
  if {$tabbed_interface} {
    set_tab_names
  }
  # update tabbed window close (X) function
  if {$tabbed_interface} {
    wm protocol . WM_DELETE_WINDOW {
      xschem exit closewindow
    }
  # restore non tabbed window close function for main window
  } else {
    wm protocol . WM_DELETE_WINDOW {
      set old  [xschem get current_win_path]
      save_ctx $old
      restore_ctx .drw
      housekeeping_ctx
      xschem new_schematic switch .drw
      xschem exit closewindow
      # did not exit (user cancel) ... switch back
      restore_ctx $old
      housekeeping_ctx
      xschem new_schematic switch $old
    }
  }
}

proc delete_tab {path} {
  regsub {\.drw$} $path {} path
  destroy .tabs$path
}

# button press on a tab, drag onto another tab, release button --> swap
proc swap_tabs {x y what} {
  if {$what eq {press} } {
    # puts "From: [winfo containing $x $y]"
    set tctx::source_swap_tab [winfo containing $x $y]
    # puts $tctx::source_swap_tab
    set cond1 [regexp {\.tabs\.x} $tctx::source_swap_tab]
    if {$cond1} {
      button .tabs.mm -padx 2 -pady 0 -anchor nw -takefocus 0 -text [$tctx::source_swap_tab cget -text]
      bind . <B1-Motion> {
        place .tabs.mm -x  [expr {[getmousex .tabs]-[winfo width .tabs.mm]/2 }] -y 0
        # raise .tabs.mm
      }
    }
  } else {
    bind . <B1-Motion> {}
    place forget .tabs.mm
    destroy .tabs.mm
    # puts "To: [winfo containing $x $y]"
    set tctx::dest_swap_tab [winfo containing $x $y]
    if {[info exists tctx::source_swap_tab] && [info exists tctx::dest_swap_tab]} {
      set cond1 [regexp {\.tabs\.x} $tctx::source_swap_tab]
      set cond2 [regexp {\.tabs\.x} $tctx::dest_swap_tab]
      set cond3 [expr { $tctx::source_swap_tab ne $tctx::dest_swap_tab }]
      if { $cond1 && $cond2  && $cond3} {
        # puts "ok for swapping ctx"
        set tablist [pack slaves .tabs]
        set sourceidx [lsearch -exact $tablist $tctx::source_swap_tab]
        set destidx [lsearch -exact $tablist $tctx::dest_swap_tab]
        incr sourceidx
        incr destidx
        set following_source_tab [lindex $tablist $sourceidx]
        set following_dest_tab [lindex $tablist $destidx]
        # puts " $tablist  --> $following_source_tab   $following_dest_tab"
        pack $tctx::source_swap_tab -side left -before $following_dest_tab
        pack $tctx::dest_swap_tab -side left -before  $following_source_tab
      }
      set tctx::source_swap_tab {}
      set tctx::dest_swap_tab {}
    }
  }
}

proc prev_tab {} {
  global tabbed_interface
  if { !$tabbed_interface} { return}
  set currwin [xschem get current_win_path]
  regsub {\.drw} $currwin {} tabname
  if {$tabname eq {}} { set tabname .x0}
  regsub {\.x} $tabname {} number
  set found 0
  set next_tab $number
  set wrap_tab $number
  set highest -10000000
  set highest_all -10000000
  set xcoord [winfo rootx .tabs$tabname]
  for {set i 0} {$i < $tctx::max_new_windows} { incr i} {
    if { $i == $number} { continue}
    if { [winfo exists .tabs.x$i] } {
      set tab_coord  [winfo rootx .tabs.x$i]
      if {$tab_coord < $xcoord && $tab_coord > $highest} {
        set found 1
        set next_tab $i
        set highest $tab_coord
      }
      if {$tab_coord > $highest_all} {
        set highest_all $tab_coord
        set wrap_tab $i
      }
    }
  }
  if {$found == 0} { set next_tab $wrap_tab}
  .tabs.x$next_tab invoke
}

proc next_tab {} {
  global tabbed_interface
  if { !$tabbed_interface} {return}
  set currwin [xschem get current_win_path]
  regsub {\.drw} $currwin {} tabname
  if {$tabname eq {}} { set tabname .x0}
  regsub {\.x} $tabname {} number
  set found 0
  set next_tab $number
  set wrap_tab $number
  set lowest 10000000
  set lowest_all 10000000
  set xcoord [winfo rootx .tabs$tabname]
  for {set i 0} {$i < $tctx::max_new_windows} { incr i} {
    if { $i == $number} { continue}
    if { [winfo exists .tabs.x$i] } {
      set tab_coord [winfo rootx .tabs.x$i]
      if {$tab_coord > $xcoord && $tab_coord < $lowest} {
        set found 1
        set next_tab $i
        set lowest $tab_coord
      }
      if {$tab_coord < $lowest_all} {
        set lowest_all $tab_coord
        set wrap_tab $i
      }
    }
  }
  if {$found == 0} { set next_tab $wrap_tab}
  .tabs.x$next_tab invoke
}

proc set_tab_names {{mod {}}} {
  global tabbed_interface has_x dark_gui_colorscheme

  if {$dark_gui_colorscheme == 0} {
    set tab_color Palegreen
  } else {
     set tab_color DarkGreen
  }

  if {[info exists has_x] && $tabbed_interface } {
    set currwin [xschem get current_win_path]
    set currsch [xschem get schname]
    regsub {\.drw} $currwin {} tabname
    if {$tabname eq {}} { set tabname .x0}
    .tabs$tabname configure -text [file tail $currsch]$mod -background $tab_color
    # puts ".tabs$tabname --> name=[file tail $currsch]$mod"
    balloon .tabs$tabname $currsch
    for { set i 0} { $i < $tctx::max_new_windows} { incr i} {
      if { [winfo exists .tabs.x$i] && ($tabname ne ".x$i")} {
         .tabs.x$i configure -background $tctx::tab_bg
      }
    }
  }
}

# what:
#      STORE    : store tab
#      GET      : get most recent tab
#      PREVIOUS : get tab before most recent
#      CLEAR    : clear all entries
#      REMOVE   : remove specified tab
#      LIST     : get full queue of tabs
# win_path:
#      .drw: main window
#      .x1.drw, .x2.drw: additional tabs
#      if unspecified or empty set to current tab.
proc tab_queue {what {win_path {}}} {
  if {$win_path eq {}} {
    set win_path [xschem get current_win_path]
  }
  if {$what eq {STORE}} {
    # remove matching existing entries
    while {[set pos [lsearch -exact $tctx::tab_queue $win_path]] >= 0} {
      set tctx::tab_queue [lreplace $tctx::tab_queue $pos $pos]
    }
    lappend tctx::tab_queue $win_path
    return $win_path
  } elseif {$what eq {GET}} {
    return [lindex $tctx::tab_queue end]
  } elseif {$what eq {PREVIOUS}} {
    set tab [lindex $tctx::tab_queue end-1]
    if {$tab eq {}} {set tab {.drw}}
    return $tab
  } elseif {$what eq {CLEAR}} {
    set tctx::tab_queue {.drw}
    return {}
  } elseif {$what eq {REMOVE}} {
    set found 0
    if {$win_path ne {.drw}} { ;# do not allow to remove main tab
      # remove matching existing entries
      while {[set pos [lsearch -exact $tctx::tab_queue $win_path]] >= 0} {
        set found 1
        set tctx::tab_queue [lreplace $tctx::tab_queue $pos $pos]
      }
    }
    return $found
  } elseif {$what eq {LIST}} {
    return $tctx::tab_queue
  }
}

proc store_geom {win filename} {
  global tabbed_interface USER_CONF_DIR

  # set geom [winfo width $win]x[winfo height $win]+[winfo rootx $win]+[winfo rooty $win]
  set geom [wm geometry $win]
  # puts "store_geom: geom=$geom"
  set geom_file $USER_CONF_DIR/geometry
  set geom_data {}
  if {$win eq {.} || $tabbed_interface eq 0} {
    if { [file exists $geom_file]} {
      set fd [ open $geom_file]
      while {[gets $fd line] >= 0} {
        if { [llength $line] == 2} {
          lassign $line f g
          set d {0}
        } elseif {[llength $line] == 3} {
          lassign $line f g d
        } else {
          continue
        }
        set f [rel_sym_path $f]
        set geom_array($f) [list $g $d]
      }
      close $fd
    }
    set geom_array($filename) [list $geom [clock seconds]]

    set geom_data {}
    foreach i [array names geom_array] {
      append geom_data "{" $i  "}" { } $geom_array($i) \n
    }
    # puts $geom_data
    # puts [llength $geom_data]
    # puts ---
    set geom_data2 [lsort -stride 3 -decreasing -index 2 -integer $geom_data]
    # puts $geom_data2
    # puts [llength $geom_data2]
    # puts ---
    set geom_data3 {}
    set j 0
    foreach i $geom_data2 {
      if {$geom_data3 ne {}} {
        if {$j % 3 == 0} {
          append geom_data3 \n
        } else {
          append geom_data3 { }
        }
      }
      append geom_data3 "{" $i  "}"
      incr j
    }

    # puts $geom_data3
    # puts [llength $geom_data3]
    # puts ===
    set geom_data $geom_data3
    write_data $geom_data\n $geom_file
  }
}

proc set_geom {win {filename {}}} {
  global USER_CONF_DIR initial_geometry fullscreen
  set geom {}
  if {$fullscreen ne 0} {return}
  if {[info exists initial_geometry]} {
    set geom $initial_geometry
  } else {
    # puts "set_geom: $win  $filename"
    set geom_file $USER_CONF_DIR/geometry
    if { [file exists $geom_file]} {

      set fd [open $geom_file]
      while {[gets $fd line] >= 0} {
        if { [llength $line] == 2} {
          lassign $line f g
          set d {}
        } elseif {[llength $line] == 3} {
          lassign $line f g d
        } else {
          continue
        }
        set f [rel_sym_path $f]
        set geom_array($f) [list $g $d]
      }
      close $fd

      if {$filename ne {} && [info exists geom_array($filename)]} {
        set geom [lindex $geom_array($filename) 0]
      }
    }
    set xmax [winfo screenwidth .]
    set ymax [winfo screenheight .]
    set n [scan $geom {%dx%d+%d+%d} x y dx dy]
    if {$n == 4} {
      # puts "xmax=$xmax, ymax=$ymax, x=$x, y=$y dx=$dx dy=$dy"
      # off screen. do not use.
      if { $dx > $xmax - 100 || $dy > $ymax - 100} {
        set geom {}
      }
    }
  }
  if {$geom ne {}} {
    # puts "set_geom: setting geometry of $win to $geom"
    wm geometry $win $geom
  }
  update
}

proc get_lastclosed {} {
  global USER_CONF_DIR

  set geom_file  $USER_CONF_DIR/geometry
  set ret {}
  if {[file exists $geom_file]} {
    set fd [open $geom_file]
    while {[gets $fd line] >= 0} {
      set ret [lindex $line 0]
      if {$ret eq {untitled.sch} || $ret eq {untitled.sym}} {
        continue
      }
      set ret [abs_sym_path $ret]
      if {[xschem check_loaded $ret] ne {}} {
        continue
      }
      if { $ret ne {}} {
        break
      }
    }
    close $fd
  }
  return $ret
}

proc get_lastopened {} {
  set f {}
  foreach f $tctx::recentfile {
    if {[xschem check_loaded $f] eq {}} {
      break
    }
  }
  return $f
}

proc quit_xschem { {force {}}} {
  global tabbed_interface

  xschem new_schematic destroy_all $force
  xschem new_schematic switch .drw
  if {[winfo exists .ins]} { .ins.bottom.dismiss invoke }
  set remaining [xschem exit closewindow $force]
  return $remaining
}

proc raise_dialog {parent window_path } {
  global file_dialog_loadfile component_browser_on_top
  foreach i ".dialog .graphdialog .load" {
    if {!$component_browser_on_top && [info exists file_dialog_loadfile ] &&
        $file_dialog_loadfile == 2 && $i eq {.load} } {
      continue
    }
    if {[winfo exists $i] && [winfo ismapped $i] && [winfo ismapped $parent] &&
        [wm stackorder $i isbelow $parent ]} {
      raise $i $window_path
    }
  }
}

proc set_old_tk_fonts {} {
  if {[info tclversion] <= 8.4} {
     set myfont {-*-helvetica-*-r-*-*-12-*-*-*-*-*-*-*}
     set mymonofont fixed
     option add *Button*font $myfont startupFile
     option add *Menubutton*font $myfont startupFile
     option add *Menu*font $myfont startupFile
     option add *Listbox*font $myfont startupFile
     option add *Entry*font $mymonofont startupFile
     option add *Text*font $mymonofont startupFile
     option add *Label*font $myfont startupFile
  }
}

proc every {interval script} {
    uplevel #0 $script
    after $interval [list every $interval $script]
}

## tcl context switching global namespace
namespace eval tctx {
  variable tctx
  variable i
  variable global_list
  variable global_array_list
  variable dialog_list
  variable tab_bg
  variable max_new_windows
  variable source_swap_tab
  variable dest_swap_tab
  variable colors
  variable tab_queue  {.drw} ;# list of last accessed tabs
}

## list of dialogs: when open do not perform context switching
## do not include .infotext as it is always open (in withdrawn mode)
set tctx::dialog_list { .ctxmenu .alert .sim .dialog .tclcmd .sl .dim }

proc no_open_dialogs {} {
  set res 1
  foreach tctx::i $tctx::dialog_list {
    if { [winfo exists $tctx::i] } {
      # puts "$tctx::i dialog open"
      set res 0
    }
  }
  return $res
}

## list of globals to save/restore on context switching
## EXCEPTIONS, not to be saved/restored:
## "textwindow_wcounter" should be kept unique as it is the number of open textwindows
## "viewdata_wcounter" should be kept unique as it is the number of open viewdatas
## "measure_id" should be kept unique since we allow only one measure tooltip in graphs
## "tabbed_interface"
## "open_in_new_window"
## "case_insensitive" case insensitive symbol lookup (on case insensitive filesystems only!)
## "dark_colors_save"
## "light_colors_save" restore default colors
## "menu_debug_var" there is only a global debug mode
## "debug_var" there is only a global debug mode
## "xschem_server_getdata" only one tcp listener per process
## "bespice_server_getdata" only one tcp listener per process
## "file_dialog_*" only one load_file_dialog window is allowed
## some file_chooser(...) vars

set tctx::global_list {
 INITIALINSTDIR INITIALLOADDIR INITIALPROPDIR INITIALTEXTDIR PDK PDK_ROOT SKYWATER_MODELS
 SKYWATER_STDCELLS XSCHEM_LIBRARY_PATH add_all_windows_drives auto_hilight
 auto_hilight_graph_nodes autofocus_mainwindow autotrim_wires bespice_listen_port big_grid_points
 bus_replacement_char cadence_compat cadgrid cadlayers cadsnap cairo_font_name cairo_font_scale
 change_lw color_ps compare_sch constr_mv copy_cell crosshair_layer crosshair_size cursor_2_hook
 custom_label_prefix custom_token dark_colors dark_colorscheme dark_gui_colorscheme
 dim_bg dim_value disable_unique_names do_all_inst draw_crosshair draw_grid draw_grid_axes
 draw_window edit_prop_pos edit_prop_size edit_symbol_prop_new_sel editprop_sympath
 en_hilight_conn_inst enable_dim_bg enable_stretch env(PDK) env(PDK_ROOT)
 enter_text_default_geometry erc_open_net_is_error erc_shorted_output_is_error ev_precision
 file_chooser(regex) file_chooser(action) file_chooser(dirs)
 filetmp
 fix_broken_tiled_fill flat_netlist fullscreen gaw_fd gaw_tcp_address graph_autoload graph_bus
 graph_change_done graph_dialog_default_geometry graph_digital graph_legend graph_linewidth_mult
 graph_logx graph_logy graph_private_cursor graph_rainbow graph_schname graph_sel_color
 graph_sel_wave graph_selected graph_sort graph_unlocked graph_use_ctrl_key 
 graph_vlegend hide_empty_graphs
 hide_symbols incr_hilight incremental_select infix_interface infowindow_text intuitive_interface
 keep_symbols launcher_default_program light_colors line_width live_cursor2_backannotate
 local_netlist_dir lvs_ignore lvs_netlist measure_text netlist_dir netlist_show netlist_type
 new_file_browser_depth new_file_browser_ext
 no_ask_save no_ask_simulate no_change_attrs nolist_libs noprint_libs only_probes
 orthogonal_wiring path pathlist persistent_command preserve_unchanged_attrs prev_symbol ps_colors
 ps_paper_size rainbow_colors rotated_text search_case search_exact
 search_found search_schematic search_select search_value select_touch
 show_hidden_texts show_infowindow show_infowindow_after_netlist simconf_default_geometry
 simconf_vpos simulate_bg snap_cursor snap_cursor_size spiceprefix split_files svg_colors
 svg_font_name sym_txt symbol symbol_width tabstop tclcmd_txt tclstop
 tctx::colors tctx::delay_flag tctx::hsize tctx::recentfile
 tctx::selected_mode tctx::old_selected_mode tctx::old_selected_tok tctx::selected_tok
 tctx::rcode tctx::vsize tctx::tctx::retval tctx::retval_orig
 tclcmd_default_geometry text_line_default_geometry text_replace_selection text_tabs_setting
 textwindow_fileid textwindow_filename textwindow_w toolbar_horiz toolbar_list toolbar_visible
 top_is_subckt transparent_svg undo_type unselect_partial_sel_wires uppercase_subckt
 use_cursor_for_selection use_lab_wire use_label_prefix use_tclreadline user_wants_copy_cell
 verilog_2001 verilog_bitblast
 viewdata_fileid viewdata_filename viewdata_w xschem_libs xschem_listen_port zoom_full_center
}

## list of global arrays to save/restore on context switching
## will be saved as tctx::.drw_dircolor, tctx::.x1.drw_dircolor and so on
## EXCEPTIONS, not to be saved/restored:
## execute
set tctx::global_array_list {
  replace_key dircolor sim enable_layer ngspice::ngspice_data
}

proc delete_ctx {context} {
  global has_x
  if {![info exists has_x]} {return}
  set tctx::tctx $context
  uplevel #0 {
    # puts "delete_ctx $tctx::tctx"
    array unset $tctx::tctx
    foreach tctx::i $tctx::global_array_list {
      regsub {::} $tctx::i {__} arr_name
      if { [array exists ${tctx::tctx}_$arr_name] } {
        array unset ${tctx::tctx}_$tctx::i
      }
    }
  }
}

proc restore_ctx {context} {
  global has_x
  if {![info exists has_x]} {return}
  # puts "restoring tcl context $context : semaphore=[xschem get semaphore]"
  set tctx::tctx $context
  array unset ::sim
  uplevel #0 {
    if { [ array exists tctx::$tctx::tctx ] } {
      # puts "restore_ctx tctx::$tctx::tctx"
      ## Cleanup these vars to avoid side effects from previous ctx
      unset -nocomplain gaw_fd
      foreach tctx::i $tctx::global_list {
        if { [info exists [subst tctx::$tctx::tctx]($tctx::i)] } {
          # puts "restoring:  $tctx::i"
          set $tctx::i [set [subst tctx::$tctx::tctx]($tctx::i)]
        }
      }
    }
    foreach tctx::i $tctx::global_array_list {
      unset -nocomplain $tctx::i
      regsub {::} $tctx::i {__} arr_name
      if { [array exists tctx::${tctx::tctx}_$arr_name] } {
        array set $tctx::i [array get [subst tctx::${tctx::tctx}_$arr_name]]
      }
    }
  }
}


# context is saved in array variable in tctx:: namespace.
# to get list of array names:
#   array names  tctx::.drw
#   array names  tctx::.x1.drw
#   .....
proc save_ctx {context} {
  global has_x
  if {![info exists has_x]} {return}
  # puts "saving tcl context $context : semaphore=[xschem get semaphore]"
  set tctx::tctx $context
  uplevel #0 {
    # puts "save_ctx $tctx::tctx"
    foreach tctx::i $tctx::global_list {
      if { [info exists $tctx::i] } {
        # puts "saving:  $tctx::i"
        set [subst tctx::$tctx::tctx]($tctx::i) [set $tctx::i]
      }
    }
    foreach tctx::i $tctx::global_array_list {
      if { [array exists $tctx::i] } {
        regsub {::} $tctx::i {__} arr_name
        array set [subst tctx::${tctx::tctx}_$arr_name] [array get $tctx::i]
      }
    }
  }
}

proc housekeeping_ctx {} {
  global has_x simulate_bg show_hidden_texts case_insensitive draw_window hide_symbols
  global netlist_type intuitive_interface file_chooser
  if {![info exists has_x]} {return}
  uplevel #0 {
  }
  # puts "housekeeping_ctx, path: [xschem get current_win_path]"
  xschem set hide_symbols $hide_symbols
  xschem set draw_window $draw_window
  xschem set intuitive_interface  $intuitive_interface
  xschem case_insensitive $case_insensitive
  set_sim_netlist_waves_buttons
  .statusbar.7 configure -text $netlist_type
}

# callback that resets simulate button color at end of simulation
proc set_simulate_button {top_path win_path} {
  global simulate_bg execute has_x

  if {![info exists has_x]} return
  set current_win [xschem get current_win_path]
  set simvar tctx::${win_path}_simulate
  set sim_button $top_path.menubar

  # puts "current_win=|$current_win|"
  # puts "simvar=|$simvar|"
  # puts "win_path=|$win_path|"
  # puts "top_path=|$top_path|"
  # puts "sim_button=|$sim_button|"
  # puts "execute(exitcode,last)=|$execute(exitcode,last)|"

  if {![info exists execute(exitcode,last)]} {
    if { $current_win eq $win_path} {
      catch {$sim_button entryconfigure Simulate -background $simulate_bg}
    }
    set $simvar $simulate_bg
  } elseif { $execute(exitcode,last) == 0} {
    if { $current_win eq $win_path} {
      catch {$sim_button entryconfigure Simulate -background Green}
    }
    set $simvar Green
  } else {
    if { $current_win eq $win_path} {
      catch {$sim_button entryconfigure Simulate -background red}
    }
    set $simvar red
  }
}


# set simulate and netlist buttons on context change
proc set_sim_netlist_waves_buttons {} {
  global simulate_bg execute has_x

  if {![info exists has_x]} {return}
  set win_path [xschem get current_win_path]
  set top_path [xschem get top_path]

  set netlist_var tctx::${win_path}_netlist
  set sim_var  tctx::${win_path}_simulate
  set waves_var tctx::${win_path}_waves
  if {![info exists $netlist_var] || [set $netlist_var] eq $simulate_bg} {
    catch {$top_path.menubar entryconfigure Netlist -background  $simulate_bg}
  } else {
     catch {$top_path.menubar entryconfigure Netlist -background [set $netlist_var]}
  }

  if {![info exists $sim_var] || [set $sim_var] eq $simulate_bg} {
    catch {$top_path.menubar entryconfigure Simulate -background  $simulate_bg}
  } else {
    catch {$top_path.menubar entryconfigure Simulate -background [set $sim_var]}
  }

  if {![info exists $waves_var] || [set $waves_var] eq $simulate_bg} {
    catch {$top_path.menubar entryconfigure Waves -background  $simulate_bg}
  } else {
    catch {$top_path.menubar entryconfigure Waves -background [set $waves_var]}
  }
}

# these two routines are workarounds for broken remote desktop connection tools
# that do not correctly return mouse coordinates (%x, %y) on KeyPress events
proc getmousex {win} {
  set x [winfo pointerx $win]     ;# absolute mouse position in root window
  set originx [winfo rootx $win]  ;# absolute position of left window corner in root window
  set relx [expr {$x - $originx}] ;# mouse position relative to window origin
  return $relx
}
proc getmousey {win} {
  set y [winfo pointery $win]     ;# absolute mouse position in root window
  set originy [winfo rooty $win]  ;# absolute position of top window corner in root window
  set rely [expr {$y - $originy}] ;# mouse position relative to window origin
  return $rely
}

proc switch_window {parent topwin event window} {
  # puts "$parent $topwin $event $window"
  raise_dialog $parent $topwin

  if { $parent eq {.}} {
    if { $window eq $parent} {
      xschem callback .drw $event 0 0 0 0 0 0
    }
  } else {
    if {$window eq $parent} {
      # send a fake event just to force context switching in callback()
      xschem callback $parent.drw $event 0 0 0 0 0 0
    }
  }
}
proc show_bindkeys {} {
        global dark_colorscheme dim_value dim_bg
        if {[winfo exists .bk]} {wm deiconify .bk; raise .bk; return}
        toplevel .bk -bg {}
        # wm attributes .bk -fullscreen 1
        wm geometry .bk [winfo width .]x[winfo height .]+[winfo rootx .]+[expr {[winfo rooty .]+30}]

        # set save $dark_colorscheme
        # set dark_colorscheme 0
        # set dim_value 0.0
        # set dim_bg 0.0
        # xschem build_colors

        xschem preview_window create .bk
        xschem preview_window draw .bk [abs_sym_path bindkeys_cheatsheet.sym]

        # set dark_colorscheme $save
        # xschem build_colors

        bind .bk <ButtonPress> {xschem preview_window destroy .bk}
        bind .bk <KeyPress> {xschem preview_window destroy .bk}
        bind .bk <Expose> {xschem preview_window draw .bk }
        wm protocol .bk WM_DELETE_WINDOW {xschem preview_window destroy .bk}
}

proc set_bindings {topwin} {
global env has_x OS autofocus_mainwindow
  ###
  ### Tk event handling
  ###

  if {($OS== "Windows" || [string length [lindex [array get env DISPLAY] 1] ] > 0 ) && [info exists has_x]} {
    set parent [winfo toplevel $topwin]
    # puts "set_binding: topwin=$topwin, parent=$parent"

    bind $parent <Expose> "if {{%W} eq {$parent}} {raise_dialog $parent $topwin}"
    bind $parent <Visibility> [list raise_dialog $parent $topwin]

    # Context switch.
    bind $parent <FocusIn> "
      switch_window $parent $topwin %T %W
      # focus $topwin ;# propagate focus to canvas
    "

    bind $topwin <Leave> "
      if {{%W} eq {$topwin}} {
        xschem callback %W %T %x %y 0 0 0 %s
        graph_show_measure stop
      }
    "

    bind $topwin <Expose> "if {{%W} eq {$topwin}} {xschem callback %W %T %x %y 0 %w %h %s}"

    # transform mousewheel events into button4/5 events
    if {[info tclversion] > 8.7} {
      bind $topwin <MouseWheel> {
        if {%D > 0} {
          xschem callback %W 4 %x %y 0 4 0 %s
        } else {
          xschem callback %W 4 %x %y 0 5 0 %s
        }
      }
    }

    bind $topwin <Double-Button-1> "xschem callback %W -3 %x %y 0 %b 0 %s"
    bind $topwin <Double-Button-2> "xschem callback %W -3 %x %y 0 %b 0 %s"
    bind $topwin <Double-Button-3> "xschem callback %W -3 %x %y 0 %b 0 %s"
    bind $topwin <Configure> "xschem callback %W %T %x %y 0 %w %h 0"
    if {$autofocus_mainwindow} {
      bind $topwin <ButtonPress> "focus $topwin; xschem callback %W %T %x %y 0 %b 0 %s"
    } else {
      bind $topwin <ButtonPress> "focus $topwin; xschem callback %W %T %x %y 0 %b 0 %s"
    }
    bind $topwin <ButtonRelease> "xschem callback %W %T %x %y 0 %b 0 %s"

    #### test: show keybindings
    bind $topwin <KeyPress> "
      if {{%K} eq {Escape}} { destroy .ctxmenu }
      xschem callback %W %T %x %y %N 0 0 %s"
    bind $topwin <KeyRelease> "xschem callback %W %T %x %y %N 0 0 %s"
    if {$autofocus_mainwindow} {
      bind $topwin <Motion> "focus $topwin; xschem callback %W %T %x %y 0 0 0 %s"
    } else {
      bind $topwin <Motion> "xschem callback %W %T %x %y 0 0 0 %s"
    }
    bind $topwin <Enter> "
      if {{%W} eq {$topwin}} {
        if {\[winfo exists .ins\]} {
          set file_chooser(enter) 1 ;# so first time mouse enters file chooser current file will be shown
        }
        destroy .ctxmenu
        if {\$autofocus_mainwindow} {focus $topwin}
        xschem callback %W %T %x %y 0 0 0 0
      }
    "
    bind $topwin <Unmap> " wm withdraw .infotext; set show_infowindow 0 "

    # on Windows Alt key mask is reported as 131072 (1<<17) so build masks manually with values passed from C code
    if {$OS == "Windows" } {
      bind $topwin <Mod4-KeyPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$Mod4Mask}]}
      bind $topwin <Shift-Mod4-KeyPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$ShiftMask + $Mod4Mask}]}
      bind $topwin <Control-Mod4-KeyPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$ControlMask + $Mod4Mask}]}
      bind $topwin <Alt-Mod4-KeyPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$Mod1Mask + $Mod4Mask}]}
      bind $topwin <Mod4-KeyPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$Mod4Mask}]}
      bind $topwin <Alt-ButtonPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$Mod1Mask}]}
      bind $topwin <Control-Alt-ButtonPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$ControlMask + $Mod1Mask}]}
      bind $topwin <Shift-Alt-ButtonPress> {xschem callback %W %T %x %y 0 %b 0 [expr {$ShiftMask + $Mod1Mask}]}
      bind $topwin <Alt-Motion> {xschem callback %W %T %x %y 0 0 0 [expr {$Mod1Mask+%s}]}
      bind $topwin <Alt-KeyPress> {xschem callback %W %T %x %y %N 0 0 [expr {$Mod1Mask}]}
      bind $topwin <Control-Alt-KeyPress> {xschem callback %W %T %x %y %N 0 0 [expr {$ControlMask + $Mod1Mask}]}
      bind $topwin <Shift-Alt-KeyPress> {xschem callback %W %T %x %y %N 0 0 [expr {$ShiftMask + $Mod1Mask}]}
      bind $topwin <Shift-Insert> {xschem callback %W %T %x %y %N 0 0 [expr {$ShiftMask}]}
      bind $topwin <MouseWheel> { ;# transform MouseWheel into button 4/5 presses.
        if {%D<0} {
          xschem callback %W 4 %x %y 0 5 0 %s
        } else {
          xschem callback %W 4 %x %y 0 4 0 %s
        }
      }
    }

    if {$parent eq {.}} { set parent {}}
    bind ${parent}.statusbar.5 <Leave> \
      "focus ${parent}.drw; set cadgrid \[${parent}.statusbar.5 get\]; xschem set cadgrid \$cadgrid"
    bind ${parent}.statusbar.3 <Leave> \
      "focus ${parent}.drw; set cadsnap \[${parent}.statusbar.3 get\]; xschem set cadsnap \$cadsnap"

    focus ${parent}.drw
  }

}

## this function sets up all tk windows and binds X events. It is executed by xinit.c after completing
## all X initialization. This avoids race conditions.
## In previous flow xschem.tcl was setting up windows and events before X initialization was completed by xinit.c.
## this could lead to crashes on some (may be slow) systems due to Configure/Expose events being delivered
## before xschem being ready to handle them.
proc pack_widgets { { topwin {} } } {
  global env has_x OS tabbed_interface toolbar_visible toolbar_horiz
  # puts "pack_widgets: $topwin"
  if {($OS== "Windows" || [string length [lindex [array get env DISPLAY] 1] ] > 0 ) && [info exists has_x]} {
    pack $topwin.statusbar.2 -side left
    pack $topwin.statusbar.3 -side left
    pack $topwin.statusbar.4 -side left
    pack $topwin.statusbar.5 -side left
    pack $topwin.statusbar.6 -side left
    pack $topwin.statusbar.7 -side left
    pack $topwin.statusbar.10 -side left
    pack $topwin.statusbar.11 -side left
    pack $topwin.statusbar.9 -side left
    pack $topwin.statusbar.8 -side left
    pack $topwin.statusbar.1 -side left -fill x
    pack $topwin.statusbar -side bottom -fill x
    pack $topwin.statusbar.12 -side left
    pack $topwin.drw -side right -fill both -expand true
    setup_tabbed_interface
    if {$toolbar_visible}  {toolbar_show $topwin}

  }
  if {$topwin ne {}} {
    $topwin.menubar.view entryconfigure {Tabbed interface} -state disabled
  }
}

# if undo_type == disk save undo to disk
# if undo_type == memory keep undo in memory
# In memory undo is faster but in case of crashes nothing can be recovered
# On disk undo is slower but can be used to recover state just before a crash
proc switch_undo {} {
  global undo_type
  if { $undo_type eq {memory} } {
    set res [tk_messageBox -type yesno -parent [xschem get topwindow] \
            -message {Ok to keep undo in memory? Undo will be reset.\
            Memory undo is faster but in case of crashes nothing can be recovered.}]
    if {$res eq {yes} } {
      xschem undo_type $undo_type
    } else {
      set undo_type disk
    }
  } else { ;# disk
    set res [tk_messageBox -type yesno -parent [xschem get topwindow] \
            -message {Ok to save undo on disk? Undo will be reset.\
            Disk undo is slower but in case of crashes previous state can be recovered.}]
    if {$res eq {yes} } {
      xschem undo_type $undo_type
    } else {
      set undo_type memory
    }
  }
}


proc select_raw {{parent {.}}} {
  global has_x netlist_dir
  regsub {/$} $netlist_dir {} netlist_dir
  set filename $netlist_dir/[file tail [file rootname [xschem get schname]]].raw
  set types {
      {{Raw Files}       {.raw}        }
      {{All Files}        *            }
  }
  if {[info exists has_x]} {
    set filename [tk_getOpenFile -title "Select file" -multiple 0 -initialdir $netlist_dir \
            -parent $parent -initialfile [file tail $filename]  -filetypes $types]
  }
  return $filename
}

proc load_raw {{type {}}} {
  global netlist_dir has_x

  regsub {/$} $netlist_dir {} netlist_dir
  if { [xschem raw_query loaded] != -1} { ;# unload existing raw file(s)
    xschem raw_clear
   }
  set filename [select_raw [xschem get topwindow]]
  if {[file exists $filename]} {
    if {$type ne {}} {
      xschem raw_read $filename $type
    } else {
      xschem raw_read $filename
    }
  }
}

## menubuttons not used anymore, but keep this code if needed in the future...
## modified binding for button-1 clicks on menubuttons. Clicking on posted menu will unpost it
# proc tk::menubutton1 {w x y} {
#     # puts $w
#     # puts "--> [array get tk::Priv] "
#     if {[winfo ismapped [$w cget -menu ]]} {
#       MenuUnpost {}
#       return
#     }
#     if {$tk::Priv(inMenubutton) ne ""} {
#       tk::MbPost $tk::Priv(inMenubutton) $x $y
#     }
# }
#
# bind Menubutton <Button-1> {tk::menubutton1 %W %X %Y}

proc build_widgets { {topwin {} } } {
  global canvas_height canvas_width
  global XSCHEM_SHAREDIR tabbed_interface simulate_bg OS sim
  global dark_gui_colorscheme draw_crosshair grid_point_size
  global color_ps transparent_svg menu_debug_var enable_stretch
  global netlist_show flat_netlist split_files compare_sch intuitive_interface
  global draw_grid big_grid_points sym_txt change_lw incr_hilight symbol_width cadence_compat
  global cadsnap cadgrid draw_window toolbar_visible hide_symbols undo_type snap_cursor
  global disable_unique_names persistent_command autotrim_wires infix_interface orthogonal_wiring en_hilight_conn_inst
  global local_netlist_dir editor netlist_type netlist_dir spiceprefix
  if { $dark_gui_colorscheme} {
    set selectcolor white
  } else {
    set selectcolor black
  }

  menu $topwin.menubar -tearoff 0 -borderwidth 0 -takefocus 0

  $topwin.menubar add cascade -label "File" -menu $topwin.menubar.file
  menu $topwin.menubar.file -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Edit" -menu $topwin.menubar.edit
  menu $topwin.menubar.edit -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Options" -menu $topwin.menubar.option
  menu $topwin.menubar.option -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "View" -menu $topwin.menubar.view
  menu $topwin.menubar.view -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Properties" -menu $topwin.menubar.prop
  menu $topwin.menubar.prop -tearoff 0 -takefocus 0

  $topwin.menubar add cascade -label "Layers" -menu $topwin.menubar.layers
  menu $topwin.menubar.layers -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Tools" -menu $topwin.menubar.tools
  menu $topwin.menubar.tools -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Symbol" -menu $topwin.menubar.sym
  menu $topwin.menubar.sym -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Highlight" -menu $topwin.menubar.hilight
  menu $topwin.menubar.hilight -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Simulation" -menu $topwin.menubar.simulation
  menu $topwin.menubar.simulation -tearoff 0 -takefocus 0
  $topwin.menubar add cascade -label "Help" -menu $topwin.menubar.help
  menu $topwin.menubar.help -tearoff 0 -takefocus 0

  if {$topwin eq {}} {
    . configure -menu .menubar
  } else {
    $topwin configure -menu $topwin.menubar
  }

  ## menubuttons not used anymore, but keep code if needed in the future
  ## activate menus when hovering the mouse, even without button pressed
  # bind Menubutton <Motion> { tk::MbMotion %W down %X %Y}

  $topwin.menubar.help add command -label "Help" -command "textwindow \"${XSCHEM_SHAREDIR}/xschem.help\" ro" \
       -accelerator {?}
  $topwin.menubar.help add command -label "Keys" -command "textwindow \"${XSCHEM_SHAREDIR}/keys.help\" ro"
  $topwin.menubar.help add command -label "Show Keybindings" -command "show_bindkeys"
  $topwin.menubar.help add command -label "About XSCHEM" -command "about"

  $topwin.menubar.file add command -label "Clear Schematic"  -accelerator Ctrl+N\
    -command {
      xschem clear schematic
    }
  $topwin.menubar.file add command -label "Clear Symbol" -accelerator Ctrl+Shift+N \
    -command {
      xschem clear symbol
    }
  $topwin.menubar.file add command -label "Component browser" -accelerator {Shift-Ins, Ctrl-I} \
    -command {
      if {$new_file_browser} {
        file_chooser
      } else {
        load_file_dialog {Insert symbol} *.sym INITIALINSTDIR 2
      }
    }
  $topwin.menubar.file add command -label "Open" -command "xschem load" -accelerator {Ctrl+O}
  $topwin.menubar.file add command -label "Open in new window" -command "xschem load_new_window" \
    -accelerator {Alt+O}
  $topwin.menubar.file add command -label "Open last closed" \
    -command {xschem load -gui -lastclosed} -accelerator {Ctrl+Shift+T}
  $topwin.menubar.file add command -label "Open most recent" \
    -command {xschem load -gui -lastopened} -accelerator {Ctrl+Shift+O}
  $topwin.menubar.file add cascade -label "Open recent" -menu $topwin.menubar.file.recent
  menu $topwin.menubar.file.recent -tearoff 0 -takefocus 0
  setup_recent_menu $topwin
  $topwin.menubar.file add command -label {Create new window/tab} -command "xschem new_schematic create" \
     -accelerator {Ctrl+T}

  $topwin.menubar.file add command -label "Open selected schematic in new window" \
      -command "open_sub_schematic" -accelerator Alt+E
      # -command "xschem schematic_in_new_window" -accelerator Alt+E
  $topwin.menubar.file add command -label "Open selected symbol in new window" \
      -command "xschem symbol_in_new_window" -accelerator Alt+I

  $topwin.menubar.file add command -label "Delete files" -command "xschem delete_files" -accelerator {Shift-D}
  $topwin.menubar.file add command -label "Save" -command "xschem save" -accelerator {Ctrl+S}
  $topwin.menubar.file add command -label "Merge" -command "xschem merge" -accelerator {B}
  $topwin.menubar.file add command -label "Reload" -accelerator {Alt+S} \
    -command {
      if {[alert_ "Are you sure you want to reload?" {} 0 1] == 1} {
        xschem reload
      }
    }
  $topwin.menubar.file add command -label "Save as" -command "xschem saveas" -accelerator {Ctrl+Shift+S}
  $topwin.menubar.file add command -label "Save as symbol" \
     -command "xschem saveas {} symbol" -accelerator {Ctrl+Alt+S}
  # added svg, png 20171022
  $topwin.menubar.file add cascade -label "Image export" -menu $topwin.menubar.file.im_exp
  menu $topwin.menubar.file.im_exp -tearoff 0 -takefocus 0
  $topwin.menubar.file.im_exp add command -label "EPS Selection Export" -command "xschem print eps"
  $topwin.menubar.file.im_exp add command -label "PDF/PS Export" -command "xschem print pdf" -accelerator {*}
  $topwin.menubar.file.im_exp add command -label "PDF/PS Export Full" -command "xschem print pdf_full"
  $topwin.menubar.file.im_exp add command -label "Hierarchical PDF/PS Export" -command "xschem hier_psprint"
  $topwin.menubar.file.im_exp add command -label "PNG Export" -command "xschem print png" -accelerator {Ctrl+*}
  $topwin.menubar.file.im_exp add command -label "SVG Export" -command "xschem print svg" -accelerator {Alt+*}


  $topwin.menubar.file add separator
  $topwin.menubar.file add command -label "Start new Xschem process" -accelerator {X} -command {
    xschem new_process
  }
  $topwin.menubar.file add command -label "Close schematic" -accelerator {Ctrl+W} -command {
    xschem exit
  }
  $topwin.menubar.file add command -label "Quit Xschem" -accelerator {Ctrl+Q} -command {
    quit_xschem
  }
  $topwin.menubar.option add checkbutton -label "Color Postscript/SVG" -variable color_ps \
     -selectcolor $selectcolor -command {
        if { $color_ps==1 } {xschem set color_ps 1} else { xschem set color_ps 0}
     }
  $topwin.menubar.option add checkbutton -selectcolor $selectcolor  \
     -label "Transparent SVG background" -variable transparent_svg
  $topwin.menubar.option add checkbutton -label "Debug mode" -variable menu_debug_var \
    -selectcolor $selectcolor  -command {
        if { $menu_debug_var==1 } {xschem debug 1} else { xschem debug 0}
     }
  $topwin.menubar.option add checkbutton -selectcolor $selectcolor \
     -label "Undo buffer on Disk" -variable undo_type \
     -onvalue disk -offvalue memory -command {switch_undo}
  $topwin.menubar.option add checkbutton -label "Enable stretch" -variable enable_stretch \
     -selectcolor $selectcolor  -accelerator Y
  $topwin.menubar.option add checkbutton -label "Enable infix-interface" -variable infix_interface \
     -selectcolor $selectcolor
  $topwin.menubar.option add checkbutton -label "Enable orthogonal wiring" -variable orthogonal_wiring \
     -selectcolor $selectcolor  -accelerator Shift-L
  $topwin.menubar.option add checkbutton -label "Unsel. partial sel. wires after stretch move" \
     -selectcolor $selectcolor -variable unselect_partial_sel_wires

  $topwin.menubar.option add checkbutton -label "Auto Join/Trim Wires" -variable autotrim_wires \
     -selectcolor $selectcolor -command {
         if {$autotrim_wires == 1} {
           xschem trim_wires
           xschem redraw
         }
     }
  $topwin.menubar.option add checkbutton -selectcolor $selectcolor \
    -label "Persistent wire/line place command" -variable persistent_command
  $topwin.menubar.option add checkbutton -label "Intuitive Click & Drag interface" \
    -variable intuitive_interface -selectcolor $selectcolor \
    -command {xschem set intuitive_interface $intuitive_interface}

  $topwin.menubar.option add cascade -label "Crosshair" \
       -menu $topwin.menubar.option.crosshair
  menu $topwin.menubar.option.crosshair -tearoff 0 -takefocus 0

  $topwin.menubar.option.crosshair add checkbutton -label "Draw snap cursor" \
    -variable snap_cursor -selectcolor $selectcolor  -accelerator {Alt-Z}
  $topwin.menubar.option.crosshair add checkbutton -label "Draw crosshair" \
    -variable draw_crosshair -selectcolor $selectcolor -accelerator {Alt-X}
  $topwin.menubar.option.crosshair add command -label "Crosshair size" \
    -command {
      input_line "Enter crosshair size (int, 0 = full screen width):" \
          "set crosshair_size" $crosshair_size
    }
  $topwin.menubar.option add command -label "Replace \[ and \] for buses in SPICE netlist" \
     -command {
       input_line "Enter two characters to replace default bus \[\] delimiters:" "set bus_replacement_char"
     }
  $topwin.menubar.option add checkbutton \
    -selectcolor $selectcolor -label "Group bus slices in Verilog instances" -variable verilog_bitblast
  $topwin.menubar.option add checkbutton -label "Draw grid" -variable draw_grid \
     -selectcolor $selectcolor -accelerator {%} \
     -command {
       xschem redraw
     }
  $topwin.menubar.option add command -label "Half Snap Threshold" -accelerator G -command {
         xschem set cadsnap [expr {$cadsnap / 2.0} ]
       }
  $topwin.menubar.option add command -label "Double Snap Threshold" -accelerator Shift-G -command {
         xschem set cadsnap [expr {$cadsnap * 2.0} ]
       }
  $topwin.menubar.option add checkbutton -label "Variable grid point size" -variable big_grid_points \
     -selectcolor $selectcolor -command { xschem redraw }
  $topwin.menubar.option add separator

  $topwin.menubar.option add checkbutton -label "No XCopyArea drawing model" -variable draw_window \
         -selectcolor $selectcolor -accelerator {Ctrl+$} \
         -command {
           if { $draw_window == 1} { xschem set draw_window 1} else { xschem set draw_window 0}
         }

  $topwin.menubar.option add checkbutton -label "Fix for GPUs with broken tiled fill" \
         -selectcolor $selectcolor -variable fix_broken_tiled_fill \
         -command {
           if { $fix_broken_tiled_fill == 1} {
             xschem set fix_broken_tiled_fill 1
           } else {
             xschem set fix_broken_tiled_fill 0
           }
           xschem resetwin 1 1 1 0 0
           xschem redraw
         }
  $topwin.menubar.option add checkbutton -label "Fix broken RDP mouse coordinates" \
     -selectcolor $selectcolor -variable fix_mouse_coord -command {xschem set fix_mouse_coord $fix_mouse_coord}

  $topwin.menubar.option add separator

  $topwin.menubar.option add cascade -label "Netlist format / Symbol mode" \
       -menu $topwin.menubar.option.netlist
  menu $topwin.menubar.option.netlist -tearoff 0 -takefocus 0

  $topwin.menubar.option.netlist add checkbutton -label "Flat netlist" -variable flat_netlist \
     -selectcolor $selectcolor -accelerator : \
     -command {
        if { $flat_netlist==1 } {xschem set flat_netlist 1} else { xschem set flat_netlist 0}
     }
  $topwin.menubar.option.netlist add checkbutton -label "Split netlist" -variable split_files \
     -selectcolor $selectcolor -accelerator {}
  $topwin.menubar.option.netlist add radiobutton -label "Spectre netlist"\
       -background grey60 -variable netlist_type -value spectre -accelerator {Ctrl+Shift+V} \
       -selectcolor $selectcolor -command "xschem set netlist_type spectre; xschem redraw"
  $topwin.menubar.option.netlist add radiobutton -label "Spice netlist"\
       -background grey60 -variable netlist_type -value spice -accelerator {Ctrl+Shift+V} \
       -selectcolor $selectcolor -command "xschem set netlist_type spice; xschem redraw"
  $topwin.menubar.option.netlist add radiobutton -label "VHDL netlist"\
       -background grey60 -variable netlist_type -value vhdl -accelerator {Ctrl+Shift+V} \
       -selectcolor $selectcolor -command "xschem set netlist_type vhdl; xschem redraw"
  $topwin.menubar.option.netlist add radiobutton -label "Verilog netlist"\
       -background grey60 -variable netlist_type -value verilog -accelerator {Ctrl+Shift+V} \
       -selectcolor $selectcolor -command "xschem set netlist_type verilog; xschem redraw"
  $topwin.menubar.option.netlist add radiobutton -label "tEDAx netlist" \
       -background grey60 -variable netlist_type -value tedax -accelerator {Ctrl+Shift+V} \
       -selectcolor $selectcolor -command "xschem set netlist_type tedax; xschem redraw"
  $topwin.menubar.option.netlist add radiobutton -label "Symbol global attrs" \
       -background grey60 -variable netlist_type -value symbol -accelerator {Ctrl+Shift+V} \
       -selectcolor $selectcolor -command "xschem set netlist_type symbol; xschem redraw"
  $topwin.menubar.edit add command -label "Undo" -command "xschem undo; xschem redraw" -accelerator U
  $topwin.menubar.edit add command -label "Redo" -command "xschem redo; xschem redraw" -accelerator {Shift+U}
  $topwin.menubar.edit add command -label "Copy" -command "xschem copy" -accelerator Ctrl+C
  $topwin.menubar.edit add command -label "Cut" -command "xschem cut"   -accelerator Ctrl+X
  $topwin.menubar.edit add command -label "Paste" -command "xschem paste" -accelerator Ctrl+V
  $topwin.menubar.edit add command -label "Delete" -command "xschem delete" -accelerator Del
  $topwin.menubar.edit add command -label "Select all" -command "xschem select_all" -accelerator Ctrl+A
  $topwin.menubar.edit add command -label "Duplicate objects" -command "xschem copy_objects" -accelerator C
  $topwin.menubar.edit add command -label "Move objects" -command "xschem move_objects" -accelerator M
  $topwin.menubar.edit add command -label "Move objects stretching attached wires" \
      -command "xschem move_objects stretch" -accelerator Control+M
  $topwin.menubar.edit add command -label "Move objects adding wires to connected pins" \
      -command "xschem move_objects kissing" -accelerator Shift+M
  $topwin.menubar.edit add command -label "Horizontal Flip in place selected objects" -state normal \
     -command {xschem flip_in_place} -accelerator {Alt-F}
  $topwin.menubar.edit add command -label "Vertical Flip in place selected objects" -state normal \
     -command {xschem flipv_in_place} -accelerator {Alt-V}
  $topwin.menubar.edit add command -label "Rotate in place selected objects" -state normal \
      -command {xschem rotate_in_place} -accelerator {Alt-R}
  $topwin.menubar.edit add command -label "Vertical Flip selected objects" -state normal \
     -command {xschem flipv} -accelerator {Shift-V}
  $topwin.menubar.edit add command -label "Horizontal Flip selected objects" -state normal \
     -command {xschem flip} -accelerator {Shift-F}
  $topwin.menubar.edit add command -label "Rotate selected objects" -state normal \
      -command {xschem rotate} -accelerator {Shift-R}
  $topwin.menubar.edit add radiobutton -label "Unconstrained move" -variable constr_mv \
     -selectcolor $selectcolor -background grey60 -value 0 -command {xschem set constr_mv 0}
  $topwin.menubar.edit add radiobutton -label "Constrained Horizontal move" -variable constr_mv \
     -selectcolor $selectcolor -background grey60 -value 1 -accelerator H -command {xschem set constr_mv 1}
  $topwin.menubar.edit add radiobutton -label "Constrained Vertical move" -variable constr_mv \
     -selectcolor $selectcolor -background grey60 -value 2 -accelerator V -command {xschem set constr_mv 2}
  $topwin.menubar.edit add command -label "Push schematic" -command "xschem descend" -accelerator E
  $topwin.menubar.edit add command -label "Push symbol" -command "xschem descend_symbol" -accelerator I
  $topwin.menubar.edit add command -label "Pop" -command "xschem go_back" -accelerator Ctrl+E

  $topwin.menubar add command -label { - } -state disabled
  $topwin.menubar add command -label Netlist -background {#888888} \
    -activebackground orange -command {xschem netlist -erc}

  $topwin.menubar add command -label Simulate -background green -background {#888888} \
    -activebackground orange -command {
     simulate_from_button
  }

  $topwin.menubar add cascade -label "Waves" -background {#888888} \
    -activebackground orange -menu $topwin.menubar.waves
  menu  $topwin.menubar.waves -tearoff 0 -takefocus 0
  $topwin.menubar.waves add command -label {External viewer} -command {waves external}
  $topwin.menubar.waves add separator
  $topwin.menubar.waves add command -label Clear -command {xschem raw_clear}
  $topwin.menubar.waves add separator
  $topwin.menubar.waves add command -label {Load first analysis found} -command {waves {}}
  $topwin.menubar.waves add command -label {Op Annotate} -command {
       set tctx::retval [select_raw [xschem get topwindow]]
       set show_hidden_texts 1
       if {$tctx::retval ne {}} {
         xschem annotate_op $tctx::retval
       } else {
         xschem annotate_op
       }
  }
  $topwin.menubar.waves add command -label Op -command {waves op}
  $topwin.menubar.waves add command -label Dc -command {waves dc}
  $topwin.menubar.waves add command -label Ac -command {waves ac}
  $topwin.menubar.waves add command -label Tran -command {waves tran}
  $topwin.menubar.waves add command -label Noise -command {waves noise}
  $topwin.menubar.waves add command -label Sp -command {waves ac}
  $topwin.menubar.waves add command -label Spectrum -command {waves ac}

  set simulate_bg [$topwin.menubar entrycget Simulate -background]

  # create  $topwin.menubar.layers.menu
  create_layers_menu $topwin
  $topwin.menubar.view add command -label "Redraw" -command "xschem redraw" -accelerator Esc
  $topwin.menubar.view add command -label "Fullscreen" \
     -accelerator "\\" -command "
        if {\$fullscreen == 1} {set fullscreen 2} ;# avoid hiding menu in true fullscreen
        xschem fullscreen $topwin.drw
     "
  $topwin.menubar.view add command -label "Zoom Full" -command "xschem zoom_full" -accelerator F
  $topwin.menubar.view add command -label "Zoom In" -command "xschem zoom_in" -accelerator Shift+Z
  $topwin.menubar.view add command -label "Zoom Out" -command "xschem zoom_out" -accelerator Ctrl+Z
  $topwin.menubar.view add command -label "Zoom box" -command "xschem zoom_box" -accelerator Z
  $topwin.menubar.view add command -label "Set snap value" \
         -command {
         input_line "Enter snap value (float):" "xschem set cadsnap" $cadsnap
       }
  $topwin.menubar.view add command -label "Set grid spacing" \
       -command {
         input_line "Enter grid spacing (float):" "xschem set cadgrid" $cadgrid
       }
  $topwin.menubar.view add command -label "Toggle colorscheme"  -accelerator {Shift+O} -command {
          xschem toggle_colorscheme
       }
  $topwin.menubar.view add command -label "Dim colors"  -accelerator {} -command {
          color_dim
       }
  $topwin.menubar.view add command -label "Change current layer color"  -accelerator {} -command {
          change_color
       }
  $topwin.menubar.view add command -label "Reset all colors to default" \
         -accelerator {} -command {
          reset_colors 1
         }

  $topwin.menubar.view add checkbutton -label "Toggle variable line width" -variable change_lw \
     -selectcolor $selectcolor -accelerator {_} -command {xschem set change_lw $change_lw}
  $topwin.menubar.view add command -label "Set line width" -accelerator {Alt+-} \
       -command {
         input_line "Enter linewidth (float):" "xschem line_width"
       }
  $topwin.menubar.view add command -label "Set grid point size" \
       -command {
         input_line "Enter Grid point size (int or -1: $grid_point_size)" "set grid_point_size" $grid_point_size
       }
  $topwin.menubar.view add checkbutton -label "Tabbed interface" -variable tabbed_interface \
    -selectcolor $selectcolor -command setup_tabbed_interface

  $topwin.menubar.view add cascade -label "Show / Hide" \
       -menu $topwin.menubar.view.show
  menu $topwin.menubar.view.show -tearoff 0 -takefocus 0

  $topwin.menubar.view.show add checkbutton -label "Show ERC Info window" \
    -selectcolor $selectcolor -variable show_infowindow -command {
       if { $show_infowindow != 0 } {wm deiconify .infotext
       } else {wm withdraw .infotext}
     }
  $topwin.menubar.view.show add command -label "Visible layers"  -accelerator {} \
    -command {
          select_layers
          xschem redraw
       }
  $topwin.menubar.view.show add checkbutton -label "Symbol text" -variable sym_txt \
     -selectcolor $selectcolor  \
     -accelerator {Ctrl+B} -command { xschem set sym_txt $sym_txt; xschem redraw }
  $topwin.menubar.view.show add checkbutton -label "Show Toolbar" -variable toolbar_visible \
     -selectcolor $selectcolor -command "
        if { \$toolbar_visible } \" toolbar_show $topwin\" else \"toolbar_hide $topwin\"
     "
  $topwin.menubar.view.show add checkbutton -label "Horizontal Toolbar" -variable toolbar_horiz \
     -selectcolor $selectcolor -command "
        if { \$toolbar_visible } \" toolbar_hide $topwin; toolbar_show $topwin \"
     "
  $topwin.menubar.view.show add checkbutton -label "Show hidden texts"  -variable show_hidden_texts \
         -selectcolor $selectcolor -command {xschem update_all_sym_bboxes; xschem redraw}
  $topwin.menubar.view.show add checkbutton -label "Draw grid axes"  -variable draw_grid_axes \
         -selectcolor $selectcolor -command {xschem redraw}
  $topwin.menubar.prop add command -label "Edit" -command "xschem edit_prop" -accelerator Q
  $topwin.menubar.prop add command -label "Edit with editor" -command "xschem edit_vi_prop" -accelerator Shift+Q
  $topwin.menubar.prop add command -label "View" -command "xschem view_prop" -accelerator Ctrl+Shift+Q
  $topwin.menubar.prop add command -label "Toggle *_ignore attribute on selected instances" \
     -command "xschem toggle_ignore" -accelerator Shift+T
  $topwin.menubar.prop add command -label "Change selected object insertion order" \
     -command { xschem change_elem_order -1 } -accelerator Shift+S
  $topwin.menubar.prop add command -label "Edit Header/License text" \
     -command { update_schematic_header } -accelerator Shift+B
  $topwin.menubar.prop add command -background red -label "Edit file (danger!)" \
     -command "xschem edit_file" -accelerator Alt+Q

  $topwin.menubar.sym add cascade -label "Show symbols" -menu $topwin.menubar.sym.sym
  menu $topwin.menubar.sym.sym -tearoff 0 -takefocus 0

  $topwin.menubar.sym.sym add radiobutton -label "Show symbol details" \
     -selectcolor $selectcolor -background grey60 -variable hide_symbols -value 0 \
     -command {xschem set hide_symbols $hide_symbols; xschem redraw} -accelerator Alt+B
  $topwin.menubar.sym.sym add radiobutton -label "Show instance Bounding boxes for subcircuit symbols" \
     -selectcolor $selectcolor -background grey60 -variable hide_symbols -value 1 \
     -command {xschem set hide_symbols $hide_symbols; xschem redraw} -accelerator Alt+B
  $topwin.menubar.sym.sym add radiobutton -label "Show instance Bounding boxes for all symbols" \
     -selectcolor $selectcolor -background grey60 -variable hide_symbols -value 2 \
     -command {xschem set hide_symbols $hide_symbols; xschem redraw} -accelerator Alt+B

  $topwin.menubar.sym add command -label "Set symbol width" \
     -command {
        input_line "Enter Symbol width ($symbol_width)" "set symbol_width" $symbol_width
      }
  $topwin.menubar.sym add command -label "Make symbol from schematic" \
     -command "xschem make_symbol" -accelerator A
  $topwin.menubar.sym add command -label "Make schematic from symbol" \
     -command "xschem make_sch" -accelerator Ctrl+L
  $topwin.menubar.sym add command -label "Make schematic and symbol from selected components" \
     -command "xschem make_sch_from_sel" -accelerator Ctrl+Shift+H
  $topwin.menubar.sym add command -label "Attach net labels to component instance" \
     -command "xschem attach_labels" -accelerator Shift+H
  $topwin.menubar.sym add command -label "Create symbol pins from selected schematic pins" \
          -command "schpins_to_sympins" -accelerator Alt+H
  $topwin.menubar.sym add command -label "Place symbol pin" \
          -command "xschem add_symbol_pin" -accelerator Alt+P
  $topwin.menubar.sym add command -label "Place schematic input port" \
          -command "xschem net_label 2" -accelerator Ctrl+P
  $topwin.menubar.sym add command -label "Place schematic output port" \
          -command "xschem net_label 3" -accelerator Ctrl+Shift+P
  $topwin.menubar.sym add command -label "Place net pin label" \
          -command "xschem net_label 1" -accelerator Alt+L
  $topwin.menubar.sym add command -label "Place net wire label" \
          -command "xschem net_label 0" -accelerator Alt+Shift+L
  $topwin.menubar.sym add command -label "Change selected inst. texts to floaters" \
          -command "xschem floaters_from_selected_inst"
  $topwin.menubar.sym add command -label "Unselect attached floaters" \
          -command "xschem unselect_attached_floaters"


  $topwin.menubar.sym add cascade -label "List of nets" -menu $topwin.menubar.sym.list
  menu $topwin.menubar.sym.list -tearoff 0 -takefocus 0

  $topwin.menubar.sym.list add command -label "Print list of highlight nets" \
          -command "xschem print_hilight_net 1" -accelerator J
  $topwin.menubar.sym.list add command -label "Print list of highlight nets, with buses expanded" \
          -command "xschem print_hilight_net 3" -accelerator Alt-Ctrl-J
  $topwin.menubar.sym.list add command -label "Create labels from highlight nets" \
          -command "xschem print_hilight_net 4" -accelerator Alt-J
  $topwin.menubar.sym.list add command -label "Create labels from highlight nets with 'i' prefix" \
          -command "xschem print_hilight_net 2" -accelerator Alt-Shift-J
  $topwin.menubar.sym.list add command -label "Create pins from highlight nets" \
          -command "xschem print_hilight_net 0" -accelerator Ctrl-J

  $topwin.menubar.sym add checkbutton \
     -selectcolor $selectcolor  \
     -label "Search all search-paths for schematic associated to symbol" -variable search_schematic
  $topwin.menubar.sym add checkbutton -label "Allow duplicated instance names (refdes)" \
      -selectcolor $selectcolor -variable disable_unique_names
  $topwin.menubar.tools add command -label "Insert symbol" -command "xschem place_symbol" -accelerator {Ins, Shift-I}
  $topwin.menubar.tools add command -label "Insert text" -command "xschem place_text" -accelerator T
  $topwin.menubar.tools add command -label "Insert wire" -command "xschem wire" -accelerator W
  $topwin.menubar.tools add command -label "Insert snap wire" -command "xschem snap_wire" -accelerator Shift+W
  $topwin.menubar.tools add command -label "Insert line" -command "xschem line" -accelerator L
  $topwin.menubar.tools add command -label "Insert rect" -command "xschem rect" -accelerator R
  $topwin.menubar.tools add command -label "Insert polygon" -command "xschem polygon" -accelerator P
  $topwin.menubar.tools add command -label "Insert arc" -command "xschem arc" -accelerator Shift+C
  $topwin.menubar.tools add command -label "Insert circle" -command "xschem circle" -accelerator Ctrl+Shift+C
  $topwin.menubar.tools add command -label "Insert JPG/PNG/SVG image" -command "xschem add_image"
  $topwin.menubar.tools add command -label "Grab screen area" -command "xschem grabscreen" \
    -accelerator {Print Scrn}
  $topwin.menubar.tools add command -label "Search" -accelerator Ctrl+F -command  property_search
  $topwin.menubar.tools add command -label "Align to Grid" -accelerator Alt+U -command  "xschem align"
  $topwin.menubar.tools add command -label "Execute TCL command" -command  "tclcmd" -accelerator {=}
  $topwin.menubar.tools add command -label "Join/Trim wires" \
     -command "xschem trim_wires" -accelerator {&}
  $topwin.menubar.tools add command -label "Break wires at selected instance pins" \
     -command "xschem break_wires" -accelerator {!}
  $topwin.menubar.tools add command -label "Remove wires running through selected inst. pins" \
     -command "xschem break_wires 1" -accelerator {Ctrl-!}
  $topwin.menubar.tools add command -label "Break wires at mouse position" \
     -command "xschem wire_cut noalign" -accelerator {Alt-Shift-Right Butt.}
  $topwin.menubar.tools add command -label "Break wires at mouse position, align cut point" \
     -command "xschem wire_cut" -accelerator {Alt-Right Butt.}
  $topwin.menubar.tools add command -label "Select all connected wires/labels/pins" \
     -accelerator {Shift-Right Butt.} \
     -command { xschem connected_nets}
  $topwin.menubar.tools add command -label "Select conn. wires, stop at junctions" \
     -accelerator {Ctrl-Righ Butt.} -command { xschem connected_nets 1 }

  $topwin.menubar.hilight add command \
   -label {Set schematic to compare and compare with} \
   -command "xschem compare_schematics; set compare_sch 1"
  $topwin.menubar.hilight add command \
   -label {Swap compare schematics} \
   -command "swap_compare_schematics"
  $topwin.menubar.hilight add checkbutton \
   -selectcolor $selectcolor -label {Compare schematics} \
   -command {
      xschem unselect_all
      xschem redraw } \
   -variable compare_sch
  $topwin.menubar.hilight add checkbutton -label "View only Probes" -variable only_probes \
         -selectcolor $selectcolor -accelerator {5} \
         -command { xschem only_probes }
  $topwin.menubar.hilight add command \
   -label {Highlight net-pin mismatches on sel. instances} \
   -command "xschem net_pin_mismatch" \
   -accelerator {Shift-X}
  $topwin.menubar.hilight add command -label {Highlight duplicate instance names} \
     -command "xschem check_unique_names 0"  -accelerator {#}
  $topwin.menubar.hilight add command -label {Rename duplicate instance names} \
     -command "xschem check_unique_names 1" -accelerator {Ctrl+#}
  $topwin.menubar.hilight add command -label {Select overlapped instances} \
     -command "xschem warning_overlapped_symbols 1; xschem redraw" -accelerator {}
  $topwin.menubar.hilight add command -label {Propagate Highlight selected net/pins} \
     -command "xschem hilight drill" -accelerator {Ctrl+Shift+K}
  $topwin.menubar.hilight add checkbutton \
     -selectcolor $selectcolor -label "Increment Hilight Color" -variable incr_hilight
  $topwin.menubar.hilight add command -label {Highlight selected net/pins} \
     -command "xschem hilight" -accelerator K
  $topwin.menubar.hilight add command -label {Send selected net/pins to Viewer} \
     -command "xschem send_to_viewer" -accelerator Alt+G
  $topwin.menubar.hilight add command -label {Select hilight nets / pins} \
     -command "xschem select_hilight_net" \
     -accelerator Alt+K
  $topwin.menubar.hilight add command -label {Un-highlight all net/pins} \
     -command "xschem unhilight_all" -accelerator Shift+K
  $topwin.menubar.hilight add command -label {Un-highlight selected net/pins} \
     -command "xschem unhilight" -accelerator Ctrl+K
  # 20160413
  $topwin.menubar.hilight add command -label {Show labels on unconnected instance pins} \
      -command "xschem show_unconnected_pins"
  $topwin.menubar.hilight add checkbutton \
     -selectcolor $selectcolor -label {Auto-highlight net/pins} -variable auto_hilight
  $topwin.menubar.hilight add checkbutton -label {Enable highlight connected instances} \
    -selectcolor $selectcolor -variable en_hilight_conn_inst

  $topwin.menubar.simulation add command -label "Set netlist Dir" \
    -command {
          set local_netlist_dir 0
          set_netlist_dir 1
    }
  $topwin.menubar.simulation add command -label "Set top level netlist name" \
    -command {
          input_line {Set netlist file name} {xschem set netlist_name} [xschem get netlist_name] 40
    }
  $topwin.menubar.simulation add command -label "Set netlist / graph / annotation precision" \
       -command { 
         input_line "Enter precision (int):" "set ev_precision" $ev_precision
       }
  $topwin.menubar.simulation add checkbutton -label "Show netlist after netlist command" \
     -selectcolor $selectcolor -variable netlist_show -accelerator {Shift+A}
  $topwin.menubar.simulation add checkbutton -label "Keep symbols when traversing hierarchy" \
     -selectcolor $selectcolor -variable keep_symbols
  $topwin.menubar.simulation add radiobutton -indicatoron 1 -label "Use netlist directory" \
    -background grey60 -variable local_netlist_dir -value 0 \
    -selectcolor $selectcolor -command {set_netlist_dir 1 }
  $topwin.menubar.simulation add radiobutton -indicatoron 1 -label "Use 'simulation' dir in schematic dir" \
    -background grey60 -variable local_netlist_dir -value 1 \
    -selectcolor $selectcolor -command {set_netlist_dir 1 }
  $topwin.menubar.simulation add radiobutton -indicatoron 1 -label "Use 'simulation/\[schname\]' dir in schematic dir" \
    -background grey60 -variable local_netlist_dir -value 2 \
    -selectcolor $selectcolor -command {set_netlist_dir 1 }
  $topwin.menubar.simulation add command -label {Configure simulators and tools} -command {simconf}
  if {$OS == {Windows}} {
    $topwin.menubar.simulation add command -label {List running sub-processes} -state disabled
  } else {
    $topwin.menubar.simulation add command -label {List running sub-processes} -command {
      list_running_cmds
    }
  }
  $topwin.menubar.simulation add command -label {View last job data} -command {
    if { [info exists execute(data,last)] } {
      viewdata $execute(data,last)
    }
  }
  $topwin.menubar.simulation add command -label {View last job errors} -command {
    if { [info exists execute(error,last)] } {
      viewdata $execute(error,last)
    }
  }
  $topwin.menubar.simulation add command -label {Utile Stimuli Editor (GUI)} -command {
     inutile [xschem get current_dirname]/stimuli.[file rootname [file tail [xschem get schname]]]
  }
  $topwin.menubar.simulation add command -label {Utile Stimuli Translate} -command {
     inutile_translate  [xschem get current_dirname]/stimuli.[file rootname [file tail [xschem get schname]]]
  }
  $topwin.menubar.simulation add command -label {Shell [simulation path]} -command {
     if { [set_netlist_dir 0] ne "" } {
        get_shell $netlist_dir
     }
   }
  $topwin.menubar.simulation add command -label {Edit Netlist} \
     -command {edit_netlist [xschem get netlist_name fallback]}
  $topwin.menubar.simulation add command -label {Send highlighted nets to viewer} \
    -command {xschem create_plot_cmd} -accelerator Ctrl+Shift+X
  $topwin.menubar.simulation add command -label {Changelog from current hierarchy} -command {
    viewdata [list_hierarchy]
  }
  $topwin.menubar.simulation add separator


  $topwin.menubar.simulation add cascade -label "Graphs" -menu $topwin.menubar.simulation.graph
  menu $topwin.menubar.simulation.graph -tearoff 0 -takefocus 0
  $topwin.menubar.simulation.graph add checkbutton -label {Auto highlight plotted nets} \
   -selectcolor $selectcolor  -variable auto_hilight_graph_nodes
  $topwin.menubar.simulation.graph add command -label {Add waveform graph} -command {xschem add_graph}
  $topwin.menubar.simulation.graph add command -label {Add waveform reload launcher} -command {
      xschem place_symbol [find_file_first launcher.sym] "name=h5\ndescr=\"load waves\"
tclcommand=\"xschem raw_read \$netlist_dir/[file tail [file rootname [xschem get current_name]]].raw tran\"
"
  }
  $topwin.menubar.simulation.graph add command -label "Annotate Operating Point into schematic" \
    -command {
       set tctx::retval [select_raw [xschem get topwindow]]
       set show_hidden_texts 1
       if {$tctx::retval ne {}} {
         xschem annotate_op $tctx::retval
       } else {
         xschem annotate_op
       }
    }
  $topwin.menubar.simulation.graph add checkbutton -label "Live annotate probes with 'b' cursor" \
     -selectcolor $selectcolor -variable live_cursor2_backannotate
  $topwin.menubar.simulation.graph add checkbutton -label "Hide graphs if no spice data loaded" \
     -selectcolor $selectcolor -variable hide_empty_graphs -command {xschem redraw}


  $topwin.menubar.simulation add cascade -label "LVS" -menu $topwin.menubar.simulation.lvs
  menu $topwin.menubar.simulation.lvs -tearoff 0 -takefocus 0
  $topwin.menubar.simulation.lvs add checkbutton -label "LVS netlist + Top level is a .subckt" \
  -selectcolor $selectcolor -variable lvs_netlist
  $topwin.menubar.simulation.lvs add checkbutton -label "Upper case .SUBCKT and .ENDS" \
  -selectcolor $selectcolor -variable uppercase_subckt
  $topwin.menubar.simulation.lvs add checkbutton -label "Top level is a .subckt" \
  -selectcolor $selectcolor -variable top_is_subckt

  $topwin.menubar.simulation.lvs add checkbutton -label "Set 'lvs_ignore' variable" \
         -selectcolor $selectcolor \
         -variable lvs_ignore -command {xschem rebuild_connectivity; xschem unhilight_all}
  $topwin.menubar.simulation.lvs add checkbutton -label "Use 'spiceprefix' attribute" -variable spiceprefix \
         -selectcolor $selectcolor -command {xschem redraw}


  # used to check status of Simulate button later. This variable is constant, never changed
  frame $topwin.drw -background {} -takefocus 1 -width $canvas_width -height $canvas_height

  if { $topwin == {} } {set rootwin .} else { set rootwin $topwin}
  wm  title $rootwin "xschem - "
  wm iconname $rootwin "xschem - "
  $rootwin configure  -background {}
  if {$tabbed_interface} {
    wm protocol $rootwin WM_DELETE_WINDOW {
      xschem exit closewindow
    }
  } elseif { $rootwin == {.}} {
    wm protocol $rootwin WM_DELETE_WINDOW {
       set old  [xschem get current_win_path]
       save_ctx $old
       restore_ctx .drw
       housekeeping_ctx
       xschem new_schematic switch .drw
       xschem exit closewindow
       # did not exit ... switch back
       restore_ctx $old
       housekeeping_ctx
       xschem new_schematic switch $old
    }
  } else {
    wm protocol $rootwin WM_DELETE_WINDOW "xschem new_schematic destroy $topwin.drw {}"
  }

  frame $topwin.statusbar   -takefocus 0
  label $topwin.statusbar.1 -text "STATUS BAR 1"
  label $topwin.statusbar.2 -text "SNAP:"
  entry $topwin.statusbar.3 -width 7 -foreground black -takefocus 0
  balloon  $topwin.statusbar.2 "Mouse coordinates are snapped\nat multiples of this number"
  entry_replace_selection $topwin.statusbar.3
  label $topwin.statusbar.4 -text "GRID:"
  entry $topwin.statusbar.5 -width 7 -foreground black -takefocus 0
  balloon  $topwin.statusbar.4 "Grid points are shown \nat multiples of this number"
  entry_replace_selection $topwin.statusbar.5
  label $topwin.statusbar.6 -text "MODE:"
  label $topwin.statusbar.7 -width 7  ;# netlisting mode
  balloon $topwin.statusbar.6 {This is the current netlisting mode}
  balloon $topwin.statusbar.7 {This is the current netlisting mode}
  label $topwin.statusbar.11 -text {Stretch:}
  label $topwin.statusbar.12 -fg red -text {} ;# xschem is ready or busy doing something

  label $topwin.statusbar.10 -activebackground green -text {} ;# DRAW WIRE or DRAW LINE or ...
  label $topwin.statusbar.9 -textvariable enable_stretch
  label $topwin.statusbar.8 -activebackground red -text {} ;# Caps lock, Num lock status
  add_toolbuttons $topwin
}

proc set_initial_dirs {} {
  global INITIALLOADDIR INITIALINSTDIR INITIALPROPDIR pathlist
  foreach i $pathlist  {
    if { [file exists $i] } {
      if {![info exists INITIALLOADDIR]} {set INITIALLOADDIR $i}
      if {![info exists INITIALINSTDIR]} {set INITIALINSTDIR $i}
      if {![info exists INITIALPROPDIR]} {set INITIALPROPDIR $i}
      break
    }
  }
  if {![info exists INITIALLOADDIR]} {set INITIALLOADDIR {}}
  if {![info exists INITIALINSTDIR]} {set INITIALINSTDIR {}}
  if {![info exists INITIALPROPDIR]} {set INITIALPROPDIR {}}
}

# called whenever XSCHEM_LUBRARY_PATH changes (see trace command at end)
proc trace_set_vars {varname idxname op} {
  # puts trace_set_vars
  if {$varname eq {XSCHEM_LIBRARY_PATH} } {
    # puts stderr "executing set_paths after XSCHEM_LIBRARY_PATH change"
    # puts stderr "   $::XSCHEM_LIBRARY_PATH"
    uplevel #0 set_paths
  } elseif {$varname eq {new_file_browser_ext}} {
    uplevel #0 {
      if {![info exists file_chooser(old_new_file_browser_ext)] ||
           $file_chooser(old_new_file_browser_ext) ne $new_file_browser_ext} {
        set file_chooser(old_new_file_browser_ext) $new_file_browser_ext
        if {[winfo exists .ins]} {
          .ins.top4.upd invoke
        }
      }
    }
  } elseif {$varname eq {new_file_browser_depth}} {
    uplevel #0 {
      if {![info exists file_chooser(old_new_file_browser_depth)] ||
           $file_chooser(old_new_file_browser_depth) ne $new_file_browser_depth} {
        set file_chooser(old_new_file_browser_depth) $new_file_browser_depth
        if {[winfo exists .ins]} {
          .ins.top4.upd invoke
        }
      }
    }
  } elseif {$varname eq {file_chooser} && idxname eq {dirs}} {
    uplevel #0 {
      if {![info exists file_chooser(old_dirs)] ||
           $file_chooser(old_dirs) ne $file_chooser(dirs)} {
        set file_chooser(old_dirs) $file_chooser(dirs)
        if {[winfo exists .ins]} {
          .ins.top4.upd invoke
        }
      }
    }
  }
}

proc cleanup_paths {paths} {
  global env
  set path_l {}
  foreach i $paths {
    regsub {^~$} $i ${env(HOME)} i
    regsub {^~/} $i ${env(HOME)}/ i
    regsub -all {/+} $i {/} i
    if {![string compare $i .]} {
      # ...
    } elseif { [regexp {^#} $i] } {
      continue
    } elseif { [regexp {\.\.\/} $i] } {
      set i [file normalize $i]
    }
    if { [file exists $i] } {
      lappend path_l $i
    }
  }
  return $path_l
}

proc set_paths {} {
  global XSCHEM_LIBRARY_PATH pathlist OS add_all_windows_drives
  # puts stderr "caching search paths"
  if { [info exists XSCHEM_LIBRARY_PATH] } {
    if {$OS == "Windows"} {
      set path_l_orig [split $XSCHEM_LIBRARY_PATH \;]
      if {$add_all_windows_drives} {
        set win_vol [file volumes]
        foreach disk $win_vol {
          lappend path_l_orig $disk
        }
      }
    } else {
      set path_l_orig [split $XSCHEM_LIBRARY_PATH :]
    }
    set pathlist [cleanup_paths $path_l_orig]
  }
  if {$pathlist eq {}} { set pathlist [pwd] }

  set_initial_dirs

  c_toolbar::clear
  load_recent_file
}

proc print_help_and_exit {} {
  global XSCHEM_SHAREDIR
  if { [xschem get help ]} {
    set fd [open "${XSCHEM_SHAREDIR}/xschem.help" r]
    set helpfile [read $fd]
    puts $helpfile
    close $fd
  }
}

proc set_missing_colors_to_black {} {
  global enable_layer cadlayers svg_colors ps_colors light_colors dark_colors
  ## pad missing colors with black
  for {set i 0} { $i<$cadlayers } { incr i} {
    set_ne enable_layer($i) 1
    foreach j { svg_colors ps_colors light_colors dark_colors } {
      if { ![string compare [lindex [set $j] $i] {} ] } {
        if { ![string compare $j {ps_colors} ] || ![string compare $j {svg_colors} ]} {
          lappend $j {0x000000}
        } else {
          lappend $j {#000000}
        }
      }
    }
  }
  foreach i {svg_colors ps_colors light_colors dark_colors} {
    if { [llength [set $i]] > $cadlayers} {
       set $i [lrange [set $i] 0 [expr {$cadlayers -1}]]
    }
  }
}

proc create_layers_menu { {topwin {} } } {
  global dark_colorscheme
  if { $dark_colorscheme == 1 } { set txt_color black} else { set txt_color white}
  set j 0
  foreach i $tctx::colors {
    ## 20121121
    if {  $j == [xschem get pinlayer] } {
      set laylab [format %2d $j]-PIN
      set layfg $txt_color
    } elseif { $j == [xschem get wirelayer] } {
      set laylab [format %2d $j]-WIRE
      set layfg $txt_color
    } elseif { $j == [xschem get textlayer] } { ;# 20161206
      set laylab [format %2d $j]-TEXT
      set layfg $txt_color
    } elseif { $j == [xschem get backlayer] } { ;# 20161206
      set laylab [format %2d $j]-BG
      if { $dark_colorscheme == 1 } {
        set layfg white
      } else {
        set layfg black
      }
    } elseif { $j == [xschem get gridlayer] } { ;# 20161206
      set laylab [format %2d $j]-GRID
      set layfg $txt_color
    } else {
      set laylab "[format %2d $j]        "
      set layfg $txt_color
    }
    $topwin.menubar.layers add command -label $laylab  -activeforeground $layfg \
       -foreground $layfg -background $i -activebackground $i \
       -command "xschem set rectcolor $j; reconfigure_layers_button $topwin"
    if { [expr {$j%10}] == 0 } { $topwin.menubar.layers entryconfigure $j -columnbreak 1 }
    incr j
  }
}

proc set_replace_key_binding { {win_path {.drw}}} {
  global replace_key
  if {[array exists replace_key]} {
    foreach i [array names replace_key] {
      key_binding "$i" "$replace_key($i)" $win_path
    }
  }
}

proc source_user_tcl_files {} {
  set subst_tcl_files [uplevel #0 {subst $tcl_files}]
  foreach i $subst_tcl_files {
    if {[catch "uplevel #0 {[list source $i]}" retval]} {
      puts $retval
    }
  }
}

proc eval_user_startup_commands {} {
  global user_startup_commands
  if {[info exists user_startup_commands]} {
    if {[catch {uplevel #0 $user_startup_commands} res]} {
      puts "executing $user_startup_commands:\n\n$res"
    }
  }
}

proc eval_postinit_commands {} {
  global postinit_commands
  if {[info exists postinit_commands]} {
    if {[catch {uplevel #0 $postinit_commands} res]} {
      puts "executing $postinit_commands:\n\n$res"
    }
  }
}

proc eval_netlist_postprocess {} {
  global netlist_postprocess
  if {[info exists netlist_postprocess]} {
    if {[catch {uplevel #0 $netlist_postprocess} res]} {
      puts "executing $netlist_postprocess:\n\n$res"
    }
  }
}

proc eval_load_file_postprocess {} {
  global load_file_postprocess
  if {[info exists load_file_postprocess]} {
    if {[catch {uplevel #0 $load_file_postprocess} res]} {
      puts "executing $load_file_postprocess:\n\n$res"
    }
  }
}

proc setup_tcp_xschem { {port_number {}} } {
  global xschem_listen_port xschem_server_getdata

  # if a server socket was already set close it.
  if {[info exists xschem_server_getdata(server)]} { close $xschem_server_getdata(server) }
  if {$port_number ne {}} { set xschem_listen_port $port_number}
  if { [info exists xschem_listen_port] && ($xschem_listen_port ne {}) } {
    if {[catch {socket -server xschem_server $xschem_listen_port} err]} {
      puts "setup_tcp_xschem: problems finding a free TCP port"
      puts $err
      return 0
    } else {
      set chan $err
      set xschem_server_getdata(server) $chan
      # this piece of code deals with automatic port number selection (port_number argument set to 0)
      # tcl will automatically choose a free tcp port.
      set assigned_port [lindex [fconfigure $chan -sockname] end]
      set xschem_listen_port $assigned_port
      return $assigned_port
    }
  }
  return 0
}

proc setup_tcp_bespice {} {
  global bespice_listen_port bespice_server_getdata
  if { [info exists bespice_listen_port] && ($bespice_listen_port ne {}) } {
    # We will attempt to open port $bespice_listen_port ... $bespice_listen_port + 1000 this should succeed ...
    # We need to make this attempt as several instances of xschem / bespice might be running.
    # Each of these instances needs it's own server listening on a dedicated port.
    # The variable $bespice_listen_port is passed to bespice wave when the application is started
    # in the function "set_sim_defaults()".

    # if $bespice_listen_port is set to 0 TCL will automatically find a free port
    if {[catch {socket -server bespice_server $bespice_listen_port} err]} {
      # failed
      puts "setup_tcp_bespice: problems finding a free TCP port"
      puts $err
      return 0
    } else {
      # succeded => set $bespice_listen_port and socket connection for communication
      set chan $err
      set bespice_server_getdata(server) $chan
      set assigned_port [lindex [fconfigure $chan -sockname] end]
      set bespice_listen_port $assigned_port
      puts stderr "setup_tcp_bespice: success : listening to TCP port: $bespice_listen_port"
      return 1
    }
    puts "setup_tcp_bespice: problems listening to TCP port: $bespice_listen_port ... $last_port"
    puts $err
    return 0
  }
  # bespice_listen_port not defined, nothing to do
  # puts "setup_tcp_bespice: the functionallity was not set up as the variable \$bespice_listen_port"
  # puts "hasn't been defined in your xschemrc file."
  return 1
}

## this proc must be called for any created entry widgets
proc entry_replace_selection {w} {
  global text_replace_selection OS
  if {$text_replace_selection && $OS != "Windows"} {
    bind $w <<Paste>> {if {[%W selection present]} {%W delete sel.first sel.last}}
  }
}

###
###   MAIN PROGRAM
###

# This variable is set in xinit.c
# set running_in_src_dir 0
# if {[file exists ./systemlib] && [file exists ./xschem.tcl] && [file exists ./xschem]} {
#   set running_in_src_dir 1
# }

# tk_focusFollowsMouse

set_ne dark_colorscheme 1
set_ne dark_gui_colorscheme 0
if { [info exists has_x]} {

  # set or get scaling factor
  if { [info exists tk_scaling] } {
    # set scaling
    tk scaling $tk_scaling ;# useful for 4k displays (set bigger widgets)
  } else {
    # get scaling
    set tk_scaling [tk scaling]
  }

  if { $dark_gui_colorscheme == 0 } { ;# normal GUI
    option add *foreground black startupFile
    option add *activeForeground black startupFile
    option add *background {grey80} startupFile
    option add *activeBackground {#f8f8f8} startupFile
    option add *disabledForeground {grey50} startupFile
    option add *disabledBackground {grey70} startupFile
    option add *readonlyBackground {grey70} startupFile
    option add *highlightBackground {white} startupFile
    option add *highlightThickness 0 startupFile
    option add *highlightColor {grey30} startupFile
    option add *insertBackground {black} startupFile
    option add *selectColor {white} startupFile ;# checkbuttons, radiobuttons
    option add *selectForeground black
    option add *selectBackground grey70
    if { [info tclversion] > 8.4} {
      ttk::style configure TCombobox -fieldbackground grey90
    }
  } else { ;# dark GUI colorscheme
    option add *foreground white startupFile
    option add *activeForeground white startupFile
    option add *background {grey20} startupFile
    option add *activeBackground {grey10} startupFile
    option add *disabledForeground {grey50} startupFile
    option add *disabledBackground {grey20} startupFile
    option add *readonlyBackground {grey20} startupFile
    option add *highlightBackground {black} startupFile
    option add *highlightThickness 0 startupFile
    option add *highlightColor {grey70} startupFile
    option add *insertBackground {white} startupFile
    option add *selectColor {grey10} startupFile ;# checkbuttons, radiobuttons
    option add *selectForeground black
    option add *selectBackground grey70
    if { [info tclversion] > 8.4} {
      ttk::style configure TCombobox -fieldbackground grey20
    }
  }
}

set OS [lindex $tcl_platform(os) 0]
set env(LC_ALL) C
# tcl variable XSCHEM_LIBRARY_PATH  should already be set in xschemrc
set_ne add_all_windows_drives 1
set_paths
print_help_and_exit
set_ne text_replace_selection 1
if {$text_replace_selection && $OS != "Windows"} {
  # deletes selected text when pasting in text widgets, courtesy Wolf-Dieter Busch
  proc tk_textPaste w {
    global tcl_platform
    if {![catch {::tk::GetSelection $w CLIPBOARD} sel]} then {
      set oldSeparator [$w cget -autoseparators]
      if {$oldSeparator} then {
        $w configure -autoseparators 0
        $w edit separator
      }
      foreach {to from} [lreverse [$w tag ranges sel]] {
        $w delete $from $to
      }
      $w insert insert $sel
      if {$oldSeparator} then {
        $w edit separator
        $w configure -autoseparators 1
      }
    }
  }
}

## allow to unpost menu entries when clicking a posted menu

if { [info exists has_x] && [info tclversion] >= 8.6 } {
  bind Menu <Button> {
     if { [%W cget -type] eq "menubar" && [info exists tk::Priv(menuActivated)]} {
       %W activate none
     } else {
       %W activate @%x,%y
     }
     tk::MenuButtonDown %W
  }
}

#####################################################################################
## Procedures to set the PRIMARY selection of selected listbox elements with listbox
## created with -exportselection 0. Courtesy Mark G. Saye
#####################################################################################
## use with:   bind .l <<ListboxSelect>> [list listbox:select %W]
proc listbox:select {W} {
  selection clear -displayof $W
  selection own -command {} $W
  selection handle -type UTF8_STRING $W [list listbox:handle $W]
  selection handle $W [list listbox:handle $W]
}

proc listbox:handle {W offset maxChars} {
  set list {}
  foreach index [$W curselection] { lappend list [$W get $index] }
  set text [join $list \n]
  return [string range $text $offset [expr {$offset+$maxChars-1}]]
}
#####################################################################################

# focus the schematic window if mouse goes over it, even if a dialog box is displayed,
# without needing to click. This allows to move/zoom/pan the schematic while editing attributes.
set_ne autofocus_mainwindow 0
if {$OS == "Windows"} {
  set_ne XSCHEM_TMP_DIR [xschem get temp_dir]
} else {
  set_ne XSCHEM_TMP_DIR {/tmp}
}

# used in C code

if {[info exists tk_scaling]} {
  set_ne canvas_width [expr {int(800. * $tk_scaling)}]
  set_ne canvas_height [expr {int(500. * $tk_scaling)}]
} else {
  set_ne canvas_width 1000
  set_ne canvas_height 700
}
set_ne xschem_libs {}
set_ne noprint_libs {}
set_ne nolist_libs {}
set_ne debug_var 0
set_ne debug_tcleval 0 ;# debug tclpropeval2 (tcleval() in xschem attributes)
# used to activate debug from menu
set_ne menu_debug_var 0
set textwindow_wcounter 1
set viewdata_wcounter 1
set tctx::retval ""
set prev_symbol ""
set symbol ""

# 20100204 flag to enable fix for dialog box positioning,  issues with some wm
set wm_fix 0

# 20171010
set tclcmd_txt {}

###
### user preferences: set default values
###

if { ![info exists dircolor] } {
  set_ne dircolor(/share/xschem/) red
  set_ne dircolor(/share/doc/xschem/) {#338844}
}

set_ne file_dialog_globfilter {*}
set_ne component_browser_on_top 1
## list of tcl procedures to load at end of xschem.tcl
set_ne tcl_files {}
set_ne download_url_helper {curl -f -s -O -J}
set_ne netlist_dir "$USER_CONF_DIR/simulations"
# this global exists only for netlist_type radiobuttons, don't use, use [xschem] subcommand to get/set values
# it is also used in xschemrc to set initial netlist type.
set_ne netlist_type spice
set_ne local_netlist_dir 0 ;# if set use <sch_dir>/simulation for netlist and sims
set_ne bus_replacement_char {} ;# use {<>} to replace [] with <> in bussed signals
set_ne lvs_netlist 0
set_ne top_is_subckt 0
set_ne uppercase_subckt 0
set_ne lvs_ignore 0
set_ne hide_empty_graphs 0 ;# if set to 1 waveform boxes will be hidden if no raw file loaded
set_ne graph_use_ctrl_key 0;# if set forces to use Control key to operate on graphs
set_ne spiceprefix 1
set_ne verilog_2001 1
set_ne verilog_bitblast 0
set_ne ev_precision 4
set_ne search_schematic 0
# if set to 1 search symbols (if not in any of the XSCHEM_LIBRARY_PATH directories) in [pwd]
# instead of in the directory of currently loaded schematic.
set_ne split_files 0
set_ne flat_netlist 0
set_ne netlist_show 0
set_ne color_ps 1
set_ne ps_page_title 1 ;# add a title in the top left page corner
set_ne draw_crosshair 0
set_ne crosshair_layer 8 ;# Yellow
set_ne crosshair_size 0
set_ne snap_cursor_size 6
set_ne ps_paper_size {a4 842 595}
set_ne transparent_svg 0
set_ne only_probes 0  ; # 20110112
set_ne fullscreen 0
set_ne unzoom_nodrift 0
set_ne zoom_full_center 0
set_ne change_lw 1
set_ne line_width 0
set_ne live_cursor2_backannotate 1
set_ne cursor_2_hook {}
set_ne draw_window 0
set_ne show_hidden_texts 0
set_ne incr_hilight 1
set_ne enable_stretch 0
set_ne constr_mv 0
set_ne unselect_partial_sel_wires 0
set_ne load_file_dialog_fullpath 1
set_ne file_dialog_others {} ;# contains 2nd, 3rd, ... selected filenames on mult. selections in load file

# if set show selected elements while dragging the selection rectangle.
# once selected these can not be unselected by retracting the selection rectangle
# if not set show selected items at end of drag.
set_ne incremental_select 1
set_ne select_touch 1

set_ne draw_grid 1
set_ne big_grid_points 0
set_ne grid_point_size -1 ;# grid point size (>=0) or unspecified (-1)
set_ne draw_grid_axes 1
set_ne persistent_command 0
set_ne intuitive_interface 1
set_ne use_cursor_for_selection 0
set_ne autotrim_wires 0
set_ne cadence_compat 0
set_ne infix_interface 1
set_ne snap_cursor 0
set_ne orthogonal_wiring 0
set_ne compare_sch 0
set_ne disable_unique_names 0
set_ne sym_txt 1
set_ne show_infowindow 0
set_ne show_infowindow_after_netlist onerror
set_ne no_ask_save 0 ;# if set to 1 ctrl-s (save) will not ask to save
set_ne no_ask_simulate 0 ;# if set to 1 no confirmation for simulation ('s')
set_ne erc_open_net_is_error 0;# if set to 1 turn warnings into errors
set_ne erc_shorted_output_is_error 0;# if set to 1 turn warnings into errors
set_ne symbol_width 150
set_ne editor {gvim -f}
set_ne rainbow_colors 0
set_ne edit_symbol_prop_new_sel {}
if {$OS == "Windows"} {
  set_ne launcher_default_program [auto_execok start]
} else {
  set_ne launcher_default_program {xdg-open}
}
set_ne auto_hilight 0
set_ne auto_hilight_graph_nodes 0
set_ne use_tclreadline 1
set_ne en_hilight_conn_inst 0
## xpm to png conversion
set_ne to_png {gm convert}
## ps to pdf conversion
set_ne to_pdf {ps2pdf}
## tab stop position
set_ne tabstop 8
if { [info tclversion] > 8.4 } {
  set text_tabs_setting {-tabs "[expr {$tabstop * [font measure TkFixedFont 0]}] left" -tabstyle wordprocessor}
} else {
  set text_tabs_setting {-tabs "[expr {$tabstop * [font measure TkFixedFont 0]}] left"}
}

# selected graph user is editing attributes with graph GUI
set_ne graph_bus 0
set_ne graph_private_cursor 0
set_ne graph_logx 0
set_ne graph_logy 0
set_ne graph_rainbow 0
set_ne graph_selected {}
set_ne graph_schname {}
set_ne graph_change_done 0 ;# used to push undo only once when editing graphs
set_ne graph_linewidth_mult 1.4 ;# default multiplier (w.r.t. xschem lines) for line width in graphs
# user clicked this wave
set_ne graph_sel_wave {}
# flag to force simulation stop (Esc key pressed)
set_ne tclstop 0
## undo_type: disk or memory
set_ne undo_type disk

## show tab bar (tabbed interface)
set_ne tabbed_interface 1

## File->Open will open in a new window/tab instead of replacing content in current one
## if enabled.
set_ne open_in_new_window 0

## case insensitive symbol lookup (on case insensitive filesystems only!)
set_ne case_insensitive 0

## New alternate load file browser (default: not enabled).
set_ne new_file_browser 0
set_ne new_file_browser_depth 0 ;# depth to descend into each dir of the search paths
set_ne new_file_browser_ext {\.(sch|sym|tcl)$} ;# file extensions (a regex) to look for

set_ne file_dialog_ext {*}

#### toolbar icons are bitmaps. Their size is 24x24. This can be changed to
#### accommodate UHD displays.
#### the zoom factor is an integer that can be used to enlarge these icons
#### Default value: 1
set_ne toolbar_icon_zoom 1
#### context menu icons are 16 x 16. they can be enlarged by the following integer
#### default value: 1
set_ne ctxmenu_icon_zoom 1

## remember edit_prop widget size
set_ne edit_prop_size 80x12
set_ne text_line_default_geometry {}
set_ne enter_text_default_geometry {}
set_ne graph_dialog_default_geometry {}
set_ne terminal xterm

# xschem tcp port number (listen to port and execute commands from there if set)
# set a port number in xschemrc if you want accept remote connections.
set_ne xschem_listen_port {}

# server for bespice waveform connection (listen to port and send commands to bespice if set)
# set a port number in xschemrc if you want xschem to be able to cross-probe to bespice
set_ne bespice_listen_port {}

set_ne keep_symbols 0 ;# if set loaded symbols will not be purged when descending/netlisting.

# hide instance details (show only bbox)
set_ne hide_symbols 0
# gaw tcp {host port}
set_ne gaw_tcp_address {localhost 2020}

## cairo stuff 20171112
set_ne cairo_font_scale 1.0
set_ne nocairo_font_xscale .85
set_ne nocairo_font_yscale .88
set_ne cairo_font_line_spacing 1.0
set_ne cairo_vert_correct 0
set_ne nocairo_vert_correct 0

# Arial, Monospace Sans-Serif; default font to use if unspecified in text elements
set_ne cairo_font_name {Sans-Serif}
set_ne svg_font_name {Sans-Serif}

# has_cairo set by c program if cairo enabled
set has_cairo 0
set rotated_text {} ;#20171208
set_ne enable_dim_bg 0
set_ne dim_bg 0.0
set_ne dim_value 0.0
set_ne fix_broken_tiled_fill 1 ;# set to 1 on some broken X11 drivers / GPUs that show garbage on screen */
# this fix uses an alternative method for getting mouse coordinates on KeyPress/KeyRelease
# events. Some remote connection softwares do not generate the correct coordinates
# on such events */
set_ne fix_mouse_coord 0

##### set colors
if {!$rainbow_colors} {
  set_ne cadlayers 22
  ## 20171113
  set_ne light_colors {
   "#ffffff" "#0099cc" "#aaaaaa" "#222222" "#229900"
   "#bb2200" "#0066cc" "#ff0000" "#888800" "#00aaaa"
   "#880088" "#00ff00" "#0000cc" "#666600" "#557755"
   "#aa2222" "#7ccc40" "#00ffcc" "#ce0097" "#d2d46b"
   "#ef6158" "#fdb200"}
  set_ne dark_colors {
   "#000000" "#00ddff" "#4f4f4f" "#cccccc" "#88dd00"
   "#bb2200" "#00aaff" "#ff0000" "#ffff00" "#ffffff"
   "#ff00ff" "#00ff00" "#0044ff" "#aaaa00" "#aaccaa"
   "#ff7777" "#bfff81" "#00ffcc" "#ce0097" "#d2d46b"
   "#ef6158" "#fdb200"}
} else {
  # rainbow colors for bitmapping
  # skip if colors defined in ~/.xschem 20121110
  set_ne cadlayers 35
  #20171113
  set_ne light_colors {
    "#000000" "#00ccee" "#aaaaaa" "#ffffff" "#88dd00"
    "#bb2200" "#0000e0" "#2000e0" "#4000e0" "#6000e0"
    "#8000e0" "#a000e0" "#c000e0" "#e000e0" "#e000c0"
    "#e000a0" "#e00080" "#e00060" "#e00040" "#e00020"
    "#e00000" "#e02000" "#e04000" "#e06000" "#e08000"
    "#e0a000" "#e0c000" "#e0e000" "#e0e020" "#e0e040"
    "#e0e060" "#e0e080" "#e0e0a0" "#e0e0c0" "#e0e0e0"
  }
  set_ne dark_colors {
    "#000000" "#00ccee" "#3f3f3f" "#ffffff" "#88dd00"
    "#bb2200" "#0000e0" "#2000e0" "#4000e0" "#6000e0"
    "#8000e0" "#a000e0" "#c000e0" "#e000e0" "#e000c0"
    "#e000a0" "#e00080" "#e00060" "#e00040" "#e00020"
    "#e00000" "#e02000" "#e04000" "#e06000" "#e08000"
    "#e0a000" "#e0c000" "#e0e000" "#e0e020" "#e0e040"
    "#e0e060" "#e0e080" "#e0e0a0" "#e0e0c0" "#e0e0e0"
  }
}


# every 0x#### hex data represents one 16 bit row of the 16x16 bit fill bitmap
# of the specified layer number.
set_ne pixdata(0) {
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
}
set_ne pixdata(1) {
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
}
set_ne pixdata(2) {
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
}
set_ne pixdata(3) {
  0x5555 0x0000 0xaaaa 0x0000 0x5555 0x0000 0xaaaa 0x0000
  0x5555 0x0000 0xaaaa 0x0000 0x5555 0x0000 0xaaaa 0x0000
}
set_ne pixdata(4) {
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
}
set_ne pixdata(5) {
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
}
set_ne pixdata(6) {
  0x0101 0x0000 0x0000 0x0000 0x1010 0x0000 0x0000 0x0000
  0x0101 0x0000 0x0000 0x0000 0x1010 0x0000 0x0000 0x0000
}
set_ne pixdata(7) {
  0x0000 0x8000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0200
  0x0000 0x0080 0x0000 0x0000 0x0000 0x0000 0x0000 0x0002
}
set_ne pixdata(8) {
  0x1111 0x0000 0x0000 0x0000 0x1111 0x0000 0x0000 0x0000
  0x1111 0x0000 0x0000 0x0000 0x1111 0x0000 0x0000 0x0000
}
set_ne pixdata(9) {
  0x0441 0x0000 0x1004 0x0000 0x4110 0x0000 0x0441 0x0000
  0x1004 0x0000 0x4110 0x0000 0x0441 0x0000 0x1004 0x0000
}
set_ne pixdata(10) {
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
  0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
}
set_ne pixdata(11) {
  0x1111 0x0000 0x4444 0x0000 0x1111 0x0000 0x4444 0x0000
  0x1111 0x0000 0x4444 0x0000 0x1111 0x0000 0x4444 0x0000
}
set_ne pixdata(12) {
  0x0404 0x0202 0x0101 0x8080 0x4040 0x2020 0x1010 0x0808
  0x0404 0x0202 0x0101 0x8080 0x4040 0x2020 0x1010 0x0808
}
set_ne pixdata(13) {
  0x1111 0x2222 0x4444 0x8888 0x1111 0x2222 0x4444 0x8888
  0x1111 0x2222 0x4444 0x8888 0x1111 0x2222 0x4444 0x8888
}
set_ne pixdata(14) {
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
}
set_ne pixdata(15) {
  0x4444 0x0000 0x0000 0x0000 0x4444 0x0000 0x0000 0x0000
  0x4444 0x0000 0x0000 0x0000 0x4444 0x0000 0x0000 0x0000
}
set_ne pixdata(16) {
  0x1111 0x0000 0x0000 0x0000 0x2222 0x0000 0x0000 0x0000
  0x4444 0x0000 0x0000 0x0000 0x8888 0x0000 0x0000 0x0000
}
set_ne pixdata(17) {
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
}
set_ne pixdata(18) {
  0x5555 0xaaaa 0x5555 0xaaaa 0x5555 0xaaaa 0x5555 0xaaaa
  0x5555 0xaaaa 0x5555 0xaaaa 0x5555 0xaaaa 0x5555 0xaaaa
}
set_ne pixdata(19) {
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
}
set_ne pixdata(20) {
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
}
set_ne pixdata(21) {
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
  0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
}


# for svg and pdf draw 20121108
regsub -all {"} $dark_colors  {} svg_colors
regsub -all {#} $svg_colors  {0x} svg_colors
regsub -all {"} $light_colors  {} ps_colors
regsub -all {#} $ps_colors  {0x} ps_colors
set_missing_colors_to_black
# read-only vars to store defaults (so we can switch to default colors)
set dark_colors_save $dark_colors
set light_colors_save $light_colors

set_ne tctx::colors $dark_colors
##### end set colors


# 20111005 added missing initialization of globals...
set_ne no_change_attrs 0
set_ne preserve_unchanged_attrs 0
set search_select 0

# 20111106 these vars are overwritten by caller with mktemp file names
if {$OS == "Windows"} {
  set filetmp $env(windir)/.tmp2
} else {
  set filetmp [pwd]/.tmp2
}

# flag bound to a checkbutton in symbol editprop form
# if set cell is copied when renaming it
set_ne copy_cell 0

load_recent_file
# schematic to preload in new windows 20090708
set_ne XSCHEM_START_WINDOW {}

set custom_token {lab}
set search_value {}
set search_exact 0
set search_case 1

# 20171005
set custom_label_prefix {}

###
### build Tk widgets
###
if { ( $OS== "Windows" || [string length [lindex [array get env DISPLAY] 1] ] > 0 ) && [info exists has_x]} {
  setup_toolbar
  # for hyperlink in about dialog
  eval  font create Underline-Font [ font actual TkDefaultFont ]
  font configure Underline-Font -underline true -size 24
  . configure -cursor left_ptr
  set_old_tk_fonts ;# for xschem compiled with old tcl-tk libs
  set infowindow_text {}
  infowindow

  build_widgets {}

  ##
  ## packing top windows (pack instructions) and event binding (bind instructions) done in proc pack_widgets
  ## executed by xinit.c after finalizing X initialization. This avoid potential race conditions
  ## like Configure or Expose events being generated before xschem being ready to handle them.
  ##

  # allow user to modify key bindings
  set_replace_key_binding

  # update
  # xschem windowid . ;# set icon for window
} ;# end if {[exists has_x]}

# read custom colors
if { [file exists ${USER_CONF_DIR}/colors] } {
  source ${USER_CONF_DIR}/colors
}

if { [llength $dark_colors] < $cadlayers || [llength $light_colors] < $cadlayers } {
  puts stderr { Warning: wrong number of configured layers in light_colors or dark_colors variables.}
}
if { $dark_colorscheme == 1} {
  set tctx::colors $dark_colors
} else {
  set tctx::colors $light_colors
}
regsub -all {"} $light_colors  {} ps_colors
regsub -all {#} $ps_colors  {0x} ps_colors
regsub -all {"} $tctx::colors {} svg_colors
regsub -all {#} $svg_colors {0x} svg_colors

if { $show_infowindow } { wm deiconify .infotext }

# xschem listen and bespice listen
setup_tcp_xschem
setup_tcp_bespice

# automatically build pathlist whenever XSCHEM_LIBRARY_PATH changes
trace add variable XSCHEM_LIBRARY_PATH write trace_set_vars
# trigger file_chooser update
trace add variable new_file_browser_depth write trace_set_vars
trace add variable new_file_browser_ext write trace_set_vars

