~johannes-rudolph/do/space-backspace-navigation

« back to all changes in this revision

Viewing changes to Do.Interface.Linux.Docky/src/Docky.Core/Docky.Core.Default/ItemsService.cs

  • Committer: Johannes Rudolph
  • Date: 2009-07-02 07:48:47 UTC
  • mfrom: (909.1.359 do)
  • Revision ID: johannes.rudolph@gmail.com-20090702074847-uhqcq2wrn7j00684
merged to upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// ItemsService.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.Collections.ObjectModel;
 
22
using System.IO;
 
23
using System.Linq;
 
24
using System.Runtime.Serialization;
 
25
using System.Runtime.Serialization.Formatters.Binary;
 
26
using System.Security;
 
27
using System.Security.Permissions;
 
28
 
 
29
using Do;
 
30
using Do.Interface;
 
31
using Do.Interface.Wink;
 
32
using Do.Universe;
 
33
using Do.Platform;
 
34
 
 
35
using Docky.Interface;
 
36
using Docky.Utilities;
 
37
 
 
38
using Wnck;
 
39
 
 
40
namespace Docky.Core.Default
 
41
{
 
42
        public class ItemsService : IItemsService
 
43
        {
 
44
                const int UpdateDelay = 20;
 
45
                
 
46
                // stores items tied with their original identifier so we can remove them later
 
47
                Dictionary<string, AbstractDockItem> custom_items;
 
48
                
 
49
                // a collection for each of the major types of item
 
50
                List<AbstractDockItem> output_items, task_items, stat_items;
 
51
                
 
52
                // this will be a readonly collection to track out output items
 
53
                ReadOnlyCollection<AbstractDockItem> readonly_output_items;
 
54
                
 
55
                bool CustomItemsRead { get; set; }
 
56
                
 
57
                IEnumerable<AbstractDockItem> Docklets {
 
58
                        get { return DockServices.DockletService.ActiveDocklets.Cast<AbstractDockItem> (); }
 
59
                }
 
60
                
 
61
                //// <value>
 
62
                /// Our main menu.  We only ever need one so we will store it here
 
63
                /// </value>
 
64
                AbstractDockItem MenuItem { get; set; }
 
65
                
 
66
                /// <value>
 
67
                /// Our separator.  we can re-use this to render over and over if need be.
 
68
                /// </value>
 
69
                AbstractDockItem Separator { get; set; }
 
70
                
 
71
                /// <value>
 
72
                /// The path to the file we will use to serialize out our custom items
 
73
                /// </value>
 
74
                string CustomItemsPath {
 
75
                        get {
 
76
                                return Path.Combine (Services.Paths.UserDataDirectory, GetType ().Name + "_CustomItems");
 
77
                        }
 
78
                }
 
79
                
 
80
                /// <value>
 
81
                /// The path to the file we will use to store our sort dictionary
 
82
                /// </value>
 
83
                string SortDictionaryPath {
 
84
                        get { 
 
85
                                return Path.Combine (Services.Paths.UserDataDirectory, GetType ().Name + "_SortDictionary");
 
86
                        }
 
87
                }
 
88
                
 
89
                string DesktopFilesDirectory {
 
90
                        get {
 
91
                                return Path.Combine (Services.Paths.UserDataDirectory, GetType ().Name + "_DesktopFiles");
 
92
                        }
 
93
                }
 
94
                
 
95
                IEnumerable<AbstractDockItem> OrderedItems {
 
96
                        get {
 
97
                                return stat_items
 
98
                                            .Concat (custom_items.Values)
 
99
                                                .Concat (task_items)
 
100
                                                .OrderBy (di => di.Position);
 
101
                        }
 
102
                }
 
103
                
 
104
                int LastPosition {
 
105
                        get {
 
106
                                if (!OrderedItems.Any ())
 
107
                                        return 0;
 
108
                                return OrderedItems.Max ((Func<AbstractDockItem, int>) (di => di.Position));
 
109
                        }
 
110
                }
 
111
                
 
112
                #region Constructor
 
113
                
 
114
                public ItemsService ()
 
115
                {
 
116
                        // build our data structures
 
117
                        custom_items = new Dictionary<string, AbstractDockItem> ();
 
118
                        task_items = new List<AbstractDockItem> ();
 
119
                        output_items = new List<AbstractDockItem> ();
 
120
                        stat_items = new List<AbstractDockItem> ();
 
121
                        
 
122
                        // hook up our read only collection
 
123
                        readonly_output_items = output_items.AsReadOnly ();
 
124
                        
 
125
                        Separator = new SeparatorItem ();
 
126
                        MenuItem = new DoDockItem ();
 
127
                        
 
128
                        if (!Directory.Exists (DesktopFilesDirectory))
 
129
                                Directory.CreateDirectory (DesktopFilesDirectory);
 
130
                        
 
131
                        RegisterEvents ();
 
132
                }
 
133
                
 
134
                #endregion
 
135
                
 
136
                #region Event Handling
 
137
                void RegisterEvents ()
 
138
                {
 
139
                        // core services
 
140
                        Services.Core.UniverseInitialized += HandleUniverseInitialized;
 
141
                        
 
142
                        // wnck events
 
143
                        Wnck.Screen.Default.WindowClosed += HandleWindowClosed; 
 
144
                        Wnck.Screen.Default.WindowOpened += HandleWindowOpened; 
 
145
                        
 
146
                        // Dock Services
 
147
                        DockPreferences.AutomaticIconsChanged += HandleAutomaticIconsChanged; 
 
148
                        DockServices.DockletService.AppletVisibilityChanged += HandleAppletVisibilityChanged; 
 
149
                        
 
150
                        RegisterDocklets ();
 
151
                }
 
152
                
 
153
                void RegisterDocklets ()
 
154
                {
 
155
                        RegisterDockItem (MenuItem);
 
156
                        
 
157
                        foreach (AbstractDockItem item in DockServices.DockletService.Docklets) {
 
158
                                RegisterDockItem (item);
 
159
                        }
 
160
                }
 
161
                
 
162
                void UnregisterEvents ()
 
163
                {
 
164
                        // core services
 
165
                        Services.Core.UniverseInitialized -= HandleUniverseInitialized;
 
166
                        
 
167
                        // wnck events
 
168
                        Wnck.Screen.Default.WindowClosed -= HandleWindowClosed; 
 
169
                        Wnck.Screen.Default.WindowOpened -= HandleWindowOpened; 
 
170
                        
 
171
                        // Dock Services
 
172
                        DockPreferences.AutomaticIconsChanged -= HandleAutomaticIconsChanged; 
 
173
                        DockServices.DockletService.AppletVisibilityChanged -= HandleAppletVisibilityChanged; 
 
174
                        
 
175
                        UnregisterDocklets ();
 
176
                }
 
177
                
 
178
                void UnregisterDocklets ()
 
179
                {
 
180
                        UnregisterDockItem (MenuItem);
 
181
                        
 
182
                        foreach (AbstractDockItem item in DockServices.DockletService.Docklets) {
 
183
                                UnregisterDockItem (item);
 
184
                        }
 
185
                }
 
186
                
 
187
                void ResetDockelts ()
 
188
                {
 
189
                        UnregisterDocklets ();
 
190
                        RegisterDocklets ();
 
191
                }
 
192
                
 
193
                void RegisterDockItem (AbstractDockItem dockItem)
 
194
                {
 
195
                        dockItem.UpdateNeeded += HandleUpdateNeeded;
 
196
                        if (dockItem is IRightClickable)
 
197
                                (dockItem as IRightClickable).RemoveClicked += HandleRemoveClicked;
 
198
                }
 
199
                
 
200
                void UnregisterDockItem (AbstractDockItem dockItem)
 
201
                {
 
202
                        dockItem.UpdateNeeded -= HandleUpdateNeeded;
 
203
                        if (dockItem is IRightClickable)
 
204
                                (dockItem as IRightClickable).RemoveClicked -= HandleRemoveClicked;
 
205
                }
 
206
 
 
207
                void HandleAppletVisibilityChanged (object sender, EventArgs e)
 
208
                {
 
209
                        ResetDockelts ();
 
210
                        OnDockItemsChanged ();
 
211
                }
 
212
 
 
213
                void HandleAutomaticIconsChanged ()
 
214
                {
 
215
                        UpdateItems ();
 
216
                }
 
217
                
 
218
                void HandleRemoveClicked (object sender, EventArgs e)
 
219
                {
 
220
                        if (sender is AbstractDockItem)
 
221
                                RemoveItem (sender as AbstractDockItem);
 
222
                }
 
223
 
 
224
                void HandleWindowOpened (object o, WindowOpenedArgs args)
 
225
                {
 
226
                        // we do a delayed update so that we allow a small gap for wnck to catch up
 
227
                        if (!args.Window.IsSkipTasklist || args.Window.Pid != System.Diagnostics.Process.GetCurrentProcess ().Id)
 
228
                                DelayUpdateItems ();
 
229
                }
 
230
 
 
231
                void HandleWindowClosed (object o, WindowClosedArgs args)
 
232
                {
 
233
                        if (!args.Window.IsSkipTasklist || args.Window.Pid != System.Diagnostics.Process.GetCurrentProcess ().Id)
 
234
                                DelayUpdateItems ();
 
235
                }
 
236
 
 
237
                void HandleUniverseInitialized (object sender, EventArgs e)
 
238
                {
 
239
                        UpdatesEnabled = true;
 
240
                        UpdateItems ();
 
241
                }
 
242
                
 
243
                void HandleUpdateNeeded (object sender, UpdateRequestArgs args)
 
244
                {
 
245
                        if (!DockItems.Contains (args.Item))
 
246
                                return;
 
247
                        if (ItemNeedsUpdate != null)
 
248
                                ItemNeedsUpdate (this, args);
 
249
                }
 
250
                
 
251
                #endregion
 
252
                
 
253
                #region Item Updating
 
254
                AbstractDockItem MaybeCreateCustomItem (ref string identifier)
 
255
                {
 
256
                        ItemDockItem customItem = null;
 
257
                        
 
258
                        if (identifier.StartsWith ("file://"))
 
259
                                identifier = identifier.Substring ("file://".Length);
 
260
                        
 
261
                        if (File.Exists (identifier) || Directory.Exists (identifier)) {
 
262
                                if (identifier.EndsWith (".desktop")) {
 
263
                                        bool writeable = true;
 
264
                                        try {
 
265
                                                using (FileStream stream = File.OpenWrite (identifier)) {
 
266
                                                        ;
 
267
                                                }
 
268
                                        } catch {
 
269
                                                writeable = false;
 
270
                                        }
 
271
                                        if (writeable && Path.GetDirectoryName (identifier) != DesktopFilesDirectory) {
 
272
                                                string newFile = Path.Combine (DesktopFilesDirectory, Path.GetFileName (identifier));
 
273
                                                try {
 
274
                                                        Log<ItemsService>.Error ("Could not add custom item with id: {0}, File already exists", identifier);
 
275
                                                        File.Copy (identifier, newFile);
 
276
                                                } catch {
 
277
                                                        
 
278
                                                        return null; 
 
279
                                                }
 
280
                                                identifier = newFile;
 
281
                                        }
 
282
                                        Item o = Services.UniverseFactory.NewApplicationItem (identifier) as Item;
 
283
                                        customItem = new ItemDockItem (o);
 
284
                                } else {
 
285
                                        Item o = Services.UniverseFactory.NewFileItem (identifier) as Item;
 
286
                                        customItem = new ItemDockItem (o);
 
287
                                }
 
288
                        } else {
 
289
                                Item e = Services.Core.GetItem (identifier);
 
290
                                if (e != null)
 
291
                                        customItem = new ItemDockItem (e);
 
292
                                else
 
293
                                        Log<ItemsService>.Error ("Could not add custom item with id: {0}", identifier);
 
294
                        }
 
295
                        return customItem;
 
296
                }
 
297
                
 
298
                void SimplifyPositions (IEnumerable<AbstractDockItem> items)
 
299
                {
 
300
                        int i = 0;
 
301
                        // we call ToArray so our enumerator does get screwed up when we change the Position
 
302
                        foreach (AbstractDockItem item in items.OrderBy (di => di.Position).ToArray ())
 
303
                                item.Position = i++;
 
304
                }
 
305
                
 
306
                void DelayUpdateItems ()
 
307
                {
 
308
                        GLib.Timeout.Add (UpdateDelay, delegate {
 
309
                                UpdateItems ();
 
310
                                return false;
 
311
                        });
 
312
                }
 
313
                
 
314
                void UpdateItems ()
 
315
                {
 
316
                        if (!UpdatesEnabled)
 
317
                                return;
 
318
                        
 
319
                        if (!CustomItemsRead) {
 
320
                                foreach (string s in ReadCustomItems ())
 
321
                                        InternalAddItemToDock (s, LastPosition + 1);
 
322
                                
 
323
                                UpdateStatItems ();
 
324
                                
 
325
                                Dictionary<string, int> sortDictionary = ReadSortDictionary ();
 
326
                                foreach (ItemDockItem item in OrderedItems.Where (di => di is ItemDockItem)) {
 
327
                                        if (item.Item == null)
 
328
                                                continue;
 
329
                                        
 
330
                                        if (sortDictionary.ContainsKey (item.Item.UniqueId))
 
331
                                                item.Position = sortDictionary [item.Item.UniqueId];
 
332
                                }
 
333
                                foreach (AbstractDockItem item in Docklets) {
 
334
                                        if (sortDictionary.ContainsKey (item.GetType ().FullName))
 
335
                                            item.Position = sortDictionary [item.GetType ().FullName];
 
336
                                }
 
337
                                
 
338
                                CustomItemsRead = true;
 
339
                        } else {
 
340
                                UpdateStatItems ();
 
341
                        }
 
342
                        
 
343
                        UpdateTaskItems ();
 
344
                        SimplifyPositions (OrderedItems);
 
345
                        SimplifyPositions (Docklets);
 
346
                        
 
347
                        OnDockItemsChanged ();
 
348
                }
 
349
                
 
350
                void UpdateStatItems ()
 
351
                {
 
352
                        List<ItemDockItem> old_items = new List<ItemDockItem> (stat_items.Where (di => di is ItemDockItem)
 
353
                                                                               .Cast<ItemDockItem> ());
 
354
                        List<ItemDockItem> local_cust = new List<ItemDockItem> (custom_items.Values
 
355
                                                                                .Where (di => di is ItemDockItem)
 
356
                                                                                .Cast<ItemDockItem> ());
 
357
                        
 
358
                        stat_items = new List<AbstractDockItem> ();
 
359
                        
 
360
                        IEnumerable<Item> mostUsedItems = MostUsedItems ();
 
361
                        
 
362
                        DateTime currentTime = DateTime.UtcNow;
 
363
                        foreach (Item item in mostUsedItems.Where (i => !local_cust.Any (ci => ci.Item != null && ci.Item.UniqueId == i.UniqueId))) {
 
364
                                
 
365
                                if (old_items.Any (di => di.Item == item)) {
 
366
                                        stat_items.AddRange (old_items
 
367
                                                             .Where (di => di.Item != null && di.Item.UniqueId == item.UniqueId)
 
368
                                                             .Cast<AbstractDockItem> ());
 
369
                                } else {
 
370
                                        ItemDockItem di = new ItemDockItem (item);
 
371
                                        RegisterDockItem (di);
 
372
                                        di.DockAddItem = currentTime;
 
373
                                        
 
374
                                        int position = LastPosition + 1;
 
375
 
 
376
                                        // TODO fixme once mono 1.9 support is dropped
 
377
                                        if (old_items.Any ())
 
378
                                                position += old_items.Max ((Func<ItemDockItem, int>) (oi => oi.Position));
 
379
 
 
380
                                        di.Position = position;
 
381
                                        stat_items.Add (di);
 
382
                                }
 
383
                        }
 
384
                        
 
385
                        // potential leak if not all items in stat_items were ItemDockItems!!!
 
386
                        foreach (ItemDockItem item in  old_items.Where (di => !stat_items.Contains (di))) {
 
387
                                UnregisterDockItem (item);
 
388
                                item.Dispose ();
 
389
                        }
 
390
                }
 
391
                
 
392
                void UpdateTaskItems ()
 
393
                {
 
394
                        foreach (ItemDockItem item in OrderedItems.Where (di => di is ItemDockItem))
 
395
                                item.UpdateApplication ();
 
396
                        
 
397
                        List<ApplicationDockItem> out_items = new List<ApplicationDockItem> ();
 
398
 
 
399
                        IEnumerable<Window> knownWindows = OrderedItems
 
400
                                    .Where (di => di is ItemDockItem)
 
401
                                        .Cast<ItemDockItem> ()
 
402
                                        .SelectMany (di => di.Windows);
 
403
                        
 
404
                        var prunedWindows = WindowUtils.GetWindows ()
 
405
                                    .Where (w => !w.IsSkipTasklist && !knownWindows.Contains (w))
 
406
                                        .GroupBy (w => SafeResClass (w));
 
407
                        
 
408
                        IEnumerable<ApplicationDockItem> apps = WindowUtils.GetWindows ()
 
409
                                    .Where (w => !w.IsSkipTasklist && !knownWindows.Contains (w))
 
410
                                        .GroupBy (w => SafeResClass (w))
 
411
                                        .Select (ws => CreateApplicationDockItem (ws, task_items));
 
412
                        
 
413
                        apps.ForEach (a => RegisterDockItem (a));
 
414
                        out_items.AddRange (apps);
 
415
                        
 
416
                        foreach (AbstractDockItem item in task_items) {
 
417
                                UnregisterDockItem (item);
 
418
                                item.Dispose ();
 
419
                        }
 
420
                        
 
421
                        foreach (ApplicationDockItem adi in out_items.OrderBy (di => di.DockAddItem)) {
 
422
                                if (adi.Position == 0) // raw and unpositioned items
 
423
                                        adi.Position = LastPosition + 1;
 
424
                        }
 
425
                                        
 
426
                        task_items.Clear ();
 
427
                        task_items.AddRange (out_items.Cast<AbstractDockItem> ());
 
428
                }
 
429
                
 
430
                ApplicationDockItem CreateApplicationDockItem (IEnumerable<Wnck.Window> windows, IEnumerable<AbstractDockItem> lastSet)
 
431
                {
 
432
                        ApplicationDockItem adi = new ApplicationDockItem (windows);
 
433
                        
 
434
                        if (lastSet.Any (di => di.Equals (adi))) {
 
435
                                AbstractDockItem match = lastSet.Where (di => di.Equals (adi)).First ();
 
436
                                adi.DockAddItem = match.DockAddItem;
 
437
                                adi.Position = match.Position;
 
438
                                } else {
 
439
                                adi.DockAddItem = DateTime.UtcNow;
 
440
                        }
 
441
                        
 
442
                        return adi;
 
443
                }
 
444
                
 
445
                string SafeResClass (Wnck.Window window)
 
446
                {
 
447
                        if (window.ClassGroup != null && window.ClassGroup.ResClass != null)
 
448
                                return window.ClassGroup.ResClass;
 
449
                        return string.Empty;
 
450
                }
 
451
                
 
452
                void OnDockItemsChanged ()
 
453
                {
 
454
                        output_items.Clear ();
 
455
                        
 
456
                        if (DockItemsChanged != null)
 
457
                                DockItemsChanged (DockItems);
 
458
                }
 
459
                #endregion
 
460
                
 
461
                #region Item Management
 
462
                bool InternalAddItemToDock (Item item, int position)
 
463
                {
 
464
                        if (!(item is Item)) {
 
465
                                Log<ItemsService>.Error ("Could not add {0} to custom items for dock", item.Safe.Name);
 
466
                                return false;
 
467
                        }
 
468
                        
 
469
                        string id = item.UniqueId;
 
470
                        if (custom_items.ContainsKey (id))
 
471
                                return false;
 
472
                        
 
473
                        AbstractDockItem dockItem = new ItemDockItem (item as Item);
 
474
                        RegisterDockItem (dockItem);
 
475
                        
 
476
                        MakeHoleAtPosition (position);
 
477
                        
 
478
                        dockItem.Position = position;
 
479
                        custom_items [id] = dockItem;
 
480
                        
 
481
                        return true;
 
482
                }
 
483
                
 
484
                bool InternalAddItemToDock (string identifier, int position)
 
485
                {
 
486
                        if (custom_items.ContainsKey (identifier)) return false;
 
487
                        
 
488
                        AbstractDockItem customItem = MaybeCreateCustomItem (ref identifier);
 
489
                        
 
490
                        if (customItem == null) return false;
 
491
                        
 
492
                        RegisterDockItem (customItem);
 
493
                        
 
494
                        MakeHoleAtPosition (position);
 
495
                        
 
496
                        customItem.Position = position;
 
497
                        custom_items [identifier] = customItem;
 
498
                        
 
499
                        return true;
 
500
                }
 
501
                #endregion
 
502
                
 
503
                #region Disk Utilities
 
504
                void WriteData ()
 
505
                {
 
506
                        WriteSortDictionary ();
 
507
                        WriteCustomItems ();
 
508
                }
 
509
                
 
510
                void WriteSortDictionary ()
 
511
                {
 
512
                        try {
 
513
                                if (File.Exists (SortDictionaryPath))
 
514
                                        File.Delete (SortDictionaryPath);
 
515
                                
 
516
                                using (StreamWriter writer = new StreamWriter (SortDictionaryPath)) {
 
517
                                        foreach (ItemDockItem di in OrderedItems.Where (di => di is ItemDockItem)) {
 
518
                                                if (di.Item == null)
 
519
                                                        continue;
 
520
                                                
 
521
                                                writer.WriteLine ("{0}|{1}", di.Item.UniqueId, di.Position);
 
522
                                        }
 
523
                                        foreach (AbstractDockItem di in Docklets) {
 
524
                                                writer.WriteLine ("{0}|{1}", di.GetType ().FullName, di.Position);
 
525
                                        }
 
526
                                }
 
527
                        } catch (Exception e) {
 
528
                                Log<ItemsService>.Error ("Could not write out sort items");
 
529
                                Log<ItemsService>.Error (e.Message);
 
530
                        }
 
531
                }
 
532
                
 
533
                Dictionary<string, int> ReadSortDictionary ()
 
534
                {
 
535
                        Dictionary<string, int> sortDictionary = new Dictionary<string, int> ();
 
536
                        try {
 
537
                                using (StreamReader reader = new StreamReader (SortDictionaryPath)) {
 
538
                                        string [] line;
 
539
                                        while (!reader.EndOfStream) {
 
540
                                                line = reader.ReadLine ().Split ('|');
 
541
                                                sortDictionary [line [0]] = Convert.ToInt32 (line [1]);
 
542
                                        }
 
543
                                }
 
544
                        } catch (FileNotFoundException e) {
 
545
                                Log<ItemsService>.Debug ("Sort Dictionary file not present, nothing to add. " + e.Message);
 
546
                        } catch (Exception e) {
 
547
                                Log<ItemsService>.Error ("Could not deserialize sort dictionary");
 
548
                        }
 
549
                        return sortDictionary;
 
550
                }
 
551
                
 
552
                void WriteCustomItems ()
 
553
                {
 
554
                        try {
 
555
                                using (Stream s = File.OpenWrite (CustomItemsPath)) {
 
556
                                        BinaryFormatter f = new BinaryFormatter ();
 
557
                                        f.Serialize (s, custom_items.Keys.ToArray ());
 
558
                                }
 
559
                        } catch (Exception e) {
 
560
                                Log<ItemsService>.Error ("Could not serialize custom items");
 
561
                                Log<ItemsService>.Error (e.Message);
 
562
                        }
 
563
                }
 
564
                
 
565
                IEnumerable<string> ReadCustomItems ()
 
566
                {
 
567
                        string[] filenames;
 
568
                        try {
 
569
                                using (Stream s = File.OpenRead (CustomItemsPath)) {
 
570
                                        BinaryFormatter f = new BinaryFormatter ();
 
571
                                        filenames = f.Deserialize (s) as string[];
 
572
                                }
 
573
                        } catch (FileNotFoundException e) {
 
574
                                Log<ItemsService>.Debug ("Custom items file not present, nothing to add. " + e.Message);
 
575
                                filenames = new string[0];
 
576
                        } catch {
 
577
                                Log<ItemsService>.Error ("Could not deserialize custom items");
 
578
                                filenames = new string[0];
 
579
                        }
 
580
                        return filenames;
 
581
                }
 
582
                #endregion
 
583
                
 
584
                #region Random Useful Functions
 
585
                bool ItemCanInteractWithPosition (AbstractDockItem item, int position)
 
586
                {
 
587
                        return (DockItems.Contains (item) || Docklets.Contains (item)) && position >= 0 && position < DockItems.Count; 
 
588
                }
 
589
                
 
590
                /// <summary>
 
591
                /// Returns the most used items out of GNOME Do and does a tiny bit of filtering and sorting on them
 
592
                /// This is mostly to encourage a better first run experience, but overall this can be improved
 
593
                /// </summary>
 
594
                /// <returns>
 
595
                /// A <see cref="IEnumerable"/> of the most used items from Do's core universe
 
596
                /// </returns>
 
597
                IEnumerable<Item> MostUsedItems ()
 
598
                {
 
599
                        return Services.Core
 
600
                                .GetItemsOrderedByRelevance ()
 
601
                                .Where (item => item.GetType ().Name != "SelectedTextItem" && 
 
602
                                                item.GetType ().Name != "GNOMETrashFileItem")
 
603
                                .Where (item => !DockPreferences.ItemBlacklist.Contains (item.UniqueId))
 
604
                                .Take (DockPreferences.AutomaticIcons)
 
605
                                .OrderByDescending (item => item is IApplicationItem)
 
606
                                .ThenBy (item => item.GetType ().Name)
 
607
                                .ThenBy (item => item.Safe.Name);
 
608
                }
 
609
                
 
610
                void MakeHoleAtPosition (int position)
 
611
                {
 
612
                        foreach (AbstractDockItem di in OrderedItems) {
 
613
                                if (di.Position >= position)
 
614
                                        di.Position++;
 
615
                        }
 
616
                }
 
617
                #endregion
 
618
                
 
619
                #region IItemsService implementation
 
620
                public event DockItemsChangedHandler DockItemsChanged;
 
621
                public event UpdateRequestHandler ItemNeedsUpdate;
 
622
                
 
623
                public bool UpdatesEnabled { get; private set; }
 
624
                
 
625
                public ReadOnlyCollection<AbstractDockItem> DockItems {
 
626
                        get {
 
627
                                if (output_items.Count == 0) {
 
628
                                        output_items.Add (MenuItem);
 
629
                                        
 
630
                                        // Add our custom/task/statistical items in one shot
 
631
                                        output_items.AddRange (OrderedItems);
 
632
                                
 
633
                                        // add a separator and any docklets that are active
 
634
                                        if (DockServices.DockletService.ActiveDocklets.Any ()) {
 
635
                                                output_items.Add (Separator);
 
636
                                                output_items.AddRange (Docklets.OrderBy (docklet => docklet.Position));
 
637
                                        }
 
638
                                }
 
639
                                return readonly_output_items;
 
640
                        }
 
641
                }
 
642
                
 
643
                public void AddItemToDock (Item item)
 
644
                {
 
645
                        AddItemToDock (item, LastPosition + 1);
 
646
                }
 
647
                
 
648
                public void AddItemToDock (string identifier)
 
649
                {
 
650
                        AddItemToDock (identifier, LastPosition + 1);
 
651
                }
 
652
                
 
653
                public void AddItemToDock (Item item, int position)
 
654
                {
 
655
                        position = DockItems [position].Position;
 
656
                        if (InternalAddItemToDock (item, position)) {
 
657
                                UpdateItems ();
 
658
                                WriteData ();
 
659
                                OnDockItemsChanged ();
 
660
                        }
 
661
                }
 
662
                
 
663
                public void AddItemToDock (string identifier, int position)
 
664
                {
 
665
                        position = DockItems [position].Position;
 
666
                        if (InternalAddItemToDock (identifier, position)) {
 
667
                                UpdateItems ();
 
668
                                WriteData ();
 
669
                                OnDockItemsChanged ();
 
670
                        }
 
671
                }
 
672
                
 
673
                public bool ItemCanBeMoved (int item)
 
674
                {
 
675
                        if (item < 0 || item > DockItems.Count)
 
676
                                return false;
 
677
                        
 
678
                        return (OrderedItems.Contains (DockItems [item]) || Docklets.Contains (DockItems [item]));
 
679
                }
 
680
                
 
681
                public bool ItemCanBeRemoved (int item)
 
682
                {
 
683
                        AbstractDockItem adi = DockItems [item];
 
684
                        return adi.WindowCount == 0 && ((GetIconSource (adi) == IconSource.Statistics && adi is ItemDockItem) ||
 
685
                                                        (GetIconSource (adi) == IconSource.Custom));
 
686
                }
 
687
                
 
688
                public void DropItemOnPosition (AbstractDockItem item, int position)
 
689
                {
 
690
                        do {
 
691
                                if (!ItemCanInteractWithPosition (item, position)) continue;
 
692
                        
 
693
                                if (DockItems [position] is TrashDockItem) {
 
694
                                        RemoveItem (item);
 
695
                                        continue;
 
696
                                }
 
697
                                
 
698
                                if (item is ApplicationDockItem) {
 
699
                                        ApplicationDockItem adi = item as ApplicationDockItem;
 
700
                                        
 
701
                                        if (adi.Launcher == null) continue;
 
702
                                        
 
703
                                        Item launcher = adi.Launcher as Item;
 
704
                                        if (launcher == null)
 
705
                                                continue;
 
706
                                        
 
707
                                        AbstractDockItem newItem = new ItemDockItem (launcher);
 
708
                                        
 
709
                                        newItem.Position = item.Position;
 
710
                                        newItem.DockAddItem = item.DockAddItem;
 
711
                                        custom_items [launcher.UniqueId] = newItem;
 
712
                                        
 
713
                                        RegisterDockItem (newItem);
 
714
                                        UpdateItems ();
 
715
                                        WriteData ();
 
716
                                }
 
717
                        } while (false);
 
718
                }
 
719
                
 
720
                public void MoveItemToPosition (AbstractDockItem item, int position)
 
721
                {
 
722
                        if (ItemCanInteractWithPosition (item, position))
 
723
                                MoveItemToPosition (DockItems.IndexOf (item), position);
 
724
                }
 
725
                
 
726
                public void MoveItemToPosition (int item, int position)
 
727
                {
 
728
                        if (item == position || 
 
729
                            item < 0 || 
 
730
                            position < 0 || 
 
731
                            position > DockItems.Count || 
 
732
                            item > DockItems.Count)
 
733
                                return;
 
734
                        
 
735
                        IconSource itemSource = GetIconSource (DockItems [item]);
 
736
                        IconSource targetSource = GetIconSource (DockItems [position]);
 
737
                        
 
738
                        if (itemSource == IconSource.Unknown || 
 
739
                            targetSource == IconSource.Unknown || 
 
740
                            (itemSource == IconSource.Docklet && targetSource != IconSource.Docklet) ||
 
741
                            (itemSource != IconSource.Docklet && targetSource == IconSource.Docklet))
 
742
                                return;
 
743
                        
 
744
                        AbstractDockItem primaryItem = DockItems [item];
 
745
                        AbstractDockItem targetItem = DockItems [position];
 
746
                        
 
747
                        int startPosition = primaryItem.Position;
 
748
                        int targetPosition = targetItem.Position;
 
749
                        
 
750
                        IEnumerable<AbstractDockItem> itemSet;
 
751
                        if (itemSource == IconSource.Docklet)
 
752
                                itemSet = Docklets;
 
753
                        else
 
754
                                itemSet = OrderedItems;
 
755
                        
 
756
                        foreach (AbstractDockItem di in itemSet) {
 
757
                                if (startPosition < targetPosition) {
 
758
                                        // the item is being shifted to the right.  Everything greater than item up to and including target item
 
759
                                        // needs to be shifted to the left
 
760
                                        if (di.Position > startPosition && di.Position <= targetPosition)
 
761
                                                di.Position--;
 
762
                                } else {
 
763
                                        // the item is being shifted to the left.  Everthing less than the item and up to and including target item
 
764
                                        // needs to be shifted to the right
 
765
                                        if (di.Position < startPosition && di.Position >= targetPosition)
 
766
                                                di.Position++;
 
767
                                }
 
768
                        }
 
769
                        
 
770
                        primaryItem.Position = targetPosition;
 
771
                        
 
772
                        OnDockItemsChanged ();
 
773
                        WriteData ();
 
774
                }
 
775
                
 
776
                public void ForceUpdate ()
 
777
                {
 
778
                        UpdateItems ();
 
779
                }
 
780
                
 
781
                public IconSource GetIconSource (AbstractDockItem item)
 
782
                {
 
783
                        if (task_items.Contains (item))
 
784
                                return IconSource.Application;
 
785
                        
 
786
                        if (stat_items.Contains (item))
 
787
                                return IconSource.Statistics;
 
788
                        
 
789
                        if (custom_items.Values.Contains (item))
 
790
                                return IconSource.Custom;
 
791
                        
 
792
                        if (Docklets.Contains (item))
 
793
                                return IconSource.Docklet;
 
794
                        
 
795
                        return IconSource.Unknown;
 
796
                }
 
797
                
 
798
                public bool RemoveItem (AbstractDockItem item)
 
799
                {
 
800
                        if (!DockItems.Contains (item))
 
801
                                return false;
 
802
                        return RemoveItem (DockItems.IndexOf (item));
 
803
                }
 
804
                
 
805
                public bool RemoveItem (int item)
 
806
                {
 
807
                        bool result = ItemCanBeRemoved (item);
 
808
                        
 
809
                        if (result) {
 
810
                                if (GetIconSource (DockItems [item]) == IconSource.Statistics) {
 
811
                                        ItemDockItem di = (DockItems [item] as ItemDockItem);
 
812
                                        DockPreferences.AddBlacklistItem ((DockItems [item] as ItemDockItem).Item.UniqueId);
 
813
                                        DockPreferences.AutomaticIcons = Math.Max (0, DockPreferences.AutomaticIcons - 1);
 
814
                                } else if (GetIconSource (DockItems [item]) == IconSource.Custom) {
 
815
                                        KeyValuePair<string, AbstractDockItem> kvp = custom_items
 
816
                                                .Where (k => k.Value.Equals (DockItems [item]))
 
817
                                                .First ();
 
818
                                        
 
819
                                        custom_items.Remove (kvp.Key);
 
820
                                        
 
821
                                        if (kvp.Key.EndsWith (".desktop") && Path.GetDirectoryName (kvp.Key) == DesktopFilesDirectory) {
 
822
                                                try {
 
823
                                                        File.Delete (kvp.Key);
 
824
                                                } catch {
 
825
                                                        // Don't really care, it was only a nice thing to do...
 
826
                                                }
 
827
                                        }
 
828
                                }
 
829
                                
 
830
                                UpdateItems ();
 
831
                                WriteData ();
 
832
                        }
 
833
                        
 
834
                        return result;
 
835
                }
 
836
                
 
837
                public bool HotSeatItem (AbstractDockItem item, List<AbstractDockItem> seatedItems)
 
838
                {
 
839
                        Log<ItemsService>.Error ("Items Service cannot currently handle hotseating");
 
840
                        return false;
 
841
                }
 
842
                
 
843
                public bool ResetHotSeat (AbstractDockItem item)
 
844
                {
 
845
                        Log<ItemsService>.Error ("Items Service cannot currently handle hotseating");
 
846
                        return false;
 
847
                }
 
848
                #endregion
 
849
 
 
850
                #region IDisposable implementation
 
851
                public void Dispose ()
 
852
                {
 
853
                        UnregisterEvents ();
 
854
                        
 
855
                        foreach (AbstractDockItem di in DockItems) {
 
856
                                if (di.Disposed)
 
857
                                        continue;
 
858
                                
 
859
                                UnregisterDockItem (di);
 
860
                                di.Dispose ();
 
861
                        }
 
862
                        
 
863
                        if (!Separator.Disposed)
 
864
                                Separator.Dispose ();
 
865
                        
 
866
                        custom_items.Clear ();
 
867
                        stat_items.Clear ();
 
868
                        output_items.Clear ();
 
869
                        task_items.Clear ();
 
870
                }
 
871
                #endregion
 
872
                
 
873
                
 
874
        }
 
875
}