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);
177 if (!locale[i] || locale[i] == '_' || locale[i] == '.' ||
180 self->language = g_strndup(locale, i);
183 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
184 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
186 if (self->language && locale[i] == '_') {
189 if (!locale[i] || locale[i] == '.' || locale[i] == '@') {
190 self->country = g_strndup(locale, i);
193 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
194 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
197 if (self->country && locale[i] == '.')
199 if (!locale[i] || locale[i] == '@')
201 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
202 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
204 if (self->country && locale[i] == '@') {
208 self->modifier = g_strndup(locale, i);
211 else if (((guchar)locale[i] < 'A' || (guchar)locale[i] > 'Z') &&
212 ((guchar)locale[i] < 'a' || (guchar)locale[i] > 'z'))
217 for (it = obt_paths_data_dirs(paths); it; it = g_slist_next(it)) {
218 if (!g_hash_table_lookup(self->path_to_priority, it->data)) {
222 base_path = g_build_filename(it->data, "applications", NULL);
224 /* add to the hash table before adding the watch. the new watch
225 will be calling our update handler, immediately with any
226 files present in the directory */
227 pri = g_new(gint, 1);
229 g_hash_table_insert(self->path_to_priority,
230 g_strdup(base_path), pri);
232 obt_watch_add(self->watch, base_path, FALSE, update, self);
240 void obt_linkbase_ref(ObtLinkBase *self)
245 void obt_linkbase_unref(ObtLinkBase *self)
247 if (--self->ref < 1) {
248 obt_watch_unref(self->watch);
249 g_hash_table_unref(self->path_to_priority);
250 g_hash_table_unref(self->base);
251 obt_paths_unref(self->paths);
252 g_slice_free(ObtLinkBase, self);