~jbicha/ubuntu/oneiric/gnome-shell/oneiric-3.2.2.1

« back to all changes in this revision

Viewing changes to src/shell-app-system.c

  • Committer: Package Import Robot
  • Author(s): Jeremy Bicha
  • Date: 2011-09-07 09:09:05 UTC
  • mfrom: (1.1.29 upstream)
  • Revision ID: package-import@ubuntu.com-20110907090905-kbo4fewcg12zt99u
Tags: 3.1.90.1-0ubuntu1
* New upstream release.
* debian/control: Bump build-depends on new mutter
* debian/patches/01_favorite_apps.patch: Updated
* debian/patches/03_remove-glx-dependency-on-armel.patch: Refreshed
* debian/patches/04_build-without-caribou.patch
  - Build without caribou since Ubuntu uses onboard and our System 
    Settings doesn't support choosing a different screen keyboard yet

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
#include "shell-app-private.h"
16
16
#include "shell-window-tracker-private.h"
17
17
#include "shell-global.h"
 
18
#include "shell-util.h"
18
19
#include "st.h"
19
20
 
20
 
#define GMENU_I_KNOW_THIS_IS_UNSTABLE
21
 
#include <gmenu-tree.h>
22
 
 
23
21
/* Vendor prefixes are something that can be preprended to a .desktop
24
22
 * file name.  Undo this.
25
23
 */
42
40
 
43
41
struct _ShellAppSystemPrivate {
44
42
  GMenuTree *apps_tree;
 
43
 
 
44
  GHashTable *entry_to_app;
 
45
 
 
46
  GSList *known_vendor_prefixes;
 
47
 
45
48
  GMenuTree *settings_tree;
46
 
 
47
 
  GHashTable *app_id_to_info;
48
 
  GHashTable *app_id_to_app;
49
 
 
50
 
  GSList *cached_flattened_apps; /* ShellAppInfo */
51
 
  GSList *cached_settings; /* ShellAppInfo */
52
 
  GSList *known_vendor_prefixes;
53
 
 
54
 
  gint app_monitor_id;
55
 
 
56
 
  guint app_change_timeout_id;
 
49
  GHashTable *setting_entry_to_app;
57
50
};
58
51
 
59
 
static char *shell_app_info_get_prefix (ShellAppInfo *info);
60
52
static void shell_app_system_finalize (GObject *object);
61
 
static gboolean on_tree_changed (gpointer user_data);
62
 
static void on_tree_changed_cb (GMenuTree *tree, gpointer user_data);
63
 
static void reread_menus (ShellAppSystem *self);
 
53
static void on_apps_tree_changed_cb (GMenuTree *tree, gpointer user_data);
 
54
static void on_settings_tree_changed_cb (GMenuTree *tree, gpointer user_data);
64
55
 
65
56
G_DEFINE_TYPE(ShellAppSystem, shell_app_system, G_TYPE_OBJECT);
66
57
 
67
 
typedef enum {
68
 
  SHELL_APP_INFO_TYPE_ENTRY,
69
 
  SHELL_APP_INFO_TYPE_DESKTOP_FILE,
70
 
  SHELL_APP_INFO_TYPE_WINDOW
71
 
} ShellAppInfoType;
72
 
 
73
 
struct _ShellAppInfo {
74
 
  ShellAppInfoType type;
75
 
 
76
 
  /* We need this for two reasons.  First, GKeyFile doesn't have a refcount.
77
 
   * http://bugzilla.gnome.org/show_bug.cgi?id=590808
78
 
   *
79
 
   * But more generally we'll always need it so we know when to free this
80
 
   * structure (short of weak references on each item).
81
 
   */
82
 
  guint refcount;
83
 
 
84
 
  char *casefolded_name;
85
 
  char *name_collation_key;
86
 
  char *casefolded_description;
87
 
  char *casefolded_exec;
88
 
 
89
 
  GMenuTreeItem *entry;
90
 
 
91
 
  GKeyFile *keyfile;
92
 
  char *keyfile_path;
93
 
 
94
 
  MetaWindow *window;
95
 
  char *window_id;
96
 
};
97
 
 
98
 
ShellAppInfo*
99
 
shell_app_info_ref (ShellAppInfo *info)
100
 
{
101
 
  info->refcount++;
102
 
  return info;
103
 
}
104
 
 
105
 
void
106
 
shell_app_info_unref (ShellAppInfo *info)
107
 
{
108
 
  if (--info->refcount > 0)
109
 
    return;
110
 
 
111
 
  g_free (info->casefolded_name);
112
 
  g_free (info->name_collation_key);
113
 
  g_free (info->casefolded_description);
114
 
 
115
 
  switch (info->type)
116
 
  {
117
 
  case SHELL_APP_INFO_TYPE_ENTRY:
118
 
    gmenu_tree_item_unref (info->entry);
119
 
    break;
120
 
  case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
121
 
    g_key_file_free (info->keyfile);
122
 
    g_free (info->keyfile_path);
123
 
    break;
124
 
  case SHELL_APP_INFO_TYPE_WINDOW:
125
 
    g_object_unref (info->window);
126
 
    g_free (info->window_id);
127
 
    break;
128
 
  }
129
 
  g_slice_free (ShellAppInfo, info);
130
 
}
131
 
 
132
 
static ShellAppInfo *
133
 
shell_app_info_new_from_tree_item (GMenuTreeItem *item)
134
 
{
135
 
  ShellAppInfo *info;
136
 
 
137
 
  if (!item)
138
 
    return NULL;
139
 
 
140
 
  info = g_slice_alloc0 (sizeof (ShellAppInfo));
141
 
  info->type = SHELL_APP_INFO_TYPE_ENTRY;
142
 
  info->refcount = 1;
143
 
  info->entry = gmenu_tree_item_ref (item);
144
 
  return info;
145
 
}
146
 
 
147
 
static ShellAppInfo *
148
 
shell_app_info_new_from_window (MetaWindow *window)
149
 
{
150
 
  ShellAppInfo *info;
151
 
 
152
 
  info = g_slice_alloc0 (sizeof (ShellAppInfo));
153
 
  info->type = SHELL_APP_INFO_TYPE_WINDOW;
154
 
  info->refcount = 1;
155
 
  info->window = g_object_ref (window);
156
 
  /* For windows, its id is simply its pointer address as a string.
157
 
   * There are various other alternatives, but the address is unique
158
 
   * and unchanging, which is pretty much the best we can do.
159
 
   */
160
 
  info->window_id = g_strdup_printf ("window:%p", window);
161
 
  return info;
162
 
}
163
 
 
164
58
static void shell_app_system_class_init(ShellAppSystemClass *klass)
165
59
{
166
60
  GObjectClass *gobject_class = (GObjectClass *)klass;
188
82
                                                   SHELL_TYPE_APP_SYSTEM,
189
83
                                                   ShellAppSystemPrivate);
190
84
 
191
 
  /* The key is owned by the value */
192
 
  priv->app_id_to_info = g_hash_table_new_full (g_str_hash, g_str_equal,
193
 
                                                NULL, (GDestroyNotify) shell_app_info_unref);
194
 
 
195
 
  /* Key is owned by info */
196
 
  priv->app_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
 
85
  priv->entry_to_app = g_hash_table_new_full (NULL, NULL,
 
86
                                              (GDestroyNotify)gmenu_tree_item_unref,
 
87
                                              (GDestroyNotify)g_object_unref);
 
88
  priv->setting_entry_to_app = g_hash_table_new_full (NULL, NULL,
 
89
                                                      (GDestroyNotify)gmenu_tree_item_unref,
 
90
                                                      (GDestroyNotify)g_object_unref);
197
91
 
198
92
  /* For now, we want to pick up Evince, Nautilus, etc.  We'll
199
93
   * handle NODISPLAY semantics at a higher level or investigate them
200
94
   * case by case.
201
95
   */
202
 
  priv->apps_tree = gmenu_tree_lookup ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
203
 
  priv->settings_tree = gmenu_tree_lookup ("gnomecc.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
204
 
 
205
 
  priv->app_change_timeout_id = 0;
206
 
 
207
 
  gmenu_tree_add_monitor (priv->apps_tree, on_tree_changed_cb, self);
208
 
  gmenu_tree_add_monitor (priv->settings_tree, on_tree_changed_cb, self);
209
 
 
210
 
  reread_menus (self);
 
96
  priv->apps_tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
 
97
  g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_apps_tree_changed_cb), self);
 
98
 
 
99
  priv->settings_tree = gmenu_tree_new ("gnomecc.menu", 0);
 
100
  g_signal_connect (priv->settings_tree, "changed", G_CALLBACK (on_settings_tree_changed_cb), self);
 
101
 
 
102
  on_apps_tree_changed_cb (priv->apps_tree, self);
 
103
  on_settings_tree_changed_cb (priv->settings_tree, self);
211
104
}
212
105
 
213
106
static void
216
109
  ShellAppSystem *self = SHELL_APP_SYSTEM (object);
217
110
  ShellAppSystemPrivate *priv = self->priv;
218
111
 
219
 
  gmenu_tree_remove_monitor (priv->apps_tree, on_tree_changed_cb, self);
220
 
  gmenu_tree_remove_monitor (priv->settings_tree, on_tree_changed_cb, self);
221
 
 
222
 
  gmenu_tree_unref (priv->apps_tree);
223
 
  gmenu_tree_unref (priv->settings_tree);
224
 
 
225
 
  g_hash_table_destroy (priv->app_id_to_info);
226
 
  g_hash_table_destroy (priv->app_id_to_app);
227
 
 
228
 
  g_slist_foreach (priv->cached_flattened_apps, (GFunc)shell_app_info_unref, NULL);
229
 
  g_slist_free (priv->cached_flattened_apps);
230
 
  priv->cached_flattened_apps = NULL;
 
112
  g_object_unref (priv->apps_tree);
 
113
  g_object_unref (priv->settings_tree);
 
114
 
 
115
  g_hash_table_destroy (priv->entry_to_app);
 
116
  g_hash_table_destroy (priv->setting_entry_to_app);
231
117
 
232
118
  g_slist_foreach (priv->known_vendor_prefixes, (GFunc)g_free, NULL);
233
119
  g_slist_free (priv->known_vendor_prefixes);
234
120
  priv->known_vendor_prefixes = NULL;
235
121
 
236
 
  g_slist_foreach (priv->cached_settings, (GFunc)shell_app_info_unref, NULL);
237
 
  g_slist_free (priv->cached_settings);
238
 
  priv->cached_settings = NULL;
239
 
 
240
 
  G_OBJECT_CLASS (shell_app_system_parent_class)->finalize(object);
 
122
  G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
241
123
}
242
124
 
243
 
static GSList *
244
 
gather_entries_recurse (ShellAppSystem     *monitor,
245
 
                        GSList             *apps,
246
 
                        GHashTable         *unique,
247
 
                        GMenuTreeDirectory *root)
 
125
static char *
 
126
get_prefix_for_entry (GMenuTreeEntry *entry)
248
127
{
249
 
  GSList *contents;
250
 
  GSList *iter;
251
 
 
252
 
  contents = gmenu_tree_directory_get_contents (root);
253
 
 
254
 
  for (iter = contents; iter; iter = iter->next)
255
 
    {
256
 
      GMenuTreeItem *item = iter->data;
257
 
      switch (gmenu_tree_item_get_type (item))
258
 
        {
259
 
          case GMENU_TREE_ITEM_ENTRY:
260
 
            {
261
 
              ShellAppInfo *app = shell_app_info_new_from_tree_item (item);
262
 
              if (!g_hash_table_lookup (unique, shell_app_info_get_id (app)))
263
 
                {
264
 
                  apps = g_slist_prepend (apps, app);
265
 
                  g_hash_table_insert (unique, (char*)shell_app_info_get_id (app), app);
266
 
                }
267
 
            }
268
 
            break;
269
 
          case GMENU_TREE_ITEM_DIRECTORY:
270
 
            {
271
 
              GMenuTreeDirectory *dir = (GMenuTreeDirectory*)item;
272
 
              apps = gather_entries_recurse (monitor, apps, unique, dir);
273
 
            }
274
 
            break;
275
 
          default:
276
 
            break;
277
 
        }
278
 
      gmenu_tree_item_unref (item);
279
 
    }
280
 
 
281
 
  g_slist_free (contents);
282
 
 
283
 
  return apps;
 
128
  char *prefix = NULL, *file_prefix = NULL;
 
129
  const char *id;
 
130
  GFile *file;
 
131
  char *name;
 
132
  int i = 0;
 
133
 
 
134
  id = gmenu_tree_entry_get_desktop_file_id (entry);
 
135
  file = g_file_new_for_path (gmenu_tree_entry_get_desktop_file_path (entry));
 
136
  name = g_file_get_basename (file);
 
137
 
 
138
  if (!name)
 
139
    {
 
140
      g_object_unref (file);
 
141
      return NULL;
 
142
    }
 
143
  for (i = 0; vendor_prefixes[i]; i++)
 
144
    {
 
145
      if (g_str_has_prefix (name, vendor_prefixes[i]))
 
146
        {
 
147
          file_prefix = g_strdup (vendor_prefixes[i]);
 
148
          break;
 
149
        }
 
150
    }
 
151
 
 
152
  while (strcmp (name, id) != 0)
 
153
    {
 
154
      char *t;
 
155
      char *pname;
 
156
      GFile *parent = g_file_get_parent (file);
 
157
 
 
158
      if (!parent)
 
159
        {
 
160
          g_warn_if_reached ();
 
161
          break;
 
162
        }
 
163
 
 
164
      pname = g_file_get_basename (parent);
 
165
      if (!pname)
 
166
        {
 
167
          g_object_unref (parent);
 
168
          break;
 
169
        }
 
170
      if (!g_strstr_len (id, -1, pname))
 
171
        {
 
172
          /* handle <LegacyDir prefix="..."> */
 
173
          char *t;
 
174
          size_t name_len = strlen (name);
 
175
          size_t id_len = strlen (id);
 
176
          char *t_id = g_strdup (id);
 
177
 
 
178
          t_id[id_len - name_len] = '\0';
 
179
          t = g_strdup(t_id);
 
180
          g_free (prefix);
 
181
          g_free (t_id);
 
182
          g_free (name);
 
183
          name = g_strdup (id);
 
184
          prefix = t;
 
185
 
 
186
          g_object_unref (file);
 
187
          file = parent;
 
188
          g_free (pname);
 
189
          g_free (file_prefix);
 
190
          file_prefix = NULL;
 
191
          break;
 
192
        }
 
193
 
 
194
      t = g_strconcat (pname, "-", name, NULL);
 
195
      g_free (name);
 
196
      name = t;
 
197
 
 
198
      t = g_strconcat (pname, "-", prefix, NULL);
 
199
      g_free (prefix);
 
200
      prefix = t;
 
201
 
 
202
      g_object_unref (file);
 
203
      file = parent;
 
204
      g_free (pname);
 
205
    }
 
206
 
 
207
  if (file)
 
208
    g_object_unref (file);
 
209
 
 
210
  if (strcmp (name, id) == 0)
 
211
    {
 
212
      g_free (name);
 
213
      if (file_prefix && !prefix)
 
214
        return file_prefix;
 
215
      if (file_prefix)
 
216
        {
 
217
          char *t = g_strconcat (prefix, "-", file_prefix, NULL);
 
218
          g_free (prefix);
 
219
          g_free (file_prefix);
 
220
          prefix = t;
 
221
        }
 
222
      return prefix;
 
223
    }
 
224
 
 
225
  g_free (name);
 
226
  g_free (prefix);
 
227
  g_free (file_prefix);
 
228
  g_return_val_if_reached (NULL);
284
229
}
285
230
 
286
231
static void
287
 
reread_entries (ShellAppSystem     *self,
288
 
                GSList            **cache,
289
 
                GHashTable         *unique,
290
 
                GMenuTree          *tree)
 
232
load_app_entry (ShellAppSystem *self,
 
233
                GMenuTreeEntry *entry)
291
234
{
292
 
  GMenuTreeDirectory *trunk;
293
 
 
294
 
  trunk = gmenu_tree_get_root_directory (tree);
295
 
 
296
 
  g_slist_foreach (*cache, (GFunc)shell_app_info_unref, NULL);
297
 
  g_slist_free (*cache);
298
 
  *cache = NULL;
299
 
 
300
 
  if (!trunk)
301
 
    {
302
 
      *cache = NULL;
303
 
    }
 
235
  char *prefix;
 
236
  ShellApp *app;
 
237
 
 
238
  if (g_hash_table_lookup (self->priv->entry_to_app, entry))
 
239
    return;
 
240
 
 
241
  prefix = get_prefix_for_entry (entry);
 
242
 
 
243
  if (prefix
 
244
      && !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix,
 
245
                               (GCompareFunc)g_strcmp0))
 
246
    self->priv->known_vendor_prefixes = g_slist_append (self->priv->known_vendor_prefixes,
 
247
                                                        prefix);
304
248
  else
305
 
    {
306
 
      *cache = gather_entries_recurse (self, *cache, unique, trunk);
307
 
      gmenu_tree_item_unref (trunk);
308
 
    }
309
 
}
310
 
 
311
 
static void
312
 
cache_by_id (ShellAppSystem *self, GSList *apps)
313
 
{
314
 
  GSList *iter;
315
 
 
316
 
  for (iter = apps; iter; iter = iter->next)
317
 
    {
318
 
      ShellAppInfo *info = iter->data;
319
 
      const char *id = shell_app_info_get_id (info);
320
 
      char *prefix = shell_app_info_get_prefix (info);
321
 
 
322
 
      shell_app_info_ref (info);
323
 
      /* the name is owned by the info itself */
324
 
 
325
 
      if (prefix
326
 
          && !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix,
327
 
                                   (GCompareFunc)g_strcmp0))
328
 
        self->priv->known_vendor_prefixes = g_slist_append (self->priv->known_vendor_prefixes,
329
 
                                                            prefix);
330
 
      else
331
 
        g_free (prefix);
332
 
      g_hash_table_replace (self->priv->app_id_to_info, (char*)id, info);
333
 
    }
334
 
}
335
 
 
336
 
static void
337
 
reread_menus (ShellAppSystem *self)
338
 
{
339
 
  GHashTable *unique = g_hash_table_new (g_str_hash, g_str_equal);
340
 
 
 
249
    g_free (prefix);
 
250
 
 
251
  app = _shell_app_new (entry);
 
252
 
 
253
  g_hash_table_insert (self->priv->entry_to_app, gmenu_tree_item_ref (entry), app);
 
254
}
 
255
 
 
256
static void
 
257
gather_apps_recurse (ShellAppSystem     *self,
 
258
                     GMenuTreeDirectory *root)
 
259
{
 
260
  GMenuTreeIter *iter = gmenu_tree_directory_iter (root);
 
261
  GMenuTreeItemType next_type;
 
262
 
 
263
  while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
 
264
    {
 
265
      gpointer item = NULL;
 
266
 
 
267
      switch (next_type)
 
268
        {
 
269
        case GMENU_TREE_ITEM_ENTRY:
 
270
          {
 
271
            item = gmenu_tree_iter_get_entry (iter);
 
272
            load_app_entry (self, (GMenuTreeEntry*)item);
 
273
          }
 
274
          break;
 
275
        case GMENU_TREE_ITEM_DIRECTORY:
 
276
          {
 
277
            item = gmenu_tree_iter_get_directory (iter);
 
278
            gather_apps_recurse (self, (GMenuTreeDirectory*)item);
 
279
          }
 
280
          break;
 
281
        default:
 
282
          break;
 
283
        }
 
284
      if (item != NULL)
 
285
        gmenu_tree_item_unref (item);
 
286
    }
 
287
 
 
288
  gmenu_tree_iter_unref (iter);
 
289
}
 
290
 
 
291
static void
 
292
gather_settings_recurse (ShellAppSystem     *self,
 
293
                         GMenuTreeDirectory *root)
 
294
{
 
295
  GMenuTreeIter *iter = gmenu_tree_directory_iter (root);
 
296
  GMenuTreeItemType next_type;
 
297
 
 
298
  while ((next_type = gmenu_tree_iter_next (iter)) != GMENU_TREE_ITEM_INVALID)
 
299
    {
 
300
      gpointer item = NULL;
 
301
 
 
302
      switch (next_type)
 
303
        {
 
304
        case GMENU_TREE_ITEM_ENTRY:
 
305
          {
 
306
            ShellApp *app;
 
307
 
 
308
            item = gmenu_tree_iter_get_entry (iter);
 
309
            if (g_hash_table_lookup (self->priv->setting_entry_to_app, item))
 
310
              return;
 
311
            
 
312
            app = _shell_app_new (item);
 
313
 
 
314
            g_hash_table_insert (self->priv->setting_entry_to_app, gmenu_tree_item_ref (item), app);
 
315
          }
 
316
          break;
 
317
        case GMENU_TREE_ITEM_DIRECTORY:
 
318
          {
 
319
            item = gmenu_tree_iter_get_directory (iter);
 
320
            gather_settings_recurse (self, (GMenuTreeDirectory*)item);
 
321
          }
 
322
          break;
 
323
        default:
 
324
          break;
 
325
        }
 
326
      if (item != NULL)
 
327
        gmenu_tree_item_unref (item);
 
328
    }
 
329
 
 
330
  gmenu_tree_iter_unref (iter);
 
331
}
 
332
 
 
333
static void
 
334
on_apps_tree_changed_cb (GMenuTree *tree,
 
335
                         gpointer   user_data)
 
336
{
 
337
  ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
 
338
  GError *error = NULL;
 
339
  GMenuTreeDirectory *root;
 
340
 
 
341
  g_assert (tree == self->priv->apps_tree);
 
342
 
 
343
  g_hash_table_remove_all (self->priv->entry_to_app);
341
344
  g_slist_foreach (self->priv->known_vendor_prefixes, (GFunc)g_free, NULL);
342
345
  g_slist_free (self->priv->known_vendor_prefixes);
343
346
  self->priv->known_vendor_prefixes = NULL;
344
347
 
345
 
  reread_entries (self, &(self->priv->cached_flattened_apps), unique, self->priv->apps_tree);
346
 
  g_hash_table_remove_all (unique);
347
 
  reread_entries (self, &(self->priv->cached_settings), unique, self->priv->settings_tree);
348
 
  g_hash_table_destroy (unique);
349
 
 
350
 
  g_hash_table_remove_all (self->priv->app_id_to_info);
351
 
 
352
 
  cache_by_id (self, self->priv->cached_flattened_apps);
353
 
  cache_by_id (self, self->priv->cached_settings);
354
 
}
355
 
 
356
 
static gboolean
357
 
on_tree_changed (gpointer user_data)
358
 
{
359
 
  ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
360
 
 
361
 
  reread_menus (self);
 
348
  if (!gmenu_tree_load_sync (self->priv->apps_tree, &error))
 
349
    {
 
350
      g_warning ("Failed to load apps: %s", error->message);
 
351
      return;
 
352
    }
 
353
 
 
354
  root = gmenu_tree_get_root_directory (self->priv->apps_tree);
 
355
 
 
356
  if (root)
 
357
    {
 
358
      gather_apps_recurse (self, root);
 
359
      gmenu_tree_item_unref (root);
 
360
    }
362
361
 
363
362
  g_signal_emit (self, signals[INSTALLED_CHANGED], 0);
364
 
 
365
 
  self->priv->app_change_timeout_id = 0;
366
 
  return FALSE;
367
363
}
368
364
 
369
365
static void
370
 
on_tree_changed_cb (GMenuTree *monitor, gpointer user_data)
 
366
on_settings_tree_changed_cb (GMenuTree *tree,
 
367
                             gpointer   user_data)
371
368
{
372
369
  ShellAppSystem *self = SHELL_APP_SYSTEM (user_data);
373
 
 
374
 
  /* GMenu currently gives us a separate notification on the entire
375
 
   * menu tree for each node in the tree that might potentially have
376
 
   * changed. (See http://bugzilla.gnome.org/show_bug.cgi?id=172046.)
377
 
   * We need to compress these to avoid doing large extra amounts of
378
 
   * work.
379
 
   *
380
 
   * Even when that bug is fixed, compression is still useful; for one
381
 
   * thing we want to need to compress across notifications of changes
382
 
   * to the settings tree. Second we want to compress if multiple
383
 
   * changes are made to the desktop files at different times but in
384
 
   * short succession.
385
 
   */
386
 
 
387
 
  if (self->priv->app_change_timeout_id != 0)
388
 
    return;
389
 
  self->priv->app_change_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 3000,
390
 
                                                          (GSourceFunc) on_tree_changed,
391
 
                                                          self, NULL);
392
 
}
393
 
 
394
 
GType
395
 
shell_app_info_get_type (void)
396
 
{
397
 
  static GType gtype = G_TYPE_INVALID;
398
 
  if (gtype == G_TYPE_INVALID)
399
 
    {
400
 
      gtype = g_boxed_type_register_static ("ShellAppInfo",
401
 
          (GBoxedCopyFunc)shell_app_info_ref,
402
 
          (GBoxedFreeFunc)shell_app_info_unref);
403
 
    }
404
 
  return gtype;
405
 
}
406
 
 
407
 
/**
408
 
 * shell_app_system_get_flattened_apps:
409
 
 *
410
 
 * Traverses a toplevel menu, and returns all items under it.  Nested items
411
 
 * are flattened.  This value is computed on initial call and cached thereafter
412
 
 * until the set of installed applications changes.
413
 
 *
414
 
 * Return value: (transfer none) (element-type ShellAppInfo): List of applications
415
 
 */
416
 
GSList *
417
 
shell_app_system_get_flattened_apps (ShellAppSystem *self)
418
 
{
419
 
  return self->priv->cached_flattened_apps;
420
 
}
421
 
 
422
 
/**
423
 
 * shell_app_system_get_all_settings:
424
 
 *
425
 
 * Returns a list of application items under "settings.menu".
426
 
 *
427
 
 * Return value: (transfer none) (element-type ShellAppInfo): List of applications
428
 
 */
429
 
GSList *
430
 
shell_app_system_get_all_settings (ShellAppSystem *monitor)
431
 
{
432
 
  return monitor->priv->cached_settings;
 
370
  GError *error = NULL;
 
371
  GMenuTreeDirectory *root;
 
372
 
 
373
  g_assert (tree == self->priv->settings_tree);
 
374
 
 
375
  g_hash_table_remove_all (self->priv->setting_entry_to_app);
 
376
  if (!gmenu_tree_load_sync (self->priv->settings_tree, &error))
 
377
    {
 
378
      g_warning ("Failed to load settings: %s", error->message);
 
379
      return;
 
380
    }
 
381
 
 
382
  root = gmenu_tree_get_root_directory (self->priv->settings_tree);
 
383
 
 
384
  if (root)
 
385
    {
 
386
      gather_settings_recurse (self, root);
 
387
      gmenu_tree_item_unref (root);
 
388
    }
 
389
}
 
390
 
 
391
/**
 
392
 * shell_app_system_get_tree:
 
393
 *
 
394
 * Return Value: (transfer none): The #GMenuTree for apps
 
395
 */
 
396
GMenuTree *
 
397
shell_app_system_get_tree (ShellAppSystem *self)
 
398
{
 
399
  return self->priv->apps_tree;
 
400
}
 
401
 
 
402
/**
 
403
 * shell_app_system_get_settings_tree:
 
404
 *
 
405
 * Return Value: (transfer none): The #GMenuTree for apps
 
406
 */
 
407
GMenuTree *
 
408
shell_app_system_get_settings_tree (ShellAppSystem *self)
 
409
{
 
410
  return self->priv->settings_tree;
 
411
}
 
412
 
 
413
/**
 
414
 * shell_app_system_lookup_setting:
 
415
 * @self:
 
416
 * @id: desktop file id
 
417
 *
 
418
 * Returns: (transfer none): Application in gnomecc.menu, or %NULL if none
 
419
 */
 
420
ShellApp *
 
421
shell_app_system_lookup_setting (ShellAppSystem *self,
 
422
                                 const char     *id)
 
423
{
 
424
  GMenuTreeEntry *entry;
 
425
  ShellApp *app;
 
426
 
 
427
  /* Actually defer to the main app set if there's overlap */
 
428
  app = shell_app_system_lookup_app (self, id);
 
429
  if (app != NULL)
 
430
    return app;
 
431
 
 
432
  entry = gmenu_tree_get_entry_by_id (self->priv->settings_tree, id);
 
433
  if (entry == NULL)
 
434
    return NULL;
 
435
  
 
436
  app = g_hash_table_lookup (self->priv->setting_entry_to_app, entry);
 
437
  if (app != NULL)
 
438
    return app;
 
439
  
 
440
  app = _shell_app_new (entry);
 
441
  g_hash_table_insert (self->priv->setting_entry_to_app, gmenu_tree_item_ref (entry), app); 
 
442
 
 
443
  return app;
433
444
}
434
445
 
435
446
/**
448
459
  return instance;
449
460
}
450
461
 
451
 
typedef struct {
452
 
  ShellAppSystem *appsys;
453
 
  ShellAppInfo *info;
454
 
} ShellAppRef;
455
 
 
456
 
static void
457
 
shell_app_system_on_app_weakref (gpointer  data,
458
 
                                 GObject  *location)
459
 
{
460
 
  ShellAppRef *ref = data;
461
 
 
462
 
  g_hash_table_remove (ref->appsys->priv->app_id_to_app, shell_app_info_get_id (ref->info));
463
 
  shell_app_info_unref (ref->info);
464
 
  g_free (ref);
465
 
}
466
 
 
467
462
/**
468
 
 * shell_app_system_get_app:
469
 
 *
470
 
 * Find or create a #ShellApp corresponding to an id; if already cached
471
 
 * elsewhere in memory, return that instance.  Otherwise, create a new
472
 
 * one.
473
 
 *
474
 
 * Return value: (transfer full): The #ShellApp for id, or %NULL if none
 
463
 * shell_app_system_lookup_app:
 
464
 *
 
465
 * Find a #ShellApp corresponding to an id.
 
466
 *
 
467
 * Return value: (transfer none): The #ShellApp for id, or %NULL if none
475
468
 */
476
469
ShellApp *
477
 
shell_app_system_get_app (ShellAppSystem   *self,
478
 
                          const char       *id)
 
470
shell_app_system_lookup_app (ShellAppSystem   *self,
 
471
                             const char       *id)
479
472
{
480
 
  ShellAppInfo *info;
481
 
  ShellApp *app;
482
 
 
483
 
  app = g_hash_table_lookup (self->priv->app_id_to_app, id);
484
 
  if (app)
485
 
    return g_object_ref (app);
486
 
 
487
 
  info = g_hash_table_lookup (self->priv->app_id_to_info, id);
488
 
  if (!info)
 
473
  GMenuTreeEntry *entry;
 
474
 
 
475
  entry = gmenu_tree_get_entry_by_id (self->priv->apps_tree, id);
 
476
  if (entry == NULL)
489
477
    return NULL;
490
478
 
491
 
  app = _shell_app_new (info);
492
 
 
493
 
  return app;
494
 
}
495
 
 
496
 
/**
497
 
 * shell_app_system_get_app_for_path:
 
479
  return g_hash_table_lookup (self->priv->entry_to_app, entry);
 
480
}
 
481
 
 
482
/**
 
483
 * shell_app_system_lookup_app_by_tree_entry:
 
484
 * @system: a #ShellAppSystem
 
485
 * @entry: a #GMenuTreeEntry
 
486
 *
 
487
 * Find a #ShellApp corresponding to a #GMenuTreeEntry.
 
488
 *
 
489
 * Return value: (transfer none): The #ShellApp for @entry, or %NULL if none
 
490
 */
 
491
ShellApp *
 
492
shell_app_system_lookup_app_by_tree_entry (ShellAppSystem  *self,
 
493
                                           GMenuTreeEntry  *entry)
 
494
{
 
495
  return g_hash_table_lookup (self->priv->entry_to_app, entry);
 
496
}
 
497
 
 
498
/**
 
499
 * shell_app_system_lookup_app_for_path:
498
500
 * @system: a #ShellAppSystem
499
501
 * @desktop_path: (type utf8): UTF-8 encoded absolute file name
500
502
 *
501
 
 * Find or create a #ShellApp corresponding to a given absolute
502
 
 * file name which must be in the standard paths (XDG_DATA_DIRS).
503
 
 * For files outside the datadirs, this function returns %NULL.
504
 
 *
505
 
 * If already cached elsewhere in memory, return that instance.
506
 
 * Otherwise, create a new one.
507
 
 *
508
 
 * Return value: (transfer full): The #ShellApp for id, or %NULL if none
 
503
 * Find or create a #ShellApp corresponding to a given absolute file
 
504
 * name which must be in the standard paths (XDG_DATA_DIRS).  For
 
505
 * files outside the datadirs, this function returns %NULL.
 
506
 *
 
507
 * Return value: (transfer none): The #ShellApp for id, or %NULL if none
509
508
 */
510
509
ShellApp *
511
 
shell_app_system_get_app_for_path (ShellAppSystem   *system,
512
 
                                   const char       *desktop_path)
 
510
shell_app_system_lookup_app_for_path (ShellAppSystem   *system,
 
511
                                      const char       *desktop_path)
513
512
{
514
513
  const char *basename;
515
 
  ShellAppInfo *info;
 
514
  const char *app_path;
 
515
  ShellApp *app;
516
516
 
517
517
  basename = g_strrstr (desktop_path, "/");
518
518
  if (basename)
520
520
  else
521
521
    basename = desktop_path;
522
522
 
523
 
  info = g_hash_table_lookup (system->priv->app_id_to_info, basename);
524
 
  if (!info)
525
 
    return NULL;
526
 
 
527
 
  if (info->type == SHELL_APP_INFO_TYPE_ENTRY)
528
 
    {
529
 
      const char *full_path = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*) info->entry);
530
 
      if (strcmp (desktop_path, full_path) != 0)
531
 
        return NULL;
532
 
    }
533
 
  else
534
 
    return NULL;
535
 
 
536
 
  return shell_app_system_get_app (system, basename);
537
 
}
538
 
 
539
 
/**
540
 
 * shell_app_system_get_app_for_window:
541
 
 * @self: A #ShellAppSystem
542
 
 * @window: A #MetaWindow
543
 
 *
544
 
 * Find or create a #ShellApp for window
545
 
 *
546
 
 * Return value: (transfer full): The #ShellApp for window, or %NULL if none
547
 
 */
548
 
ShellApp *
549
 
shell_app_system_get_app_for_window (ShellAppSystem *self,
550
 
                                     MetaWindow *window)
551
 
{
552
 
  char *id = g_strdup_printf ("window:%p", window);
553
 
  ShellApp *app = g_hash_table_lookup (self->priv->app_id_to_app, id);
554
 
 
555
 
  if (app)
556
 
    g_object_ref (G_OBJECT (app));
557
 
  else
558
 
    app = _shell_app_new_for_window (window);
559
 
 
560
 
  g_free (id);
 
523
  app = shell_app_system_lookup_app (system, basename);
 
524
  if (!app)
 
525
    return NULL;
 
526
 
 
527
  app_path = gmenu_tree_entry_get_desktop_file_path (shell_app_get_tree_entry (app));
 
528
  if (strcmp (desktop_path, app_path) != 0)
 
529
    return NULL;
561
530
 
562
531
  return app;
563
532
}
564
533
 
565
 
/* ShellAppSystem ensures we have a unique instance of
566
 
 * apps per id.
567
 
 */
568
 
void
569
 
_shell_app_system_register_app (ShellAppSystem   *self,
570
 
                                ShellApp         *app)
571
 
{
572
 
  const char *id;
573
 
  ShellAppRef *ref;
574
 
 
575
 
  id = shell_app_get_id (app);
576
 
 
577
 
  g_return_if_fail (g_hash_table_lookup (self->priv->app_id_to_app, id) == NULL);
578
 
 
579
 
  ref = g_new0 (ShellAppRef, 1);
580
 
  ref->appsys = self;
581
 
  ref->info = shell_app_info_ref (_shell_app_get_info (app));
582
 
  g_hash_table_insert (self->priv->app_id_to_app, (char*)shell_app_info_get_id (ref->info), app);
583
 
  g_object_weak_ref (G_OBJECT (app), shell_app_system_on_app_weakref, ref);
584
 
}
585
 
 
586
 
/**
587
 
 * shell_app_system_create_from_window:
588
 
 *
589
 
 * In the case where we can't otherwise determine an application
590
 
 * associated with a window, this function can create a "fake"
591
 
 * application just backed by information from the window itself.
592
 
 *
593
 
 * Return value: (transfer full): A new #ShellAppInfo
594
 
 */
595
 
ShellAppInfo *
596
 
shell_app_system_create_from_window (ShellAppSystem *system, MetaWindow *window)
597
 
{
598
 
  return shell_app_info_new_from_window (window);
599
 
}
600
 
 
601
534
/**
602
535
 * shell_app_system_lookup_heuristic_basename:
603
536
 * @system: a #ShellAppSystem
607
540
 * heuristically determined application identifier
608
541
 * string, or %NULL if none.
609
542
 *
610
 
 * Returns: (transfer full): A #ShellApp for @name
 
543
 * Returns: (transfer none): A #ShellApp for @name
611
544
 */
612
545
ShellApp *
613
546
shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
614
 
                                            const char *name)
 
547
                                            const char     *name)
615
548
{
616
549
  ShellApp *result;
617
550
  GSList *prefix;
618
 
  result = shell_app_system_get_app (system, name);
 
551
 
 
552
  result = shell_app_system_lookup_app (system, name);
619
553
  if (result != NULL)
620
554
    return result;
 
555
 
621
556
  for (prefix = system->priv->known_vendor_prefixes; prefix; prefix = g_slist_next (prefix))
622
557
    {
623
558
      char *tmpid = g_strconcat ((char*)prefix->data, name, NULL);
624
 
      result = shell_app_system_get_app (system, tmpid);
 
559
      result = shell_app_system_lookup_app (system, tmpid);
625
560
      g_free (tmpid);
626
561
      if (result != NULL)
627
562
        return result;
630
565
  return NULL;
631
566
}
632
567
 
633
 
typedef enum {
634
 
  MATCH_NONE,
635
 
  MATCH_SUBSTRING, /* Not prefix, substring */
636
 
  MATCH_MULTIPLE_SUBSTRING, /* Matches multiple criteria with substrings */
637
 
  MATCH_PREFIX, /* Strict prefix */
638
 
  MATCH_MULTIPLE_PREFIX, /* Matches multiple criteria, at least one prefix */
639
 
} ShellAppInfoSearchMatch;
640
 
 
641
 
static char *
642
 
normalize_and_casefold (const char *str)
 
568
/**
 
569
 * shell_app_system_get_all:
 
570
 * @self:
 
571
 *
 
572
 * Returns: (transfer container) (element-type ShellApp): All installed applications
 
573
 */
 
574
GSList *
 
575
shell_app_system_get_all (ShellAppSystem  *self)
643
576
{
644
 
  char *normalized, *result;
645
 
 
646
 
  if (str == NULL)
647
 
    return NULL;
648
 
 
649
 
  normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
650
 
  result = g_utf8_casefold (normalized, -1);
651
 
  g_free (normalized);
 
577
  GSList *result = NULL;
 
578
  GHashTableIter iter;
 
579
  gpointer key, value;
 
580
 
 
581
  g_hash_table_iter_init (&iter, self->priv->entry_to_app);
 
582
  while (g_hash_table_iter_next (&iter, &key, &value))
 
583
    {
 
584
      ShellApp *app = value;
 
585
      if (!g_desktop_app_info_get_nodisplay (shell_app_get_app_info (app)))
 
586
        result = g_slist_prepend (result, app);
 
587
    }
652
588
  return result;
653
589
}
654
590
 
655
 
static char *
656
 
trim_exec_line (const char *str)
657
 
{
658
 
  const char *start, *end, *pos;
659
 
 
660
 
  end = strchr (str, ' ');
661
 
  if (end == NULL)
662
 
    end = str + strlen (str);
663
 
 
664
 
  start = str;
665
 
  while ((pos = strchr (start, '/')) && pos < end)
666
 
    start = ++pos;
667
 
 
668
 
  return g_strndup (start, end - start);
669
 
}
670
 
 
671
 
static void
672
 
shell_app_info_init_search_data (ShellAppInfo *info)
673
 
{
674
 
  const char *name;
675
 
  const char *exec;
676
 
  const char *comment;
677
 
  char *normalized_exec;
678
 
 
679
 
  g_assert (info->type == SHELL_APP_INFO_TYPE_ENTRY);
680
 
 
681
 
  name = gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry);
682
 
  info->casefolded_name = normalize_and_casefold (name);
683
 
 
684
 
  comment = gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry);
685
 
  info->casefolded_description = normalize_and_casefold (comment);
686
 
 
687
 
  exec = gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info->entry);
688
 
  normalized_exec = normalize_and_casefold (exec);
689
 
  info->casefolded_exec = trim_exec_line (normalized_exec);
690
 
  g_free (normalized_exec);
691
 
}
692
 
 
693
 
static ShellAppInfoSearchMatch
694
 
shell_app_info_match_terms (ShellAppInfo  *info,
695
 
                            GSList        *terms)
696
 
{
697
 
  GSList *iter;
698
 
  ShellAppInfoSearchMatch match;
699
 
 
700
 
  if (G_UNLIKELY(!info->casefolded_name))
701
 
    shell_app_info_init_search_data (info);
702
 
 
703
 
  match = MATCH_NONE;
704
 
  for (iter = terms; iter; iter = iter->next)
705
 
    {
706
 
      ShellAppInfoSearchMatch current_match;
707
 
      const char *term = iter->data;
708
 
      const char *p;
709
 
 
710
 
      current_match = MATCH_NONE;
711
 
 
712
 
      p = strstr (info->casefolded_name, term);
713
 
      if (p == info->casefolded_name)
714
 
        current_match = MATCH_PREFIX;
715
 
      else if (p != NULL)
716
 
        current_match = MATCH_SUBSTRING;
717
 
 
718
 
      p = strstr (info->casefolded_exec, term);
719
 
      if (p != NULL)
720
 
        {
721
 
          if (p == info->casefolded_exec)
722
 
            current_match = (current_match == MATCH_NONE) ? MATCH_PREFIX
723
 
                                                          : MATCH_MULTIPLE_PREFIX;
724
 
          else if (current_match < MATCH_PREFIX)
725
 
            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
726
 
                                                          : MATCH_MULTIPLE_SUBSTRING;
727
 
        }
728
 
 
729
 
      if (info->casefolded_description && current_match < MATCH_PREFIX)
730
 
        {
731
 
          /* Only do substring matches, as prefix matches are not meaningful
732
 
           * enough for descriptions
733
 
           */
734
 
          p = strstr (info->casefolded_description, term);
735
 
          if (p != NULL)
736
 
            current_match = (current_match == MATCH_NONE) ? MATCH_SUBSTRING
737
 
                                                          : MATCH_MULTIPLE_SUBSTRING;
738
 
        }
739
 
 
740
 
      if (current_match == MATCH_NONE)
741
 
        return current_match;
742
 
 
743
 
      if (current_match > match)
744
 
        match = current_match;
745
 
    }
746
 
  return match;
747
 
}
748
 
 
749
591
static gint
750
 
shell_app_info_compare (gconstpointer a,
751
 
                        gconstpointer b,
752
 
                        gpointer      data)
 
592
compare_apps_by_name (gconstpointer a,
 
593
                      gconstpointer b,
 
594
                      gpointer      data)
753
595
{
754
 
  ShellAppSystem *system = data;
755
 
  const char *id_a = a;
756
 
  const char *id_b = b;
757
 
  ShellAppInfo *info_a = g_hash_table_lookup (system->priv->app_id_to_info, id_a);
758
 
  ShellAppInfo *info_b = g_hash_table_lookup (system->priv->app_id_to_info, id_b);
759
 
 
760
 
  if (!info_a->name_collation_key)
761
 
    info_a->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_a->entry), -1);
762
 
  if (!info_b->name_collation_key)
763
 
    info_b->name_collation_key = g_utf8_collate_key (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info_b->entry), -1);
764
 
 
765
 
  return strcmp (info_a->name_collation_key, info_b->name_collation_key);
 
596
  ShellApp *app_a = (ShellApp*)a;
 
597
  ShellApp *app_b = (ShellApp*)b;
 
598
 
 
599
  return shell_app_compare_by_name (app_a, app_b);
766
600
}
767
601
 
768
602
static GSList *
773
607
                         GSList         *substring_matches)
774
608
{
775
609
  multiple_prefix_matches = g_slist_sort_with_data (multiple_prefix_matches,
776
 
                                                    shell_app_info_compare,
 
610
                                                    compare_apps_by_name,
777
611
                                                    system);
778
612
  prefix_matches = g_slist_sort_with_data (prefix_matches,
779
 
                                           shell_app_info_compare,
 
613
                                           compare_apps_by_name,
780
614
                                           system);
781
615
  multiple_substring_matches = g_slist_sort_with_data (multiple_substring_matches,
782
 
                                                       shell_app_info_compare,
 
616
                                                       compare_apps_by_name,
783
617
                                                       system);
784
618
  substring_matches = g_slist_sort_with_data (substring_matches,
785
 
                                              shell_app_info_compare,
 
619
                                              compare_apps_by_name,
786
620
                                              system);
787
621
  return g_slist_concat (multiple_prefix_matches, g_slist_concat (prefix_matches, g_slist_concat (multiple_substring_matches, substring_matches)));
788
622
}
801
635
  for (iter = terms; iter; iter = iter->next)
802
636
    {
803
637
      const char *term = iter->data;
804
 
      normalized_terms = g_slist_prepend (normalized_terms, normalize_and_casefold (term));
 
638
      normalized_terms = g_slist_prepend (normalized_terms, shell_util_normalize_and_casefold (term));
805
639
    }
806
640
  return normalized_terms;
807
641
}
808
642
 
809
 
static inline void
810
 
shell_app_system_do_match (ShellAppSystem   *system,
811
 
                           ShellAppInfo     *info,
812
 
                           GSList           *terms,
813
 
                           GSList          **multiple_prefix_results,
814
 
                           GSList          **prefix_results,
815
 
                           GSList          **multiple_substring_results,
816
 
                           GSList          **substring_results)
817
 
{
818
 
  const char *id = shell_app_info_get_id (info);
819
 
  ShellAppInfoSearchMatch match;
820
 
 
821
 
  if (shell_app_info_get_is_nodisplay (info))
822
 
    return;
823
 
 
824
 
  match = shell_app_info_match_terms (info, terms);
825
 
  switch (match)
826
 
    {
827
 
      case MATCH_NONE:
828
 
        break;
829
 
      case MATCH_MULTIPLE_PREFIX:
830
 
        *multiple_prefix_results = g_slist_prepend (*multiple_prefix_results,
831
 
                                                    (char *) id);
832
 
        break;
833
 
      case MATCH_PREFIX:
834
 
        *prefix_results = g_slist_prepend (*prefix_results, (char *) id);
835
 
        break;
836
 
      case MATCH_MULTIPLE_SUBSTRING:
837
 
        *multiple_substring_results = g_slist_prepend (*multiple_substring_results,
838
 
                                                    (char *) id);
839
 
        break;
840
 
      case MATCH_SUBSTRING:
841
 
        *substring_results = g_slist_prepend (*substring_results, (char *) id);
842
 
        break;
843
 
    }
844
 
}
845
 
 
846
643
static GSList *
847
 
shell_app_system_initial_search_internal (ShellAppSystem  *self,
848
 
                                          GSList          *terms,
849
 
                                          GSList          *source)
 
644
search_tree (ShellAppSystem *self,
 
645
             GSList         *terms,
 
646
             GHashTable     *apps)
850
647
{
851
648
  GSList *multiple_prefix_results = NULL;
852
649
  GSList *prefix_results = NULL;
853
650
  GSList *multiple_subtring_results = NULL;
854
651
  GSList *substring_results = NULL;
855
 
  GSList *iter;
856
 
  GSList *normalized_terms = normalize_terms (terms);
857
 
 
858
 
  for (iter = source; iter; iter = iter->next)
 
652
  GSList *normalized_terms;
 
653
  GHashTableIter iter;
 
654
  gpointer key, value;
 
655
 
 
656
  normalized_terms = normalize_terms (terms);
 
657
 
 
658
  g_hash_table_iter_init (&iter, apps);
 
659
  while (g_hash_table_iter_next (&iter, &key, &value))
859
660
    {
860
 
      ShellAppInfo *info = iter->data;
861
 
 
862
 
      shell_app_system_do_match (self, info, normalized_terms,
863
 
                                 &multiple_prefix_results, &prefix_results,
864
 
                                 &multiple_subtring_results, &substring_results);
 
661
      const char *id = key;
 
662
      ShellApp *app = value;
 
663
      (void)id;
 
664
      _shell_app_do_match (app, normalized_terms,
 
665
                           &multiple_prefix_results, &prefix_results,
 
666
                           &multiple_subtring_results, &substring_results);
865
667
    }
866
668
  g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
867
669
  g_slist_free (normalized_terms);
868
670
 
869
 
  return sort_and_concat_results (self, multiple_prefix_results, prefix_results, multiple_subtring_results, substring_results);
 
671
  return sort_and_concat_results (self, multiple_prefix_results, prefix_results,
 
672
                                  multiple_subtring_results, substring_results);
 
673
 
870
674
}
871
675
 
872
676
/**
873
677
 * shell_app_system_initial_search:
874
678
 * @system: A #ShellAppSystem
875
 
 * @prefs: %TRUE if we should search preferences instead of apps
876
679
 * @terms: (element-type utf8): List of terms, logical AND
877
680
 *
878
 
 * Search through applications for the given search terms.  Note that returned
879
 
 * strings are only valid until a return to the main loop.
 
681
 * Search through applications for the given search terms.
880
682
 *
881
 
 * Returns: (transfer container) (element-type utf8): List of application identifiers
 
683
 * Returns: (transfer container) (element-type ShellApp): List of applications
882
684
 */
883
685
GSList *
884
686
shell_app_system_initial_search (ShellAppSystem  *self,
885
 
                                 gboolean         prefs,
886
687
                                 GSList          *terms)
887
688
{
888
 
  return shell_app_system_initial_search_internal (self, terms,
889
 
            prefs ? self->priv->cached_settings : self->priv->cached_flattened_apps);
 
689
  return search_tree (self, terms, self->priv->entry_to_app);
890
690
}
891
691
 
892
692
/**
893
693
 * shell_app_system_subsearch:
894
694
 * @system: A #ShellAppSystem
895
 
 * @prefs: %TRUE if we should search preferences instead of apps
896
 
 * @previous_results: (element-type utf8): List of previous results
 
695
 * @previous_results: (element-type ShellApp): List of previous results
897
696
 * @terms: (element-type utf8): List of terms, logical AND
898
697
 *
899
698
 * Search through a previous result set; for more information, see
901
700
 * the same as passed to shell_app_system_initial_search().  Note that returned
902
701
 * strings are only valid until a return to the main loop.
903
702
 *
904
 
 * Returns: (transfer container) (element-type utf8): List of application identifiers
 
703
 * Returns: (transfer container) (element-type ShellApp): List of application identifiers
905
704
 */
906
705
GSList *
907
706
shell_app_system_subsearch (ShellAppSystem   *system,
908
 
                            gboolean          prefs,
909
707
                            GSList           *previous_results,
910
708
                            GSList           *terms)
911
709
{
916
714
  GSList *substring_results = NULL;
917
715
  GSList *normalized_terms = normalize_terms (terms);
918
716
 
919
 
  /* Note prefs is deliberately ignored; both apps and prefs are in app_id_to_app,
920
 
   * but we have the parameter for consistency and in case in the future
921
 
   * they're not in the same data structure.
922
 
   */
923
 
 
924
717
  for (iter = previous_results; iter; iter = iter->next)
925
718
    {
926
 
      const char *id = iter->data;
927
 
      ShellAppInfo *info;
928
 
 
929
 
      info = g_hash_table_lookup (system->priv->app_id_to_info, id);
930
 
      if (!info)
931
 
        continue;
932
 
 
933
 
      shell_app_system_do_match (system, info, normalized_terms,
934
 
                                 &multiple_prefix_results, &prefix_results,
935
 
                                 &multiple_substring_results, &substring_results);
 
719
      ShellApp *app = iter->data;
 
720
      
 
721
      _shell_app_do_match (app, normalized_terms,
 
722
                           &multiple_prefix_results, &prefix_results,
 
723
                           &multiple_substring_results, &substring_results);
936
724
    }
937
725
  g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
938
726
  g_slist_free (normalized_terms);
943
731
  return sort_and_concat_results (system, multiple_prefix_results, prefix_results, multiple_substring_results, substring_results);
944
732
}
945
733
 
946
 
const char *
947
 
shell_app_info_get_id (ShellAppInfo *info)
948
 
{
949
 
  switch (info->type)
950
 
  {
951
 
    case SHELL_APP_INFO_TYPE_ENTRY:
952
 
      return gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info->entry);
953
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
954
 
      return info->keyfile_path;
955
 
    case SHELL_APP_INFO_TYPE_WINDOW:
956
 
      return info->window_id;
957
 
  }
958
 
  g_assert_not_reached ();
959
 
  return NULL;
960
 
}
961
 
 
962
 
static char *
963
 
shell_app_info_get_prefix (ShellAppInfo *info)
964
 
{
965
 
  char *prefix = NULL, *file_prefix = NULL;
966
 
  const char *id;
967
 
  GFile *file;
968
 
  char *name;
969
 
  int i = 0;
970
 
 
971
 
  if (info->type != SHELL_APP_INFO_TYPE_ENTRY)
972
 
    return NULL;
973
 
 
974
 
  id = gmenu_tree_entry_get_desktop_file_id ((GMenuTreeEntry*)info->entry);
975
 
  file = g_file_new_for_path (gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info->entry));
976
 
  name = g_file_get_basename (file);
977
 
 
978
 
  if (!name)
979
 
    {
980
 
      g_object_unref (file);
981
 
      return NULL;
982
 
    }
983
 
  for (i = 0; vendor_prefixes[i]; i++)
984
 
    {
985
 
      if (g_str_has_prefix (name, vendor_prefixes[i]))
986
 
        {
987
 
          file_prefix = g_strdup (vendor_prefixes[i]);
988
 
          break;
989
 
        }
990
 
    }
991
 
 
992
 
  while (strcmp (name, id) != 0)
993
 
    {
994
 
      char *t;
995
 
      char *pname;
996
 
      GFile *parent = g_file_get_parent (file);
997
 
 
998
 
      if (!parent)
999
 
        {
1000
 
          g_warn_if_reached ();
1001
 
          break;
1002
 
        }
1003
 
 
1004
 
      pname = g_file_get_basename (parent);
1005
 
      if (!pname)
1006
 
        {
1007
 
          g_object_unref (parent);
1008
 
          break;
1009
 
        }
1010
 
      if (!g_strstr_len (id, -1, pname))
1011
 
        {
1012
 
          /* handle <LegacyDir prefix="..."> */
1013
 
          char *t;
1014
 
          size_t name_len = strlen (name);
1015
 
          size_t id_len = strlen (id);
1016
 
          char *t_id = g_strdup (id);
1017
 
 
1018
 
          t_id[id_len - name_len] = '\0';
1019
 
          t = g_strdup(t_id);
1020
 
          g_free (prefix);
1021
 
          g_free (t_id);
1022
 
          g_free (name);
1023
 
          name = g_strdup (id);
1024
 
          prefix = t;
1025
 
 
1026
 
          g_object_unref (file);
1027
 
          file = parent;
1028
 
          g_free (pname);
1029
 
          g_free (file_prefix);
1030
 
          file_prefix = NULL;
1031
 
          break;
1032
 
        }
1033
 
 
1034
 
      t = g_strconcat (pname, "-", name, NULL);
1035
 
      g_free (name);
1036
 
      name = t;
1037
 
 
1038
 
      t = g_strconcat (pname, "-", prefix, NULL);
1039
 
      g_free (prefix);
1040
 
      prefix = t;
1041
 
 
1042
 
      g_object_unref (file);
1043
 
      file = parent;
1044
 
      g_free (pname);
1045
 
    }
1046
 
 
1047
 
  if (file)
1048
 
    g_object_unref (file);
1049
 
 
1050
 
  if (strcmp (name, id) == 0)
1051
 
    {
1052
 
      g_free (name);
1053
 
      if (file_prefix && !prefix)
1054
 
        return file_prefix;
1055
 
      if (file_prefix)
1056
 
        {
1057
 
          char *t = g_strconcat (prefix, "-", file_prefix, NULL);
1058
 
          g_free (prefix);
1059
 
          g_free (file_prefix);
1060
 
          prefix = t;
1061
 
        }
1062
 
      return prefix;
1063
 
    }
1064
 
 
1065
 
  g_free (name);
1066
 
  g_free (prefix);
1067
 
  g_free (file_prefix);
1068
 
  g_return_val_if_reached (NULL);
1069
 
}
1070
 
 
1071
 
#define DESKTOP_ENTRY_GROUP "Desktop Entry"
1072
 
 
1073
 
char *
1074
 
shell_app_info_get_name (ShellAppInfo *info)
1075
 
{
1076
 
  switch (info->type)
1077
 
  {
1078
 
    case SHELL_APP_INFO_TYPE_ENTRY:
1079
 
      return g_strdup (gmenu_tree_entry_get_name ((GMenuTreeEntry*)info->entry));
1080
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
1081
 
      return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Name", NULL, NULL);
1082
 
    case SHELL_APP_INFO_TYPE_WINDOW:
1083
 
      {
1084
 
        const char *name;
1085
 
 
1086
 
        name = meta_window_get_wm_class (info->window);
1087
 
        if (!name)
1088
 
          name = _("Unknown");
1089
 
        return g_strdup (name);
1090
 
      }
1091
 
  }
1092
 
  g_assert_not_reached ();
1093
 
  return NULL;
1094
 
}
1095
 
 
1096
 
char *
1097
 
shell_app_info_get_description (ShellAppInfo *info)
1098
 
{
1099
 
  switch (info->type)
1100
 
  {
1101
 
    case SHELL_APP_INFO_TYPE_ENTRY:
1102
 
      return g_strdup (gmenu_tree_entry_get_comment ((GMenuTreeEntry*)info->entry));
1103
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
1104
 
      return g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Comment", NULL, NULL);
1105
 
    case SHELL_APP_INFO_TYPE_WINDOW:
1106
 
      return NULL;
1107
 
  }
1108
 
  g_assert_not_reached ();
1109
 
  return NULL;
1110
 
}
1111
 
 
1112
 
char *
1113
 
shell_app_info_get_executable (ShellAppInfo *info)
1114
 
{
1115
 
  switch (info->type)
1116
 
  {
1117
 
    case SHELL_APP_INFO_TYPE_ENTRY:
1118
 
      return g_strdup (gmenu_tree_entry_get_exec ((GMenuTreeEntry*)info->entry));
1119
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
1120
 
      return g_key_file_get_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Exec", NULL);
1121
 
    case SHELL_APP_INFO_TYPE_WINDOW:
1122
 
      return NULL;
1123
 
  }
1124
 
  g_assert_not_reached ();
1125
 
  return NULL;
1126
 
}
1127
 
 
1128
 
char *
1129
 
shell_app_info_get_desktop_file_path (ShellAppInfo *info)
1130
 
{
1131
 
  switch (info->type)
1132
 
  {
1133
 
    case SHELL_APP_INFO_TYPE_ENTRY:
1134
 
      return g_strdup (gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry*)info->entry));
1135
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
1136
 
      return g_strdup (info->keyfile_path);
1137
 
    case SHELL_APP_INFO_TYPE_WINDOW:
1138
 
      return NULL;
1139
 
  }
1140
 
  g_assert_not_reached ();
1141
 
  return NULL;
1142
 
}
1143
 
 
1144
 
static GIcon *
1145
 
themed_icon_from_name (const char *iconname)
1146
 
{
1147
 
  GIcon *icon;
1148
 
 
1149
 
  if (!iconname)
1150
 
     return NULL;
1151
 
 
1152
 
  if (g_path_is_absolute (iconname))
1153
 
    {
1154
 
      GFile *file;
1155
 
      file = g_file_new_for_path (iconname);
1156
 
      icon = G_ICON (g_file_icon_new (file));
1157
 
      g_object_unref (file);
1158
 
     }
1159
 
  else
1160
 
    {
1161
 
      char *tmp_name, *p;
1162
 
      tmp_name = strdup (iconname);
1163
 
      /* Work around a common mistake in desktop files */
1164
 
      if ((p = strrchr (tmp_name, '.')) != NULL &&
1165
 
          (strcmp (p, ".png") == 0 ||
1166
 
           strcmp (p, ".xpm") == 0 ||
1167
 
           strcmp (p, ".svg") == 0))
1168
 
        {
1169
 
          *p = 0;
1170
 
        }
1171
 
      icon = g_themed_icon_new (tmp_name);
1172
 
      g_free (tmp_name);
1173
 
    }
1174
 
 
1175
 
  return icon;
1176
 
}
1177
 
 
1178
 
/**
1179
 
 * shell_app_info_get_icon:
1180
 
 * @info: A #ShellAppInfo
1181
 
 *
1182
 
 * Get the #GIcon associated with this app; for apps "faked" from a #MetaWindow,
1183
 
 * return %NULL.
1184
 
 *
1185
 
 * Returns: (transfer full): The icon for @info, or %NULL
1186
 
 */
1187
 
GIcon *
1188
 
shell_app_info_get_icon (ShellAppInfo *info)
1189
 
{
1190
 
  char *iconname = NULL;
1191
 
  GIcon *icon;
1192
 
 
1193
 
  /* This code adapted from gdesktopappinfo.c
1194
 
   * Copyright (C) 2006-2007 Red Hat, Inc.
1195
 
   * Copyright © 2007 Ryan Lortie
1196
 
   * LGPL
1197
 
   */
1198
 
 
1199
 
  switch (info->type)
1200
 
  {
1201
 
    case SHELL_APP_INFO_TYPE_ENTRY:
1202
 
      return themed_icon_from_name (gmenu_tree_entry_get_icon ((GMenuTreeEntry*)info->entry));
1203
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
1204
 
      iconname = g_key_file_get_locale_string (info->keyfile, DESKTOP_ENTRY_GROUP, "Icon", NULL, NULL);
1205
 
      icon = themed_icon_from_name (iconname);
1206
 
      g_free (iconname);
1207
 
      return icon;
1208
 
      break;
1209
 
    case SHELL_APP_INFO_TYPE_WINDOW:
1210
 
      return NULL;
1211
 
  }
1212
 
  g_assert_not_reached ();
1213
 
  return NULL;
1214
 
}
1215
 
 
1216
 
/**
1217
 
 * shell_app_system_get_sections:
1218
 
 *
1219
 
 * return names of sections in applications menu.
1220
 
 *
1221
 
 * Returns: (element-type utf8) (transfer full): List of Names
1222
 
 */
1223
 
GList *
1224
 
shell_app_system_get_sections (ShellAppSystem *system)
1225
 
{
1226
 
  GList *res = NULL;
1227
 
  GSList *i, *contents;
1228
 
  GMenuTreeDirectory *root;
1229
 
 
1230
 
  root = gmenu_tree_get_root_directory (system->priv->apps_tree);
1231
 
 
1232
 
  if (G_UNLIKELY (!root))
1233
 
    g_error ("applications.menu not found.");
1234
 
 
1235
 
  contents = gmenu_tree_directory_get_contents (root);
1236
 
 
1237
 
  for (i = contents; i; i = i->next)
1238
 
    {
1239
 
      GMenuTreeItem *item = i->data;
1240
 
      if (gmenu_tree_item_get_type (item) == GMENU_TREE_ITEM_DIRECTORY)
1241
 
        {
1242
 
          char *name = g_strdup (gmenu_tree_directory_get_name ((GMenuTreeDirectory*)item));
1243
 
 
1244
 
          g_assert (name);
1245
 
 
1246
 
          res = g_list_append (res, name);
1247
 
        }
1248
 
      gmenu_tree_item_unref (item);
1249
 
    }
1250
 
 
1251
 
  g_slist_free (contents);
1252
 
 
1253
 
  return res;
1254
 
}
1255
 
 
1256
 
/**
1257
 
 * shell_app_info_get_section:
1258
 
 *
1259
 
 * return name of section, that contain this application.
1260
 
 * Returns: (transfer full): section name
1261
 
 */
1262
 
char *
1263
 
shell_app_info_get_section (ShellAppInfo *info)
1264
 
{
1265
 
  char *name;
1266
 
  GMenuTreeDirectory *dir, *parent;
1267
 
 
1268
 
  if (info->type != SHELL_APP_INFO_TYPE_ENTRY)
1269
 
    return NULL;
1270
 
 
1271
 
  dir = gmenu_tree_item_get_parent ((GMenuTreeItem*)info->entry);
1272
 
  if (!dir)
1273
 
    return NULL;
1274
 
 
1275
 
  parent = gmenu_tree_item_get_parent ((GMenuTreeItem*)dir);
1276
 
  if (!parent)
1277
 
    return NULL;
1278
 
 
1279
 
  while (TRUE)
1280
 
    {
1281
 
      GMenuTreeDirectory *pparent = gmenu_tree_item_get_parent ((GMenuTreeItem*)parent);
1282
 
      if (!pparent)
1283
 
        break;
1284
 
      gmenu_tree_item_unref ((GMenuTreeItem*)dir);
1285
 
      dir = parent;
1286
 
      parent = pparent;
1287
 
    }
1288
 
 
1289
 
  name = g_strdup (gmenu_tree_directory_get_name (dir));
1290
 
 
1291
 
  gmenu_tree_item_unref ((GMenuTreeItem*)dir);
1292
 
  gmenu_tree_item_unref ((GMenuTreeItem*)parent);
1293
 
  return name;
1294
 
}
1295
 
 
1296
 
gboolean
1297
 
shell_app_info_get_is_nodisplay (ShellAppInfo *info)
1298
 
{
1299
 
  switch (info->type)
1300
 
  {
1301
 
    case SHELL_APP_INFO_TYPE_ENTRY:
1302
 
      return gmenu_tree_entry_get_is_nodisplay ((GMenuTreeEntry*)info->entry);
1303
 
    case SHELL_APP_INFO_TYPE_DESKTOP_FILE:
1304
 
    case SHELL_APP_INFO_TYPE_WINDOW:
1305
 
      return FALSE;
1306
 
  }
1307
 
  g_assert_not_reached ();
1308
 
  return TRUE;
1309
 
}
1310
 
 
1311
 
/**
1312
 
 * shell_app_info_is_transient:
1313
 
 *
1314
 
 * A "transient" application is one which represents
1315
 
 * just an open window, i.e. we don't know how to launch it
1316
 
 * again.
1317
 
 */
1318
 
gboolean
1319
 
shell_app_info_is_transient (ShellAppInfo *info)
1320
 
{
1321
 
  return info->type == SHELL_APP_INFO_TYPE_WINDOW;
1322
 
}
1323
 
 
1324
 
/**
1325
 
 * shell_app_info_create_icon_texture:
1326
 
 *
1327
 
 * Look up the icon for this application, and create a #ClutterTexture
1328
 
 * for it at the given size.
1329
 
 *
1330
 
 * Return value: (transfer none): A floating #ClutterActor
1331
 
 */
1332
 
ClutterActor *
1333
 
shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
1334
 
{
1335
 
  GIcon *icon;
1336
 
  ClutterActor *ret;
1337
 
 
1338
 
  ret = NULL;
1339
 
 
1340
 
  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
1341
 
    {
1342
 
      ret = st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
1343
 
                                                   G_OBJECT (info->window),
1344
 
                                                   "icon");
1345
 
    }
1346
 
  else
1347
 
    {
1348
 
      icon = shell_app_info_get_icon (info);
1349
 
      if (icon != NULL)
1350
 
        {
1351
 
          ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, (int)size);
1352
 
          g_object_unref (icon);
1353
 
        }
1354
 
    }
1355
 
 
1356
 
  if (ret == NULL)
1357
 
    {
1358
 
      icon = g_themed_icon_new ("application-x-executable");
1359
 
      ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), NULL, icon, (int)size);
1360
 
      g_object_unref (icon);
1361
 
    }
1362
 
 
1363
 
  return ret;
1364
 
}
1365
 
 
1366
 
/**
1367
 
 * shell_app_info_get_source_window:
1368
 
 * @info: A #ShellAppInfo
1369
 
 *
1370
 
 * Returns: (transfer none): If @info is tracking a #MetaWindow,
1371
 
 *   return that window.  Otherwise, return %NULL.
1372
 
 */
1373
 
MetaWindow *
1374
 
shell_app_info_get_source_window (ShellAppInfo *info)
1375
 
{
1376
 
  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
1377
 
    return info->window;
1378
 
  return NULL;
1379
 
}
1380
 
 
1381
 
static void
1382
 
_gather_pid_callback (GDesktopAppInfo   *gapp,
1383
 
                      GPid               pid,
1384
 
                      gpointer           data)
1385
 
{
1386
 
  ShellApp *app;
1387
 
  ShellWindowTracker *tracker;
1388
 
 
1389
 
  g_return_if_fail (data != NULL);
1390
 
 
1391
 
  app = SHELL_APP (data);
1392
 
  tracker = shell_window_tracker_get_default ();
1393
 
 
1394
 
  _shell_window_tracker_add_child_process_app (tracker,
1395
 
                                               pid,
1396
 
                                               app);
1397
 
}
1398
 
 
1399
 
/**
1400
 
 * shell_app_info_launch_full:
1401
 
 * @timestamp: Event timestamp, or 0 for current event timestamp
1402
 
 * @uris: List of uris to pass to application
1403
 
 * @workspace: Start on this workspace, or -1 for default
1404
 
 * @startup_id: (out): Returned startup notification ID, or %NULL if none
1405
 
 * @error: A #GError
1406
 
 */
1407
 
gboolean
1408
 
shell_app_info_launch_full (ShellAppInfo *info,
1409
 
                            guint         timestamp,
1410
 
                            GList        *uris,
1411
 
                            int           workspace,
1412
 
                            char        **startup_id,
1413
 
                            GError      **error)
1414
 
{
1415
 
  ShellApp *shell_app;
1416
 
  GDesktopAppInfo *gapp;
1417
 
  GdkAppLaunchContext *context;
1418
 
  gboolean ret;
1419
 
  ShellGlobal *global;
1420
 
  MetaScreen *screen;
1421
 
 
1422
 
  if (startup_id)
1423
 
    *startup_id = NULL;
1424
 
 
1425
 
  if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
1426
 
    {
1427
 
      /* We can't pass URIs into a window; shouldn't hit this
1428
 
       * code path.  If we do, fix the caller to disallow it.
1429
 
       */
1430
 
      g_return_val_if_fail (uris == NULL, TRUE);
1431
 
 
1432
 
      meta_window_activate (info->window, timestamp);
1433
 
      return TRUE;
1434
 
    }
1435
 
  else if (info->type == SHELL_APP_INFO_TYPE_ENTRY)
1436
 
    {
1437
 
      /* Can't use g_desktop_app_info_new, see bug 614879 */
1438
 
      const char *filename = gmenu_tree_entry_get_desktop_file_path ((GMenuTreeEntry *)info->entry);
1439
 
      gapp = g_desktop_app_info_new_from_filename (filename);
1440
 
    }
1441
 
  else
1442
 
    {
1443
 
      char *filename = shell_app_info_get_desktop_file_path (info);
1444
 
      gapp = g_desktop_app_info_new_from_filename (filename);
1445
 
      g_free (filename);
1446
 
    }
1447
 
 
1448
 
  if (!gapp)
1449
 
    {
1450
 
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Not found");
1451
 
      return FALSE;
1452
 
    }
1453
 
 
1454
 
  global = shell_global_get ();
1455
 
  screen = shell_global_get_screen (global);
1456
 
 
1457
 
  if (timestamp == 0)
1458
 
    timestamp = clutter_get_current_event_time ();
1459
 
 
1460
 
  if (workspace < 0)
1461
 
    workspace = meta_screen_get_active_workspace_index (screen);
1462
 
 
1463
 
  context = gdk_app_launch_context_new ();
1464
 
  gdk_app_launch_context_set_timestamp (context, timestamp);
1465
 
  gdk_app_launch_context_set_desktop (context, workspace);
1466
 
 
1467
 
  shell_app = shell_app_system_get_app (shell_app_system_get_default (),
1468
 
                                        shell_app_info_get_id (info));
1469
 
 
1470
 
  /* In the case where we know an app, we handle reaping the child internally,
1471
 
   * in the window tracker.
1472
 
   */
1473
 
  if (shell_app != NULL)
1474
 
    ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
1475
 
                                                     G_APP_LAUNCH_CONTEXT (context),
1476
 
                                                     G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
1477
 
                                                     NULL, NULL,
1478
 
                                                     _gather_pid_callback, shell_app,
1479
 
                                                     error);
1480
 
  else
1481
 
    ret = g_desktop_app_info_launch_uris_as_manager (gapp, uris,
1482
 
                                                     G_APP_LAUNCH_CONTEXT (context),
1483
 
                                                     G_SPAWN_SEARCH_PATH,
1484
 
                                                     NULL, NULL,
1485
 
                                                     NULL, NULL,
1486
 
                                                     error);
1487
 
 
1488
 
  g_object_unref (G_OBJECT (gapp));
1489
 
 
1490
 
  return ret;
1491
 
}
1492
 
 
1493
 
gboolean
1494
 
shell_app_info_launch (ShellAppInfo    *info,
1495
 
                       GError         **error)
1496
 
{
1497
 
  return shell_app_info_launch_full (info, 0, NULL, -1, NULL, error);
 
734
/**
 
735
 * shell_app_system_search_settings:
 
736
 * @system: A #ShellAppSystem
 
737
 * @terms: (element-type utf8): List of terms, logical AND
 
738
 *
 
739
 * Search through settings for the given search terms.
 
740
 *
 
741
 * Returns: (transfer container) (element-type ShellApp): List of setting applications
 
742
 */
 
743
GSList *
 
744
shell_app_system_search_settings (ShellAppSystem  *self,
 
745
                                  GSList          *terms)
 
746
{
 
747
  return search_tree (self, terms, self->priv->setting_entry_to_app);
1498
748
}