~and471/+junk/do-with-docky

« back to all changes in this revision

Viewing changes to Do.Interface.Linux.Docky/src/Docky.Interface/DnDTracker.cs

  • Committer: rugby471 at gmail
  • Date: 2010-10-15 16:08:38 UTC
  • Revision ID: rugby471@gmail.com-20101015160838-z9m3utbf7bxzb5ty
reverted to before docky removal

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//  
 
2
//  Copyright (C) 2009 GNOME Do
 
3
// 
 
4
//  This program is free software: you can redistribute it and/or modify
 
5
//  it under the terms of the GNU General Public License as published by
 
6
//  the Free Software Foundation, either version 3 of the License, or
 
7
//  (at your option) any later version.
 
8
// 
 
9
//  This program is distributed in the hope that it will be useful,
 
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
//  GNU General Public License for more details.
 
13
// 
 
14
//  You should have received a copy of the GNU General Public License
 
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
// 
 
17
 
 
18
using System;
 
19
using System.Collections.Generic;
 
20
using System.Collections.ObjectModel;
 
21
using System.Linq;
 
22
using System.Text.RegularExpressions;
 
23
 
 
24
using Gdk;
 
25
using Gtk;
 
26
 
 
27
using Docky.Core;
 
28
using Docky.Utilities;
 
29
 
 
30
using Do.Interface;
 
31
 
 
32
namespace Docky.Interface
 
33
{
 
34
        public enum DragEdge {
 
35
                None = 0,
 
36
                Top,
 
37
                Left,
 
38
                Right,
 
39
        }
 
40
        
 
41
        public class DnDTracker : IDisposable
 
42
        {
 
43
                public event EventHandler DrawRequired;
 
44
                public event EventHandler DragEnded;
 
45
                
 
46
                const int DragHotZoneSize = 8;
 
47
 
 
48
                public bool DragResizing { get; private set; }
 
49
 
 
50
                public DragEdge DragEdge { get; private set; }
 
51
 
 
52
                public DragState DragState { get; set; }
 
53
 
 
54
                public bool GtkDragging { get; set; }
 
55
                
 
56
                public bool PreviewIsDesktopFile { get; set; }
 
57
                
 
58
                bool gtk_drag_source_set;
 
59
                
 
60
                int drag_start_icon_size;
 
61
                
 
62
                DockArea parent;
 
63
                
 
64
                Gdk.Point drag_start_point;
 
65
                
 
66
                Gdk.CursorType cursor_type;
 
67
 
 
68
                Gdk.Window drag_proxy;
 
69
                
 
70
                Gdk.DragContext drag_context;
 
71
 
 
72
                IEnumerable<string> uri_list;
 
73
                
 
74
                Gdk.Point Cursor {
 
75
                        get { return CursorTracker.Cursor; }
 
76
                }
 
77
                
 
78
                Gdk.Rectangle MinimumDockArea {
 
79
                        get { return parent.MinimumDockArea; }
 
80
                }
 
81
                
 
82
                ReadOnlyCollection<AbstractDockItem> DockItems { 
 
83
                        get { return DockServices.ItemsService.DockItems; } 
 
84
                }
 
85
                
 
86
                ItemPositionProvider PositionProvider { get; set; }
 
87
                
 
88
                CursorTracker CursorTracker { get; set; }
 
89
                
 
90
                AbstractDockItem CurrentDockItem {
 
91
                        get {
 
92
                                try { return DockItems [PositionProvider.IndexAtPosition (Cursor)]; }
 
93
                                catch { return null; }
 
94
                        }
 
95
                }
 
96
                
 
97
                bool CursorNearTopDraggableEdge {
 
98
                        get {
 
99
                                return MinimumDockArea.Contains (Cursor) && CurrentDockItem is SeparatorItem;
 
100
                        }
 
101
                }
 
102
                
 
103
                bool CursorNearLeftEdge {
 
104
                        get {
 
105
                                return parent.CursorIsOverDockArea && Math.Abs (Cursor.X - MinimumDockArea.X) < DragHotZoneSize;
 
106
                        }
 
107
                }
 
108
                
 
109
                bool CursorNearRightEdge {
 
110
                        get {
 
111
                                return parent.CursorIsOverDockArea && Math.Abs (Cursor.X - (MinimumDockArea.X + MinimumDockArea.Width)) < DragHotZoneSize;
 
112
                        }
 
113
                }
 
114
                
 
115
                bool CursorNearDraggableEdge {
 
116
                        get {
 
117
                                return CursorNearTopDraggableEdge || 
 
118
                                           CursorNearRightEdge || 
 
119
                                           CursorNearLeftEdge;
 
120
                        }
 
121
                }
 
122
                
 
123
                public bool InternalDragActive {
 
124
                        get { return DragResizing && drag_start_point != Cursor; }
 
125
                }
 
126
                
 
127
                DragEdge CurrentDragEdge {
 
128
                        get {
 
129
                                if (CursorNearTopDraggableEdge)
 
130
                                        return DragEdge.Top;
 
131
                                else if (CursorNearLeftEdge)
 
132
                                        return DragEdge.Left;
 
133
                                else if (CursorNearRightEdge)
 
134
                                        return DragEdge.Right;
 
135
                                return DragEdge.None;
 
136
                        }
 
137
                }
 
138
                
 
139
                IEnumerable<Gdk.Window> WindowStack {
 
140
                        get {
 
141
                                try {
 
142
                                        return parent.Screen.WindowStack;
 
143
                                } catch { 
 
144
                                        try {
 
145
                                                return Wnck.Screen.Default.WindowsStacked.Select (wnk => Gdk.Window.ForeignNew ((uint) wnk.Xid));
 
146
                                        } catch {
 
147
                                                return null;
 
148
                                        }
 
149
                                }
 
150
                        }
 
151
                }
 
152
                
 
153
                internal DnDTracker (DockArea parent, ItemPositionProvider position, CursorTracker cursorTracker)
 
154
                {
 
155
                        this.parent = parent;
 
156
                        cursor_type = CursorType.LeftPtr;
 
157
                        PositionProvider = position;
 
158
                        CursorTracker = cursorTracker;
 
159
 
 
160
                        DragState = new DragState (Cursor, null);
 
161
                        DragState.IsFinished = true;
 
162
                        
 
163
                        RegisterEvents ();
 
164
                        
 
165
                        RegisterGtkDragDest ();
 
166
                        RegisterGtkDragSource ();
 
167
                }
 
168
                
 
169
                public void Enable ()
 
170
                {
 
171
                        RegisterGtkDragSource ();
 
172
                }
 
173
                
 
174
                public void Disable ()
 
175
                {
 
176
                        UnregisterGtkDragSource ();
 
177
                }
 
178
                
 
179
                void RegisterEvents ()
 
180
                {
 
181
                        parent.DragDataReceived  += HandleDragDataReceived;
 
182
                        parent.DragMotion        += HandleDragMotionEvent; 
 
183
                        parent.DragBegin         += HandleDragBegin;
 
184
                        parent.DragEnd           += HandleDragEnd; 
 
185
                        parent.DragDrop          += HandleDragDrop;
 
186
                        parent.DragFailed        += HandleDragFailed; 
 
187
                        parent.ButtonPressEvent  += HandleButtonPressEvent;
 
188
                        parent.MotionNotifyEvent += HandleMotionNotifyEvent;
 
189
                        parent.ButtonReleaseEvent += HandleButtonReleaseEvent; 
 
190
                        
 
191
                        CursorTracker.CursorUpdated += HandleCursorUpdated; 
 
192
                }
 
193
 
 
194
                void UnregisterEvents ()
 
195
                {
 
196
                        parent.DragDataReceived  -= HandleDragDataReceived;
 
197
                        parent.DragMotion        -= HandleDragMotionEvent; 
 
198
                        parent.DragBegin         -= HandleDragBegin;
 
199
                        parent.DragEnd           -= HandleDragEnd; 
 
200
                        parent.DragDrop          -= HandleDragDrop;
 
201
                        parent.DragFailed        -= HandleDragFailed; 
 
202
                        parent.ButtonPressEvent  -= HandleButtonPressEvent; 
 
203
                        parent.MotionNotifyEvent -= HandleMotionNotifyEvent;
 
204
                        parent.ButtonReleaseEvent -= HandleButtonReleaseEvent;
 
205
                        
 
206
                        CursorTracker.CursorUpdated -= HandleCursorUpdated; 
 
207
                }
 
208
                
 
209
                void HandleMotionNotifyEvent(object o, MotionNotifyEventArgs args)
 
210
                {
 
211
                        GtkDragging = false;
 
212
                }
 
213
 
 
214
                void HandleButtonPressEvent(object o, ButtonPressEventArgs args)
 
215
                {
 
216
                        if (CursorNearDraggableEdge)
 
217
                                StartDrag ();
 
218
                }
 
219
 
 
220
                void HandleButtonReleaseEvent(object o, ButtonReleaseEventArgs args)
 
221
                {
 
222
                        if (DragResizing)
 
223
                                EndDrag ();
 
224
                }
 
225
                
 
226
                void HandleCursorUpdated(object sender, CursorUpdatedArgs e)
 
227
                {
 
228
                        if (GtkDragging && (CursorTracker.ModifierType & ModifierType.Button1Mask) != ModifierType.Button1Mask) {
 
229
                                GtkDragging = false;
 
230
                        }
 
231
                        
 
232
                        SetDragProxy ();
 
233
                        
 
234
                        ConfigureCursor ();
 
235
                        
 
236
                        if (DragResizing)
 
237
                                HandleDragMotion ();
 
238
                }
 
239
 
 
240
                void HandleDragFailed (object o, DragFailedArgs args)
 
241
                {
 
242
                        // disable the animation
 
243
                        args.RetVal = parent.CursorIsOverDockArea || DockServices.ItemsService.ItemCanBeRemoved (DockItems.IndexOf (DragState.DragItem));
 
244
                }
 
245
 
 
246
                void HandleDragDrop (object o, DragDropArgs args)
 
247
                {
 
248
                        int index = PositionProvider.IndexAtPosition (Cursor);
 
249
                        if (parent.CursorIsOverDockArea && index >= 0 && index < DockItems.Count && uri_list != null) {
 
250
                                foreach (string uri in uri_list) {
 
251
                                        if (CurrentDockItem != null && CurrentDockItem.IsAcceptingDrops && !uri.EndsWith (".desktop")) {
 
252
                                                CurrentDockItem.ReceiveItem (uri);
 
253
                                        } else {
 
254
                                                Gdk.Point center = PositionProvider.IconUnzoomedPosition (index);
 
255
                                                if (center.X < Cursor.X && index < DockItems.Count - 1)
 
256
                                                        index++;
 
257
                                                DockServices.ItemsService.AddItemToDock (uri, index);
 
258
                                        }
 
259
                                }
 
260
                        }
 
261
                        
 
262
                        Gtk.Drag.Finish (args.Context, true, true, args.Time);
 
263
                        args.RetVal = true;
 
264
                }
 
265
 
 
266
                void HandleDragEnd (object o, DragEndArgs args)
 
267
                {
 
268
                        if (parent.CursorIsOverDockArea) {
 
269
                                int currentPosition = PositionProvider.IndexAtPosition (Cursor);
 
270
                                if (currentPosition != -1)
 
271
                                        DockServices.ItemsService.DropItemOnPosition (DragState.DragItem, currentPosition);
 
272
                        } else {
 
273
                                bool result = DockServices.ItemsService.RemoveItem (DragState.DragItem);
 
274
                                if (result) {
 
275
                                        PoofWindow poof = new PoofWindow (DockPreferences.FullIconSize);
 
276
                                        poof.SetCenterPosition (CursorTracker.RootCursor);
 
277
                                        poof.Run ();
 
278
                                }
 
279
                        }
 
280
                        
 
281
                        DragState.IsFinished = true;
 
282
                        GtkDragging = false;
 
283
                        SetDragProxy ();
 
284
                
 
285
                        OnDragEnded ();
 
286
                        OnDrawRequired ();
 
287
                        args.RetVal = true;
 
288
                }
 
289
 
 
290
                void HandleDragBegin (object o, DragBeginArgs args)
 
291
                {
 
292
                        GtkDragging = true;
 
293
                        // the user might not end the drag on the same horizontal position they start it on
 
294
                        int item = PositionProvider.IndexAtPosition (Cursor);
 
295
 
 
296
                        if (item != -1 && DockServices.ItemsService.ItemCanBeMoved (item))
 
297
                                DragState = new DragState (Cursor, DockItems [item]);
 
298
                        else
 
299
                                DragState = new DragState (Cursor, null);
 
300
                        
 
301
                        Gdk.Pixbuf pbuf;
 
302
                        if (DragState.DragItem == null) {
 
303
                                pbuf = IconProvider.PixbufFromIconName ("gtk-remove", DockPreferences.IconSize);
 
304
                        } else {
 
305
                                pbuf = DragState.DragItem.GetDragPixbuf ();
 
306
                        }
 
307
                                
 
308
                        if (pbuf != null)
 
309
                                Gtk.Drag.SetIconPixbuf (args.Context, pbuf, pbuf.Width / 2, pbuf.Height / 2);
 
310
                        args.RetVal = true;
 
311
                }
 
312
 
 
313
                void HandleDragMotionEvent (object o, DragMotionArgs args)
 
314
                {
 
315
                        GtkDragging = true;
 
316
                        
 
317
                        do {
 
318
                                if (DragState.DragItem == null || DragState.IsFinished || 
 
319
                                    !DockItems.Contains (DragState.DragItem) || !parent.CursorIsOverDockArea)
 
320
                                        continue;
 
321
                                
 
322
                                int draggedPosition = DockItems.IndexOf (DragState.DragItem);
 
323
                                int currentPosition = PositionProvider.IndexAtPosition (Cursor);
 
324
                                if (draggedPosition == currentPosition || currentPosition == -1)
 
325
                                        continue;
 
326
                                
 
327
                                DockServices.ItemsService.MoveItemToPosition (draggedPosition, currentPosition);
 
328
                        } while (false);
 
329
                        
 
330
                        OnDrawRequired ();
 
331
                        
 
332
                        if (drag_context != args.Context) {
 
333
                                Gdk.Atom target = Gtk.Drag.DestFindTarget (parent, args.Context, null);
 
334
                                if (target != null)
 
335
                                        Gtk.Drag.GetData (parent, args.Context, target, Gtk.Global.CurrentEventTime);
 
336
                                drag_context = args.Context;
 
337
                                
 
338
                        }
 
339
                        
 
340
                        Gdk.Drag.Status (args.Context, DragAction.Copy, args.Time);
 
341
                        args.RetVal = true;
 
342
                }
 
343
 
 
344
                void HandleDragDataReceived (object o, DragDataReceivedArgs args)
 
345
                {
 
346
                        IEnumerable<string> uriList;
 
347
                        try {
 
348
                                string data = System.Text.Encoding.UTF8.GetString (args.SelectionData.Data);
 
349
                                data = System.Uri.UnescapeDataString (data);
 
350
                                //sometimes we get a null at the end, and it crashes us
 
351
                                data = data.TrimEnd ('\0'); 
 
352
                                
 
353
                                uriList = Regex.Split (data, "\r\n")
 
354
                                        .Where (uri => uri.StartsWith ("file://"))
 
355
                                        .Select (uri => uri.Substring ("file://".Length));
 
356
                        } catch {
 
357
                                uriList = Enumerable.Empty<string> ();
 
358
                        }
 
359
                        
 
360
                        uri_list = uriList;
 
361
                        PreviewIsDesktopFile = !uriList.Any () || uriList.Any (s => s.EndsWith (".desktop"));
 
362
                        args.RetVal = true;
 
363
                }
 
364
                
 
365
                
 
366
                void RegisterGtkDragSource ()
 
367
                {
 
368
                        gtk_drag_source_set = true;
 
369
                        // we dont really want to offer the drag to anything, merely pretend to, so we set a mimetype nothing takes
 
370
                        TargetEntry te = new TargetEntry ("nomatch", TargetFlags.App | TargetFlags.OtherApp, 0);
 
371
                        Gtk.Drag.SourceSet (parent, Gdk.ModifierType.Button1Mask, new [] {te}, DragAction.Copy);
 
372
                }
 
373
                
 
374
                void RegisterGtkDragDest ()
 
375
                {
 
376
                        TargetEntry dest_te = new TargetEntry ("text/uri-list", 0, 0);
 
377
                        Gtk.Drag.DestSet (parent, 0, new [] {dest_te}, Gdk.DragAction.Copy);
 
378
                }
 
379
                
 
380
                void UnregisterGtkDragSource ()
 
381
                {
 
382
                        gtk_drag_source_set = false;
 
383
                        Gtk.Drag.SourceUnset (parent);
 
384
                }
 
385
 
 
386
                void SetDragProxy ()
 
387
                {
 
388
                        if ((CursorTracker.ModifierType & ModifierType.Button1Mask) != ModifierType.Button1Mask || parent.CursorIsOverDockArea) {
 
389
                                if (drag_proxy == null)
 
390
                                        return;
 
391
                                drag_proxy = null;
 
392
                                RegisterGtkDragDest ();
 
393
                        } else {
 
394
                                Gdk.Point local_cursor = CursorTracker.RootCursor;
 
395
        
 
396
                                IEnumerable<Gdk.Window> windows = WindowStack;
 
397
 
 
398
                                foreach (Gdk.Window w in windows.Reverse ()) {
 
399
                                        if (w == null || w == DockWindow.Window.GdkWindow || !w.IsVisible)
 
400
                                                continue;
 
401
                                        
 
402
                                        Gdk.Rectangle rect;
 
403
                                        int depth;
 
404
                                        w.GetGeometry (out rect.X, out rect.Y, out rect.Width, out rect.Height, out depth);
 
405
                                        if (rect.Contains (local_cursor)) {
 
406
                                                if (w == drag_proxy)
 
407
                                                        break;
 
408
                                                
 
409
                                                drag_proxy = w;
 
410
                                                Gtk.Drag.DestSetProxy (parent, w, DragProtocol.Xdnd, true);
 
411
                                                break;
 
412
                                        }
 
413
                                }
 
414
                        }
 
415
                }
 
416
 
 
417
                void ConfigureCursor ()
 
418
                {
 
419
                        // we do this so that our custom drag isn't destroyed by gtk's drag
 
420
                        if (gtk_drag_source_set && CursorNearDraggableEdge) {
 
421
                                UnregisterGtkDragSource ();
 
422
 
 
423
                                if (cursor_type != CursorType.SbVDoubleArrow && CursorNearTopDraggableEdge) {
 
424
                                        SetCursor (CursorType.SbVDoubleArrow);
 
425
                                        
 
426
                                } else if (cursor_type != CursorType.LeftSide && CursorNearLeftEdge) {
 
427
                                        SetCursor (CursorType.LeftSide);
 
428
                                        
 
429
                                } else if (cursor_type != CursorType.RightSide && CursorNearRightEdge) {
 
430
                                        SetCursor (CursorType.RightSide);
 
431
                                }
 
432
                                
 
433
                        } else if (!gtk_drag_source_set && !DragResizing && !CursorNearDraggableEdge) {
 
434
                                if (!parent.PainterOverlayVisible)
 
435
                                        RegisterGtkDragSource ();
 
436
                                if (cursor_type != CursorType.LeftPtr)
 
437
                                        SetCursor (CursorType.LeftPtr);
 
438
                        }
 
439
                }
 
440
                
 
441
                void SetCursor (Gdk.CursorType type)
 
442
                {
 
443
                        cursor_type = type;
 
444
                        Gdk.Cursor tmp_cursor = new Gdk.Cursor (type);
 
445
                        parent.GdkWindow.Cursor = tmp_cursor;
 
446
                        tmp_cursor.Dispose ();
 
447
                }
 
448
 
 
449
                void StartDrag ()
 
450
                {
 
451
                        if (parent.PainterOverlayVisible) return;
 
452
                        
 
453
                        drag_start_point = Cursor;
 
454
                        drag_start_icon_size = DockPreferences.IconSize;
 
455
                        DragResizing = true;
 
456
                        DragEdge = CurrentDragEdge;
 
457
                }
 
458
                
 
459
                void EndDrag ()
 
460
                {
 
461
                        if (parent.PainterOverlayVisible) return;
 
462
                        
 
463
                        DragEdge = DragEdge.None;
 
464
                        DragResizing = false;
 
465
                        
 
466
                        OnDragEnded ();
 
467
                }
 
468
                
 
469
                void HandleDragMotion ()
 
470
                {
 
471
                        int movement = 0;
 
472
                        switch (DragEdge) {
 
473
                        case DragEdge.Top:
 
474
                                int delta = drag_start_point.Y - Cursor.Y;
 
475
                                if (DockPreferences.Orientation == DockOrientation.Top)
 
476
                                        delta = 0 - delta;
 
477
                                DockPreferences.IconSize = Math.Min (drag_start_icon_size + delta, DockPreferences.MaxIconSize);
 
478
                                return;
 
479
                        case DragEdge.Left:
 
480
                                movement = drag_start_point.X - Cursor.X;
 
481
                                break;
 
482
                        case DragEdge.Right:
 
483
                                movement = Cursor.X - drag_start_point.X;
 
484
                                break;
 
485
                        }
 
486
 
 
487
                        if (movement > DockPreferences.IconSize / 2 + 2) {
 
488
                                DockPreferences.AutomaticIcons++;
 
489
                        } else if (movement < 0 - (DockPreferences.IconSize / 2 + 2)) {
 
490
                                DockPreferences.AutomaticIcons--;
 
491
                        } else {
 
492
                                return;
 
493
                        }
 
494
                        
 
495
                        drag_start_point = Cursor;
 
496
                }
 
497
                
 
498
                void OnDrawRequired ()
 
499
                {
 
500
                        if (DrawRequired != null)
 
501
                                DrawRequired (this, EventArgs.Empty);
 
502
                }
 
503
                
 
504
                void OnDragEnded ()
 
505
                {
 
506
                        if (DragEnded != null)
 
507
                                DragEnded (this, EventArgs.Empty);
 
508
                }
 
509
 
 
510
                #region IDisposable implementation
 
511
                public void Dispose ()
 
512
                {
 
513
                        UnregisterEvents ();
 
514
                }
 
515
                #endregion
 
516
 
 
517
        }
 
518
}