#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
#  Copyright (c) 2001 Miguel Filgueiras (mig@ncc.up.pt) / Universidade do Porto
#
#    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.
#
#  File: gendials.c
#  Last change:  4 September 2001
#
# Includes contributions by Brian Baulch (baulchb@onthenet.com.au)
#  marked "BSB contribution"
#

  # modal dialogs

proc GMMessage {mess} {
    # create modal dialog for displaying message
    #  single button: OK; binding: return
    global COLOUR EPOSX EPOSY TXT

    set wait 1
    destroy .mess
    toplevel .mess
    wm protocol .mess WM_DELETE_WINDOW { set wait 0 }
    wm title .mess "GPS Manager: $TXT(message)"
    wm transient .mess .
    wm geometry .mess +$EPOSX+$EPOSY
    bind .mess <Key-Return> { set wait 0 }

    frame .mess.fr -borderwidth 5 -bg $COLOUR(messbg)
    label .mess.fr.title -text "!!!" -relief sunken
    label .mess.fr.text -text "$mess"
    button .mess.fr.ok -text Ok -command { set wait 0 }
    pack .mess.fr -side top
    pack .mess.fr.title .mess.fr.text .mess.fr.ok -side top -pady 5
    update idletasks
    grab .mess
    RaiseWindow .mess
    tkwait variable wait
    destroy .mess
    update idletasks
    return
}

proc GMConfirm {mess} {
    # create modal dialog for displaying message
    #  buttons: OK, Cancel; bindings: return, delete
    global Temp2 COLOUR EPOSX EPOSY TXT

    set wait2 1
    destroy .mess
    toplevel .mess
    wm protocol .mess WM_DELETE_WINDOW { set Temp2 0 ; set wait2 0 }
    wm title .mess "GPS Manager: $TXT(message)"
    wm transient .mess .
    wm geometry .mess +$EPOSX+$EPOSY
    bind .mess <Key-Return> { set Temp2 1 ; set wait2 0 }
    bind .mess <Key-Delete> { set Temp2 0 ; set wait2 0 }

    frame .mess.fr -borderwidth 5 -bg $COLOUR(confbg)
    label .mess.fr.title -text "???" -relief sunken
    label .mess.fr.text -text "$mess"
    frame .mess.fr.bs
    button .mess.fr.bs.ok -text Ok -command "set Temp2 1 ; set wait2 0"
    button .mess.fr.bs.cancel -text $TXT(cancel) \
	    -command "set Temp2 0 ; set wait2 0"
    pack .mess.fr.bs.ok .mess.fr.bs.cancel -side left -pady 5
    pack .mess.fr.title .mess.fr.text .mess.fr.bs -side top -pady 5
    pack .mess.fr -side top
    update idletasks
    grab .mess
    RaiseWindow .mess
    tkwait variable wait2
    destroy .mess
    update idletasks
    return $Temp2
}

proc GMSelect {mess blist vlist} {
    # create modal dialog for selecting one element from list $blist
    #  with associated return values in $vlist
    # buttons are created for each list element
    #  bindings: return for first, delete for last element
    # (see proc GMChooseFrom for selection using a listbox)
    global Temp3 COLOUR EPOSX EPOSY TXT

    # assumes first and last elements of vlist are return values for
    #  Return and Delete keys, respectively

    set wait3 1
    destroy .mess
    toplevel .mess
    wm protocol .mess WM_DELETE_WINDOW \
	    "set Temp3 [lindex $vlist 0] ; set wait3 0"
    wm title .mess "GPS Manager: $TXT(selection)"
    wm transient .mess .
    wm geometry .mess +$EPOSX+$EPOSY
    bind .mess <Key-Return> "set Temp3 [lindex $vlist 0] ; set wait3 0"
    bind .mess <Key-Delete> "set Temp3 [lindex $vlist end] ; set wait3 0"

    frame .mess.fr -borderwidth 5 -bg $COLOUR(selbg)
    label .mess.fr.title -text "???" -relief sunken
    label .mess.fr.text -text "$mess"
    frame .mess.fr.frsel
    foreach e $blist v $vlist {
	button .mess.fr.frsel.b$e -text $e -command "set Temp3 $v; set wait3 0"
	pack .mess.fr.frsel.b$e -side left
    }
    pack .mess.fr -side top
    pack .mess.fr.title .mess.fr.text .mess.fr.frsel -side top -pady 5
    update idletasks
    grab .mess
    RaiseWindow .mess
    tkwait variable wait3
    destroy .mess
    update idletasks
    return "$Temp3"
}

proc GMOpenFile {act wh mode} {
    # create modal dialog for selecting and opening a file
    #  $act is string describing the action to do on the file
    #  $wh in $FileTypes (see proc GMStart, main.tcl)
    #  $mode in {r, w}
    #  buttons: OK, Cancel
    #  binding: return and double-left for commit, left-click for select
    global Temp4 COLOUR DPOSX DPOSY LISTHEIGHT File FileTypes TXT MESS \
	    tcl_platform

    toplevel .fdlg
    wm protocol .fdlg WM_DELETE_WINDOW { set Temp4 cnc ; set wait4 0 }
    wm title .fdlg "GPS Manager: $TXT(file)"
    wm transient .fdlg .
    wm geometry .fdlg +$DPOSX+$DPOSY
    bind .fdlg <Key-Return> { set Temp4 ok ; set wait4 0 }

    frame .fdlg.fr -borderwidth 5 -bg $COLOUR(selbg)
    label .fdlg.fr.title -text [format $MESS(fileact) $act $TXT(name$wh)] \
	    -relief sunken
    if { "$tcl_platform(platform)" == "windows" } {
	menubutton .fdlg.fr.vols -text $TXT(volume) -menu .fdlg.fr.vols.m
	menu .fdlg.fr.vols.m
	bind .fdlg.fr.vols <Button-1> {
	    FillMenuExec .fdlg.fr.vols.m {ChangeVolume .fdlg} file volume
	}
    }
    entry .fdlg.fr.wdir -width 30
    ShowTEdit .fdlg.fr.wdir [pwd] 0

    frame .fdlg.fr.frbx
    listbox .fdlg.fr.frbx.box -height $LISTHEIGHT -width 30 \
	    -yscrollcommand ".fdlg.fr.frbx.bscr set" \
 	    -selectmode single -exportselection 1
    bind .fdlg.fr.frbx.box <Double-1> {
            global Temp4
            set Temp4 [%W nearest %y]
            set wait4 0
    }
    bind .fdlg.fr.frbx.box <Button-1> {
	.fdlg.fr.fn delete 0 end
	.fdlg.fr.fn insert 0 [%W get [%W nearest %y]]
    }
    scrollbar .fdlg.fr.frbx.bscr -command ".fdlg.fr.frbx.box yview"
    FillDir .fdlg.fr.frbx.box

    entry .fdlg.fr.fn -width 30
    .fdlg.fr.fn insert 0 $File($wh)
    TextBindings .fdlg.fr.fn
    
    frame .fdlg.fr.bs
    button .fdlg.fr.bs.ok -text Ok -command { set Temp4 ok ; set wait4 0 }
    button .fdlg.fr.bs.cnc -text $TXT(cancel) \
	    -command { set Temp4 cnc ; set wait4 0 }

    pack .fdlg.fr.bs.ok .fdlg.fr.bs.cnc -side left -pady 5
    pack .fdlg.fr.frbx.box .fdlg.fr.frbx.bscr -side left -fill y
    if { "$tcl_platform(platform)" != "windows" } {
	pack .fdlg.fr.title .fdlg.fr.wdir .fdlg.fr.frbx .fdlg.fr.fn \
		.fdlg.fr.bs -side top -pady 5
    } else {
	pack .fdlg.fr.title .fdlg.fr.vols .fdlg.fr.wdir .fdlg.fr.frbx \
		.fdlg.fr.fn .fdlg.fr.bs -side top -pady 5
    }
    pack .fdlg.fr -side top

    update idletasks
    grab .fdlg
    RaiseWindow .fdlg
    while 1 {
	tkwait variable wait4

	switch "$Temp4" {
	    ""  { }
	    cnc { 
		  destroy .fdlg
	          update idletasks
	          return ".."
	        }
	    ok  {
		  set fn [.fdlg.fr.fn get]
		  set f [GMCheckFile open $fn $mode]
		  if { "$f" != ".." } {
		      set File($wh) "$fn"
		      destroy .fdlg
		      update idletasks
		      return $f
		  }
	        }
	    0   {
		  cd ..
		  ShowTEdit .fdlg.fr.wdir [pwd] 0
		  .fdlg.fr.frbx.box delete 0 end ; FillDir .fdlg.fr.frbx.box
		  .fdlg.fr.fn delete 0 end
		  foreach i $FileTypes { set File($i) "" }
	        }
	    default {
		  set fn [.fdlg.fr.frbx.box get $Temp4]
		  set f [GMCheckFile open $fn $mode]
		  if { "$f" != ".." } {
		      set File($wh) "$fn"
		      destroy .fdlg
		      update idletasks
		      return $f
		  }
		}
	}
    }
}

proc GMOpenFileParms {act wh mode vars vals} {
    # create modal dialog for selecting and opening a file and parameters
    # see arguments of proc GMGetFileName
    global File

    set fname [GMGetFileName $act $wh $mode $vars $vals]
    if { "$fname" == ".." } { return ".." }
    return [open $fname $mode]
}

proc GMGetFileName {act wh mode vars vals} {
    # create modal dialog for selecting a file name and parameters
    #  $act is string describing the action to do on the file
    #  $wh in $FileTypes (see proc GMStart, main.tcl)
    #  $mode in {r, w}
    #  $vars is list of (global) vars to set
    #  $vals is associated list of value descriptions (see proc GMSetupParams)
    #  buttons: OK, Cancel
    #  binding: return and double-left for commit, left-click for select
    global Temp5 COLOUR DPOSX DPOSY LISTHEIGHT File FileTypes TXT MESS \
	    tcl_platform

    toplevel .fdlg
    wm protocol .fdlg WM_DELETE_WINDOW { set Temp5 cnc ; set wait5 0 }
    wm title .fdlg "GPS Manager: $TXT(file)"
    wm transient .fdlg .
    wm geometry .fdlg +$DPOSX+$DPOSY
    bind .fdlg <Key-Return> { set Temp5 ok ; set wait5 0 }

    frame .fdlg.fr -borderwidth 5 -bg $COLOUR(selbg)
    label .fdlg.fr.title -text [format $MESS(fileact) $act $TXT(name$wh)] \
	    -relief sunken
    if { "$tcl_platform(platform)" == "windows" } {
	menubutton .fdlg.fr.vols -text $TXT(volume) -menu .fdlg.fr.vols.m
	menu .fdlg.fr.vols.m
	bind .fdlg.fr.vols <Button-1> {
	    FillMenuExec .fdlg.fr.vols.m {ChangeVolume .fdlg} file volume
	}
    }
    entry .fdlg.fr.wdir -width 30
    ShowTEdit .fdlg.fr.wdir [pwd] 0

    frame .fdlg.fr.frbx
    listbox .fdlg.fr.frbx.box -height $LISTHEIGHT -width 30 \
	    -yscrollcommand ".fdlg.fr.frbx.bscr set" \
 	    -selectmode single -exportselection 1
    bind .fdlg.fr.frbx.box <Double-1> {
            global Temp5
            set Temp5 [%W nearest %y]
            set wait5 0
    }
    bind .fdlg.fr.frbx.box <Button-1> {
	.fdlg.fr.fn delete 0 end
	.fdlg.fr.fn insert 0 [%W get [%W nearest %y]]
    }
    scrollbar .fdlg.fr.frbx.bscr -command ".fdlg.fr.frbx.box yview"
    FillDir .fdlg.fr.frbx.box
    # BSB contribution: wheelmouse scrolling
    Mscroll .fdlg.fr.frbx.box

    entry .fdlg.fr.fn -width 30
    .fdlg.fr.fn insert 0 $File($wh)
    TextBindings .fdlg.fr.fn

    frame .fdlg.fr.fopt
    set es [GMSetupParams .fdlg.fr.fopt $vars $vals]

    frame .fdlg.fr.bs
    button .fdlg.fr.bs.ok -text Ok -command { set Temp5 ok ; set wait5 0 }
    button .fdlg.fr.bs.cnc -text $TXT(cancel) \
	    -command { set Temp5 cnc ; set wait5 0 }

    pack .fdlg.fr.bs.ok .fdlg.fr.bs.cnc -side left -pady 5
    pack .fdlg.fr.frbx.box .fdlg.fr.frbx.bscr -side left -fill y
    if { "$tcl_platform(platform)" != "windows" } {
	pack .fdlg.fr.title .fdlg.fr.wdir .fdlg.fr.frbx .fdlg.fr.fn \
		.fdlg.fr.fopt .fdlg.fr.bs -side top -pady 5
    } else {
	pack .fdlg.fr.title .fdlg.fr.vols .fdlg.fr.wdir .fdlg.fr.frbx \
		.fdlg.fr.fn .fdlg.fr.fopt .fdlg.fr.bs -side top -pady 5
    }
    pack .fdlg.fr -side top

    update idletasks
    grab .fdlg
    RaiseWindow .fdlg
    while 1 {
	tkwait variable wait5

	switch $Temp5 {
	    ""  { }
	    cnc { 
		  destroy .fdlg
	          update idletasks
	          return ".."
	        }
	    ok  {
		  set fn [.fdlg.fr.fn get]
		  set f [GMCheckFile check $fn $mode]
		  if { "$f" != ".." } {
		      set File($wh) $fn
		      GMUseEntries .fdlg.fr.fopt $es
		      destroy .fdlg
		      update idletasks
		      return $fn
		  }
	        }
	    0   {
		  cd ..
		  ShowTEdit .fdlg.fr.wdir [pwd] 0
		  .fdlg.fr.frbx.box delete 0 end ; FillDir .fdlg.fr.frbx.box
		  .fdlg.fr.fn delete 0 end
		  foreach i $FileTypes { set File($i) "" }
	        }
	    default {
		  set fn [.fdlg.fr.frbx.box get $Temp5]
		  set f [GMCheckFile check $fn $mode]
		  if { "$f" != ".." } {
		      set File($wh) $fn
		      destroy .fdlg
		      update idletasks
		      return $fn
		  }
		}
	}
    }
}

proc GMCheckFile {how f mode} {
    # check name of file $f and if ok either open it and return file descriptor
    #  or return file name; otherwise return ".."
    #  $how in {open check}
    #  $mode in {r, w}
    global PERMS File FileTypes TXT MESS

    if { "$f" == "" } { bell ; return ".." }
    if { [file isdirectory $f] } {
	if { [file executable $f] } {
	    cd $f
	    ShowTEdit .fdlg.fr.wdir [pwd] 0
	    .fdlg.fr.frbx.box delete 0 end
	    FillDir .fdlg.fr.frbx.box
	    .fdlg.fr.fn delete 0 end
	    foreach i $FileTypes { set File($i) "" }
	} else {
	    bell
	}
    } elseif { "$mode" == "r" } {
	if { [file readable $f] } {
	    switch $how {
		open { return [open $f r] }
		check { return $f }
	    }
	} else {
	    bell
	}
    } elseif { [file exists $f] } {
	if { [file writable $f] } {
	    set m [GMSelect $MESS(filexists) \
		    [list $TXT(ovwrt) $TXT(app) $TXT(cancel)] "w a 0"]
	    if { $m != 0 } {
		switch $how {
		    open { return [open $f $m $PERMS] }
		    check { return $f }
		}
	    }
	} else {
	    bell
	}
    } elseif { [file writable [pwd]] } {
	switch $how {
	    open { return [open $f $mode $PERMS] }
	    check { return $f }
	}
    } else {
	bell
    }
    return ".."
}

proc ChangeVolume {w vol} {
    # file volume has changed $vol in file-selection dialog $w
    global FileTypes

    if { ! [file isdirectory $vol] } { bell ; return }
    cd $vol
    ShowTEdit .fdlg.fr.wdir [pwd] 0
    .fdlg.fr.frbx.box delete 0 end ; FillDir .fdlg.fr.frbx.box
    .fdlg.fr.fn delete 0 end
    foreach i $FileTypes { set File($i) "" }
    return
}

proc GMSetupParams {w vars vals} {
    # set-up widgets for setting parameters in a dialog
    #  $w is window parent
    #  $vars is list of (global) vars to set
    #  $vals is associated list of value descriptions as:
    #      @TEXT   checkbutton with label TEXT, values 0 1
    #      =TEXT   entry with label TEXT
    #      LIST    radiobutton with possible values in LIST
    # return list of entries, each as a pair: path from $w to entry and
    #  name of global variable to set; the list can be processed by
    #  proc GMUseEntries
    global COLOUR

    set i 0 ; set es ""
    foreach v $vars os $vals {
	global $v
	frame $w.f$i
	switch -glob $os {
	    @* {
		set os [string range $os 1 end]
		checkbutton $w.f$i.c$v -text $os -variable $v \
		   -anchor w -onvalue 1 -offvalue 0 -selectcolor $COLOUR(check)
		pack $w.f$i.c$v
	    }
	    =* {
		set os [string range $os 1 end]
		label $w.f$i.l$v -text $os -width 12
		entry $w.f$i.e$v -width 30
		catch "$w.f$i.e$v insert 0 [set $v]"
		TextBindings $w.f$i.e$v
		pack $w.f$i.l$v $w.f$i.e$v -side left
		lappend es "f$i.e$v $v"
	    }
	    default {
		set d 1
		foreach o $os {
		    radiobutton $w.f$i.r$o -text $o -variable $v \
			    -value $o -anchor w -selectcolor $COLOUR(check)
		    if { $d } { $w.f$i.r$o invoke ; set d 0 }
		    pack $w.f$i.r$o -side left -padx 2
		}
	    }
	}
	pack $w.f$i -side top -fill x -expand 1
	incr i
    }
    return $es
}

proc GMUseEntries {w es} {
    # set global variables according to entries set-up by proc GMSetupParams
    #  $w is window parent
    #  $es is list of pairs with path from $w to entry and
    #   name of global variable to set

    foreach e $es {
	set v [lindex $e 1]
	global $v
	set $v [$w.[lindex $e 0] get]
    }
    return
}

proc GMChooseFrom {how mess wd blist vlist args} {
    # create modal dialog for selecting elements from list $blist
    #  with associated return values in $vlist
    #  $how in {single, many} defines number of elements that can be selected
    # a listbox is used with width $wd
    #  $args if present is a pair with $vars $vals, suitable for use
    #   with proc GMSetupParams, so that parameters may be selected
    #  buttons: OK, Cancel
    #  bindings: return for commit, extended select mode on listbox,
    #   make visible by initial char on listbox
    # (see also proc GMSelect for selection of only one element with buttons)
    global TempChFr DPOSX DPOSY COLOUR TXT

    set w .gmchoosefr
    if { [winfo exists $w] } { Raise $w ; bell ; return }

    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "set TempChFr cnc ; set waitchfr 0"
    wm title $w "GPS Manager: $mess"
    wm transient $w .
    wm geometry $w +$DPOSX+$DPOSY
    bind $w <Key-Return> "set TempChFr ok ; set waitchfr 0"

    frame $w.fr -borderwidth 5 -bg $COLOUR(dialbg)
    label $w.fr.text -text "$mess"

    if { [set l [llength $blist]] > 15 } {
	set l 15
    }
    frame $w.fr.frbx
    if { "$how" == "many" } {
	set mode extended
    } else { set mode single }
    listbox $w.fr.frbx.bx -height $l -width $wd -relief flat \
	    -selectmode $mode -yscrollcommand "$w.fr.frbx.bscr set" \
	    -exportselection 0
    bind $w.fr.frbx.bx <Enter> { focus %W }
    bind $w.fr.frbx.bx <Leave> "focus $w.fr.frbx"
    bind $w.fr.frbx.bx <Key> { ScrollListIndex %W %A }
    scrollbar $w.fr.frbx.bscr -command "$w.fr.frbx.bx yview"
    foreach i $blist { $w.fr.frbx.bx insert end $i }

    if { "$args" != "" } {
	set opts 1
	frame $w.fr.fopt
	set es [GMSetupParams $w.fr.fopt [lindex $args 0] [lindex $args 1]]
    } else { set opts 0 }

    frame $w.fr.frbt
    button $w.fr.frbt.ok -text Ok -command "set TempChFr ok ; set waitchfr 0"
    button $w.fr.frbt.cnc -text $TXT(cancel) \
	    -command "set TempChFr cnc ; set waitchfr 0"

    pack $w.fr.frbt.ok $w.fr.frbt.cnc -side left -pady 5
    pack $w.fr.frbx.bx $w.fr.frbx.bscr -side left -fill y
    if { $opts } {
	pack $w.fr.text $w.fr.frbx $w.fr.fopt $w.fr.frbt -side top -pady 5
    } else {
	pack $w.fr.text $w.fr.frbx $w.fr.frbt -side top -pady 5
    }
    pack $w.fr

    update idletasks
    grab $w
    RaiseWindow .fdlg
    while 1 {
	tkwait variable waitchfr

	switch $TempChFr {
	    ""  { }
	    cnc { 
		destroy $w
		update idletasks
		return ""
	    }
	    ok {
		set ss [$w.fr.frbx.bx curselection]
		if { "$ss" != "" } {
		    set r ""
		    foreach i $ss {
			lappend r [lindex $vlist $i]
		    }
		    if { $opts } {
			GMUseEntries $w.fr.fopt $es
		    }
		    destroy $w
		    return $r
		}
		bell
	    }
	}
    }
}

