#!/bin/bash
#\
exec wish -f "$0" ${1+"$@"}
#
# wacomcpl-exec -- Wacom Control Panel Utility to Change Configuration Setting.
#
# Author	: Ping Cheng <pinglinux@gmail.com>
# Creation date	: 04/05/2003
# Updated       : 2003 - 2010
#
# Based on calibrate 1998-99 Patrick Lecoanet --
#
# There are three ways to run this program:
#  wacomcpl:  full control panel features with pressure and button configuration, 
#		tablet and screen mapping, as well as calibration. 
#
#  wacomcpl calibrate dev: fesatures calibration for a LCD device (identified as dev)
#
#  wacomcpl calibrate dev on: fesatures calibration for a LCD device (identified as dev)
#			      and display warnings when abnormal coordinates are detected
#
# This code is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This code 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this code; if not, write to the Free
# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# 		WARNING
# The variables getOption() and getOptionDefault() are arrays, not function
# calls. getOption(foo) thus retrieves the previously stored value for key
# "foo", it does not execute anything more.
# Yes, this is awkward.

package require LIBWACOMXI

set currentScreen 0
set desktopWidth [ winfo screenwidth . ]
set desktopHeight [ winfo screenheight . ]
set screenWidth [ winfo screenwidth . ]
set screenHeight [ winfo screenheight . ]
set screenXBottom [ winfo screenwidth . ]
set screenYBottom [ winfo screenheight . ]

set screenX_org 0
set screenY_org 0
set swapThresh 100
set size 200
set circle_size 10
set line_size 30
set config_file "~/.wacomcplrc"

set origin_x [ expr ($desktopWidth/2) - ($desktopWidth/4) ]
set origin_y [ expr ($desktopHeight/2) - ($desktopHeight/4) ]
set windowSize "700x300"

set device ""
set showHelp 0
set modeToggleB 33
set ignoreButton 34
set doubleClickB 35
set displayToggleB 36
set keystrokeB 38
set maxNumTablets 227
set numControls 0
set maxNumButtons 18
set maxNumStripEvents 4
set calibration_sequence_only 0
set calibration_with_warning 0

proc updateCurrentScreenInfo {} {
    global device numScreens currentScreen 
    global screenXBottom screenYBottom screenX_org screenY_org 
    global getScreenInfo screenWidth screenHeight

    if { $numScreens($device) != 1 } {
        set screenInfo $getScreenInfo($device,Screen$currentScreen)
	set screenXBottom [ lindex $screenInfo 0 ]
	set screenYBottom [ lindex $screenInfo 1 ]
	set screenX_org [ lindex $screenInfo 2 ]
	set screenY_org [ lindex $screenInfo 3 ]
	set screenWidth abs([ expr $screenXBottom-$screenX_org ])
	set screenHeight abs([ expr $screenYBottom-$screenY_org ])
   }
}

proc pad {name geometry} {
    global size circle_size line_size

    set circleox [ expr $size/2-$circle_size/2 ]
    set circleoy $circleox
    set circlecx [ expr $circleox+$circle_size ]
    set circlecy $circlecx
    set vertx [ expr $size/2 ]
    set vertoy [ expr ($size-$line_size)/2 ]
    set vertcy [ expr $vertoy+$line_size ]
    set horizox [ expr ($size-$line_size)/2 ]
    set horizcx [ expr $horizox+$line_size ]
    set horizy [ expr $size/2 ]

    toplevel $name
    wm geometry $name $geometry
    wm overrideredirect $name true
    canvas $name.m -height $size -width $size -bg "#505075"
    $name.m create oval $circleox $circleoy $circlecx $circlecy -outline white
    $name.m create line $vertx $vertoy $vertx $vertcy -fill white
    $name.m create line $horizox $horizy $horizcx $horizy -fill white
    pack $name.m
}

proc updateWacomrc {device option value} {
    global config_file

    if { ![ file exists $config_file ] } {
	exec echo "xsetwacom set \"$device\" $option \"$value\"" > $config_file
    } else {
	file copy -force $config_file $config_file~
	exec sed -e "/xsetwacom set \"$device\" $option /d" < $config_file > /tmp/wacomcpl.tmp
	exec cat /tmp/wacomcpl.tmp > $config_file
	exec echo "xsetwacom set \"$device\" $option \"$value\"" >> $config_file
	file delete -force /tmp/wacomcpl.tmp
    }
    if { $option == "PressCurve" } {
	set index 0
	set v1 [ lindex $value $index ]
    	for { set i 2 } { $i < 5 } { incr i 1 } {
	    set index [ expr $index+1 ]
	    set v$i [ lindex $value $index ]
	}
	exec xsetwacom set "$device" $option $v1 $v2 $v3 $v4
    } else {
	exec xsetwacom set "$device" $option $value
    }
}

proc verifycalibResultsTopLeft { } {
    global device getOptionDefault xDevMin yDevMin
    global screenWidth screenHeight size end

    set tol_w [expr ($getOptionDefault($device,BottomY) - $getOptionDefault($device,TopY)) * $size / $screenHeight]
    set tol_h [expr ($getOptionDefault($device,BottomX) - $getOptionDefault($device,TopX)) * $size / $screenWidth]

    set tol_top [expr $getOptionDefault($device,TopY)]
    set tol_bottom [expr $tol_top + $tol_h]
    set tol_left [expr $getOptionDefault($device,TopX)]
    set tol_right [expr $tol_left + $tol_w]

    if { ($xDevMin < $tol_left) || ($xDevMin > $tol_right) ||
    	($yDevMin < $tol_top) || ($yDevMin > $tol_bottom) } {
    	.topleft.m configure -background "#ff0000"
    	set end 0
    	after 100 set end 1
    	vwait end	

    	.topleft.m configure -background "#505075"
    	set end 0
    	after 70 set end 1
    	vwait end	

    	.topleft.m configure -background "#ff0000"
    	set end 0
    	after 100 set end 1
    	vwait end

    	set xDevMin [expr ($getOptionDefault($device,TopX) + ($tol_w / 2))]
    	set yDevMin [expr ($getOptionDefault($device,TopY) + ($tol_h / 2))]
    }
}

proc verifycalibResultsBottomRight { } {
    global device getOptionDefault xDevMax yDevMax
    global screenWidth screenHeight size end

    set tol_w [expr ($getOptionDefault($device,BottomY) - $getOptionDefault($device,TopY)) * $size / $screenHeight]
    set tol_h [expr ($getOptionDefault($device,BottomX) - $getOptionDefault($device,TopX)) * $size / $screenWidth]

    set tol_bottom [expr $getOptionDefault($device,BottomY)]
    set tol_top [expr $tol_bottom - $tol_h]
    set tol_right [expr $getOptionDefault($device,BottomX)]
    set tol_left [expr $tol_right - $tol_w]

    if { ($xDevMax < $tol_left) || ($xDevMax > $tol_right) ||
    	($yDevMax < $tol_top) || ($yDevMax > $tol_bottom) } {
    
    	.bottomright.m configure -background "#ff0000"
    	set end 0
    	after 100 set end 1
    	vwait end	

    	.bottomright.m configure -background "#505075"
    	set end 0
    	after 70 set end 1
    	vwait end	

    	.bottomright.m configure -background "#ff0000"
    	set end 0
    	after 100 set end 1
    	vwait end
    
    	set xDevMax [expr ($getOptionDefault($device,BottomX) - ($tol_w / 2))]
    	set yDevMax [expr ($getOptionDefault($device,BottomY) - ($tol_h / 2))]
    }
}

proc verifycalibResults { } {
    global device getOptionDefault xDevMin xDevMax yDevMin yDevMax
    global screenWidth screenHeight size clickCheck tvID

    # Verifying the end results
    if { $tvID == 1 } { # a Nvidia Xinerama setup
	set tol_offset [expr ($getOptionDefault($device,BottomY) / 4)]
	set tol_left [expr ($getOptionDefault($device,BottomY) - $tol_offset) / 2]
	set tol_right [expr ($getOptionDefault($device,BottomY) + $tol_offset) / 2]
	set tol_bottom [expr ($getOptionDefault($device,BottomY) - $tol_offset)]

	# set tablet Y values to normal when offset is by whole of the tablet size.
	# Whole has to be first and we do it two times to make sure.
	if { $yDevMin > $tol_bottom } {
	    set yDevMin [expr ($yDevMin - $getOptionDefault($device,BottomY))]
	    set yDevMax [expr ($yDevMax - $getOptionDefault($device,BottomY))]
	} 
	if { $yDevMin > $tol_bottom } {
	    set yDevMin [expr ($yDevMin - $getOptionDefault($device,BottomY))]
	    set yDevMax [expr ($yDevMax - $getOptionDefault($device,BottomY))]
	} elseif { ($yDevMin > $tol_left) && ($yDevMin < $tol_right) } {
	    # set tablet Y values to normal when offset is by half tablet size
	    set yDevMin [expr ($yDevMin - ($getOptionDefault($device,BottomY) / 2))]
	    set yDevMax [expr ($yDevMax - ($getOptionDefault($device,BottomY) / 2))]
	}

	set tol_offset [expr ($getOptionDefault($device,BottomX) / 4)]
	set tol_left [expr ($getOptionDefault($device,BottomX) - $tol_offset) / 2]
	set tol_right [expr ($getOptionDefault($device,BottomX) + $tol_offset) / 2]
	set tol_bottom [expr ($getOptionDefault($device,BottomX) - $tol_offset)]

	# set tablet X values to normal when offset is by whole of the tablet size. 
	# Whole has to be first and we do it two times to make sure.
	if { $xDevMin > $tol_bottom } {
	    set xDevMin [expr ($xDevMin - $getOptionDefault($device,BottomX))]
	    set xDevMax [expr ($xDevMax - $getOptionDefault($device,BottomX))]
	} 
	if { $xDevMin > $tol_bottom } {
	    set xDevMin [expr ($xDevMin - $getOptionDefault($device,BottomX))]
	    set xDevMax [expr ($xDevMax - $getOptionDefault($device,BottomX))]
	} elseif { ($xDevMin > $tol_left) && ($xDevMin < $tol_right) } {
	    # set tablet X values to normal when offset is by half tablet size
	    set xDevMin [expr ($xDevMin - ($getOptionDefault($device,BottomX) / 2))]
	   set xDevMax [expr ($xDevMax - ($getOptionDefault($device,BottomX) / 2))]
	}
    } else {
	set tol_w [expr ($getOptionDefault($device,BottomY) - $getOptionDefault($device,TopY)) * $size / $screenHeight]
	set tol_h [expr ($getOptionDefault($device,BottomX) - $getOptionDefault($device,TopX)) * $size / $screenWidth]

	set tol_top [expr $getOptionDefault($device,TopY)]
	set tol_bottom [expr $tol_top + $tol_h]
	set tol_left [expr $getOptionDefault($device,TopX)]
	set tol_right [expr $tol_left + $tol_w]

	if { ($xDevMin < $tol_left) || ($xDevMin > $tol_right) ||
		($yDevMin < $tol_top) || ($yDevMin > $tol_bottom) } {
	    set clickCheck 1
	}

	set tol_w [expr ($getOptionDefault($device,BottomY) - $getOptionDefault($device,TopY)) * $size / $screenHeight]
	set tol_h [expr ($getOptionDefault($device,BottomX) - $getOptionDefault($device,TopX)) * $size / $screenWidth]

	set tol_bottom [expr $getOptionDefault($device,BottomY)]
	set tol_top [expr $tol_bottom - $tol_h]
	set tol_right [expr $getOptionDefault($device,BottomX)]
	set tol_left [expr $tol_right - $tol_w]

	if { ($xDevMax < $tol_left) || ($xDevMax > $tol_right) ||
		($yDevMax < $tol_top) || ($yDevMax > $tol_bottom) } {
	    set clickCheck 1
	}
    }
}

proc calibrationSequence {which xDev yDev} {
    global device calibResults screenY_org screenX_org orig_device
    global workingTags screenTags size numScreens
    global swapThresh screenWidth screenHeight getOptionDefault
    global getDeviceModel screenXBottom screenYBottom
    global calibX_org calibY_org calibX_botm calibY_botm
    global xDevMin xDevMax yDevMin yDevMax
    global calibration_sequence_only clickCheck 
    global calibration_with_warning
    global currentScreen numScreens

    set scaling [exec xsetwacom get "$device" xscaling]
    if { $scaling == 1 } {
	set xNewDev [expr (($xDev - $calibX_org) * $getOptionDefault($device,BottomX))]
	set xDev [expr ($xNewDev / ($calibX_botm - $calibX_org))]
	set yNewDev [expr (($yDev - $calibY_org) * $getOptionDefault($device,BottomY))]
	set yDev [expr ($yNewDev / ($calibY_botm - $calibY_org))]
    }

    set calibResults(xDev,$which) $xDev
    set calibResults(yDev,$which) $yDev

    if { $which == 0 } {
	wacomxi::bindevent .topleft.m "$orig_device" <ButtonRelease> ""

	set xDevMin $calibResults(xDev,0)
	set yDevMin $calibResults(yDev,0)

	if { $calibration_with_warning != 0 } {
		verifycalibResultsTopLeft
	}

	.topleft.m configure -background "#505075"
	.topleft.m configure -cursor "watch #505075 #505075"
	.bottomright.m configure -background "#df94df"
	#hide the cursor
	.bottomright.m configure -cursor "center_ptr #df94df #df94df"
	wacomxi::bindevent .bottomright.m "$orig_device" <ButtonRelease> \
		{calibrationSequence 1 %0 %1}
    } elseif { $which == 1 } {
	wacomxi::bindevent .bottomright.m "$orig_device" <ButtonRelease> ""	
	set borderOffset [expr ($size / 2 )]

	set xDevMax $calibResults(xDev,1)
	set yDevMax $calibResults(yDev,1)
	.bottomright.m configure -background "#505075"
	.bottomright.m configure -cursor "watch #505075 #505075"

	# A direction verification of the click
	set clickCheck 0
	if { $calibResults(xDev,1) < $calibResults(xDev,0) } {
	    set clickCheck 1
	    set xDevMin $calibResults(xDev,1)
	    set xDevMax $calibResults(xDev,0)
	}
	if { $calibResults(yDev,1) < $calibResults(yDev,0) } {
	    set clickCheck 1
	    set yDevMin $calibResults(yDev,1)
	    set yDevMax $calibResults(yDev,0)
	}

	if { $calibration_with_warning != 0 } {
	    verifycalibResultsBottomRight
	    .bottomright.m configure -background "#505075"
	} else {
	    verifycalibResults
	}

	set widthDev [expr $xDevMax - $xDevMin]
	set heightDev [expr $yDevMax - $yDevMin]
 	set widthX [expr $screenWidth - $size]
 	set heightX [expr $screenHeight - $size]

	set xDevMin [expr $xDevMin - ($borderOffset * $widthDev / $widthX)]
	set xDevMax [expr $xDevMax + ($borderOffset * $widthDev / $widthX)]
	set yDevMin [expr $yDevMin - ($borderOffset * $heightDev / $heightX)]
	set yDevMax [expr $yDevMax + ($borderOffset * $heightDev / $heightX)]

	if { $numScreens($device) > 1} {
	    updateWacomrc $device "Screen_No" $currentScreen
	}

	updateWacomrc $device topx $xDevMin
	updateWacomrc $device topy $yDevMin
	updateWacomrc $device bottomx $xDevMax
	updateWacomrc $device bottomy $yDevMax

	# send the same data to the associated eraser if it is a stylus
	set type $getDeviceModel($device,type)
	if { ![ string compare -nocase $type "stylus" ] } {
	    set eraser $getDeviceModel($device,eraser)
	    if { [ string compare $eraser $device ] && $eraser != "" } {
		updateWacomrc $eraser topx $xDevMin
		updateWacomrc $eraser topy $yDevMin
		updateWacomrc $eraser bottomx $xDevMax
		updateWacomrc $eraser bottomy $yDevMax
	    }
	}

	if { $clickCheck == 1 } {
	    messageWindow "Warning !!!" \
		"\n\nIf the cursor follows the tip of your\n\
		tool better than before, you are fine.\n\
		Otherwise, please rerun the calibration\n\
		steps and make sure you are clicking\n\
		on the center of the pink crosshairs."
	}

	destroy .topleft .bottomright
	if { $numScreens($device) > 1 } {
            if { $calibration_sequence_only == 0 } {
		.screen.list.label configure -text ""
 		bindtags .screen.list.list $screenTags
	    } else {
		.list.label configure -text ""
 		bindtags .list.list $screenTags
	    }
	} else {
            if { $calibration_sequence_only == 1 } {
                exit 0
	    } else {
		closeTabWindow
	    }
	}
    }
}

proc Calibration {} {
    global numScreens device screenTags getOption Option 
    global tvID calibration_sequence_only

    # mainly to get the defaults
    set Option(1) "TopX"
    set Option(2) "TopY"
    set Option(3) "BottomX"
    set Option(4) "BottomY"
    set Option(5) "Mode"
    set Option(6) "TwinView"

    getDeviceOptionProc $device 6
    set mode $getOption($device,Mode)
    set tvID $getOption($device,TwinView)

    if { $calibration_sequence_only == 0 } {
	if { $mode != 1 && $mode != "Absolute" } {
	    disableButtons
	    messageWindow "Warning " "\n\nDevice $device is in relative mode. \n\
		You can calibrate $device only when it is in absolute mode. \n\
		Please swith $device's mode and try again. "
	    closeTabWindow
	    return
	}
    }

#    bindtags .workingDev.list .
    if { $numScreens($device) > 1 } {
	displayScreenList $device
	if { $calibration_sequence_only == 0 } {
		set screenTags [ bindtags .screen.list.list]
		.screen.list.title configure -text "Select $device associated Screen:"
		wm state .screen normal
	} else {
		set screenTags [ bindtags .list.list]
		.list.title configure -text "Select $device associated Screen:"
		wm state . normal
	}
    } else {
	updateCurrentScreenInfo
	startCalibration
    }

    if { $calibration_sequence_only == 0 } {
	disableButtons
    }
}

proc startCalibration {} {
    global device size numScreens tvID orig_device
    global calibResults calibX_org calibY_org calibX_botm calibY_botm
    global screenX_org screenY_org screenXBottom screenYBottom
    global screenWidth screenHeight calibration_sequence_only
    
    if { $numScreens($device) > 1 } {
	if { $calibration_sequence_only == 0 } {
	     bindtags .screen.list.list .
	} else {
	     bindtags .list.list .
	}
    }

    # convert underscores back to spaces for wacomxi.c
    regsub -all {_} $device " " orig_device

    if { $tvID == 1 } { # Nvidia Xinerama setup starts at (0,0) even when it is not in xorg.conf
	set calibX_org 0
	set calibY_org 0
	set calibX_botm $screenWidth
	set calibY_botm $screenHeight
    } else {
	set calibX_org $screenX_org
	set calibY_org $screenY_org
	set calibX_botm $screenXBottom
	set calibY_botm $screenYBottom
    }
    set y_coor [ expr $calibY_botm-$size*(abs($calibY_botm)/$calibY_botm) ]
    set x_coor [ expr $calibX_botm-$size*(abs($calibX_botm)/$calibX_botm) ]
    pad .topleft +$calibX_org+$calibY_org
    pad .bottomright +$x_coor+$y_coor
    update
    #
    # Start calib sequence
    catch {unset calibResults}
    exec xsetwacom set "$device" xydefault 0
    .topleft.m configure -background "#df94df"
    # hide the cursor
    .topleft.m configure -cursor "center_ptr #df94df #df94df"
    wacomxi::bindevent .topleft.m "$orig_device" <ButtonRelease> \
		{calibrationSequence 0 %0 %1}
    .bottomright.m configure -background "#505075"
    #hide the cursor
    .bottomright.m configure -cursor "watch #505075 #505075"
    helpWindow "Help Window " \
		"\n\nPlease click on the center of \n\
		the pink crosshair using $device \n\
		Please don't click on anything else \n\
		by $device before you finish"
}

proc helpWindow { tString mString } {
    global showHelp

    if { $showHelp }  {
	messageWindow $tString $mString
    }
}

proc messageWindow { tString mString } {
    toplevel .mWindow
    wm title .mWindow $tString
    wm transient .mWindow .
    text .mWindow.text -background gray -width 40 -height 10
    button .mWindow.dismiss -text "Dismiss" \
		-command "destroy .mWindow; set ok 1"
    .mWindow.text insert end $mString
    pack .mWindow.text .mWindow.dismiss

    tkwait variable ok 
}

proc updateScreenList {} {
    global currentScreen numScreens screenWidth screenHeight
    global device screenX_org screenY_org origin_x origin_y
    global calibration_sequence_only

    if { $numScreens($device) > 1 } {
	if { $calibration_sequence_only == 0 } {
	    set cScreen [ .screen.list.list get anchor ]
	} else {
	    set cScreen [ .list.list get anchor ]
	}

	for { set i 0 } { $i < $numScreens($device) } { incr i 1 } {
	    if { $cScreen == "Screen$i" } {
		set currentScreen $i
		set i $numScreens($device)
	    }
	}
	exec xsetwacom set "$device" Screen_No $currentScreen
    }
    updateCurrentScreenInfo
    set origin_x [ expr $screenX_org+$screenWidth/2-$screenWidth/4 ]
    set origin_y [ expr $screenY_org+$screenHeight/2-$screenHeight/4 ]
    wm geometry . =+$origin_x+$origin_y

    if { $calibration_sequence_only == 0 } {
	.screen.list.label configure -text $cScreen
	set o_x [ expr $origin_x+100 ]
	set o_y [ expr $origin_y+20 ]
	wm geometry .screen =+$o_x+$o_y
    } else {
	.list.label configure -text $cScreen
    }

    startCalibration
}

proc disableButtons {} {
    global bName numButton numStrips currentW currentb

    if { $currentb } {
	for { set i 1 } { $i <= [ expr ($numButton+$numStrips) ] } { incr i 1 } {
	    .allothers.f.$i configure -state disabled
	}
	.allothers.f.ok configure -state disable
	.allothers.f.default configure -state disable
	.allothers.f.cancel configure -state disable
    } else {
	if { $bName(pressure) } {
	    .panel.pressure configure -state disabled
	}
	if { $bName(calibrate) } {
	    .panel.calibrate configure -state disabled
	}
	if { $bName(button) } {
	    .panel.button configure -state disabled
	}
	if { $bName(mapping) } {
	    .panel.mapping configure -state disabled
	}
	if { $bName(advanced) } {
	    .panel.advanced configure -state disabled
	}
    }
}

# Retrieve the option numbered $i from the given device $dev.
# This comes from the global Option array, but this seems to be at least
# partially filled by hand??
proc getDeviceOptionProc { dev i } {
    global getOption getOptionDefault Option oldKeys modeToggleB
    global displayToggleB ignoreButton keystrokeB
    global numStrips numControls numButton spName

    for { set j 1 } { $j < [ expr $i+1 ] } { incr j 1 } {
	# not all properties exist on all devices, skip the missing ones
	if { [ catch { set value [ exec xsetwacom get "$dev" $Option($j) ] } ] } {
	    continue
	}

	# NumScreens is special
	if { ![ string compare -nocase -length 10 $Option($j) "NumScreen" ] } {
	    set getOption($dev,NumScreen) [ getNumScreen $dev ]

	# PressCurve is special, returns 4 values
	} elseif { ![ string compare -nocase -length 10 $Option($j) "PressCurve" ] } {
	    # Wacomcpl doesn't export the pressure curve as curve but merely
	    # as a setting between 1 and 7 that maps into a set of ranges.
	    # the default value for this is 4 (0 0 100 100).

	    # ignore non-existing properties
	    if { [ llength $value ] == 0 } {
		# 0 is an invalid value for pressure curve, we use it in the
		# GUI
		set getOption($dev,PressCurve) 0
		set getOptionDefault($dev,PressCurve) 0
		continue
	    }

	    set getOption($dev,PressCurve) 4
	    set getOptionDefault($dev,PressCurve) 4

	    # PressCurve returns a space-separated list of the 4 values.
            # linuxwacom's xsetwacom returned one value, shifted into a
            # 4-byte value like this: x0 | y0 | x1 | y1
            # now we just take x0 and y0
	    set p0 [ expr ( [ lindex $value 0 ] ) & 0xFF ]
	    set p1 [ expr ( [ lindex $value 1 ] ) & 0xFF ]
	    if { ($p1 > 5) && ($p1 < 35) } {
		set getOption($dev,PressCurve) 3
	    }
	    if { ($p1 >= 35) && ($p1 < 65) } {
		set getOption($dev,PressCurve) 2
	    }
	    if { ($p1 >= 65) && ($p1 < 95) } {
		set getOption($dev,PressCurve) 1
	    }
	    if { ($p0 > 5) && ($p0 < 35) } {
		set getOption($dev,PressCurve) 5
	    }
	    if { ($p0 >= 35) && ($p0 < 65) } {
		set getOption($dev,PressCurve) 6
	    }
	    if { ($p0 >= 65) && ($p0 < 95) } {
		set getOption($dev,PressCurve) 7
	    }
	} else {
	    set match 0
	    # are they button/wheel/ring/strip?
	    for { set k 1 } { $k <= $numControls } { incr k 1 } {
		if { ![string compare -nocase $Option($j) $spName($k)] } {
		    set match 1
		    break
		}
	    }
	    set getOption($dev,$Option($j)) $value
	    set getOptionDefault($dev,$Option($j)) $value
	    if { $match } {
		if { ($value > $ignoreButton) || ($value == 0) } {
		    if { ![string compare -nocase -length 4 CORE $value ]  ||
			 ![string compare -nocase -length 3 KEY $value ] ||
			 ![string compare -nocase -length 6 BUTTON $value ] } {
			set getOption($dev,$Option($j)) $keystrokeB
			set oldKeys($Option($j)) $value
		    }
		    if { ![string compare -nocase -length 10 MODETOGGLE $value ] } {
			set getOption($dev,$Option($j)) $modeToggleB
		    }
		    if { ![string compare -nocase -length 13 DISPLAYTOGGLE $value ] } {
			set getOption($dev,$Option($j)) $displayToggleB
		    }
		    if { $value == 0 } {
			set getOption($dev,$Option($j)) $ignoreButton
		    }
		}
	    }
	}
    }
}

proc CreateDevicePanel { type model } {
    global hasPad isLCD hasTouch hasGesture

    set type [ string tolower $type ]

    if { ![ string compare $type "pad" ] } {
	if { $hasPad($model) } {
	    createPanel 0 1 0 0
	}
    } elseif { ![ string compare $type "touch" ] } {
	if { $hasTouch($model) } {
	    if { $hasGesture($model) } {
		createPanel 1 1 0 1
	    } else {
		createPanel 1 0 0 1
	    }
	}
    } elseif { $isLCD($model) } { 
	if { ![ string compare $type "stylus" ] } {
	    createPanel 1 1 0 1
	} elseif { [ string compare $type "cursor" ] } {
	    createPanel 1 1 0 0
	}
    } elseif { [ string compare $type "cursor" ] } {
	createPanel 1 1 1 0
    } else {
	createPanel 0 1 1 0
    }
}

proc updateDevice {} {
    global device orig_device getDeviceModel

    set olddev $device
    set device [ .workingDev.list get anchor ]
    if { $device != $olddev && $olddev != ""} {
	# convert underscores back to spaces for wacomxi.c
   	regsub -all {_} $olddev " " orig_device

 	# Clear old state related to preceding device
	wacomxi::bindevent . $orig_device <ButtonPress> ""
	wacomxi::bindevent . $orig_device <ButtonRelease> ""
    }

    if { $device != ""} {
	#
	# Update the entry indicator
	#
	.workingDev.label configure -text $device
	set model $getDeviceModel($device,model)
	set type $getDeviceModel($device,type)
	destroy .panel
	CreateDevicePanel $type $model
    } else {
	#
	# Update the entry indicator
	#
	.workingDev.label configure -text $device
    }
}

proc createDeviceList {} {
    global getDeviceModel deviceList

    set infoString [exec xsetwacom list]
    set deviceList ""

    set index [ string first "\n" $infoString ]
    set dev [ string range $infoString 0 $index ]


    while { $dev != "" } {
	set type [ lindex $dev end ]
	set dev [ lrange $dev 0 end-1 ]

	if { [catch { exec xsetwacom get "$dev" TabletID } model] == 0 } {
	    set getDeviceModel($dev,type) $type
	    set getDeviceModel($dev,model) $model
	    set deviceList "$deviceList \"$dev\""
	    set index [ expr $index+1 ]
	}

	set infoString [ string range $infoString $index end ]
	set index [ string first "\n" $infoString ]
	if { $index == -1 } { set index [ string length $infoString ] }
	set dev [ string range $infoString 0 $index ]
    }

    foreach dev $deviceList {
	# initial related erasers for styli
	set getDeviceModel($dev,eraser) ""
    }

    foreach dev $deviceList {
	set type $getDeviceModel($dev,type)
	if { ![string compare -nocase $type "eraser"] } {
	    set model $getDeviceModel($dev,model)
	    foreach dev1 $deviceList {
		set type1 $getDeviceModel($dev1,type)
		set model1 $getDeviceModel($dev1,model)
		if { ( $model == $model1 ) &&
			 ![string compare -nocase $type1 "stylus"] } {
		    set getDeviceModel($dev1,eraser) $dev
		}
	    }
	}
    }
}

proc createDeviceListPanel {} {
    global deviceList

    frame .workingDev
    label .workingDev.title -text "Select the Device:"
    label .workingDev.label -background gray
    listbox .workingDev.list -width 40 -height 12 \
	    -yscrollcommand ".workingDev.sb set"
    scrollbar .workingDev.sb -width 10 \
	    -command ".workingDev.list yview"
    grid .workingDev.title -row 0 -column 0 -columnspan 8 -sticky we
    grid .workingDev.label -row 1 -column 0 -columnspan 8 -sticky we
    grid .workingDev.list -row 2 -column 0
    grid .workingDev.sb -row 2 -sticky nse

    createDeviceList
    foreach dev $deviceList {
	.workingDev.list insert end $dev
	createScreenList $dev
    }
    bind .workingDev.list <ButtonRelease-1> updateDevice
}

# NumScreens was an exported property by the wacom driver. Values were
# ScreenCount(3) for non-NVIDIA TwinView, and 2 for NV-TV. We can get
# that through other means.
# I don't know how to get the ScreenCount with Tk though, so we parse
# the xdpyinfo output. Note that this duplicates the linuxwacom bug - on
# RandR 1.2, screenInfo.numScreens is always 1 though there may be two
# phys. monitors.
proc getNumScreen { dev } {
    set num_screen [exec xdpyinfo | grep "number of screens" | sed -e "s/number of screens:\[ \]\*//"]

    if { $num_screen == 1 } {
	set twinview [ exec xsetwacom get "$dev" TwinView ]
	if { $twinview != "none" } {
	    set num_screen 2
	}
    }

    return $num_screen
}

proc createScreenList { dev } {
    global numScreens currentScreen getScreenInfo

    set num_screen [ getNumScreen $dev ]

    set numScreens($dev) $num_screen
    if { [ catch {
	for { set i 0 } {$i < $numScreens($dev)} { incr i 1 } {
	    set s1 [ exec xsetwacom get "$dev" SBottomX$i ]
	    set s2 [ exec xsetwacom get "$dev" SBottomY$i ]
	    set s3 [ exec xsetwacom get "$dev" STopX$i ]
	    set s4 [ exec xsetwacom get "$dev" STopY$i ]
	}
	set getScreenInfo($dev,Screen$i) "$s1 $s2 $s3 $s4"
    } msg ] } {
	# if the above fails, the driver only sees one screen but we
	# pretend to have 2 (NVIDIA TwinView). The above fails because
	# "propert offset doesn't exist" so we have to fake the actual
	# values by querying TVResolution0/1 that was hopefully magically
	# set by the user beforehand...
	set tvres [ exec xsetwacom get "$dev" "TVResolution" ]
	set s0x [ lindex $tvres 0 ]
	set s0y [ lindex $tvres 1 ]
	set s1x [ lindex $tvres 2 ]
	set s1y [ lindex $tvres 3 ]

	if { $s0x == 0 && $s0y == 0 && $s1x == 0 && $s1y == 0 } {
	    messageWindow "Error" "\n\n Device $dev configured \n\
			  for TwinView but option TVResolution \n\
			  is not set.\n\
			  Please set TVResolution and try again."
			  exit 1
	}

	# getScreenInfo for some reason stores as bottom x/y top x/y instead
	# of the other way round.  Usage of s() is top x/y bottom x/y though.
	for { set i 0 } { $i < 8 } { incr i 1 } {
	    set s($i) 0
	}

	set tv_config [ exec xsetwacom get "$dev" "TwinView" ]

	switch $tv_config {
	    "normal" { return }
	    "horizontal" {
		set s(2) $s0x
		set s(3) $s0y
		set s(4) [ expr $s0x ]
		set s(5) 0
		set s(6) [ expr $s0x + $s1x ]
		set s(7) $s1y
	    }
	    "leftof" {
		set s(6) $s0x
		set s(7) $s0y
		set s(0) [ expr $s0x ]
		set s(1) 0
		set s(2) [ expr $s0x + $s1x ]
		set s(3) $s1y
	    }
	    "vertical" {
		set s(2) $s0x
		set s(3) $s0y
		set s(4) 0
		set s(5) [ expr $s0y ]
		set s(6) [ expr $s0x ]
		set s(7) [ expr $s0y + $s1y ]
	    }
	    "belowof" {
		set s(6) $s0x
		set s(7) $s0y
		set s(0) 0
		set s(1) [ expr $s0y ]
		set s(2) [ expr $s0x ]
		set s(3) [ expr $s0y + $s1y ]
	    }
	}

	set getScreenInfo($dev,Screen0) "$s(2) $s(3) $s(0) $s(1)"
	set getScreenInfo($dev,Screen1) "$s(6) $s(7) $s(4) $s(5)"
    }
}

proc screenCancel {} {
    global calibration_sequence_only

    destroy .topleft .bottomright
    if { $calibration_sequence_only == 0 } {
	closeTabWindow
	destroy .screen
    } else {
	exit 0
    }
}

proc displayScreenList { dev } {
    global numScreens currentScreen calibration_sequence_only

    if {  $numScreens($dev) <= 1  } {
	return
    }

    if { $calibration_sequence_only == 0} {
	toplevel .screen
	wm title .screen "Screen List Window"
	wm transient .screen .
	wm geometry .screen =250x200
	wm state .screen withdraw
	button .screen.cancel -text "Close" -command screenCancel
	grid .screen.cancel -row 10
	frame .screen.list
	label .screen.list.title -text "Select the Screen:"
	label .screen.list.label -background gray
	listbox .screen.list.list -width 12 -height 5 -yscrollcommand ".screen.list.sb set"
	scrollbar .screen.list.sb -width 10 -command ".screen.list yview"
	grid .screen.list.title -row 2 -column 0 -columnspan 3 -sticky we
	grid .screen.list.label -row 3 -column 0 -columnspan 3 -sticky we
	grid .screen.list.list -row 4 -column 0
	grid .screen.list.sb -row 4 -column 1 -sticky nse
	for { set i 0 } { $i < $numScreens($dev) } { incr i } {
	    .screen.list.list insert end "Screen$i"
        }
	bind .screen.list.list <ButtonRelease-1> updateScreenList
	pack .screen.list .screen.cancel
    } else {
	button .cancel -text "Close" -command screenCancel
	grid .cancel -row 10
	frame .list
	label .list.title -text "Select the Screen:"
	label .list.label -background gray
	listbox .list.list -width 12 -height 5 -yscrollcommand ".list.sb set"
	scrollbar .list.sb -width 10 -command ".list yview"
	grid .list.title -row 2 -column 0 -columnspan 3 -sticky we
	grid .list.label -row 3 -column 0 -columnspan 3 -sticky we
	grid .list.list -row 4 -column 0
	grid .list.sb -row 4 -column 1 -sticky nse
	for { set i 0 } { $i < $numScreens($dev) } { incr i } {
	    .list.list insert end "Screen$i"
	}
	bind .list.list <ButtonRelease-1> updateScreenList
	pack .list .cancel
    }
}

proc updateButton {} {
    global device getDeviceModel sm getOption spName isLCD
    global dm currentW oldKeys numButton cKeys numStrips startS
    global modeToggleB ignoreButton keystrokeB
    global displayToggleB

    set type $getDeviceModel($device,type)
    set model $getDeviceModel($device,model)

    for { set i 1 } { $i <= [ expr ($numButton + $numStrips)] } { incr i 1 } {
	set k $i
	if { $i > $numButton } {
	    set k [ expr ($startS-1+$i) ]
	}
	switch [ $currentW.f.$i cget -text ] {
	    "Left"
		{ set j 1 
		  set v "1" }
	    "Middle"
		{ set j 2 
		 set v "2" }
	    "Right"
		{ set j 3 
		 set v "3" }
	    "Fourth"
		{ set j 4 
		 set v "4" }
	    "Fifth"
		{ set j 5 
		 set v "5" }
	    "Mode Toggle"
		{ set j $modeToggleB
		 set v "ModeToggle 1" }
	    "Display Toggle"
		{ set j $displayToggleB
		 set v "DisplayToggle 1" }
	    "KeyStroke"
		{ set j $keystrokeB 
		 if { $cKeys($spName($k)) == "" } {
		     set v $oldKeys($spName($k))
		 } else {
		    set v $cKeys($spName($k)) 
		    set oldKeys($spName($k)) $cKeys($spName($k))
		 } }
	    "Ignore"
		{ set j $ignoreButton
		 set v "0" }
	}
	set getOption($device,$spName($k)) $j
	updateWacomrc $device $spName($k) $v

	# reset key strokes 
	if { $j != $keystrokeB } {
	    set $oldKeys($spName($k)) ""
	    set cKeys($spName($k)) ""
	}
    }

    if { !$isLCD($model) && [string compare -nocase $type "pad"] } { 
	set mode [ $currentW.f.mode cget -text ]
	updateWacomrc $device mode $mode
	if { $mode == $dm(1) } {
	    set getOption($device,Mode) 0
	} else {
	    set getOption($device,Mode) 1
	}
    }

    if { ![ string compare -nocase $type "stylus" ] } {
	    set smode [ $currentW.f.smode cget -text ]
	    if { $smode == $sm(1) } {
		updateWacomrc $device TPCButton off
		set getOption($device,TPCButton) 0
	    } else {
		updateWacomrc $device TPCButton on
		set getOption($device,TPCButton) 1
	    }
    }
    closeSubWindow
}

proc defaultButton {} {
    global db db1 db2 db3 db4 db5 db6 db7 db8 db9 db10
    global db11 db12 db13 db14 isLCD
    global dm sm dmv smv numButton startS numStrips spName
    global device getOptionDefault getDeviceModel
    global ignoreButton keystrokeB modeToggleB displayToggleB

    for { set i 1 } { $i <= $numButton } { incr i 1 } {
	set btn $getOptionDefault($device,$spName($i))
	if { $btn == 0 } { set btn $ignoreButton }
	if { ![string compare -nocase -length 4 CORE $btn ]  ||
		    ![string compare -nocase -length 3 KEY $btn ] ||
		    ![string compare -nocase -length 6 BUTTON $btn ] } {
	    set btn $keystrokeB
	}
	if { ![string compare -nocase -length 10 MODETOGGLE $btn ] } {
	    set btn $modeToggleB
	}
	if { ![string compare -nocase -length 13 DISPLAYTOGGLE $btn ] } {
	    set btn $displayToggleB
	}
	set db$i $db($btn)
    }

    if { $startS } {
	for { set i $startS } { $i <= [ expr ($startS+$numStrips-1) ] } { incr i 1 } {
	    set k [ expr ($numButton+$i-$startS+1) ]
	    set db$k $db($getOptionDefault($device,$spName($k)))
	}
    }

    set model $getDeviceModel($device,model)
    set type $getDeviceModel($device,type)

    if { !$isLCD($model) && [ string compare -nocase $type "pad" ] } { 
	# dmv is the return value of the tk_optionMenu that displays the
	# mode options. yes, indeed. whoop. dee. doo.
	set mode $getOptionDefault($device,Mode)
	if { $mode == "Absolute" } { set mode 1 }
	if { $mode == "Relative" } { set mode 0 }
	set dmv $dm([ expr $mode + 1 ])
    }

    if { ![ string compare -nocase $type "stylus" ] } {
	set tpcbutton $getOptionDefault($device,TPCButton)
	if { $tpcbutton == "on" } { set tpcbutton 1 }
	if { $tpcbutton == "off" } { set tpcbutton 0 }
	set smv $sm([ expr $tpcbutton + 1 ])
    }
}

proc getNumButton { type } {
    global device Option numButton getDeviceModel numPadButtons
    
    set Option(1) "Button1"
    set t 1
    if { [ string compare -nocase $type "eraser" ] } {
	set Option(2) "Button2"
	set Option(3) "Button3"
	set t [ expr ($t+2) ]
	if { [ string compare -nocase $type "stylus" ] } {
	    set Option(4) "Button4"
	    set Option(5) "Button5"
	    set t [ expr ($t+2) ]
	}
	if { ![ string compare -nocase $type "pad" ] } {
	    set model $getDeviceModel($device,model)
	    set t $numPadButtons($model)
	    for { set i 6 } { $i <= $t } { incr i 1 } {
		set Option($i) "Button$i"
	    }
	}
    }
    set numButton $t
}

proc setspName {} {
    global numButton spName startS device Option cKeys oldKeys
    global numPadRelW numPadStrips numPadRings getDeviceModel
    global numControls numStrips getOption

    for { set i 1 } { $i <= $numButton } { incr i 1 } {
	set spName($i) Button$i
    }

    set spName([expr ($numButton + 1) ]) "RelWUp"
    set spName([expr ($numButton + 2) ]) "RelWDn"
    set spName([expr ($numButton + 3) ]) "AbsWUp"
    set spName([expr ($numButton + 4) ]) "AbsWDn"
    set spName([expr ($numButton + 5) ]) "StripLUp"
    set spName([expr ($numButton + 6) ]) "StripLDn"
    set spName([expr ($numButton + 7) ]) "StripRUp"
    set spName([expr ($numButton + 8) ]) "StripRDn"

    set numControls [expr ($numButton+8)]
    for { set i 1 } { $i <= $numControls } { incr i 1 } {
	set Option($i) $spName($i)
    }

    # initial keys before we call getDeviceOptionProc
    for { set i 1 } { $i <= $numControls } { incr i 1 } {
	set oldKeys($spName($i)) ""
	set cKeys($spName($i)) ""
    }

    getDeviceOptionProc $device $numControls

    set s1 5
    set s2 8
    set model $getDeviceModel($device,model)
    if { $numPadRelW($model) } { # G4
	set s1 1
	set s2 2
    } elseif { $numPadStrips($model) == 1 } { # I3 4x5
	set s1 5
	set s2 6
    } elseif { $numPadRings($model) } { # Bamboo
	set s1 3
	set s2 4
    }
    set startS $s1
    set numStrips [expr ($s2 - $s1) + 1]

}

proc initialButton {} {
    global device Option numButton getDeviceModel 
    global spName numStrips

    set type $getDeviceModel($device,type)
    set t $numButton

    if { [ string compare -nocase $type "pad" ] } {
	set t [expr ($t+1)]
	set Option($t) "Mode"
	set t [expr ($t+1)]
	set Option($t) "TPCButton"
    }
    getDeviceOptionProc $device $t

    if { [ string compare -nocase $type "pad" ] } {
    	displayMode
    }

    # initial controls
    setspName

    for { set i 1 } { $i <= $numButton } { incr i 1 } {
	addMenu $i
    }

    # then touch strip
    if { ![ string compare -nocase $type "pad" ] } {
	expressKeys
    } else {
	set numStrips 0
    }
}

proc displayMode {} {
    global device getDeviceModel sm smv
    global dmv dm getOption currentW isLCD

    set model $getDeviceModel($device,model)
    set type $getDeviceModel($device,type)

    if { !$isLCD($model) && [ string compare -nocase $type "pad" ]  } {
	set mode $getOption($device,Mode)
	if { $mode == "Absolute" } { set mode 1 }
	if { $mode == "Relative" } { set mode 0 }

	set dmv $dm([ expr $mode+1 ])
	tk_optionMenu $currentW.f.mode dmv $dm(1) $dm(2)
	label $currentW.f.modeL -text "Positioning Mode: "
	grid $currentW.f.mode -row 4 -column 1
	grid $currentW.f.modeL -row 4 -column 0
    }

    if { ![ string compare -nocase $type "stylus" ] } {
	set tpcbutton $getOption($device,TPCButton)
	if { $tpcbutton == "on" } { set tpcbutton 1 }
	if { $tpcbutton == "off" } { set tpcbutton 0 }
	set smv $sm([ expr $tpcbutton + 1 ])
	tk_optionMenu $currentW.f.smode smv $sm(1) $sm(2)
	label $currentW.f.smodeL -text "Side Switch Mode: "
	grid $currentW.f.smode -row 4 -column 4 
	grid $currentW.f.smodeL -row 4 -column 3
    }
}

# Sets up a subwindow.
# okF is called when pressing the OK button
# deF is called when pressing the Default (now Reset) button
# initial is called on the first run to fill in the GUI.
proc displaySubWindow { okF deF initial winID cbutton kcurrent} {
    global currentb currentW wName currentk

    set currentb $cbutton
    set currentk $kcurrent
    if { $cbutton } {
	set currentW .keystrokes
   } else {
	set currentW .allothers
    }
    toplevel $currentW
    wm geometry $currentW =650x425
    wm title $currentW $wName($winID)
    wm transient $currentW .
    wm state $currentW normal
    frame $currentW.f
    button $currentW.f.ok -text "Save" -command $okF
    button $currentW.f.cancel -text "Cancel" -command closeSubWindow

    set columnN 0
    if { $deF != 0 } {
	button $currentW.f.default -text "Reset" -command $deF
	grid $currentW.f.default -row 8 -column 0 -columnspan 3 -padx 10 -pady 10
	#
	# Suppress tags on listboxes to prevent changing the
	# device and disable other controls
	bindtags .workingDev.list .
	set columnN 4
    }

    disableButtons
    $initial

    grid $currentW.f -row 0 -column 0 -sticky nw
    grid $currentW.f.ok -row 8 -column [expr ($columnN + 4)] -columnspan 3 -padx 10 -pady 10
    grid $currentW.f.cancel -row 8 -column $columnN -columnspan 3 -padx 10 -pady 10
}

proc closeSubWindow {} {
    global currentW
    destroy $currentW
    closeTabWindow
}

proc closeTabWindow {} {
    global bName workingTags currentW numButton 
    global currentb cKeys spName numStrips 

    if { $bName(keystroke) } {
	set bName(keystroke) 0
	set currentW .allothers
	for { set i 1 } { $i <= [ expr ($numButton+$numStrips) ] } { incr i 1 } {
	    $currentW.f.$i configure -state normal
	}
	$currentW.f.default configure -state normal
	$currentW.f.ok configure -state normal
	$currentW.f.cancel configure -state normal
	set currentb 0
    } else {
	if { $bName(pressure) } {
	    .panel.pressure configure -state normal
	}
	if { $bName(calibrate) } {
	    .panel.calibrate configure -state normal
	}
	if { $bName(button) } {
	    .panel.button configure -state normal
	}
	if { $bName(mapping) } {
	    .panel.mapping configure -state normal
	}
	if { $bName(advanced) } {
	    .panel.advanced configure -state normal
	}
	for { set i 1 } { $i <= [ expr ($numButton+$numStrips) ] } { incr i 1 } {
	    set cKeys($spName($i)) ""
	}
	bindtags .workingDev.list $workingTags
    }
}

proc expressKeys { } {
    global db db1 db2 db3 db4 db5 db6 db7 db8 db9 db10
    global db11 db12 db13 db14 startS 
    global currentW device numButton spName numStrips
    global getDeviceModel cKeys oldKeys getOption
    global displayToggleB ignoreButton keystrokeB
    global numPadRings numPadStrips numPadAbsW numPadRelW

    set name(1) "Wheel Up"
    set name(2) "Wheel Down"
    set name(3) "Ring Anticlockwise"
    set name(4) "Ring Clockwise"
    set name(5) "Left Strip Up"
    set name(6) "Left Strip Down"
    set name(7) "Right Strip Up"
    set name(8) "Right Strip Down"

    set s2 [expr ($startS + $numStrips) - 1]
    for { set i $startS } { $i <= $s2 } { incr i 1 } {
	set cur [ expr ($numButton + $i - $startS + 1) ]
	set curOption [ expr ($numButton + $i) ]
	set opt $getOption($device,$spName($curOption))
	if { $opt == "" } { set opt $ignoreButton }

	set db$cur $db($opt) 
	#reset keys
	if { $opt != $keystrokeB } {
	    set cKeys($spName($curOption)) ""
	    set oldKeys($spName($curOption)) ""
	}
	set bmenu [ tk_optionMenu $currentW.f.$cur db$cur $db(1) $db(2) \
	    $db(3) $db(4) $db(5) $db($ignoreButton) ]
	$bmenu insert 7 radiobutton -label "$db($keystrokeB)" \
	    -variable menvar -command "displaySubWindow \
	    updateKeys 0 initialKeys 5 $cur $curOption"

	label $currentW.f.name$cur -text "$name($i): "
	set t2 [expr ($numButton+1)/2+1]
	set t1 0
	if { [expr ($s2 - $startS) > 1] } {
	    if { $i == [expr ($startS+1)] || \
		    $i == [expr ($startS+3)] } { 
		set t2 [expr ($numButton+1)/2+2]
	    }
	    if { [expr ($i - $startS) > 1] } {
		set t1 2
	    }
	} else {
	    if { [expr ($i - $startS) > 0] } {
		set t1 2
	    }
	}
	grid $currentW.f.$cur -row $t2 -column [expr $t1+1] 
	grid $currentW.f.name$cur -row $t2 -column $t1
    }
}

proc addMenu { curb } {
    global db db1 db2 db3 db4 db5 db6 db7 db8 db9 db10 
    global db11 db12 db13 db14 currentW device spName
    global getOption getDeviceModel cKeys oldKeys 
    global keystrokeB
    global displayToggleB ignoreButton modeToggleB

    set model $getDeviceModel($device,model)
    set type $getDeviceModel($device,type)

    set opt $getOption($device,$spName($curb))
    if { $opt == "" } { set opt $curb }
    set db$curb $db($opt) 
    #reset keys
    if { $opt != $keystrokeB } {
	set cKeys($spName($curb)) ""
	set oldKeys($spName($curb)) ""
    }

    if { [string compare -nocase $type "pad"] } {
	set bmenu [ tk_optionMenu $currentW.f.$curb db$curb $db(1) $db(2) \
		$db(3) $db(4) $db(5) $db($modeToggleB) \
		$db($displayToggleB) $db($ignoreButton) ]
	set i 9
    } else {
	set bmenu [ tk_optionMenu $currentW.f.$curb db$curb $db(1) $db(2) \
		$db(3) $db(4) $db(5) $db($displayToggleB) \
		$db($ignoreButton) ]
	set i 8
    }

    set i [expr ($i+1)]
    $bmenu insert $i radiobutton -label "$db($keystrokeB)" \
	-variable menvar -command "displaySubWindow \
	updateKeys 0 initialKeys 5 $curb $curb"

    label $currentW.f.name$curb -text "Button $curb: "
    set t2 [expr ($curb-1)/2]
    if { [expr ($curb/2)] == [expr ($curb+1)/2] } {
	set t1 2
    } else {
	set t1 0
    }

    grid $currentW.f.$curb -row $t2 -column [expr ($t1+1)]
    grid $currentW.f.name$curb -row $t2 -column $t1
}

# sets up the mapping window to change the top/bottom x/y parameters
proc initialT {} {
    global device getDeviceModel currentW
    global getOption getOptionDefault Option

    set Option(1) "TopX"
    set Option(2) "TopY"
    set Option(3) "BottomX"
    set Option(4) "BottomY"
    set Option(5) "Mode"

    getDeviceOptionProc $device 7

    set mode $getOption($device,Mode)
    frame $currentW.f.group -bd 10 -bg beige -width 150 -height 150

    if { $mode == 1 || $mode == "Absolute" } {
	label $currentW.f.groupL -text "Mapping: "
	for { set i 1 } { $i < 5 } { incr i 1 } {
	    addMapScale $i
	}
	grid $currentW.f.group -row 0 -column 5
	grid $currentW.f.groupL -row 0 -column 2
    }

    # FIXME: speed levels were removed, should be controlled by the X server
    # properties.

}

proc addMapScale { t } {
    global getOption Option getOptionDefault device currentW

    label $currentW.f.group.l$t -text "$Option($t): "
    grid $currentW.f.group.l$t -row [ expr $t-1 ] -column 6
    set j $t
    if { $j < 3 } { set j [expr $j+2] }
    scale $currentW.f.group.scale$t -orient horizontal -length 200 \
		-from -200 -to [expr $getOptionDefault($device,$Option($j))+200]
    grid $currentW.f.group.scale$t -row [ expr $t-1 ] -column 8
    $currentW.f.group.scale$t set $getOption($device,$Option($t))
}

proc updateT {} {
    global getOption getOptionDefault Option device currentW

    set mode $getOption($device,Mode)
    if { $mode == 1 || $mode == "Absolute" } {
	for { set i 1 } { $i < 5 } { incr i 1 } {
	    set value [ $currentW.f.group.scale$i get ]
	    updateWacomrc $device $Option($i) $value
	    set getOption($device,$Option($i)) $value
	}
    }

    closeSubWindow
}

proc defaultT {} {
    global getOption getOptionDefault Option device currentW

    set mode $getOption($device,Mode)
    if { $mode == 1 || $mode == "Absolute" } {
	for { set i 1 } { $i < 5 } { incr i 1 } {
	    $currentW.f.group.scale$i set $getOptionDefault($device,$Option($i))
	}
    }
}

proc initialTip {} {
    global device getDeviceModel getOption Option currentW

    set Option(1) "PressCurve"
    set Option(2) "ClickForce"
    set Option(3) "RawSample"
    set Option(4) "Suppress"
    getDeviceOptionProc $device 4

    frame $currentW.f.group -bd 10 -bg beige -width 150 -height 150

    # 0 is a special value, see getDeviceOptionProc.
    # Only display Tip sensitivity for those that have PressCurve
    if { $getOption($device,PressCurve) != 0 } {
	label $currentW.f.group.groupl1 -text "Tip Sensitivity: "
	grid $currentW.f.group.groupl1 -row 0 -column 0
	scale $currentW.f.group.scale1 -orient horizontal -length 100 \
	-from 1 -to 7
	grid $currentW.f.group.scale1 -row 0 -column 8
	set curve $getOption($device,PressCurve)

	$currentW.f.group.scale1 set $curve
	label $currentW.f.group.l2 -text "Soft"
	grid $currentW.f.group.l2 -row 1 -column 7
	label $currentW.f.group.l3 -text "Firm"
	grid $currentW.f.group.l3 -row 1 -column 9
    }

    label $currentW.f.group.groupl2 -text "Click Force: "
    grid $currentW.f.group.groupl2 -row 3 -column 0
    label $currentW.f.group.l4 -text "Low"
    grid $currentW.f.group.l4 -row 4 -column 7
    label $currentW.f.group.l5 -text "High"
    grid $currentW.f.group.l5 -row 4 -column 9
    scale $currentW.f.group.scale2 -orient horizontal -length 100 \
		-from 1 -to 21
    grid $currentW.f.group.scale2 -row 3 -column 8
    $currentW.f.group.scale2 set $getOption($device,ClickForce)

    label $currentW.f.group.groupl3 -text "Smoothness: "
    grid $currentW.f.group.groupl3 -row 5 -column 0
    label $currentW.f.group.l6 -text "Low"
    grid $currentW.f.group.l6 -row 6 -column 7
    label $currentW.f.group.l7 -text "High"
    grid $currentW.f.group.l7 -row 6 -column 9
    scale $currentW.f.group.scale3 -orient horizontal -length 100 \
               -from 1 -to 20
    grid $currentW.f.group.scale3 -row 5 -column 8
    $currentW.f.group.scale3 set $getOption($device,RawSample)

    label $currentW.f.group.groupl4 -text "Suppress Points: "
    grid $currentW.f.group.groupl4 -row 7 -column 0
    label $currentW.f.group.l8 -text "Low"
    grid $currentW.f.group.l8 -row 8 -column 7
    label $currentW.f.group.l9 -text "High"
    grid $currentW.f.group.l9 -row 8 -column 9
    scale $currentW.f.group.scale4 -orient horizontal -length 100 \
               -from 1 -to 20
    grid $currentW.f.group.scale4 -row 7 -column 8
    $currentW.f.group.scale4 set $getOption($device,Suppress)

    grid $currentW.f.group -row 0 -column 5 
}

proc updateTip {} {
    global device currentW getOption

    # 0 is a special value for PressCurve, see getDeviceOptionProc
    if { $getOption($device,PressCurve) != 0 } {
	    switch [ $currentW.f.group.scale1 get ] {
		1
		{ set curve "0 75 25 100" }
		2
		{ set curve "0 50 50 100" }
		3
		{ set curve "0 25 75 100" }
		4
		{ set curve "0 0 100 100" }
		5
		{ set curve "25 0 100 75" }
		6
		{ set curve "50 0 100 50" }
		7
		{ set curve "75 0 100 25" }
	}
	updateWacomrc $device PressCurve $curve
	set getOption($device,PressCurve) $curve
    }
    updateWacomrc $device ClickForce [ $currentW.f.group.scale2 get ]
    set getOption($device,ClickForce) [ $currentW.f.group.scale2 get ]
    updateWacomrc $device RawSample [ $currentW.f.group.scale3 get ]
    set getOption($device,RawSample) [ $currentW.f.group.scale3 get ]
    updateWacomrc $device Suppress [ $currentW.f.group.scale4 get ]
    set getOption($device,Suppress) [ $currentW.f.group.scale4 get ]

    closeSubWindow
}

proc defaultTip {} {
    global currentW

    $currentW.f.group.scale1 set 4
    $currentW.f.group.scale2 set 6
}

proc initialSet {} {
    global device getDeviceModel getOption Option
    global tvd tvID snd currentW

    # used by both TwinView and non TwinView multimonitor setup
    label $currentW.f.sns -text "Display Mapping: "

    set Option(1) "NumScreen"
    set Option(2) "TwinView"
    set Option(3) "Screen_no"
    getDeviceOptionProc $device 3

    set numS  $getOption($device,NumScreen)
    set tvID $getOption($device,TwinView)
    set snd $getOption($device,Screen_no)

    if { $snd == -1 || $snd == 255} {
	set snd "Desktop" 
    } else {
	set snd "Screen$snd"
    }

    if { $numS == 1 } {
	set sn0 "Desktop" 
	set sn1 "Screen0"
	set sn2 "Screen1"
	tk_optionMenu $currentW.f.snsmenu snd $sn0 $sn1 $sn2
    } else {
	set smenu [ tk_optionMenu $currentW.f.snsmenu snd "Desktop" ]
	pack $currentW.f.snsmenu -side left
	for { set i 0 } { $i < $numS } { incr i } {
	    $smenu insert $i radiobutton -label "Screen$i" \
		-variable smenvar -command \
		{ global smenvar; set snd $smenvar }
	}
    }

    grid $currentW.f.sns -row 5 -column 0 
    grid $currentW.f.snsmenu -row 5 -column 1
    if { $numS > 1 } {
	$currentW.f.snsmenu configure -state normal
    } else {
	$currentW.f.snsmenu configure -state disable 
    }

    label $currentW.f.tv -text "TwinView Setup: "
    set tv(0) "None"
    set tv(1) "Vertical"
    set tv(2) "Horizontal"
    set tv(3) "AboveOf"
    set tv(4) "LeftOf"

    for { set i 0 } { $i < 5 } { incr i 1 } {
	if { ![ string compare -nocase $tvID $tv($i) ] } {
	    set tvd $tv($i)
	    break
	}
    }

    tk_optionMenu $currentW.f.tvmenu tvd $tv(0) $tv(1) $tv(2) $tv(3) $tv(4)
    grid $currentW.f.tv -row 4 -column 0 
    grid $currentW.f.tvmenu -row 4 -column 1

    $currentW.f.tvmenu configure -state normal
    label $currentW.f.message1 -text "\nThe \"TwinView Setup\" option "
    label $currentW.f.message2 -text "is only for Nvidia graphic card."
    label $currentW.f.message3 -text "If your graphic card is not from Nvidia, "
    label $currentW.f.message4 -text "please leave the option as \"None\".\n"
 
    grid $currentW.f.message1 -row 0 -column 1
    grid $currentW.f.message2 -row 1 -column 1
    grid $currentW.f.message3 -row 2 -column 1
    grid $currentW.f.message4 -row 3 -column 1
}

proc guessTVResolution { device tv} {
    # xdpyinfo output is e.g. #head0: 1280x1024 @ 0,0
    # extract the resolution into a "1280 1024 0 0" string
    set regex { s/.* \([0-9]\+\)x\([0-9]\+\) @ \([0-9]\+\),\([0-9]\+\).*/\1 \2 \3 \4/ }

    set head0 [ exec xdpyinfo -ext XINERAMA | grep "head #0" | sed -e $regex ]
    set head1 [ exec xdpyinfo -ext XINERAMA | grep "head #1" | sed -e $regex ]

    catch {
	set tvres0 [split $head0 " "]
	set tvres1 [split $head1 " "]

	set w1 [ lindex $tvres0 0 ]
	set h1 [ lindex $tvres0 1 ]
	set w2 [ lindex $tvres1 0 ]
	set h2 [ lindex $tvres1 1 ]

	set off1 [ lindex $tvres0 2 ]
	set off2 [ lindex $tvres0 3 ]

	if { $off1 != 0 || $off2 != 0 } {
	    # first screen has non-zero offset, swap around
	    set tmp $w1
	    set w1 $w2
	    set w2 $tmp

	    set tmp $h1
	    set h1 $h2
	    set h2 $tmp
	}

	exec xsetwacom set "$device" TVResolution "$w1 $h1 $w2 $h2"
	updateWacomrc $device TVResolution "$w1 $h1 $w2 $h2"
    }
}

proc updateSet {} {
    global device currentW tvd snd tvID

    # See procCreateScreenList for comment to next line
    set numS [ getNumScreen $device ]

    if { $numS > 1 } {
	set sn [ $currentW.f.snsmenu cget -text ]
	if { ![ string compare $sn "Desktop" ] } {
	    set sn -1
	} else {
	    for { set i 0 } { $i < $numS } { incr i 1 } {
		if { $sn == "Screen$i" } {
		    set sn $i
		}
	    }
	}
	updateWacomrc $device Screen_No $sn
	set getOption($device,Screen_No) $sn
    }

    set tv [ $currentW.f.tvmenu cget -text ]
    if { ![ string compare $tv "None" ] } {
	set tv "none"
    }
    if { ![ string compare $tv "Horizontal" ] } {
	set tv "horizontal"
    }
    if { ![ string compare $tv "Vertical" ] } {
	set tv "vertical"
    }
    if { ![ string compare $tv "LeftOf" ] } {
	set tv "leftof"
    }
    if { ![ string compare $tv "AboveOf" ] } {
	set tv "aboveof"
    }

    if { $tv != $tvID } {
	updateWacomrc $device TwinView $tv
	set getOption($device,TwinView) $tv
	if { $tv != "none" } {
	    guessTVResolution $device $tv
	}
    }

    closeSubWindow
}

proc defaultSet {} {
    global tvd snd

    set tvd "None"
    set snd "Desktop"
}

proc initialKeys {} {
    global device getDeviceModel getOption cKeys spName 
    global Option oldKeys currentW bName currentb currentk

    frame $currentW.f.panelt
    set bName(keystroke) 1
    text $currentW.f.panelt.input -width 40 -height 10 \
	-yscrollcommand "$currentW.f.panelt.srl_y set" 

    if { $cKeys($spName($currentk)) != "" } {
	$currentW.f.panelt.input insert end $cKeys($spName($currentk))
    } else {
	if { $oldKeys($spName($currentk)) != "" } {
	    $currentW.f.panelt.input insert end $oldKeys($spName($currentk))
	    set cKeys($spName($currentk)) $oldKeys($spName($currentk))
	}
    }
    scrollbar $currentW.f.panelt.srl_y -width 10 -command \
	"$currentW.f.panelt.input yview"
    label $currentW.f.panelt.title1 -text "Entered keystrokes (type the keys if they are not in the lists):"
    grid $currentW.f.panelt.title1 -row 0 -column 0 -sticky we
    grid $currentW.f.panelt.input -row 1 -column 0 -sticky we
    grid $currentW.f.panelt.srl_y -row 1 -sticky nse

    frame $currentW.f.panel
    set mod(1) "SHIFT"
    set mod(2) "CTRL"
    set mod(3) "ALT"
    set mod(4) "META"
    set mod(5) "SUPER"
    set mod(6) "HYPER"
    set mmenu [ tk_optionMenu $currentW.f.panel.modmenu mod0 "Modifiers" ]
    pack $currentW.f.panel.modmenu -side left
    label $currentW.f.panel.title2 -text "Select special keys below:"
    grid $currentW.f.panel.title2 -row 0 -column 2 
    grid $currentW.f.panel.modmenu -row 1 -column 2 
    $mmenu delete 0
    for { set i 1 } { $i <= 6 } { incr i 1 } {
	$mmenu insert $i radiobutton -label $mod($i) \
	-variable menvar -command \
        { $currentW.f.panelt.input insert end " "; \
	global menvar; \
	$currentW.f.panelt.input insert end $menvar; \
	$currentW.f.panelt.input insert end " " }
    }

    set fk(1) "F1"
    set fk(2) "F2"
    set fk(3) "F3"
    set fk(4) "F4"
    set fk(5) "F5"
    set fk(6) "F6"
    set fk(7) "F7"
    set fk(8) "F8"
    set fk(9) "F9"
    set fk(10) "F10"
    set fk(11) "F11"
    set fk(12) "F12"
    set fmenu [ tk_optionMenu $currentW.f.panel.fmenu fk0 "Function Keys" ]
    pack $currentW.f.panel.fmenu -side left
    grid $currentW.f.panel.fmenu -row 2 -column 2
    $fmenu delete 0
    for { set i 1 } { $i <= 12 } { incr i 1 } {
	$fmenu insert $i radiobutton -label $fk($i) \
	-variable fmenvar -command \
        { $currentW.f.panelt.input insert end " "; \
	global fmenvar; \
	$currentW.f.panelt.input insert end $fmenvar; \
	$currentW.f.panelt.input insert end " " }
    }

    set fs(1) "quotedbl"
    set fs(2) "Pause"
    set fs(3) "ScrollLock"
    set fs(4) "SysReq"
    set fs(5) "End"
    set fs(6) "Insert"
    set fs(7) "Delete"
    set fs(8) "Enter"
    set fs(9) "Home"
    set fs(10) "backslash"
    set fs(11) "break"
    set fs(12) "print"
    set fsmenu [ tk_optionMenu $currentW.f.panel.fsmenu fs0 "Special Keys 1" ]
    pack $currentW.f.panel.fsmenu -side left
    grid $currentW.f.panel.fsmenu -row 3 -column 2 
    $fsmenu delete 0
    for { set i 1 } { $i <= 12 } { incr i 1 } {
	$fsmenu insert $i radiobutton -label $fs($i) \
	-variable fsmenvar -command \
        { $currentW.f.panelt.input insert end " "; \
	global fsmenvar; \
	$currentW.f.panelt.input insert end $fsmenvar; \
	$currentW.f.panelt.input insert end " " }
    }

    set sk(1) "Esc"
    set sk(2) "Tab"
    set sk(3) "CapsLock"
    set sk(4) "PageUp"
    set sk(5) "PageDown"
    set sk(6) "Left"
    set sk(7) "Up"
    set sk(8) "Down"
    set sk(9) "Right"
    set sk(10) "space"
    set sk(11) "NumLock"
    set sk(12) "BackSpace"
    set skmenu [ tk_optionMenu $currentW.f.panel.skmenu sk0 "Special Keys 2" ]
    pack $currentW.f.panel.skmenu -side left
    grid $currentW.f.panel.skmenu -row 4 -column 2 
    $skmenu delete 0
    for { set i 1 } { $i <= 12 } { incr i 1 } {
	$skmenu insert $i radiobutton -label $sk($i) \
	-variable skmenvar -command \
        { $currentW.f.panelt.input insert end " "; \
	global skmenvar; \
	$currentW.f.panelt.input insert end $skmenvar; \
	$currentW.f.panelt.input insert end " " }
    }

    set kp(1) "PgUp"
    set kp(2) "PgDn"
    set kp(3) "KPLeft"
    set kp(4) "KPUp"
    set kp(5) "KPDown"
    set kp(6) "KPRight"
    set kp(7) "Plus"
    set kp(8) "Minus"
    set kp(9) "Divide"
    set kp(10) "Multiply"
    set kp(11) "KPEnd"
    set kp(12) "Ins"
    set kp(13) "Del"
    set kp(14) "KPEnter"
    set kp(15) "KPHome"
    set kpmenu [ tk_optionMenu $currentW.f.panel.kpmenu kp0 "KeyPad Keys" ]
    pack $currentW.f.panel.kpmenu -side left
    grid $currentW.f.panel.kpmenu -row 5 -column 2 
    $kpmenu delete 0
    for { set i 1 } { $i <= 15 } { incr i 1 } {
	$kpmenu insert $i radiobutton -label $kp($i) \
	-variable kpmenvar -command \
        { $currentW.f.panelt.input insert end " "; \
	global kpmenvar; \
	$currentW.f.panelt.input insert end $kpmenvar; \
	$currentW.f.panelt.input insert end " " }
    }

    grid $currentW.f.panelt -row 0 -column 0
    grid $currentW.f.panel -row 0 -column 2 
}

proc updateKeys {} {
    global device oldKeys currentb cKeys spName currentW
    global db db1 db2 db3 db4 db5 db6 db7 db8 db9 db10
    global db11 db12 db13 db14 currentk
    global ignoreButton keystrokeB

    set keys [ $currentW.f.panelt.input get 1.0 end ]

    # remove newline characters 
    set index [ string last "\n" $keys ]
    while { $index != -1 } {
	set keys [ string replace $keys $index [expr ($index+1) ] ]
	set index [ string last "\n" $keys ]
    }

    if { $oldKeys($spName($currentk)) != $keys } {
	if { [string compare -nocase -length 8 $keys "core key"] } {
	    set cKeys($spName($currentk)) "core key $keys"
	} else {
	    set cKeys($spName($currentk)) $keys
	}
    }

    if { $cKeys($spName($currentk)) != "" } {
	set db$currentb $db($keystrokeB)
    } else {
	set db$currentb $db($ignoreButton)
    }

    if { [ string length $cKeys($spName($currentk)) ] > 240 } {
	helpWindow "Help Window " \
		"\n\nYou have entered more 240 keys. \n\
		wacomcpl only accepts 240 keys. \n\
		Please reduce your keys. "
    } else {
	closeSubWindow
    }
}

proc touchState { theCButton } {
    global device touchButton getDeviceModel

    checkbutton $theCButton -text "Disable Touch" -anchor w \
	-variable touchButton -state normal -command switchTouch
}

proc gestureState { theCButton } {
    global device touchButton getDeviceModel hasGesture
    global gestureButton 

    # gesture supported and touch is on 
    if { [ exec xsetwacom get "$device" touch ] } {
	checkbutton $theCButton -text "Disable Touch Gesture" -anchor w \
	    -variable gestureButton -state normal -command switchGesture
    } else {
	checkbutton $theCButton -text "Disable Touch Gesture" -anchor w \
	    -variable gestureButton -state disable -command switchGesture
    }
}

proc switchTouch { } {
    global device touchButton gestureButton hasGesture getDeviceModel

    set model $getDeviceModel($device,model)
    if { $touchButton } {
	exec xsetwacom set "$device" touch 0
	updateWacomrc $device touch 0
	if { $hasGesture($model) } {
	    .panel.button configure -state disabled
	}
    } else {
	exec xsetwacom set "$device" touch 1
	updateWacomrc $device touch 1
	if { $hasGesture($model) } {
	    .panel.button configure -state normal
	}
   }
}

proc switchGesture { } {
    global device touchButton gestureButton

    if { $gestureButton } {
	exec xsetwacom set "$device" gesture 0
	updateWacomrc $device gesture 0
    } else {
	exec xsetwacom set "$device" gesture 1
	updateWacomrc $device gesture 1
   }
}

# the 4 parameters are booleans, specifying which buttons should be present.
proc createPanel { pressure button mapping calibrate } {
    global bName device getOption getDeviceModel currentb 
    global wName startS Option touchButton hasGesture gestureButton

    frame .panel
    set bName(pressure) $pressure
    set bName(button) $button
    set bName(mapping) $mapping
    set bName(calibrate) $calibrate
    set currentb 0
    set type $getDeviceModel($device,type)

    # buttons and expresskeys
    getNumButton $type
    setspName
    set SN [ exec xsetwacom get "$device" ToolSerial ]
    if { $SN && [string compare type "pad"] } {
	set hexSN [format %X $SN]
	label .panel.snt -text "Serial Number: "
	label .panel.sn -text "$hexSN"
	grid .panel.snt -row 0 -column 0 -columnspan 2 -padx 3 -pady 3
	grid .panel.sn -row 0 -column 3 -columnspan 2 -padx 3 -pady 3
    }

    if { $calibrate } {
	button .panel.calibrate -text "Calibration" \
	    -command Calibration -state normal
	grid .panel.calibrate -row 5 -column 3 -columnspan 2 -sticky news -padx 10
    }
    if { $pressure } {
	if { ![string compare $getDeviceModel($device,type) "touch"] } {
	    set touchButton 1
	    if { [exec xsetwacom get "$device" touch] } {
		set touchButton 0
	    }
	    touchState .panel.pressure 
	} else {
	    set wName(1) "Feel"
	    button .panel.pressure -text $wName(1) \
		-state normal -command "displaySubWindow \
	    updateTip defaultTip initialTip 1 0 0"
	}
	grid .panel.pressure -row 5 -column 0 -columnspan 2 -sticky news -padx 10
    }
    if { $button } {
	if { ![string compare $getDeviceModel($device,type) "touch"] } {
	    set model $getDeviceModel($device,model)
	    if { $hasGesture($model) } {
		set gestureButton 1
		if { [ exec xsetwacom get "$device" gesture ] } {
		    set gestureButton 0
		}
		gestureState .panel.button 
	    }
	} elseif { ![string compare $getDeviceModel($device,type) "pad"] } {
	    button .panel.button -text $wName(6) \
		-state normal -command "displaySubWindow \
		updateButton defaultButton initialButton 6 0 $startS"
	} else {
	    button .panel.button -text $wName(2) \
		-state normal -command "displaySubWindow \
		updateButton defaultButton initialButton 2 0 0"
	}
	grid .panel.button -row 6 -column 0 -columnspan 2 -sticky news -padx 10
    }
    if { $mapping } {
	button .panel.mapping -text $wName(3) \
	   -state normal -command "displaySubWindow \
	   updateT defaultT initialT 3 0 0"
	grid .panel.mapping -row 6 -column 3 -columnspan 2 -sticky news -padx 10
    }

    set bName(advanced) 0
    if { [string compare $getDeviceModel($device,type) "touch"] &&
	    [string compare $getDeviceModel($device,type) "pad"] } { #not a pad or touch
	set Option(1) "Mode"
	getDeviceOptionProc $device 1
	set mode $getOption($device,Mode)
	if { ($pressure || $mapping) && ($mode == "Absolute" || $mode == "1") } {  #and in absolute mode
	    set bName(advanced) 1
	    button .panel.advanced -text $wName(4) \
		-state normal -command "displaySubWindow \
		updateSet defaultSet initialSet 4 0 0"
	    grid .panel.advanced -row 9 -column 2 -columnspan 2 -sticky news -padx 10
	}
    }

    grid .panel -row 0 -column 1 -columnspan 8 -sticky news -padx 10 -pady 40
}

proc updateModelInfo { } {
    global isLCD numPadButtons numPadRings hasPad hasTouch
    global numPadStrips numPadAbsW numPadRelW 
    global maxNumTablets hasGesture 

    for { set i 0 } { $i <= $maxNumTablets } { incr i 1 } {
	set isLCD($i) 0 
	set numPadButtons($i) 0
	set numPadRings($i) 0
	set numPadStrips($i) 0
	set numPadAbsW($i) 0
	set numPadRelW($i) 0
	set hasPad($i) 0
	set hasTouch($i) 0
	set hasGesture($i) 0
    }
    #PL
    for { set i 48} { $i <= 63 } { incr i 1 } {
	set isLCD($i) 1 
    }
    #TabletPC
    for { set i 144 } { $i <= 159 } { incr i 1 } {
	set isLCD($i) 1 
    }
    #TabletPC with 2FG touch
    set isLCD(226) 1 
    set isLCD(227) 1 

    #Cintiq
    for { set i 197 } { $i <= 204 } { incr i 1 } {
	set isLCD($i) 1 
    }

    # G4
    set numPadButtons(21) 2
    set numPadButtons(22) 2
    set numPadRelW(21) 1
    set numPadRelW(22) 1
    # Bamboo Fun
    set numPadButtons(23) 4 
    set numPadRings(23) 1
    set numPadButtons(24) 4
    set numPadRings(24) 1
    #Cintiq 21UX
    set numPadButtons(63) 8
    set numPadStrips(63) 2

    #Cintiq 21UX2
    set numPadButtons(204) 18
    set numPadStrips(204) 2

    # Bamboo
    for { set i 101 } { $i <= 105 } { incr i 1 } {
	set numPadButtons($i) 4
	set numPadRings($i) 1
   }

    # Bamboo Pen and Touch
    for { set i 208 } { $i < 212 } { incr i 1 } {
        set hasPad($i) 1
        set numPadButtons($i) 4
	set hasTouch($i) 1
	set hasGesture($i) 1
    }

    # I3
    set numPadButtons(176) 4
    set numPadStrips(176) 1
    for { set i 177 } { $i <= 182 } { incr i 1 } {
	set numPadButtons($i) 8
	set numPadStrips($i) 2
    }
    set numPadButtons(183) 4
    set numPadStrips(183) 1

    # Cintiq 20WSX
    set numPadButtons(197) 10
    set numPadStrips(197) 2

    # Hummingbird (Cintiq 12WX)
    set numPadButtons(198) 10
    set numPadStrips(198) 2

    # I4
    set numPadButtons(184) 7
    set numPadRings(184) 1
    for { set i 185 } { $i <= 187 } { incr i 1 } {
	set numPadButtons($i) 9
	set numPadRings($i) 1
    }

    for { set i 0 } { $i <= $maxNumTablets } { incr i 1 } {
	if { $numPadButtons($i) || $numPadRings($i) 
		|| $numPadStrips($i) || $numPadAbsW($i) 
		|| $numPadRelW($i) } {
	    set hasPad($i) 1
	}
    }

    # one finger 
    for { set i 147 } { $i <= 159 } { incr i 1 } {
	set hasTouch($i) 1
    }

    # 2FG
    for { set i 226 } { $i <= 227 } { incr i 1 } {
	set hasTouch($i) 1
	set hasGesture($i) 1
    }
}

proc createControls { } {
    global numScreens currentScreen cKeys spName
    global oldKeys numStrips maxNumButtons maxNumStripEvents
    global workingTags db dm sm wName bName numButton
    global ignoreButton displayToggleB
    global modeToggleB keystrokeB

    createDeviceListPanel 
    updateModelInfo

    set db(1) "Left"
    set db(2) "Middle"
    set db(3) "Right"
    set db(4) "Fourth"
    set db(5) "Fifth"

    set numB [ expr ($maxNumButtons + $maxNumStripEvents) ]
    for { set i 6 } { $i <= $numB } { incr i 1 } {
	set db($i) "Ignore"
    }

    set db($modeToggleB) "Mode Toggle"
    set db($displayToggleB) "Display Toggle"
    set db($ignoreButton) "Ignore"
    set db($keystrokeB) "KeyStroke"
    set dm(1) "Relative"
    set dm(2) "Absolute"

    set sm(1) "Side Switch Only"
    set sm(2) "Side Switch + Tip"

    set bName(keystroke) 0
    set wName(1) "Feel"
    set wName(2) "Tool Buttons"
    set wName(3) "Tracking"
    set wName(4) "Screen Mapping"
    set wName(5) "Keystrokes"
    set wName(6) "Tablet Controls"

#   Real help will be supported later
#    checkbutton .showHelp -text "Turn Help on" -anchor w \
#	    -variable showHelp -state normal  

    button .exit -text "Exit" -command "exit 0" -padx 40
    
#    grid .showHelp -row 0 -column 2 -sticky nw -padx 30
    grid .exit -row 20 -column 2 -sticky news -padx 20
    grid .workingDev -row 0 -rowspan 25 -column 0 -sticky news -padx 20

    set workingTags [ bindtags .workingDev.list]

    grid columnconfigure . 1 -weight 1
    grid rowconfigure . 7 -weight 5
}

proc CalibrateOnly { numArg argList } {
    global device calibration_sequence_only calibration_with_warning
    global deviceList

    set calibration_sequence_only 1
    set device [ lrange $argList 1 end ]

    createDeviceList

    set found 0
    foreach dev $deviceList {
	if { [ string compare $dev $device ] == 0 } {
	    set found 1
	    break;
	}
    }

    if { !$found } {
	    messageWindow "Error" "\n\n Device $device not found. \n"
	    exit 1
    }

    createScreenList $device
    if { $numArg == 3 } {
	set calibration_with_warning 1
    }
    Calibration
}

if { $argc > 1 && [lindex $argv 0] == "calibrate" } {
    wm title . "Wacom Control Panel -Calibration"	
    CalibrateOnly $argc $argv
    wm geometry . =350x200+$origin_x+$origin_y
} else {
    wm title . "Wacom Control Panel"
    createControls
    wm geometry . =$windowSize+$origin_x+$origin_y
}

#
# Local Variables:
# mode: tcl
# End:
#

# vim: set noexpandtab shiftwidth=4:
