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 const gchar *language;
43 const gchar *modifier;
47 /*! This holds a GSList of ObtLinkBaseEntrys sorted by priority in
48 increasing order (by precedence in decreasing order). */
50 /*! This holds the paths in which we look for links, and the data is an
51 integer that is the priority of that directory. */
52 GHashTable *path_to_priority;
55 static void base_entry_free(ObtLinkBaseEntry *e)
57 obt_link_unref(e->link);
58 g_slice_free(ObtLinkBaseEntry, e);
61 static void base_entry_list_free(GSList *list)
64 for (it = list; it; it = g_slist_next(it))
65 base_entry_free(it->data);
69 static GSList* find_base_entry_path(GSList *list, const gchar *full_path)
72 for (it = list; it; it = g_slist_next(it)) {
73 ObtLinkBaseEntry *e = it->data;
74 if (strcmp(obt_link_source_file(e->link), full_path) == 0)
80 /*! Finds the first entry in the list with a priority number >= @priority. */
81 static GSList* find_base_entry_priority(GSList *list, gint priority)
84 for (it = list; it; it = g_slist_next(it)) {
85 ObtLinkBaseEntry *e = it->data;
86 if (e->priority >= priority)
92 /*! Called when a change happens in the filesystem. */
93 static void update(ObtWatch *w, const gchar *base_path,
94 const gchar *sub_path,
95 const gchar *full_path,
96 ObtWatchNotifyType type,
99 ObtLinkBase *self = data;
103 gboolean add = FALSE;
105 if (!g_str_has_suffix(sub_path, ".desktop"))
106 return; /* ignore non-.desktop files */
108 id = obt_link_id_from_ddfile(sub_path);
109 list = g_hash_table_lookup(self->base, id);
112 case OBT_WATCH_SELF_REMOVED:
114 case OBT_WATCH_REMOVED:
115 it = find_base_entry_path(list, full_path);
116 list = g_slist_delete_link(list, it);
117 base_entry_free(it->data);
119 /* this will free 'id' */
120 g_hash_table_insert(self->base, id, list);
123 case OBT_WATCH_MODIFIED:
124 it = find_base_entry_path(list, full_path);
125 list = g_slist_delete_link(list, it);
126 base_entry_free(it->data);
127 add = TRUE; /* this will put the modified list into the hash table */
129 case OBT_WATCH_ADDED:
130 priority = g_hash_table_lookup(self->path_to_priority, base_path);
133 /* find the first position in the list with a higher priority value */
134 if ((it = find_base_entry_priority(list, *priority))) {
135 const ObtLinkBaseEntry *e = it->data;
136 if (e->priority == *priority) {
145 ObtLinkBaseEntry *e = g_slice_new(ObtLinkBaseEntry);
146 e->priority = *priority;
147 e->link = obt_link_from_ddfile(full_path, self->paths,
148 self->language, self->country,
150 list = g_slist_insert_before(list, it, e);
152 /* this will free 'id' */
153 g_hash_table_insert(self->base, id, list);
160 ObtLinkBase* obt_linkbase_new(ObtPaths *paths, const gchar *locale)
167 self = g_slice_new0(ObtLinkBase);
168 self->watch = obt_watch_new();
169 self->base = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
170 (GDestroyNotify)base_entry_list_free);
171 self->path_to_priority = g_hash_table_new_full(g_str_hash, g_str_equal,
174 obt_paths_ref(paths);
176 /* parse the locale string to determine the language, country, and
179 if (!locale[i] || locale[i] == '_' || locale[i] == '.' ||
182 self->language = g_strndup(locale, i);
185 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
186 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
188 if (self->language && locale[i] == '_') {
191 if (!locale[i] || locale[i] == '.' || locale[i] == '@') {
192 self->country = g_strndup(locale, i);
195 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
196 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
199 if (self->country && locale[i] == '.')
201 if (!locale[i] || locale[i] == '@')
203 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
204 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
206 if (self->country && locale[i] == '@') {
210 self->modifier = g_strndup(locale, i);
213 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
214 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
218 /* run through each directory, foo, in the XDG_DATA_DIRS, and add
219 foo/applications to the list of directories to watch here, with
220 increasing priority (decreasing importance). */
222 for (it = obt_paths_data_dirs(paths); it; it = g_slist_next(it)) {
223 if (!g_hash_table_lookup(self->path_to_priority, it->data)) {
227 base_path = g_build_filename(it->data, "applications", NULL);
229 /* add to the hash table before adding the watch. the new watch
230 will be calling our update handler, immediately with any
231 files present in the directory */
232 pri = g_new(gint, 1);
234 g_hash_table_insert(self->path_to_priority,
235 g_strdup(base_path), pri);
237 obt_watch_add(self->watch, base_path, FALSE, update, self);
245 void obt_linkbase_ref(ObtLinkBase *self)
250 void obt_linkbase_unref(ObtLinkBase *self)
252 if (--self->ref < 1) {
253 obt_watch_unref(self->watch);
254 g_hash_table_unref(self->path_to_priority);
255 g_hash_table_unref(self->base);
256 obt_paths_unref(self->paths);
257 g_slice_free(ObtLinkBase, self);