1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import time
16 from __pyjamas__ import wnd, doc
17 from pyjamas import DOM
18 from pyjamas import Window
19 from pyjamas.ui import GlassWidget
20 from pyjamas.ui.RootPanel import RootPanel
21 from pyjamas.ui import Event
22 from pyjamas.Timer import Timer
23 from pyjamas.dnd.utils import DraggingWidget, isCanceled, \
24 findDraggable, eventCoordinates, \
25 getElementUnderMouse
26 from pyjamas.dnd.DataTransfer import DataTransfer, DragDataStore
27 from pyjamas.dnd.DragEvent import DragEvent
28 from pyjamas.dnd import READ_ONLY, READ_WRITE, PROTECTED
29
30 ACTIVELY_DRAGGING = 3
31 DRAGGING_NO_MOVEMENT_YET = 2
32 NOT_DRAGGING = 1
33
35 """
36 DNDHelper is a singleton drag and drop agent.
37
38 It acts as dragging/dropping agent for platforms that do not support html5
39 drag and drop.
40 """
41
43 self.dropTargets = []
44 self.dragging = NOT_DRAGGING
45 self.dragBusy = False
46 self._currentTargetElement = None
47 self.previousDropTarget = None
48 self.draggingImage = None
49 self.origMouseX = 0
50 self.origMouseY = 0
51 self.currentDragOperation = 'none'
52 self.data = None
53 self.returnTimer = Timer(notify=self.onReturningWidget)
54 self.mouseEvent = None
55 self.dragDataStore = None
56
58 if self._currentTargetElement is not None:
59 if not DOM.compare(self._currentTargetElement, element):
60
61
62 self.fireDNDEvent('dragleave', self.currentTargetElement,
63 self.currentDropWidget)
64
65
66 self._currentTargetElement = element
67
69 return self._currentTargetElement
70
71 currentTargetElement = property(getCurrentTargetElement,
72 setCurrentTargetElement)
73
75 """
76 ie6 GlassWidget impl needs this
77 """
78 return self.dragWidget.getElement()
79
81 """
82 http://dev.w3.org/html5/spec/dnd.html#dragevent
83 """
84
85 dropEffect='none'
86
87 if event_type in ['dragover', 'dragenter']:
88 ea = dataTransfer.getEffectAllowed()
89 if ea == 'none':
90 dropEffect = 'none'
91 elif ea.startswith('copy') or ea == 'all':
92 dropEffect = 'copy'
93 elif ea.startswith('link'):
94 dropEffect = 'link'
95 elif ea == 'move':
96 dropEffect = 'move'
97 else:
98 dropEffect = 'copy'
99 elif event_type in ['drop', 'dragend']:
100 dropEffect = self.currentDragOperation
101 dataTransfer.dropEffect = dropEffect
102
104 """
105 http://dev.w3.org/html5/spec/dnd.html
106 """
107 dataTransfer = event.dataTransfer
108 ea = dataTransfer.effectAllowed
109 de = dataTransfer.dropEffect
110 if (de == 'copy' and ea in
111 ['uninitialized', 'copy','copyLink', 'copyMove', 'all']):
112 self.currentDragOperation = 'copy'
113 elif (de == 'link' and ea in
114 ['uninitialized', 'link', 'copyLink', 'linkMove', 'all']):
115 self.currentDragOperation = 'link'
116 elif (de == 'move' and ea in
117 ['uninitialized', 'move', 'copyMove', 'linkMove', 'all']):
118 self.currentDragOperation = 'move'
119 else:
120 self.currentDragOperation = 'none'
121
123 dt = drag_event.dataTransfer
124 self.dragDataStore.allowed_effects_state = dt.effectAllowed
125
127 """
128 Rather than searching the entire document for drop target widgets and
129 maybe drop targets within widgets, this implementation holds a list of
130 widgets and searches only within this list for potential drop targets.
131 """
132 if not target in self.dropTargets:
133 self.dropTargets.append(target)
134
136 """
137 I dont know why, but a widget may no longer want to be registered
138 as a drop target.
139 """
140 while target in self.dropTargets:
141 self.dropTargets.remove(target)
142
143
163
165 """
166 This is called from DataTransfer
167 """
168 if self.draggingImage:
169 self.draggingImage.addElement(element)
170 else:
171 self.createDraggingImage(element)
172
174 self.draggingImage = DraggingWidget(element)
175 return self.draggingImage
176
178 """
179 Move the dragging image around.
180 """
181 elt_top = y - self.dragTopOffset
182 elt_left = x - self.dragLeftOffset
183
184
185
186
187
188 self.draggingImage.setStyleAttribute('top', elt_top )
189 self.draggingImage.setStyleAttribute('left', elt_left)
190
192 """
193 GlassWidget wants this
194 """
195
196
197
198 return self.dragWidget.getAbsoluteLeft()
199
200
202 """
203 GlassWidget wants this
204 """
205
206
207
208 return self.dragWidget.getAbsoluteTop()
209
215
217 self.dragDataStore.allowed_effects_state = \
218 event.dataTransfer.effectAllowed
219 if event.type in ['dragstart', 'drop']:
220 self.dragDataStore.setMode(PROTECTED)
221 event.dataTransfer.dataStore = None
222
232
234 ds = self.dragDataStore
235 x = 0
236 y = 0
237 if ds.bitmap is not None:
238 if ds.hotspot_coordinate is not None:
239 offset = ds.hotspot_coordinate
240 x = offset[0]
241 y = offset[1]
242 self.setDragImage(ds.bitmap, x, y)
243 return
244 if self.dragDataStore.elements:
245 for element in self.dragDataStore.elements:
246 self.addFeedbackElement(element)
247
248
250 event = DOM.eventGetCurrentEvent()
251 self.mouseEvent = event
252 button = DOM.eventGetButton(event)
253 if not button == Event.BUTTON_LEFT:
254 return
255
256
257
258
259
260
261
262
263
264
265 x, y = eventCoordinates(event)
266
267 if self.dragging == DRAGGING_NO_MOVEMENT_YET:
268 self.origMouseX = x
269 self.origMouseY = y
270 self.currentDragOperation = 'none'
271 fromElement = self.dragWidget.getElement()
272
273 try:
274 draggable = fromElement.draggable
275 except:
276 draggable = False
277
278 if not draggable:
279 fromElement = findDraggable(sender.getElement(),
280 self.origMouseX, self.origMouseY)
281
282 if fromElement is None:
283 self.dragging = NOT_DRAGGING
284 return
285
286
287
288
289
290
291
292
293
294
295
296
297 self.origTop = DOM.getAbsoluteTop(fromElement)
298 self.origLeft = DOM.getAbsoluteLeft(fromElement)
299
300
301 position_absolute = DOM.getStyleAttribute(fromElement,
302 'position') == 'absolute'
303 if position_absolute:
304 self.dragLeftOffset = (self.origMouseX -
305 DOM.getAbsoluteLeft(fromElement.offsetParent))
306 self.dragTopOffset = (self.origMouseY -
307 DOM.getAbsoluteTop(fromElement.offsetParent))
308 else:
309 self.dragLeftOffset = self.origMouseX - self.origLeft
310 self.dragTopOffset = self.origMouseY - self.origTop
311
312
313
314
315 self.dragDataStore.elements = [fromElement]
316 dragStartEvent = self.fireDNDEvent('dragstart', None,
317 self.dragWidget)
318 if not isCanceled(dragStartEvent):
319 self.initFeedbackImage()
320 RootPanel().add(self.draggingImage)
321 self.setDragImageLocation(x, y)
322 self.dragging = ACTIVELY_DRAGGING
323 GlassWidget.show(self)
324 elif self.dragging == ACTIVELY_DRAGGING:
325 try:
326 doc().selection.empty()
327 except:
328 wnd().getSelection().removeAllRanges()
329
330 self.setDragImageLocation(x, y)
331
332
333
334 if self.dragBusy or time.time() - self.drag_time < 0.25:
335 return
336
337 self.doDrag(event, x, y)
338 self.drag_time = time.time()
339
340
341
342 - def doDrag(self, event, x, y):
343 self.dragBusy = True
344
345 drag_event = self.fireDNDEvent('drag', None, self.dragWidget)
346
347 if not isCanceled(drag_event):
348 target = None
349 widget = None
350
351
352 for widget in self.dropTargets:
353 target = getElementUnderMouse(widget, x, y)
354 if target is not None:
355 break
356 if target:
357 drop_widget = widget
358 drop_element = target
359 if (not self.currentTargetElement or
360 not DOM.compare(drop_element, self.currentTargetElement)):
361
362
363 enter_event = self.fireDNDEvent('dragenter', drop_element,
364 drop_widget)
365
366
367 if isCanceled(enter_event):
368 self.currentTargetElement = drop_element
369 self.currentDropWidget = drop_widget
370
371 if self.currentTargetElement is not None:
372
373
374
375 over_event = self.fireDNDEvent('dragover', drop_element,
376 self.currentDropWidget)
377
378
379 if isCanceled(over_event):
380 self.updateDragOperation(over_event)
381 else:
382 self.currentDragOperation = 'none'
383 self.draggingImage.updateCursor(self.currentDragOperation)
384 else:
385 self.currentTargetElement = None
386
387 else:
388 self.currentDragOperation = 'none'
389 self.dragBusy = False
390
404
439
440
441
442
443
447
449 self.moveItemTo(self.draggingImage,self.origLeft, self.origTop)
450
451 - def returnXY(self, start, destination, count):
452 start_x, start_y = start
453 destination_x, destination_y = destination
454 diff_x = (start_x - destination_x) / count
455 diff_y = (start_y - destination_y) / count
456 while (abs(start_x - destination_x) > 10
457 or abs(start_y - destination_y) > 10):
458 start_x -= diff_x
459 start_y -= diff_y
460 yield start_x, start_y
461 raise StopIteration
462
473
475 self.returnWidget = widget
476 returnWidgetDestination = x, y
477 widgetStart = widget.getAbsoluteLeft(), widget.getAbsoluteTop()
478 self.return_iterator = self.returnXY(widgetStart,
479 returnWidgetDestination, 10)
480 self.returnTimer.schedule(50)
481
484
488
491
494
495 dndHelper = None
496
501
502 initDNDHelper()
503