]> icculus.org git repositories - dana/openbox.git/blob - obt/link.c
Move the GSource attach out to the generic watch code, and avoid blocking reads
[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        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/ddparse.h"
21 #include "obt/paths.h"
22 #include <glib.h>
23
24 struct _ObtLink {
25     guint ref;
26
27     gchar *path; /*!< The path to the file where the link came from */
28
29     ObtLinkType type;
30     gchar *name; /*!< Specific name for the object (eg Firefox) */
31     gboolean display; /*<! When false, do not display this link in menus or
32                            launchers, etc */
33     gboolean deleted; /*<! When true, the Link could exist but is deleted
34                            for the current user */
35     gchar *generic; /*!< Generic name for the object (eg Web Browser) */
36     gchar *comment; /*!< Comment/description to display for the object */
37     gchar *icon; /*!< Name/path for an icon for the object */
38     guint env_required; /*!< The environments that must be present to use this
39                           link. */
40     guint env_restricted; /*!< The environments that must _not_ be present to
41                             use this link. */
42
43     union _ObtLinkData {
44         struct _ObtLinkApp {
45             gchar *exec; /*!< Executable to run for the app */
46             gchar *wdir; /*!< Working dir to run the app in */
47             gboolean term; /*!< Run the app in a terminal or not */
48             ObtLinkAppOpen open;
49
50             gchar **mime; /*!< Mime types the app can open */
51
52             GQuark *categories; /*!< Array of quarks representing the
53                                   application's categories */
54             gulong  n_categories; /*!< Number of categories for the app */
55
56             ObtLinkAppStartup startup;
57             gchar *startup_wmclass;
58         } app;
59         struct _ObtLinkLink {
60             gchar *addr;
61         } url;
62         struct _ObtLinkDir {
63         } dir;
64     } d;
65 };
66
67 ObtLink* obt_link_from_ddfile(const gchar *path, ObtPaths *p,
68                               const gchar *language,
69                               const gchar *country,
70                               const gchar *modifier)
71 {
72     ObtLink *link;
73     GHashTable *groups, *keys;
74     ObtDDParseGroup *g;
75     ObtDDParseValue *v;
76
77     /* parse the file, and get a hash table of the groups */
78     groups = obt_ddparse_file(path, language, country, modifier);
79     if (!groups) return NULL; /* parsing failed */
80
81     /* grab the Desktop Entry group */
82     g = g_hash_table_lookup(groups, "Desktop Entry");
83     g_assert(g != NULL);
84     /* grab the keys that appeared in the Desktop Entry group */
85     keys = obt_ddparse_group_keys(g);
86
87     /* build the ObtLink (we steal all strings from the parser) */
88     link = g_slice_new0(ObtLink);
89     link->ref = 1;
90     link->path = g_strdup(path);
91     link->display = TRUE;
92
93     v = g_hash_table_lookup(keys, "Type");
94     g_assert(v);
95     link->type = v->value.enumerable;
96
97     if ((v = g_hash_table_lookup(keys, "Hidden")))
98         link->deleted = v->value.boolean;
99
100     if ((v = g_hash_table_lookup(keys, "NoDisplay")))
101         link->display = !v->value.boolean;
102
103     if ((v = g_hash_table_lookup(keys, "GenericName")))
104         link->generic = v->value.string, v->value.string = NULL;
105
106     if ((v = g_hash_table_lookup(keys, "Comment")))
107         link->comment = v->value.string, v->value.string = NULL;
108
109     if ((v = g_hash_table_lookup(keys, "Icon")))
110         link->icon = v->value.string, v->value.string = NULL;
111
112     if ((v = g_hash_table_lookup(keys, "OnlyShowIn")))
113         link->env_required = v->value.environments;
114     else
115         link->env_required = 0;
116
117     if ((v = g_hash_table_lookup(keys, "NotShowIn")))
118         link->env_restricted = v->value.environments;
119     else
120         link->env_restricted = 0;
121
122     /* type-specific keys */
123
124     if (link->type == OBT_LINK_TYPE_APPLICATION) {
125         gchar *c;
126         gboolean percent;
127
128         v = g_hash_table_lookup(keys, "Exec");
129         g_assert(v);
130         link->d.app.exec = v->value.string;
131         v->value.string = NULL;
132
133         /* parse link->d.app.exec to determine link->d.app.open */
134         percent = FALSE;
135         for (c = link->d.app.exec; *c; ++c) {
136             if (percent) {
137                 switch (*c) {
138                 case 'f': link->d.app.open = OBT_LINK_APP_SINGLE_LOCAL; break;
139                 case 'F': link->d.app.open = OBT_LINK_APP_MULTI_LOCAL; break;
140                 case 'u': link->d.app.open = OBT_LINK_APP_SINGLE_URL; break;
141                 case 'U': link->d.app.open = OBT_LINK_APP_MULTI_URL; break;
142                 default: percent = FALSE;
143                 }
144                 if (percent) break; /* found f/F/u/U */
145             }
146             else if (*c == '%') percent = TRUE;
147         }
148
149         if ((v = g_hash_table_lookup(keys, "TryExec"))) {
150             /* XXX spawn a thread to check TryExec? */
151             link->display = link->display &&
152                 obt_paths_try_exec(p, v->value.string);
153         }
154
155         if ((v = g_hash_table_lookup(keys, "Path"))) {
156             /* steal the string */
157             link->d.app.wdir = v->value.string;
158             v->value.string = NULL;
159         }
160
161         if ((v = g_hash_table_lookup(keys, "Terminal")))
162             link->d.app.term = v->value.boolean;
163
164         if ((v = g_hash_table_lookup(keys, "StartupNotify")))
165             link->d.app.startup = v->value.boolean ?
166                 OBT_LINK_APP_STARTUP_PROTOCOL_SUPPORT :
167                 OBT_LINK_APP_STARTUP_NO_SUPPORT;
168         else {
169             link->d.app.startup = OBT_LINK_APP_STARTUP_LEGACY_SUPPORT;
170             if ((v = g_hash_table_lookup(keys, "StartupWMClass"))) {
171                 /* steal the string */
172                 link->d.app.startup_wmclass = v->value.string;
173                 v->value.string = NULL;
174             }
175         }
176
177         if ((v = g_hash_table_lookup(keys, "Categories"))) {
178             gulong i;
179             gchar *end;
180
181             link->d.app.categories = g_new(GQuark, v->value.strings.n);
182             link->d.app.n_categories = v->value.strings.n;
183
184             for (i = 0; i < v->value.strings.n; ++i) {
185                 link->d.app.categories[i] =
186                     g_quark_from_string(v->value.strings.a[i]);
187                 c = end = end+1; /* next */
188             }
189         }
190
191         if ((v = g_hash_table_lookup(keys, "MimeType"))) {
192             /* steal the string array */
193             link->d.app.mime = v->value.strings.a;
194             v->value.strings.a = NULL;
195             v->value.strings.n = 0;
196         }
197     }
198     else if (link->type == OBT_LINK_TYPE_URL) {
199         v = g_hash_table_lookup(keys, "URL");
200         g_assert(v);
201         link->d.url.addr = v->value.string;
202         v->value.string = NULL;
203     }
204
205     /* destroy the parsing info */
206     g_hash_table_destroy(groups);
207
208     return link;
209 }
210
211 void obt_link_ref(ObtLink *dd)
212 {
213     ++dd->ref;
214 }
215
216 void obt_link_unref(ObtLink *dd)
217 {
218     if (--dd->ref < 1) {
219         g_free(dd->name);
220         g_free(dd->generic);
221         g_free(dd->comment);
222         g_free(dd->icon);
223         if (dd->type == OBT_LINK_TYPE_APPLICATION) {
224             g_free(dd->d.app.exec);
225             g_free(dd->d.app.wdir);
226             g_strfreev(dd->d.app.mime);
227             g_free(dd->d.app.categories);
228             g_free(dd->d.app.startup_wmclass);
229         }
230         else if (dd->type == OBT_LINK_TYPE_URL)
231             g_free(dd->d.url.addr);
232         g_slice_free(ObtLink, dd);
233     }
234 }
235
236 const GQuark* obt_link_app_categories(ObtLink *e, gulong *n)
237 {
238     g_return_val_if_fail(e != NULL, NULL);
239     g_return_val_if_fail(e->type == OBT_LINK_TYPE_APPLICATION, NULL);
240     g_return_val_if_fail(n != NULL, NULL);
241
242     *n = e->d.app.n_categories;
243     return e->d.app.categories;
244 }
245
246 const gchar *obt_link_source_file(ObtLink *e)
247 {
248     return e->path;
249 }
250
251 gchar* obt_link_id_from_ddfile(const gchar *filename)
252 {
253     return obt_ddparse_file_to_id(filename);
254 }
255
256 gboolean obt_link_display(ObtLink *e, const guint environments)
257 {
258     return
259         /* display if the link wants to be displayed and TryExec passed */
260         e->display &&
261         /* display if no environment is required, or we match at least one of
262            the requirements */
263         (!e->env_required || (e->env_required & environments)) &&
264         /* display if no environment is restricted, or we do not match any of
265            the restrictions */
266         (!e->env_restricted || !(e->env_restricted & environments));
267 }