Thumbnail file hits. Based on a patch from D Bera
[beagle.git] / beagled / LauncherQueryable / LauncherQueryable.cs
blobb124dd3b7d0a75a714c2e7656260b8913017a596
1 //
2 // LauncherQueryable.cs
3 //
4 // Copyright (C) 2004 Joe Gasiorek
5 // Copyright (C) 2004 Novell, Inc.
6 //
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a
10 // copy of this software and associated documentation files (the "Software"),
11 // to deal in the Software without restriction, including without limitation
12 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 // and/or sell copies of the Software, and to permit persons to whom the
14 // Software is furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 // DEALINGS IN THE SOFTWARE.
28 using System;
29 using System.Collections;
30 using System.IO;
31 using System.Text;
32 using System.Diagnostics;
34 using Beagle.Daemon;
35 using Beagle.Util;
37 namespace Beagle.Daemon.LauncherQueryable {
39 [QueryableFlavor (Name="Launcher", Domain=QueryDomain.Local, RequireInotify=false)]
40 public class LauncherQueryable : LuceneQueryable {
42 private static Logger log = Logger.Get ("LauncherQueryable");
43 ArrayList Dirs;
44 int polling_interval_in_hours = 1;
46 public LauncherQueryable () : base ("LauncherIndex")
48 Dirs = new ArrayList ();
49 // Add GNOME dirs
50 string path = Path.Combine (ExternalStringsHack.GnomePrefix, "share");
51 Dirs.Add (Path.Combine (path, "applications"));
52 Dirs.Add (Path.Combine (path, "gnome/apps"));
53 Dirs.Add (Path.Combine (path, "control-center-2.0"));
54 Dirs.Add (Path.Combine (path, "control-center"));
55 Dirs.Add (Path.Combine (PathFinder.HomeDir, ".gnome2/panel2.d/default/launchers"));
57 // Add KDE dirs
58 foreach (string kde_dir in KdeUtils.KdeLocations) {
59 if (kde_dir == null || kde_dir == String.Empty)
60 continue;
62 string share_dir = Path.Combine (kde_dir, "share");
63 Dirs.Add (Path.Combine(share_dir, "applications"));
65 Dirs.Add (Path.Combine (PathFinder.HomeDir, ".local/share/applications"));
68 override protected IFileAttributesStore BuildFileAttributesStore (string index_fingerprint)
70 return new FileAttributesStore_Mixed (IndexDirectory, Driver.Fingerprint);
73 public override void Start ()
75 base.Start ();
77 log.Info ("Starting launcher backend");
78 Stopwatch timer = new Stopwatch ();
79 timer.Start ();
81 Crawl ();
83 if (!Inotify.Enabled) {
84 Scheduler.Task task = Scheduler.TaskFromHook (new Scheduler.TaskHook (CrawlHook));
85 task.Tag = "Crawling system launchers";
86 ThisScheduler.Add (task);
89 timer.Stop ();
90 log.Info ("Launcher backend worker thread done in {0}", timer);
93 private void Crawl ()
95 log.Info ("Scanning Launchers");
96 Stopwatch timer = new Stopwatch ();
97 int launchers_found = 0;
98 foreach (String dir in Dirs)
99 launchers_found += CrawlLaunchers (dir);
101 log.Info ("Found {0} Launchers in {1}", launchers_found, timer);
104 private void CrawlHook (Scheduler.Task task)
106 Crawl ();
107 task.Reschedule = true;
108 task.TriggerTime = DateTime.Now.AddHours (this.polling_interval_in_hours);
111 // Crawl the specified directory and all subdirectories, indexing all
112 // discovered launchers. If Inotify is available, every directory
113 // scanned will be watched.
114 private int CrawlLaunchers (string path)
116 DirectoryInfo root = new DirectoryInfo (path);
117 if (! root.Exists)
118 return 0;
119 int fileCount = 0;
121 Queue queue = new Queue ();
122 queue.Enqueue (root);
124 while (queue.Count > 0) {
125 DirectoryInfo dir = queue.Dequeue () as DirectoryInfo;
127 if (Inotify.Enabled) {
128 Inotify.Subscribe (dir.FullName, OnInotifyEvent, Inotify.EventType.Create | Inotify.EventType.Modify);
131 foreach (FileInfo file in dir.GetFiles ()) {
132 IndexLauncher (file, Scheduler.Priority.Delayed);
133 ++fileCount;
136 foreach (DirectoryInfo subdir in dir.GetDirectories ())
137 queue.Enqueue (subdir);
140 return fileCount;
143 private void OnInotifyEvent (Inotify.Watch watch,
144 string path,
145 string subitem,
146 string srcpath,
147 Inotify.EventType type)
149 if (subitem == "")
150 return;
152 string fullPath = Path.Combine (path, subitem);
154 if ((type & Inotify.EventType.Create) != 0 && (type & Inotify.EventType.IsDirectory) != 0) {
155 CrawlLaunchers (fullPath);
156 return;
158 if ((type & Inotify.EventType.Modify) != 0) {
159 IndexLauncher (new FileInfo (fullPath), Scheduler.Priority.Immediate);
160 return;
164 private void IndexLauncher (FileInfo file, Scheduler.Priority priority)
166 if ((! file.Exists)
167 || (this.FileAttributesStore.IsUpToDate (file.FullName)))
168 return;
170 /* Check to see if file is a launcher */
171 if (Beagle.Util.VFS.Mime.GetMimeType (file.FullName) != "application/x-desktop")
172 return;
174 StreamReader reader;
176 try {
177 reader = new StreamReader (file.Open (FileMode.Open, FileAccess.Read, FileShare.Read));
178 } catch (Exception e) {
179 log.Warn ("Could not open '{0}': {1}", file.FullName, e.Message);
180 return;
183 if (reader.ReadLine () != "[Desktop Entry]") {
184 reader.Close ();
185 return;
188 /* I'm convinced it is a launcher */
189 Indexable indexable = new Indexable (UriFu.PathToFileUri (file.FullName));
191 indexable.Timestamp = file.LastWriteTime;
192 indexable.Type = "Launcher";
193 indexable.MimeType = "application/x-desktop";
195 // desktop files must have a name
196 bool have_name = false;
198 String line;
199 while ((line = reader.ReadLine ()) != null) {
200 string [] sline = line.Split ('=');
201 if (sline.Length != 2)
202 continue;
204 // FIXME: We shouldnt really search fields that are in other locales than the current should we?
206 if (sline [0].Equals ("Icon") || sline [0].Equals ("Exec")) {
207 indexable.AddProperty (Beagle.Property.NewUnsearched ("fixme:" + sline[0], sline[1]));
208 } else if (sline [0].StartsWith ("Name")) {
209 if (sline [0] == "Name")
210 have_name = true;
211 indexable.AddProperty (Beagle.Property.NewKeyword ("fixme:" + sline[0], sline[1]));
212 } else if (sline[0].StartsWith ("Comment")) {
213 indexable.AddProperty (Beagle.Property.New ("fixme:" + sline[0], sline[1]));
216 reader.Close ();
218 if (have_name) {
219 Scheduler.Task task = NewAddTask (indexable);
220 task.Priority = priority;
221 ThisScheduler.Add (task);