/* sunone-folder-tree.c * * Copyright (C) 2002-2004 Sun Microsystems, Inc * * AUTHORS * Harry Lu * Alfred Peng * Jedy Wang * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "sunone-config-listener.h" #include "sunone-permissions-dialog.h" #include "sunone-subscription-dialog.h" #include "sunone-invitation-list.h" #include "sunone-offline-listener.h" #include "sunone-folder-tree.h" #define d(x) static GObjectClass *parent_class = NULL; static GHashTable *full_hash = NULL; struct _SunOneFolderTreePrivate { GtkTreeView *treeview; GtkTreeStore *treeview_model; GtkWidget *scrolled; GtkNotebook *body; GList *invitation_models; GHashTable *uri_to_page; }; static GType col_types[] = { G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN }; static gboolean sft_tree_button_press (GtkTreeView *treeview, GdkEventButton *event, SunOneFolderTree *tree); static gboolean sft_tree_popup_menu (GtkWidget *widget, SunOneFolderTree *tree); G_DEFINE_TYPE (SunOneFolderTree, sunone_folder_tree, G_TYPE_OBJECT) static void destroy_page (gpointer key, gpointer value, gpointer user_data) { g_free (key); g_object_unref (GTK_WIDGET (value)); } static void dispose (GObject *object) { SunOneFolderTree *tree = SUNONE_FOLDER_TREE (object); SunOneFolderTreePrivate *priv = tree->priv; GList *l; if (priv) { for (l = priv->invitation_models; l != NULL; l = l->next) g_object_unref (G_OBJECT (l->data)); g_list_free (priv->invitation_models); priv->invitation_models = NULL; if (priv->uri_to_page) { g_hash_table_foreach (priv->uri_to_page, destroy_page, NULL); g_hash_table_destroy (priv->uri_to_page); priv->uri_to_page = NULL; } g_free (priv); tree->priv = NULL; } G_OBJECT_CLASS (parent_class)->dispose (object); } static void finalize (GObject *object) { G_OBJECT_CLASS (parent_class)->finalize (object); } static void sunone_folder_tree_class_init (SunOneFolderTreeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); object_class->dispose = dispose; object_class->finalize = finalize; } static void sunone_folder_tree_init (SunOneFolderTree *backend) { SunOneFolderTreePrivate *priv; backend->priv = g_new0 (SunOneFolderTreePrivate, 1); priv = backend->priv; priv->invitation_models = NULL; priv->uri_to_page = g_hash_table_new (g_str_hash, g_str_equal); } static GtkWidget * sunone_folder_tree_view_new (SunOneFolderTree *tree, const char *physical_uri) { SunOneInvitationListModel *model; GtkWidget *widget = NULL; model = sunone_folder_tree_get_invitation_list_model (tree, physical_uri); g_return_val_if_fail (model, NULL); widget = sunone_invitation_list_new (model); gtk_widget_show (widget); g_object_ref (widget); return widget; } static GtkWidget * sunone_folder_tree_no_view () { static GtkWidget *label = NULL; if (!label) { label = gtk_label_new (_("Cannot display this folder in this view.\n Please switch to Calendars/Tasks view to see the calendars/tasks.\n\n This view is used to create or delete calendar/tasks folders,\n manage folder permissions and subscriptions, show your invitations.")); gtk_label_set_justify ((GtkLabel *)label, GTK_JUSTIFY_CENTER); } g_object_ref (label); return label; } static gboolean update_invitation_model (gpointer data) { sunone_invitation_list_model_poll_cb (data); return FALSE; } static char * sunone_folder_tree_get_selected_uri (SunOneFolderTree *tree) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; char *physical_uri = NULL; g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL); selection = gtk_tree_view_get_selection (tree->priv->treeview); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, COL_STRING_URI, &physical_uri, -1); return physical_uri; } static void account_selection_changed (GtkTreeSelection *selection, gpointer user_data) { SunOneFolderTree *view = user_data; SunOneFolderTreePrivate *priv = view->priv; GtkWidget *page; char *physical_uri, *type; int page_num; physical_uri = sunone_folder_tree_get_selected_uri (view); if (!physical_uri) { return; } type = sunone_util_get_parameter_from_uri (physical_uri, "type"); page = g_hash_table_lookup (priv->uri_to_page, physical_uri); if (page) { /* we need to do a refresh on invitations view */ page_num = gtk_notebook_page_num (priv->body, page); if (!strcmp (type, "invitations")) { SunOneInvitationListModel *model = sunone_folder_tree_get_invitation_list_model (view, physical_uri); if (model) g_idle_add (update_invitation_model, model); } } else { if (!strcmp (type, "invitations")) page = sunone_folder_tree_view_new (view, physical_uri); else { page = sunone_folder_tree_no_view (); page_num = gtk_notebook_page_num (priv->body, page); if (page_num != -1) gtk_notebook_remove_page (priv->body, page_num); } gtk_widget_show (page); gtk_notebook_append_page (priv->body, page, NULL); page_num = gtk_notebook_page_num (priv->body, page); g_hash_table_insert (priv->uri_to_page, g_strdup (physical_uri), page); } g_free (type); g_free (physical_uri); gtk_notebook_set_current_page (priv->body, page_num); } static void full_hash_insert (SunOneFolderTree *tree, GtkTreeIter *iter, const char *full_name) { SunOneFolderTreePrivate *priv = tree->priv; GtkTreeRowReference *uri_row; GtkTreePath *path; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); if (!full_hash) full_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)gtk_tree_row_reference_free); path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->treeview_model), iter); uri_row = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->treeview_model), path); gtk_tree_path_free (path); g_hash_table_insert (full_hash, g_strdup (full_name), uri_row); } static void add_folder_tree_item (SunOneFolderTree *tree, SunOneAccount *account, GtkTreeIter *iter, const char *calid, const char *display_name, gboolean personal) { SunOneFolderTreePrivate *priv = tree->priv; GtkTreeIter iter1, iter2; SunOneInvitationListModel *model; char *full_name; char *physical_uri; static GStaticMutex mutex = G_STATIC_MUTEX_INIT; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); g_return_if_fail (account != NULL); /* create the invitation list model now */ physical_uri = sunone_account_get_physical_uri (account, calid, "invitations"); g_static_mutex_lock (&mutex); model = SUNONE_INVITATION_LIST_MODEL (sunone_invitation_list_model_new (physical_uri)); g_static_mutex_unlock (&mutex); g_free (physical_uri); g_return_if_fail (model != NULL); priv->invitation_models = g_list_prepend (priv->invitation_models, model); full_name = g_strdup_printf ("/%s/%s/", sunone_account_get_name (account), calid); physical_uri = sunone_account_get_physical_uri (account, calid, "folder"); gtk_tree_store_append (priv->treeview_model, &iter1, iter); gtk_tree_store_set (priv->treeview_model, &iter1, COL_STRING_DISPLAY_NAME, display_name, COL_POINTER_ACCOUNT, account, COL_STRING_FOLDER_TYPE, "folder", COL_STRING_URI, physical_uri, COL_STRING_FULLNAME, full_name, COL_BOOL_IS_STORE, TRUE, COL_BOOL_IS_ACCOUNT, FALSE, COL_BOOL_IS_PERSONAL, personal, -1); g_free (physical_uri); full_hash_insert (tree, &iter1, full_name); g_free (full_name); full_name = g_strdup_printf ("/%s/%s/invitations", sunone_account_get_name (account), calid); physical_uri = sunone_account_get_physical_uri (account, calid, "invitations"); gtk_tree_store_append (priv->treeview_model, &iter2, &iter1); gtk_tree_store_set (priv->treeview_model, &iter2, COL_STRING_DISPLAY_NAME, "Invitations", COL_POINTER_ACCOUNT, account, COL_STRING_FOLDER_TYPE, "invitations", COL_STRING_URI, physical_uri, COL_STRING_FULLNAME, full_name, COL_BOOL_IS_STORE, FALSE, COL_BOOL_IS_ACCOUNT, FALSE, COL_BOOL_IS_PERSONAL, FALSE, -1); g_free (physical_uri); full_hash_insert (tree, &iter2, full_name); g_free (full_name); } static void add_folder_tree (SunOneFolderTree *tree, SunOneAccount *account, GtkTreeIter *iter, SunOneConnectionPreferences *prefs) { GList *l; SunOneCalendarSubscription *sub; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); g_return_if_fail (account != NULL); for (l = prefs->subscriptions; l != NULL; l = l->next) { sub = (SunOneCalendarSubscription *) l->data; if (!sub) continue; if (!strcasecmp (sub->calid, prefs->default_calendar)) { add_folder_tree_item (tree, account, iter, sub->calid, sub->display_name && *(sub->display_name) ? sub->display_name : sub->calid, TRUE); } else { add_folder_tree_item (tree, account, iter, sub->calid, sub->display_name && *(sub->display_name) ? sub->display_name : sub->calid, FALSE); } } } static void sunone_folder_tree_connect (SunOneFolderTree *tree, SunOneAccount *account, GtkTreeIter *iter) { SunOneConnectionPreferences *prefs; SunOneConnection *cnc; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); sunone_account_set_connecting (account, TRUE); cnc = sunone_account_get_connection (account); if (!cnc) { sunone_account_set_connecting (account, FALSE); return; } prefs = sunone_connection_get_preferences (cnc); if (!prefs) { sunone_account_set_connecting (account, FALSE); return; } /* add tree items to sunone folder tree */ add_folder_tree (tree, account, iter, prefs); /* add sources to Calendar and Task */ add_sources (account, prefs); sunone_connection_free_preferences (prefs); sunone_account_set_connecting (account, FALSE); } static void remove_calendar (SunOneFolderTree *tree, const char *full_name) { SunOneFolderTreePrivate *priv = tree->priv; GtkTreeStore *model; GtkTreeRowReference *uri_row; GtkTreePath *path; GtkTreeIter iter; GtkTreeIter child; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); if (!full_hash) return; uri_row = g_hash_table_lookup (full_hash, full_name); if (!uri_row) return; model = priv->treeview_model; path = gtk_tree_row_reference_get_path (uri_row); gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); /*remove all children*/ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL (model), &child, &iter, 0); while (gtk_tree_store_iter_is_valid (model, &child)) { char *child_full_name; gtk_tree_model_get (GTK_TREE_MODEL (model), &child, COL_STRING_FULLNAME, &child_full_name, -1); gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &child); remove_calendar (tree, child_full_name); g_hash_table_remove (full_hash, child_full_name); g_free (child_full_name); } gtk_tree_store_remove (model, &iter); gtk_tree_path_free (path); g_hash_table_remove (full_hash, full_name); } typedef struct { SunOneFolderTree *tree; SunOneAccount *account; GtkTreeIter iter; } TreeCreateData; static void create_folder_tree_with_thread (TreeCreateData *data) { SunOneFolderTreePrivate *priv = data->tree->priv; SunOneAccount *account = data->account; GtkTreeIter iter = data->iter; GtkTreeIter sub_iter; GtkTreePath *path; gtk_tree_store_append (priv->treeview_model, &sub_iter, &iter); gtk_tree_store_set (priv->treeview_model, &sub_iter, COL_STRING_DISPLAY_NAME, _("Connecting..."), COL_POINTER_ACCOUNT, NULL, COL_STRING_FOLDER_TYPE, "", COL_STRING_URI, NULL, COL_STRING_FULLNAME, NULL, COL_BOOL_IS_STORE, TRUE, COL_BOOL_IS_ACCOUNT, FALSE, COL_BOOL_IS_PERSONAL, FALSE, -1); path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->treeview_model), &iter); gtk_tree_view_expand_row (priv->treeview, path, FALSE); gtk_tree_path_free (path); sunone_folder_tree_connect (data->tree, account, &iter); gtk_tree_store_remove (priv->treeview_model, &sub_iter); g_object_unref (data->account); g_free (data); sunone_folder_tree_sync (); } void sunone_folder_tree_created (SunOneFolderTree *tree, SunOneAccount *account) { SunOneFolderTreePrivate *priv; TreeCreateData *data; GtkTreeIter iter; GThread *thread = NULL; GError *error = NULL; char *physical_uri; char *full_name; int mode; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); g_return_if_fail (account != NULL); priv = tree->priv; /*remove old tree, if exists*/ full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account)); remove_calendar (tree, full_name); physical_uri = sunone_account_get_physical_uri (account, sunone_account_get_user (account), "folder"); gtk_tree_store_append (priv->treeview_model, &iter, NULL); gtk_tree_store_set (priv->treeview_model, &iter, COL_STRING_DISPLAY_NAME, sunone_account_get_name (account), COL_POINTER_ACCOUNT, account, COL_STRING_FOLDER_TYPE, "folder", COL_STRING_URI, physical_uri, COL_STRING_FULLNAME, full_name, COL_BOOL_IS_STORE, TRUE, COL_BOOL_IS_ACCOUNT, TRUE, COL_BOOL_IS_PERSONAL, FALSE, -1); g_free (physical_uri); full_hash_insert (tree, &iter, full_name); g_free (full_name); /*only connect if we are online */ sunone_account_is_offline (account, &mode); if (mode == ONLINE_MODE) { data = g_malloc (sizeof (TreeCreateData)); data->tree = tree; data->account = g_object_ref (account); data->iter = iter; thread = g_thread_create ((GThreadFunc)create_folder_tree_with_thread, data, FALSE, &error); if (!thread) { g_warning (G_STRLOC ": %s", error->message); g_error_free (error); g_object_unref (data->account); g_free (data); } } } void sunone_folder_tree_removed (SunOneFolderTree *tree, SunOneAccount *account) { SunOneFolderTreePrivate *priv = tree->priv; char *full_name; GtkWidget *page; int page_num; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account)); remove_calendar (tree, full_name); g_free (full_name); remove_source (account, sunone_account_get_user (account), TRUE); page = sunone_folder_tree_no_view (); page_num = gtk_notebook_page_num (priv->body, page); if (page_num != -1) gtk_notebook_remove_page (priv->body, page_num); gtk_widget_show (page); gtk_notebook_append_page (priv->body, page, NULL); page_num = gtk_notebook_page_num (priv->body, page); gtk_notebook_set_current_page (priv->body, page_num); } void sunone_folder_tree_remove_calendar (SunOneFolderTree *tree, SunOneAccount *account, const char *calid) { char *full_name; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); g_return_if_fail (account != NULL); full_name = g_strdup_printf ("/%s/%s/", sunone_account_get_name (account), calid); remove_calendar (tree, full_name); g_free (full_name); remove_source (account, calid, FALSE); } void sunone_folder_tree_add_calendar (SunOneFolderTree *tree, SunOneAccount *account, const char *calid, const char *display_name) { SunOneFolderTreePrivate *priv = tree->priv; GtkTreeRowReference *uri_row; GtkTreePath *path; GtkTreeIter iter; char *full_name; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account)); uri_row = g_hash_table_lookup (full_hash, full_name); g_free (full_name); path = gtk_tree_row_reference_get_path (uri_row); gtk_tree_model_get_iter ((GtkTreeModel *)priv->treeview_model, &iter, path); gtk_tree_path_free (path); add_folder_tree_item (tree, account, &iter, calid, display_name, FALSE); add_source (account, calid, display_name, FALSE); } void sunone_folder_tree_rename_calendar (SunOneFolderTree *tree, SunOneAccount *account, const char *calid, const char *display_name) { SunOneFolderTreePrivate *priv = tree->priv; GtkTreeRowReference *uri_row; GtkTreePath *path; GtkTreeIter iter; char *full_name; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); g_return_if_fail (account != NULL); /* remove old folder */ full_name = g_strdup_printf ("/%s/%s/", sunone_account_get_name (account), calid); remove_calendar (tree, full_name); g_free (full_name); /* add new folder */ full_name = g_strdup_printf ("/%s/", sunone_account_get_name (account)); uri_row = g_hash_table_lookup (full_hash, full_name); g_free (full_name); path = gtk_tree_row_reference_get_path (uri_row); gtk_tree_model_get_iter ((GtkTreeModel *)priv->treeview_model, &iter, path); add_folder_tree_item (tree, account, &iter, calid, display_name, FALSE); /* rename source */ rename_source (account, calid, display_name, FALSE); } enum { FOLDER_ICON_NORMAL, FOLDER_ICON_INVITATION, FOLDER_ICON_LAST }; static GdkPixbuf *folder_icons[FOLDER_ICON_LAST]; static void render_pixbuf (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { static gboolean initialised = FALSE; GdkPixbuf *pixbuf = NULL; gboolean is_store; char *folder_type; if (!initialised) { folder_icons[FOLDER_ICON_NORMAL] = e_icon_factory_get_icon ("stock_normal", E_ICON_SIZE_MENU); folder_icons[FOLDER_ICON_INVITATION] = e_icon_factory_get_icon ("stock_new-meeting", E_ICON_SIZE_MENU); initialised = TRUE; } gtk_tree_model_get (model, iter, COL_STRING_FOLDER_TYPE, &folder_type, COL_BOOL_IS_STORE, &is_store, -1); if (!is_store && folder_type != NULL) { if (!strcmp (folder_type, "invitations")) pixbuf = folder_icons[FOLDER_ICON_INVITATION]; else pixbuf = folder_icons[FOLDER_ICON_NORMAL]; } g_object_set (renderer, "pixbuf", pixbuf, "visible", !is_store, NULL); g_free (folder_type); } static void render_display_name (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data) { gboolean is_account, is_store, bold = FALSE; char *name, *physical_uri, *folder_type, *display; SunOneFolderTree *tree = user_data; SunOneAccount *account; gtk_tree_model_get (model, iter, COL_STRING_DISPLAY_NAME, &name, COL_BOOL_IS_STORE, &is_store, COL_BOOL_IS_ACCOUNT, &is_account, COL_STRING_URI, &physical_uri, COL_STRING_FOLDER_TYPE, &folder_type, COL_POINTER_ACCOUNT, &account, -1); display = name; if (folder_type && !strcmp ("invitations", folder_type)) { /* check unread count for invitation list */ SunOneInvitationListModel *model; model = sunone_folder_tree_get_invitation_list_model (tree, physical_uri); if (model) { gint count = gtk_tree_model_iter_n_children ((GtkTreeModel *)model, NULL); if (count > 0) { display = g_strdup_printf ("%s (%u)", name, count); g_free (name); bold = TRUE; } } } if (!bold) bold = is_store; if (is_account) { int state; sunone_account_is_offline (account, &state); if (state == OFFLINE_MODE) { display = g_strdup_printf ("%s (%s)", name, _("Offline")); g_free (name); } } g_object_set (renderer, "text", display, "weight", bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL, NULL); g_free (display); g_free (folder_type); g_free (physical_uri); } SunOneFolderTree * sunone_folder_tree_new_with_model (GtkTreeModel *model) { SunOneFolderTree *tree; SunOneFolderTreePrivate *priv; GtkTreeViewColumn *column; GtkTreeSelection *selection; GtkWidget *widget, *scrolled; GtkCellRenderer *renderer; tree = g_object_new (SUNONE_TYPE_FOLDER_TREE, NULL); priv = tree->priv; widget = gtk_notebook_new (); gtk_widget_show (widget); priv->body = GTK_NOTEBOOK (widget); gtk_notebook_set_show_tabs (priv->body, FALSE); gtk_notebook_set_show_border (priv->body, FALSE); gtk_notebook_append_page (priv->body, gtk_drawing_area_new (), NULL); priv->treeview_model = GTK_TREE_STORE (model); widget = gtk_tree_view_new_with_model (model); GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS); column = gtk_tree_view_column_new (); gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column); renderer = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_set_cell_data_func (column, renderer, render_pixbuf, NULL, NULL); renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_set_cell_data_func (column, renderer, render_display_name, tree, NULL); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE); gtk_tree_view_set_search_column (GTK_TREE_VIEW (widget), COL_STRING_DISPLAY_NAME); gtk_widget_show (widget); priv->treeview = GTK_TREE_VIEW (widget); g_signal_connect (priv->treeview, "button-press-event", G_CALLBACK (sft_tree_button_press), tree); g_signal_connect (priv->treeview, "popup_menu", G_CALLBACK (sft_tree_popup_menu), tree); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN); gtk_container_add (GTK_CONTAINER (scrolled), widget); gtk_widget_show (scrolled); priv->scrolled = scrolled; selection = gtk_tree_view_get_selection (priv->treeview); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); g_signal_connect (selection, "changed", G_CALLBACK (account_selection_changed), tree); return tree; } static void error_handler (guint errcode) { switch (errcode) { case SUNONE_ERROR_ILLEGAL_CALID_NAME: e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Cannot create a folder with that name")); break; case SUNONE_ERROR_CANNOT_MODIFY_LINKED_OBJECTS: e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Operation not supported")); break; case SUNONE_ERROR_ACCESS_DENIED: case SUNONE_ERROR_MUSTBEOWNER_OPERATION: case SUNONE_ERROR_NOT_ALLOWED_TO_REQUEST_PUBLISH: e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Permission denied")); break; default: e_notice(NULL, GTK_MESSAGE_ERROR, _("Cannot create the specified folder: Generic error")); break; } } SunOneFolderTree * sunone_folder_tree_new (void) { SunOneFolderTree *tree; GtkTreeModel *model; model = GTK_TREE_MODEL (gtk_tree_store_newv (NUM_COLUMNS, col_types)); tree = sunone_folder_tree_new_with_model (model); return tree; } static void name_entry_changed (GtkEntry *entry, GtkWidget *dialog) { const char *text = NULL; gboolean active; if (entry->text_length > 0) text = gtk_entry_get_text (entry); active = text && !strchr (text, '/'); gtk_dialog_set_response_sensitive ((GtkDialog *) dialog, GTK_RESPONSE_OK, active); } static gboolean check_offline (SunOneAccount *account, GtkWidget *parent) { int state; sunone_account_is_offline (account, &state); if (state == OFFLINE_MODE) { e_notice (parent, GTK_MESSAGE_ERROR, _("This operation cannot be performed in offline mode")); return FALSE; } return TRUE; } static void sft_popup_new_folder_response (GtkWidget *dialog, int response, SunOneFolderTree *tree) { GtkEntry *entry; SunOneAccount *account; guint errcode; const char *name; char *real_calid = NULL; char *p; SunOneConnection *cnc; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (dialog); return; } account = sunone_folder_tree_get_account (tree); if (!check_offline (account, dialog)) return; cnc = sunone_account_get_connection (account); if (!cnc) { gtk_widget_destroy (dialog); return; } entry = g_object_get_data (G_OBJECT (dialog), "entry"); name = gtk_entry_get_text (entry); for (p = (char *)name; *p; p++) { /* don't allow illegal charactors in the calendar name */ if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '.' || *p == '_' || *p == '-' || *p == '&' || *p == '!' || *p == '@')) *p = '_'; } errcode = sunone_connection_createcalendar (cnc, name, &real_calid); if (SUNONE_ERROR_IS_SUCCESSFUL (errcode)) { sunone_folder_tree_add_calendar (tree, account, real_calid, name); sunone_folder_tree_sync (); } else { error_handler (errcode); } g_free (real_calid); gtk_widget_destroy (dialog); } static void sft_popup_new_calendar (EPopup *ep, EPopupItem *item, void *data) { SunOneFolderTree *tree = data; GtkWidget *dialog; GtkWidget *prompt_label; GtkWidget *vbox; GtkWidget *entry; const char *title = _("Create calendar"); g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); if (!sunone_folder_tree_get_selected (tree)) return; dialog = gtk_dialog_new_with_buttons (title, NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_window_set_default_size (GTK_WINDOW (dialog), 275, -1); gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); vbox = GTK_DIALOG (dialog)->vbox; prompt_label = gtk_label_new (_("Please input the name of the new calendar:")); gtk_box_pack_start (GTK_BOX (vbox), prompt_label, TRUE, TRUE, 6); gtk_box_set_spacing (GTK_BOX (vbox), 6); entry = gtk_entry_new (); gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); g_signal_connect (GTK_ENTRY (entry), "changed", G_CALLBACK (name_entry_changed), dialog); gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 3); g_object_set_data (G_OBJECT (dialog), "entry", entry); g_signal_connect (dialog, "response", G_CALLBACK (sft_popup_new_folder_response), tree); gtk_widget_grab_focus (entry); gtk_widget_show (prompt_label); gtk_widget_show (entry); gtk_widget_show (dialog); } static void sft_popup_rename_folder_response (GtkWidget *dialog, int response, SunOneFolderTree *tree) { SunOneAccount *account; SunOneCalendarProperties *calprops; SunOneConnectionPreferences *prefs; SunOneConnection *cnc; SunOneCalendarSubscription *sub; GtkWidget *entry; GList *l; guint errcode = SUNONE_ERROR_CANNOT_MODIFY_LINKED_OBJECTS; char *physical_uri, *calid, *type, *p; const char *display_name = NULL; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (dialog); return; } account = sunone_folder_tree_get_account (tree); if (!check_offline (account, dialog)) return; cnc = sunone_account_get_connection (account); if (!cnc) { gtk_widget_destroy (dialog); return; } physical_uri = sunone_folder_tree_get_selected_uri (tree); calid = sunone_util_get_calid_from_uri (physical_uri); type = sunone_util_get_parameter_from_uri (physical_uri, "type"); if (strcmp (type, "folder") || !strcmp (calid, sunone_account_get_user (account))) goto out; entry = g_object_get_data (G_OBJECT (dialog), "entry"); display_name = gtk_entry_get_text (GTK_ENTRY (entry)); for (p = (char *)display_name; *p; p++) { /* don't allow illegal charactors in the calendar name */ if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '.' || *p == '_' || *p == '-' || *p == '&' || *p == '!' || *p == '@')) *p = '_'; } calprops = sunone_connection_get_calprops (cnc, calid, FALSE); if (calprops) { if (calprops->display_name) g_free (calprops->display_name); calprops->display_name = g_strdup (display_name); errcode = sunone_connection_set_calprops (cnc, calid, calprops); if (errcode == SUNONE_ERROR_COMMAND_SUCCESSFUL) { /* now, update the subscription list */ prefs = sunone_connection_get_preferences (cnc); if (prefs) { for (l = prefs->subscriptions; l != NULL; l = l->next) { sub = l->data; if (!strcmp (calid, sub->calid)) { if (sub->display_name) g_free (sub->display_name); sub->display_name = g_strdup (display_name); break; } } if (!SUNONE_ERROR_IS_SUCCESSFUL ( sunone_connection_set_preferences (cnc, prefs))) errcode = SUNONE_ERROR_COMMAND_FAILED; sunone_connection_free_preferences (prefs); } else errcode = SUNONE_ERROR_COMMAND_FAILED; } sunone_connection_free_calprops (calprops); } else errcode = SUNONE_ERROR_COMMAND_FAILED; out: if (SUNONE_ERROR_IS_SUCCESSFUL (errcode)) { sunone_folder_tree_rename_calendar (tree, account, calid, display_name); sunone_folder_tree_sync (); } else error_handler (errcode); g_free (physical_uri); g_free (type); g_free (calid); gtk_widget_destroy (dialog); } static void sft_popup_rename_folder (EPopup *ep, EPopupItem *item, void *data) { SunOneFolderTree *tree = data; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *dialog; GtkWidget *prompt_label; GtkWidget *vbox; GtkWidget *entry; const char *title = _("Rename folder"); char *display_name, *prompt; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); selection = gtk_tree_view_get_selection (tree->priv->treeview); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, COL_STRING_DISPLAY_NAME, &display_name, -1); prompt = g_strdup_printf (_("Rename the \"%s\" folder to:"), display_name); dialog = gtk_dialog_new_with_buttons (title, NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_window_set_default_size (GTK_WINDOW (dialog), 275, -1); gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); vbox = GTK_DIALOG (dialog)->vbox; prompt_label = gtk_label_new (prompt); gtk_box_pack_start (GTK_BOX (vbox), prompt_label, TRUE, TRUE, 6); gtk_box_set_spacing (GTK_BOX (vbox), 6); entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry), display_name); gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); g_signal_connect (GTK_ENTRY (entry), "changed", G_CALLBACK (name_entry_changed), dialog); gtk_box_pack_start (GTK_BOX (vbox), entry, TRUE, TRUE, 3); g_object_set_data (G_OBJECT (dialog), "entry", entry); g_signal_connect (dialog, "response", G_CALLBACK (sft_popup_rename_folder_response), tree); gtk_widget_grab_focus (entry); g_free (display_name); g_free (prompt); gtk_widget_show (prompt_label); gtk_widget_show (entry); gtk_widget_show (dialog); } static void sft_popup_delete_folder_response (GtkWidget *dialog, int response, SunOneFolderTree *tree) { SunOneAccount *account; SunOneConnection *cnc; guint errcode; char *physical_uri, *calid; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); if (response != GTK_RESPONSE_OK) { gtk_widget_destroy (dialog); return; } account = sunone_folder_tree_get_account (tree); if (!check_offline (account, dialog)) return; cnc = sunone_account_get_connection (account); if (!cnc) { gtk_widget_destroy (dialog); return; } physical_uri = sunone_folder_tree_get_selected_uri (tree); calid = sunone_util_get_calid_from_uri (physical_uri); errcode = sunone_connection_deletecalendar (cnc, calid); if (SUNONE_ERROR_IS_SUCCESSFUL (errcode)) { sunone_folder_tree_remove_calendar (tree, account, calid); sunone_folder_tree_sync (); } else { error_handler (errcode); } g_free (physical_uri); g_free (calid); gtk_widget_destroy (dialog); } static void sft_popup_delete_folder (EPopup *ep, EPopupItem *item, void *data) { SunOneFolderTree *tree = data; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *dialog; char *display_name; char *title; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); selection = gtk_tree_view_get_selection (tree->priv->treeview); if (gtk_tree_selection_get_selected (selection, &model, &iter)) gtk_tree_model_get (model, &iter, COL_STRING_DISPLAY_NAME, &display_name, -1); dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, (_("Really delete folder \"%s\"?")), display_name); gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_DELETE, GTK_RESPONSE_OK); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); title = g_strdup_printf (("Delete \"%s\""), display_name); gtk_window_set_title (GTK_WINDOW (dialog), title); g_free (title); g_free (display_name); g_signal_connect (dialog, "response", G_CALLBACK (sft_popup_delete_folder_response), tree); gtk_widget_show (dialog); } static void sft_popup_permission (EPopup *ep, EPopupItem *item, void *data) { SunOneFolderTree *tree = data; SunOneFolderTreePrivate *priv = tree->priv; SunOneAccount *account; SunOneConnection *cnc; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *dialog = NULL; char *type, *physical_uri; char *calid = NULL; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); selection = gtk_tree_view_get_selection (priv->treeview); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, COL_POINTER_ACCOUNT, &account, COL_STRING_FOLDER_TYPE, &type, COL_STRING_URI, &physical_uri, -1); g_return_if_fail (physical_uri != NULL); g_return_if_fail (account != NULL); if (!check_offline (account, NULL)) return; if (!strcmp (type, "invitations")) { e_notice(NULL, GTK_MESSAGE_ERROR, ("There are no permissions to set for this folder")); } else { calid = sunone_util_get_calid_from_uri (physical_uri); cnc = sunone_account_get_connection (account); if (!cnc) return; if (calid) { dialog = sunone_permissions_dialog_new (cnc, calid); g_free (calid); } } g_free (physical_uri); g_free (type); if (dialog) gtk_widget_show (dialog); } static void sft_popup_subscription (EPopup *ep, EPopupItem *item, void *data) { SunOneFolderTree *tree = data; SunOneAccount *account; SunOneConnection *cnc; GtkWidget *dialog = NULL; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); account = sunone_folder_tree_get_account (tree); if (!account) return; if (!check_offline (account, NULL)) return; cnc = sunone_account_get_connection (account); if (!cnc) return; dialog = sunone_subscription_dialog_new (tree, cnc); if (dialog) gtk_widget_show (dialog); } static void sft_popup_login (EPopup *ep, EPopupItem *item, void *data) { SunOneFolderTree *tree = data; SunOneAccount *account; g_return_if_fail (SUNONE_IS_FOLDER_TREE (tree)); account = sunone_folder_tree_get_account (tree); if (!account) return; /* recreate the folder list */ sunone_account_set_dont_ask_password (account, FALSE); sunone_account_forget_password (account); sunone_folder_tree_created (tree, account); } static EPopupItem sft_popup_menu[] = { { E_POPUP_ITEM, "10.ej.00", N_("_New Calendar..."), sft_popup_new_calendar, NULL, "stock_folder",EJ_POPUP_FOLDER_NEW_CALENDAR ,0 }, { E_POPUP_ITEM, "10.ej.01", N_("_Rename"), sft_popup_rename_folder, NULL, NULL,EJ_POPUP_FOLDER_OP ,0 }, { E_POPUP_ITEM, "10.ej.02", N_("_Delete"), sft_popup_delete_folder, NULL, "stock_delete",EJ_POPUP_FOLDER_OP ,0 }, { E_POPUP_BAR, "20.ej" }, { E_POPUP_ITEM, "20.ej.00", N_("_Permissions..."), sft_popup_permission, NULL, "stock_folder-properties",EJ_POPUP_FOLDER_PERM ,0 }, { E_POPUP_ITEM, "20.ej.01", N_("Manage _Subscriptions..."), sft_popup_subscription, NULL, "stock_subscript",EJ_POPUP_FOLDER_SUB ,0 }, { E_POPUP_ITEM, "20.ej.02", N_("_Login"), sft_popup_login, NULL, NULL,EJ_POPUP_FOLDER_LOGIN ,0 }, }; static void sunone_popup_free(EPopup *ep, GSList *items, void *data) { g_slist_free(items); } static gboolean sft_tree_popup (SunOneFolderTree *tree, GdkEvent *event) { SunOneAccount *account; GtkMenu *menu; GSList *menus = NULL; int i; EPopup *sp; EPopupTarget *target; GtkTreeSelection * selection; GtkTreeModel *model; GtkTreeIter iter; gboolean is_store, is_account, is_personal; guint32 flags = ~0ul; selection = gtk_tree_view_get_selection (tree->priv->treeview); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return FALSE; gtk_tree_model_get (model, &iter, COL_POINTER_ACCOUNT, &account, COL_BOOL_IS_STORE, &is_store, COL_BOOL_IS_ACCOUNT, &is_account, COL_BOOL_IS_PERSONAL, &is_personal, -1); if (is_account) { flags &= ~(EJ_POPUP_FOLDER_LOGIN | EJ_POPUP_FOLDER_SUB | EJ_POPUP_FOLDER_NEW_CALENDAR); } else if (is_store) { if (is_personal) flags = ~EJ_POPUP_FOLDER_PERM; else flags = ~(EJ_POPUP_FOLDER_OP | EJ_POPUP_FOLDER_PERM); } sp = e_popup_new ("org.gnome.evolution-jescs.foldertree.popup"); target = e_popup_target_new(sp, 0, sizeof(*target)); for (i = 0; i < sizeof (sft_popup_menu) /sizeof (sft_popup_menu[0]); i++) { menus = g_slist_prepend (menus, &sft_popup_menu[i]); } e_popup_add_items (sp, menus, NULL, sunone_popup_free, tree); menu = e_popup_create_menu_once (sp, target, flags); /* FIXME free g_list menus*/ if (event == NULL || event->type == GDK_KEY_PRESS) { gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); } else { gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event->button.button, event->button.time); } return TRUE; } static gboolean sft_tree_button_press (GtkTreeView *treeview, GdkEventButton *event, SunOneFolderTree *tree) { GtkTreePath *tree_path; g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), FALSE); if (event->button != 3 && !(event->button == 1 && event->type == GDK_2BUTTON_PRESS)) return FALSE; if (!gtk_tree_view_get_path_at_pos (treeview, (int) event->x, (int) event->y, &tree_path, NULL, NULL, NULL)) return FALSE; /* select/focus the row that was right-clicked or double-clicked */ gtk_tree_view_set_cursor (treeview, tree_path, NULL, FALSE); gtk_tree_path_free (tree_path); if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { return TRUE; } return sft_tree_popup (tree, (GdkEvent *) event); } static gboolean sft_tree_popup_menu (GtkWidget *widget, SunOneFolderTree *tree) { return sft_tree_popup (tree, NULL); } gboolean sunone_folder_tree_get_selected (SunOneFolderTree *tree) { GtkTreeSelection *selection; g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), FALSE); selection = gtk_tree_view_get_selection (tree->priv->treeview); if (gtk_tree_selection_get_selected (selection, NULL, NULL)) return TRUE; return FALSE; } GtkWidget * sunone_folder_tree_get_scrolled (SunOneFolderTree *tree) { g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL); return tree->priv->scrolled; } GtkNotebook * sunone_folder_tree_get_body (SunOneFolderTree *tree) { g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL); return tree->priv->body; } GtkTreeView * sunone_folder_tree_get_tree_view (SunOneFolderTree *tree) { g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL); return tree->priv->treeview; } GtkTreeModel * sunone_folder_tree_get_model (SunOneFolderTree *tree) { g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL); return (GtkTreeModel *)tree->priv->treeview_model; } SunOneAccount * sunone_folder_tree_get_account (SunOneFolderTree *tree) { SunOneFolderTreePrivate *priv = tree->priv; SunOneAccount *account; GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; g_return_val_if_fail (SUNONE_IS_FOLDER_TREE (tree), NULL); selection = gtk_tree_view_get_selection (priv->treeview); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return NULL; gtk_tree_model_get (model, &iter, COL_POINTER_ACCOUNT, &account, -1); return account; } SunOneInvitationListModel * sunone_folder_tree_get_invitation_list_model (SunOneFolderTree *tree, const char *physical_uri) { SunOneFolderTreePrivate *priv = tree->priv; GList *l; for (l = priv->invitation_models; l != NULL; l = l->next) { SunOneInvitationListModel *model; model = l->data; if (sunone_invitation_list_model_get_uri (model) && !strcmp (sunone_invitation_list_model_get_uri (model), physical_uri)) return model; } return NULL; }