30
30
namespace Do.Universe.Linux {
32
public class ApplicationItemSource : ItemSource {
32
public class ApplicationItemSource : DynamicItemSource {
34
34
const bool show_hidden = false;
35
Dictionary<string, ApplicationItem> app_items = new Dictionary<string, ApplicationItem> ();
36
List<CategoryItem> categories = new List<CategoryItem> ();
35
38
static IEnumerable<string> desktop_file_directories;
37
IEnumerable<Item> app_items;
39
List<FileSystemWatcher> directoryMonitors = new List<FileSystemWatcher> ();
39
41
static ApplicationItemSource ()
41
43
desktop_file_directories = GetDesktopFileDirectories ();
44
public ApplicationItemSource ()
46
app_items = Enumerable.Empty<Item> ();
49
46
public override IEnumerable<Type> SupportedItemTypes {
51
48
yield return typeof (ApplicationItem);
122
120
return app.IsAppropriateForCurrentDesktop && (show_hidden || !app.NoDisplay);
125
public override void UpdateItems ()
127
IEnumerable<ApplicationItem> appItems = desktop_file_directories
128
.SelectMany (dir => LoadDesktopFiles (dir));
130
IEnumerable<CategoryItem> categoryItems = appItems
131
.SelectMany (a => LoadCategoryItems (a));
135
.Concat (categoryItems.Cast<Item> ())
140
public override IEnumerable<Item> Items {
141
get { return app_items; }
123
override protected void Enable ()
125
ItemsAvailableEventArgs eventArgs = new ItemsAvailableEventArgs ();
127
foreach (var directory in desktop_file_directories.Where (dir => Directory.Exists (dir))) {
128
var monitor = new FileSystemWatcher (directory, "*.desktop");
129
monitor.Created += OnFileCreated;
130
monitor.Deleted += OnFileDeleted;
131
monitor.Renamed += OnFileRenamed;
132
monitor.Error += OnWatcherError;
133
monitor.EnableRaisingEvents = true;
134
directoryMonitors.Add (monitor);
135
Log<ApplicationItemSource>.Debug ("Watching directory {0} for application changes.", directory);
137
foreach (var fileItemPair in desktop_file_directories.SelectMany (dir => LoadDesktopFiles (dir))) {
138
var previousMatch = app_items.FirstOrDefault (pair => pair.Value == fileItemPair.Value);
139
if (previousMatch.Key == null && previousMatch.Value == null) {
140
app_items.Add (fileItemPair.Key, fileItemPair.Value);
141
} else if (fileItemPair.Key != previousMatch.Key){
142
Log.Debug ("Desktop file {0} hides previous file {1}", fileItemPair.Key, previousMatch.Key);
143
app_items.Remove (previousMatch.Key);
144
app_items.Add (fileItemPair.Key, fileItemPair.Value);
147
eventArgs.newItems = app_items.Values.Cast<Item> ().ToList ();
149
categories = app_items.SelectMany (pair => LoadCategoryItems (pair.Value)).Distinct ().ToList ();
150
eventArgs.newItems = eventArgs.newItems.Concat (categories.ToArray ());
152
RaiseItemsAvailable (eventArgs);
155
override protected void Disable ()
157
foreach (var watcher in directoryMonitors) {
160
directoryMonitors.Clear ();
164
void OnWatcherError (object sender, ErrorEventArgs e)
166
Log<ApplicationItemSource>.Error ("Error in directory watcher: {0}", e.GetException ().Message);
169
void OnFileDeleted (object sender, FileSystemEventArgs e)
171
Item disappearingItem;
173
Log<ApplicationItemSource>.Debug ("Deskop file removed: {0}", e.FullPath);
174
if (!app_items.ContainsKey (e.FullPath)) {
175
Log.Error ("Desktop file {0} deleted, but not found in Universe", e.FullPath);
176
// FIXME: Should this throw an exception?
179
disappearingItem = app_items[e.FullPath];
180
app_items.Remove (e.FullPath);
182
RaiseItemsUnavailable (new ItemsUnavailableEventArgs () { unavailableItems = new Item[] { disappearingItem }});
185
void OnFileCreated (object sender, FileSystemEventArgs e)
187
Log<ApplicationItemSource>.Debug ("New Desktop file found: {0}", e.FullPath);
188
var newItem = ApplicationItem.MaybeCreateFromDesktopItem (e.FullPath);
189
if (newItem == null) {
190
Log.Error ("Found new Desktop file {0} but unable to create an item in the Universe", e.FullPath);
194
if (app_items.ContainsKey (e.FullPath)) {
195
Log.Error ("Attempting to add duplicate ApplicationItem {0} to Universe", e.FullPath);
198
app_items[e.FullPath] = newItem;
200
RaiseItemsAvailable (new ItemsAvailableEventArgs () { newItems = new Item[] { newItem }});
203
void OnFileRenamed (object sender, RenamedEventArgs e)
205
Item disappearingItem = null;
206
ApplicationItem newItem = null;
208
if (app_items.ContainsKey (e.OldFullPath)) {
209
Log<ApplicationItemSource>.Debug ("Desktop file {0} moved away", e.OldFullPath);
210
disappearingItem = app_items[e.OldFullPath];
211
app_items.Remove (e.OldFullPath);
213
if (e.FullPath.EndsWith (".desktop")) {
214
Log<ApplicationItemSource>.Debug ("Desktop file {0} moved into watched directory", e.FullPath);
215
newItem = ApplicationItem.MaybeCreateFromDesktopItem (e.FullPath);
216
if (newItem == null) {
217
Log.Error ("Found new Desktop file {0} but unable to create an item in the Universe", e.FullPath);
219
app_items[e.FullPath] = newItem;
223
if (disappearingItem != null) {
224
RaiseItemsUnavailable (new ItemsUnavailableEventArgs () { unavailableItems = new Item[] { disappearingItem }});
226
if (newItem != null) {
227
RaiseItemsAvailable (new ItemsAvailableEventArgs () { newItems = new Item[] { newItem }});
144
231
public override IEnumerable<Item> ChildrenOfItem (Item item)
146
233
if (item is CategoryItem) {
147
234
CategoryItem catItem = item as CategoryItem;
235
return app_items.Values
149
236
.Where (a => a is ApplicationItem)
150
.Where (a => (a as ApplicationItem).Categories.Contains (catItem.Category));
237
.Where (a => (a as ApplicationItem).Categories.Contains (catItem.Category))
152
240
return Enumerable.Empty<Item> ();