1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/linkbase.c for the Openbox window manager
4 Copyright (c) 2010 Dana Jansens
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.
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.
16 See the COPYING file for a copy of the GNU General Public License.
19 #include "obt/linkbase.h"
21 #include "obt/paths.h"
22 #include "obt/watch.h"
28 typedef struct _ObtLinkBaseEntry ObtLinkBaseEntry;
30 struct _ObtLinkBaseEntry {
31 /*! Links come from a set of paths. Links found in earlier paths get lower
32 priority values (higher precedence). This is the index in that set of
33 paths of the base directory under which the link was found. */
41 /*! A bitflag of values from ObtLinkEnvFlags indicating which environments
42 are to be considered active. */
45 const gchar *language;
47 const gchar *modifier;
51 /*! This holds a GSList of ObtLinkBaseEntrys sorted by priority in
52 increasing order (by precedence in decreasing order). */
54 /*! This holds the paths in which we look for links, and the data is an
55 integer that is the priority of that directory. */
56 GHashTable *path_to_priority;
59 static void base_entry_free(ObtLinkBaseEntry *e)
61 obt_link_unref(e->link);
62 g_slice_free(ObtLinkBaseEntry, e);
65 static void base_entry_list_free(GSList *list)
68 for (it = list; it; it = g_slist_next(it))
69 base_entry_free(it->data);
73 static GSList* find_base_entry_path(GSList *list, const gchar *full_path)
76 for (it = list; it; it = g_slist_next(it)) {
77 ObtLinkBaseEntry *e = it->data;
78 if (strcmp(obt_link_source_file(e->link), full_path) == 0)
84 /*! Finds the first entry in the list with a priority number >= @priority. */
85 static GSList* find_base_entry_priority(GSList *list, gint priority)
88 for (it = list; it; it = g_slist_next(it)) {
89 ObtLinkBaseEntry *e = it->data;
90 if (e->priority >= priority)
96 /*! Called when a change happens in the filesystem. */
97 static void update(ObtWatch *w, const gchar *base_path,
98 const gchar *sub_path,
99 const gchar *full_path,
100 ObtWatchNotifyType type,
103 ObtLinkBase *self = data;
107 gboolean add = FALSE;
109 if (!g_str_has_suffix(sub_path, ".desktop"))
110 return; /* ignore non-.desktop files */
112 id = obt_link_id_from_ddfile(sub_path);
113 list = g_hash_table_lookup(self->base, id);
116 case OBT_WATCH_SELF_REMOVED:
118 case OBT_WATCH_REMOVED:
119 it = find_base_entry_path(list, full_path);
120 list = g_slist_delete_link(list, it);
121 base_entry_free(it->data);
123 /* this will free 'id' */
124 g_hash_table_insert(self->base, id, list);
127 case OBT_WATCH_MODIFIED:
128 it = find_base_entry_path(list, full_path);
129 list = g_slist_delete_link(list, it);
130 base_entry_free(it->data);
131 add = TRUE; /* this will put the modified list into the hash table */
133 case OBT_WATCH_ADDED:
134 priority = g_hash_table_lookup(self->path_to_priority, base_path);
137 /* find the first position in the list with a higher priority value */
138 if ((it = find_base_entry_priority(list, *priority))) {
139 const ObtLinkBaseEntry *e = it->data;
140 if (e->priority == *priority) {
151 link = obt_link_from_ddfile(full_path, self->paths,
152 self->language, self->country,
154 if (!obt_link_display(link, self->environments)) {
155 obt_link_unref(link);
158 ObtLinkBaseEntry *e = g_slice_new(ObtLinkBaseEntry);
159 e->priority = *priority;
161 list = g_slist_insert_before(list, it, e);
163 /* this will free 'id' */
164 g_hash_table_insert(self->base, id, list);
172 ObtLinkBase* obt_linkbase_new(ObtPaths *paths, const gchar *locale,
180 self = g_slice_new0(ObtLinkBase);
181 self->environments = environments;
182 self->watch = obt_watch_new();
183 self->base = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
184 (GDestroyNotify)base_entry_list_free);
185 self->path_to_priority = g_hash_table_new_full(g_str_hash, g_str_equal,
188 obt_paths_ref(paths);
190 /* parse the locale string to determine the language, country, and
193 if (!locale[i] || locale[i] == '_' || locale[i] == '.' ||
196 self->language = g_strndup(locale, i);
199 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
200 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
202 if (self->language && locale[i] == '_') {
205 if (!locale[i] || locale[i] == '.' || locale[i] == '@') {
206 self->country = g_strndup(locale, i);
209 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
210 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
213 if (self->country && locale[i] == '.')
215 if (!locale[i] || locale[i] == '@')
217 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
218 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
220 if (self->country && locale[i] == '@') {
224 self->modifier = g_strndup(locale, i);
227 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
228 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
232 /* run through each directory, foo, in the XDG_DATA_DIRS, and add
233 foo/applications to the list of directories to watch here, with
234 increasing priority (decreasing importance). */
236 for (it = obt_paths_data_dirs(paths); it; it = g_slist_next(it)) {
237 if (!g_hash_table_lookup(self->path_to_priority, it->data)) {
241 base_path = g_build_filename(it->data, "applications", NULL);
243 /* add to the hash table before adding the watch. the new watch
244 will be calling our update handler, immediately with any
245 files present in the directory */
246 pri = g_new(gint, 1);
248 g_hash_table_insert(self->path_to_priority,
249 g_strdup(base_path), pri);
251 obt_watch_add(self->watch, base_path, FALSE, update, self);
259 void obt_linkbase_ref(ObtLinkBase *self)
264 void obt_linkbase_unref(ObtLinkBase *self)
266 if (--self->ref < 1) {
267 obt_watch_unref(self->watch);
268 g_hash_table_unref(self->path_to_priority);
269 g_hash_table_unref(self->base);
270 obt_paths_unref(self->paths);
271 g_slice_free(ObtLinkBase, self);