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-storage.h"
29 #include "e-folder-tree.h"
31 #include <glib/gi18n-lib.h>
32 #include <libedataserver/e-data-server-util.h>
36 #define PARENT_TYPE G_TYPE_OBJECT
37 static GObjectClass *parent_class = NULL;
39 struct EStoragePrivate {
40 /* The set of folders we have in this storage. */
41 EFolderTree *folder_tree;
43 /* Internal name of the storage */
54 static guint signals[LAST_SIGNAL] = { 0 };
56 /* Destroy notification function for the folders in the tree. */
59 folder_destroy_notify (EFolderTree *tree,
67 /* The root folder has no EFolder associated to it. */
71 e_folder = E_FOLDER (data);
72 g_object_unref (e_folder);
75 /* Signal callbacks for the EFolders. */
78 folder_changed_cb (EFolder *folder,
82 EStoragePrivate *priv;
86 g_assert (E_IS_STORAGE (data));
88 storage = E_STORAGE (data);
91 path = e_folder_tree_get_path_for_data (priv->folder_tree, folder);
92 g_assert (path != NULL);
94 g_signal_emit (storage, signals[UPDATED_FOLDER], 0, path);
96 highlight = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (folder), "last_highlight"));
97 if (highlight != e_folder_get_highlighted (folder)) {
98 highlight = !highlight;
99 g_object_set_data (G_OBJECT (folder), "last_highlight", GINT_TO_POINTER (highlight));
100 p = strrchr (path, '/');
101 if (p && p != path) {
104 name = g_strndup (path, p - path);
105 folder = e_folder_tree_get_folder (priv->folder_tree, name);
108 e_folder_set_child_highlight (folder, highlight);
113 /* GObject methods. */
116 impl_finalize (GObject *object)
119 EStoragePrivate *priv;
121 storage = E_STORAGE (object);
122 priv = storage->priv;
124 if (priv->folder_tree != NULL)
125 e_folder_tree_destroy (priv->folder_tree);
131 (* G_OBJECT_CLASS (parent_class)->finalize) (object);
134 /* EStorage methods. */
137 impl_get_subfolder_paths (EStorage *storage,
140 EStoragePrivate *priv;
142 priv = storage->priv;
144 return e_folder_tree_get_subfolders (priv->folder_tree, path);
148 impl_get_folder (EStorage *storage,
151 EStoragePrivate *priv;
154 priv = storage->priv;
156 e_folder = (EFolder *) e_folder_tree_get_folder (priv->folder_tree, path);
162 impl_get_name (EStorage *storage)
164 return storage->priv->name;
168 impl_async_create_folder (EStorage *storage,
171 EStorageResultCallback callback,
174 (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
178 impl_async_remove_folder (EStorage *storage,
180 EStorageResultCallback callback,
183 (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
187 impl_async_xfer_folder (EStorage *storage,
188 const char *source_path,
189 const char *destination_path,
190 gboolean remove_source,
191 EStorageResultCallback callback,
194 (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
198 impl_async_open_folder (EStorage *storage,
200 EStorageDiscoveryCallback callback,
203 (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, NULL, data);
207 impl_will_accept_folder (EStorage *storage,
211 EStoragePrivate *priv = storage->priv;
212 const char *parent_path, *source_path;
215 /* By default, we only disallow dragging a folder into
216 * a subfolder of itself.
219 if (new_parent == source)
222 parent_path = e_folder_tree_get_path_for_data (priv->folder_tree,
224 source_path = e_folder_tree_get_path_for_data (priv->folder_tree,
226 if (!parent_path || !source_path)
229 source_len = strlen (source_path);
230 if (!strncmp (parent_path, source_path, source_len) &&
231 parent_path[source_len] == '/')
238 impl_async_discover_shared_folder (EStorage *storage,
240 const char *folder_name,
241 EStorageDiscoveryCallback callback,
244 (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, NULL, data);
248 impl_async_remove_shared_folder (EStorage *storage,
250 EStorageResultCallback callback,
253 (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
256 /* Initialization. */
259 e_storage_class_init (EStorageClass *class)
261 GObjectClass *object_class;
263 object_class = G_OBJECT_CLASS (class);
264 parent_class = g_type_class_ref (PARENT_TYPE);
266 object_class->finalize = impl_finalize;
268 class->get_subfolder_paths = impl_get_subfolder_paths;
269 class->get_folder = impl_get_folder;
270 class->get_name = impl_get_name;
271 class->async_create_folder = impl_async_create_folder;
272 class->async_remove_folder = impl_async_remove_folder;
273 class->async_xfer_folder = impl_async_xfer_folder;
274 class->async_open_folder = impl_async_open_folder;
275 class->will_accept_folder = impl_will_accept_folder;
277 class->async_discover_shared_folder = impl_async_discover_shared_folder;
278 class->async_remove_shared_folder = impl_async_remove_shared_folder;
279 signals[NEW_FOLDER] =
280 g_signal_new ("new_folder",
281 G_OBJECT_CLASS_TYPE (object_class),
283 G_STRUCT_OFFSET (EStorageClass, new_folder),
285 g_cclosure_marshal_VOID__STRING,
288 signals[UPDATED_FOLDER] =
289 g_signal_new ("updated_folder",
290 G_OBJECT_CLASS_TYPE (object_class),
292 G_STRUCT_OFFSET (EStorageClass, updated_folder),
294 g_cclosure_marshal_VOID__STRING,
297 signals[REMOVED_FOLDER] =
298 g_signal_new ("removed_folder",
299 G_OBJECT_CLASS_TYPE (object_class),
301 G_STRUCT_OFFSET (EStorageClass, removed_folder),
303 g_cclosure_marshal_VOID__STRING,
309 e_storage_init (EStorage *storage)
311 EStoragePrivate *priv;
313 priv = g_new0 (EStoragePrivate, 1);
315 priv->folder_tree = e_folder_tree_new (folder_destroy_notify, NULL);
317 storage->priv = priv;
323 e_storage_construct (EStorage *storage,
325 EFolder *root_folder)
327 EStoragePrivate *priv;
329 g_return_if_fail (E_IS_STORAGE (storage));
331 priv = storage->priv;
333 priv->name = g_strdup (name);
335 e_storage_new_folder (storage, "/", root_folder);
339 e_storage_new (const char *name,
340 EFolder *root_folder)
344 new = g_object_new (e_storage_get_type (), NULL);
346 e_storage_construct (new, name, root_folder);
352 e_storage_path_is_absolute (const char *path)
354 g_return_val_if_fail (path != NULL, FALSE);
360 e_storage_path_is_relative (const char *path)
362 g_return_val_if_fail (path != NULL, FALSE);
368 e_storage_get_subfolder_paths (EStorage *storage,
371 g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
372 g_return_val_if_fail (path != NULL, NULL);
373 g_return_val_if_fail (g_path_is_absolute (path), NULL);
375 return (* E_STORAGE_GET_CLASS (storage)->get_subfolder_paths) (storage, path);
379 e_storage_get_folder (EStorage *storage,
382 g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
383 g_return_val_if_fail (path != NULL, NULL);
384 g_return_val_if_fail (e_storage_path_is_absolute (path), NULL);
386 return (* E_STORAGE_GET_CLASS (storage)->get_folder) (storage, path);
390 e_storage_get_name (EStorage *storage)
392 g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
394 return (* E_STORAGE_GET_CLASS (storage)->get_name) (storage);
397 /* Folder operations. */
400 e_storage_async_create_folder (EStorage *storage,
403 EStorageResultCallback callback,
406 g_return_if_fail (E_IS_STORAGE (storage));
407 g_return_if_fail (path != NULL);
408 g_return_if_fail (g_path_is_absolute (path));
409 g_return_if_fail (type != NULL);
410 g_return_if_fail (callback != NULL);
412 (* E_STORAGE_GET_CLASS (storage)->async_create_folder) (storage, path, type, callback, data);
416 e_storage_async_remove_folder (EStorage *storage,
418 EStorageResultCallback callback,
421 g_return_if_fail (E_IS_STORAGE (storage));
422 g_return_if_fail (path != NULL);
423 g_return_if_fail (g_path_is_absolute (path));
424 g_return_if_fail (callback != NULL);
426 (* E_STORAGE_GET_CLASS (storage)->async_remove_folder) (storage, path, callback, data);
430 e_storage_async_xfer_folder (EStorage *storage,
431 const char *source_path,
432 const char *destination_path,
433 const gboolean remove_source,
434 EStorageResultCallback callback,
437 g_return_if_fail (E_IS_STORAGE (storage));
438 g_return_if_fail (source_path != NULL);
439 g_return_if_fail (g_path_is_absolute (source_path));
440 g_return_if_fail (destination_path != NULL);
441 g_return_if_fail (g_path_is_absolute (destination_path));
443 if (strcmp (source_path, destination_path) == 0) {
444 (* callback) (storage, E_STORAGE_OK, data);
452 source_len = strlen (source_path);
453 destination_len = strlen (destination_path);
455 if (source_len < destination_len
456 && destination_path[source_len] == '/'
457 && strncmp (destination_path, source_path, source_len) == 0) {
458 (* callback) (storage, E_STORAGE_CANTMOVETODESCENDANT, data);
463 (* E_STORAGE_GET_CLASS (storage)->async_xfer_folder) (storage, source_path, destination_path, remove_source, callback, data);
467 e_storage_async_open_folder (EStorage *storage,
469 EStorageDiscoveryCallback callback,
472 EStoragePrivate *priv;
475 g_return_if_fail (E_IS_STORAGE (storage));
476 g_return_if_fail (path != NULL);
477 g_return_if_fail (g_path_is_absolute (path));
479 priv = storage->priv;
481 folder = e_folder_tree_get_folder (priv->folder_tree, path);
482 if (folder == NULL) {
483 (* callback) (storage, E_STORAGE_NOTFOUND, path, data);
487 if (! e_folder_get_has_subfolders (folder)) {
488 (* callback) (storage, E_STORAGE_OK, path, data);
492 (* E_STORAGE_GET_CLASS (storage)->async_open_folder) (storage, path, callback, data);
496 e_storage_will_accept_folder (EStorage *storage,
497 EFolder *new_parent, EFolder *source)
499 g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
500 g_return_val_if_fail (E_IS_FOLDER (new_parent), FALSE);
501 g_return_val_if_fail (E_IS_FOLDER (source), FALSE);
503 return (* E_STORAGE_GET_CLASS (storage)->will_accept_folder) (storage, new_parent, source);
506 /* Shared folders. */
509 e_storage_async_discover_shared_folder (EStorage *storage,
511 const char *folder_name,
512 EStorageDiscoveryCallback callback,
515 g_return_if_fail (E_IS_STORAGE (storage));
516 g_return_if_fail (owner != NULL);
517 g_return_if_fail (folder_name != NULL);
519 (* E_STORAGE_GET_CLASS (storage)->async_discover_shared_folder) (storage, owner, folder_name, callback, data);
523 e_storage_cancel_discover_shared_folder (EStorage *storage,
525 const char *folder_name)
527 g_return_if_fail (E_IS_STORAGE (storage));
528 g_return_if_fail (owner != NULL);
529 g_return_if_fail (folder_name != NULL);
530 g_return_if_fail (E_STORAGE_GET_CLASS (storage)->cancel_discover_shared_folder != NULL);
532 (* E_STORAGE_GET_CLASS (storage)->cancel_discover_shared_folder) (storage, owner, folder_name);
536 e_storage_async_remove_shared_folder (EStorage *storage,
538 EStorageResultCallback callback,
541 g_return_if_fail (E_IS_STORAGE (storage));
542 g_return_if_fail (path != NULL);
543 g_return_if_fail (g_path_is_absolute (path));
545 (* E_STORAGE_GET_CLASS (storage)->async_remove_shared_folder) (storage, path, callback, data);
549 e_storage_result_to_string (EStorageResult result)
553 return _("No error");
554 case E_STORAGE_GENERICERROR:
555 return _("Generic error");
556 case E_STORAGE_EXISTS:
557 return _("A folder with the same name already exists");
558 case E_STORAGE_INVALIDTYPE:
559 return _("The specified folder type is not valid");
560 case E_STORAGE_IOERROR:
561 return _("I/O error");
562 case E_STORAGE_NOSPACE:
563 return _("Not enough space to create the folder");
564 case E_STORAGE_NOTEMPTY:
565 return _("The folder is not empty");
566 case E_STORAGE_NOTFOUND:
567 return _("The specified folder was not found");
568 case E_STORAGE_NOTIMPLEMENTED:
569 return _("Function not implemented in this storage");
570 case E_STORAGE_PERMISSIONDENIED:
571 return _("Permission denied");
572 case E_STORAGE_UNSUPPORTEDOPERATION:
573 return _("Operation not supported");
574 case E_STORAGE_UNSUPPORTEDTYPE:
575 return _("The specified type is not supported in this storage");
576 case E_STORAGE_CANTCHANGESTOCKFOLDER:
577 return _("The specified folder cannot be modified or removed");
578 case E_STORAGE_CANTMOVETODESCENDANT:
579 return _("Cannot make a folder a child of one of its descendants");
580 case E_STORAGE_INVALIDNAME:
581 return _("Cannot create a folder with that name");
582 case E_STORAGE_NOTONLINE:
583 return _("This operation cannot be performed in off-line mode");
585 return _("Unknown error");
589 /* Public utility functions. */
592 const char *physical_uri;
594 } GetPathForPhysicalUriForeachData;
597 get_path_for_physical_uri_foreach (EFolderTree *folder_tree,
602 GetPathForPhysicalUriForeachData *foreach_data;
603 const char *physical_uri;
606 foreach_data = (GetPathForPhysicalUriForeachData *) user_data;
607 if (foreach_data->retval != NULL)
610 e_folder = (EFolder *) path_data;
611 if (e_folder == NULL)
614 physical_uri = e_folder_get_physical_uri (e_folder);
615 if (physical_uri == NULL)
618 if (strcmp (foreach_data->physical_uri, physical_uri) == 0)
619 foreach_data->retval = g_strdup (path);
623 * e_storage_get_path_for_physical_uri:
624 * @storage: A storage
625 * @physical_uri: A physical URI
627 * Look for the folder having the specified @physical_uri.
629 * Return value: The path of the folder having the specified @physical_uri in
630 * @storage. If such a folder does not exist, just return NULL. The return
631 * value must be freed by the caller.
634 e_storage_get_path_for_physical_uri (EStorage *storage,
635 const char *physical_uri)
637 GetPathForPhysicalUriForeachData foreach_data;
638 EStoragePrivate *priv;
640 g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
641 g_return_val_if_fail (physical_uri != NULL, NULL);
643 priv = storage->priv;
645 foreach_data.physical_uri = physical_uri;
646 foreach_data.retval = NULL;
648 e_folder_tree_foreach (priv->folder_tree, get_path_for_physical_uri_foreach, &foreach_data);
650 return foreach_data.retval;
653 /* Protected functions. */
655 /* These functions are used by subclasses to add and remove folders from the
656 state stored in the storage object. */
659 remove_subfolders_except (EStorage *storage, const char *path, const char *except)
661 EStoragePrivate *priv;
662 GList *subfolders, *f;
663 const char *folder_path;
665 priv = storage->priv;
667 subfolders = e_folder_tree_get_subfolders (priv->folder_tree, path);
668 for (f = subfolders; f; f = f->next) {
669 folder_path = f->data;
670 if (!except || strcmp (folder_path, except) != 0)
671 e_storage_removed_folder (storage, folder_path);
673 for (f = subfolders; f != NULL; f = f->next)
676 g_list_free (subfolders);
680 e_storage_new_folder (EStorage *storage,
684 EStoragePrivate *priv;
685 char *parent_path, *p;
688 g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
689 g_return_val_if_fail (path != NULL, FALSE);
690 g_return_val_if_fail (g_path_is_absolute (path), FALSE);
691 g_return_val_if_fail (E_IS_FOLDER (e_folder), FALSE);
693 priv = storage->priv;
695 if (! e_folder_tree_add (priv->folder_tree, path, e_folder))
698 /* If this is the child of a folder that has a pseudo child,
699 * remove the pseudo child now.
701 p = strrchr (path, '/');
703 parent_path = g_strndup (path, p - path);
705 parent_path = g_strdup ("/");
706 parent = e_folder_tree_get_folder (priv->folder_tree, parent_path);
707 if (parent && e_folder_get_has_subfolders (parent)) {
708 remove_subfolders_except (storage, parent_path, path);
709 e_folder_set_has_subfolders (parent, FALSE);
711 g_free (parent_path);
713 g_signal_connect_object (e_folder, "changed", G_CALLBACK (folder_changed_cb), storage, 0);
715 g_signal_emit (storage, signals[NEW_FOLDER], 0, path);
717 folder_changed_cb (e_folder, storage);
722 /* This really should be called e_storage_set_has_subfolders, but then
723 * it would look like it was an EStorageSet function. (Fact o' the
724 * day: The word "set" has more distinct meanings than any other word
725 * in the English language.) Anyway, we now return you to your
726 * regularly scheduled source code, already in progress.
729 e_storage_declare_has_subfolders (EStorage *storage,
733 EStoragePrivate *priv;
734 EFolder *parent, *pseudofolder;
735 char *pseudofolder_path;
738 g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
739 g_return_val_if_fail (path != NULL, FALSE);
740 g_return_val_if_fail (g_path_is_absolute (path), FALSE);
741 g_return_val_if_fail (message != NULL, FALSE);
743 priv = storage->priv;
745 parent = e_folder_tree_get_folder (priv->folder_tree, path);
748 if (e_folder_get_has_subfolders (parent))
751 remove_subfolders_except (storage, path, NULL);
753 pseudofolder = e_folder_new (message, "working", "");
754 if (strcmp (path, "/") == 0)
755 pseudofolder_path = g_strdup_printf ("/%s", message);
757 pseudofolder_path = g_strdup_printf ("%s/%s", path, message);
758 e_folder_set_physical_uri (pseudofolder, pseudofolder_path);
760 ok = e_storage_new_folder (storage, pseudofolder_path, pseudofolder);
761 g_free (pseudofolder_path);
763 g_object_unref (pseudofolder);
767 e_folder_set_has_subfolders (parent, TRUE);
772 e_storage_get_has_subfolders (EStorage *storage,
775 EStoragePrivate *priv;
778 g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
779 g_return_val_if_fail (path != NULL, FALSE);
780 g_return_val_if_fail (g_path_is_absolute (path), FALSE);
782 priv = storage->priv;
784 folder = e_folder_tree_get_folder (priv->folder_tree, path);
786 return folder && e_folder_get_has_subfolders (folder);
790 e_storage_removed_folder (EStorage *storage,
793 EStoragePrivate *priv;
797 g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
798 g_return_val_if_fail (path != NULL, FALSE);
799 g_return_val_if_fail (g_path_is_absolute (path), FALSE);
801 priv = storage->priv;
803 folder = e_folder_tree_get_folder (priv->folder_tree, path);
807 p = strrchr (path, '/');
808 if (p != NULL && p != path) {
809 EFolder *parent_folder;
812 parent_path = g_strndup (path, p - path);
813 parent_folder = e_folder_tree_get_folder (priv->folder_tree, parent_path);
815 if (e_folder_get_highlighted (folder))
816 e_folder_set_child_highlight (parent_folder, FALSE);
818 g_free (parent_path);
821 g_signal_emit (storage, signals[REMOVED_FOLDER], 0, path);
823 e_folder_tree_remove (priv->folder_tree, path);
828 G_DEFINE_TYPE (EStorage, e_storage, G_TYPE_OBJECT)