dropsite.tcl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. # ------------------------------------------------------------------------------
  2. # dropsite.tcl
  3. # This file is part of Unifix BWidget Toolkit
  4. # $Id$
  5. # ------------------------------------------------------------------------------
  6. # Index of commands:
  7. # - DropSite::include
  8. # - DropSite::setdrop
  9. # - DropSite::register
  10. # - DropSite::setcursor
  11. # - DropSite::setoperation
  12. # - DropSite::_update_operation
  13. # - DropSite::_compute_operation
  14. # - DropSite::_draw_operation
  15. # - DropSite::_init_drag
  16. # - DropSite::_motion
  17. # - DropSite::_release
  18. # ------------------------------------------------------------------------------
  19. namespace eval DropSite {
  20. Widget::declare DropSite {
  21. {-dropovercmd String "" 0}
  22. {-dropcmd String "" 0}
  23. {-droptypes String "" 0}
  24. }
  25. proc use { } {}
  26. variable _top ".drag"
  27. variable _opw ".drag.\#op"
  28. variable _target ""
  29. variable _status 0
  30. variable _tabops
  31. variable _defops
  32. variable _source
  33. variable _type
  34. variable _data
  35. variable _evt
  36. # key win unix
  37. # shift 1 | 1 -> 1
  38. # control 4 | 4 -> 4
  39. # alt 8 | 16 -> 24
  40. # meta | 64 -> 88
  41. array set _tabops {
  42. mod,none 0
  43. mod,shift 1
  44. mod,control 4
  45. mod,alt 24
  46. ops,copy 1
  47. ops,move 1
  48. ops,link 1
  49. }
  50. if { $tcl_platform(platform) == "unix" } {
  51. set _tabops(mod,alt) 8
  52. } else {
  53. set _tabops(mod,alt) 16
  54. }
  55. array set _defops \
  56. [list \
  57. copy,mod shift \
  58. move,mod control \
  59. link,mod alt \
  60. copy,img @[file join $env(BWIDGET_LIBRARY) "images" "opcopy.xbm"] \
  61. move,img @[file join $env(BWIDGET_LIBRARY) "images" "opmove.xbm"] \
  62. link,img @[file join $env(BWIDGET_LIBRARY) "images" "oplink.xbm"]]
  63. bind DragTop <KeyPress-Shift_L> {DropSite::_update_operation [expr %s | 1]}
  64. bind DragTop <KeyPress-Shift_R> {DropSite::_update_operation [expr %s | 1]}
  65. bind DragTop <KeyPress-Control_L> {DropSite::_update_operation [expr %s | 4]}
  66. bind DragTop <KeyPress-Control_R> {DropSite::_update_operation [expr %s | 4]}
  67. if { $tcl_platform(platform) == "unix" } {
  68. bind DragTop <KeyPress-Alt_L> {DropSite::_update_operation [expr %s | 8]}
  69. bind DragTop <KeyPress-Alt_R> {DropSite::_update_operation [expr %s | 8]}
  70. } else {
  71. bind DragTop <KeyPress-Alt_L> {DropSite::_update_operation [expr %s | 16]}
  72. bind DragTop <KeyPress-Alt_R> {DropSite::_update_operation [expr %s | 16]}
  73. }
  74. bind DragTop <KeyRelease-Shift_L> {DropSite::_update_operation [expr %s & ~1]}
  75. bind DragTop <KeyRelease-Shift_R> {DropSite::_update_operation [expr %s & ~1]}
  76. bind DragTop <KeyRelease-Control_L> {DropSite::_update_operation [expr %s & ~4]}
  77. bind DragTop <KeyRelease-Control_R> {DropSite::_update_operation [expr %s & ~4]}
  78. if { $tcl_platform(platform) == "unix" } {
  79. bind DragTop <KeyRelease-Alt_L> {DropSite::_update_operation [expr %s & ~8]}
  80. bind DragTop <KeyRelease-Alt_R> {DropSite::_update_operation [expr %s & ~8]}
  81. } else {
  82. bind DragTop <KeyRelease-Alt_L> {DropSite::_update_operation [expr %s & ~16]}
  83. bind DragTop <KeyRelease-Alt_R> {DropSite::_update_operation [expr %s & ~16]}
  84. }
  85. }
  86. # ------------------------------------------------------------------------------
  87. # Command DropSite::include
  88. # ------------------------------------------------------------------------------
  89. proc DropSite::include { class types } {
  90. set dropoptions {
  91. {-dropenabled Boolean 0 0}
  92. {-dropovercmd String "" 0}
  93. {-dropcmd String "" 0}
  94. }
  95. lappend dropoptions [list -droptypes String $types 0]
  96. Widget::declare $class $dropoptions
  97. }
  98. # ------------------------------------------------------------------------------
  99. # Command DropSite::setdrop
  100. # Widget interface to register
  101. # ------------------------------------------------------------------------------
  102. proc DropSite::setdrop { path subpath dropover drop {force 0}} {
  103. set cen [Widget::hasChanged $path -dropenabled en]
  104. set ctypes [Widget::hasChanged $path -droptypes types]
  105. if { $en } {
  106. if { $force || $cen || $ctypes } {
  107. register $subpath \
  108. -droptypes $types \
  109. -dropcmd $drop \
  110. -dropovercmd $dropover
  111. }
  112. } else {
  113. register $subpath
  114. }
  115. }
  116. # ------------------------------------------------------------------------------
  117. # Command DropSite::register
  118. # ------------------------------------------------------------------------------
  119. proc DropSite::register { path args } {
  120. variable _tabops
  121. variable _defops
  122. upvar \#0 DropSite::$path drop
  123. Widget::init DropSite .drop$path $args
  124. if { [info exists drop] } {
  125. unset drop
  126. }
  127. set dropcmd [Widget::getoption .drop$path -dropcmd]
  128. set types [Widget::getoption .drop$path -droptypes]
  129. set overcmd [Widget::getoption .drop$path -dropovercmd]
  130. Widget::destroy .drop$path
  131. if { $dropcmd != "" && $types != "" } {
  132. set drop(dropcmd) $dropcmd
  133. set drop(overcmd) $overcmd
  134. foreach {type ops} $types {
  135. set drop($type,ops) {}
  136. foreach {descop lmod} $ops {
  137. if { ![llength $descop] || [llength $descop] > 3 } {
  138. return -code error "invalid operation description \"$descop\""
  139. }
  140. foreach {subop baseop imgop} $descop {
  141. set subop [string trim $subop]
  142. if { ![string length $subop] } {
  143. return -code error "sub operation is empty"
  144. }
  145. if { ![string length $baseop] } {
  146. set baseop $subop
  147. }
  148. if { [info exists drop($type,ops,$subop)] } {
  149. return -code error "operation \"$subop\" already defined"
  150. }
  151. if { ![info exists _tabops(ops,$baseop)] } {
  152. return -code error "invalid base operation \"$baseop\""
  153. }
  154. if { [string compare $subop $baseop] &&
  155. [info exists _tabops(ops,$subop)] } {
  156. return -code error "sub operation \"$subop\" is a base operation"
  157. }
  158. if { ![string length $imgop] } {
  159. set imgop $_defops($baseop,img)
  160. }
  161. }
  162. if { ![string compare $lmod "program"] } {
  163. set drop($type,ops,$subop) $baseop
  164. set drop($type,img,$subop) $imgop
  165. } else {
  166. if { ![string length $lmod] } {
  167. set lmod $_defops($baseop,mod)
  168. }
  169. set mask 0
  170. foreach mod $lmod {
  171. if { ![info exists _tabops(mod,$mod)] } {
  172. return -code error "invalid modifier \"$mod\""
  173. }
  174. set mask [expr {$mask | $_tabops(mod,$mod)}]
  175. }
  176. if { ($mask == 0) != ([string compare $subop "default"] == 0) } {
  177. return -code error "sub operation default can only be used with modifier \"none\""
  178. }
  179. set drop($type,mod,$mask) $subop
  180. set drop($type,ops,$subop) $baseop
  181. set drop($type,img,$subop) $imgop
  182. lappend masklist $mask
  183. }
  184. }
  185. if { ![info exists drop($type,mod,0)] } {
  186. set drop($type,mod,0) default
  187. set drop($type,ops,default) copy
  188. set drop($type,img,default) $_defops(copy,img)
  189. lappend masklist 0
  190. }
  191. set drop($type,ops,force) copy
  192. set drop($type,img,force) $_defops(copy,img)
  193. foreach mask [lsort -integer -decreasing $masklist] {
  194. lappend drop($type,ops) $mask $drop($type,mod,$mask)
  195. }
  196. }
  197. }
  198. }
  199. # ------------------------------------------------------------------------------
  200. # Command DropSite::setcursor
  201. # ------------------------------------------------------------------------------
  202. proc DropSite::setcursor { cursor } {
  203. catch {.drag configure -cursor $cursor}
  204. }
  205. # ------------------------------------------------------------------------------
  206. # Command DropSite::setoperation
  207. # ------------------------------------------------------------------------------
  208. proc DropSite::setoperation { op } {
  209. variable _curop
  210. variable _dragops
  211. variable _target
  212. variable _type
  213. upvar \#0 DropSite::$_target drop
  214. if { [info exist drop($_type,ops,$op)] &&
  215. $_dragops($drop($_type,ops,$op)) } {
  216. set _curop $op
  217. } else {
  218. # force to a copy operation
  219. set _curop force
  220. }
  221. }
  222. # ------------------------------------------------------------------------------
  223. # Command DropSite::_init_drag
  224. # ------------------------------------------------------------------------------
  225. proc DropSite::_init_drag { top evt source state X Y type ops data } {
  226. variable _top
  227. variable _source
  228. variable _type
  229. variable _data
  230. variable _target
  231. variable _status
  232. variable _state
  233. variable _dragops
  234. variable _opw
  235. variable _evt
  236. catch {unset _dragops}
  237. array set _dragops {copy 1 move 0 link 0}
  238. foreach op $ops {
  239. set _dragops($op) 1
  240. }
  241. set _target ""
  242. set _status 0
  243. set _top $top
  244. set _source $source
  245. set _type $type
  246. set _data $data
  247. label $_opw -relief flat -bd 0 -highlightthickness 0 \
  248. -foreground black -background white
  249. bind $top <ButtonRelease-$evt> {DropSite::_release %X %Y}
  250. bind $top <B$evt-Motion> {DropSite::_motion %X %Y}
  251. bind $top <Motion> {DropSite::_release %X %Y}
  252. set _state $state
  253. set _evt $evt
  254. _motion $X $Y
  255. }
  256. # ------------------------------------------------------------------------------
  257. # Command DropSite::_update_operation
  258. # ------------------------------------------------------------------------------
  259. proc DropSite::_update_operation { state } {
  260. variable _top
  261. variable _status
  262. variable _state
  263. if { $_status & 3 } {
  264. set _state $state
  265. _motion [winfo pointerx $_top] [winfo pointery $_top]
  266. }
  267. }
  268. # ------------------------------------------------------------------------------
  269. # Command DropSite::_compute_operation
  270. # ------------------------------------------------------------------------------
  271. proc DropSite::_compute_operation { target state type } {
  272. variable _curop
  273. variable _dragops
  274. upvar \#0 DropSite::$target drop
  275. foreach {mask op} $drop($type,ops) {
  276. if { ($state & $mask) == $mask } {
  277. if { $_dragops($drop($type,ops,$op)) } {
  278. set _curop $op
  279. return
  280. }
  281. }
  282. }
  283. set _curop force
  284. }
  285. # ------------------------------------------------------------------------------
  286. # Command DropSite::_draw_operation
  287. # ------------------------------------------------------------------------------
  288. proc DropSite::_draw_operation { target type } {
  289. variable _opw
  290. variable _curop
  291. variable _dragops
  292. variable _tabops
  293. variable _status
  294. upvar \#0 DropSite::$target drop
  295. if { !($_status & 1) } {
  296. catch {place forget $_opw}
  297. return
  298. }
  299. if { 0 } {
  300. if { ![info exist drop($type,ops,$_curop)] ||
  301. !$_dragops($drop($type,ops,$_curop)) } {
  302. # force to a copy operation
  303. set _curop copy
  304. catch {
  305. $_opw configure -bitmap $_tabops(img,copy)
  306. place $_opw -relx 1 -rely 1 -anchor se
  307. }
  308. }
  309. } elseif { ![string compare $_curop "default"] } {
  310. catch {place forget $_opw}
  311. } else {
  312. catch {
  313. $_opw configure -bitmap $drop($type,img,$_curop)
  314. place $_opw -relx 1 -rely 1 -anchor se
  315. }
  316. }
  317. }
  318. # ------------------------------------------------------------------------------
  319. # Command DropSite::_motion
  320. # ------------------------------------------------------------------------------
  321. proc DropSite::_motion { X Y } {
  322. variable _top
  323. variable _target
  324. variable _status
  325. variable _state
  326. variable _curop
  327. variable _type
  328. variable _data
  329. variable _source
  330. variable _evt
  331. set script [bind $_top <B$_evt-Motion>]
  332. bind $_top <B$_evt-Motion> {}
  333. bind $_top <Motion> {}
  334. wm geometry $_top "+[expr {$X+1}]+[expr {$Y+1}]"
  335. update
  336. if { ![winfo exists $_top] } {
  337. return
  338. }
  339. set path [winfo containing $X $Y]
  340. if { [string compare $path $_target] } {
  341. # path != current target
  342. if { $_status & 2 } {
  343. # current target is valid and has recall status
  344. # generate leave event
  345. upvar \#0 DropSite::$_target drop
  346. uplevel \#0 $drop(overcmd) [list $_target $_source leave $X $Y $_curop $_type $_data]
  347. }
  348. set _target $path
  349. upvar \#0 DropSite::$_target drop
  350. if { [info exists drop($_type,ops)] } {
  351. # path is a valid target
  352. _compute_operation $_target $_state $_type
  353. if { $drop(overcmd) != "" } {
  354. set arg [list $_target $_source enter $X $Y $_curop $_type $_data]
  355. set _status [uplevel \#0 $drop(overcmd) $arg]
  356. } else {
  357. set _status 1
  358. catch {$_top configure -cursor based_arrow_down}
  359. }
  360. _draw_operation $_target $_type
  361. update
  362. catch {
  363. bind $_top <B$_evt-Motion> {DropSite::_motion %X %Y}
  364. bind $_top <Motion> {DropSite::_release %X %Y}
  365. }
  366. return
  367. } else {
  368. set _status 0
  369. catch {$_top configure -cursor dot}
  370. _draw_operation "" ""
  371. }
  372. } elseif { $_status & 2 } {
  373. upvar \#0 DropSite::$_target drop
  374. _compute_operation $_target $_state $_type
  375. set arg [list $_target $_source motion $X $Y $_curop $_type $_data]
  376. set _status [uplevel \#0 $drop(overcmd) $arg]
  377. _draw_operation $_target $_type
  378. }
  379. update
  380. catch {
  381. bind $_top <B$_evt-Motion> {DropSite::_motion %X %Y}
  382. bind $_top <Motion> {DropSite::_release %X %Y}
  383. }
  384. }
  385. # ------------------------------------------------------------------------------
  386. # Command DropSite::_release
  387. # ------------------------------------------------------------------------------
  388. proc DropSite::_release { X Y } {
  389. variable _target
  390. variable _status
  391. variable _curop
  392. variable _source
  393. variable _type
  394. variable _data
  395. if { $_status & 1 } {
  396. upvar \#0 DropSite::$_target drop
  397. set res [uplevel \#0 $drop(dropcmd) [list $_target $_source $X $Y $_curop $_type $_data]]
  398. DragSite::_end_drag $_source $_target $drop($_type,ops,$_curop) $_type $_data $res
  399. } else {
  400. if { $_status & 2 } {
  401. # notify leave event
  402. upvar \#0 DropSite::$_target drop
  403. uplevel \#0 $drop(overcmd) [list $_target $_source leave $X $Y $_curop $_type $_data]
  404. }
  405. DragSite::_end_drag $_source "" "" $_type $_data 0
  406. }
  407. }