1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 2000, 2001 Ximian, Inc.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU Lesser General Public
8 * License as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
20 * Author: Ettore Perazzoli
27 #include "e-folder-tree.h"
33 struct Folder *parent;
38 typedef struct Folder Folder;
41 GHashTable *path_to_folder;
42 GHashTable *data_to_path;
44 EFolderDestroyNotify folder_destroy_notify;
45 void *folder_destroy_notify_closure;
48 /* Utility functions. */
51 get_parent_path (const char *path)
53 const char *last_separator;
55 g_assert (g_path_is_absolute (path));
57 last_separator = strrchr (path, '/');
59 if (last_separator == path)
60 return g_strdup ("/");
62 return g_strndup (path, last_separator - path);
66 traverse_subtree (EFolderTree *tree,
68 EFolderTreeForeachFunc foreach_func,
73 g_assert (foreach_func != NULL);
75 (* foreach_func) (tree, root_folder->path, root_folder->data, data);
77 for (p = root_folder->subfolders; p != NULL; p = p->next) {
80 folder = (Folder *) p->data;
81 traverse_subtree (tree, folder, foreach_func, data);
85 /* Folder handling. */
88 folder_new (const char *path,
93 folder = g_new0 (Folder, 1);
94 folder->path = g_strdup (path);
101 folder_remove_subfolder (Folder *folder,
104 folder->subfolders = g_list_remove (folder->subfolders, subfolder);
105 subfolder->parent = NULL;
109 folder_add_subfolder (Folder *folder,
112 folder->subfolders = g_list_prepend (folder->subfolders, subfolder);
113 subfolder->parent = folder;
117 folder_destroy (Folder *folder)
119 g_assert (folder->subfolders == NULL);
121 if (folder->parent != NULL)
122 folder_remove_subfolder (folder->parent, folder);
124 g_free (folder->path);
130 remove_folder (EFolderTree *folder_tree,
133 if (folder->subfolders != NULL) {
136 for (p = folder->subfolders; p != NULL; p = p->next) {
139 subfolder = (Folder *) p->data;
140 subfolder->parent = NULL;
141 remove_folder (folder_tree, subfolder);
144 g_list_free (folder->subfolders);
145 folder->subfolders = NULL;
148 g_hash_table_remove (folder_tree->path_to_folder, folder->path);
149 g_hash_table_remove (folder_tree->data_to_path, folder->data);
151 if (folder_tree->folder_destroy_notify != NULL)
152 (* folder_tree->folder_destroy_notify) (folder_tree,
155 folder_tree->folder_destroy_notify_closure);
157 folder_destroy (folder);
162 * @folder_destroy_notify: Function to be called when a folder gets removed from the tree
163 * @closure: Additional data to pass to @folder_destroy_notify
165 * Create a new EFolderTree.
167 * Return value: A pointer to the newly created EFolderTree.
170 e_folder_tree_new (EFolderDestroyNotify folder_destroy_notify,
175 new = g_new0 (EFolderTree, 1);
177 new->folder_destroy_notify = folder_destroy_notify;
178 new->folder_destroy_notify_closure = closure;
180 new->path_to_folder = g_hash_table_new (g_str_hash, g_str_equal);
181 new->data_to_path = g_hash_table_new (g_direct_hash, g_direct_equal);
183 e_folder_tree_add (new, "/", NULL);
189 * e_folder_tree_destroy:
190 * @folder_tree: A pointer to an EFolderTree
192 * Destroy @folder_tree.
195 e_folder_tree_destroy (EFolderTree *folder_tree)
199 g_return_if_fail (folder_tree != NULL);
201 root_folder = g_hash_table_lookup (folder_tree->path_to_folder, "/");
202 remove_folder (folder_tree, root_folder);
204 g_hash_table_destroy (folder_tree->path_to_folder);
205 g_hash_table_destroy (folder_tree->data_to_path);
207 g_free (folder_tree);
212 * @folder_tree: A pointer to an EFolderTree
213 * @path: Path at which the new folder must be added
214 * @data: Data associated with the new folder
216 * Insert a new folder at @path, with the specified @data.
218 * Return value: %TRUE if successful, %FALSE if failed.
221 e_folder_tree_add (EFolderTree *folder_tree,
225 Folder *parent_folder;
227 const char *existing_path;
230 g_return_val_if_fail (folder_tree != NULL, FALSE);
231 g_return_val_if_fail (path != NULL, FALSE);
232 g_return_val_if_fail (g_path_is_absolute (path), FALSE);
234 /* Can only "add" a new root folder if the tree is empty */
235 if (! strcmp (path, "/")) {
236 folder = g_hash_table_lookup (folder_tree->path_to_folder, path);
238 if (folder->subfolders) {
239 g_warning ("e_folder_tree_add() -- Trying to change root folder after adding children");
242 remove_folder (folder_tree, folder);
245 folder = folder_new (path, data);
246 g_hash_table_insert (folder_tree->path_to_folder, folder->path, folder);
247 g_hash_table_insert (folder_tree->data_to_path, data, folder->path);
251 parent_path = get_parent_path (path);
253 parent_folder = g_hash_table_lookup (folder_tree->path_to_folder, parent_path);
254 if (parent_folder == NULL) {
255 g_warning ("e_folder_tree_add() -- Trying to add a subfolder to a path that does not exist yet -- %s",
257 g_free (parent_path);
260 g_free (parent_path);
262 folder = g_hash_table_lookup (folder_tree->path_to_folder, path);
263 if (folder != NULL) {
264 g_warning ("e_folder_tree_add() -- Trying to add a subfolder for a path that already exists -- %s",
269 existing_path = g_hash_table_lookup (folder_tree->data_to_path, data);
270 if (existing_path != NULL) {
271 g_warning ("e_folder_tree_add() -- Trying to add a folder with duplicate data -- %s",
276 folder = folder_new (path, data);
277 folder_add_subfolder (parent_folder, folder);
279 g_hash_table_insert (folder_tree->path_to_folder, folder->path, folder);
280 g_hash_table_insert (folder_tree->data_to_path, data, folder->path);
286 * e_folder_tree_remove:
287 * @folder_tree: A pointer to an EFolderTree
288 * @path: Path of the folder to remove
290 * Remove the folder at @path from @folder_tree.
292 * Return value: %TRUE if successful, %FALSE if failed.
295 e_folder_tree_remove (EFolderTree *folder_tree,
300 g_return_val_if_fail (folder_tree != NULL, FALSE);
301 g_return_val_if_fail (path != NULL, FALSE);
302 g_return_val_if_fail (g_path_is_absolute (path), FALSE);
304 folder = g_hash_table_lookup (folder_tree->path_to_folder, path);
308 remove_folder (folder_tree, folder);
313 count_nodes (EFolderTree *tree,
318 int *count = closure;
324 * e_folder_tree_get_count:
325 * @folder_tree: A pointer to an EFolderTree
327 * Gets the number of folders in the tree
329 * Return value: The number of folders in the tree
332 e_folder_tree_get_count (EFolderTree *folder_tree)
336 e_folder_tree_foreach (folder_tree, count_nodes, &count);
342 * e_folder_tree_get_folder:
343 * @folder_tree: A pointer to an EFolderTree
344 * @path: Path of the folder for which we want to get the data
346 * Get the data for the folder at @path.
348 * Return value: The pointer to the data for the folder at @path.
351 e_folder_tree_get_folder (EFolderTree *folder_tree,
356 g_return_val_if_fail (folder_tree != NULL, NULL);
357 g_return_val_if_fail (path != NULL, NULL);
358 g_return_val_if_fail (g_path_is_absolute (path), NULL);
360 folder = g_hash_table_lookup (folder_tree->path_to_folder, path);
368 * e_folder_tree_get_subfolders:
369 * @folder_tree: A pointer to an EFolderTree
370 * @path: A path in @folder_tree
372 * Get a list of the paths of the subfolders of @path.
374 * Return value: A list of pointers to the paths of the subfolders. The list
375 * and the strings must be freed by the caller.
378 e_folder_tree_get_subfolders (EFolderTree *folder_tree,
385 g_return_val_if_fail (folder_tree != NULL, NULL);
386 g_return_val_if_fail (path != NULL, NULL);
387 g_return_val_if_fail (g_path_is_absolute (path), NULL);
389 folder = g_hash_table_lookup (folder_tree->path_to_folder, path);
394 for (p = folder->subfolders; p != NULL; p = p->next) {
395 const Folder *folder;
397 folder = (const Folder *) p->data;
398 list = g_list_prepend (list, g_strdup (folder->path));
405 * e_folder_tree_foreach:
410 * Call @foreach_func with the specified @data for all the folders
411 * in @folder_tree, starting at the root node.
414 e_folder_tree_foreach (EFolderTree *folder_tree,
415 EFolderTreeForeachFunc foreach_func,
420 g_return_if_fail (folder_tree != NULL);
421 g_return_if_fail (foreach_func != NULL);
423 root_node = g_hash_table_lookup (folder_tree->path_to_folder, "/");
424 if (root_node == NULL) {
425 g_warning ("e_folder_tree_foreach -- What?! No root node!?");
429 traverse_subtree (folder_tree, root_node, foreach_func, data);
434 * e_folder_tree_get_path_for_data:
435 * @folder_tree: A pointer to an EFolderTree
436 * @data: The data for the folder for which the path is needed
438 * Look up the path for the specified @data.
440 * Return value: The path for the folder that holds that @data.
443 e_folder_tree_get_path_for_data (EFolderTree *folder_tree,
446 g_return_val_if_fail (folder_tree != NULL, NULL);
447 g_return_val_if_fail (data != NULL, NULL);
449 return (const char *) g_hash_table_lookup (folder_tree->data_to_path, data);