]> icculus.org git repositories - dana/openbox.git/blob - obt/link.c
Make warnings about parse problems in .desktop files "debug" messages. Most people...
[dana/openbox.git] / obt / link.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    obt/link.c for the Openbox window manager
4    Copyright (c) 2009-2011   Dana Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "obt/link.h"
20 #include "obt/bsearch.h"
21 #include "obt/ddparse.h"
22 #include "obt/paths.h"
23 #include "obt/util.h"
24 #include <glib.h>
25
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29
30 struct _ObtLink {
31     guint ref;
32
33     gchar *path; /*!< The path to the file where the link came from.
34                    Encoded in UTF-8. */
35
36     ObtLinkType type;
37     gchar *name; /*!< Specific name for the object (eg Firefox) */
38     gchar *collate_key; /*!< Internal key for sorting by name. */
39     gboolean display; /*<! When false, do not display this link in menus or
40                            launchers, etc */
41     gboolean deleted; /*<! When true, the Link could exist but is deleted
42                            for the current user */
43     gchar *generic; /*!< Generic name for the object (eg Web Browser) */
44     gchar *comment; /*!< Comment/description to display for the object */
45     gchar *icon; /*!< Name/path for an icon for the object */
46     guint env_required; /*!< The environments that must be present to use this
47                           link. */
48     guint env_restricted; /*!< The environments that must _not_ be present to
49                             use this link. */
50
51     union _ObtLinkData {
52         struct _ObtLinkApp {
53             gchar *exec; /*!< Executable to run for the app.
54                            Encoded in UTF-8. */
55             gchar *wdir; /*!< Working dir to run the app in.
56                            Encoded in UTF-8. */
57             gboolean term; /*!< Run the app in a terminal or not */
58             ObtLinkAppOpen open;
59
60             gchar **mime; /*!< Mime types the app can open */
61
62             GQuark *categories; /*!< Array of quarks representing the
63                                   application's categories */
64             gulong  n_categories; /*!< Number of categories for the app */
65
66             ObtLinkAppStartup startup;
67             gchar *startup_wmclass;
68         } app;
69         struct _ObtLinkLink {
70             gchar *addr;
71         } url;
72         struct _ObtLinkDir {
73         } dir;
74     } d;
75 };
76
77 ObtLink* obt_link_from_ddfile(const gchar *path, ObtPaths *p,
78                               const gchar *language,
79                               const gchar *country,
80                               const gchar *modifier)
81 {
82     ObtLink *link;
83     GHashTable *groups, *keys;
84     ObtDDParseGroup *g;
85     ObtDDParseValue *v;
86
87     /* parse the file, and get a hash table of the groups */
88     groups = obt_ddparse_file(path, language, country, modifier);
89     if (!groups) return NULL; /* parsing failed */
90
91     /* grab the Desktop Entry group */
92     g = g_hash_table_lookup(groups, "Desktop Entry");
93     g_assert(g != NULL);
94     /* grab the keys that appeared in the Desktop Entry group */
95     keys = obt_ddparse_group_keys(g);
96
97     /* build the ObtLink (we steal all strings from the parser) */
98     link = g_slice_new0(ObtLink);
99     link->ref = 1;
100     link->path = g_strdup(path);
101     link->display = TRUE;
102
103     v = g_hash_table_lookup(keys, "Type");
104     g_assert(v);
105     link->type = v->value.enumerable;
106
107     v = g_hash_table_lookup(keys, "Name");
108     g_assert(v);
109     link->name = v->value.string, v->value.string = NULL; /* steal it */
110     link->collate_key = g_utf8_collate_key(link->name, -1);
111
112     if ((v = g_hash_table_lookup(keys, "Hidden")))
113         link->deleted = v->value.boolean;
114
115     if ((v = g_hash_table_lookup(keys, "NoDisplay")))
116         link->display = !v->value.boolean;
117
118     if ((v = g_hash_table_lookup(keys, "GenericName")))
119         link->generic = v->value.string, v->value.string = NULL;
120
121     if ((v = g_hash_table_lookup(keys, "Comment")))
122         link->comment = v->value.string, v->value.string = NULL;
123
124     if ((v = g_hash_table_lookup(keys, "Icon")))
125         link->icon = v->value.string, v->value.string = NULL;
126
127     if ((v = g_hash_table_lookup(keys, "OnlyShowIn")))
128         link->env_required = v->value.environments;
129     else
130         link->env_required = 0;
131
132     if ((v = g_hash_table_lookup(keys, "NotShowIn")))
133         link->env_restricted = v->value.environments;
134     else
135         link->env_restricted = 0;
136
137     /* type-specific keys */
138
139     if (link->type == OBT_LINK_TYPE_APPLICATION) {
140         gchar *c;
141         gboolean percent;
142
143         v = g_hash_table_lookup(keys, "Exec");
144         g_assert(v);
145         link->d.app.exec = v->value.string;
146         v->value.string = NULL;
147
148         /* parse link->d.app.exec to determine link->d.app.open */
149         percent = FALSE;
150         for (c = link->d.app.exec; *c; ++c) {
151             if (percent) {
152                 switch (*c) {
153                 case 'f': link->d.app.open = OBT_LINK_APP_SINGLE_LOCAL; break;
154                 case 'F': link->d.app.open = OBT_LINK_APP_MULTI_LOCAL; break;
155                 case 'u': link->d.app.open = OBT_LINK_APP_SINGLE_URL; break;
156                 case 'U': link->d.app.open = OBT_LINK_APP_MULTI_URL; break;
157                 default: percent = FALSE;
158                 }
159                 if (percent) break; /* found f/F/u/U */
160             }
161             else if (*c == '%') percent = TRUE;
162         }
163
164         if ((v = g_hash_table_lookup(keys, "TryExec"))) {
165             /* XXX spawn a thread to check TryExec? */
166             link->display = link->display &&
167                 obt_paths_try_exec(p, v->value.string);
168         }
169
170         if ((v = g_hash_table_lookup(keys, "Path"))) {
171             /* steal the string */
172             link->d.app.wdir = v->value.string;
173             v->value.string = NULL;
174         }
175
176         if ((v = g_hash_table_lookup(keys, "Terminal")))
177             link->d.app.term = v->value.boolean;
178
179         if ((v = g_hash_table_lookup(keys, "StartupNotify")))
180             link->d.app.startup = v->value.boolean ?
181                 OBT_LINK_APP_STARTUP_PROTOCOL_SUPPORT :
182                 OBT_LINK_APP_STARTUP_NO_SUPPORT;
183         else {
184             link->d.app.startup = OBT_LINK_APP_STARTUP_LEGACY_SUPPORT;
185             if ((v = g_hash_table_lookup(keys, "StartupWMClass"))) {
186                 /* steal the string */
187                 link->d.app.startup_wmclass = v->value.string;
188                 v->value.string = NULL;
189             }
190         }
191
192         if ((v = g_hash_table_lookup(keys, "Categories"))) {
193             gulong i;
194
195             link->d.app.categories = g_new(GQuark, v->value.strings.n);
196             link->d.app.n_categories = v->value.strings.n;
197
198             for (i = 0; i < v->value.strings.n; ++i) {
199                 link->d.app.categories[i] =
200                     g_quark_from_string(v->value.strings.a[i]);
201             }
202         }
203
204         if ((v = g_hash_table_lookup(keys, "MimeType"))) {
205             /* steal the string array */
206             link->d.app.mime = v->value.strings.a;
207             v->value.strings.a = NULL;
208             v->value.strings.n = 0;
209         }
210     }
211     else if (link->type == OBT_LINK_TYPE_URL) {
212         v = g_hash_table_lookup(keys, "URL");
213         g_assert(v);
214         link->d.url.addr = v->value.string;
215         v->value.string = NULL;
216     }
217
218     /* destroy the parsing info */
219     g_hash_table_destroy(groups);
220
221     return link;
222 }
223
224 void obt_link_ref(ObtLink *dd)
225 {
226     ++dd->ref;
227 }
228
229 void obt_link_unref(ObtLink *dd)
230 {
231     if (--dd->ref < 1) {
232         g_free(dd->name);
233         g_free(dd->collate_key);
234         g_free(dd->generic);
235         g_free(dd->comment);
236         g_free(dd->icon);
237         if (dd->type == OBT_LINK_TYPE_APPLICATION) {
238             g_free(dd->d.app.exec);
239             g_free(dd->d.app.wdir);
240             g_strfreev(dd->d.app.mime);
241             g_free(dd->d.app.categories);
242             g_free(dd->d.app.startup_wmclass);
243         }
244         else if (dd->type == OBT_LINK_TYPE_URL)
245             g_free(dd->d.url.addr);
246         g_slice_free(ObtLink, dd);
247     }
248 }
249
250 ObtLinkType obt_link_type (ObtLink *e)
251 {
252     g_return_val_if_fail(e != NULL, 0);
253
254     return e->type;
255 }
256
257 const GQuark* obt_link_app_categories(ObtLink *e, gulong *n)
258 {
259     g_return_val_if_fail(e != NULL, NULL);
260     g_return_val_if_fail(e->type == OBT_LINK_TYPE_APPLICATION, NULL);
261     g_return_val_if_fail(n != NULL, NULL);
262
263     *n = e->d.app.n_categories;
264     return e->d.app.categories;
265 }
266
267 const gchar *obt_link_source_file(ObtLink *e)
268 {
269     return e->path;
270 }
271
272 gchar* obt_link_id_from_ddfile(const gchar *filename)
273 {
274     return obt_ddparse_file_to_id(filename);
275 }
276
277 const gchar* obt_link_name(ObtLink *e)
278 {
279     return e->name;
280 }
281
282 const gchar* obt_link_generic_name(ObtLink *e)
283 {
284     return e->generic;
285 }
286
287 const gchar* obt_link_comment(ObtLink *e)
288 {
289     return e->comment;
290 }
291
292 gboolean obt_link_display(ObtLink *e, const guint environments)
293 {
294     return
295         /* display if the link wants to be displayed and TryExec passed */
296         e->display &&
297         /* display if no environment is required, or we match at least one of
298            the requirements */
299         (!e->env_required || (e->env_required & environments)) &&
300         /* display if no environment is restricted, or we do not match any of
301            the restrictions */
302         (!e->env_restricted || !(e->env_restricted & environments));
303 }
304
305 int obt_link_cmp_by_name(const void *a, const void *b)
306 {
307     const ObtLink *const la = *(ObtLink**)a, *const lb = *(ObtLink**)b;
308     int r = strcmp(la->collate_key, lb->collate_key);
309     if (r) return r;
310     /* fallback to differentiating on the path */
311     return strcmp(la->path, lb->path);
312 }