#************************************************************
#
# bkmk.tcl -- Bookmarking tools. (NLV)
#
# Author: Dan Gunter
# Date  : 7/31/98
#
#************************************************************

global Bkmk()
set Bkmk(Num) 0
set Bkmk(Lold) 0
set Bkmk(color) \#c88332
set Bkmk(hcolor) \#ff0000
set Bkmk(record) {time type severity loc comment eset}
set Bkmk(fd) ""
set Bkmk(auto) 1
set Bkmk(lock) 0
set Bkmk(dir) "!NEW!"
set Bkmk(fname) "!NEW!"
set Bkmk(file) ""

######################
# Refresh visible
# bookmarks.
# Also erase old
# bookmarks.
#
proc BkmkRefresh { } {
  ClearAllHighlights 
  bkListShow no
}

######################
#
# BkmkDlgSetup --
#
#   Common code for dialog boxes from the
#   bookmark menu.
#
proc BkmkDlgSetup { {view 0} } {
    global Bkmk param

    if { $view && [info exists Bkmk(View)]} {
	return 1
    }
    set Bkmk(eopts) [list -bg white\
			 -relief sunken \
			 -borderwidth 2\
			 -highlightthickness 0]
    set Bkmk(lopts) [list -bg $param(color-ctlbg)\
			 -relief flat \
			 -borderwidth 0\
			 -highlightthickness 0]
    set Bkmk(bopts) [list -bg $param(color-buttonbg) \
			 -fg $param(color-buttonfg) \
			 -activebackground $param(color-buttonabg) \
			 -highlightbackground $param(color-ctlbg)\
			 -font $param(bigfont) ]
    
    return 0
}

######################
#
# BkmkAdd -- Add a new bookmark.
#
#
proc BkmkAdd { } {
    global param widget Bkmk

    if { ![bkGetLock] } { return }

    $widget(SL) config -cursor {sb_up_arrow}
    set Bkmk(set_time) 0

    #
    # Make a dialog box for bookmark parameters.
    #
    set eopts $Bkmk(eopts)
    set lopts $Bkmk(lopts)
    set bopts $Bkmk(bopts)
    set dlg .bkdlg
    toplevel $dlg
    wm title $dlg "Add Bookmark"
    wm minsize $dlg 200 150
    #
    set maxwid 0
    foreach field $Bkmk(record) { 
	if { $field != "comment" } {
	    set maxwid [max [string length $field] $maxwid]
	}
    }
    #
    # Build dialog box for new entry
    #
    set ctr 0
    set Bkmk(set_time)     [format "%.2f" [expr [SLGetTime $widget(SL) $widget(SL,urhere)] / 1000.0 + $param(TimeWindow) / 2]]
    set Bkmk(set_type)     "U"
    set Bkmk(set_priority) 1
    set Bkmk(set_comment)  ""
    foreach field $Bkmk(record) {
	set Field [string toupper [string range $field 0 0]][string range $field 1 end]
	if { $Field == "Time" } { set Field "Time (s)" }
	set f [frame $dlg.f$ctr -bg $param(color-ctlbg)]

	if { $field != "comment" } {
	    eval {label $f.nm$field -text "$Field" -width $maxwid -anchor w} $lopts
	    eval {entry $f.v$field -width 20 -textvariable Bkmk(set_$field)} $eopts
	    pack $f.nm$field $f.v$field -side left -anchor w -fill x
	} else {
	    eval {label $f.nm$field -text "Comment:" -width $maxwid -anchor w} $lopts
	    set f2 [frame $f.txt]
	    text $f2.comment -height 6 -width 40 -wrap word \
		-yscrollcommand "$f2.sy set" -bg white
	    scrollbar $f2.sy -orient vert -command "$f2.v$field yview"
	    pack $f2.sy -side right -fill y
	    pack $f2.comment -side left -fill both -expand 1
	    pack $f.nm$field $f2 -side top -anchor w
	    set Bkmk(W,comment) $f2.comment
	}
	pack $f -side top -anchor w -fill both -expand 1
	incr ctr
    }
    #
    set f [frame $dlg.cmd -bg $param(color-ctlbg)]
    eval {button $f.ok -text OK -command bkAddOk } $bopts
    eval {button $f.cancel -text Cancel -command bkAddCancel } $bopts
    pack $f.ok $f.cancel -side left -expand 1 -fill x
    pack $f -expand 1 -fill both
    #
    # Bindings for summary-line canvas during the
    # bookmark process.
    #
    # On motion, make the dialog box visible, and
    # update the bookmark time if the pointer is
    # close enough to the line.

   set Bkmk(bind_SLMotion) [bind $widget(SL) <Motion>]
   bind $widget(SL) <Motion> {
	focus .bkdlg
	if { [expr abs(%y - $param(SL,mid))] < 10 } {
	    set Bkmk(set_time) [max 0 [SLPosToTime %x]]
	}
    }
    # On click, set the time and add a marker
    set Bkmk(bind_SLB1) [bind $widget(SL) <Button-1>]
    bind $widget(SL) <Button-1> {
	if { [expr abs(%y - $param(SL,mid))] < 10 } {		
	    bkAddMarker yes
	}
	focus .bkdlg
    }
    # When we leave the canvas, make the time equal
    # to the marker's, if there is one, and make the dialog
    # box visible.
    set Bkmk(bind_SLLeave) [bind $widget(SL) <Leave>]
    bind $widget(SL) <Leave> {
	set w [$widget(SL) find withtag bkmk$Bkmk(Num)]
	if { $w != "" } {
	    set Bkmk(set_time) \
		[SLPosToTime [lindex [$widget(SL) coords $w] 0]] 
	}
	focus .bkdlg
    }
    #
    # Bindings for the graph widget during the
    # bookmark process.
    #
    # On click, set the time and add a marker and
    # also make the dialog box visible
    set Bkmk(bind_B1) [bind $widget(graph,0) <Button-1>]
    set Bkmk(bind_Motion) [bind $widget(graph,0) <B1-Motion>]
    set Bkmk(bind_Release) [bind $widget(graph,0) <ButtonRelease-1>]
    bind $widget(graph,0) <Button-1> {
	set x [lindex [$widget(graph,0) invtransform %x %y] 0]
	set Bkmk(set_time) [prec [expr $x / $param(_scale) ]]
	bkAddMarker yes
	focus .bkdlg
    }
    # Get rid of zoombox bindings, reinstate when done.
    bind $widget(graph,0) <B1-Motion> {}
    bind $widget(graph,0) <ButtonRelease-1> {}	
}

proc prec { var {num 2}} { set s "%.${num}f" ; return [format $s $var] }

proc bkAddOk { } {
    global Bkmk widget

    set n $Bkmk(Num)
    #
    # Add the bookmark to the arrays of
    # times and comments
    #
    set cmnt [$Bkmk(W,comment) get 1.0 end]
    if { $cmnt == "" } {
	set cmnt "<None>"
    }
    foreach field $Bkmk(record) {
	if { $field != "comment" } {
	    set Bkmk($n,$field) $Bkmk(set_$field)
	}
    }
    set Bkmk($n,comment) $cmnt
    set chk [bkAddCheck]
    if {$chk != ""} {
	ErrorBox "$chk" "Bookmark Value Error" .bkerr
	bkReleaseLock
    } else {
	bkAddMarker
	incr Bkmk(Num)
	bkAddCleanup
	BkmkSave
	if { !$Bkmk(auto) } {
	    bkListUpdate
	}
    }
}
proc bkAddCheck { {t "NULL"} {p "NULL"} {ty "NULL"} } {
    global Bkmk

    if { $t == "NULL"  } { set t $Bkmk(set_time) }
    if { $p == "NULL"  } { set p $Bkmk(set_priority) }
    if { $ty == "NULL" } { set ty $Bkmk(set_type) }
    set r ""
    set numexp {-?[0-9]+\.?[0-9]*}
    if { ! [regexp -- $numexp $t] } {
	set r "Invalid time: must be a number.\n"
    }
    if { ! [regexp -- $numexp $p] } {
	set r "${r}Invalid severity: must be a number.\n"
    }
    if { ! [regexp "U|S" $ty] } {
	set r "${r}Invalid type: must be U(ser) or S(ystem).\n"
    }
    
    return $r
}
proc bkAddMarker { {move no} {t -1} {n -1}} {
    global widget Bkmk
    if { $n < 0 } {
	set n $Bkmk(Num)
    }
    if { $t < 0 } { 
	set t $Bkmk(set_time)
    }
    set w [$widget(SL) find withtag bkmk$n]
    if { $w == "" } {
	SLAddMarker bkmk$n $widget(SL) adown $Bkmk(color) $t up no
    } elseif { $move } {
	SLMoveMarker $widget(SL) bkmk$n $t
    }
}
    
proc bkAddCancel { } {
    global Bkmk widget

    SLRemoveMarker $widget(SL) bkmk$Bkmk(Num)
    bkAddCleanup
}
proc bkAddCleanup { } {
    global widget Bkmk

    destroy .bkdlg
    $widget(SL) config -cursor {left_ptr}
    bind $widget(SL) <Motion> $Bkmk(bind_SLMotion)
    bind $widget(SL) <Button-1> $Bkmk(bind_SLB1)
    bind $widget(SL) <Leave> $Bkmk(bind_SLLeave)
    bind $widget(graph,0) <Button-1> $Bkmk(bind_B1)
    bind $widget(graph,0) <B1-Motion> $Bkmk(bind_Motion)
    bind $widget(graph,0) <ButtonRelease-1> $Bkmk(bind_Release)

    bkReleaseLock
}

######################
#
# BkmkModify -- Edit existing bookmark
#
#

proc BkmkModify { } {
}


######################
#
# BkmkSave -- Save bookmarks to a file
#
#

proc BkmkSave { } {
    global Bkmk

    if { $Bkmk(file) == "" } { return }

    set f [open $Bkmk(file) "w"]
    for { set i 0 } { $i < $Bkmk(Num) } { incr i } {
	foreach field $Bkmk(record) {
	    regsub -all "\n" $Bkmk($i,$field) " " $field
	}
	puts $f [bkSaveFormat "$time" "$type" "$severity" "$loc" "$comment" "$eset" ]
    }
    close $f
}
######################
# Format a bookmark to a string
#
proc bkSaveFormat { time type severity loclist comment eset } {
    if { $type == "User" || $type == "U" } {
	set ctype "U"
    } else {
	set ctype "S"
    }
    set locstr [join $loclist ","]

    return "$ctype;$severity;$time;$locstr;$comment;$eset"
}

######################
#
# BkmkLoad -- 
#
#   Load in bookmarks
#   from a file.
#

proc bkLoadAdd { f } {
    global Bkmk

    set cnt 0
    set i $Bkmk(Num)
    set l1 [gets $f]
    while { ![eof $f] } {
	if { $l1 == "" } {
	    ErrorBox "Empty line in bookmark file.\n$cnt bookmarks loaded." \
		"Bookmark Load Error" .bkerr
	    set Bkmk(Num) $i
	    return
	}
	set r [bkLoadParse $i "$l1"]
	if { $r != "" } {
	    ErrorBox "$r.\n$cnt bookmarks loaded." "Bookmark Load Error" .bkerr
	    set Bkmk(Num) $i
	    return
	}	    
	set r [bkAddCheck $Bkmk($i,time) $Bkmk($i,severity) $Bkmk($i,type)]
	if { $r != "" } {
	    ErrorBox "$r.\n$cnt bookmarks loaded." "Bookmark Load Error" .bkerr
	    set Bkmk(Num) $i
	    return
	}
	bkAddMarker no $Bkmk($i,time) $i
	bkShowPosition $Bkmk($i,time)
	incr cnt
	incr i
	set l1 [gets $f]
    }
    set Bkmk(Num) $i
}
#################################
#
# bkLoadParse --
#
#      Parse bookmarks from a string.
#
proc bkLoadParse { num s } {
    global Bkmk

    set locs ""
    regexp -nocase {([^\;]+);([0-9]*);([0-9.E-]*);([^;]*);([^;]*);([^;]*)} $s \
	matchvar Bkmk($num,type) Bkmk($num,severity) Bkmk($num,time) \
	locs Bkmk($num,comment) Bkmk($num,eset)
   set Bkmk($num,loc) [split $locs ","]

    return
}

#################################
#
# Merge bookmarks into existing ones.
#
proc bkLoadMerge { f } {
    global Bkmk

    if { $f == "" } { return }
    bkLoadAdd $f
    close $f
    bkListUpdate
}

#################################
#
# Replace bookmarks with existing ones.
#
proc bkLoadReplace { f } {
    global Bkmk

    if { $f == "" } { return }
    set Bkmk(Num) 0
    bkLoadAdd $f
    close $f
    bkListUpdate
}

######################
#
# BkmkList -- List all bookmarks.
#
#

proc BkmkList { } {
    global param widget Bkmk

    if { [BkmkDlgSetup 1] != 0 } {
	return
    }
    set Bkmk(View) yes

    #
    # Set up the dialog box
    #
    set dlg .bklist
    toplevel $dlg
    wm title $dlg "List of Bookmarks"
    wm resizable $dlg 0 0
    #
    set lopts $Bkmk(lopts)
    set bopts $Bkmk(bopts)
    # Frames
    frame $dlg.lbl -bg $param(color-ctlbg)
    frame $dlg.but -bg $param(color-ctlbg)
    frame $dlg.but.1 -bg $param(color-ctlbg)
    frame $dlg.but.2 -bg $param(color-ctlbg)
    # Labels for the listboxes
    set f $dlg.lbl
    eval {label $f.nm1 -padx 5 -text "Type"     -width 4} $lopts
    eval {label $f.nm2 -padx 5 -text "Lvl"      -width 3} $lopts
    eval {label $f.nm3 -padx 5 -text "Time (sec)" -width 8} $lopts
    eval {label $f.nm4 -padx 5 -text "Comment"  -width 36} $lopts
    # eval {label $f.nm5 -padx 5 -text "Location(s)" -width 16} $lopts
    eval {label $f.nm5 -padx 5 -text "Key Set" -width 8} $lopts
    pack $f.nm1 $f.nm2 $f.nm3 $f.nm4 $f.nm5  -side left
    pack $dlg.lbl -side top -expand 1 -fill both
    #
    # Buttons with commands
    #
    set f $dlg.but.1 
    eval {button $f.sv -text "Save" -command BkmkSave} $bopts
    eval {button $f.ld -text "Load" -command BkmkLoad} $bopts
    if { $Bkmk(auto) } {
	$f.sv configure -state disabled
	$f.ld configure -state disabled
    }
    pack $f.sv $f.ld -side left
    set f $dlg.but.2
    eval {button $f.ad -text "Add" -command BkmkAdd } $bopts
    eval {button $f.rm -text "Delete" -command bkListDelete } $bopts
    eval {button $f.go -text "Goto" -command bkListGoto } $bopts
    eval {button $f.sw -text "Show" -command bkListShow } $bopts
    eval {button $f.ok -text "Done" -command bkListOk } $bopts
    pack $f.ad $f.rm $f.go $f.sw -side left -anchor w
    pack $f.ok -side right -anchor e

    pack $dlg.but.1 $dlg.but.2 -side left -expand 1
    pack $dlg.but -side bottom -fill both -expand 1
    #
    set f [frame $dlg.f2 -bg $param(color-ctlbg)]
    # procedure ScrolledLB defined in "keydlg.tcl"
    set lb1 [ScrolledLBh $f.1 -width 4  -selectmode 1 -height 10 -background white]
    set lb2 [ScrolledLBh $f.2 -width 3  -selectmode 1 -height 10 -background white]
    set lb3 [ScrolledLBh $f.3 -width 8 -selectmode 1 -height 10 -background white]
    set lb4 [ScrolledLBh $f.4 -width 36 -selectmode 1 -height 10 -background white]
    set lb5 [ScrolledLB $f.5 -width 8 -selectmode 1 -height 10 -background white]
    # there can be only one (scrollbar)
    $f.5.sy configure -command [list multiscroll $f]
    # pack 'em up!
    pack $f.1 $f.2 $f.3 $f.4 $f.5 -side left
    pack $f -side bottom -expand 1 -fill both
    foreach i {1 2 3 4 5} { 
	set widget(BkmkL$i) [set lb$i]
    }
    #
    #  Fill listboxes with data
    #
    bkListUpdate
    #
    # Bind selection in any listbox with highlighting of
    # the associated marker.
    #
    foreach i {1 2 3 4} {
	set e lb$i
	bind [set $e] <ButtonPress-1> { set Bkmk(GotoSel) [%W nearest %y] }       
	bind [set $e] <ButtonPress-2> { set Bkmk(GotoSel) [%W nearest %y]; bkListGoto } 
    }
}
# for scrolling all listboxes at once
proc multiscroll { b cmd v {wrd "!"}} {
  foreach i {1 2 3 4 5} {
    if { $wrd == "!" } {
      $b.$i.lb yview $cmd $v
    } else {
      $b.$i.lb yview $cmd $v $wrd
    }
  }
}
#################################
# Update list
#
proc bkListUpdate { } {
    global Bkmk widget

    if { ![info exists Bkmk(View)] } { 
	bkReleaseLock
	return 
    }

    foreach i {1 2 3 4 5} {
	$widget(BkmkL$i) delete 0 end
    }
    for { set i 0 } { $i < $Bkmk(Num) } { incr i } {
	$widget(BkmkL1) insert end $Bkmk($i,type)
	$widget(BkmkL2) insert end $Bkmk($i,severity)
	$widget(BkmkL3) insert end [prec $Bkmk($i,time)]
	$widget(BkmkL4) insert end "$Bkmk($i,comment) "
#	$widget(BkmkL5) insert end "[join $Bkmk($i,loc) ,] "
	$widget(BkmkL5) insert end $Bkmk($i,eset) 
    }
    update idletasks
}
proc bkListOk { } {
    global widget Bkmk
    bkListCleanup
}
proc bkListCleanup { } {
    global widget Bkmk
    destroy .bklist
    unset Bkmk(View)
}
##################################
# Go to and show the selected
# bookmark
#
proc bkListGoto { } {
    global Bkmk widget param

    if { ![info exists Bkmk(GotoSel)] } {
      bkReleaseLock
      return
    }
    set sel $Bkmk(GotoSel)
    if { ![info exists Bkmk($sel,time)] } {
	  bkReleaseLock
	  return
    }
    # Center in window
    set t [expr $Bkmk($sel,time) - $param(TimeWindow) / 2]
    if { $t < 0 } { set t 0 }
    # Move marker
    SLMoveMarker $widget(SL) $widget(SL,urhere) $t
    # Refresh display
    Refresh 1
    # Show it
    bkListShow no

    bkReleaseLock
}
##################################
# Show the selected bookmark
#
proc bkListShow { {refresh no} } {
    global Bkmk widget param

    if { ![info exists Bkmk(GotoSel)] } {
       bkReleaseLock
       return
    }
    set sel $Bkmk(GotoSel)
    if { ![info exists Bkmk($sel,time)] } {
	bkReleaseLock
	return
    }
    # Draw a dashed line
    bkShowPosition $Bkmk($sel,time)
    # Show elements
    SetFilePosSymbols $Bkmk($sel,loc) $param(bookmarksymbol)
    # Refresh display ..?
    if { $refresh } {
	Refresh 1
    }

    bkReleaseLock
}
##################################
# Delete a bookmark
#
proc bkListDelete { } {
    global Bkmk widget

    if { ![bkGetLock] } { return }

    set sel [$widget(BkmkL1) curselection]
    # Don't allow deletion of multiple bookmarks with
    # one command.
    if { [llength $sel] > 1 || ![info exists Bkmk($sel,time)] } { 
	bkReleaseLock
	return
    }
    # Delete marker on "stat-line"
    SLRemoveMarker $widget(SL) bkmk$sel
    # Renumber the tags
    for { set i $sel } { $i < [expr $Bkmk(Num)-1] } { incr i } {
	set j [expr $i+1]
	foreach fld $Bkmk(record) {
	    set Bkmk($i,$fld) $Bkmk($j,$fld)
	}
	SLRenameMarker $widget(SL) bkmk$j bkmk$i
    }
    # Delete element from list-boxes
    for {set i 1} {$i <= 5} {incr i} {
	$widget(BkmkL$i) delete $sel
    }
    # Decrement # of bookmarks
    incr Bkmk(Num) -1
    # Update file
    BkmkSave

    bkReleaseLock
}
##################################
# Show the bookmark on the screen 
# with a dashed line.
#
proc bkShowPosition { t } {
    global param widget

    set g $widget(graph,0)
    set t [expr $t * $param(_scale) * 1.0 ]
    $g element configure bkmk -xdata [list $t $t] -ydata [$g yaxis limits]
}
############################
#
# BkmkFileDlg --
#
#     Allow the user to choose
#  the bookmark file and,
#  optionally, change the auto-
#  update option.
#
proc BkmkFileDlg { } {
    global Bkmk param env

    if { ![bkGetLock] } { return }

    BkmkDlgSetup 0
    set eopts $Bkmk(eopts)
    set lopts $Bkmk(lopts)
    set bopts $Bkmk(bopts)
    set dlg .bkfile
    toplevel $dlg
    wm title $dlg "Choose Bookmark File"
    wm minsize $dlg 350 150
    #
    set f1 [frame $dlg.f1 -bg $param(color-ctlbg)]
    set f2 [frame $dlg.f2 -bg $param(color-ctlbg)]
    set f3 [frame $dlg.f3 -bg $param(color-ctlbg)]
    eval { label $f1.dnm -text "Directory" -width 10 } $lopts
    eval { entry $f1.de -textvariable Bkmk(dir) -width 32 } $eopts
    eval { label $f2.fnm -text "File" -width 10 } $lopts
    eval { entry $f2.fe -textvariable Bkmk(fname) -width 32 } $eopts
    eval { button $f3.ok -text "OK" -command bkFileOK } $bopts
    eval { button $f3.cancel -text "Cancel" -command bkFileCancel } $bopts
    eval { checkbutton $f3.auto -text "Auto-update" -variable Bkmk(auto) } $bopts
    #
    set Bkmk(old_dir)   $Bkmk(dir)
    set Bkmk(old_file)  $Bkmk(file)
    set Bkmk(old_fname) $Bkmk(fname)
    set Bkmk(old_auto)  $Bkmk(auto)
    #
    if { $Bkmk(dir) == "!NEW!" } {
	set df [bkCleanFilename $env(PWD) $param(fname)]
	set f [lindex $df 1]
	set len [string length $f]
	if { $len > 5 && [string range $f [expr $len - 4] end] == ".log" } {
	    set Bkmk(fname) "[string range $f 0 [expr $len - 5]].bkmk"
	} else {
	    set Bkmk(fname) "$f.bkmk"
	}
	set Bkmk(dir) [lindex $df 0]
    } else {
	set df [bkCleanFilename $Bkmk(dir) $Bkmk(fname)]
	set Bkmk(dir) [lindex $df 0]
	set Bkmk(fname) [lindex $df 1]
    }
    #
    pack $f1.dnm $f1.de -side left -anchor w
    pack $f2.fnm $f2.fe -side left -anchor w
    pack $f3.ok -side left -padx 20
    pack $f3.auto -side right -anchor e
    pack $f3.cancel -side right -padx 20
    pack $f1 $f2 $f3 -side top -anchor w -expand 1 -fill both
}
##################################
# Attempt to open the new file.
#
proc bkFileOK { {gui 1} } {
    global Bkmk

    set err "Bookmark Error"
    set win .errdlg

    if { $Bkmk(fname) == "" } {
	ErrorBox "No file specified." $err $win
    } else {
	set tmp [bkCleanFilename $Bkmk(dir) $Bkmk(fname)]
	set Bkmk(dir) [lindex $tmp 0]
	set Bkmk(fname) [lindex $tmp 1]
	set Bkmk(file) "$Bkmk(dir)/$Bkmk(fname)"
	puts "file = $Bkmk(file)"
	set f $Bkmk(file)
	if { ![file exists $f] } {
	    close [open $f "w"]
	}
	if { ![file writable $f] || ![file readable $f] } {
	    ErrorBox "Cannot read and write to file '$f'." $err $win
	} else {
	    if { $gui } { bkFileRemoveDlg }
	    bkUpdateFromFile
	}
    }
}
##################################
# Forget changes to file name
# and/or directory
proc bkFileCancel { } {
    global Bkmk

    set Bkmk(file) $Bkmk(old_file)
    set Bkmk(fname) $Bkmk(old_fname)
    set Bkmk(dir)  $Bkmk(old_dir)
    set Bkmk(auto) $Bkmk(old_auto)
    bkFileRemoveDlg
}
##################################
# Get rid of file-choice dialog
# window.
proc bkFileRemoveDlg { } {
    bkReleaseLock
    destroy .bkfile
}
################################
#
# bkCleanFilename --
#
# Take leading path from filename and 
# put in directory string.
# Return { dir filename }
#
proc bkCleanFilename { cwd f } {
    regexp {^.*\/} $f fpath
    if { [info exists fpath] } {
	regsub {^.*\/} $f "" f
	cd $fpath
	set x [pwd]
	cd $cwd
	return [list $x $f]
    }
    return [list $cwd $f]
}

#################################
#
# bkUpdateFromFile --
#
#      Reload all the bookmarks
# from the file because somebody
# else asynchronously changed them.
#
proc bkUpdateFromFile { } {
    global Bkmk

    if { ![bkGetLock] } { return }
    bkLoadReplace [open $Bkmk(file) "r"]
    bkReleaseLock
}

################################
# Get the exclusive lock
#
proc bkGetLock { } {
    global Bkmk

#    puts "Bkmk(lock) = $Bkmk(lock)"
    if { !$Bkmk(lock) } {
	set Bkmk(lock) 1
	return 1
    }
    return 0
}

#################################
# Release the exclusive lock
#
proc bkReleaseLock { } {
    global Bkmk

#    puts "release lock"
    set Bkmk(lock) 0
}
