~ubuntu-branches/ubuntu/natty/gnome-do/natty

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Iain Lane
  • Date: 2009-03-22 22:44:39 UTC
  • mto: (1.1.7 upstream) (0.1.4 squeeze)
  • mto: This revision was merged to the branch mainline in revision 14.
  • Revision ID: james.westby@ubuntu.com-20090322224439-ztmk4tbq19s3safs
ImportĀ upstreamĀ versionĀ 0.8.1.3+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// DockItemProvider.cs
2
 
// 
3
 
// Copyright (C) 2008 GNOME Do
4
 
//
5
 
// This program is free software: you can redistribute it and/or modify
6
 
// it under the terms of the GNU General Public License as published by
7
 
// the Free Software Foundation, either version 3 of the License, or
8
 
// (at your option) any later version.
9
 
//
10
 
// This program is distributed in the hope that it will be useful,
11
 
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
// GNU General Public License for more details.
14
 
//
15
 
// You should have received a copy of the GNU General Public License
16
 
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 
//
18
 
 
19
 
using System;
20
 
using System.Collections.Generic;
21
 
using System.IO;
22
 
using System.Linq;
23
 
using System.Runtime.Serialization;
24
 
using System.Runtime.Serialization.Formatters.Binary;
25
 
 
26
 
using Do;
27
 
using Do.Interface;
28
 
using Do.Universe;
29
 
using Do.Platform;
30
 
 
31
 
using Docky.Utilities;
32
 
 
33
 
using Wnck;
34
 
 
35
 
namespace Docky.Interface
36
 
{
37
 
        public enum IconSource {
38
 
                Statistics,
39
 
                Custom,
40
 
                Application,
41
 
                Unknown,
42
 
        }
43
 
        
44
 
        public delegate void UpdateRequestHandler (object sender, UpdateRequestArgs args);
45
 
        public delegate void DockItemsChangedHandler (IEnumerable<BaseDockItem> items);
46
 
        
47
 
        public class DockItemProvider : IDisposable
48
 
        {
49
 
                
50
 
                public event DockItemsChangedHandler DockItemsChanged;
51
 
                public event UpdateRequestHandler ItemNeedsUpdate;
52
 
                
53
 
                Dictionary<string, DockItem> custom_items;
54
 
                List<DockItem> statistical_items;
55
 
                List<BaseDockItem> output_items; 
56
 
                List<ApplicationDockItem> task_items;
57
 
                bool enable_serialization = true;
58
 
                bool custom_items_read;
59
 
                
60
 
                string DesktopFilesPath {
61
 
                        get {
62
 
                                return Path.Combine (Services.Paths.UserDataDirectory, "dock_desktop_files");
63
 
                        }
64
 
                }
65
 
                
66
 
                string SortDictionaryPath {
67
 
                        get { 
68
 
                                return Path.Combine (Services.Paths.UserDataDirectory, "dock_sort_dictionary");
69
 
                        }
70
 
                }
71
 
                
72
 
                int LastPosition {
73
 
                        get {
74
 
                                if (!DragableItems.Any ())
75
 
                                        return 0;
76
 
                                //TODO make sane once mono 1.9 support is dropped
77
 
                                return DragableItems.Max ((Func<DockItem, int>) (di => di.Position));
78
 
                        }
79
 
                }
80
 
                
81
 
                BaseDockItem Separator { get; set; }
82
 
                BaseDockItem MenuItem { get; set; }
83
 
                BaseDockItem TrashItem { get; set; }
84
 
                
85
 
                IEnumerable<DockItem> DragableItems {
86
 
                        get { return statistical_items.Concat (custom_items.Values).OrderBy (di => di.Position); }
87
 
                }
88
 
                
89
 
                public bool UpdatesEnabled { get; set; }
90
 
                
91
 
                public List<BaseDockItem> DockItems {
92
 
                        get {
93
 
                                if (output_items == null) {
94
 
                                        output_items = new List<BaseDockItem> ();
95
 
                                        output_items.Add (MenuItem);
96
 
                                        output_items.AddRange (DragableItems.Cast<BaseDockItem> ());
97
 
                                
98
 
                                        if (task_items.Any () || DockPreferences.ShowTrash)
99
 
                                                output_items.Add (Separator);
100
 
                                        
101
 
                                        if (task_items.Any ()) {
102
 
                                                output_items.AddRange (task_items.Cast<BaseDockItem> ());
103
 
                                        }
104
 
                                
105
 
                                        if (DockPreferences.ShowTrash)
106
 
                                                output_items.Add (TrashItem);
107
 
                                }
108
 
                                return output_items;
109
 
                        }
110
 
                }
111
 
                
112
 
                public DockItemProvider ()
113
 
                {
114
 
                        Separator = new SeparatorItem ();
115
 
                        MenuItem = new DoDockItem ();
116
 
                        TrashItem = new TrashDockItem ();
117
 
                        
118
 
                        custom_items = new Dictionary<string, DockItem> ();
119
 
                        statistical_items = new List<DockItem> ();
120
 
                        task_items = new List<ApplicationDockItem> ();
121
 
                        
122
 
                        RegisterEvents ();
123
 
                }
124
 
                
125
 
                void RegisterEvents ()
126
 
                {
127
 
                        Services.Core.UniverseInitialized += OnUniverseInitialized;
128
 
                        Wnck.Screen.Default.WindowClosed += OnWindowClosed;
129
 
                        Wnck.Screen.Default.WindowOpened += OnWindowOpened;
130
 
                        DockPreferences.TrashVisibilityChanged += OnDockItemsChanged;
131
 
                        DockPreferences.AutomaticIconsChanged += UpdateItems;
132
 
                }
133
 
                
134
 
                void UnregisterEvents ()
135
 
                {
136
 
                        Services.Core.UniverseInitialized -= OnUniverseInitialized;
137
 
                        Wnck.Screen.Default.WindowClosed -= OnWindowClosed;
138
 
                        Wnck.Screen.Default.WindowOpened -= OnWindowOpened;
139
 
                        DockPreferences.TrashVisibilityChanged -= OnDockItemsChanged;
140
 
                        DockPreferences.AutomaticIconsChanged -= UpdateItems;
141
 
                }
142
 
                
143
 
                private void OnWindowClosed (object o, WindowClosedArgs args) 
144
 
                {
145
 
                        if (args.Window.IsSkipTasklist)
146
 
                                        return;
147
 
                        UpdateItems ();
148
 
                }
149
 
                
150
 
                private void OnWindowOpened (object o, WindowOpenedArgs args) 
151
 
                {
152
 
                        if (args.Window.IsSkipTasklist)
153
 
                                        return;
154
 
                        UpdateItems ();
155
 
                }
156
 
                
157
 
                public void AddCustomItem (Element item)
158
 
                {
159
 
                        if (!(item is Item)) {
160
 
                                Log<DockItemProvider>.Error ("Could not add {0} to custom items for dock", item.Safe.Name);
161
 
                                return;
162
 
                        }
163
 
                        string id = item.UniqueId;
164
 
                        if (custom_items.ContainsKey (id))
165
 
                                return;
166
 
                        
167
 
                        DockItem di = new DockItem (item as Item);
168
 
                        di.RemoveClicked += HandleRemoveClicked;
169
 
                        di.UpdateNeeded += HandleUpdateNeeded;
170
 
                        di.Position = LastPosition + 1;
171
 
                        custom_items [id] = di;
172
 
                        
173
 
                        UpdateStatisticalItems ();
174
 
                        OnDockItemsChanged ();
175
 
                        
176
 
                        if (enable_serialization)
177
 
                                SerializeData ();
178
 
                }
179
 
                
180
 
                public void AddCustomItem (string identifier)
181
 
                {
182
 
                        if (custom_items.ContainsKey (identifier))
183
 
                                return;
184
 
                        
185
 
                        DockItem customItem = GetCustomItem (identifier);
186
 
                        
187
 
                        if (customItem != null) {
188
 
                                customItem.RemoveClicked += HandleRemoveClicked;
189
 
                                customItem.UpdateNeeded += HandleUpdateNeeded;
190
 
                                customItem.Position = LastPosition + 1;
191
 
                                custom_items [identifier] = customItem;
192
 
                        }
193
 
                        
194
 
                        UpdateStatisticalItems ();
195
 
                        OnDockItemsChanged ();
196
 
                        
197
 
                        if (enable_serialization)
198
 
                                SerializeData ();
199
 
                }
200
 
                
201
 
                DockItem GetCustomItem (string identifier)
202
 
                {
203
 
                        DockItem customItem = null;
204
 
                        
205
 
                        if (identifier.StartsWith ("file://"))
206
 
                                identifier = identifier.Substring ("file://".Length);
207
 
                        
208
 
                        if (File.Exists (identifier) || Directory.Exists (identifier)) {
209
 
                                if (identifier.EndsWith (".desktop")) {
210
 
                                        Item o = Services.UniverseFactory.NewApplicationItem (identifier) as Item;
211
 
                                        customItem = new DockItem (o);
212
 
                                } else {
213
 
                                        Item o = Services.UniverseFactory.NewFileItem (identifier) as Item;
214
 
                                        customItem = new DockItem (o);
215
 
                                }
216
 
                        } else {
217
 
                                Item e = Services.Core.GetElement (identifier) as Item;
218
 
                                if (e != null)
219
 
                                        customItem = new DockItem (e);
220
 
                                else
221
 
                                        Log<DockItemProvider>.Error ("Could not add custom item with id: {0}", identifier);
222
 
                        }
223
 
                        return customItem;
224
 
                }
225
 
                
226
 
                public bool ItemCanBeMoved (int item)
227
 
                {
228
 
                        if (DockItems [item] is DockItem) {
229
 
                                return DragableItems.Contains (DockItems [item] as DockItem);
230
 
                        }
231
 
                        return false;
232
 
                }
233
 
                
234
 
                public void MoveItemToPosition (int item, int position)
235
 
                {
236
 
                        if (item == position)
237
 
                                return;
238
 
                        
239
 
                        IconSource itemSource = GetIconSource (DockItems [item]);
240
 
                        IconSource targetSource = GetIconSource (DockItems [position]);
241
 
                        
242
 
                        if (itemSource == IconSource.Application || itemSource == IconSource.Unknown)
243
 
                                return;
244
 
                        
245
 
                        if (targetSource == IconSource.Application || targetSource == IconSource.Unknown) {
246
 
                                if (DockItems [position] is TrashDockItem) {
247
 
                                        RemoveItem (item);
248
 
                                }
249
 
                                return;
250
 
                        }
251
 
                        
252
 
                        DockItem primaryItem = DockItems [item] as DockItem;
253
 
                        DockItem targetItem = DockItems [position] as DockItem;
254
 
                        
255
 
                        int startPosition = primaryItem.Position;
256
 
                        int targetPosition = targetItem.Position;
257
 
                        
258
 
                        foreach (DockItem di in DragableItems) {
259
 
                                if (startPosition < targetPosition) {
260
 
                                        // the item is being shifted to the right.  Everything greater than item up to and including target item
261
 
                                        // needs to be shifted to the left
262
 
                                        if (di.Position > startPosition && di.Position <= targetPosition)
263
 
                                                di.Position--;
264
 
                                } else {
265
 
                                        // the item is being shifted to the left.  Everthing less than the item and up to and including target item
266
 
                                        // needs to be shifted to the right
267
 
                                        if (di.Position < startPosition && di.Position >= targetPosition)
268
 
                                                di.Position++;
269
 
                                }
270
 
                        }
271
 
                        
272
 
                        primaryItem.Position = targetPosition;
273
 
                        
274
 
                        OnDockItemsChanged ();
275
 
                        SerializeData ();
276
 
                }
277
 
                
278
 
                public void ForceUpdate ()
279
 
                {
280
 
                        UpdateItems ();
281
 
                }
282
 
                
283
 
                public IconSource GetIconSource (BaseDockItem item) {
284
 
                        if (item is ApplicationDockItem && task_items.Contains (item as ApplicationDockItem))
285
 
                                return IconSource.Application;
286
 
                        
287
 
                        if (item is DockItem && statistical_items.Contains (item as DockItem))
288
 
                                return IconSource.Statistics;
289
 
                        
290
 
                        if (item is DockItem && custom_items.Values.Contains (item as DockItem))
291
 
                                return IconSource.Custom;
292
 
                        
293
 
                        return IconSource.Unknown;
294
 
                }
295
 
                
296
 
                void HandleUpdateNeeded(object sender, UpdateRequestArgs args)
297
 
                {
298
 
                        if (ItemNeedsUpdate != null)
299
 
                                ItemNeedsUpdate (this, args);
300
 
                }
301
 
 
302
 
                void HandleRemoveClicked(object sender, EventArgs e)
303
 
                {
304
 
                        for (int i=0; i<DockItems.Count; i++) {
305
 
                                if (DockItems [i] == sender) {
306
 
                                        RemoveItem (i);
307
 
                                        break;
308
 
                                }
309
 
                        }
310
 
                }
311
 
                
312
 
                IEnumerable<Item> MostUsedItems ()
313
 
                {
314
 
                        return Services.Core
315
 
                                .GetItemsOrderedByRelevance ()
316
 
                                .Where (item => item.GetType ().Name != "SelectedTextItem" && item.GetType ().Name != "GNOMETrashFileItem")
317
 
                                .Where (item => !DockPreferences.ItemBlacklist.Contains (item.UniqueId))
318
 
                                .Take (DockPreferences.AutomaticIcons)
319
 
                                .OrderByDescending (item => item is IApplicationItem)
320
 
                                .ThenBy (item => item.GetType ().Name)
321
 
                                .ThenBy (item => item.Safe.Name);
322
 
                }
323
 
                
324
 
                public bool RemoveItem (int item)
325
 
                {
326
 
                        bool ret_val = false;
327
 
                        
328
 
                        if (GetIconSource (DockItems [item]) == IconSource.Statistics) {
329
 
                                DockPreferences.AddBlacklistItem ((DockItems [item] as DockItem).Element.UniqueId);
330
 
                                DockPreferences.AutomaticIcons--;
331
 
                                UpdateItems ();
332
 
                                ret_val = true;
333
 
                        } else if (GetIconSource (DockItems [item]) == IconSource.Custom) {
334
 
                                foreach (KeyValuePair<string, DockItem> kvp in custom_items) {
335
 
                                        if (kvp.Value.Equals (DockItems [item])) {
336
 
                                                custom_items.Remove (kvp.Key);
337
 
                                                
338
 
                                                UpdateItems ();
339
 
                                                ret_val = true;
340
 
                                                break;
341
 
                                        }
342
 
                                }
343
 
                        }
344
 
                        
345
 
                        UpdateItems ();
346
 
                        if (enable_serialization)
347
 
                                SerializeData ();
348
 
                        return ret_val;
349
 
                }
350
 
                
351
 
                void SerializeData ()
352
 
                {
353
 
                        SerializeCustomItems ();
354
 
                        SerializeSortDictionary ();
355
 
                }
356
 
                
357
 
                void SerializeCustomItems ()
358
 
                {
359
 
                        try {
360
 
                                using (Stream s = File.OpenWrite (DesktopFilesPath)) {
361
 
                                        BinaryFormatter f = new BinaryFormatter ();
362
 
                                        f.Serialize (s, custom_items.Keys.ToArray ());
363
 
                                }
364
 
                        } catch (Exception e) {
365
 
                                Log<DockItemProvider>.Error ("Could not serialize custom items");
366
 
                                Log<DockItemProvider>.Error (e.Message);
367
 
                        }
368
 
                }
369
 
                
370
 
                void SerializeSortDictionary ()
371
 
                {
372
 
                        try {
373
 
                                using (Stream s = File.OpenWrite (SortDictionaryPath)) {
374
 
                                        BinaryFormatter f = new BinaryFormatter ();
375
 
                                        f.Serialize (s, DragableItems.ToDictionary (di => di.Element.UniqueId, di => di.Position));
376
 
                                }
377
 
                        } catch (Exception e) {
378
 
                                Log<DockItemProvider>.Error ("Could not serialize sort items");
379
 
                                Log<DockItemProvider>.Error (e.Message);
380
 
                        }
381
 
                }
382
 
                
383
 
                string[] DeserializeCustomItems ()
384
 
                {
385
 
                        string[] filenames;
386
 
                        try {
387
 
                                using (Stream s = File.OpenRead (DesktopFilesPath)) {
388
 
                                        BinaryFormatter f = new BinaryFormatter ();
389
 
                                        filenames = f.Deserialize (s) as string[];
390
 
                                }
391
 
                        } catch (FileNotFoundException e) {
392
 
                                Log<DockItemProvider>.Debug ("Custom items file not present, nothing to add. " + e.Message);
393
 
                                filenames = new string[0];
394
 
                        } catch {
395
 
                                Log<DockItemProvider>.Error ("Could not deserialize custom items");
396
 
                                filenames = new string[0];
397
 
                        }
398
 
                        return filenames;
399
 
                }
400
 
                
401
 
                Dictionary<string, int> DeserializeSortDictionary ()
402
 
                {
403
 
                        Dictionary<string, int> sortDictionary;
404
 
                        try {
405
 
                                using (Stream s = File.OpenRead (SortDictionaryPath)) {
406
 
                                        BinaryFormatter f = new BinaryFormatter ();
407
 
                                        sortDictionary = f.Deserialize (s) as Dictionary<string, int>;
408
 
                                }
409
 
                        } catch (FileNotFoundException e) {
410
 
                                Log<DockItemProvider>.Debug ("Sort Dictionary file not present, nothing to add. " + e.Message);
411
 
                                sortDictionary = new Dictionary<string, int> ();
412
 
                        } catch {
413
 
                                Log<DockItemProvider>.Error ("Could not deserialize sort dictionary");
414
 
                                sortDictionary = new Dictionary<string, int> ();
415
 
                        }
416
 
                        return sortDictionary;
417
 
                }
418
 
                
419
 
                void UpdateItems ()
420
 
                {
421
 
                        if (!UpdatesEnabled)
422
 
                                return;
423
 
                        
424
 
                        UpdateStatisticalItems ();
425
 
                        
426
 
                        if (!custom_items_read) {
427
 
                                enable_serialization = false;
428
 
                                foreach (string s in DeserializeCustomItems ())
429
 
                                        AddCustomItem (s);
430
 
                                enable_serialization = true;
431
 
                                
432
 
                                custom_items_read = true;
433
 
                                
434
 
                                Dictionary<string, int> sortDictionary = DeserializeSortDictionary ();
435
 
                                foreach (DockItem item in DragableItems) {
436
 
                                        if (sortDictionary.ContainsKey (item.Element.UniqueId))
437
 
                                                item.Position = sortDictionary [item.Element.UniqueId];
438
 
                                }
439
 
                        }
440
 
                        
441
 
                        UpdateWindowItems ();
442
 
                        SimplifyPositions (DragableItems);
443
 
                        OnDockItemsChanged ();
444
 
                        
445
 
                }
446
 
                
447
 
                void UpdateStatisticalItems ()
448
 
                {
449
 
                        List<DockItem> old_items = statistical_items;
450
 
                        statistical_items = new List<DockItem> ();
451
 
                        
452
 
                        IEnumerable<Item> mostUsedItems = MostUsedItems ();
453
 
                        
454
 
                        foreach (Item item in mostUsedItems) {
455
 
                                if (custom_items.Values.Any (di => di.Element == item))
456
 
                                        continue;
457
 
                                
458
 
                                if (old_items.Any (di => di.Element == item)) {
459
 
                                        statistical_items.AddRange (old_items.Where (di => di.Element == item));
460
 
                                } else {
461
 
                                        DockItem di = new DockItem (item);
462
 
                                        di.RemoveClicked += HandleRemoveClicked;
463
 
                                        di.UpdateNeeded += HandleUpdateNeeded;
464
 
                                        di.DockAddItem = DateTime.UtcNow;
465
 
                                        
466
 
                                        int position = LastPosition + 1;
467
 
 
468
 
                                        //TODO fixme once mono 1.9 support is dropped
469
 
                                        if (old_items.Any ())
470
 
                                                position += old_items.Max ((Func<DockItem, int>) (oi => oi.Position));
471
 
 
472
 
                                        di.Position = position;
473
 
                                        statistical_items.Add (di);
474
 
                                }
475
 
                        }
476
 
                        
477
 
                        foreach (DockItem item in  old_items.Where (di => !statistical_items.Contains (di))) {
478
 
                                item.RemoveClicked -= HandleRemoveClicked;
479
 
                                item.UpdateNeeded -= HandleUpdateNeeded;
480
 
                                item.Dispose ();
481
 
                        }
482
 
                }
483
 
 
484
 
                void UpdateWindowItems ()
485
 
                {
486
 
                        foreach (DockItem di in statistical_items.Concat (custom_items.Values)) {
487
 
                                di.UpdateApplication ();
488
 
                        }
489
 
                        
490
 
                        if (Wnck.Screen.Default.ActiveWorkspace == null)
491
 
                                return;
492
 
                        List<ApplicationDockItem> out_items = new List<ApplicationDockItem> ();
493
 
 
494
 
                        IEnumerable<int> knownPids = statistical_items.Concat (custom_items.Values).SelectMany (di => di.Pids);
495
 
                        
496
 
                        IEnumerable<Application> prunedApps = WindowUtils.GetApplications ()
497
 
                                .Where (app => app.Windows.Any (w => !w.IsSkipTasklist))
498
 
                                .Where (app => !knownPids.Contains (app.Pid) && app.Windows.Any ());
499
 
                        
500
 
                        foreach (IEnumerable<Wnck.Application> apps in prunedApps.
501
 
                                 GroupBy (app => (app as Wnck.Application).Windows [0].ClassGroup.ResClass)) {
502
 
                                ApplicationDockItem api = new ApplicationDockItem (apps);
503
 
 
504
 
                                if (task_items.Any (di => di.Equals (api)))
505
 
                                        api.DockAddItem = task_items.Where (di => di.Equals (api)).First ().DockAddItem;
506
 
                                else
507
 
                                        api.DockAddItem = DateTime.UtcNow;
508
 
 
509
 
                                out_items.Add (api);
510
 
                                api.UpdateNeeded += HandleUpdateNeeded;
511
 
                        }
512
 
                        
513
 
                        foreach (ApplicationDockItem item in task_items) {
514
 
                                item.Dispose ();
515
 
                                item.UpdateNeeded -= HandleUpdateNeeded;
516
 
                        }
517
 
                                        
518
 
                        task_items = out_items;
519
 
                }
520
 
                
521
 
                void SimplifyPositions (IEnumerable<DockItem> items)
522
 
                {
523
 
                        int i=0;
524
 
                        foreach (DockItem item in items.OrderBy (di => di.Position).ToArray ()) {
525
 
                                item.Position = i++;
526
 
                        }
527
 
                }
528
 
                
529
 
                void OnDockItemsChanged ()
530
 
                {
531
 
                        // clear the cache
532
 
                        output_items = null;
533
 
                        
534
 
                        if (DockItemsChanged != null)
535
 
                                DockItemsChanged (DockItems);
536
 
                }
537
 
 
538
 
                void OnUniverseInitialized (object sender, EventArgs e) 
539
 
                {
540
 
                        UpdatesEnabled = true;
541
 
                        UpdateItems ();
542
 
                }
543
 
                
544
 
                public void Dispose ()
545
 
                {
546
 
                        UnregisterEvents ();
547
 
                        
548
 
                        foreach (BaseDockItem di in DockItems) {
549
 
                                if (di is IRightClickable)
550
 
                                        (di as IRightClickable).RemoveClicked -= HandleRemoveClicked;
551
 
                                if (di is IDockAppItem)
552
 
                                        (di as IDockAppItem).UpdateNeeded -= HandleUpdateNeeded;
553
 
                                
554
 
                                di.Dispose ();
555
 
                        }
556
 
 
557
 
                        if (!DockItems.Contains (TrashItem))
558
 
                                TrashItem.Dispose ();
559
 
                        
560
 
                        if (!DockItems.Contains (Separator))
561
 
                                Separator.Dispose ();
562
 
 
563
 
                        custom_items.Clear ();
564
 
                        statistical_items.Clear ();
565
 
                        output_items.Clear (); 
566
 
                        task_items.Clear ();
567
 
                }
568
 
        }
569
 
}