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;
58 ObtLinkBaseUpdateFunc update_func;
62 static void base_entry_free(ObtLinkBaseEntry *e)
64 obt_link_unref(e->link);
65 g_slice_free(ObtLinkBaseEntry, e);
68 static void base_entry_list_free(GSList *list)
71 for (it = list; it; it = g_slist_next(it))
72 base_entry_free(it->data);
76 static GSList* find_base_entry_path(GSList *list, const gchar *full_path)
79 for (it = list; it; it = g_slist_next(it)) {
80 ObtLinkBaseEntry *e = it->data;
81 if (strcmp(obt_link_source_file(e->link), full_path) == 0)
87 /*! Finds the first entry in the list with a priority number >= @priority. */
88 static GSList* find_base_entry_priority(GSList *list, gint priority)
91 for (it = list; it; it = g_slist_next(it)) {
92 ObtLinkBaseEntry *e = it->data;
93 if (e->priority >= priority)
99 /*! Called when a change happens in the filesystem. */
100 static void update(ObtWatch *w, const gchar *base_path,
101 const gchar *sub_path,
102 const gchar *full_path,
103 ObtWatchNotifyType type,
106 ObtLinkBase *self = data;
110 gboolean add = FALSE;
112 if (!g_str_has_suffix(sub_path, ".desktop"))
113 return; /* ignore non-.desktop files */
115 id = obt_link_id_from_ddfile(sub_path);
116 list = g_hash_table_lookup(self->base, id);
119 case OBT_WATCH_SELF_REMOVED:
121 case OBT_WATCH_REMOVED:
122 it = find_base_entry_path(list, full_path);
124 /* it may be false if the link was skipped during the add because
125 it did not want to be displayed */
126 list = g_slist_delete_link(list, it);
127 base_entry_free(it->data);
129 /* this will free 'id' */
130 g_hash_table_insert(self->base, id, list);
134 case OBT_WATCH_MODIFIED:
135 it = find_base_entry_path(list, full_path);
137 /* it may be false if the link was skipped during the add because
138 it did not want to be displayed */
139 list = g_slist_delete_link(list, it);
140 base_entry_free(it->data);
141 /* this will put the modified list into the hash table */
145 case OBT_WATCH_ADDED:
146 priority = g_hash_table_lookup(self->path_to_priority, base_path);
149 /* find the first position in the list with a higher priority value */
150 if ((it = find_base_entry_priority(list, *priority))) {
151 const ObtLinkBaseEntry *e = it->data;
152 if (e->priority == *priority) {
163 link = obt_link_from_ddfile(full_path, self->paths,
164 self->language, self->country,
167 if (!obt_link_display(link, self->environments)) {
168 obt_link_unref(link);
171 ObtLinkBaseEntry *e = g_slice_new(ObtLinkBaseEntry);
172 e->priority = *priority;
174 list = g_slist_insert_before(list, it, e);
176 /* this will free 'id' */
177 g_hash_table_insert(self->base, id, list);
185 if (self->update_func)
186 self->update_func(self, self->update_data);
189 ObtLinkBase* obt_linkbase_new(ObtPaths *paths, const gchar *locale,
197 self = g_slice_new0(ObtLinkBase);
198 self->environments = environments;
199 self->watch = obt_watch_new();
200 self->base = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
201 (GDestroyNotify)base_entry_list_free);
202 self->path_to_priority = g_hash_table_new_full(g_str_hash, g_str_equal,
205 obt_paths_ref(paths);
207 /* parse the locale string to determine the language, country, and
210 if (!locale[i] || locale[i] == '_' || locale[i] == '.' ||
213 self->language = g_strndup(locale, i);
216 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
217 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
219 if (self->language && locale[i] == '_') {
222 if (!locale[i] || locale[i] == '.' || locale[i] == '@') {
223 self->country = g_strndup(locale, i);
226 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
227 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
230 if (self->country && locale[i] == '.')
232 if (!locale[i] || locale[i] == '@')
234 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
235 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
237 if (self->country && locale[i] == '@') {
241 self->modifier = g_strndup(locale, i);
244 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
245 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
249 /* run through each directory, foo, in the XDG_DATA_DIRS, and add
250 foo/applications to the list of directories to watch here, with
251 increasing priority (decreasing importance). */
253 for (it = obt_paths_data_dirs(paths); it; it = g_slist_next(it)) {
254 if (!g_hash_table_lookup(self->path_to_priority, it->data)) {
258 base_path = g_build_filename(it->data, "applications", NULL);
260 /* add to the hash table before adding the watch. the new watch
261 will be calling our update handler, immediately with any
262 files present in the directory */
263 pri = g_new(gint, 1);
265 g_hash_table_insert(self->path_to_priority,
266 g_strdup(base_path), pri);
268 obt_watch_add(self->watch, base_path, FALSE, update, self);
276 void obt_linkbase_ref(ObtLinkBase *self)
281 void obt_linkbase_unref(ObtLinkBase *self)
283 if (--self->ref < 1) {
284 obt_watch_unref(self->watch);
285 g_hash_table_unref(self->path_to_priority);
286 g_hash_table_unref(self->base);
287 obt_paths_unref(self->paths);
288 g_slice_free(ObtLinkBase, self);
292 void obt_linkbase_set_update_func(ObtLinkBase *lb, ObtLinkBaseUpdateFunc func,
295 lb->update_func = func;
296 lb->update_data = data;