Merged older cs.po file with newest pot file.
[gliv/czech_localization.git] / src / tree.c
blob97eac3501979c6a7d8d69c1b9162988c353417d7
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /************************************************************
22 * The filenames tree, shown in images menus and serialized *
23 ************************************************************/
25 #include <string.h> /* strchr(), strrchr(), strlen() */
27 #include "gliv.h"
28 #include "tree.h"
29 #include "str_utils.h"
30 #include "files_list.h"
31 #include "timestamp.h"
33 /* Last made tree. */
34 static GNode *last_tree = NULL;
36 /* Its creation date. */
37 static DECLARE_TIMESTAMP(timestamp);
39 /* We make sure there is only one user. */
40 static volatile gboolean cancel = FALSE;
41 G_LOCK_DEFINE_STATIC(tree);
43 void end_using_tree(void)
45 cancel = FALSE;
46 G_UNLOCK(tree);
49 gboolean cancel_using_tree(void)
51 cancel = TRUE;
52 return FALSE;
55 gboolean canceled_using_tree(void)
57 return cancel;
60 /* Constructor. */
61 static tree_item *new_item(gchar * name, gchar * path)
63 tree_item *item;
65 item = g_new(tree_item, 1);
66 item->name = name;
67 item->path = path;
68 item->thumb_key = NULL;
69 item->thumb = NULL;
71 return item;
74 typedef enum {
75 DIR_PARENT,
76 DIR_EQUAL,
77 DIR_DIFFERENT
78 } dir_type;
80 /* Whether dir1 is parent, equal or different from the dir where file2 is. */
81 static dir_type dir_wrt_file(const gchar * dir1, const gchar * file2)
83 const gchar *ptr1;
84 gchar *ptr2, *last_slash;
85 dir_type res;
87 if (dir1[0] == '\0')
88 /* dir1 == "". */
89 return DIR_PARENT;
91 if (dir1[0] == '/' && dir1[1] == '\0' && file2[0] == '/')
92 /* dir1 == "/" and file2 == "/...". */
93 return strchr(file2 + 1, '/') == NULL ? DIR_EQUAL : DIR_PARENT;
95 /* We temporarily cut the filename to have the dirname. */
96 ptr2 = (gchar *) file2;
97 last_slash = strrchr(file2, '/');
98 if (last_slash)
99 *last_slash = '\0';
101 ptr1 = dir1;
103 /* Skip identical characters in the paths. */
104 while (*ptr1 != '\0' && *ptr1 == *ptr2) {
105 ptr1++;
106 ptr2++;
109 if (*ptr1 == *ptr2)
110 res = DIR_EQUAL;
112 else if (*ptr1 == '\0' && *ptr2 == '/')
113 res = DIR_PARENT;
115 else
116 res = DIR_DIFFERENT;
118 if (last_slash)
119 *last_slash = '/';
121 return res;
124 /* When file == parent"/a/b/c...", it returns "a". */
125 static gchar *first_dir(const gchar * file, const gchar * parent)
127 const gchar *res;
128 gint prefix_length;
129 gchar *end;
131 if (parent[0] == '\0' && file[0] == '/')
132 return g_strdup("/");
134 prefix_length = common_prefix_length(file, parent);
135 file += prefix_length;
136 parent += prefix_length;
138 /* Suppress the leading separator. */
139 if (file[0] == '/')
140 file++;
142 res = file;
144 /* Keep only the first dir by looking for the second one, or the file */
145 end = strchr(file, '/');
147 /* and throwing it away. */
148 return g_strndup(res, end - res);
151 static void make_tree_rec(GNode * parent, gchar *** array_ptr)
153 gchar *this_dir;
154 gchar *name, *path;
155 GNode *node;
156 tree_item *item;
158 item = parent->data;
159 this_dir = item->path;
161 while (**array_ptr != NULL) {
162 switch (dir_wrt_file(this_dir, **array_ptr)) {
163 case DIR_PARENT:
164 /* Subdirectory => add it, then recursive call. */
165 name = first_dir(**array_ptr, this_dir);
166 path = g_build_filename(this_dir, name, NULL);
167 item = new_item(name, path);
169 node = g_node_new(item);
170 g_node_append(parent, node);
172 make_tree_rec(node, array_ptr);
173 break;
175 case DIR_EQUAL:
176 /* Same directory => add the filename, stay in the loop. */
177 name = **array_ptr + strlen(this_dir);
178 while (name[0] == '/')
179 name++;
181 item = new_item(g_strdup(name), **array_ptr);
182 g_node_append_data(parent, item);
184 /* I like stars :) */
185 do {
186 (*array_ptr)++;
187 } while (**array_ptr != NULL &&
188 g_str_equal(**array_ptr, *(*array_ptr - 1)));
189 break;
191 /* case DIR_DIFFERENT: */
192 default:
193 /* Return from the recursive calls to find a common directory. */
194 return;
199 static gboolean destroy_node(GNode * node)
201 tree_item *item = node->data;
203 if (G_NODE_IS_LEAF(node))
204 g_free(item->thumb_key);
205 else
206 g_free(item->path);
208 g_free(item->name);
209 g_free(item);
210 return FALSE;
213 static void set_parent(GNode * node, gpointer parent)
215 node->parent = parent;
218 static void compress_node(GNode * node)
220 GNode *child;
221 tree_item *item, *child_item;
222 gchar *new_name;
224 if (g_node_nth_child(node, 1) != NULL)
225 /* More than one child */
226 return;
228 item = node->data;
230 child = g_node_first_child(node);
231 child_item = child->data;
233 /* Skip one level. */
234 g_node_children_foreach(child, G_TRAVERSE_ALL, set_parent, node);
235 node->children = child->children;
237 /* Concatenate the names. */
238 new_name = g_build_filename(item->name, child_item->name, NULL);
239 g_free(item->name);
240 item->name = new_name;
242 /* Use the child path. */
243 g_free(item->path);
244 item->path = child_item->path;
245 child_item->path = NULL;
247 /* Get rid of the compressed child. */
248 destroy_node(child);
249 child->parent = NULL;
250 child->children = NULL;
251 g_node_destroy(child);
254 static gboolean to_utf8(GNode * node)
256 tree_item *item;
257 gchar *converted;
259 item = node->data;
260 converted = (gchar *) filename_to_utf8(item->name);
261 if (item->name != converted) {
262 g_free(item->name);
263 item->name = g_strdup(converted);
266 return FALSE;
270 * We change the tree structure so it is
271 * safer to use our own traverse function.
273 static void compress_tree(GNode * node)
275 if (G_NODE_IS_LEAF(node) == FALSE) {
276 g_node_children_foreach(node, G_TRAVERSE_ALL,
277 (GNodeForeachFunc) compress_tree, NULL);
278 compress_node(node);
282 static void print_tree(GNode * tree, gint level);
284 /* Can we avoid rebuilding a tree? */
285 static gboolean last_tree_ok(void)
287 timestamp_t list_timestamp = get_list_timestamp();
289 if (last_tree == NULL)
290 /* No tree to reuse. */
291 return FALSE;
293 /* Check if the list has changed. */
294 return up_to_date(timestamp, list_timestamp);
297 gboolean more_recent_than_tree(timestamp_t ts)
299 if (last_tree_ok() == FALSE)
300 return FALSE;
302 return up_to_date(ts, timestamp);
305 GNode *make_tree(void)
307 GNode *tree;
308 tree_item *item;
309 gchar **array;
310 gchar **array_ptr;
312 if (G_TRYLOCK(tree) == FALSE)
313 return NULL;
315 cancel = FALSE;
316 if (last_tree_ok())
317 return last_tree;
319 invalidate_last_tree();
321 array_ptr = array = get_sorted_files_array();
322 if (array == NULL) {
323 G_UNLOCK(tree);
324 return NULL;
327 item = new_item(g_strdup(""), g_strdup(""));
328 tree = g_node_new(item);
330 /* Build the tree. */
331 while (*array_ptr != NULL)
332 make_tree_rec(tree, &array_ptr);
334 g_free(array);
336 compress_tree(tree);
338 g_node_traverse(tree, G_POST_ORDER, G_TRAVERSE_ALL, -1,
339 (GNodeTraverseFunc) to_utf8, NULL);
341 print_tree(tree, 0);
343 last_tree = tree;
344 touch(&timestamp);
345 return tree;
348 void destroy_tree(GNode * tree)
350 g_node_traverse(tree, G_POST_ORDER, G_TRAVERSE_ALL, -1,
351 (GNodeTraverseFunc) destroy_node, NULL);
353 g_node_destroy(tree);
356 void invalidate_last_tree(void)
358 if (last_tree) {
359 destroy_tree(last_tree);
360 last_tree = NULL;
361 reset_timestamp(&timestamp);
365 static gboolean increment(GNode * unused, gint * count)
367 (*count)++;
368 return FALSE;
371 gint tree_count_files(void)
373 gint count = 0;
375 g_node_traverse(last_tree, G_IN_ORDER, G_TRAVERSE_LEAFS, -1,
376 (GNodeTraverseFunc) increment, &count);
378 return count;
381 static void print_tree(GNode * tree, gint level)
383 #if 0
384 gint i;
385 tree_item *item = tree->data;
387 for (i = 0; i < level; i++)
388 printf(" ");
390 printf("name: \"%s\", path: \"%s\"\n", item->name, item->path);
392 level++;
393 g_node_children_foreach(tree, G_TRAVERSE_ALL,
394 print_tree, GINT_TO_POINTER(level));
395 #endif