1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
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
G_DEFINE_TYPE (EStorage, e_storage, G_TYPE_OBJECT)
41
struct EStoragePrivate {
42
/* The set of folders we have in this storage. */
43
EFolderTree *folder_tree;
45
/* Internal name of the storage */
56
static guint signals[LAST_SIGNAL] = { 0 };
58
/* Destroy notification function for the folders in the tree. */
61
folder_destroy_notify (EFolderTree *tree,
69
/* The root folder has no EFolder associated to it. */
73
e_folder = E_FOLDER (data);
74
g_object_unref (e_folder);
77
/* Signal callbacks for the EFolders. */
80
folder_changed_cb (EFolder *folder,
84
EStoragePrivate *priv;
85
const gchar *path, *p;
88
g_assert (E_IS_STORAGE (data));
90
storage = E_STORAGE (data);
93
path = e_folder_tree_get_path_for_data (priv->folder_tree, folder);
94
g_assert (path != NULL);
96
g_signal_emit (storage, signals[UPDATED_FOLDER], 0, path);
98
highlight = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (folder), "last_highlight"));
99
if (highlight != e_folder_get_highlighted (folder)) {
100
highlight = !highlight;
101
g_object_set_data (G_OBJECT (folder), "last_highlight", GINT_TO_POINTER (highlight));
102
p = strrchr (path, '/');
103
if (p && p != path) {
106
name = g_strndup (path, p - path);
107
folder = e_folder_tree_get_folder (priv->folder_tree, name);
110
e_folder_set_child_highlight (folder, highlight);
115
/* GObject methods. */
118
impl_finalize (GObject *object)
121
EStoragePrivate *priv;
123
storage = E_STORAGE (object);
124
priv = storage->priv;
126
if (priv->folder_tree != NULL)
127
e_folder_tree_destroy (priv->folder_tree);
133
(* G_OBJECT_CLASS (parent_class)->finalize) (object);
136
/* EStorage methods. */
139
impl_get_subfolder_paths (EStorage *storage,
142
EStoragePrivate *priv;
144
priv = storage->priv;
146
return e_folder_tree_get_subfolders (priv->folder_tree, path);
150
impl_get_folder (EStorage *storage,
153
EStoragePrivate *priv;
156
priv = storage->priv;
158
e_folder = (EFolder *) e_folder_tree_get_folder (priv->folder_tree, path);
164
impl_get_name (EStorage *storage)
166
return storage->priv->name;
170
impl_async_create_folder (EStorage *storage,
173
EStorageResultCallback callback,
176
(* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
180
impl_async_remove_folder (EStorage *storage,
182
EStorageResultCallback callback,
185
(* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
189
impl_async_xfer_folder (EStorage *storage,
190
const gchar *source_path,
191
const gchar *destination_path,
192
gboolean remove_source,
193
EStorageResultCallback callback,
196
(* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
200
impl_async_open_folder (EStorage *storage,
202
EStorageDiscoveryCallback callback,
205
(* callback) (storage, E_STORAGE_NOTIMPLEMENTED, NULL, data);
209
impl_will_accept_folder (EStorage *storage,
213
EStoragePrivate *priv = storage->priv;
214
const gchar *parent_path, *source_path;
217
/* By default, we only disallow dragging a folder into
218
* a subfolder of itself.
221
if (new_parent == source)
224
parent_path = e_folder_tree_get_path_for_data (priv->folder_tree,
226
source_path = e_folder_tree_get_path_for_data (priv->folder_tree,
228
if (!parent_path || !source_path)
231
source_len = strlen (source_path);
232
if (!strncmp (parent_path, source_path, source_len) &&
233
parent_path[source_len] == '/')
240
impl_async_discover_shared_folder (EStorage *storage,
242
const gchar *folder_name,
243
EStorageDiscoveryCallback callback,
246
(* callback) (storage, E_STORAGE_NOTIMPLEMENTED, NULL, data);
250
impl_async_remove_shared_folder (EStorage *storage,
252
EStorageResultCallback callback,
255
(* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
258
/* Initialization. */
261
e_storage_class_init (EStorageClass *class)
263
GObjectClass *object_class;
265
object_class = G_OBJECT_CLASS (class);
266
parent_class = g_type_class_ref (PARENT_TYPE);
268
object_class->finalize = impl_finalize;
270
class->get_subfolder_paths = impl_get_subfolder_paths;
271
class->get_folder = impl_get_folder;
272
class->get_name = impl_get_name;
273
class->async_create_folder = impl_async_create_folder;
274
class->async_remove_folder = impl_async_remove_folder;
275
class->async_xfer_folder = impl_async_xfer_folder;
276
class->async_open_folder = impl_async_open_folder;
277
class->will_accept_folder = impl_will_accept_folder;
279
class->async_discover_shared_folder = impl_async_discover_shared_folder;
280
class->async_remove_shared_folder = impl_async_remove_shared_folder;
281
signals[NEW_FOLDER] =
282
g_signal_new ("new_folder",
283
G_OBJECT_CLASS_TYPE (object_class),
285
G_STRUCT_OFFSET (EStorageClass, new_folder),
287
g_cclosure_marshal_VOID__STRING,
290
signals[UPDATED_FOLDER] =
291
g_signal_new ("updated_folder",
292
G_OBJECT_CLASS_TYPE (object_class),
294
G_STRUCT_OFFSET (EStorageClass, updated_folder),
296
g_cclosure_marshal_VOID__STRING,
299
signals[REMOVED_FOLDER] =
300
g_signal_new ("removed_folder",
301
G_OBJECT_CLASS_TYPE (object_class),
303
G_STRUCT_OFFSET (EStorageClass, removed_folder),
305
g_cclosure_marshal_VOID__STRING,
311
e_storage_init (EStorage *storage)
313
EStoragePrivate *priv;
315
priv = g_new0 (EStoragePrivate, 1);
317
priv->folder_tree = e_folder_tree_new (folder_destroy_notify, NULL);
319
storage->priv = priv;
325
e_storage_construct (EStorage *storage,
327
EFolder *root_folder)
329
EStoragePrivate *priv;
331
g_return_if_fail (E_IS_STORAGE (storage));
333
priv = storage->priv;
335
priv->name = g_strdup (name);
337
e_storage_new_folder (storage, "/", root_folder);
341
e_storage_new (const gchar *name,
342
EFolder *root_folder)
346
new = g_object_new (e_storage_get_type (), NULL);
348
e_storage_construct (new, name, root_folder);
354
e_storage_path_is_absolute (const gchar *path)
356
g_return_val_if_fail (path != NULL, FALSE);
362
e_storage_path_is_relative (const gchar *path)
364
g_return_val_if_fail (path != NULL, FALSE);
370
e_storage_get_subfolder_paths (EStorage *storage,
373
g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
374
g_return_val_if_fail (path != NULL, NULL);
375
g_return_val_if_fail (g_path_is_absolute (path), NULL);
377
return (* E_STORAGE_GET_CLASS (storage)->get_subfolder_paths) (storage, path);
381
e_storage_get_folder (EStorage *storage,
384
g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
385
g_return_val_if_fail (path != NULL, NULL);
386
g_return_val_if_fail (e_storage_path_is_absolute (path), NULL);
388
return (* E_STORAGE_GET_CLASS (storage)->get_folder) (storage, path);
392
e_storage_get_name (EStorage *storage)
394
g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
396
return (* E_STORAGE_GET_CLASS (storage)->get_name) (storage);
399
/* Folder operations. */
402
e_storage_async_create_folder (EStorage *storage,
405
EStorageResultCallback callback,
408
g_return_if_fail (E_IS_STORAGE (storage));
409
g_return_if_fail (path != NULL);
410
g_return_if_fail (g_path_is_absolute (path));
411
g_return_if_fail (type != NULL);
412
g_return_if_fail (callback != NULL);
414
(* E_STORAGE_GET_CLASS (storage)->async_create_folder) (storage, path, type, callback, data);
418
e_storage_async_remove_folder (EStorage *storage,
420
EStorageResultCallback callback,
423
g_return_if_fail (E_IS_STORAGE (storage));
424
g_return_if_fail (path != NULL);
425
g_return_if_fail (g_path_is_absolute (path));
426
g_return_if_fail (callback != NULL);
428
(* E_STORAGE_GET_CLASS (storage)->async_remove_folder) (storage, path, callback, data);
432
e_storage_async_xfer_folder (EStorage *storage,
433
const gchar *source_path,
434
const gchar *destination_path,
435
const gboolean remove_source,
436
EStorageResultCallback callback,
439
g_return_if_fail (E_IS_STORAGE (storage));
440
g_return_if_fail (source_path != NULL);
441
g_return_if_fail (g_path_is_absolute (source_path));
442
g_return_if_fail (destination_path != NULL);
443
g_return_if_fail (g_path_is_absolute (destination_path));
445
if (strcmp (source_path, destination_path) == 0) {
446
(* callback) (storage, E_STORAGE_OK, data);
451
gint destination_len;
454
source_len = strlen (source_path);
455
destination_len = strlen (destination_path);
457
if (source_len < destination_len
458
&& destination_path[source_len] == '/'
459
&& strncmp (destination_path, source_path, source_len) == 0) {
460
(* callback) (storage, E_STORAGE_CANTMOVETODESCENDANT, data);
465
(* E_STORAGE_GET_CLASS (storage)->async_xfer_folder) (storage, source_path, destination_path, remove_source, callback, data);
469
e_storage_async_open_folder (EStorage *storage,
471
EStorageDiscoveryCallback callback,
474
EStoragePrivate *priv;
477
g_return_if_fail (E_IS_STORAGE (storage));
478
g_return_if_fail (path != NULL);
479
g_return_if_fail (g_path_is_absolute (path));
481
priv = storage->priv;
483
folder = e_folder_tree_get_folder (priv->folder_tree, path);
484
if (folder == NULL) {
485
(* callback) (storage, E_STORAGE_NOTFOUND, path, data);
489
if (! e_folder_get_has_subfolders (folder)) {
490
(* callback) (storage, E_STORAGE_OK, path, data);
494
(* E_STORAGE_GET_CLASS (storage)->async_open_folder) (storage, path, callback, data);
498
e_storage_will_accept_folder (EStorage *storage,
499
EFolder *new_parent, EFolder *source)
501
g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
502
g_return_val_if_fail (E_IS_FOLDER (new_parent), FALSE);
503
g_return_val_if_fail (E_IS_FOLDER (source), FALSE);
505
return (* E_STORAGE_GET_CLASS (storage)->will_accept_folder) (storage, new_parent, source);
508
/* Shared folders. */
511
e_storage_async_discover_shared_folder (EStorage *storage,
513
const gchar *folder_name,
514
EStorageDiscoveryCallback callback,
517
g_return_if_fail (E_IS_STORAGE (storage));
518
g_return_if_fail (owner != NULL);
519
g_return_if_fail (folder_name != NULL);
521
(* E_STORAGE_GET_CLASS (storage)->async_discover_shared_folder) (storage, owner, folder_name, callback, data);
525
e_storage_cancel_discover_shared_folder (EStorage *storage,
527
const gchar *folder_name)
529
g_return_if_fail (E_IS_STORAGE (storage));
530
g_return_if_fail (owner != NULL);
531
g_return_if_fail (folder_name != NULL);
532
g_return_if_fail (E_STORAGE_GET_CLASS (storage)->cancel_discover_shared_folder != NULL);
534
(* E_STORAGE_GET_CLASS (storage)->cancel_discover_shared_folder) (storage, owner, folder_name);
538
e_storage_async_remove_shared_folder (EStorage *storage,
540
EStorageResultCallback callback,
543
g_return_if_fail (E_IS_STORAGE (storage));
544
g_return_if_fail (path != NULL);
545
g_return_if_fail (g_path_is_absolute (path));
547
(* E_STORAGE_GET_CLASS (storage)->async_remove_shared_folder) (storage, path, callback, data);
551
e_storage_result_to_string (EStorageResult result)
555
return _("No error");
556
case E_STORAGE_GENERICERROR:
557
return _("Generic error");
558
case E_STORAGE_EXISTS:
559
return _("A folder with the same name already exists");
560
case E_STORAGE_INVALIDTYPE:
561
return _("The specified folder type is not valid");
562
case E_STORAGE_IOERROR:
563
return _("I/O error");
564
case E_STORAGE_NOSPACE:
565
return _("Not enough space to create the folder");
566
case E_STORAGE_NOTEMPTY:
567
return _("The folder is not empty");
568
case E_STORAGE_NOTFOUND:
569
return _("The specified folder was not found");
570
case E_STORAGE_NOTIMPLEMENTED:
571
return _("Function not implemented in this storage");
572
case E_STORAGE_PERMISSIONDENIED:
573
return _("Permission denied");
574
case E_STORAGE_UNSUPPORTEDOPERATION:
575
return _("Operation not supported");
576
case E_STORAGE_UNSUPPORTEDTYPE:
577
return _("The specified type is not supported in this storage");
578
case E_STORAGE_CANTCHANGESTOCKFOLDER:
579
return _("The specified folder cannot be modified or removed");
580
case E_STORAGE_CANTMOVETODESCENDANT:
581
return _("Cannot make a folder a child of one of its descendants");
582
case E_STORAGE_INVALIDNAME:
583
return _("Cannot create a folder with that name");
584
case E_STORAGE_NOTONLINE:
585
return _("This operation cannot be performed in off-line mode");
587
return _("Unknown error");
591
/* Public utility functions. */
594
const gchar *physical_uri;
596
} GetPathForPhysicalUriForeachData;
599
get_path_for_physical_uri_foreach (EFolderTree *folder_tree,
604
GetPathForPhysicalUriForeachData *foreach_data;
605
const gchar *physical_uri;
608
foreach_data = (GetPathForPhysicalUriForeachData *) user_data;
609
if (foreach_data->retval != NULL)
612
e_folder = (EFolder *) path_data;
613
if (e_folder == NULL)
616
physical_uri = e_folder_get_physical_uri (e_folder);
617
if (physical_uri == NULL)
620
if (strcmp (foreach_data->physical_uri, physical_uri) == 0)
621
foreach_data->retval = g_strdup (path);
625
* e_storage_get_path_for_physical_uri:
626
* @storage: A storage
627
* @physical_uri: A physical URI
629
* Look for the folder having the specified @physical_uri.
631
* Return value: The path of the folder having the specified @physical_uri in
632
* @storage. If such a folder does not exist, just return NULL. The return
633
* value must be freed by the caller.
636
e_storage_get_path_for_physical_uri (EStorage *storage,
637
const gchar *physical_uri)
639
GetPathForPhysicalUriForeachData foreach_data;
640
EStoragePrivate *priv;
642
g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
643
g_return_val_if_fail (physical_uri != NULL, NULL);
645
priv = storage->priv;
647
foreach_data.physical_uri = physical_uri;
648
foreach_data.retval = NULL;
650
e_folder_tree_foreach (priv->folder_tree, get_path_for_physical_uri_foreach, &foreach_data);
652
return foreach_data.retval;
655
/* Protected functions. */
657
/* These functions are used by subclasses to add and remove folders from the
658
state stored in the storage object. */
661
remove_subfolders_except (EStorage *storage, const gchar *path, const gchar *except)
663
EStoragePrivate *priv;
664
GList *subfolders, *f;
665
const gchar *folder_path;
667
priv = storage->priv;
669
subfolders = e_folder_tree_get_subfolders (priv->folder_tree, path);
670
for (f = subfolders; f; f = f->next) {
671
folder_path = f->data;
672
if (!except || strcmp (folder_path, except) != 0)
673
e_storage_removed_folder (storage, folder_path);
675
for (f = subfolders; f != NULL; f = f->next)
678
g_list_free (subfolders);
682
e_storage_new_folder (EStorage *storage,
686
EStoragePrivate *priv;
687
gchar *parent_path, *p;
690
g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
691
g_return_val_if_fail (path != NULL, FALSE);
692
g_return_val_if_fail (g_path_is_absolute (path), FALSE);
693
g_return_val_if_fail (E_IS_FOLDER (e_folder), FALSE);
695
priv = storage->priv;
697
if (! e_folder_tree_add (priv->folder_tree, path, e_folder))
700
/* If this is the child of a folder that has a pseudo child,
701
* remove the pseudo child now.
703
p = strrchr (path, '/');
705
parent_path = g_strndup (path, p - path);
707
parent_path = g_strdup ("/");
708
parent = e_folder_tree_get_folder (priv->folder_tree, parent_path);
709
if (parent && e_folder_get_has_subfolders (parent)) {
710
remove_subfolders_except (storage, parent_path, path);
711
e_folder_set_has_subfolders (parent, FALSE);
713
g_free (parent_path);
715
g_signal_connect_object (e_folder, "changed", G_CALLBACK (folder_changed_cb), storage, 0);
717
g_signal_emit (storage, signals[NEW_FOLDER], 0, path);
719
folder_changed_cb (e_folder, storage);
724
/* This really should be called e_storage_set_has_subfolders, but then
725
* it would look like it was an EStorageSet function. (Fact o' the
726
* day: The word "set" has more distinct meanings than any other word
727
* in the English language.) Anyway, we now return you to your
728
* regularly scheduled source code, already in progress.
731
e_storage_declare_has_subfolders (EStorage *storage,
733
const gchar *message)
735
EStoragePrivate *priv;
736
EFolder *parent, *pseudofolder;
737
gchar *pseudofolder_path;
740
g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
741
g_return_val_if_fail (path != NULL, FALSE);
742
g_return_val_if_fail (g_path_is_absolute (path), FALSE);
743
g_return_val_if_fail (message != NULL, FALSE);
745
priv = storage->priv;
747
parent = e_folder_tree_get_folder (priv->folder_tree, path);
750
if (e_folder_get_has_subfolders (parent))
753
remove_subfolders_except (storage, path, NULL);
755
pseudofolder = e_folder_new (message, "working", "");
756
if (strcmp (path, "/") == 0)
757
pseudofolder_path = g_strdup_printf ("/%s", message);
759
pseudofolder_path = g_strdup_printf ("%s/%s", path, message);
760
e_folder_set_physical_uri (pseudofolder, pseudofolder_path);
762
ok = e_storage_new_folder (storage, pseudofolder_path, pseudofolder);
763
g_free (pseudofolder_path);
765
g_object_unref (pseudofolder);
769
e_folder_set_has_subfolders (parent, TRUE);
774
e_storage_get_has_subfolders (EStorage *storage,
777
EStoragePrivate *priv;
780
g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
781
g_return_val_if_fail (path != NULL, FALSE);
782
g_return_val_if_fail (g_path_is_absolute (path), FALSE);
784
priv = storage->priv;
786
folder = e_folder_tree_get_folder (priv->folder_tree, path);
788
return folder && e_folder_get_has_subfolders (folder);
792
e_storage_removed_folder (EStorage *storage,
795
EStoragePrivate *priv;
799
g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
800
g_return_val_if_fail (path != NULL, FALSE);
801
g_return_val_if_fail (g_path_is_absolute (path), FALSE);
803
priv = storage->priv;
805
folder = e_folder_tree_get_folder (priv->folder_tree, path);
809
p = strrchr (path, '/');
810
if (p != NULL && p != path) {
811
EFolder *parent_folder;
814
parent_path = g_strndup (path, p - path);
815
parent_folder = e_folder_tree_get_folder (priv->folder_tree, parent_path);
817
if (e_folder_get_highlighted (folder))
818
e_folder_set_child_highlight (parent_folder, FALSE);
820
g_free (parent_path);
823
g_signal_emit (storage, signals[REMOVED_FOLDER], 0, path);
825
e_folder_tree_remove (priv->folder_tree, path);