Spaces:
Build error
Build error
# | |
# tkdnd.tcl -- | |
# | |
# This file implements some utility procedures that are used by the TkDND | |
# package. | |
# | |
# This software is copyrighted by: | |
# George Petasis, National Centre for Scientific Research "Demokritos", | |
# Aghia Paraskevi, Athens, Greece. | |
# e-mail: petasis@iit.demokritos.gr | |
# | |
# The following terms apply to all files associated | |
# with the software unless explicitly disclaimed in individual files. | |
# | |
# The authors hereby grant permission to use, copy, modify, distribute, | |
# and license this software and its documentation for any purpose, provided | |
# that existing copyright notices are retained in all copies and that this | |
# notice is included verbatim in any distributions. No written agreement, | |
# license, or royalty fee is required for any of the authorized uses. | |
# Modifications to this software may be copyrighted by their authors | |
# and need not follow the licensing terms described here, provided that | |
# the new terms are clearly indicated on the first page of each file where | |
# they apply. | |
# | |
# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY | |
# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY | |
# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE | |
# POSSIBILITY OF SUCH DAMAGE. | |
# | |
# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, | |
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, | |
# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE | |
# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE | |
# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR | |
# MODIFICATIONS. | |
# | |
package require Tk | |
namespace eval ::tkdnd { | |
variable _package_dir {} | |
variable _topw ".drag" | |
variable _tabops | |
variable _state | |
variable _x0 | |
variable _y0 | |
variable _platform_namespace | |
variable _drop_file_temp_dir | |
variable _auto_update 1 | |
variable _dx 3 ;# The difference in pixels before a drag is initiated. | |
variable _dy 3 ;# The difference in pixels before a drag is initiated. | |
variable _windowingsystem | |
if {[info exists ::TKDND_DEBUG_LEVEL]} { | |
variable _debug_level $::TKDND_DEBUG_LEVEL | |
} elseif {[info exists ::env(TKDND_DEBUG_LEVEL)]} { | |
variable _debug_level $::env(TKDND_DEBUG_LEVEL) | |
} else { | |
variable _debug_level 0 | |
} | |
bind TkDND_Drag1 <ButtonPress-1> {tkdnd::_begin_drag press 1 %W %s %X %Y %x %y} | |
bind TkDND_Drag1 <B1-Motion> {tkdnd::_begin_drag motion 1 %W %s %X %Y %x %y} | |
bind TkDND_Drag2 <ButtonPress-2> {tkdnd::_begin_drag press 2 %W %s %X %Y %x %y} | |
bind TkDND_Drag2 <B2-Motion> {tkdnd::_begin_drag motion 2 %W %s %X %Y %x %y} | |
bind TkDND_Drag3 <ButtonPress-3> {tkdnd::_begin_drag press 3 %W %s %X %Y %x %y} | |
bind TkDND_Drag3 <B3-Motion> {tkdnd::_begin_drag motion 3 %W %s %X %Y %x %y} | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::debug_enabled: returns the requested debug level (0 = no debug). | |
# ---------------------------------------------------------------------------- | |
proc debug_enabled { {level {}} } { | |
variable _debug_level | |
if {$level != {}} { | |
if {[string is integer -strict $level]} { | |
set _debug_level $level | |
} elseif {[string is true $level]} { | |
set _debug_level 1 | |
} | |
} | |
return $_debug_level | |
};# debug_enabled | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::source: source a Tcl fileInitialise the TkDND package. | |
# ---------------------------------------------------------------------------- | |
proc source { filename { encoding utf-8 } } { | |
variable _package_dir | |
# If in debug mode, enable debug statements... | |
set dbg_lvl [debug_enabled] | |
if {$dbg_lvl} { | |
puts "tkdnd::source (debug level $dbg_lvl) $filename" | |
set fd [open $filename r] | |
fconfigure $fd -encoding $encoding | |
set script [read $fd] | |
close $fd | |
set map {} | |
for {set lvl 0} {$lvl <= $dbg_lvl} {incr lvl} { | |
lappend map "\#\D\B\G$lvl " {} ;# Do not remove these \\ | |
} | |
lappend map "\#\D\B\G\ " {} ;# Do not remove these \\ | |
set script [string map $map $script] | |
return [eval $script] | |
} | |
::source -encoding $encoding $filename | |
};# source | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::initialise: Initialise the TkDND package. | |
# ---------------------------------------------------------------------------- | |
proc initialise { dir PKG_LIB_FILE PACKAGE_NAME} { | |
variable _package_dir | |
variable _platform_namespace | |
variable _drop_file_temp_dir | |
variable _windowingsystem | |
global env | |
set _package_dir $dir | |
switch [tk windowingsystem] { | |
x11 { | |
set _windowingsystem x11 | |
} | |
win32 - | |
windows { | |
set _windowingsystem windows | |
} | |
aqua { | |
set _windowingsystem aqua | |
} | |
default { | |
error "unknown Tk windowing system" | |
} | |
} | |
## Get User's home directory: We try to locate the proper path from a set of | |
## environmental variables... | |
foreach var {HOME HOMEPATH USERPROFILE ALLUSERSPROFILE APPDATA} { | |
if {[info exists env($var)]} { | |
if {[file isdirectory $env($var)]} { | |
set UserHomeDir $env($var) | |
break | |
} | |
} | |
} | |
## Should use [tk windowingsystem] instead of tcl platform array: | |
## OS X returns "unix," but that's not useful because it has its own | |
## windowing system, aqua | |
## Under windows we have to also combine HOMEDRIVE & HOMEPATH... | |
if {![info exists UserHomeDir] && | |
[string equal $_windowingsystem windows] && | |
[info exists env(HOMEDRIVE)] && [info exists env(HOMEPATH)]} { | |
if {[file isdirectory $env(HOMEDRIVE)$env(HOMEPATH)]} { | |
set UserHomeDir $env(HOMEDRIVE)$env(HOMEPATH) | |
} | |
} | |
## Have we located the needed path? | |
if {![info exists UserHomeDir]} { | |
set UserHomeDir [pwd] | |
} | |
set UserHomeDir [file normalize $UserHomeDir] | |
## Try to locate a temporary directory... | |
foreach var {TKDND_TEMP_DIR TEMP TMP} { | |
if {[info exists env($var)]} { | |
if {[file isdirectory $env($var)] && [file writable $env($var)]} { | |
set _drop_file_temp_dir $env($var) | |
break | |
} | |
} | |
} | |
if {![info exists _drop_file_temp_dir]} { | |
foreach _dir [list "$UserHomeDir/Local Settings/Temp" \ | |
"$UserHomeDir/AppData/Local/Temp" \ | |
/tmp \ | |
C:/WINDOWS/Temp C:/Temp C:/tmp \ | |
D:/WINDOWS/Temp D:/Temp D:/tmp] { | |
if {[file isdirectory $_dir] && [file writable $_dir]} { | |
set _drop_file_temp_dir $_dir | |
break | |
} | |
} | |
} | |
if {![info exists _drop_file_temp_dir]} { | |
set _drop_file_temp_dir $UserHomeDir | |
} | |
set _drop_file_temp_dir [file native $_drop_file_temp_dir] | |
source $dir/tkdnd_generic.tcl | |
switch $_windowingsystem { | |
x11 { | |
source $dir/tkdnd_unix.tcl | |
set _platform_namespace xdnd | |
} | |
win32 - | |
windows { | |
source $dir/tkdnd_windows.tcl | |
set _platform_namespace olednd | |
} | |
aqua { | |
source $dir/tkdnd_macosx.tcl | |
set _platform_namespace macdnd | |
} | |
default { | |
error "unknown Tk windowing system" | |
} | |
} | |
load $dir/$PKG_LIB_FILE $PACKAGE_NAME | |
source $dir/tkdnd_compat.tcl | |
${_platform_namespace}::initialise | |
};# initialise | |
proc GetDropFileTempDirectory { } { | |
variable _drop_file_temp_dir | |
return $_drop_file_temp_dir | |
} | |
proc SetDropFileTempDirectory { dir } { | |
variable _drop_file_temp_dir | |
set _drop_file_temp_dir $dir | |
} | |
proc debug {msg} { | |
puts $msg | |
};# debug | |
};# namespace ::tkdnd | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::drag_source | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::drag_source { mode path { types {} } { event 1 } | |
{ tagprefix TkDND_Drag } } { | |
#DBG debug "::tkdnd::drag_source $mode $path $types $event $tagprefix" | |
foreach single_event $event { | |
set tags [bindtags $path] | |
set idx [lsearch $tags ${tagprefix}$single_event] | |
switch -- $mode { | |
register { | |
if { $idx != -1 } { | |
## No need to do anything! | |
# bindtags $path [lreplace $tags $idx $idx ${tagprefix}$single_event] | |
} else { | |
bindtags $path [linsert $tags 1 ${tagprefix}$single_event] | |
} | |
_drag_source_update_types $path $types | |
} | |
unregister { | |
if { $idx != -1 } { | |
bindtags $path [lreplace $tags $idx $idx] | |
} | |
} | |
} | |
} | |
};# tkdnd::drag_source | |
proc ::tkdnd::_drag_source_update_types { path types } { | |
set types [platform_specific_types $types] | |
set old_types [bind $path <<DragSourceTypes>>] | |
foreach type $types { | |
if {[lsearch $old_types $type] < 0} {lappend old_types $type} | |
} | |
bind $path <<DragSourceTypes>> $old_types | |
};# ::tkdnd::_drag_source_update_types | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::drop_target | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::drop_target { mode path { types {} } } { | |
variable _windowingsystem | |
set types [platform_specific_types $types] | |
switch -- $mode { | |
register { | |
switch $_windowingsystem { | |
x11 { | |
_register_types $path [winfo toplevel $path] $types | |
} | |
win32 - | |
windows { | |
_RegisterDragDrop $path | |
bind <Destroy> $path {+ tkdnd::_RevokeDragDrop %W} | |
} | |
aqua { | |
macdnd::registerdragwidget [winfo toplevel $path] $types | |
} | |
default { | |
error "unknown Tk windowing system" | |
} | |
} | |
set old_types [bind $path <<DropTargetTypes>>] | |
set new_types {} | |
foreach type $types { | |
if {[lsearch -exact $old_types $type] < 0} {lappend new_types $type} | |
} | |
if {[llength $new_types]} { | |
bind $path <<DropTargetTypes>> [concat $old_types $new_types] | |
} | |
} | |
unregister { | |
switch $_windowingsystem { | |
x11 { | |
} | |
win32 - | |
windows { | |
_RevokeDragDrop $path | |
} | |
aqua { | |
error todo | |
} | |
default { | |
error "unknown Tk windowing system" | |
} | |
} | |
bind $path <<DropTargetTypes>> {} | |
} | |
} | |
};# tkdnd::drop_target | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::_begin_drag | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::_begin_drag { event button source state X Y x y } { | |
variable _x0 | |
variable _y0 | |
variable _state | |
switch -- $event { | |
press { | |
set _x0 $X | |
set _y0 $Y | |
set _state "press" | |
} | |
motion { | |
if { ![info exists _state] } { | |
# This is just extra protection. There seem to be | |
# rare cases where the motion comes before the press. | |
return | |
} | |
if { [string equal $_state "press"] } { | |
variable _dx | |
variable _dy | |
if { abs($_x0-$X) > ${_dx} || abs($_y0-$Y) > ${_dy} } { | |
set _state "done" | |
_init_drag $button $source $state $X $Y $x $y | |
} | |
} | |
} | |
} | |
};# tkdnd::_begin_drag | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::_init_drag | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::_init_drag { button source state rootX rootY X Y } { | |
#DBG debug "::tkdnd::_init_drag $button $source $state $rootX $rootY $X $Y" | |
# Call the <<DragInitCmd>> binding. | |
set cmd [bind $source <<DragInitCmd>>] | |
#DBG debug "CMD: $cmd" | |
if {[string length $cmd]} { | |
set cmd [string map [list %W [list $source] \ | |
%X $rootX %Y $rootY %x $X %y $Y \ | |
%S $state %e <<DragInitCmd>> %A \{\} %% % \ | |
%b \{$button\} \ | |
%t \{[bind $source <<DragSourceTypes>>]\}] $cmd] | |
set code [catch {uplevel \#0 $cmd} info options] | |
#DBG debug "CODE: $code ---- $info" | |
switch -exact -- $code { | |
0 {} | |
3 - 4 { | |
# FRINK: nocheck | |
return | |
} | |
default { | |
return -options $options $info | |
} | |
} | |
set len [llength $info] | |
if {$len == 3} { | |
foreach { actions types _data } $info { break } | |
set types [platform_specific_types $types] | |
set data [list] | |
foreach type $types { | |
lappend data $_data | |
} | |
unset _data | |
} elseif {$len == 2} { | |
foreach { actions _data } $info { break } | |
set data [list]; set types [list] | |
foreach {t d} $_data { | |
foreach t [platform_specific_types $t] { | |
lappend types $t; lappend data $d | |
} | |
} | |
unset _data t d | |
} else { | |
foreach { actions } $info { break } | |
if {$len == 1 && [string equal [lindex $actions 0] "refuse_drop"]} { | |
return | |
} | |
error "not enough items in the result of the <<DragInitCmd>>\ | |
event binding. Either 2 or 3 items are expected. The command | |
executed was: \"$cmd\"\nResult was: \"$info\"" | |
} | |
set action refuse_drop | |
## Custom Cursors... | |
# Call the <<DragCursorMap>> binding. | |
set cursor_map [bind $source <<DragCursorMap>>] | |
variable _windowingsystem | |
#DBG debug "Source: \"$source\"" | |
#DBG debug "Types: \"[join $types {", "}]\"" | |
#DBG debug "Actions: \"[join $actions {", "}]\"" | |
#DBG debug "Button: \"$button\"" | |
#DBG debug "Data: \"[string range $data 0 100]\"" | |
#DBG debug "CursorMap: \"[string range $cursor_map 0 100]\"" | |
switch $_windowingsystem { | |
x11 { | |
set action [xdnd::_dodragdrop $source $actions $types $data $button $cursor_map] | |
} | |
win32 - | |
windows { | |
set action [_DoDragDrop $source $actions $types $data $button] | |
} | |
aqua { | |
set action [macdnd::dodragdrop $source $actions $types $data $button] | |
} | |
default { | |
error "unknown Tk windowing system" | |
} | |
} | |
## Call _end_drag to notify the widget of the result of the drag | |
## operation... | |
_end_drag $button $source {} $action {} $data {} $state $rootX $rootY $X $Y | |
} | |
};# tkdnd::_init_drag | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::_end_drag | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::_end_drag { button source target action type data result | |
state rootX rootY X Y } { | |
set rootX 0 | |
set rootY 0 | |
# Call the <<DragEndCmd>> binding. | |
set cmd [bind $source <<DragEndCmd>>] | |
if {[string length $cmd]} { | |
set cmd [string map [list %W [list $source] \ | |
%X $rootX %Y $rootY %x $X %y $Y %% % \ | |
%b \{$button\} \ | |
%S $state %e <<DragEndCmd>> %A \{$action\}] $cmd] | |
set info [uplevel \#0 $cmd] | |
# if { $info != "" } { | |
# variable _windowingsystem | |
# foreach { actions types data } $info { break } | |
# set types [platform_specific_types $types] | |
# switch $_windowingsystem { | |
# x11 { | |
# error "dragging from Tk widgets not yet supported" | |
# } | |
# win32 - | |
# windows { | |
# set action [_DoDragDrop $source $actions $types $data $button] | |
# } | |
# aqua { | |
# macdnd::dodragdrop $source $actions $types $data | |
# } | |
# default { | |
# error "unknown Tk windowing system" | |
# } | |
# } | |
# ## Call _end_drag to notify the widget of the result of the drag | |
# ## operation... | |
# _end_drag $button $source {} $action {} $data {} $state $rootX $rootY | |
# } | |
} | |
};# tkdnd::_end_drag | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::platform_specific_types | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::platform_specific_types { types } { | |
variable _platform_namespace | |
${_platform_namespace}::platform_specific_types $types | |
}; # tkdnd::platform_specific_types | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::platform_independent_types | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::platform_independent_types { types } { | |
variable _platform_namespace | |
${_platform_namespace}::platform_independent_types $types | |
}; # tkdnd::platform_independent_types | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::platform_specific_type | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::platform_specific_type { type } { | |
variable _platform_namespace | |
${_platform_namespace}::platform_specific_type $type | |
}; # tkdnd::platform_specific_type | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::platform_independent_type | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::platform_independent_type { type } { | |
variable _platform_namespace | |
${_platform_namespace}::platform_independent_type $type | |
}; # tkdnd::platform_independent_type | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::bytes_to_string | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::bytes_to_string { bytes } { | |
set string {} | |
foreach byte $bytes { | |
append string [binary format c $byte] | |
} | |
return $string | |
};# tkdnd::bytes_to_string | |
# ---------------------------------------------------------------------------- | |
# Command tkdnd::urn_unquote | |
# ---------------------------------------------------------------------------- | |
proc ::tkdnd::urn_unquote {url} { | |
set result "" | |
set start 0 | |
while {[regexp -start $start -indices {%[0-9a-fA-F]{2}} $url match]} { | |
foreach {first last} $match break | |
append result [string range $url $start [expr {$first - 1}]] | |
append result [format %c 0x[string range $url [incr first] $last]] | |
set start [incr last] | |
} | |
append result [string range $url $start end] | |
return [encoding convertfrom utf-8 $result] | |
};# tkdnd::urn_unquote | |