~mterry/ubuntu/natty/gnome-shell/wip

« back to all changes in this revision

Viewing changes to js/ui/dnd.js

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2009-10-12 22:44:00 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20091012224400-k91p42yvou07i525
Tags: 2.28.0-0ubuntu1
* New upstream version
* debian/control:
  - updated build requirement
* debian/patches/80_git_change_fix_alt_tab_ressource_usage.patch:
  - git change to fix ressources not being freed on alt-tab

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
 
9
9
const SNAP_BACK_ANIMATION_TIME = 0.25;
10
10
 
 
11
let eventHandlerActor = null;
 
12
let currentDraggable = null;
 
13
 
 
14
function _getEventHandlerActor() {
 
15
    if (!eventHandlerActor) {
 
16
        eventHandlerActor = new Clutter.Rectangle();
 
17
        eventHandlerActor.width = 0;
 
18
        eventHandlerActor.height = 0;
 
19
        global.stage.add_actor(eventHandlerActor);
 
20
        // We connect to 'event' rather than 'captured-event' because the capturing phase doesn't happen
 
21
        // when you've grabbed the pointer.
 
22
        eventHandlerActor.connect('event',
 
23
                                  function(actor, event) {
 
24
                                      return currentDraggable._onEvent(actor, event);
 
25
                                  });
 
26
    }
 
27
    return eventHandlerActor;
 
28
}
 
29
 
11
30
function _Draggable(actor, manualMode) {
12
31
    this._init(actor, manualMode);
13
32
}
18
37
        if (!manualMode)
19
38
            this.actor.connect('button-press-event',
20
39
                               Lang.bind(this, this._onButtonPress));
21
 
        this._haveSourceGrab = false;
 
40
        this._onEventId = null;
 
41
 
 
42
        this._buttonDown = false; // The mouse button has been pressed and has not yet been released.
 
43
        this._dragInProgress = false; // The drag has been started, and has not been dropped or cancelled yet.
 
44
        this._snapBackInProgress = false; // The drag has been cancelled and the item is in the process of snapping back.
22
45
    },
23
46
 
24
47
    _onButtonPress : function (actor, event) {
27
50
        if (Tweener.getTweenCount(actor))
28
51
            return false;
29
52
 
30
 
        this._haveSourceGrab = true;
31
 
        this._grabActor(actor);
 
53
        this._buttonDown = true;
 
54
        this._grabActor();
32
55
 
33
56
        let [stageX, stageY] = event.get_coords();
34
57
        this._dragStartX = stageX;
37
60
        return false;
38
61
    },
39
62
    
40
 
    _grabActor : function (actor) {
41
 
        Clutter.grab_pointer(actor);
42
 
 
43
 
        // We intercept motion and button-release events so that when
44
 
        // you release after dragging, the app doesn't see that and
45
 
        // think you just clicked. We connect to 'event' rather than
46
 
        // 'captured-event' because the capturing phase doesn't happen
47
 
        // when you've grabbed the pointer.
48
 
        this._onEventId = actor.connect('event',
49
 
                                        Lang.bind(this, this._onEvent));
50
 
    },
51
 
 
52
 
    _ungrabActor : function (actor) {
53
 
        Clutter.ungrab_pointer();
54
 
        actor.disconnect(this._onEventId);
55
 
    },
56
 
 
57
 
    _onEvent : function (actor, event) {
58
 
        if (this._dragActor) {
59
 
            if (actor != this._dragActor )
 
63
    _grabActor: function() {
 
64
        Clutter.grab_pointer(this.actor);
 
65
        this._onEventId = this.actor.connect('event',
 
66
                                             Lang.bind(this, this._onEvent));
 
67
    },
 
68
 
 
69
    _ungrabActor: function() {
 
70
        Clutter.ungrab_pointer();
 
71
        this.actor.disconnect(this._onEventId);
 
72
        this._onEventId = null;
 
73
    },
 
74
 
 
75
    _grabEvents: function() {
 
76
        Clutter.grab_pointer(_getEventHandlerActor());
 
77
        Clutter.grab_keyboard(_getEventHandlerActor());
 
78
    },
 
79
 
 
80
    _ungrabEvents: function() {
 
81
        Clutter.ungrab_pointer();
 
82
        Clutter.ungrab_keyboard();
 
83
    },
 
84
 
 
85
    _onEvent: function(actor, event) {
 
86
        // We intercept BUTTON_RELEASE event to know that the button was released in case we
 
87
        // didn't start the drag, to drop the draggable in case the drag was in progress, and
 
88
        // to complete the drag and ensure that whatever happens to be under the pointer does
 
89
        // not get triggered if the drag was cancelled with Esc.
 
90
        if (event.type() == Clutter.EventType.BUTTON_RELEASE) {
 
91
            this._buttonDown = false;
 
92
            if (this._dragInProgress) {
 
93
                return this._dragActorDropped(event);
 
94
            } else if (this._dragActor != null && !this._snapBackInProgress) {
 
95
                // Drag must have been cancelled with Esc.
 
96
                this._dragComplete();
 
97
                return true;
 
98
            } else {
 
99
                // Drag has never started.
 
100
                this._ungrabActor();
60
101
                return false;
61
 
        } else if (actor != this.actor)
62
 
            return false;
 
102
            }
 
103
        // We intercept MOTION event to figure out if the drag has started and to draw
 
104
        // this._dragActor under the pointer when dragging is in progress
 
105
        } else if (event.type() == Clutter.EventType.MOTION) {
 
106
            if (this._dragInProgress) {
 
107
                return this._updateDragPosition(event);
 
108
            } else if (this._dragActor == null) {
 
109
                return this._maybeStartDrag(event);
 
110
            }
 
111
        // We intercept KEY_PRESS event so that we can process Esc key press to cancel
 
112
        // dragging and ignore all other key presses.
 
113
        } else if (event.type() == Clutter.EventType.KEY_PRESS && this._dragInProgress) {
 
114
            let symbol = event.get_key_symbol();
 
115
            if (symbol == Clutter.Escape) {
 
116
                this._cancelDrag(event.get_time());
 
117
                return true;
 
118
            }
 
119
        }
63
120
 
64
 
        if (event.type() == Clutter.EventType.BUTTON_RELEASE)
65
 
            return this._onButtonRelease(actor, event);
66
 
        else if (event.type() == Clutter.EventType.MOTION)
67
 
            return this._onMotion(actor, event);
68
 
        else
69
 
            return false;
 
121
        return false;
70
122
    },
71
123
 
72
124
    /**
73
125
     * startDrag:
74
 
     * @actor: Origin actor for drag and drop
75
126
     * @stageX: X coordinate of event
76
127
     * @stageY: Y coordinate of event
77
128
     * @time: Event timestamp
80
131
     * This function is useful to call if you've specified manualMode
81
132
     * for the draggable.
82
133
     */
83
 
    startDrag: function (actor, stageX, stageY, time) {
 
134
    startDrag: function (stageX, stageY, time) {
 
135
        currentDraggable = this;
 
136
        this._dragInProgress = true;
 
137
 
84
138
        this.emit('drag-begin', time);
 
139
        if (this._onEventId)
 
140
            this._ungrabActor();
 
141
        this._grabEvents();
85
142
 
86
143
        this._dragStartX = stageX;
87
144
        this._dragStartY = stageY;
112
169
                this._dragActorSource = this.actor;
113
170
            }
114
171
            this._dragOrigParent = undefined;
115
 
            if (this._haveSourceGrab) {
116
 
                this._haveSourceGrab = false;
117
 
                this._ungrabActor(actor);
118
 
            }
119
 
            this._grabActor(this._dragActor);
120
172
 
121
173
            this._dragOffsetX = this._dragActor.x - this._dragStartX;
122
174
            this._dragOffsetY = this._dragActor.y - this._dragStartY;
123
175
        } else {
124
 
            this._dragActor = actor;
 
176
            this._dragActor = this.actor;
125
177
            this._dragActorSource = undefined;
126
 
            this._dragOrigParent = actor.get_parent();
 
178
            this._dragOrigParent = this.actor.get_parent();
127
179
            this._dragOrigX = this._dragActor.x;
128
180
            this._dragOrigY = this._dragActor.y;
129
181
            this._dragOrigScale = this._dragActor.scale_x;
130
182
 
131
 
            let [actorStageX, actorStageY] = actor.get_transformed_position();
 
183
            let [actorStageX, actorStageY] = this.actor.get_transformed_position();
132
184
            this._dragOffsetX = actorStageX - this._dragStartX;
133
185
            this._dragOffsetY = actorStageY - this._dragStartY;
134
186
 
135
187
            // Set the actor's scale such that it will keep the same
136
188
            // transformed size when it's reparented to the stage
137
 
            let [scaledWidth, scaledHeight] = actor.get_transformed_size();
138
 
            actor.set_scale(scaledWidth / actor.width,
139
 
                           scaledHeight / actor.height);
 
189
            let [scaledWidth, scaledHeight] = this.actor.get_transformed_size();
 
190
            this.actor.set_scale(scaledWidth / this.actor.width,
 
191
                                 scaledHeight / this.actor.height);
140
192
        }
141
193
 
142
 
        this._dragActor.reparent(actor.get_stage());
 
194
        this._dragActor.reparent(this.actor.get_stage());
143
195
        this._dragActor.raise_top();
144
196
    },
145
197
 
146
 
    _onMotion : function (actor, event) {
 
198
    _maybeStartDrag:  function(event) {
147
199
        let [stageX, stageY] = event.get_coords();
148
200
 
149
 
        // If we haven't begun a drag, see if the user has moved the
150
 
        // mouse enough to trigger a drag
 
201
        // See if the user has moved the mouse enough to trigger a drag
151
202
        let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
152
 
        if (!this._dragActor &&
153
 
            (Math.abs(stageX - this._dragStartX) > threshold ||
 
203
        if ((Math.abs(stageX - this._dragStartX) > threshold ||
154
204
             Math.abs(stageY - this._dragStartY) > threshold)) {
155
 
                this.startDrag(actor, stageX, stageY, event.get_time());
 
205
                this.startDrag(stageX, stageY, event.get_time());
 
206
                this._updateDragPosition(event);
156
207
        }
157
208
 
 
209
        return true;
 
210
    },
 
211
 
 
212
    _updateDragPosition : function (event) {
 
213
        let [stageX, stageY] = event.get_coords();
 
214
 
158
215
        // If we are dragging, update the position
159
216
        if (this._dragActor) {
160
217
            this._dragActor.set_position(stageX + this._dragOffsetX,
161
218
                                         stageY + this._dragOffsetY);
162
219
            // Because we want to find out what other actor is located at the current position of this._dragActor,
163
220
            // we have to temporarily hide this._dragActor.
164
 
            this._dragActor.hide(); 
165
 
            let target = actor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
166
 
                                                            stageX + this._dragOffsetX,
167
 
                                                            stageY + this._dragOffsetY);
 
221
            this._dragActor.hide();
 
222
            let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
 
223
                                                                      stageX + this._dragOffsetX,
 
224
                                                                      stageY + this._dragOffsetY);
168
225
            this._dragActor.show();
169
226
            while (target) {
170
227
                if (target._delegate && target._delegate.handleDragOver) {
172
229
                    // We currently loop through all parents on drag-over even if one of the children has handled it.
173
230
                    // We can check the return value of the function and break the loop if it's true if we don't want
174
231
                    // to continue checking the parents.
175
 
                    target._delegate.handleDragOver(this.actor._delegate, actor,
 
232
                    target._delegate.handleDragOver(this.actor._delegate, this._dragActor,
176
233
                                                    (stageX + this._dragOffsetX - targX) / target.scale_x,
177
234
                                                    (stageY + this._dragOffsetY - targY) / target.scale_y,
178
235
                                                    event.get_time());
184
241
        return true;
185
242
    },
186
243
 
187
 
    _onButtonRelease : function (actor, event) {
188
 
        this._ungrabActor(actor);
189
 
 
190
 
        let dragging = (actor == this._dragActor);
191
 
        this._dragActor = undefined;
192
 
 
193
 
        if (!dragging)
194
 
            return false;
195
 
 
196
 
        // Find a drop target
197
 
        actor.hide();
 
244
    _dragActorDropped: function(event) {
 
245
        // Find a drop target. Because we want to find out what other actor is located at
 
246
        // the current position of this._dragActor, we have to temporarily hide this._dragActor.
 
247
        this._dragActor.hide();
198
248
        let [dropX, dropY] = event.get_coords();
199
 
        let target = actor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
200
 
                                                        dropX, dropY);
201
 
        actor.show();
 
249
        let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
 
250
                                                                  dropX, dropY);
 
251
        this._dragActor.show();
202
252
        while (target) {
203
253
            if (target._delegate && target._delegate.acceptDrop) {
204
254
                let [targX, targY] = target.get_transformed_position();
205
 
                if (target._delegate.acceptDrop(this.actor._delegate, actor,
 
255
                if (target._delegate.acceptDrop(this.actor._delegate, this._dragActor,
206
256
                                                (dropX - targX) / target.scale_x,
207
257
                                                (dropY - targY) / target.scale_y,
208
258
                                                event.get_time())) {
209
259
                    // If it accepted the drop without taking the actor,
210
260
                    // destroy it.
211
 
                    if (actor.get_parent() == actor.get_stage())
212
 
                        actor.destroy();
 
261
                    if (this._dragActor.get_parent() == this._dragActor.get_stage())
 
262
                        this._dragActor.destroy();
213
263
 
 
264
                    this._dragInProgress = false;
214
265
                    this.emit('drag-end', event.get_time(), true);
 
266
                    this._dragComplete();
215
267
                    return true;
216
268
                }
217
269
            }
218
270
            target = target.get_parent();
219
271
        }
220
272
 
 
273
        this._cancelDrag(event.get_time());
 
274
 
 
275
        return true;
 
276
    },
 
277
 
 
278
    _cancelDrag: function(eventTime) {
 
279
        this._dragInProgress = false;
221
280
        // Snap back to the actor source if the source is still around, snap back 
222
281
        // to the original location if the actor itself was being dragged or the
223
282
        // source is no longer around.
227
286
            [snapBackX, snapBackY] = this._dragActorSource.get_transformed_position();
228
287
        }
229
288
 
 
289
        this._snapBackInProgress = true;
230
290
        // No target, so snap back
231
 
        Tweener.addTween(actor,
 
291
        Tweener.addTween(this._dragActor,
232
292
                         { x: snapBackX,
233
293
                           y: snapBackY,
234
294
                           time: SNAP_BACK_ANIMATION_TIME,
235
295
                           transition: "easeOutQuad",
236
296
                           onComplete: this._onSnapBackComplete,
237
297
                           onCompleteScope: this,
238
 
                           onCompleteParams: [actor, event.get_time()]
 
298
                           onCompleteParams: [this._dragActor, eventTime]
239
299
                         });
240
 
        return true;
241
300
    },
242
301
 
243
302
    _onSnapBackComplete : function (dragActor, eventTime) {
249
308
            dragActor.destroy();
250
309
        }
251
310
        this.emit('drag-end', eventTime, false);
 
311
 
 
312
        this._snapBackInProgress = false;
 
313
        if (!this._buttonDown)
 
314
            this._dragComplete();
 
315
    },
 
316
 
 
317
    _dragComplete: function() {
 
318
        this._dragActor = undefined;
 
319
        currentDraggable = null;
 
320
        this._ungrabEvents();
252
321
    }
253
322
};
254
323