/* sunone-invitation-list.c * * Copyright (C) 2002-2004 Sun Microsystems, Inc * * AUTHORS * Jack Jia * Harry Lu * Alfred Peng * Rodrigo Moya * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lib/sunone-util.h" #include "evolution-calendar.h" #include "sunone-account.h" #include "sunone-invitation-list.h" #include "sunone-itip-view.h" #define INVITATION_CONTROL_IID "OAFIID:GNOME_Evolution_Calendar_iTip_Control:" EVOLUTION_BASE_VERSION #define COMPEDITORFACTORY "OAFIID:GNOME_Evolution_Calendar_CompEditorFactory:" EVOLUTION_BASE_VERSION #define USE_ITIP_VIEW 0 struct _SunOneInvitationListPrivate { EConfigListener *config; SunOneInvitationListModel *model; GtkWidget *pane; GtkTreeView *treeview; GtkWidget *control; }; extern char *evolution_dir; static void sunone_invitation_list_class_init (SunOneInvitationListClass *klass); static void sunone_invitation_list_init (SunOneInvitationList *object); static void sunone_invitation_list_destroy (GtkObject *object); static GObjectClass *parent_class = NULL; static void sunone_invitation_list_class_init (SunOneInvitationListClass *klass) { GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); parent_class = g_type_class_ref (GTK_TYPE_VBOX); object_class->destroy = sunone_invitation_list_destroy; } static void sunone_invitation_list_init (SunOneInvitationList *object) { SunOneInvitationList *list = SUNONE_INVITATION_LIST (object); SunOneInvitationListPrivate *priv; GtkWidget *scroll; int pos; list->priv = g_new0 (SunOneInvitationListPrivate, 1); priv = list->priv; priv->config = e_config_listener_new (); priv->model = NULL; priv->treeview = NULL; priv->pane = gtk_vpaned_new (); gtk_box_set_homogeneous (GTK_BOX (list), FALSE); scroll = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); #if USE_ITIP_VIEW priv->control = sunone_itip_view_new (); #else priv->control = bonobo_widget_new_control (INVITATION_CONTROL_IID, NULL); #endif gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scroll), priv->control); gtk_paned_add2 (GTK_PANED (priv->pane), GTK_WIDGET (scroll)); pos = e_config_listener_get_long_with_default (priv->config, "/apps/evolution/evolution-jescs/invitationList/panePosition", 100, NULL); gtk_paned_set_position (GTK_PANED (priv->pane), pos); gtk_box_pack_start (GTK_BOX (list), priv->pane, TRUE, TRUE, 0); } static void sunone_invitation_list_destroy (GtkObject *object) { SunOneInvitationList *list = SUNONE_INVITATION_LIST (object); SunOneInvitationListPrivate *priv = list->priv; g_return_if_fail (IS_SUNONE_INVITATION_LIST (list)); if (priv) { if (priv->config) { int pos = gtk_paned_get_position (GTK_PANED (priv->pane)); e_config_listener_set_long (priv->config, "/apps/evolution/evolution-jescs/invitationList/panePosition", pos); g_object_unref (G_OBJECT (priv->config)); priv->config = NULL; } if (priv->model) { /* Don't listen to model updates any more */ g_object_unref (G_OBJECT (priv->model)); priv->model = NULL; } if (priv->treeview) { priv->treeview = NULL; } #if USE_ITIP_VIEW if (priv->control) { g_object_unref (priv->control); } #endif g_free (priv); list->priv = NULL; } if ( GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } G_DEFINE_TYPE (SunOneInvitationList, sunone_invitation_list, GTK_TYPE_VBOX) static icalcomponent * toplevel_with_zones (SunOneInvitationList *list, ECalComponent *comp) { icalcomponent *top_level, *icomp; icalproperty *prop; icalvalue *value; top_level = e_cal_util_new_top_level (); prop = icalproperty_new (ICAL_METHOD_PROPERTY); value = icalvalue_new_method (ICAL_METHOD_REQUEST); icalproperty_set_value (prop, value); icalcomponent_add_property (top_level, prop); icomp = e_cal_component_get_icalcomponent (comp); icomp = icalcomponent_new_clone (icomp); icalcomponent_add_component (top_level, icomp); return top_level; } static void set_data (BonoboWidget *control, const char *string) { Bonobo_PersistStream persist; BonoboObject *bstream; CORBA_Environment ev; persist = (Bonobo_PersistStream) Bonobo_Unknown_queryInterface ( bonobo_widget_get_objref (control), "IDL:Bonobo/PersistStream:1.0", &ev); if (persist == CORBA_OBJECT_NIL) { g_object_ref_sink (G_OBJECT (control)); return ; } bstream = bonobo_stream_mem_create (string, strlen (string), TRUE, FALSE); CORBA_exception_init (&ev); Bonobo_PersistStream_load (persist, BONOBO_OBJREF (bstream), "text/calendar", &ev); bonobo_object_unref (BONOBO_OBJECT (bstream)); Bonobo_Unknown_unref (persist, &ev); CORBA_Object_release (persist, &ev); } static void set_data_and_props (SunOneInvitationList *list, int row) { SunOneInvitationListPrivate *priv = list->priv; BonoboControlFrame *control_frame; Bonobo_PropertyBag prop_bag; ECalComponent *comp; icalcomponent *top_level; char *string; CORBA_Environment ev; if (row == -1) { set_data (BONOBO_WIDGET (priv->control), ""); return; } comp = sunone_invitation_list_model_get_comp (priv->model, row); if (!comp) return; /* Set the from address on the control */ control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (priv->control)); prop_bag = bonobo_control_frame_get_control_property_bag (control_frame, NULL); if (prop_bag != CORBA_OBJECT_NIL){ ECalComponentOrganizer organizer; CORBA_exception_init (&ev); e_cal_component_get_organizer (comp, &organizer); bonobo_property_bag_client_set_value_string (prop_bag, "from_address", organizer.value, &ev); CORBA_exception_init (&ev); bonobo_property_bag_client_set_value_gint (prop_bag, "view_only", 1, &ev); Bonobo_Unknown_unref (prop_bag, &ev); CORBA_exception_free (&ev); } /* Send the icalendar code over */ top_level = toplevel_with_zones (list, comp); string = icalcomponent_as_ical_string (top_level); set_data (BONOBO_WIDGET (priv->control), string); icalcomponent_free (top_level); } #if USE_ITIP_VIEW static const gchar * itip_strip_mailto (const gchar *address) { if (address == NULL) return NULL; if (!g_strncasecmp (address, "mailto:", 7)) address += 7; return address; } static void set_itip_view (SunOneInvitationList *list, int row) { SunOneInvitationListPrivate *priv = list->priv; ECalComponent *comp; ECalComponentText text; const char *string; ECalComponentDateTime datetime; GString *gstring = NULL; GSList *description_list, *l; SunOneItipView *itip_view; ECalComponentOrganizer organizer; if (row == -1) { if (GTK_WIDGET_VISIBLE (priv->control)) gtk_widget_hide (priv->control); return; } comp = sunone_invitation_list_model_get_comp (priv->model, row); if (!comp) return; itip_view = (SunOneItipView *)priv->control; sunone_itip_view_set_mode (itip_view, SUNONE_ITIP_VIEW_MODE_REQUEST); sunone_itip_view_set_item_type (itip_view, E_CAL_SOURCE_TYPE_EVENT); e_cal_component_get_organizer (comp, &organizer); sunone_itip_view_set_organizer (itip_view, organizer.cn ? organizer.cn : itip_strip_mailto (organizer.value)); sunone_itip_view_set_sentby (itip_view, organizer.sentby); e_cal_component_get_summary (comp, &text); sunone_itip_view_set_summary (itip_view, text.value ? text.value : _("None")); e_cal_component_get_location (comp, &string); sunone_itip_view_set_location (itip_view, string); e_cal_component_get_description_list (comp, &description_list); for (l = description_list; l; l = l->next) { ECalComponentText *text = l->data; if (!gstring && text->value) gstring = g_string_new (text->value); else if (text->value) g_string_append_printf (gstring, "\n\n%s", text->value); } e_cal_component_free_text_list (description_list); if (gstring) { sunone_itip_view_set_description (itip_view, gstring->str); g_string_free (gstring, TRUE); } else sunone_itip_view_set_description (itip_view, NULL); e_cal_component_get_dtstart (comp, &datetime); if (datetime.value) { struct tm start_tm; start_tm = icaltimetype_to_tm_with_zone (datetime.value, icaltimezone_get_utc_timezone (), priv->model->zone); sunone_itip_view_set_start (itip_view, &start_tm); } e_cal_component_free_datetime (&datetime); e_cal_component_get_dtend (comp, &datetime); if (datetime.value) { struct tm end_tm; end_tm = icaltimetype_to_tm_with_zone (datetime.value, icaltimezone_get_utc_timezone (), priv->model->zone); sunone_itip_view_set_end (itip_view, &end_tm); } e_cal_component_free_datetime (&datetime); /* Recurrence info */ sunone_itip_view_clear_upper_info_items (itip_view); if (e_cal_component_has_recurrences (comp)) { sunone_itip_view_add_upper_info_item (itip_view, SUNONE_ITIP_VIEW_INFO_ITEM_TYPE_INFO, "This meeting recurs"); } sunone_itip_view_set_status (itip_view, NULL); sunone_itip_view_set_comment (itip_view, NULL); sunone_itip_view_set_show_rsvp (itip_view, FALSE); if (!GTK_WIDGET_VISIBLE (priv->control)) gtk_widget_show (priv->control); } #endif static void invitation_row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { SunOneInvitationList *list = SUNONE_INVITATION_LIST (data); SunOneInvitationListPrivate *priv = list->priv; GtkTreeSelection *selection; GtkTreePath *select_path, *changed_path; GtkTreeIter select_iter; gint select_row, changed_row; selection = gtk_tree_view_get_selection (priv->treeview); if (!gtk_tree_selection_get_selected (selection, NULL, &select_iter)) return; select_path = gtk_tree_model_get_path (model, &select_iter); select_row = gtk_tree_path_get_indices (select_path)[0]; gtk_tree_path_free (select_path); changed_path = gtk_tree_model_get_path (model, iter); changed_row = gtk_tree_path_get_indices (changed_path)[0]; gtk_tree_path_free (changed_path); if (changed_row == select_row) #if USE_ITIP_VIEW set_itip_view (list, select_row); #else set_data_and_props (list, select_row); #endif } static void invitation_row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data) { SunOneInvitationList *list = SUNONE_INVITATION_LIST (data); SunOneInvitationListPrivate *priv = list->priv; GtkTreeSelection *selection; selection = gtk_tree_view_get_selection (priv->treeview); if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) #if USE_ITIP_VIEW set_itip_view (list, -1); #else set_data_and_props (list, -1); #endif } static void invitation_cursor_changed_cb (GtkTreeView *treeview, gpointer data) { SunOneInvitationList *list = SUNONE_INVITATION_LIST (data); GtkTreeSelection *selection; GtkTreeModel *model; GtkTreePath *path; GtkTreeIter iter; gint row; selection = gtk_tree_view_get_selection (treeview); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; path = gtk_tree_model_get_path (model, &iter); row = gtk_tree_path_get_indices (path)[0]; gtk_tree_path_free (path); #if USE_ITIP_VIEW set_itip_view (list, row); #else set_data_and_props (list, row); #endif } #if 0 static gint invitation_double_clicked_cb (ETable *et, int row, int col, GdkEvent *event, gpointer data) { SunOneInvitationList *list = SUNONE_INVITATION_LIST (data); SunOneInvitationListPrivate *priv = list->priv; ECalComponent *comp, *clone; GNOME_Evolution_Calendar_CompEditorFactory factory; const char *uid; CORBA_Environment ev; comp = sunone_invitation_list_model_get_comp (priv->model, row); if (!comp) return FALSE; clone = e_cal_component_clone (comp); sunone_util_mangle_uid (clone); e_cal_component_get_uid (clone, &uid); /* Get the factory */ CORBA_exception_init (&ev); factory = bonobo_activation_activate_from_id (COMPEDITORFACTORY, 0, NULL, &ev); if (BONOBO_EX (&ev)) { g_message ("%s: Could not activate the component editor factory (%s)", G_GNUC_FUNCTION, CORBA_exception_id (&ev)); CORBA_exception_free (&ev); return FALSE; } GNOME_Evolution_Calendar_CompEditorFactory_editExisting (factory, priv->model->uri, (char *)uid, GNOME_Evolution_Calendar_CompEditorFactory_EDITOR_MODE_EVENT, &ev); if (BONOBO_EX (&ev)) { g_message ("%s: Execption while editing the component (%s)", G_GNUC_FUNCTION, CORBA_exception_id (&ev)); } g_object_unref (G_OBJECT (clone)); CORBA_exception_free (&ev); bonobo_object_release_unref (factory, NULL); return FALSE; } #endif static void status_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *text, GtkTreeView *view) { SunOneInvitationListModel *model = SUNONE_INVITATION_LIST_MODEL (gtk_tree_view_get_model (view)); GtkTreePath *treepath = gtk_tree_path_new_from_string (path); int row = gtk_tree_path_get_indices (treepath)[0]; sunone_invitation_list_model_set_value (model, STATUS_COL, row, text); gtk_tree_path_free (treepath); } static void setup_invitation_list (SunOneInvitationList *list) { SunOneInvitationListPrivate *priv = list->priv; GtkWidget *scrolled_window; GList *strings; GtkCellRenderer *renderer; GtkTreeViewColumn *column; gboolean editable; priv->treeview = (GtkTreeView *)gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->model)); gtk_tree_view_set_headers_visible (priv->treeview, TRUE); gtk_tree_view_set_rules_hint (priv->treeview, TRUE); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Starts"), renderer, "text", START_COL, NULL); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_append_column (priv->treeview, column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Summary"), renderer, "text", SUMMARY_COL, NULL); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_append_column (priv->treeview, column); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes (_("Organizer"), renderer, "text", ORGANIZER_COL, NULL); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_append_column (priv->treeview, column); strings = NULL; strings = g_list_append (strings, (char*) _("Needs Action")); strings = g_list_append (strings, (char*) _("Accepted")); strings = g_list_append (strings, (char*) _("Declined")); strings = g_list_append (strings, (char*) _("Tentative")); renderer = e_cell_renderer_combo_new (); editable = sunone_util_has_permissions (priv->model->props, sunone_account_get_user (priv->model->account), SUNONE_ACE_CONTEXT_CALENDAR_COMPONENTS, SUNONE_ACE_PERMISSION_WRITE | SUNONE_ACE_PERMISSION_REPLY); g_object_set (G_OBJECT (renderer), "list", strings, "editable", editable, NULL); gtk_tree_view_insert_column_with_attributes (priv->treeview, -1, _("Status"), renderer, "text", STATUS_COL, NULL); g_signal_connect (renderer, "edited", G_CALLBACK (status_edited_cb), priv->treeview); scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy ((GtkScrolledWindow *)scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *)scrolled_window, GTK_SHADOW_IN); gtk_scrolled_window_add_with_viewport ((GtkScrolledWindow *)scrolled_window, (GtkWidget *)priv->treeview); gtk_paned_add1 (GTK_PANED (priv->pane), scrolled_window); g_signal_connect (priv->treeview, "cursor_changed", G_CALLBACK (invitation_cursor_changed_cb), list); g_signal_connect (priv->model, "row_deleted", G_CALLBACK (invitation_row_deleted_cb), list); g_signal_connect (priv->model, "row_changed", G_CALLBACK (invitation_row_changed_cb), list); #if 0 g_signal_connect (G_OBJECT (et), "double_click", G_CALLBACK (invitation_double_clicked_cb), list); #endif } GtkWidget * sunone_invitation_list_new (SunOneInvitationListModel *model) { SunOneInvitationList *list; SunOneInvitationListPrivate *priv; list = g_object_new (SUNONE_INVITATION_LIST_TYPE, NULL); priv = list->priv; g_object_ref (G_OBJECT (model)); priv->model = model; /* Display the list of invitations */ setup_invitation_list (list); gtk_widget_show_all ((GtkWidget *)list); #if USE_ITIP_VIEW gtk_widget_hide (priv->control); #endif return (GtkWidget *)list; }