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