diff options
author | Mike Kestner <mkestner@ximian.com> | 2003-10-01 06:39:04 +0800 |
---|---|---|
committer | Mike Kestner <mkestner@src.gnome.org> | 2003-10-01 06:39:04 +0800 |
commit | 54869d90bd04f0a816b1786530d3555ac2b034cc (patch) | |
tree | 3b2859280d478fa6b57227a419125eadd455a615 /calendar | |
parent | 642e0e5abd8486793e95fab56e9fd9f3dd0394f8 (diff) | |
download | gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.tar gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.tar.gz gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.tar.bz2 gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.tar.lz gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.tar.xz gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.tar.zst gsoc2013-evolution-54869d90bd04f0a816b1786530d3555ac2b034cc.zip |
new VOID:STRING,STRING,STRING build the new view/store/renderer/editable
2003-09-30 Mike Kestner <mkestner@ximian.com>
* cal-util/cal-util-marshal.list : new VOID:STRING,STRING,STRING
* gui/Makefile.am : build the new view/store/renderer/editable
* gui/e-select-names-renderer.* : new completion cell renderer
* gui/e-select-names-editable.* : new completion cell editable
* gui/e-meeting-model.* : killed. code reused in list-view/store
* gui/e-meeting-store.* : port of EMeetingModel to GtkTreeModel
* gui/e-meeting-list-view.* : GtkTreeView subclass for attendee lists
* gui/e-meeting-time-sel.c : Use the new store/view
* gui/e-meeting-time-sel-item.c : Use the new store/view
* gui/dialogs/Makefile.am : don't install the etspec anymore.
* gui/dialogs/event-editor.c : Use the new store/view.
* gui/dialogs/meeting-page.c : Use the new store/view.
* gui/dialogs/shedule-page.c : Use the new store/view.
* gui/dialogs/task-editor.c : Use the new store/view.
svn path=/trunk/; revision=22774
Diffstat (limited to 'calendar')
24 files changed, 2712 insertions, 2290 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index 567639adda..20ac5c32b1 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,20 @@ +2003-09-30 Mike Kestner <mkestner@ximian.com> + + * cal-util/cal-util-marshal.list : new VOID:STRING,STRING,STRING + * gui/Makefile.am : build the new view/store/renderer/editable + * gui/e-select-names-renderer.* : new completion cell renderer + * gui/e-select-names-editable.* : new completion cell editable + * gui/e-meeting-model.* : killed. code reused in list-view/store + * gui/e-meeting-store.* : port of EMeetingModel to GtkTreeModel + * gui/e-meeting-list-view.* : GtkTreeView subclass for attendee lists + * gui/e-meeting-time-sel.c : Use the new store/view + * gui/e-meeting-time-sel-item.c : Use the new store/view + * gui/dialogs/Makefile.am : don't install the etspec anymore. + * gui/dialogs/event-editor.c : Use the new store/view. + * gui/dialogs/meeting-page.c : Use the new store/view. + * gui/dialogs/shedule-page.c : Use the new store/view. + * gui/dialogs/task-editor.c : Use the new store/view. + 2003-09-29 JP Rosevear <jpr@ximian.com> * conduits/todo/Makefile.am: link to libical-evolution diff --git a/calendar/cal-util/cal-util-marshal.list b/calendar/cal-util/cal-util-marshal.list index 7c58793852..77509b1c50 100644 --- a/calendar/cal-util/cal-util-marshal.list +++ b/calendar/cal-util/cal-util-marshal.list @@ -3,6 +3,7 @@ NONE:ENUM,ENUM NONE:ENUM,STRING NONE:STRING,BOOL,INT,INT NONE:STRING,STRING +NONE:STRING,STRING,STRING NONE:POINTER,ENUM NONE:POINTER,STRING NONE:POINTER,POINTER diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index 5cee4ae0e3..553186d85c 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -127,8 +127,10 @@ libevolution_calendar_la_SOURCES = \ e-itip-control.c \ e-meeting-attendee.c \ e-meeting-attendee.h \ - e-meeting-model.c \ - e-meeting-model.h \ + e-meeting-list-view.c \ + e-meeting-list-view.h \ + e-meeting-store.c \ + e-meeting-store.h \ e-meeting-time-sel.c \ e-meeting-time-sel.h \ e-meeting-time-sel-item.c \ @@ -136,6 +138,10 @@ libevolution_calendar_la_SOURCES = \ e-meeting-types.h \ e-meeting-utils.c \ e-meeting-utils.h \ + e-select-names-editable.c \ + e-select-names-editable.h \ + e-select-names-renderer.c \ + e-select-names-renderer.h \ e-week-view-event-item.c \ e-week-view-event-item.h \ e-week-view-layout.c \ diff --git a/calendar/gui/dialogs/Makefile.am b/calendar/gui/dialogs/Makefile.am index 4bbdd90ca5..e6067d4d23 100644 --- a/calendar/gui/dialogs/Makefile.am +++ b/calendar/gui/dialogs/Makefile.am @@ -26,7 +26,6 @@ INCLUDES = \ -DEVOLUTION_DATADIR=\""$(datadir)"\" \ -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \ -DEVOLUTION_IMAGESDIR=\""$(imagesdir)"\" \ - -DEVOLUTION_ETSPECDIR=\""$(etspecdir)"\" \ -DEVOLUTION_UIDIR=\""$(evolutionuidir)"\" \ -DPREFIX=\""$(prefix)"\" \ $(EVOLUTION_CALENDAR_CFLAGS) @@ -92,7 +91,6 @@ glade_DATA = \ task-details-page.glade \ task-page.glade -etspec_DATA = meeting-page.etspec CLEANFILES = $(BUILT_SOURCES) @@ -100,5 +98,4 @@ dist-hook: cd $(distdir); rm -f $(BUILT_SOURCES) EXTRA_DIST = \ - $(glade_DATA) \ - $(etspec_DATA) + $(glade_DATA) diff --git a/calendar/gui/dialogs/event-editor.c b/calendar/gui/dialogs/event-editor.c index a67f2b14f9..978a2dbb22 100644 --- a/calendar/gui/dialogs/event-editor.c +++ b/calendar/gui/dialogs/event-editor.c @@ -26,6 +26,7 @@ #include <config.h> #include <string.h> #include <glade/glade.h> +#include <gal/util/e-util.h> #include <gal/widgets/e-unicode.h> #include <libgnome/gnome-i18n.h> #include <widgets/misc/e-dateedit.h> @@ -45,7 +46,7 @@ struct _EventEditorPrivate { MeetingPage *meet_page; SchedulePage *sched_page; - EMeetingModel *model; + EMeetingStore *model; gboolean meeting_shown; gboolean updating; @@ -65,8 +66,8 @@ static void refresh_meeting_cmd (GtkWidget *widget, gpointer data); static void cancel_meeting_cmd (GtkWidget *widget, gpointer data); static void forward_cmd (GtkWidget *widget, gpointer data); -static void model_row_changed_cb (ETableModel *etm, int row, gpointer data); -static void row_count_changed_cb (ETableModel *etm, int row, int count, gpointer data); +static void model_row_change_insert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); +static void model_row_delete_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data); static EPixmap pixmaps [] = { E_PIXMAP ("/Toolbar/Actions/ActionScheduleMeeting", "schedule-meeting-24.png"), @@ -162,12 +163,12 @@ init_widgets (EventEditor *ee) priv = ee->priv; - g_signal_connect((priv->model), "model_row_changed", - G_CALLBACK (model_row_changed_cb), ee); - g_signal_connect((priv->model), "model_rows_inserted", - G_CALLBACK (row_count_changed_cb), ee); - g_signal_connect((priv->model), "model_rows_deleted", - G_CALLBACK (row_count_changed_cb), ee); + g_signal_connect((priv->model), "row_changed", + G_CALLBACK (model_row_change_insert_cb), ee); + g_signal_connect((priv->model), "row_inserted", + G_CALLBACK (model_row_change_insert_cb), ee); + g_signal_connect((priv->model), "row_deleted", + G_CALLBACK (model_row_delete_cb), ee); } /* Object initialization function for the event editor */ @@ -179,7 +180,7 @@ event_editor_init (EventEditor *ee) priv = g_new0 (EventEditorPrivate, 1); ee->priv = priv; - priv->model = E_MEETING_MODEL (e_meeting_model_new ()); + priv->model = E_MEETING_STORE (e_meeting_store_new ()); priv->meeting_shown = TRUE; priv->updating = FALSE; } @@ -232,6 +233,7 @@ event_editor_construct (EventEditor *ee, CalClient *client) init_widgets (ee); set_menu_sens (ee); + gtk_window_set_default_size (GTK_WINDOW (ee), 300, 225); return ee; } @@ -245,7 +247,7 @@ event_editor_set_cal_client (CompEditor *editor, CalClient *client) ee = EVENT_EDITOR (editor); priv = ee->priv; - e_meeting_model_set_cal_client (priv->model, client); + e_meeting_store_set_cal_client (priv->model, client); if (parent_class->set_cal_client) parent_class->set_cal_client (editor, client); @@ -275,7 +277,7 @@ event_editor_edit_comp (CompEditor *editor, CalComponent *comp) cal_component_get_attendee_list (comp, &attendees); /* Clear things up */ - e_meeting_model_remove_all_attendees (priv->model); + e_meeting_store_remove_all_attendees (priv->model); /* Set up the attendees */ if (attendees == NULL) { @@ -304,7 +306,7 @@ event_editor_edit_comp (CompEditor *editor, CalComponent *comp) /* If we aren't the organizer or the attendee is just delegating, don't allow editing */ if (!comp_editor_get_user_org (editor) || e_meeting_attendee_is_set_delto (ia)) e_meeting_attendee_set_edit_level (ia, E_MEETING_ATTENDEE_EDIT_NONE); - e_meeting_model_add_attendee (priv->model, ia); + e_meeting_store_add_attendee (priv->model, ia); g_object_unref(ia); } @@ -321,7 +323,7 @@ event_editor_edit_comp (CompEditor *editor, CalComponent *comp) account = (EAccount*)e_iterator_get(it); - ia = e_meeting_model_find_attendee (priv->model, account->id->address, &row); + ia = e_meeting_store_find_attendee (priv->model, account->id->address, &row); if (ia != NULL) e_meeting_attendee_set_edit_level (ia, E_MEETING_ATTENDEE_EDIT_STATUS); } @@ -329,7 +331,7 @@ event_editor_edit_comp (CompEditor *editor, CalComponent *comp) } else if (cal_client_get_organizer_must_attend (client)) { EMeetingAttendee *ia; - ia = e_meeting_model_find_attendee (priv->model, organizer.value, &row); + ia = e_meeting_store_find_attendee (priv->model, organizer.value, &row); if (ia != NULL) e_meeting_attendee_set_edit_level (ia, E_MEETING_ATTENDEE_EDIT_NONE); } @@ -363,7 +365,7 @@ event_editor_send_comp (CompEditor *editor, CalComponentItipMethod method) CalClient *client; gboolean result; - client = e_meeting_model_get_cal_client (priv->model); + client = e_meeting_store_get_cal_client (priv->model); result = itip_send_comp (CAL_COMPONENT_METHOD_CANCEL, comp, client, NULL); g_object_unref((comp)); @@ -498,29 +500,23 @@ forward_cmd (GtkWidget *widget, gpointer data) } static void -model_row_changed_cb (ETableModel *etm, int row, gpointer data) +model_changed (EventEditor *ee) { - EventEditor *ee = EVENT_EDITOR (data); - EventEditorPrivate *priv; - - priv = ee->priv; - - if (!priv->updating) { + if (!ee->priv->updating) { comp_editor_set_changed (COMP_EDITOR (ee), TRUE); comp_editor_set_needs_send (COMP_EDITOR (ee), TRUE); } } static void -row_count_changed_cb (ETableModel *etm, int row, int count, gpointer data) +model_row_change_insert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { - EventEditor *ee = EVENT_EDITOR (data); - EventEditorPrivate *priv; - - priv = ee->priv; - - if (!priv->updating) { - comp_editor_set_changed (COMP_EDITOR (ee), TRUE); - comp_editor_set_needs_send (COMP_EDITOR (ee), TRUE); - } + model_changed (EVENT_EDITOR (data)); } + +static void +model_row_delete_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data) +{ + model_changed (EVENT_EDITOR (data)); +} + diff --git a/calendar/gui/dialogs/meeting-page.c b/calendar/gui/dialogs/meeting-page.c index e603978520..6c8212e93c 100644 --- a/calendar/gui/dialogs/meeting-page.c +++ b/calendar/gui/dialogs/meeting-page.c @@ -34,10 +34,7 @@ #include <libgnome/gnome-i18n.h> #include <glade/glade.h> #include <libgnomeui/gnome-stock-icons.h> -#include <gal/e-table/e-cell-combo.h> -#include <gal/e-table/e-cell-text.h> -#include <gal/e-table/e-table-simple.h> -#include <gal/e-table/e-table-scrolled.h> +#include <gal/util/e-util.h> #include <gal/widgets/e-unicode.h> #include <gal/widgets/e-popup-menu.h> #include <gal/widgets/e-gui-utils.h> @@ -47,27 +44,13 @@ #include "../calendar-component.h" #include "../e-meeting-attendee.h" -#include "../e-meeting-model.h" +#include "../e-meeting-store.h" +#include "../e-meeting-list-view.h" #include "../itip-utils.h" #include "comp-editor-util.h" #include "e-delegate-dialog.h" #include "meeting-page.h" - - -enum columns { - MEETING_ATTENDEE_COL, - MEETING_MEMBER_COL, - MEETING_TYPE_COL, - MEETING_ROLE_COL, - MEETING_RSVP_COL, - MEETING_DELTO_COL, - MEETING_DELFROM_COL, - MEETING_STATUS_COL, - MEETING_CN_COL, - MEETING_LANG_COL, - MEETING_COLUMN_COUNT -}; /* Private part of the MeetingPage structure */ struct _MeetingPagePrivate { @@ -94,9 +77,9 @@ struct _MeetingPagePrivate { GtkWidget *existing_organizer_btn; GtkWidget *invite; - /* E Table stuff */ - EMeetingModel *model; - ETableScrolled *etable; + /* ListView stuff */ + EMeetingStore *model; + EMeetingListView *list_view; gint row; /* For handling who the organizer is */ @@ -116,8 +99,6 @@ static void meeting_page_focus_main_widget (CompEditorPage *page); static void meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp); static gboolean meeting_page_fill_component (CompEditorPage *page, CalComponent *comp); -static gint right_click_cb (ETable *etable, gint row, gint col, GdkEvent *event, gpointer data); - static CompEditorPageClass *parent_class = NULL; @@ -178,7 +159,7 @@ meeting_page_init (MeetingPage *mpage) priv->invite = NULL; priv->model = NULL; - priv->etable = NULL; + priv->list_view = NULL; priv->updating = FALSE; } @@ -377,14 +358,12 @@ meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp) page->client, CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) gtk_widget_hide (priv->existing_organizer_btn); - e_meeting_model_etable_click_to_add (priv->model, TRUE); } else { if (cal_client_get_static_capability ( page->client, CAL_STATIC_CAPABILITY_ORGANIZER_NOT_EMAIL_ADDRESS)) gtk_widget_hide (priv->existing_organizer_btn); gtk_widget_hide (priv->invite); - e_meeting_model_etable_click_to_add (priv->model, FALSE); } if (organizer.cn != NULL) @@ -401,7 +380,7 @@ meeting_page_fill_widgets (CompEditorPage *page, CalComponent *comp) a = get_current_account (mpage); if (a != NULL) { - priv->ia = e_meeting_model_add_attendee_with_defaults (priv->model); + priv->ia = e_meeting_store_add_attendee_with_defaults (priv->model); g_object_ref (priv->ia); e_meeting_attendee_set_address (priv->ia, g_strdup_printf ("MAILTO:%s", a->id->address)); @@ -453,12 +432,12 @@ meeting_page_fill_component (CompEditorPage *page, CalComponent *comp) g_free (addr); } - if (e_meeting_model_count_actual_attendees (priv->model) < 1) { + if (e_meeting_store_count_actual_attendees (priv->model) < 1) { e_notice (page, GTK_MESSAGE_ERROR, _("At least one attendee is required.")); return FALSE; } - set_attendees (comp, e_meeting_model_get_attendees (priv->model)); + set_attendees (comp, e_meeting_store_get_attendees (priv->model)); return TRUE; } @@ -535,10 +514,10 @@ org_changed_cb (GtkWidget *widget, gpointer data) e_meeting_attendee_set_address (priv->ia, g_strdup_printf ("MAILTO:%s", a->id->address)); e_meeting_attendee_set_cn (priv->ia, g_strdup (a->id->name)); - if (!e_meeting_model_find_attendee (priv->model, e_meeting_attendee_get_address (priv->ia), NULL)) - e_meeting_model_add_attendee (priv->model, priv->ia); + if (!e_meeting_store_find_attendee (priv->model, e_meeting_attendee_get_address (priv->ia), NULL)) + e_meeting_store_add_attendee (priv->model, priv->ia); } else { - e_meeting_model_remove_attendee (priv->model, priv->ia); + e_meeting_store_remove_attendee (priv->model, priv->ia); } } @@ -558,7 +537,6 @@ change_clicked_cb (GtkWidget *widget, gpointer data) gtk_widget_show (priv->organizer_table); gtk_widget_hide (priv->existing_organizer_table); gtk_widget_show (priv->invite); - e_meeting_model_etable_click_to_add (priv->model, TRUE); comp_editor_page_notify_needs_send (COMP_EDITOR_PAGE (mpage)); @@ -575,7 +553,7 @@ invite_cb (GtkWidget *widget, gpointer data) mpage = MEETING_PAGE (data); priv = mpage->priv; - e_meeting_model_invite_others_dialog (priv->model); + e_meeting_list_view_invite_others_dialog (priv->list_view); } /* Hooks the widget signals */ @@ -598,67 +576,6 @@ init_widgets (MeetingPage *mpage) G_CALLBACK (invite_cb), mpage); } -#if 0 -static void -popup_delegate_cb (GtkWidget *widget, gpointer data) -{ - MeetingPage *mpage = MEETING_PAGE (data); - MeetingPagePrivate *priv; - EDelegateDialog *edd; - GtkWidget *dialog; - EMeetingAttendee *ia; - char *address = NULL, *name = NULL; - - priv = mpage->priv; - - ia = e_meeting_model_find_attendee_at_row (priv->model, priv->row); - - /* Show dialog. */ - edd = e_delegate_dialog_new (NULL, itip_strip_mailto (e_meeting_attendee_get_delto (ia))); - dialog = e_delegate_dialog_get_toplevel (edd); - - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK){ - EMeetingAttendee *ic; - - name = e_delegate_dialog_get_delegate_name (edd); - address = e_delegate_dialog_get_delegate (edd); - - /* Make sure we can add the new delegatee person */ - if (e_meeting_model_find_attendee (priv->model, address, NULL) != NULL) { - e_notice (mpage, GTK_MESSAGE_ERROR, - _("That person is already attending the meeting!")); - goto cleanup; - } - - /* Update information for attendee */ - if (e_meeting_attendee_is_set_delto (ia)) { - EMeetingAttendee *ib; - - ib = e_meeting_model_find_attendee (priv->model, itip_strip_mailto (e_meeting_attendee_get_delto (ia)), NULL); - if (ib != NULL) { - g_object_ref((ib)); - g_ptr_array_add (priv->deleted_attendees, ib); - - e_meeting_model_remove_attendee (priv->model, ib); - } - } - e_meeting_attendee_set_delto (ia, g_strdup_printf ("MAILTO:%s", address)); - - /* Construct delegatee information */ - ic = e_meeting_model_add_attendee_with_defaults (priv->model); - - e_meeting_attendee_set_address (ic, g_strdup_printf ("MAILTO:%s", address)); - e_meeting_attendee_set_delfrom (ic, g_strdup (e_meeting_attendee_get_address (ia))); - e_meeting_attendee_set_cn (ic, g_strdup (name)); - } - - cleanup: - g_free (name); - g_free (address); - g_object_unref((edd)); -} -#endif - static void popup_delete_cb (GtkWidget *widget, gpointer data) { @@ -669,7 +586,7 @@ popup_delete_cb (GtkWidget *widget, gpointer data) priv = mpage->priv; - ia = e_meeting_model_find_attendee_at_row (priv->model, priv->row); + ia = e_meeting_store_find_attendee_at_row (priv->model, priv->row); /* If the user deletes the attendee explicitly, assume they no longer want the organizer showing up */ @@ -682,7 +599,7 @@ popup_delete_cb (GtkWidget *widget, gpointer data) if (e_meeting_attendee_is_set_delfrom (ia)) { EMeetingAttendee *ib; - ib = e_meeting_model_find_attendee (priv->model, e_meeting_attendee_get_delfrom (ia), &pos); + ib = e_meeting_store_find_attendee (priv->model, e_meeting_attendee_get_delfrom (ia), &pos); if (ib != NULL) { e_meeting_attendee_set_delto (ib, NULL); e_meeting_attendee_set_edit_level (ib, E_MEETING_ATTENDEE_EDIT_FULL); @@ -695,10 +612,10 @@ popup_delete_cb (GtkWidget *widget, gpointer data) g_object_ref((ia)); g_ptr_array_add (priv->deleted_attendees, ia); - e_meeting_model_remove_attendee (priv->model, ia); + e_meeting_store_remove_attendee (priv->model, ia); if (e_meeting_attendee_get_delto (ia) != NULL) - ib = e_meeting_model_find_attendee (priv->model, e_meeting_attendee_get_delto (ia), NULL); + ib = e_meeting_store_find_attendee (priv->model, e_meeting_attendee_get_delto (ia), NULL); ia = ib; } } @@ -719,6 +636,8 @@ static EPopupMenu context_menu[] = { E_POPUP_TERMINATOR }; +#if 0 +/* FIXME: handle context menu for treeview */ /* handle context menu over message-list */ static gint right_click_cb (ETable *etable, gint row, gint col, GdkEvent *event, gpointer data) @@ -752,26 +671,12 @@ right_click_cb (ETable *etable, gint row, gint col, GdkEvent *event, gpointer da return TRUE; } - +#endif - -/* Callback used when the ETable gets a focus-out event. We have to commit any - * pending click-to-add state for if the event editor is being destroyed. - */ -static gint -table_canvas_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, gpointer data) +static void +add_btn_clicked_cb (GtkButton *btn, MeetingPage *mpage) { - MeetingPage *mpage; - MeetingPagePrivate *priv; - ETable *etable; - - mpage = MEETING_PAGE (data); - priv = mpage->priv; - - etable = e_table_scrolled_get_table (priv->etable); - - e_table_commit_click_to_add (etable); - return TRUE; + e_meeting_store_add_attendee_with_defaults (mpage->priv->model); } /** @@ -784,16 +689,15 @@ table_canvas_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, gpointer dat * be created. **/ MeetingPage * -meeting_page_construct (MeetingPage *mpage, EMeetingModel *emm, +meeting_page_construct (MeetingPage *mpage, EMeetingStore *ems, CalClient *client) { MeetingPagePrivate *priv; - ETable *real_table; - gchar *filename; const char *backend_address; EIterator *it; EAccount *def_account; GList *address_strings = NULL, *l; + GtkWidget *sw, *btn; priv = mpage->priv; @@ -849,25 +753,27 @@ meeting_page_construct (MeetingPage *mpage, EMeetingModel *emm, g_free (l->data); g_list_free (address_strings); - /* The etable displaying attendees and their status */ - g_object_ref((emm)); - priv->model = emm; + g_object_ref((ems)); + priv->model = ems; + + btn = gtk_button_new_with_label ("Add Attendee"); + g_signal_connect(btn, "clicked", G_CALLBACK (add_btn_clicked_cb), mpage); + gtk_widget_show (btn); + gtk_box_pack_start (GTK_BOX (priv->main), btn, FALSE, FALSE, 6); - filename = g_strdup_printf ("%s/config/et-header-meeting-page", evolution_dir); - priv->etable = e_meeting_model_etable_from_model (priv->model, - EVOLUTION_ETSPECDIR "/meeting-page.etspec", - filename); - g_free (filename); + priv->list_view = e_meeting_list_view_new (priv->model); - real_table = e_table_scrolled_get_table (priv->etable); +#if 0 + /* FIXME: handle context menu for treeview */ g_signal_connect((real_table), "right_click", G_CALLBACK (right_click_cb), mpage); +#endif - g_signal_connect((real_table->table_canvas), "focus_out_event", - G_CALLBACK (table_canvas_focus_out_cb), mpage); - - gtk_widget_show (GTK_WIDGET (priv->etable)); - gtk_box_pack_start (GTK_BOX (priv->main), GTK_WIDGET (priv->etable), TRUE, TRUE, 6); + gtk_widget_show (GTK_WIDGET (priv->list_view)); + sw = gtk_scrolled_window_new (NULL, NULL); + gtk_widget_show (sw); + gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (priv->list_view)); + gtk_box_pack_start (GTK_BOX (priv->main), sw, TRUE, TRUE, 6); /* Init the widget signals */ init_widgets (mpage); @@ -884,12 +790,12 @@ meeting_page_construct (MeetingPage *mpage, EMeetingModel *emm, * not be created. **/ MeetingPage * -meeting_page_new (EMeetingModel *emm, CalClient *client) +meeting_page_new (EMeetingStore *ems, CalClient *client) { MeetingPage *mpage; mpage = g_object_new (TYPE_MEETING_PAGE, NULL); - if (!meeting_page_construct (mpage, emm, client)) { + if (!meeting_page_construct (mpage, ems, client)) { g_object_unref((mpage)); return NULL; } diff --git a/calendar/gui/dialogs/meeting-page.etspec b/calendar/gui/dialogs/meeting-page.etspec deleted file mode 100644 index 96bc480fe9..0000000000 --- a/calendar/gui/dialogs/meeting-page.etspec +++ /dev/null @@ -1,21 +0,0 @@ -<ETableSpecification click-to-add="true" click-to-add-end="true" _click-to-add-message="Click here to add an attendee" draw-grid="true"> - <ETableColumn model_col= "0" _title="Attendee" expansion="2.0" minimum_width="10" resizable="true" cell="string" compare="string"/> - <ETableColumn model_col= "1" _title="Member" expansion="2.0" minimum_width="10" resizable="true" cell="string" compare="string"/> - <ETableColumn model_col= "2" _title="Type" expansion="1.0" minimum_width="10" resizable="true" cell="typeedit" compare="string"/> - <ETableColumn model_col= "3" _title="Role" expansion="1.0" minimum_width="10" resizable="true" cell="roleedit" compare="string"/> - <ETableColumn model_col= "4" _title="RSVP" expansion="1.0" minimum_width="10" resizable="true" cell="rsvpedit" compare="string"/> - <ETableColumn model_col= "5" _title="Delegated To" expansion="2.0" minimum_width="10" resizable="true" cell="string" compare="string"/> - <ETableColumn model_col= "6" _title="Delegated From" expansion="2.0" minimum_width="10" resizable="true" cell="string" compare="string"/> - <ETableColumn model_col= "7" _title="Status" expansion="1.0" minimum_width="10" resizable="true" cell="statusedit" compare="string"/> - <ETableColumn model_col= "8" _title="Common Name" expansion="2.0" minimum_width="10" resizable="true" cell="string" compare="string"/> - <ETableColumn model_col= "9" _title="Language" expansion="2.0" minimum_width="10" resizable="true" cell="string" compare="string"/> - - <ETableState> - <column source="0"/> - <column source="2"/> - <column source="3"/> - <column source="4"/> - <column source="7"/> - <grouping></grouping> - </ETableState> -</ETableSpecification> diff --git a/calendar/gui/dialogs/meeting-page.h b/calendar/gui/dialogs/meeting-page.h index 2a209b586b..5891189810 100644 --- a/calendar/gui/dialogs/meeting-page.h +++ b/calendar/gui/dialogs/meeting-page.h @@ -24,7 +24,7 @@ #ifndef MEETING_PAGE_H #define MEETING_PAGE_H -#include "../e-meeting-model.h" +#include "../e-meeting-store.h" #include "../itip-utils.h" #include "comp-editor-page.h" @@ -54,9 +54,9 @@ typedef struct { GtkType meeting_page_get_type (void); MeetingPage *meeting_page_construct (MeetingPage *mpage, - EMeetingModel *emm, + EMeetingStore *ems, CalClient *client); -MeetingPage *meeting_page_new (EMeetingModel *emm, +MeetingPage *meeting_page_new (EMeetingStore *ems, CalClient *client); CalComponent *meeting_page_get_cancel_comp (MeetingPage *mpage); diff --git a/calendar/gui/dialogs/schedule-page.c b/calendar/gui/dialogs/schedule-page.c index 2163d51f1e..8cad77360b 100644 --- a/calendar/gui/dialogs/schedule-page.c +++ b/calendar/gui/dialogs/schedule-page.c @@ -60,7 +60,7 @@ struct _SchedulePagePrivate { GtkWidget *main; /* Model */ - EMeetingModel *model; + EMeetingStore *model; /* Selector */ EMeetingTimeSelector *sel; @@ -164,7 +164,7 @@ schedule_page_finalize (GObject *object) priv->xml = NULL; } - g_object_unref((priv->model)); + g_object_unref(priv->model); g_free (priv); spage->priv = NULL; @@ -251,7 +251,7 @@ update_time (SchedulePage *spage, CalComponentDateTime *start_date, CalComponent if (start_zone != end_zone) { icaltimezone_convert_time (&end_tt, end_zone, start_zone); } - e_meeting_model_set_zone (priv->model, priv->zone); + e_meeting_store_set_zone (priv->model, priv->zone); all_day = (start_tt.is_date && end_tt.is_date) ? TRUE : FALSE; @@ -405,7 +405,7 @@ init_widgets (SchedulePage *spage) * be created. **/ SchedulePage * -schedule_page_construct (SchedulePage *spage, EMeetingModel *emm) +schedule_page_construct (SchedulePage *spage, EMeetingStore *ems) { SchedulePagePrivate *priv; @@ -426,11 +426,11 @@ schedule_page_construct (SchedulePage *spage, EMeetingModel *emm) } /* Model */ - g_object_ref((emm)); - priv->model = emm; + g_object_ref (ems); + priv->model = ems; /* Selector */ - priv->sel = E_MEETING_TIME_SELECTOR (e_meeting_time_selector_new (emm)); + priv->sel = E_MEETING_TIME_SELECTOR (e_meeting_time_selector_new (ems)); e_meeting_time_selector_set_working_hours (priv->sel, calendar_config_get_day_start_hour (), calendar_config_get_day_start_minute (), @@ -457,12 +457,12 @@ schedule_page_construct (SchedulePage *spage, EMeetingModel *emm) * not be created. **/ SchedulePage * -schedule_page_new (EMeetingModel *emm) +schedule_page_new (EMeetingStore *ems) { SchedulePage *spage; spage = g_object_new (TYPE_SCHEDULE_PAGE, NULL); - if (!schedule_page_construct (spage, emm)) { + if (!schedule_page_construct (spage, ems)) { g_object_unref((spage)); return NULL; } diff --git a/calendar/gui/dialogs/schedule-page.h b/calendar/gui/dialogs/schedule-page.h index 42745962cd..d4c7eafbdd 100644 --- a/calendar/gui/dialogs/schedule-page.h +++ b/calendar/gui/dialogs/schedule-page.h @@ -21,7 +21,7 @@ #ifndef SCHEDULE_PAGE_H #define SCHEDULE_PAGE_H -#include "../e-meeting-model.h" +#include "../e-meeting-store.h" #include "comp-editor-page.h" G_BEGIN_DECLS @@ -49,8 +49,8 @@ typedef struct { GtkType schedule_page_get_type (void); -SchedulePage *schedule_page_construct (SchedulePage *mpage, EMeetingModel *emm); -SchedulePage *schedule_page_new (EMeetingModel *emm); +SchedulePage *schedule_page_construct (SchedulePage *mpage, EMeetingStore *ems); +SchedulePage *schedule_page_new (EMeetingStore *ems); diff --git a/calendar/gui/dialogs/task-editor.c b/calendar/gui/dialogs/task-editor.c index eaf4c2eaca..4ef971fe85 100644 --- a/calendar/gui/dialogs/task-editor.c +++ b/calendar/gui/dialogs/task-editor.c @@ -26,6 +26,7 @@ #include <config.h> #include <string.h> #include <glade/glade.h> +#include <gal/util/e-util.h> #include <gal/widgets/e-unicode.h> #include <libgnome/gnome-i18n.h> @@ -40,7 +41,7 @@ struct _TaskEditorPrivate { TaskDetailsPage *task_details_page; MeetingPage *meet_page; - EMeetingModel *model; + EMeetingStore *model; gboolean assignment_shown; gboolean updating; @@ -60,8 +61,8 @@ static void refresh_task_cmd (GtkWidget *widget, gpointer data); static void cancel_task_cmd (GtkWidget *widget, gpointer data); static void forward_cmd (GtkWidget *widget, gpointer data); -static void model_row_changed_cb (ETableModel *etm, int row, gpointer data); -static void row_count_changed_cb (ETableModel *etm, int row, int count, gpointer data); +static void model_row_change_insert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); +static void model_row_delete_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data); static BonoboUIVerb verbs [] = { BONOBO_UI_UNSAFE_VERB ("ActionAssignTask", assign_task_cmd), @@ -154,12 +155,12 @@ init_widgets (TaskEditor *te) priv = te->priv; - g_signal_connect((priv->model), "model_row_changed", - G_CALLBACK (model_row_changed_cb), te); - g_signal_connect((priv->model), "model_rows_inserted", - G_CALLBACK (row_count_changed_cb), te); - g_signal_connect((priv->model), "model_rows_deleted", - G_CALLBACK (row_count_changed_cb), te); + g_signal_connect((priv->model), "row_changed", + G_CALLBACK (model_row_change_insert_cb), te); + g_signal_connect((priv->model), "row_inserted", + G_CALLBACK (model_row_change_insert_cb), te); + g_signal_connect((priv->model), "row_deleted", + G_CALLBACK (model_row_delete_cb), te); } /* Object initialization function for the task editor */ @@ -171,7 +172,7 @@ task_editor_init (TaskEditor *te) priv = g_new0 (TaskEditorPrivate, 1); te->priv = priv; - priv->model = E_MEETING_MODEL (e_meeting_model_new ()); + priv->model = E_MEETING_STORE (e_meeting_store_new ()); priv->assignment_shown = TRUE; priv->updating = FALSE; @@ -224,7 +225,7 @@ task_editor_set_cal_client (CompEditor *editor, CalClient *client) te = TASK_EDITOR (editor); priv = te->priv; - e_meeting_model_set_cal_client (priv->model, client); + e_meeting_store_set_cal_client (priv->model, client); if (parent_class->set_cal_client) parent_class->set_cal_client (editor, client); @@ -254,7 +255,7 @@ task_editor_edit_comp (CompEditor *editor, CalComponent *comp) cal_component_get_attendee_list (comp, &attendees); /* Clear things up */ - e_meeting_model_remove_all_attendees (priv->model); + e_meeting_store_remove_all_attendees (priv->model); if (attendees == NULL) { comp_editor_remove_page (editor, COMP_EDITOR_PAGE (priv->meet_page)); @@ -276,7 +277,7 @@ task_editor_edit_comp (CompEditor *editor, CalComponent *comp) /* If we aren't the organizer or the attendee is just delegating, don't allow editing */ if (!comp_editor_get_user_org (editor) || e_meeting_attendee_is_set_delto (ia)) e_meeting_attendee_set_edit_level (ia, E_MEETING_ATTENDEE_EDIT_NONE); - e_meeting_model_add_attendee (priv->model, ia); + e_meeting_store_add_attendee (priv->model, ia); g_object_unref(ia); } @@ -293,7 +294,7 @@ task_editor_edit_comp (CompEditor *editor, CalComponent *comp) account = (EAccount*)e_iterator_get(it); - ia = e_meeting_model_find_attendee (priv->model, account->id->address, &row); + ia = e_meeting_store_find_attendee (priv->model, account->id->address, &row); if (ia != NULL) e_meeting_attendee_set_edit_level (ia, E_MEETING_ATTENDEE_EDIT_STATUS); } @@ -301,7 +302,7 @@ task_editor_edit_comp (CompEditor *editor, CalComponent *comp) } else if (cal_client_get_organizer_must_attend (client)) { EMeetingAttendee *ia; - ia = e_meeting_model_find_attendee (priv->model, organizer.value, &row); + ia = e_meeting_store_find_attendee (priv->model, organizer.value, &row); if (ia != NULL) e_meeting_attendee_set_edit_level (ia, E_MEETING_ATTENDEE_EDIT_NONE); } @@ -335,7 +336,7 @@ task_editor_send_comp (CompEditor *editor, CalComponentItipMethod method) CalClient *client; gboolean result; - client = e_meeting_model_get_cal_client (priv->model); + client = e_meeting_store_get_cal_client (priv->model); result = itip_send_comp (CAL_COMPONENT_METHOD_CANCEL, comp, client, NULL); g_object_unref((comp)); @@ -464,29 +465,22 @@ forward_cmd (GtkWidget *widget, gpointer data) } static void -model_row_changed_cb (ETableModel *etm, int row, gpointer data) +model_changed (TaskEditor *te) { - TaskEditor *te = TASK_EDITOR (data); - TaskEditorPrivate *priv; - - priv = te->priv; - - if (!priv->updating) { + if (!te->priv->updating) { comp_editor_set_changed (COMP_EDITOR (te), TRUE); comp_editor_set_needs_send (COMP_EDITOR (te), TRUE); } } static void -row_count_changed_cb (ETableModel *etm, int row, int count, gpointer data) +model_row_change_insert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { - TaskEditor *te = TASK_EDITOR (data); - TaskEditorPrivate *priv; - - priv = te->priv; + model_changed (TASK_EDITOR (data)); +} - if (!priv->updating) { - comp_editor_set_changed (COMP_EDITOR (te), TRUE); - comp_editor_set_needs_send (COMP_EDITOR (te), TRUE); - } +static void +model_row_delete_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data) +{ + model_changed (TASK_EDITOR (data)); } diff --git a/calendar/gui/e-meeting-list-view.c b/calendar/gui/e-meeting-list-view.c new file mode 100644 index 0000000000..7d3a4ce6c6 --- /dev/null +++ b/calendar/gui/e-meeting-list-view.c @@ -0,0 +1,483 @@ +/* + * e-meeting-list-view.c + * + * Authors: Mike Kestner <mkestner@ximian.com> + * JP Rosevear <jpr@ximian.com> + * + * Copyright (C) 2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glib.h> +#include <gtk/gtktreemodel.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-widget.h> +#include <bonobo/bonobo-exception.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomevfs/gnome-vfs.h> +#include <ebook/e-book.h> +#include <ebook/e-book-util.h> +#include <ebook/e-card-types.h> +#include <ebook/e-card-cursor.h> +#include <ebook/e-card.h> +#include <ebook/e-card-simple.h> +#include <cal-util/cal-component.h> +#include <cal-util/cal-util.h> +#include <cal-util/timeutil.h> +#include "Evolution-Addressbook-SelectNames.h" +#include "calendar-config.h" +#include "e-meeting-list-view.h" +#include <misc/e-cell-renderer-combo.h> +#include "e-select-names-renderer.h" + +#define SELECT_NAMES_OAFID "OAFIID:GNOME_Evolution_Addressbook_SelectNames" + +struct _EMeetingListViewPrivate +{ + EMeetingStore *store; + + EBook *ebook; + gboolean book_loaded; + gboolean book_load_wait; + + GNOME_Evolution_Addressbook_SelectNames corba_select_names; +}; + +#define BUF_SIZE 1024 + +static char *sections[] = {N_("Chair Persons"), + N_("Required Participants"), + N_("Optional Participants"), + N_("Resources"), + NULL}; +static icalparameter_role roles[] = {ICAL_ROLE_CHAIR, + ICAL_ROLE_REQPARTICIPANT, + ICAL_ROLE_OPTPARTICIPANT, + ICAL_ROLE_NONPARTICIPANT, + ICAL_ROLE_NONE}; + +static GtkTreeViewClass *parent_class = NULL; + +static void +book_open_cb (EBook *book, EBookStatus status, gpointer data) +{ + EMeetingListView *view = E_MEETING_LIST_VIEW (data); + + if (status == E_BOOK_STATUS_SUCCESS) + view->priv->book_loaded = TRUE; + else + g_warning ("Book not loaded"); + + if (view->priv->book_load_wait) { + view->priv->book_load_wait = FALSE; + gtk_main_quit (); + } +} + +static void +start_addressbook_server (EMeetingListView *view) +{ + view->priv->ebook = e_book_new (); + e_book_load_default_book (view->priv->ebook, book_open_cb, view); +} + +static void +emlv_finalize (GObject *obj) +{ + EMeetingListView *view = E_MEETING_LIST_VIEW (obj); + EMeetingListViewPrivate *priv = view->priv; + + if (priv->ebook != NULL) + g_object_unref (priv->ebook); + + if (priv->corba_select_names != CORBA_OBJECT_NIL) { + CORBA_Environment ev; + CORBA_exception_init (&ev); + bonobo_object_release_unref (priv->corba_select_names, &ev); + CORBA_exception_free (&ev); + } + + g_free (priv); + + if (G_OBJECT_CLASS (parent_class)->finalize) + (* G_OBJECT_CLASS (parent_class)->finalize) (obj); +} + +static void +emlv_class_init (GObjectClass *klass) +{ + parent_class = g_type_class_peek_parent (klass); + + klass->finalize = emlv_finalize; +} + + +static void +emlv_init (EMeetingListView *view) +{ + EMeetingListViewPrivate *priv; + + priv = g_new0 (EMeetingListViewPrivate, 1); + + view->priv = priv; + + priv->corba_select_names = CORBA_OBJECT_NIL; + + start_addressbook_server (view); +} + +E_MAKE_TYPE (e_meeting_list_view, "EMeetingListView", EMeetingListView, emlv_class_init, emlv_init, GTK_TYPE_TREE_VIEW); +static GList * +get_type_strings () +{ + GList *strings = NULL; + + strings = g_list_append (strings, (char*) _("Individual")); + strings = g_list_append (strings, (char*) _("Group")); + strings = g_list_append (strings, (char*) _("Resource")); + strings = g_list_append (strings, (char*) _("Room")); + strings = g_list_append (strings, (char*) _("Unknown")); + + return strings; +} + +static GList * +get_role_strings () +{ + GList *strings = NULL; + + strings = g_list_append (strings, (char*) _("Chair")); + strings = g_list_append (strings, (char*) _("Required Participant")); + strings = g_list_append (strings, (char*) _("Optional Participant")); + strings = g_list_append (strings, (char*) _("Non-Participant")); + strings = g_list_append (strings, (char*) _("Unknown")); + + return strings; +} + +static GList * +get_rsvp_strings () +{ + GList *strings = NULL; + + strings = g_list_append (strings, (char*) _("Yes")); + strings = g_list_append (strings, (char*) _("No")); + + return strings; +} + +static GList * +get_status_strings () +{ + GList *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")); + strings = g_list_append (strings, (char*) _("Delegated")); + + return strings; +} + +static void +value_edited (GtkTreeView *view, gint col, const gchar *path, const gchar *text) +{ + EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view)); + GtkTreePath *treepath = gtk_tree_path_new_from_string (path); + int row = gtk_tree_path_get_indices (treepath)[0]; + + e_meeting_store_set_value (model, row, col, text); + gtk_tree_path_free (treepath); +} + +static void +attendee_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *address, const gchar *name, GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_ADDRESS_COL, path, address); + value_edited (view, E_MEETING_STORE_CN_COL, path, name); +} + +static void +type_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *text, GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_TYPE_COL, path, text); +} + +static void +role_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *text, GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_ROLE_COL, path, text); +} + +static void +rsvp_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *text, GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_RSVP_COL, path, text); +} + +static void +status_edited_cb (GtkCellRenderer *renderer, const gchar *path, const gchar *text, GtkTreeView *view) +{ + value_edited (view, E_MEETING_STORE_STATUS_COL, path, text); +} + +static void +build_table (GtkTreeView *view) +{ + GtkCellRenderer *renderer; + + gtk_tree_view_set_headers_visible (view, TRUE); + gtk_tree_view_set_rules_hint (view, TRUE); + + renderer = e_select_names_renderer_new (); + g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL); + gtk_tree_view_insert_column_with_attributes (view, -1, _("Attendee"), renderer, + "text", E_MEETING_STORE_ATTENDEE_COL, + "address", E_MEETING_STORE_ADDRESS_COL, + "underline", E_MEETING_STORE_ATTENDEE_UNDERLINE_COL, + NULL); + g_signal_connect (renderer, "cell_edited", G_CALLBACK (attendee_edited_cb), view); + + renderer = e_cell_renderer_combo_new (); + g_object_set (G_OBJECT (renderer), "list", get_type_strings (), "editable", TRUE, NULL); + gtk_tree_view_insert_column_with_attributes (view, -1, _("Type"), renderer, + "text", E_MEETING_STORE_TYPE_COL, + NULL); + g_signal_connect (renderer, "edited", G_CALLBACK (type_edited_cb), view); + + renderer = e_cell_renderer_combo_new (); + g_object_set (G_OBJECT (renderer), "list", get_role_strings (), "editable", TRUE, NULL); + gtk_tree_view_insert_column_with_attributes (view, -1, _("Role"), renderer, + "text", E_MEETING_STORE_ROLE_COL, + NULL); + g_signal_connect (renderer, "edited", G_CALLBACK (role_edited_cb), view); + + renderer = e_cell_renderer_combo_new (); + g_object_set (G_OBJECT (renderer), "list", get_rsvp_strings (), "editable", TRUE, NULL); + gtk_tree_view_insert_column_with_attributes (view, -1, _("RSVP"), renderer, + "text", E_MEETING_STORE_RSVP_COL, + NULL); + g_signal_connect (renderer, "edited", G_CALLBACK (rsvp_edited_cb), view); + + renderer = e_cell_renderer_combo_new (); + g_object_set (G_OBJECT (renderer), "list", get_status_strings (), "editable", TRUE, NULL); + gtk_tree_view_insert_column_with_attributes (view, -1, _("Status"), renderer, + "text", E_MEETING_STORE_STATUS_COL, + NULL); + g_signal_connect (renderer, "edited", G_CALLBACK (status_edited_cb), view); +} + +EMeetingListView * +e_meeting_list_view_new (EMeetingStore *store) +{ + EMeetingListView *view = g_object_new (E_TYPE_MEETING_LIST_VIEW, NULL); + + if (view) { + view->priv->store = store; + gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (store)); + build_table (GTK_TREE_VIEW (view)); + } + + return view; +} + +void +e_meeting_list_view_column_set_visible (EMeetingListView *view, const gchar *col_name, gboolean visible) +{ + GList *cols, *l; + + cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (view)); + + for (l = cols; l; l = l->next) { + GtkTreeViewColumn *col = (GtkTreeViewColumn *) l->data; + + if (strcmp (gtk_tree_view_column_get_title (col), col_name) == 0) { + gtk_tree_view_column_set_visible (col, visible); + break; + } + } +} + +static void +process_section (EMeetingListView *view, GNOME_Evolution_Addressbook_SimpleCardList *cards, icalparameter_role role) +{ + EMeetingListViewPrivate *priv; + int i; + + priv = view->priv; + for (i = 0; i < cards->_length; i++) { + const char *name, *attendee = NULL, *attr; + GNOME_Evolution_Addressbook_SimpleCard card; + CORBA_Environment ev; + + card = cards->_buffer[i]; + + CORBA_exception_init (&ev); + + /* Get the CN */ + name = GNOME_Evolution_Addressbook_SimpleCard_get (card, GNOME_Evolution_Addressbook_SimpleCard_FullName, &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + continue; + } + + /* Get the field as attendee from the backend */ + attr = cal_client_get_ldap_attribute (e_meeting_store_get_cal_client (priv->store)); + if (attr) { + /* FIXME this should be more general */ + if (!g_ascii_strcasecmp (attr, "icscalendar")) + attendee = GNOME_Evolution_Addressbook_SimpleCard_get (card, GNOME_Evolution_Addressbook_SimpleCard_Icscalendar, &ev); + } + + CORBA_exception_init (&ev); + + /* If we couldn't get the attendee prior, get the email address as the default */ + if (attendee == NULL || *attendee == '\0') { + attendee = GNOME_Evolution_Addressbook_SimpleCard_get (card, GNOME_Evolution_Addressbook_SimpleCard_Email, &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + continue; + } + } + + CORBA_exception_free (&ev); + + if (attendee == NULL || *attendee == '\0') + continue; + + if (e_meeting_store_find_attendee (priv->store, attendee, NULL) == NULL) { + EMeetingAttendee *ia = e_meeting_store_add_attendee_with_defaults (priv->store); + + e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", attendee)); + e_meeting_attendee_set_role (ia, role); + if (role == ICAL_ROLE_NONPARTICIPANT) + e_meeting_attendee_set_cutype (ia, ICAL_CUTYPE_RESOURCE); + e_meeting_attendee_set_cn (ia, g_strdup (name)); + } + } +} + +static void +select_names_ok_cb (BonoboListener *listener, const char *event_name, const CORBA_any *arg, CORBA_Environment *ev, gpointer data) +{ + EMeetingListView *view = E_MEETING_LIST_VIEW (data); + BonoboArg *card_arg; + int i; + + for (i = 0; sections[i] != NULL; i++) { + Bonobo_Control corba_control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection + (view->priv->corba_select_names, sections[i], ev); + GtkWidget *control_widget = bonobo_widget_new_control_from_objref (corba_control, CORBA_OBJECT_NIL); + BonoboControlFrame *control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (control_widget)); + Bonobo_PropertyBag pb = bonobo_control_frame_get_control_property_bag (control_frame, NULL); + card_arg = bonobo_property_bag_client_get_value_any (pb, "simple_card_list", NULL); + if (card_arg != NULL) { + GNOME_Evolution_Addressbook_SimpleCardList cards; + cards = BONOBO_ARG_GET_GENERAL (card_arg, TC_GNOME_Evolution_Addressbook_SimpleCardList, + GNOME_Evolution_Addressbook_SimpleCardList, NULL); + process_section (view, &cards, roles[i]); + bonobo_arg_release (card_arg); + } + } +} + +static void +add_section (GNOME_Evolution_Addressbook_SelectNames corba_select_names, const char *name) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Addressbook_SelectNames_addSection ( + corba_select_names, name, gettext (name), &ev); + + CORBA_exception_free (&ev); +} + +static gboolean +get_select_name_dialog (EMeetingListView *view) +{ + EMeetingListViewPrivate *priv; + CORBA_Environment ev; + int i; + + priv = view->priv; + + CORBA_exception_init (&ev); + + if (priv->corba_select_names != CORBA_OBJECT_NIL) { + int i; + + for (i = 0; sections[i] != NULL; i++) { + GtkWidget *control_widget; + Bonobo_Control corba_control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection + (priv->corba_select_names, sections[i], &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return FALSE; + } + + control_widget = bonobo_widget_new_control_from_objref (corba_control, CORBA_OBJECT_NIL); + + bonobo_widget_set_property (BONOBO_WIDGET (control_widget), "text", TC_CORBA_string, "", NULL); + } + CORBA_exception_free (&ev); + + return TRUE; + } + + priv->corba_select_names = bonobo_activation_activate_from_id (SELECT_NAMES_OAFID, 0, NULL, &ev); + + for (i = 0; sections[i] != NULL; i++) + add_section (priv->corba_select_names, sections[i]); + + bonobo_event_source_client_add_listener (priv->corba_select_names, + (BonoboListenerCallbackFn) select_names_ok_cb, + "GNOME/Evolution:ok:dialog", NULL, view); + + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return FALSE; + } + + CORBA_exception_free (&ev); + + return TRUE; +} + +void +e_meeting_list_view_invite_others_dialog (EMeetingListView *view) +{ + CORBA_Environment ev; + + if (!get_select_name_dialog (view)) + return; + + CORBA_exception_init (&ev); + + GNOME_Evolution_Addressbook_SelectNames_activateDialog ( + view->priv->corba_select_names, _("Required Participants"), &ev); + + CORBA_exception_free (&ev); +} + diff --git a/calendar/gui/e-meeting-list-view.h b/calendar/gui/e-meeting-list-view.h new file mode 100644 index 0000000000..dd539b9652 --- /dev/null +++ b/calendar/gui/e-meeting-list-view.h @@ -0,0 +1,62 @@ +/* + * e-meeting-list-view.h + * + * Author: Mike Kestner <mkestner@ximian.com> + * + * Copyright (C) 2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _E_MEETING_LIST_VIEW_H_ +#define _E_MEETING_LIST_VIEW_H_ + +#include <gtk/gtktreeview.h> +#include "e-meeting-store.h" + +G_BEGIN_DECLS + +#define E_TYPE_MEETING_LIST_VIEW (e_meeting_list_view_get_type ()) +#define E_MEETING_LIST_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_MEETING_LIST_VIEW, EMeetingListView)) +#define E_MEETING_LIST_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_MEETING_LIST_VIEW, EMeetingListViewClass)) +#define E_IS_MEETING_LIST_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_MEETING_LIST_VIEW)) +#define E_IS_MEETING_LIST_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_MEETING_LIST_VIEW)) + + +typedef struct _EMeetingListView EMeetingListView; +typedef struct _EMeetingListViewPrivate EMeetingListViewPrivate; +typedef struct _EMeetingListViewClass EMeetingListViewClass; + +struct _EMeetingListView { + GtkTreeView parent; + + EMeetingListViewPrivate *priv; +}; + +struct _EMeetingListViewClass { + GtkTreeViewClass parent_class; +}; + +GType e_meeting_list_view_get_type (void); + +EMeetingListView *e_meeting_list_view_new (EMeetingStore *store); + +void e_meeting_list_view_column_set_visible (EMeetingListView *emlv, const gchar *col_name, gboolean visible); + +void e_meeting_list_view_invite_others_dialog (EMeetingListView *emlv); + +G_END_DECLS + +#endif diff --git a/calendar/gui/e-meeting-model.c b/calendar/gui/e-meeting-model.c deleted file mode 100644 index a7cd54fd85..0000000000 --- a/calendar/gui/e-meeting-model.c +++ /dev/null @@ -1,1788 +0,0 @@ -/* -*- Mod:e C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* itip-model.c - * - * Copyright (C) 2001 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: JP Rosevear - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib.h> -#include <bonobo/bonobo-control.h> -#include <bonobo/bonobo-widget.h> -#include <bonobo/bonobo-exception.h> -#include <libgnome/gnome-i18n.h> -#include <libgnome/gnome-util.h> -#include <libgnomevfs/gnome-vfs.h> -#include <gal/e-table/e-table-without.h> -#include <gal/e-table/e-cell-text.h> -#include <gal/e-table/e-cell-popup.h> -#include <gal/e-table/e-cell-combo.h> -#include <ebook/e-book.h> -#include <ebook/e-book-util.h> -#include <ebook/e-card-types.h> -#include <ebook/e-card-cursor.h> -#include <ebook/e-card.h> -#include <ebook/e-card-simple.h> -#include <cal-util/cal-component.h> -#include <cal-util/cal-util.h> -#include <cal-util/timeutil.h> -#include "Evolution-Addressbook-SelectNames.h" -#include "calendar-config.h" -#include "itip-utils.h" -#include "e-meeting-utils.h" -#include "e-meeting-attendee.h" -#include "e-meeting-model.h" - -#define SELECT_NAMES_OAFID "OAFIID:GNOME_Evolution_Addressbook_SelectNames" - -struct _EMeetingModelPrivate -{ - GPtrArray *attendees; - - GList *tables; - - CalClient *client; - icaltimezone *zone; - - EBook *ebook; - gboolean book_loaded; - gboolean book_load_wait; - - GPtrArray *refresh_queue; - GHashTable *refresh_data; - guint refresh_idle_id; - - /* For invite others dialogs */ - GNOME_Evolution_Addressbook_SelectNames corba_select_names; -}; - -#define BUF_SIZE 1024 - -static char *sections[] = {N_("Chair Persons"), - N_("Required Participants"), - N_("Optional Participants"), - N_("Resources"), - NULL}; -static icalparameter_role roles[] = {ICAL_ROLE_CHAIR, - ICAL_ROLE_REQPARTICIPANT, - ICAL_ROLE_OPTPARTICIPANT, - ICAL_ROLE_NONPARTICIPANT, - ICAL_ROLE_NONE}; - -typedef struct _EMeetingModelQueueData EMeetingModelQueueData; -struct _EMeetingModelQueueData { - EMeetingModel *im; - EMeetingAttendee *ia; - - gboolean refreshing; - - EMeetingTime start; - EMeetingTime end; - - char buffer[BUF_SIZE]; - GString *string; - - GPtrArray *call_backs; - GPtrArray *data; -}; - - -static void class_init (EMeetingModelClass *klass); -static void init (EMeetingModel *model); -static void finalize (GObject *obj); - -static void refresh_queue_add (EMeetingModel *im, int row, - EMeetingTime *start, - EMeetingTime *end, - EMeetingModelRefreshCallback call_back, - gpointer data); -static void refresh_queue_remove (EMeetingModel *im, - EMeetingAttendee *ia); -static gboolean refresh_busy_periods (gpointer data); - -static void attendee_changed_cb (EMeetingAttendee *ia, gpointer data); -static void select_names_ok_cb (BonoboListener *listener, - const char *event_name, - const CORBA_any *arg, - CORBA_Environment *ev, - gpointer data); - -static void table_destroy_state_cb (ETableScrolled *etable, gpointer data); -static void table_destroy_list_cb (ETableScrolled *etable, gpointer data); - -static ETableModelClass *parent_class = NULL; - -E_MAKE_TYPE (e_meeting_model, "EMeetingModel", EMeetingModel, - class_init, init, E_TABLE_MODEL_TYPE); - -static void -book_open_cb (EBook *book, EBookStatus status, gpointer data) -{ - EMeetingModel *im = E_MEETING_MODEL (data); - EMeetingModelPrivate *priv; - - priv = im->priv; - - if (status == E_BOOK_STATUS_SUCCESS) - priv->book_loaded = TRUE; - else - g_warning ("Book not loaded"); - - if (priv->book_load_wait) { - priv->book_load_wait = FALSE; - gtk_main_quit (); - } -} - -static void -start_addressbook_server (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - - priv = im->priv; - - priv->ebook = e_book_new (); - e_book_load_default_book (priv->ebook, book_open_cb, im); -} - -static EMeetingAttendee * -find_match (EMeetingModel *im, const char *address, int *pos) -{ - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - const gchar *ia_address; - int i; - - priv = im->priv; - - if (address == NULL) - return NULL; - - /* Make sure we can add the new delegatee person */ - for (i = 0; i < priv->attendees->len; i++) { - ia = g_ptr_array_index (priv->attendees, i); - - ia_address = e_meeting_attendee_get_address (ia); - if (ia_address != NULL && !g_strcasecmp (itip_strip_mailto (ia_address), itip_strip_mailto (address))) { - if (pos != NULL) - *pos = i; - return ia; - } - } - - return NULL; -} - -static icalparameter_cutype -text_to_type (const char *type) -{ - if (!g_strcasecmp (type, _("Individual"))) - return ICAL_CUTYPE_INDIVIDUAL; - else if (!g_strcasecmp (type, _("Group"))) - return ICAL_CUTYPE_GROUP; - else if (!g_strcasecmp (type, _("Resource"))) - return ICAL_CUTYPE_RESOURCE; - else if (!g_strcasecmp (type, _("Room"))) - return ICAL_CUTYPE_ROOM; - else - return ICAL_CUTYPE_NONE; -} - -static char * -type_to_text (icalparameter_cutype type) -{ - switch (type) { - case ICAL_CUTYPE_INDIVIDUAL: - return _("Individual"); - case ICAL_CUTYPE_GROUP: - return _("Group"); - case ICAL_CUTYPE_RESOURCE: - return _("Resource"); - case ICAL_CUTYPE_ROOM: - return _("Room"); - default: - return _("Unknown"); - } - - return NULL; - -} - -static icalparameter_role -text_to_role (const char *role) -{ - if (!g_strcasecmp (role, _("Chair"))) - return ICAL_ROLE_CHAIR; - else if (!g_strcasecmp (role, _("Required Participant"))) - return ICAL_ROLE_REQPARTICIPANT; - else if (!g_strcasecmp (role, _("Optional Participant"))) - return ICAL_ROLE_OPTPARTICIPANT; - else if (!g_strcasecmp (role, _("Non-Participant"))) - return ICAL_ROLE_NONPARTICIPANT; - else - return ICAL_ROLE_NONE; -} - -static char * -role_to_text (icalparameter_role role) -{ - switch (role) { - case ICAL_ROLE_CHAIR: - return _("Chair"); - case ICAL_ROLE_REQPARTICIPANT: - return _("Required Participant"); - case ICAL_ROLE_OPTPARTICIPANT: - return _("Optional Participant"); - case ICAL_ROLE_NONPARTICIPANT: - return _("Non-Participant"); - default: - return _("Unknown"); - } - - return NULL; -} - -static gboolean -text_to_boolean (const char *role) -{ - if (!g_strcasecmp (role, _("Yes"))) - return TRUE; - else - return FALSE; -} - -static char * -boolean_to_text (gboolean b) -{ - if (b) - return _("Yes"); - else - return _("No"); -} - -static icalparameter_partstat -text_to_partstat (const char *partstat) -{ - if (!g_strcasecmp (partstat, _("Needs Action"))) - return ICAL_PARTSTAT_NEEDSACTION; - else if (!g_strcasecmp (partstat, _("Accepted"))) - return ICAL_PARTSTAT_ACCEPTED; - else if (!g_strcasecmp (partstat, _("Declined"))) - return ICAL_PARTSTAT_DECLINED; - else if (!g_strcasecmp (partstat, _("Tentative"))) - return ICAL_PARTSTAT_TENTATIVE; - else if (!g_strcasecmp (partstat, _("Delegated"))) - return ICAL_PARTSTAT_DELEGATED; - else if (!g_strcasecmp (partstat, _("Completed"))) - return ICAL_PARTSTAT_COMPLETED; - else if (!g_strcasecmp (partstat, _("In Process"))) - return ICAL_PARTSTAT_INPROCESS; - else - return ICAL_PARTSTAT_NONE; -} - -static char * -partstat_to_text (icalparameter_partstat partstat) -{ - switch (partstat) { - case ICAL_PARTSTAT_NEEDSACTION: - return _("Needs Action"); - case ICAL_PARTSTAT_ACCEPTED: - return _("Accepted"); - case ICAL_PARTSTAT_DECLINED: - return _("Declined"); - case ICAL_PARTSTAT_TENTATIVE: - return _("Tentative"); - case ICAL_PARTSTAT_DELEGATED: - return _("Delegated"); - case ICAL_PARTSTAT_COMPLETED: - return _("Completed"); - case ICAL_PARTSTAT_INPROCESS: - return _("In Process"); - case ICAL_PARTSTAT_NONE: - default: - return _("Unknown"); - } - - return NULL; -} - -static int -column_count (ETableModel *etm) -{ - return E_MEETING_MODEL_COLUMN_COUNT; -} - -static int -row_count (ETableModel *etm) -{ - EMeetingModel *im; - EMeetingModelPrivate *priv; - - im = E_MEETING_MODEL (etm); - priv = im->priv; - - return (priv->attendees->len); -} - -static void -append_row (ETableModel *etm, ETableModel *source, int row) -{ - EMeetingModel *im; - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - char *address; - - im = E_MEETING_MODEL (etm); - priv = im->priv; - - address = (char *) e_table_model_value_at (source, E_MEETING_MODEL_ADDRESS_COL, row); - if (find_match (im, address, NULL) != NULL) { - return; - } - - ia = E_MEETING_ATTENDEE (e_meeting_attendee_new ()); - - e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", address)); - e_meeting_attendee_set_member (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_MEMBER_COL, row))); - e_meeting_attendee_set_cutype (ia, text_to_type (e_table_model_value_at (source, E_MEETING_MODEL_TYPE_COL, row))); - e_meeting_attendee_set_role (ia, text_to_role (e_table_model_value_at (source, E_MEETING_MODEL_ROLE_COL, row))); - e_meeting_attendee_set_rsvp (ia, text_to_boolean (e_table_model_value_at (source, E_MEETING_MODEL_RSVP_COL, row))); - e_meeting_attendee_set_delto (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_DELTO_COL, row))); - e_meeting_attendee_set_delfrom (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_DELFROM_COL, row))); - e_meeting_attendee_set_status (ia, text_to_partstat (e_table_model_value_at (source, E_MEETING_MODEL_STATUS_COL, row))); - e_meeting_attendee_set_cn (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_CN_COL, row))); - e_meeting_attendee_set_language (ia, g_strdup (e_table_model_value_at (source, E_MEETING_MODEL_LANGUAGE_COL, row))); - - e_meeting_model_add_attendee (E_MEETING_MODEL (etm), ia); - g_object_unref (ia); -} - -static void * -value_at (ETableModel *etm, int col, int row) -{ - EMeetingModel *im; - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - - im = E_MEETING_MODEL (etm); - priv = im->priv; - - ia = g_ptr_array_index (priv->attendees, row); - - switch (col) { - case E_MEETING_MODEL_ADDRESS_COL: - return (void *)itip_strip_mailto (e_meeting_attendee_get_address (ia)); - case E_MEETING_MODEL_MEMBER_COL: - return (void *)e_meeting_attendee_get_member (ia); - case E_MEETING_MODEL_TYPE_COL: - return type_to_text (e_meeting_attendee_get_cutype (ia)); - case E_MEETING_MODEL_ROLE_COL: - return role_to_text (e_meeting_attendee_get_role (ia)); - case E_MEETING_MODEL_RSVP_COL: - return boolean_to_text (e_meeting_attendee_get_rsvp (ia)); - case E_MEETING_MODEL_DELTO_COL: - return (void *)itip_strip_mailto (e_meeting_attendee_get_delto (ia)); - case E_MEETING_MODEL_DELFROM_COL: - return (void *)itip_strip_mailto (e_meeting_attendee_get_delfrom (ia)); - case E_MEETING_MODEL_STATUS_COL: - return partstat_to_text (e_meeting_attendee_get_status (ia)); - case E_MEETING_MODEL_CN_COL: - return (void *)e_meeting_attendee_get_cn (ia); - case E_MEETING_MODEL_LANGUAGE_COL: - return (void *)e_meeting_attendee_get_language (ia); - } - - return NULL; -} - -static void -set_value_at (ETableModel *etm, int col, int row, const void *val) -{ - EMeetingModel *im; - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - icalparameter_cutype type; - - im = E_MEETING_MODEL (etm); - priv = im->priv; - - ia = g_ptr_array_index (priv->attendees, row); - - e_table_model_pre_change (etm); - - switch (col) { - case E_MEETING_MODEL_ADDRESS_COL: - if (val != NULL && *((char *)val)) - e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", (char *) val)); - break; - case E_MEETING_MODEL_MEMBER_COL: - e_meeting_attendee_set_member (ia, g_strdup (val)); - break; - case E_MEETING_MODEL_TYPE_COL: - type = text_to_type (val); - e_meeting_attendee_set_cutype (ia, text_to_type (val)); - if (type == ICAL_CUTYPE_RESOURCE) { - e_meeting_attendee_set_role (ia, ICAL_ROLE_NONPARTICIPANT); - e_table_model_cell_changed (etm, E_MEETING_MODEL_ROLE_COL, row); - } - break; - case E_MEETING_MODEL_ROLE_COL: - e_meeting_attendee_set_role (ia, text_to_role (val)); - break; - case E_MEETING_MODEL_RSVP_COL: - e_meeting_attendee_set_rsvp (ia, text_to_boolean (val)); - break; - case E_MEETING_MODEL_DELTO_COL: - e_meeting_attendee_set_delto (ia, g_strdup (val)); - break; - case E_MEETING_MODEL_DELFROM_COL: - e_meeting_attendee_set_delfrom (ia, g_strdup (val)); - break; - case E_MEETING_MODEL_STATUS_COL: - e_meeting_attendee_set_status (ia, text_to_partstat (val)); - break; - case E_MEETING_MODEL_CN_COL: - e_meeting_attendee_set_cn (ia, g_strdup (val)); - break; - case E_MEETING_MODEL_LANGUAGE_COL: - e_meeting_attendee_set_language (ia, g_strdup (val)); - break; - } - - e_table_model_cell_changed (etm, col, row); -} - -static gboolean -is_cell_editable (ETableModel *etm, int col, int row) -{ - EMeetingModel *im; - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - EMeetingAttendeeEditLevel level; - - im = E_MEETING_MODEL (etm); - priv = im->priv; - - if (col == E_MEETING_MODEL_DELTO_COL - || col == E_MEETING_MODEL_DELFROM_COL) - return FALSE; - - if (row == -1) - return TRUE; - if (row >= priv->attendees->len) - return TRUE; - - ia = g_ptr_array_index (priv->attendees, row); - level = e_meeting_attendee_get_edit_level (ia); - - switch (level) { - case E_MEETING_ATTENDEE_EDIT_FULL: - return TRUE; - case E_MEETING_ATTENDEE_EDIT_STATUS: - return col == E_MEETING_MODEL_STATUS_COL; - case E_MEETING_ATTENDEE_EDIT_NONE: - return FALSE; - } - - return TRUE; -} - -static void * -duplicate_value (ETableModel *etm, int col, const void *val) -{ - return g_strdup (val); -} - -static void -free_value (ETableModel *etm, int col, void *val) -{ - g_free (val); -} - -static void * -init_value (ETableModel *etm, int col) -{ - switch (col) { - case E_MEETING_MODEL_ADDRESS_COL: - return g_strdup (""); - case E_MEETING_MODEL_MEMBER_COL: - return g_strdup (""); - case E_MEETING_MODEL_TYPE_COL: - return g_strdup (_("Individual")); - case E_MEETING_MODEL_ROLE_COL: - return g_strdup (_("Required Participant")); - case E_MEETING_MODEL_RSVP_COL: - return g_strdup (_("Yes")); - case E_MEETING_MODEL_DELTO_COL: - return g_strdup (""); - case E_MEETING_MODEL_DELFROM_COL: - return g_strdup (""); - case E_MEETING_MODEL_STATUS_COL: - return g_strdup (_("Needs Action")); - case E_MEETING_MODEL_CN_COL: - return g_strdup (""); - case E_MEETING_MODEL_LANGUAGE_COL: - return g_strdup ("en"); - } - - return g_strdup (""); -} - -static gboolean -value_is_empty (ETableModel *etm, int col, const void *val) -{ - - switch (col) { - case E_MEETING_MODEL_ADDRESS_COL: - case E_MEETING_MODEL_MEMBER_COL: - case E_MEETING_MODEL_DELTO_COL: - case E_MEETING_MODEL_DELFROM_COL: - case E_MEETING_MODEL_CN_COL: - if (val && !g_strcasecmp (val, "")) - return TRUE; - else - return FALSE; - default: - ; - } - - return TRUE; -} - -static char * -value_to_string (ETableModel *etm, int col, const void *val) -{ - return g_strdup (val); -} - -static void -class_init (EMeetingModelClass *klass) -{ - GObjectClass *gobject_class; - ETableModelClass *etm_class; - - gobject_class = G_OBJECT_CLASS (klass); - etm_class = E_TABLE_MODEL_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = finalize; - - etm_class->column_count = column_count; - etm_class->row_count = row_count; - etm_class->value_at = value_at; - etm_class->set_value_at = set_value_at; - etm_class->is_cell_editable = is_cell_editable; - etm_class->append_row = append_row; - etm_class->duplicate_value = duplicate_value; - etm_class->free_value = free_value; - etm_class->initialize_value = init_value; - etm_class->value_is_empty = value_is_empty; - etm_class->value_to_string = value_to_string; -} - - -static void -init (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - - priv = g_new0 (EMeetingModelPrivate, 1); - - im->priv = priv; - - priv->attendees = g_ptr_array_new (); - - priv->tables = NULL; - - priv->client = NULL; - priv->zone = icaltimezone_get_builtin_timezone (calendar_config_get_timezone ()); - - priv->ebook = NULL; - priv->book_loaded = FALSE; - priv->book_load_wait = FALSE; - - priv->refresh_queue = g_ptr_array_new (); - priv->refresh_data = g_hash_table_new (g_direct_hash, g_direct_equal); - priv->refresh_idle_id = 0; - - priv->corba_select_names = CORBA_OBJECT_NIL; - - start_addressbook_server (im); -} - -static void -finalize (GObject *obj) -{ - EMeetingModel *im = E_MEETING_MODEL (obj); - EMeetingModelPrivate *priv; - GList *l; - int i; - - priv = im->priv; - - for (i = 0; i < priv->attendees->len; i++) - g_object_unref (g_ptr_array_index (priv->attendees, i)); - g_ptr_array_free (priv->attendees, TRUE); - - for (l = priv->tables; l != NULL; l = l->next) - g_signal_handlers_disconnect_matched (G_OBJECT (l->data), G_SIGNAL_MATCH_DATA, - 0, 0, NULL, NULL, im); - g_list_free (priv->tables); - - if (priv->client != NULL) - g_object_unref (priv->client); - - if (priv->ebook != NULL) - g_object_unref (priv->ebook); - - if (priv->corba_select_names != CORBA_OBJECT_NIL) { - CORBA_Environment ev; - CORBA_exception_init (&ev); - bonobo_object_release_unref (priv->corba_select_names, &ev); - CORBA_exception_free (&ev); - } - - while (priv->refresh_queue->len > 0) - refresh_queue_remove (im, g_ptr_array_index (priv->refresh_queue, 0)); - g_ptr_array_free (priv->refresh_queue, TRUE); - g_hash_table_destroy (priv->refresh_data); - - if (priv->refresh_idle_id) - g_source_remove (priv->refresh_idle_id); - - g_free (priv); - - if (G_OBJECT_CLASS (parent_class)->finalize) - (* G_OBJECT_CLASS (parent_class)->finalize) (obj); -} - -GtkObject * -e_meeting_model_new (void) -{ - return g_object_new (E_TYPE_MEETING_MODEL, NULL); -} - - -CalClient * -e_meeting_model_get_cal_client (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - - priv = im->priv; - - return priv->client; -} - -void -e_meeting_model_set_cal_client (EMeetingModel *im, CalClient *client) -{ - EMeetingModelPrivate *priv; - - priv = im->priv; - - if (priv->client != NULL) - g_object_unref (priv->client); - - if (client != NULL) - g_object_ref (client); - priv->client = client; -} - -icaltimezone * -e_meeting_model_get_zone (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - - g_return_val_if_fail (im != NULL, NULL); - g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); - - priv = im->priv; - - return priv->zone; -} - -void -e_meeting_model_set_zone (EMeetingModel *im, icaltimezone *zone) -{ - EMeetingModelPrivate *priv; - - g_return_if_fail (im != NULL); - g_return_if_fail (E_IS_MEETING_MODEL (im)); - - priv = im->priv; - - priv->zone = zone; -} - -static ETableScrolled * -build_etable (ETableModel *model, const gchar *spec_file, const gchar *state_file) -{ - GtkWidget *etable; - ETable *real_table; - ETableExtras *extras; - GList *strings; - ECell *popup_cell, *cell; - - extras = e_table_extras_new (); - - /* For type */ - cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); - popup_cell = e_cell_combo_new (); - e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); - g_object_unref (cell); - - strings = NULL; - strings = g_list_append (strings, (char*) _("Individual")); - strings = g_list_append (strings, (char*) _("Group")); - strings = g_list_append (strings, (char*) _("Resource")); - strings = g_list_append (strings, (char*) _("Room")); - strings = g_list_append (strings, (char*) _("Unknown")); - - e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); - e_table_extras_add_cell (extras, "typeedit", popup_cell); - - /* For role */ - cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); - popup_cell = e_cell_combo_new (); - e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); - g_object_unref (cell); - - strings = NULL; - strings = g_list_append (strings, (char*) _("Chair")); - strings = g_list_append (strings, (char*) _("Required Participant")); - strings = g_list_append (strings, (char*) _("Optional Participant")); - strings = g_list_append (strings, (char*) _("Non-Participant")); - strings = g_list_append (strings, (char*) _("Unknown")); - - e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); - e_table_extras_add_cell (extras, "roleedit", popup_cell); - - /* For rsvp */ - cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); - popup_cell = e_cell_combo_new (); - e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); - g_object_unref (cell); - - strings = NULL; - strings = g_list_append (strings, (char*) _("Yes")); - strings = g_list_append (strings, (char*) _("No")); - - e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); - e_table_extras_add_cell (extras, "rsvpedit", popup_cell); - - /* For status */ - cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); - popup_cell = e_cell_combo_new (); - e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell); - g_object_unref (cell); - - 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")); - strings = g_list_append (strings, (char*) _("Delegated")); - - e_cell_combo_set_popdown_strings (E_CELL_COMBO (popup_cell), strings); - e_table_extras_add_cell (extras, "statusedit", popup_cell); - - etable = e_table_scrolled_new_from_spec_file (model, extras, spec_file, NULL); - real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (etable)); - g_object_set (G_OBJECT (real_table), "uniform_row_height", TRUE, NULL); - e_table_load_state (real_table, state_file); - -#if 0 - g_signal_connect (real_table, "right_click", G_CALLBACK (right_click_cb), mpage); -#endif - - g_signal_connect (etable, "destroy", G_CALLBACK (table_destroy_state_cb), g_strdup (state_file)); - - g_object_unref (extras); - - return E_TABLE_SCROLLED (etable); -} - -void -e_meeting_model_add_attendee (EMeetingModel *im, EMeetingAttendee *ia) -{ - EMeetingModelPrivate *priv; - - priv = im->priv; - - e_table_model_pre_change (E_TABLE_MODEL (im)); - - g_object_ref (ia); - g_ptr_array_add (priv->attendees, ia); - - g_signal_connect (ia, "changed", G_CALLBACK (attendee_changed_cb), im); - - e_table_model_row_inserted (E_TABLE_MODEL (im), row_count (E_TABLE_MODEL (im)) - 1); -} - -EMeetingAttendee * -e_meeting_model_add_attendee_with_defaults (EMeetingModel *im) -{ - EMeetingAttendee *ia; - char *str; - - ia = E_MEETING_ATTENDEE (e_meeting_attendee_new ()); - - e_meeting_attendee_set_address (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_ADDRESS_COL)); - e_meeting_attendee_set_member (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_MEMBER_COL)); - - str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_TYPE_COL); - e_meeting_attendee_set_cutype (ia, text_to_type (str)); - g_free (str); - str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_ROLE_COL); - e_meeting_attendee_set_role (ia, text_to_role (str)); - g_free (str); - str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_RSVP_COL); - e_meeting_attendee_set_rsvp (ia, text_to_boolean (str)); - g_free (str); - - e_meeting_attendee_set_delto (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_DELTO_COL)); - e_meeting_attendee_set_delfrom (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_DELFROM_COL)); - - str = init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_STATUS_COL); - e_meeting_attendee_set_status (ia, text_to_partstat (str)); - g_free (str); - - e_meeting_attendee_set_cn (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_CN_COL)); - e_meeting_attendee_set_language (ia, init_value (E_TABLE_MODEL (im), E_MEETING_MODEL_LANGUAGE_COL)); - - e_meeting_model_add_attendee (im, ia); - - return ia; -} - -void -e_meeting_model_remove_attendee (EMeetingModel *im, EMeetingAttendee *ia) -{ - EMeetingModelPrivate *priv; - gint i, row = -1; - - priv = im->priv; - - for (i = 0; i < priv->attendees->len; i++) { - if (ia == g_ptr_array_index (priv->attendees, i)) { - row = i; - break; - } - } - - if (row != -1) { - e_table_model_pre_change (E_TABLE_MODEL (im)); - - g_ptr_array_remove_index (priv->attendees, row); - g_object_unref (ia); - - e_table_model_row_deleted (E_TABLE_MODEL (im), row); - } -} - -void -e_meeting_model_remove_all_attendees (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - gint i, len; - - priv = im->priv; - - e_table_model_pre_change (E_TABLE_MODEL (im)); - - len = priv->attendees->len; - - for (i = 0; i < len; i++) { - EMeetingAttendee *ia = g_ptr_array_index (priv->attendees, i); - g_object_unref (ia); - } - g_ptr_array_set_size (priv->attendees, 0); - - e_table_model_rows_deleted (E_TABLE_MODEL (im), 0, len); -} - -EMeetingAttendee * -e_meeting_model_find_attendee (EMeetingModel *im, const gchar *address, gint *row) -{ - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - int i; - - priv = im->priv; - - if (address == NULL) - return NULL; - - for (i = 0; i < priv->attendees->len; i++) { - const gchar *ia_address; - - ia = g_ptr_array_index (priv->attendees, i); - - ia_address = e_meeting_attendee_get_address (ia); - if (ia_address && !g_strcasecmp (itip_strip_mailto (ia_address), itip_strip_mailto (address))) { - if (row != NULL) - *row = i; - - return ia; - } - } - - return NULL; -} - -EMeetingAttendee * -e_meeting_model_find_attendee_at_row (EMeetingModel *im, gint row) -{ - EMeetingModelPrivate *priv; - - g_return_val_if_fail (im != NULL, NULL); - g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); - g_return_val_if_fail (row >= 0, NULL); - - priv = im->priv; - g_return_val_if_fail (row < priv->attendees->len, NULL); - - return g_ptr_array_index (priv->attendees, row); -} - -gint -e_meeting_model_count_actual_attendees (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - - priv = im->priv; - - return e_table_model_row_count (E_TABLE_MODEL (im)); -} - -const GPtrArray * -e_meeting_model_get_attendees (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - - priv = im->priv; - - return priv->attendees; -} - -static icaltimezone * -find_zone (icalproperty *ip, icalcomponent *tz_top_level) -{ - icalparameter *param; - icalcomponent *sub_comp; - const char *tzid; - icalcompiter iter; - - if (tz_top_level == NULL) - return NULL; - - param = icalproperty_get_first_parameter (ip, ICAL_TZID_PARAMETER); - if (param == NULL) - return NULL; - tzid = icalparameter_get_tzid (param); - - iter = icalcomponent_begin_component (tz_top_level, ICAL_VTIMEZONE_COMPONENT); - while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { - icalcomponent *clone; - const char *tz_tzid; - - tz_tzid = icalproperty_get_tzid (sub_comp); - if (!strcmp (tzid, tz_tzid)) { - icaltimezone *zone; - - zone = icaltimezone_new (); - clone = icalcomponent_new_clone (sub_comp); - icaltimezone_set_component (zone, clone); - - return zone; - } - - icalcompiter_next (&iter); - } - - return NULL; -} - - -static void -refresh_queue_add (EMeetingModel *im, int row, - EMeetingTime *start, - EMeetingTime *end, - EMeetingModelRefreshCallback call_back, - gpointer data) -{ - EMeetingModelPrivate *priv; - EMeetingAttendee *ia; - EMeetingModelQueueData *qdata; - - priv = im->priv; - - ia = g_ptr_array_index (priv->attendees, row); - if (ia == NULL) - return; - - qdata = g_hash_table_lookup (priv->refresh_data, ia); - if (qdata == NULL) { - qdata = g_new0 (EMeetingModelQueueData, 1); - - qdata->im = im; - qdata->ia = ia; - e_meeting_attendee_clear_busy_periods (ia); - e_meeting_attendee_set_has_calendar_info (ia, FALSE); - - qdata->start = *start; - qdata->end = *end; - qdata->string = g_string_new (NULL); - qdata->call_backs = g_ptr_array_new (); - qdata->data = g_ptr_array_new (); - g_ptr_array_add (qdata->call_backs, call_back); - g_ptr_array_add (qdata->data, data); - - g_hash_table_insert (priv->refresh_data, ia, qdata); - } else { - if (e_meeting_time_compare_times (start, &qdata->start) == -1) - qdata->start = *start; - if (e_meeting_time_compare_times (end, &qdata->end) == 1) - qdata->end = *end; - g_ptr_array_add (qdata->call_backs, call_back); - g_ptr_array_add (qdata->data, data); - } - - g_object_ref (ia); - g_ptr_array_add (priv->refresh_queue, ia); - - if (priv->refresh_idle_id == 0) - priv->refresh_idle_id = g_idle_add (refresh_busy_periods, im); -} - -static void -refresh_queue_remove (EMeetingModel *im, EMeetingAttendee *ia) -{ - EMeetingModelPrivate *priv; - EMeetingModelQueueData *qdata; - - priv = im->priv; - - /* Free the queue data */ - qdata = g_hash_table_lookup (priv->refresh_data, ia); - g_assert (qdata != NULL); - - g_hash_table_remove (priv->refresh_data, ia); - g_ptr_array_free (qdata->call_backs, TRUE); - g_ptr_array_free (qdata->data, TRUE); - g_free (qdata); - - /* Unref the attendee */ - g_ptr_array_remove (priv->refresh_queue, ia); - g_object_unref (ia); -} - -static void -process_callbacks (EMeetingModelQueueData *qdata) -{ - EMeetingModel *im; - int i; - - for (i = 0; i < qdata->call_backs->len; i++) { - EMeetingModelRefreshCallback call_back; - gpointer *data; - - call_back = g_ptr_array_index (qdata->call_backs, i); - data = g_ptr_array_index (qdata->data, i); - - call_back (data); - } - - im = qdata->im; - refresh_queue_remove (qdata->im, qdata->ia); - g_object_unref (im); -} - -static void -process_free_busy_comp (EMeetingAttendee *ia, - icalcomponent *fb_comp, - icaltimezone *zone, - icalcomponent *tz_top_level) -{ - icalproperty *ip; - - ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY); - if (ip != NULL) { - struct icaltimetype dtstart; - icaltimezone *ds_zone; - - dtstart = icalproperty_get_dtstart (ip); - if (!dtstart.is_utc) - ds_zone = find_zone (ip, tz_top_level); - else - ds_zone = icaltimezone_get_utc_timezone (); - icaltimezone_convert_time (&dtstart, ds_zone, zone); - e_meeting_attendee_set_start_busy_range (ia, - dtstart.year, - dtstart.month, - dtstart.day, - dtstart.hour, - dtstart.minute); - } - - ip = icalcomponent_get_first_property (fb_comp, ICAL_DTEND_PROPERTY); - if (ip != NULL) { - struct icaltimetype dtend; - icaltimezone *de_zone; - - dtend = icalproperty_get_dtend (ip); - if (!dtend.is_utc) - de_zone = find_zone (ip, tz_top_level); - else - de_zone = icaltimezone_get_utc_timezone (); - icaltimezone_convert_time (&dtend, de_zone, zone); - e_meeting_attendee_set_end_busy_range (ia, - dtend.year, - dtend.month, - dtend.day, - dtend.hour, - dtend.minute); - } - - ip = icalcomponent_get_first_property (fb_comp, ICAL_FREEBUSY_PROPERTY); - while (ip != NULL) { - icalparameter *param; - struct icalperiodtype fb; - EMeetingFreeBusyType busy_type = E_MEETING_FREE_BUSY_LAST; - icalparameter_fbtype fbtype = ICAL_FBTYPE_BUSY; - - fb = icalproperty_get_freebusy (ip); - param = icalproperty_get_first_parameter (ip, ICAL_FBTYPE_PARAMETER); - if (param != NULL) - fbtype = icalparameter_get_fbtype (param); - - switch (fbtype) { - case ICAL_FBTYPE_BUSY: - busy_type = E_MEETING_FREE_BUSY_BUSY; - break; - - case ICAL_FBTYPE_BUSYUNAVAILABLE: - busy_type = E_MEETING_FREE_BUSY_OUT_OF_OFFICE; - break; - - case ICAL_FBTYPE_BUSYTENTATIVE: - busy_type = E_MEETING_FREE_BUSY_TENTATIVE; - break; - - default: - break; - } - - if (busy_type != E_MEETING_FREE_BUSY_LAST) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - - icaltimezone_convert_time (&fb.start, utc_zone, zone); - icaltimezone_convert_time (&fb.end, utc_zone, zone); - e_meeting_attendee_add_busy_period (ia, - fb.start.year, - fb.start.month, - fb.start.day, - fb.start.hour, - fb.start.minute, - fb.end.year, - fb.end.month, - fb.end.day, - fb.end.hour, - fb.end.minute, - busy_type); - } - - ip = icalcomponent_get_next_property (fb_comp, ICAL_FREEBUSY_PROPERTY); - } -} - -static void -process_free_busy (EMeetingModelQueueData *qdata, char *text) -{ - EMeetingModel *im = qdata->im; - EMeetingModelPrivate *priv; - EMeetingAttendee *ia = qdata->ia; - icalcomponent *main_comp; - icalcomponent_kind kind = ICAL_NO_COMPONENT; - - priv = im->priv; - - main_comp = icalparser_parse_string (text); - if (main_comp == NULL) { - process_callbacks (qdata); - return; - } - - kind = icalcomponent_isa (main_comp); - if (kind == ICAL_VCALENDAR_COMPONENT) { - icalcompiter iter; - icalcomponent *tz_top_level, *sub_comp; - - tz_top_level = cal_util_new_top_level (); - - iter = icalcomponent_begin_component (main_comp, ICAL_VTIMEZONE_COMPONENT); - while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { - icalcomponent *clone; - - clone = icalcomponent_new_clone (sub_comp); - icalcomponent_add_component (tz_top_level, clone); - - icalcompiter_next (&iter); - } - - iter = icalcomponent_begin_component (main_comp, ICAL_VFREEBUSY_COMPONENT); - while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { - process_free_busy_comp (ia, sub_comp, priv->zone, tz_top_level); - - icalcompiter_next (&iter); - } - icalcomponent_free (tz_top_level); - } else if (kind == ICAL_VFREEBUSY_COMPONENT) { - process_free_busy_comp (ia, main_comp, priv->zone, NULL); - } - - icalcomponent_free (main_comp); - - process_callbacks (qdata); -} - -static void -async_close (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer data) -{ - EMeetingModelQueueData *qdata = data; - - process_free_busy (qdata, qdata->string->str); -} - -static void -async_read (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer buffer, - GnomeVFSFileSize requested, - GnomeVFSFileSize read, - gpointer data) -{ - EMeetingModelQueueData *qdata = data; - GnomeVFSFileSize buf_size = BUF_SIZE - 1; - - if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) { - gnome_vfs_async_close (handle, async_close, qdata); - return; - } - - ((char *)buffer)[read] = '\0'; - qdata->string = g_string_append (qdata->string, buffer); - - if (result == GNOME_VFS_ERROR_EOF) { - gnome_vfs_async_close (handle, async_close, qdata); - return; - } - - gnome_vfs_async_read (handle, qdata->buffer, buf_size, async_read, qdata); -} - -static void -async_open (GnomeVFSAsyncHandle *handle, - GnomeVFSResult result, - gpointer data) -{ - EMeetingModelQueueData *qdata = data; - GnomeVFSFileSize buf_size = BUF_SIZE - 1; - - if (result != GNOME_VFS_OK) { - gnome_vfs_async_close (handle, async_close, qdata); - return; - } - - gnome_vfs_async_read (handle, qdata->buffer, buf_size, async_read, qdata); -} - -static void -cursor_cb (EBook *book, EBookStatus status, ECardCursor *cursor, gpointer data) -{ - EMeetingModelQueueData *qdata = data; - int length, i; - - if (status != E_BOOK_STATUS_SUCCESS) - return; - - length = e_card_cursor_get_length (cursor); - for (i = 0; i < length; i ++) { - GnomeVFSAsyncHandle *handle; - ECard *card = e_card_cursor_get_nth (cursor, i); - const char *addr; - - if (card->fburl == NULL) - continue; - - addr = itip_strip_mailto (e_meeting_attendee_get_address (qdata->ia)); - if (!e_card_email_match_string (card, addr)) - continue; - - /* Read in free/busy data from the url */ - gnome_vfs_async_open (&handle, card->fburl, GNOME_VFS_OPEN_READ, - GNOME_VFS_PRIORITY_DEFAULT, async_open, qdata); - return; - } - - process_callbacks (qdata); -} - -static gboolean -refresh_busy_periods (gpointer data) -{ - EMeetingModel *im = E_MEETING_MODEL (data); - EMeetingModelPrivate *priv; - EMeetingAttendee *ia = NULL; - EMeetingModelQueueData *qdata = NULL; - char *query; - int i; - - priv = im->priv; - - /* Check to see if there are any remaining attendees in the queue */ - for (i = 0; i < priv->refresh_queue->len; i++) { - ia = g_ptr_array_index (priv->refresh_queue, i); - g_assert (ia != NULL); - - qdata = g_hash_table_lookup (priv->refresh_data, ia); - g_assert (qdata != NULL); - - if (!qdata->refreshing) - break; - } - - /* The everything in the queue is being refreshed */ - if (i >= priv->refresh_queue->len) { - priv->refresh_idle_id = 0; - return FALSE; - } - - /* Indicate we are trying to refresh it */ - qdata->refreshing = TRUE; - - /* We take a ref in case we get destroyed in the gui during a callback */ - g_object_ref (qdata->im); - - /* Check the server for free busy data */ - if (priv->client) { - GList *fb_data, *users = NULL; - struct icaltimetype itt; - time_t startt, endt; - const char *user; - - itt = icaltime_null_time (); - itt.year = g_date_year (&qdata->start.date); - itt.month = g_date_month (&qdata->start.date); - itt.day = g_date_day (&qdata->start.date); - itt.hour = qdata->start.hour; - itt.minute = qdata->start.minute; - startt = icaltime_as_timet_with_zone (itt, priv->zone); - - itt = icaltime_null_time (); - itt.year = g_date_year (&qdata->end.date); - itt.month = g_date_month (&qdata->end.date); - itt.day = g_date_day (&qdata->end.date); - itt.hour = qdata->end.hour; - itt.minute = qdata->end.minute; - endt = icaltime_as_timet_with_zone (itt, priv->zone); - - user = itip_strip_mailto (e_meeting_attendee_get_address (ia)); - users = g_list_append (users, g_strdup (user)); - fb_data = cal_client_get_free_busy (priv->client, users, startt, endt); - - g_list_foreach (users, (GFunc)g_free, NULL); - g_list_free (users); - - if (fb_data != NULL) { - CalComponent *comp = fb_data->data; - char *comp_str; - - comp_str = cal_component_get_as_string (comp); - process_free_busy (qdata, comp_str); - g_free (comp_str); - return TRUE; - } - } - - /* Look for fburl's of attendee with no free busy info on server */ - if (!priv->book_loaded) { - priv->book_load_wait = TRUE; - gtk_main (); - } - - if (!e_meeting_attendee_is_set_address (ia)) { - process_callbacks (qdata); - return TRUE; - } - - query = g_strdup_printf ("(contains \"email\" \"%s\")", - itip_strip_mailto (e_meeting_attendee_get_address (ia))); - if (!e_book_get_cursor (priv->ebook, query, cursor_cb, qdata)) - process_callbacks (qdata); - g_free (query); - - return TRUE; -} - -void -e_meeting_model_refresh_all_busy_periods (EMeetingModel *im, - EMeetingTime *start, - EMeetingTime *end, - EMeetingModelRefreshCallback call_back, - gpointer data) -{ - EMeetingModelPrivate *priv; - int i; - - g_return_if_fail (im != NULL); - g_return_if_fail (E_IS_MEETING_MODEL (im)); - - priv = im->priv; - - for (i = 0; i < priv->attendees->len; i++) - refresh_queue_add (im, i, start, end, call_back, data); -} - -void -e_meeting_model_refresh_busy_periods (EMeetingModel *im, - int row, - EMeetingTime *start, - EMeetingTime *end, - EMeetingModelRefreshCallback call_back, - gpointer data) -{ - EMeetingModelPrivate *priv; - - g_return_if_fail (im != NULL); - g_return_if_fail (E_IS_MEETING_MODEL (im)); - - priv = im->priv; - - refresh_queue_add (im, row, start, end, call_back, data); -} - -ETableScrolled * -e_meeting_model_etable_from_model (EMeetingModel *im, const gchar *spec_file, const gchar *state_file) -{ - EMeetingModelPrivate *priv; - ETableScrolled *ets; - - g_return_val_if_fail (im != NULL, NULL); - g_return_val_if_fail (E_IS_MEETING_MODEL (im), NULL); - - priv = im->priv; - - ets = build_etable (E_TABLE_MODEL (im), spec_file, state_file); - - priv->tables = g_list_prepend (priv->tables, ets); - - g_signal_connect (ets, "destroy", G_CALLBACK (table_destroy_list_cb), im); - - return ets; -} - -void -e_meeting_model_etable_click_to_add (EMeetingModel *im, gboolean click_to_add) -{ - EMeetingModelPrivate *priv; - GList *l; - - g_return_if_fail (im != NULL); - g_return_if_fail (E_IS_MEETING_MODEL (im)); - - priv = im->priv; - - for (l = priv->tables; l != NULL; l = l->next) { - ETableScrolled *ets; - ETable *real_table; - - ets = l->data; - real_table = e_table_scrolled_get_table (ets); - - g_object_set (G_OBJECT (real_table), "use_click_to_add", click_to_add, NULL); - } -} - -int -e_meeting_model_etable_model_to_view_row (ETable *et, EMeetingModel *im, int model_row) -{ - EMeetingModelPrivate *priv; - - g_return_val_if_fail (im != NULL, -1); - g_return_val_if_fail (E_IS_MEETING_MODEL (im), -1); - - priv = im->priv; - - return e_table_model_to_view_row (et, model_row); -} - -int -e_meeting_model_etable_view_to_model_row (ETable *et, EMeetingModel *im, int view_row) -{ - EMeetingModelPrivate *priv; - - g_return_val_if_fail (im != NULL, -1); - g_return_val_if_fail (E_IS_MEETING_MODEL (im), -1); - - priv = im->priv; - - return e_table_view_to_model_row (et, view_row); -} - - -static void -add_section (GNOME_Evolution_Addressbook_SelectNames corba_select_names, const char *name) -{ - CORBA_Environment ev; - - CORBA_exception_init (&ev); - - GNOME_Evolution_Addressbook_SelectNames_addSection (corba_select_names, - name, - gettext (name), - &ev); - - CORBA_exception_free (&ev); -} - -static gboolean -get_select_name_dialog (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - CORBA_Environment ev; - int i; - - priv = im->priv; - - if (priv->corba_select_names != CORBA_OBJECT_NIL) { - Bonobo_Control corba_control; - GtkWidget *control_widget; - int i; - - CORBA_exception_init (&ev); - for (i = 0; sections[i] != NULL; i++) { - corba_control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection - (priv->corba_select_names, sections[i], &ev); - if (BONOBO_EX (&ev)) { - CORBA_exception_free (&ev); - return FALSE; - } - - control_widget = bonobo_widget_new_control_from_objref (corba_control, CORBA_OBJECT_NIL); - - bonobo_widget_set_property (BONOBO_WIDGET (control_widget), "text", TC_CORBA_string, "", NULL); - } - CORBA_exception_free (&ev); - - return TRUE; - } - - CORBA_exception_init (&ev); - - priv->corba_select_names = bonobo_activation_activate_from_id (SELECT_NAMES_OAFID, 0, NULL, &ev); - - for (i = 0; sections[i] != NULL; i++) - add_section (priv->corba_select_names, sections[i]); - - bonobo_event_source_client_add_listener (priv->corba_select_names, - (BonoboListenerCallbackFn) select_names_ok_cb, - "GNOME/Evolution:ok:dialog", - NULL, im); - - if (BONOBO_EX (&ev)) { - CORBA_exception_free (&ev); - return FALSE; - } - - CORBA_exception_free (&ev); - - return TRUE; -} - -void -e_meeting_model_invite_others_dialog (EMeetingModel *im) -{ - EMeetingModelPrivate *priv; - CORBA_Environment ev; - - priv = im->priv; - - if (!get_select_name_dialog (im)) - return; - - CORBA_exception_init (&ev); - - GNOME_Evolution_Addressbook_SelectNames_activateDialog ( - priv->corba_select_names, _("Required Participants"), &ev); - - CORBA_exception_free (&ev); -} - -static void -process_section (EMeetingModel *im, GNOME_Evolution_Addressbook_SimpleCardList *cards, icalparameter_role role) -{ - EMeetingModelPrivate *priv; - int i; - - priv = im->priv; - for (i = 0; i < cards->_length; i++) { - EMeetingAttendee *ia; - const char *name, *attendee = NULL, *attr; - GNOME_Evolution_Addressbook_SimpleCard card; - CORBA_Environment ev; - - card = cards->_buffer[i]; - - CORBA_exception_init (&ev); - - /* Get the CN */ - name = GNOME_Evolution_Addressbook_SimpleCard_get (card, GNOME_Evolution_Addressbook_SimpleCard_FullName, &ev); - if (BONOBO_EX (&ev)) { - CORBA_exception_free (&ev); - continue; - } - - /* Get the field as attendee from the backend */ - attr = cal_client_get_ldap_attribute (priv->client); - if (attr) { - /* FIXME this should be more general */ - if (!strcmp (attr, "icscalendar")) - attendee = GNOME_Evolution_Addressbook_SimpleCard_get (card, GNOME_Evolution_Addressbook_SimpleCard_Icscalendar, &ev); - } - - CORBA_exception_init (&ev); - - /* If we couldn't get the attendee prior, get the email address as the default */ - if (attendee == NULL || *attendee == '\0') { - attendee = GNOME_Evolution_Addressbook_SimpleCard_get (card, GNOME_Evolution_Addressbook_SimpleCard_Email, &ev); - if (BONOBO_EX (&ev)) { - CORBA_exception_free (&ev); - continue; - } - } - - CORBA_exception_free (&ev); - - if (attendee == NULL || *attendee == '\0') - continue; - - if (e_meeting_model_find_attendee (im, attendee, NULL) == NULL) { - ia = e_meeting_model_add_attendee_with_defaults (im); - - e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", attendee)); - e_meeting_attendee_set_role (ia, role); - if (role == ICAL_ROLE_NONPARTICIPANT) - e_meeting_attendee_set_cutype (ia, ICAL_CUTYPE_RESOURCE); - e_meeting_attendee_set_cn (ia, g_strdup (name)); - } - } -} - -static void -select_names_ok_cb (BonoboListener *listener, - const char *event_name, - const CORBA_any *arg, - CORBA_Environment *ev, - gpointer data) -{ - EMeetingModel *im = data; - EMeetingModelPrivate *priv; - Bonobo_Control corba_control; - GtkWidget *control_widget; - BonoboControlFrame *control_frame; - Bonobo_PropertyBag pb; - BonoboArg *card_arg; - GNOME_Evolution_Addressbook_SimpleCardList cards; - int i; - - priv = im->priv; - - for (i = 0; sections[i] != NULL; i++) { - corba_control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection - (priv->corba_select_names, sections[i], ev); - control_widget = bonobo_widget_new_control_from_objref - (corba_control, CORBA_OBJECT_NIL); - - control_frame = bonobo_widget_get_control_frame (BONOBO_WIDGET (control_widget)); - pb = bonobo_control_frame_get_control_property_bag (control_frame, NULL); - card_arg = bonobo_property_bag_client_get_value_any (pb, "simple_card_list", NULL); - if (card_arg != NULL) { - cards = BONOBO_ARG_GET_GENERAL (card_arg, TC_GNOME_Evolution_Addressbook_SimpleCardList, GNOME_Evolution_Addressbook_SimpleCardList, NULL); - process_section (im, &cards, roles[i]); - bonobo_arg_release (card_arg); - } - } -} - -static void -attendee_changed_cb (EMeetingAttendee *ia, gpointer data) -{ - EMeetingModel *im = E_MEETING_MODEL (data); - EMeetingModelPrivate *priv; - gint row = -1, i; - - priv = im->priv; - - /* FIXME: Ideally I think you are supposed to call pre_change() before - the data structures are changed. */ - e_table_model_pre_change (E_TABLE_MODEL (im)); - - for (i = 0; i < priv->attendees->len; i++) { - if (ia == g_ptr_array_index (priv->attendees, i)) { - row = i; - break; - } - } - - if (row == -1) - e_table_model_no_change (E_TABLE_MODEL (im)); - else - e_table_model_row_changed (E_TABLE_MODEL (im), row); -} - -static void -table_destroy_state_cb (ETableScrolled *etable, gpointer data) -{ - ETable *real_table; - char *filename = data; - - real_table = e_table_scrolled_get_table (etable); - e_table_save_state (real_table, filename); - - g_free (data); -} - -static void -table_destroy_list_cb (ETableScrolled *etable, gpointer data) -{ - EMeetingModel *im = E_MEETING_MODEL (data); - EMeetingModelPrivate *priv; - - priv = im->priv; - - priv->tables = g_list_remove (priv->tables, etable); -} - diff --git a/calendar/gui/e-meeting-model.h b/calendar/gui/e-meeting-model.h deleted file mode 100644 index 50fba6c7a6..0000000000 --- a/calendar/gui/e-meeting-model.h +++ /dev/null @@ -1,116 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* e-model.h - * - * Copyright (C) 2001 Ximian, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: JP Rosevear - */ - -#ifndef _E_MEETING_MODEL_H_ -#define _E_MEETING_MODEL_H_ - -#include <gtk/gtk.h> -#include <gal/e-table/e-table-scrolled.h> -#include <gal/e-table/e-table-model.h> -#include <cal-client/cal-client.h> -#include "e-meeting-attendee.h" - -G_BEGIN_DECLS - -#define E_TYPE_MEETING_MODEL (e_meeting_model_get_type ()) -#define E_MEETING_MODEL(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MEETING_MODEL, EMeetingModel)) -#define E_MEETING_MODEL_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), E_TYPE_MEETING_MODEL, EMeetingModelClass)) -#define E_IS_MEETING_MODEL(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MEETING_MODEL)) -#define E_IS_MEETING_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((obj), E_TYPE_MEETING_MODEL)) - - -typedef struct _EMeetingModel EMeetingModel; -typedef struct _EMeetingModelPrivate EMeetingModelPrivate; -typedef struct _EMeetingModelClass EMeetingModelClass; - -typedef enum { - E_MEETING_MODEL_ADDRESS_COL, - E_MEETING_MODEL_MEMBER_COL, - E_MEETING_MODEL_TYPE_COL, - E_MEETING_MODEL_ROLE_COL, - E_MEETING_MODEL_RSVP_COL, - E_MEETING_MODEL_DELTO_COL, - E_MEETING_MODEL_DELFROM_COL, - E_MEETING_MODEL_STATUS_COL, - E_MEETING_MODEL_CN_COL, - E_MEETING_MODEL_LANGUAGE_COL, - E_MEETING_MODEL_COLUMN_COUNT -} EMeetingModelColumns; - -struct _EMeetingModel { - ETableModel parent; - - EMeetingModelPrivate *priv; -}; - -struct _EMeetingModelClass { - ETableModelClass parent_class; -}; - -typedef void (* EMeetingModelRefreshCallback) (gpointer data); - - -GtkType e_meeting_model_get_type (void); -GtkObject *e_meeting_model_new (void); - -CalClient *e_meeting_model_get_cal_client (EMeetingModel *im); -void e_meeting_model_set_cal_client (EMeetingModel *im, CalClient *client); - -icaltimezone *e_meeting_model_get_zone (EMeetingModel *im); -void e_meeting_model_set_zone (EMeetingModel *im, icaltimezone *zone); - -void e_meeting_model_add_attendee (EMeetingModel *im, EMeetingAttendee *ia); -EMeetingAttendee *e_meeting_model_add_attendee_with_defaults (EMeetingModel *im); - -void e_meeting_model_remove_attendee (EMeetingModel *im, EMeetingAttendee *ia); -void e_meeting_model_remove_all_attendees (EMeetingModel *im); - -EMeetingAttendee *e_meeting_model_find_attendee (EMeetingModel *im, const gchar *address, gint *row); -EMeetingAttendee *e_meeting_model_find_attendee_at_row (EMeetingModel *im, gint row); - -gint e_meeting_model_count_actual_attendees (EMeetingModel *im); -const GPtrArray *e_meeting_model_get_attendees (EMeetingModel *im); - -void e_meeting_model_refresh_all_busy_periods (EMeetingModel *im, - EMeetingTime *start, - EMeetingTime *end, - EMeetingModelRefreshCallback call_back, - gpointer data); -void e_meeting_model_refresh_busy_periods (EMeetingModel *im, - int row, - EMeetingTime *start, - EMeetingTime *end, - EMeetingModelRefreshCallback call_back, - gpointer data); - - -/* Helpful functions */ -ETableScrolled *e_meeting_model_etable_from_model (EMeetingModel *im, const gchar *spec_file, const gchar *state_file); -void e_meeting_model_etable_click_to_add (EMeetingModel *im, gboolean click_to_add); -int e_meeting_model_etable_model_to_view_row (ETable *et, EMeetingModel *im, int model_row); -int e_meeting_model_etable_view_to_model_row (ETable *et, EMeetingModel *im, int view_row); - -void e_meeting_model_invite_others_dialog (EMeetingModel *im); - -G_END_DECLS - -#endif diff --git a/calendar/gui/e-meeting-store.c b/calendar/gui/e-meeting-store.c new file mode 100644 index 0000000000..0129756e7c --- /dev/null +++ b/calendar/gui/e-meeting-store.c @@ -0,0 +1,1317 @@ +/* + * e-meeting-store.c + * + * Copyright (C) 2001-2003 Ximian, Inc. + * + * Authors: JP Rosevear <jpr@ximian.com> + * Mike Kestner <mkestner@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <glib.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomevfs/gnome-vfs.h> +#include <ebook/e-book.h> +#include <ebook/e-book-util.h> +#include <cal-util/cal-component.h> +#include <cal-util/cal-util.h> +#include <cal-util/timeutil.h> +#include "calendar-config.h" +#include "itip-utils.h" +#include "e-meeting-utils.h" +#include "e-meeting-attendee.h" +#include "e-meeting-store.h" + +#define ROW_VALID(store, row) (row >= 0 && row < store->priv->attendees->len) + +struct _EMeetingStorePrivate { + GPtrArray *attendees; + gint stamp; + + CalClient *client; + icaltimezone *zone; + + EBook *ebook; + gboolean book_loaded; + gboolean book_load_wait; + + GPtrArray *refresh_queue; + GHashTable *refresh_data; + guint refresh_idle_id; +}; + +#define BUF_SIZE 1024 + +typedef struct _EMeetingStoreQueueData EMeetingStoreQueueData; +struct _EMeetingStoreQueueData { + EMeetingStore *store; + EMeetingAttendee *attendee; + + gboolean refreshing; + + EMeetingTime start; + EMeetingTime end; + + char buffer[BUF_SIZE]; + GString *string; + + GPtrArray *call_backs; + GPtrArray *data; +}; + + +static GObjectClass *parent_class = NULL; + +static void +book_open_cb (EBook *book, EBookStatus status, gpointer data) +{ + EMeetingStore *store = E_MEETING_STORE (data); + + if (status == E_BOOK_STATUS_SUCCESS) + store->priv->book_loaded = TRUE; + else + g_warning ("Book not loaded"); + + if (store->priv->book_load_wait) { + store->priv->book_load_wait = FALSE; + gtk_main_quit (); + } +} + +static void +start_addressbook_server (EMeetingStore *store) +{ + store->priv->ebook = e_book_new (); + e_book_load_default_book (store->priv->ebook, book_open_cb, store); +} + +static icalparameter_cutype +text_to_type (const char *type) +{ + if (!g_strcasecmp (type, _("Individual"))) + return ICAL_CUTYPE_INDIVIDUAL; + else if (!g_strcasecmp (type, _("Group"))) + return ICAL_CUTYPE_GROUP; + else if (!g_strcasecmp (type, _("Resource"))) + return ICAL_CUTYPE_RESOURCE; + else if (!g_strcasecmp (type, _("Room"))) + return ICAL_CUTYPE_ROOM; + else + return ICAL_CUTYPE_NONE; +} + +static char * +type_to_text (icalparameter_cutype type) +{ + switch (type) { + case ICAL_CUTYPE_INDIVIDUAL: + return _("Individual"); + case ICAL_CUTYPE_GROUP: + return _("Group"); + case ICAL_CUTYPE_RESOURCE: + return _("Resource"); + case ICAL_CUTYPE_ROOM: + return _("Room"); + default: + return _("Unknown"); + } + + return NULL; + +} + +static icalparameter_role +text_to_role (const char *role) +{ + if (!g_strcasecmp (role, _("Chair"))) + return ICAL_ROLE_CHAIR; + else if (!g_strcasecmp (role, _("Required Participant"))) + return ICAL_ROLE_REQPARTICIPANT; + else if (!g_strcasecmp (role, _("Optional Participant"))) + return ICAL_ROLE_OPTPARTICIPANT; + else if (!g_strcasecmp (role, _("Non-Participant"))) + return ICAL_ROLE_NONPARTICIPANT; + else + return ICAL_ROLE_NONE; +} + +static char * +role_to_text (icalparameter_role role) +{ + switch (role) { + case ICAL_ROLE_CHAIR: + return _("Chair"); + case ICAL_ROLE_REQPARTICIPANT: + return _("Required Participant"); + case ICAL_ROLE_OPTPARTICIPANT: + return _("Optional Participant"); + case ICAL_ROLE_NONPARTICIPANT: + return _("Non-Participant"); + default: + return _("Unknown"); + } + + return NULL; +} + +static gboolean +text_to_boolean (const char *role) +{ + if (!g_strcasecmp (role, _("Yes"))) + return TRUE; + else + return FALSE; +} + +static char * +boolean_to_text (gboolean b) +{ + if (b) + return _("Yes"); + else + return _("No"); +} + +static icalparameter_partstat +text_to_partstat (const char *partstat) +{ + if (!g_strcasecmp (partstat, _("Needs Action"))) + return ICAL_PARTSTAT_NEEDSACTION; + else if (!g_strcasecmp (partstat, _("Accepted"))) + return ICAL_PARTSTAT_ACCEPTED; + else if (!g_strcasecmp (partstat, _("Declined"))) + return ICAL_PARTSTAT_DECLINED; + else if (!g_strcasecmp (partstat, _("Tentative"))) + return ICAL_PARTSTAT_TENTATIVE; + else if (!g_strcasecmp (partstat, _("Delegated"))) + return ICAL_PARTSTAT_DELEGATED; + else if (!g_strcasecmp (partstat, _("Completed"))) + return ICAL_PARTSTAT_COMPLETED; + else if (!g_strcasecmp (partstat, _("In Process"))) + return ICAL_PARTSTAT_INPROCESS; + else + return ICAL_PARTSTAT_NONE; +} + +static char * +partstat_to_text (icalparameter_partstat partstat) +{ + switch (partstat) { + case ICAL_PARTSTAT_NEEDSACTION: + return _("Needs Action"); + case ICAL_PARTSTAT_ACCEPTED: + return _("Accepted"); + case ICAL_PARTSTAT_DECLINED: + return _("Declined"); + case ICAL_PARTSTAT_TENTATIVE: + return _("Tentative"); + case ICAL_PARTSTAT_DELEGATED: + return _("Delegated"); + case ICAL_PARTSTAT_COMPLETED: + return _("Completed"); + case ICAL_PARTSTAT_INPROCESS: + return _("In Process"); + case ICAL_PARTSTAT_NONE: + default: + return _("Unknown"); + } + + return NULL; +} + +static GtkTreeModelFlags +get_flags (GtkTreeModel *model) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), 0); + + return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY; +} + +static int +get_n_columns (GtkTreeModel *model) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), 0); + + return E_MEETING_STORE_COLUMN_COUNT; +} + +static GType +get_column_type (GtkTreeModel *model, int col) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), G_TYPE_INVALID); + + switch (col) { + case E_MEETING_STORE_ADDRESS_COL: + case E_MEETING_STORE_MEMBER_COL: + case E_MEETING_STORE_TYPE_COL: + case E_MEETING_STORE_ROLE_COL: + case E_MEETING_STORE_RSVP_COL: + case E_MEETING_STORE_DELTO_COL: + case E_MEETING_STORE_DELFROM_COL: + case E_MEETING_STORE_STATUS_COL: + case E_MEETING_STORE_CN_COL: + case E_MEETING_STORE_LANGUAGE_COL: + case E_MEETING_STORE_ATTENDEE_COL: + return G_TYPE_STRING; + case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL: + return PANGO_TYPE_UNDERLINE; + default: + return G_TYPE_INVALID; + } +} + +static gboolean +get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *path) +{ + int row; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE); + + row = gtk_tree_path_get_indices (path) [0]; + + if (!ROW_VALID (E_MEETING_STORE (model), row)) + return FALSE; + + iter->stamp = E_MEETING_STORE (model)->priv->stamp; + iter->user_data = GINT_TO_POINTER (row); + + return TRUE; +} + +static GtkTreePath * +get_path (GtkTreeModel *model, GtkTreeIter *iter) +{ + int row; + GtkTreePath *result; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), NULL); + g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, NULL); + + row = GPOINTER_TO_INT (iter->user_data); + + g_return_val_if_fail (ROW_VALID (E_MEETING_STORE (model), row), NULL); + + result = gtk_tree_path_new (); + gtk_tree_path_append_index (result, row); + return result; +} + +static void +get_value (GtkTreeModel *model, GtkTreeIter *iter, int col, GValue *value) +{ + EMeetingStore *store; + EMeetingAttendee *attendee; + const gchar *cn; + int row; + + g_return_if_fail (E_IS_MEETING_STORE (model)); + g_return_if_fail (col >= 0 && col < E_MEETING_STORE_COLUMN_COUNT); + + row = GPOINTER_TO_INT (iter->user_data); + store = E_MEETING_STORE (model); + + g_return_if_fail (iter->stamp == store->priv->stamp); + g_return_if_fail (ROW_VALID (E_MEETING_STORE (model), row)); + + attendee = g_ptr_array_index (store->priv->attendees, row); + + switch (col) { + case E_MEETING_STORE_ADDRESS_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_address (attendee))); + break; + case E_MEETING_STORE_MEMBER_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, e_meeting_attendee_get_member (attendee)); + break; + case E_MEETING_STORE_TYPE_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, type_to_text (e_meeting_attendee_get_cutype (attendee))); + break; + case E_MEETING_STORE_ROLE_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, role_to_text (e_meeting_attendee_get_role (attendee))); + break; + case E_MEETING_STORE_RSVP_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, boolean_to_text (e_meeting_attendee_get_rsvp (attendee))); + break; + case E_MEETING_STORE_DELTO_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_delto (attendee))); + break; + case E_MEETING_STORE_DELFROM_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_delfrom (attendee))); + break; + case E_MEETING_STORE_STATUS_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, partstat_to_text (e_meeting_attendee_get_status (attendee))); + break; + case E_MEETING_STORE_CN_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, e_meeting_attendee_get_cn (attendee)); + break; + case E_MEETING_STORE_LANGUAGE_COL: + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, e_meeting_attendee_get_language (attendee)); + break; + case E_MEETING_STORE_ATTENDEE_COL: + g_value_init (value, G_TYPE_STRING); + cn = e_meeting_attendee_get_cn (attendee); + if (strcmp (cn, "")) + g_value_set_string (value, cn); + else + g_value_set_string (value, itip_strip_mailto (e_meeting_attendee_get_address (attendee))); + break; + case E_MEETING_STORE_ATTENDEE_UNDERLINE_COL: + cn = e_meeting_attendee_get_cn (attendee); + g_value_init (value, PANGO_TYPE_UNDERLINE); + g_value_set_enum (value, strcmp ("", cn) == 0 ? PANGO_UNDERLINE_NONE : PANGO_UNDERLINE_SINGLE); + } +} + +static gboolean +iter_next (GtkTreeModel *model, GtkTreeIter *iter) +{ + int row; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, FALSE); + + row = GPOINTER_TO_INT (iter->user_data) + 1; + iter->user_data = GINT_TO_POINTER (row); + + return ROW_VALID (E_MEETING_STORE (model), row); +} + +static gboolean +iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent) +{ + EMeetingStore *store; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + + store = E_MEETING_STORE (model); + + if (parent || store->priv->attendees->len <= 0) + return FALSE; + + iter->stamp = store->priv->stamp; + iter->user_data = GINT_TO_POINTER (0); + + return TRUE; +} + +static gboolean +iter_has_child (GtkTreeModel *model, GtkTreeIter *iter) +{ + return FALSE; +} + +static int +iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (model), -1); + + if (!iter) + return E_MEETING_STORE (model)->priv->attendees->len; + + g_return_val_if_fail (iter->stamp == E_MEETING_STORE (model)->priv->stamp, -1); + + return 0; +} + +static gboolean +iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent, int n) +{ + EMeetingStore *store; + + g_return_val_if_fail (E_IS_MEETING_STORE (model), FALSE); + + store = E_MEETING_STORE (model); + + if (parent || !ROW_VALID (store, n)) + return FALSE; + + iter->stamp = store->priv->stamp; + iter->user_data = GINT_TO_POINTER (n); + + return TRUE; +} + +static gboolean +iter_parent (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *child) +{ + return FALSE; +} + +static void +ems_tree_model_init (GtkTreeModelIface *iface) +{ + iface->get_flags = get_flags; + iface->get_n_columns = get_n_columns; + iface->get_column_type = get_column_type; + iface->get_iter = get_iter; + iface->get_path = get_path; + iface->get_value = get_value; + iface->iter_next = iter_next; + iface->iter_children = iter_children; + iface->iter_has_child = iter_has_child; + iface->iter_n_children = iter_n_children; + iface->iter_nth_child = iter_nth_child; + iface->iter_parent = iter_parent; +} + +void +e_meeting_store_set_value (EMeetingStore *store, int row, int col, const gchar *val) +{ + icalparameter_cutype type; + EMeetingAttendee *attendee = g_ptr_array_index (store->priv->attendees, row); + + switch (col) { + case E_MEETING_STORE_ADDRESS_COL: + if (val != NULL && *((char *)val)) + e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", (char *) val)); + break; + case E_MEETING_STORE_MEMBER_COL: + e_meeting_attendee_set_member (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_TYPE_COL: + type = text_to_type (val); + e_meeting_attendee_set_cutype (attendee, text_to_type (val)); + if (type == ICAL_CUTYPE_RESOURCE) { + e_meeting_attendee_set_role (attendee, ICAL_ROLE_NONPARTICIPANT); + } + break; + case E_MEETING_STORE_ROLE_COL: + e_meeting_attendee_set_role (attendee, text_to_role (val)); + break; + case E_MEETING_STORE_RSVP_COL: + e_meeting_attendee_set_rsvp (attendee, text_to_boolean (val)); + break; + case E_MEETING_STORE_DELTO_COL: + e_meeting_attendee_set_delto (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_DELFROM_COL: + e_meeting_attendee_set_delfrom (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_STATUS_COL: + e_meeting_attendee_set_status (attendee, text_to_partstat (val)); + break; + case E_MEETING_STORE_CN_COL: + e_meeting_attendee_set_cn (attendee, g_strdup (val)); + break; + case E_MEETING_STORE_LANGUAGE_COL: + e_meeting_attendee_set_language (attendee, g_strdup (val)); + break; + } +} + +static gboolean +is_cell_editable (EMeetingStore *etm, int col, int row) +{ + EMeetingStore *store; + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee; + EMeetingAttendeeEditLevel level; + + store = E_MEETING_STORE (etm); + priv = store->priv; + + if (col == E_MEETING_STORE_DELTO_COL + || col == E_MEETING_STORE_DELFROM_COL) + return FALSE; + + if (row == -1) + return TRUE; + if (row >= priv->attendees->len) + return TRUE; + + attendee = g_ptr_array_index (priv->attendees, row); + level = e_meeting_attendee_get_edit_level (attendee); + + switch (level) { + case E_MEETING_ATTENDEE_EDIT_FULL: + return TRUE; + case E_MEETING_ATTENDEE_EDIT_STATUS: + return col == E_MEETING_STORE_STATUS_COL; + case E_MEETING_ATTENDEE_EDIT_NONE: + return FALSE; + } + + return TRUE; +} + +static void +refresh_queue_remove (EMeetingStore *store, EMeetingAttendee *attendee) +{ + EMeetingStorePrivate *priv; + EMeetingStoreQueueData *qdata; + + priv = store->priv; + + /* Free the queue data */ + qdata = g_hash_table_lookup (priv->refresh_data, attendee); + if (qdata) { + g_hash_table_remove (priv->refresh_data, attendee); + g_ptr_array_free (qdata->call_backs, TRUE); + g_ptr_array_free (qdata->data, TRUE); + g_free (qdata); + } + + /* Unref the attendee */ + g_ptr_array_remove (priv->refresh_queue, attendee); + g_object_unref (attendee); +} + +static void +ems_finalize (GObject *obj) +{ + EMeetingStore *store = E_MEETING_STORE (obj); + EMeetingStorePrivate *priv; + int i; + + priv = store->priv; + + for (i = 0; i < priv->attendees->len; i++) + g_object_unref (g_ptr_array_index (priv->attendees, i)); + g_ptr_array_free (priv->attendees, TRUE); + + if (priv->client != NULL) + g_object_unref (priv->client); + + if (priv->ebook != NULL) + g_object_unref (priv->ebook); + + while (priv->refresh_queue->len > 0) + refresh_queue_remove (store, g_ptr_array_index (priv->refresh_queue, 0)); + g_ptr_array_free (priv->refresh_queue, TRUE); + g_hash_table_destroy (priv->refresh_data); + + if (priv->refresh_idle_id) + g_source_remove (priv->refresh_idle_id); + + g_free (priv); + + if (G_OBJECT_CLASS (parent_class)->finalize) + (* G_OBJECT_CLASS (parent_class)->finalize) (obj); +} + +static void +ems_class_init (GObjectClass *klass) +{ + parent_class = g_type_class_peek_parent (klass); + + klass->finalize = ems_finalize; +} + + +static void +ems_init (EMeetingStore *store) +{ + EMeetingStorePrivate *priv; + + priv = g_new0 (EMeetingStorePrivate, 1); + + store->priv = priv; + + priv->attendees = g_ptr_array_new (); + + priv->zone = icaltimezone_get_builtin_timezone (calendar_config_get_timezone ()); + + priv->refresh_queue = g_ptr_array_new (); + priv->refresh_data = g_hash_table_new (g_direct_hash, g_direct_equal); + + start_addressbook_server (store); +} + +GType +e_meeting_store_get_type (void) +{ + static GType ems_type = 0; + + if (!ems_type) { + static const GTypeInfo ems_info = { + sizeof (GtkListStoreClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) ems_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkListStore), + 0, + (GInstanceInitFunc) ems_init }; + + static const GInterfaceInfo tree_model_info = { + (GInterfaceInitFunc) ems_tree_model_init, + NULL, + NULL }; + + ems_type = g_type_register_static (G_TYPE_OBJECT, + "EMeetingStore", + &ems_info, 0); + + g_type_add_interface_static (ems_type, + GTK_TYPE_TREE_MODEL, + &tree_model_info); + } + + return ems_type; +} + +GObject * +e_meeting_store_new (void) +{ + return g_object_new (E_TYPE_MEETING_STORE, NULL); +} + + +CalClient * +e_meeting_store_get_cal_client (EMeetingStore *store) +{ + return store->priv->client; +} + +void +e_meeting_store_set_cal_client (EMeetingStore *store, CalClient *client) +{ + if (store->priv->client != NULL) + g_object_unref (store->priv->client); + + if (client != NULL) + g_object_ref (client); + store->priv->client = client; +} + +icaltimezone * +e_meeting_store_get_zone (EMeetingStore *store) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL); + + return store->priv->zone; +} + +void +e_meeting_store_set_zone (EMeetingStore *store, icaltimezone *zone) +{ + g_return_if_fail (E_IS_MEETING_STORE (store)); + + store->priv->zone = zone; +} + +static void +attendee_changed_cb (EMeetingAttendee *attendee, gpointer data) +{ + EMeetingStore *store = E_MEETING_STORE (data); + GtkTreePath *path; + GtkTreeIter iter; + gint row = -1, i; + + for (i = 0; i < store->priv->attendees->len; i++) { + if (attendee == g_ptr_array_index (store->priv->attendees, i)) { + row = i; + break; + } + } + + if (row == -1) + return; + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, row); + get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (store), path, &iter); + gtk_tree_path_free (path); +} + +void +e_meeting_store_add_attendee (EMeetingStore *store, EMeetingAttendee *attendee) +{ + GtkTreePath *path; + GtkTreeIter iter; + + g_return_if_fail (E_IS_MEETING_STORE (store)); + + g_object_ref (attendee); + g_ptr_array_add (store->priv->attendees, attendee); + + g_signal_connect (attendee, "changed", G_CALLBACK (attendee_changed_cb), store); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, store->priv->attendees->len - 1); + get_iter (GTK_TREE_MODEL (store), &iter, path); + gtk_tree_model_row_inserted (GTK_TREE_MODEL (store), path, &iter); + gtk_tree_path_free (path); +} + +EMeetingAttendee * +e_meeting_store_add_attendee_with_defaults (EMeetingStore *store) +{ + EMeetingAttendee *attendee; + char *str; + + attendee = E_MEETING_ATTENDEE (e_meeting_attendee_new ()); + + e_meeting_attendee_set_address (attendee, g_strdup ("")); + e_meeting_attendee_set_member (attendee, g_strdup ("")); + + str = g_strdup (_("Individual")); + e_meeting_attendee_set_cutype (attendee, text_to_type (str)); + g_free (str); + str = g_strdup (_("Required Participant")); + e_meeting_attendee_set_role (attendee, text_to_role (str)); + g_free (str); + str = g_strdup (_("Yes")); + e_meeting_attendee_set_rsvp (attendee, text_to_boolean (str)); + g_free (str); + + e_meeting_attendee_set_delto (attendee, g_strdup ("")); + e_meeting_attendee_set_delfrom (attendee, g_strdup ("")); + + str = g_strdup (_("Needs Action")); + e_meeting_attendee_set_status (attendee, text_to_partstat (str)); + g_free (str); + + e_meeting_attendee_set_cn (attendee, g_strdup ("")); + e_meeting_attendee_set_language (attendee, g_strdup ("en")); + + e_meeting_store_add_attendee (store, attendee); + + return attendee; +} + +void +e_meeting_store_remove_attendee (EMeetingStore *store, EMeetingAttendee *attendee) +{ + gint i, row = -1; + GtkTreePath *path; + + for (i = 0; i < store->priv->attendees->len; i++) { + if (attendee == g_ptr_array_index (store->priv->attendees, i)) { + row = i; + break; + } + } + + if (row != -1) { + g_ptr_array_remove_index (store->priv->attendees, row); + g_object_unref (attendee); + + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, row); + gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path); + gtk_tree_path_free (path); + } +} + +void +e_meeting_store_remove_all_attendees (EMeetingStore *store) +{ + gint i; + GtkTreePath *path = gtk_tree_path_new (); + + gtk_tree_path_append_index (path, 0); + + for (i = 0; i < store->priv->attendees->len; i++) { + EMeetingAttendee *attendee = g_ptr_array_index (store->priv->attendees, i); + g_object_unref (attendee); + + gtk_tree_model_row_deleted (GTK_TREE_MODEL (store), path); + gtk_tree_path_next (path); + } + + g_ptr_array_set_size (store->priv->attendees, 0); + gtk_tree_path_free (path); +} + +EMeetingAttendee * +e_meeting_store_find_attendee (EMeetingStore *store, const gchar *address, gint *row) +{ + EMeetingAttendee *attendee; + int i; + + if (address == NULL) + return NULL; + + for (i = 0; i < store->priv->attendees->len; i++) { + const gchar *attendee_address; + + attendee = g_ptr_array_index (store->priv->attendees, i); + + attendee_address = e_meeting_attendee_get_address (attendee); + if (attendee_address && !g_strcasecmp (itip_strip_mailto (attendee_address), itip_strip_mailto (address))) { + if (row != NULL) + *row = i; + + return attendee; + } + } + + return NULL; +} + +EMeetingAttendee * +e_meeting_store_find_attendee_at_row (EMeetingStore *store, gint row) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL); + g_return_val_if_fail (ROW_VALID (store, row), NULL); + + return g_ptr_array_index (store->priv->attendees, row); +} + +gint +e_meeting_store_count_actual_attendees (EMeetingStore *store) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), 0); + + return store->priv->attendees->len; +} + +const GPtrArray * +e_meeting_store_get_attendees (EMeetingStore *store) +{ + g_return_val_if_fail (E_IS_MEETING_STORE (store), NULL); + + return store->priv->attendees; +} + +static icaltimezone * +find_zone (icalproperty *ip, icalcomponent *tz_top_level) +{ + icalparameter *param; + icalcomponent *sub_comp; + const char *tzid; + icalcompiter iter; + + if (tz_top_level == NULL) + return NULL; + + param = icalproperty_get_first_parameter (ip, ICAL_TZID_PARAMETER); + if (param == NULL) + return NULL; + tzid = icalparameter_get_tzid (param); + + iter = icalcomponent_begin_component (tz_top_level, ICAL_VTIMEZONE_COMPONENT); + while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { + icalcomponent *clone; + const char *tz_tzid; + + tz_tzid = icalproperty_get_tzid (sub_comp); + if (!strcmp (tzid, tz_tzid)) { + icaltimezone *zone; + + zone = icaltimezone_new (); + clone = icalcomponent_new_clone (sub_comp); + icaltimezone_set_component (zone, clone); + + return zone; + } + + icalcompiter_next (&iter); + } + + return NULL; +} + +static void +process_callbacks (EMeetingStoreQueueData *qdata) +{ + EMeetingStore *store; + int i; + + for (i = 0; i < qdata->call_backs->len; i++) { + EMeetingStoreRefreshCallback call_back; + gpointer *data; + + call_back = g_ptr_array_index (qdata->call_backs, i); + data = g_ptr_array_index (qdata->data, i); + + call_back (data); + } + + store = qdata->store; + refresh_queue_remove (qdata->store, qdata->attendee); + g_object_unref (store); +} + +static void +process_free_busy_comp (EMeetingAttendee *attendee, + icalcomponent *fb_comp, + icaltimezone *zone, + icalcomponent *tz_top_level) +{ + icalproperty *ip; + + ip = icalcomponent_get_first_property (fb_comp, ICAL_DTSTART_PROPERTY); + if (ip != NULL) { + struct icaltimetype dtstart; + icaltimezone *ds_zone; + + dtstart = icalproperty_get_dtstart (ip); + if (!dtstart.is_utc) + ds_zone = find_zone (ip, tz_top_level); + else + ds_zone = icaltimezone_get_utc_timezone (); + icaltimezone_convert_time (&dtstart, ds_zone, zone); + e_meeting_attendee_set_start_busy_range (attendee, + dtstart.year, + dtstart.month, + dtstart.day, + dtstart.hour, + dtstart.minute); + } + + ip = icalcomponent_get_first_property (fb_comp, ICAL_DTEND_PROPERTY); + if (ip != NULL) { + struct icaltimetype dtend; + icaltimezone *de_zone; + + dtend = icalproperty_get_dtend (ip); + if (!dtend.is_utc) + de_zone = find_zone (ip, tz_top_level); + else + de_zone = icaltimezone_get_utc_timezone (); + icaltimezone_convert_time (&dtend, de_zone, zone); + e_meeting_attendee_set_end_busy_range (attendee, + dtend.year, + dtend.month, + dtend.day, + dtend.hour, + dtend.minute); + } + + ip = icalcomponent_get_first_property (fb_comp, ICAL_FREEBUSY_PROPERTY); + while (ip != NULL) { + icalparameter *param; + struct icalperiodtype fb; + EMeetingFreeBusyType busy_type = E_MEETING_FREE_BUSY_LAST; + icalparameter_fbtype fbtype = ICAL_FBTYPE_BUSY; + + fb = icalproperty_get_freebusy (ip); + param = icalproperty_get_first_parameter (ip, ICAL_FBTYPE_PARAMETER); + if (param != NULL) + fbtype = icalparameter_get_fbtype (param); + + switch (fbtype) { + case ICAL_FBTYPE_BUSY: + busy_type = E_MEETING_FREE_BUSY_BUSY; + break; + + case ICAL_FBTYPE_BUSYUNAVAILABLE: + busy_type = E_MEETING_FREE_BUSY_OUT_OF_OFFICE; + break; + + case ICAL_FBTYPE_BUSYTENTATIVE: + busy_type = E_MEETING_FREE_BUSY_TENTATIVE; + break; + + default: + break; + } + + if (busy_type != E_MEETING_FREE_BUSY_LAST) { + icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); + + icaltimezone_convert_time (&fb.start, utc_zone, zone); + icaltimezone_convert_time (&fb.end, utc_zone, zone); + e_meeting_attendee_add_busy_period (attendee, + fb.start.year, + fb.start.month, + fb.start.day, + fb.start.hour, + fb.start.minute, + fb.end.year, + fb.end.month, + fb.end.day, + fb.end.hour, + fb.end.minute, + busy_type); + } + + ip = icalcomponent_get_next_property (fb_comp, ICAL_FREEBUSY_PROPERTY); + } +} + +static void +process_free_busy (EMeetingStoreQueueData *qdata, char *text) +{ + EMeetingStore *store = qdata->store; + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee = qdata->attendee; + icalcomponent *main_comp; + icalcomponent_kind kind = ICAL_NO_COMPONENT; + + priv = store->priv; + + main_comp = icalparser_parse_string (text); + if (main_comp == NULL) { + process_callbacks (qdata); + return; + } + + kind = icalcomponent_isa (main_comp); + if (kind == ICAL_VCALENDAR_COMPONENT) { + icalcompiter iter; + icalcomponent *tz_top_level, *sub_comp; + + tz_top_level = cal_util_new_top_level (); + + iter = icalcomponent_begin_component (main_comp, ICAL_VTIMEZONE_COMPONENT); + while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { + icalcomponent *clone; + + clone = icalcomponent_new_clone (sub_comp); + icalcomponent_add_component (tz_top_level, clone); + + icalcompiter_next (&iter); + } + + iter = icalcomponent_begin_component (main_comp, ICAL_VFREEBUSY_COMPONENT); + while ((sub_comp = icalcompiter_deref (&iter)) != NULL) { + process_free_busy_comp (attendee, sub_comp, priv->zone, tz_top_level); + + icalcompiter_next (&iter); + } + icalcomponent_free (tz_top_level); + } else if (kind == ICAL_VFREEBUSY_COMPONENT) { + process_free_busy_comp (attendee, main_comp, priv->zone, NULL); + } + + icalcomponent_free (main_comp); + + process_callbacks (qdata); +} + +static gboolean +refresh_busy_periods (gpointer data) +{ + EMeetingStore *store = E_MEETING_STORE (data); + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee = NULL; + EMeetingStoreQueueData *qdata = NULL; + char *query; + int i; + + priv = store->priv; + + /* Check to see if there are any remaining attendees in the queue */ + for (i = 0; i < priv->refresh_queue->len; i++) { + attendee = g_ptr_array_index (priv->refresh_queue, i); + g_assert (attendee != NULL); + + qdata = g_hash_table_lookup (priv->refresh_data, attendee); + if (!qdata) + continue; + + if (!qdata->refreshing) + break; + } + + /* The everything in the queue is being refreshed */ + if (i >= priv->refresh_queue->len) { + priv->refresh_idle_id = 0; + return FALSE; + } + + /* Indicate we are trying to refresh it */ + qdata->refreshing = TRUE; + + /* We take a ref in case we get destroyed in the gui during a callback */ + g_object_ref (qdata->store); + + /* Check the server for free busy data */ + if (priv->client) { + GList *fb_data, *users = NULL; + struct icaltimetype itt; + time_t startt, endt; + const char *user; + + itt = icaltime_null_time (); + itt.year = g_date_year (&qdata->start.date); + itt.month = g_date_month (&qdata->start.date); + itt.day = g_date_day (&qdata->start.date); + itt.hour = qdata->start.hour; + itt.minute = qdata->start.minute; + startt = icaltime_as_timet_with_zone (itt, priv->zone); + + itt = icaltime_null_time (); + itt.year = g_date_year (&qdata->end.date); + itt.month = g_date_month (&qdata->end.date); + itt.day = g_date_day (&qdata->end.date); + itt.hour = qdata->end.hour; + itt.minute = qdata->end.minute; + endt = icaltime_as_timet_with_zone (itt, priv->zone); + + user = itip_strip_mailto (e_meeting_attendee_get_address (attendee)); + users = g_list_append (users, g_strdup (user)); + fb_data = cal_client_get_free_busy (priv->client, users, startt, endt); + + g_list_foreach (users, (GFunc)g_free, NULL); + g_list_free (users); + + if (fb_data != NULL) { + CalComponent *comp = fb_data->data; + char *comp_str; + + comp_str = cal_component_get_as_string (comp); + process_free_busy (qdata, comp_str); + g_free (comp_str); + return TRUE; + } + } + + /* Look for fburl's of attendee with no free busy info on server */ + if (!priv->book_loaded) { + priv->book_load_wait = TRUE; + gtk_main (); + } + + if (!e_meeting_attendee_is_set_address (attendee)) { + process_callbacks (qdata); + return TRUE; + } + + query = g_strdup_printf ("(contains \"email\" \"%s\")", + itip_strip_mailto (e_meeting_attendee_get_address (attendee))); + process_callbacks (qdata); + g_free (query); + + return TRUE; +} + +static void +refresh_queue_add (EMeetingStore *store, int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data) +{ + EMeetingStorePrivate *priv; + EMeetingAttendee *attendee; + EMeetingStoreQueueData *qdata; + + priv = store->priv; + + attendee = g_ptr_array_index (priv->attendees, row); + if (attendee == NULL) + return; + + qdata = g_hash_table_lookup (priv->refresh_data, attendee); + if (qdata == NULL) { + qdata = g_new0 (EMeetingStoreQueueData, 1); + + qdata->store = store; + qdata->attendee = attendee; + e_meeting_attendee_clear_busy_periods (attendee); + e_meeting_attendee_set_has_calendar_info (attendee, FALSE); + + qdata->start = *start; + qdata->end = *end; + qdata->string = g_string_new (NULL); + qdata->call_backs = g_ptr_array_new (); + qdata->data = g_ptr_array_new (); + g_ptr_array_add (qdata->call_backs, call_back); + g_ptr_array_add (qdata->data, data); + + g_hash_table_insert (priv->refresh_data, attendee, qdata); + } else { + if (e_meeting_time_compare_times (start, &qdata->start) == -1) + qdata->start = *start; + if (e_meeting_time_compare_times (end, &qdata->end) == 1) + qdata->end = *end; + g_ptr_array_add (qdata->call_backs, call_back); + g_ptr_array_add (qdata->data, data); + } + + g_object_ref (attendee); + g_ptr_array_add (priv->refresh_queue, attendee); + + if (priv->refresh_idle_id == 0) + priv->refresh_idle_id = g_idle_add (refresh_busy_periods, store); +} + +static void +async_close (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer data) +{ + EMeetingStoreQueueData *qdata = data; + + process_free_busy (qdata, qdata->string->str); +} + +static void +async_read (GnomeVFSAsyncHandle *handle, + GnomeVFSResult result, + gpointer buffer, + GnomeVFSFileSize requested, + GnomeVFSFileSize read, + gpointer data) +{ + EMeetingStoreQueueData *qdata = data; + GnomeVFSFileSize buf_size = BUF_SIZE - 1; + + if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) { + gnome_vfs_async_close (handle, async_close, qdata); + return; + } + + ((char *)buffer)[read] = '\0'; + qdata->string = g_string_append (qdata->string, buffer); + + if (result == GNOME_VFS_ERROR_EOF) { + gnome_vfs_async_close (handle, async_close, qdata); + return; + } + + gnome_vfs_async_read (handle, qdata->buffer, buf_size, async_read, qdata); +} + +void +e_meeting_store_refresh_all_busy_periods (EMeetingStore *store, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data) +{ + int i; + + g_return_if_fail (E_IS_MEETING_STORE (store)); + + for (i = 0; i < store->priv->attendees->len; i++) + refresh_queue_add (store, i, start, end, call_back, data); +} + +void +e_meeting_store_refresh_busy_periods (EMeetingStore *store, + int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data) +{ + g_return_if_fail (E_IS_MEETING_STORE (store)); + + refresh_queue_add (store, row, start, end, call_back, data); +} + diff --git a/calendar/gui/e-meeting-store.h b/calendar/gui/e-meeting-store.h new file mode 100644 index 0000000000..e5e518289d --- /dev/null +++ b/calendar/gui/e-meeting-store.h @@ -0,0 +1,108 @@ +/* + * e-meeting-store.h + * + * Copyright (C) 2003 Ximian, Inc. + * + * Author: Mike Kestner + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _E_MEETING_STORE_H_ +#define _E_MEETING_STORE_H_ + +#include <gtk/gtkliststore.h> +#include <cal-client/cal-client.h> +#include "e-meeting-attendee.h" + +G_BEGIN_DECLS + +#define E_TYPE_MEETING_STORE (e_meeting_store_get_type ()) +#define E_MEETING_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_MEETING_STORE, EMeetingStore)) +#define E_MEETING_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_MEETING_STORE, EMeetingStoreClass)) +#define E_IS_MEETING_STORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_MEETING_STORE)) +#define E_IS_MEETING_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), E_TYPE_MEETING_STORE)) + +typedef struct _EMeetingStore EMeetingStore; +typedef struct _EMeetingStorePrivate EMeetingStorePrivate; +typedef struct _EMeetingStoreClass EMeetingStoreClass; + +typedef enum { + E_MEETING_STORE_ADDRESS_COL, + E_MEETING_STORE_MEMBER_COL, + E_MEETING_STORE_TYPE_COL, + E_MEETING_STORE_ROLE_COL, + E_MEETING_STORE_RSVP_COL, + E_MEETING_STORE_DELTO_COL, + E_MEETING_STORE_DELFROM_COL, + E_MEETING_STORE_STATUS_COL, + E_MEETING_STORE_CN_COL, + E_MEETING_STORE_LANGUAGE_COL, + E_MEETING_STORE_ATTENDEE_COL, + E_MEETING_STORE_ATTENDEE_UNDERLINE_COL, + E_MEETING_STORE_COLUMN_COUNT +} EMeetingStoreColumns; + +struct _EMeetingStore { + GtkListStore parent; + + EMeetingStorePrivate *priv; +}; + +struct _EMeetingStoreClass { + GtkListStoreClass parent_class; +}; + +typedef void (* EMeetingStoreRefreshCallback) (gpointer data); + +GType e_meeting_store_get_type (void); +GObject *e_meeting_store_new (void); + +void e_meeting_store_set_value (EMeetingStore *im, int row, int col, const gchar *val); + +CalClient *e_meeting_store_get_cal_client (EMeetingStore *im); +void e_meeting_store_set_cal_client (EMeetingStore *im, CalClient *client); + +icaltimezone *e_meeting_store_get_zone (EMeetingStore *im); +void e_meeting_store_set_zone (EMeetingStore *im, icaltimezone *zone); + +void e_meeting_store_add_attendee (EMeetingStore *im, EMeetingAttendee *ia); +EMeetingAttendee *e_meeting_store_add_attendee_with_defaults (EMeetingStore *im); + +void e_meeting_store_remove_attendee (EMeetingStore *im, EMeetingAttendee *ia); +void e_meeting_store_remove_all_attendees (EMeetingStore *im); + +EMeetingAttendee *e_meeting_store_find_attendee (EMeetingStore *im, const gchar *address, gint *row); +EMeetingAttendee *e_meeting_store_find_attendee_at_row (EMeetingStore *im, gint row); + +gint e_meeting_store_count_actual_attendees (EMeetingStore *im); +const GPtrArray *e_meeting_store_get_attendees (EMeetingStore *im); + +void e_meeting_store_refresh_all_busy_periods (EMeetingStore *im, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data); +void e_meeting_store_refresh_busy_periods (EMeetingStore *im, + int row, + EMeetingTime *start, + EMeetingTime *end, + EMeetingStoreRefreshCallback call_back, + gpointer data); + + +G_END_DECLS + +#endif diff --git a/calendar/gui/e-meeting-time-sel-item.c b/calendar/gui/e-meeting-time-sel-item.c index e19a148e72..c714684569 100644 --- a/calendar/gui/e-meeting-time-sel-item.c +++ b/calendar/gui/e-meeting-time-sel-item.c @@ -256,7 +256,6 @@ e_meeting_time_selector_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, { EMeetingTimeSelector *mts; EMeetingTimeSelectorItem *mts_item; - ETable *real_table; EMeetingAttendee *ia; gint day_x, meeting_start_x, meeting_end_x, bar_y, bar_height; gint row, row_y, start_x, end_x; @@ -270,8 +269,6 @@ e_meeting_time_selector_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, gc = mts_item->main_gc; stipple_gc = mts_item->stipple_gc; - real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (mts->etable)); - is_display_top = (GTK_WIDGET (item->canvas) == mts->display_top) ? TRUE : FALSE; @@ -321,14 +318,11 @@ e_meeting_time_selector_item_draw (GnomeCanvasItem *item, GdkDrawable *drawable, gdk_gc_set_fill (stipple_gc, GDK_OPAQUE_STIPPLED); row = y / mts->row_height; row_y = row * mts->row_height - y; - while (row < e_meeting_model_count_actual_attendees (mts->model) && row_y < height) { - gint model_row = e_meeting_model_etable_view_to_model_row (real_table, - mts->model, row); - - ia = e_meeting_model_find_attendee_at_row (mts->model, model_row); + while (row < e_meeting_store_count_actual_attendees (mts->model) && row_y < height) { + ia = e_meeting_store_find_attendee_at_row (mts->model, row); if (e_meeting_attendee_get_has_calendar_info (ia)) { - if (e_meeting_time_selector_item_calculate_busy_range (mts, model_row, x, width, &start_x, &end_x)) { + if (e_meeting_time_selector_item_calculate_busy_range (mts, row, x, width, &start_x, &end_x)) { if (start_x >= width || end_x <= 0) { gdk_draw_rectangle (drawable, stipple_gc, TRUE, 0, row_y, width, mts->row_height); } else { @@ -522,9 +516,9 @@ e_meeting_time_selector_item_paint_all_attendees_busy_periods (EMeetingTimeSelec y = 2 * mts->row_height - scroll_y - 1; /* Get the first visible busy periods for all the attendees. */ - first_periods = g_new (gint, e_meeting_model_count_actual_attendees (mts->model)); - for (row = 0; row < e_meeting_model_count_actual_attendees (mts->model); row++) { - ia = e_meeting_model_find_attendee_at_row (mts->model, row); + first_periods = g_new (gint, e_meeting_store_count_actual_attendees (mts->model)); + for (row = 0; row < e_meeting_store_count_actual_attendees (mts->model); row++) { + ia = e_meeting_store_find_attendee_at_row (mts->model, row); first_periods[row] = e_meeting_time_selector_item_find_first_busy_period (mts_item, date, row); } @@ -532,7 +526,7 @@ e_meeting_time_selector_item_paint_all_attendees_busy_periods (EMeetingTimeSelec busy_type < E_MEETING_FREE_BUSY_LAST; busy_type++) { gdk_gc_set_foreground (gc, &mts->busy_colors[busy_type]); - for (row = 0; row < e_meeting_model_count_actual_attendees (mts->model); row++) { + for (row = 0; row < e_meeting_store_count_actual_attendees (mts->model); row++) { if (first_periods[row] == -1) continue; e_meeting_time_selector_item_paint_attendee_busy_periods (mts_item, drawable, x, y, width, row, first_periods[row], busy_type); @@ -569,7 +563,7 @@ e_meeting_time_selector_item_paint_day (EMeetingTimeSelectorItem *mts_item, grid_y < height; grid_y += mts->row_height) { - if (attendee_index <= e_meeting_model_count_actual_attendees (mts->model)) { + if (attendee_index <= e_meeting_store_count_actual_attendees (mts->model)) { gdk_gc_set_foreground (gc, &mts->grid_color); gdk_draw_line (drawable, gc, 0, grid_y, width, grid_y); @@ -582,7 +576,7 @@ e_meeting_time_selector_item_paint_day (EMeetingTimeSelectorItem *mts_item, } /* Draw the vertical grid lines. */ - unused_y = (e_meeting_model_count_actual_attendees (mts->model) * mts->row_height) - scroll_y; + unused_y = (e_meeting_store_count_actual_attendees (mts->model) * mts->row_height) - scroll_y; if (unused_y >= 0) { gdk_gc_set_foreground (gc, &mts->grid_color); for (grid_x = mts->col_width - 1; @@ -624,15 +618,12 @@ e_meeting_time_selector_item_paint_busy_periods (EMeetingTimeSelectorItem *mts_i { EMeetingTimeSelector *mts; EMeetingFreeBusyType busy_type; - ETable *real_table; - gint row, model_row, y, first_period; + gint row, y, first_period; GdkGC *gc; mts = mts_item->mts; gc = mts_item->main_gc; - real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (mts->etable)); - /* Calculate the first visible attendee row. */ row = scroll_y / mts->row_height; @@ -640,11 +631,10 @@ e_meeting_time_selector_item_paint_busy_periods (EMeetingTimeSelectorItem *mts_i y = row * mts->row_height - scroll_y; /* Step through the attendees painting the busy periods. */ - while (y < height && row < e_meeting_model_count_actual_attendees (mts->model)) { - model_row = e_meeting_model_etable_view_to_model_row (real_table, mts->model, row); + while (y < height && row < e_meeting_store_count_actual_attendees (mts->model)) { /* Find the first visible busy period. */ - first_period = e_meeting_time_selector_item_find_first_busy_period (mts_item, date, model_row); + first_period = e_meeting_time_selector_item_find_first_busy_period (mts_item, date, row); if (first_period != -1) { /* Paint the different types of busy periods, in reverse order of precedence, so the highest @@ -653,7 +643,7 @@ e_meeting_time_selector_item_paint_busy_periods (EMeetingTimeSelectorItem *mts_i busy_type < E_MEETING_FREE_BUSY_LAST; busy_type++) { gdk_gc_set_foreground (gc, &mts->busy_colors[busy_type]); - e_meeting_time_selector_item_paint_attendee_busy_periods (mts_item, drawable, x, y, width, model_row, first_period, busy_type); + e_meeting_time_selector_item_paint_attendee_busy_periods (mts_item, drawable, x, y, width, row, first_period, busy_type); } } y += mts->row_height; @@ -677,7 +667,7 @@ e_meeting_time_selector_item_find_first_busy_period (EMeetingTimeSelectorItem *m mts = mts_item->mts; - ia = e_meeting_model_find_attendee_at_row (mts->model, row); + ia = e_meeting_store_find_attendee_at_row (mts->model, row); period_num = e_meeting_attendee_find_first_busy_period (ia, date); if (period_num == -1) @@ -710,7 +700,7 @@ e_meeting_time_selector_item_paint_attendee_busy_periods (EMeetingTimeSelectorIt mts = mts_item->mts; gc = mts_item->main_gc; - ia = e_meeting_model_find_attendee_at_row (mts->model, row); + ia = e_meeting_store_find_attendee_at_row (mts->model, row); busy_periods = e_meeting_attendee_get_busy_periods (ia); for (period_num = first_period; @@ -990,7 +980,7 @@ e_meeting_time_selector_item_calculate_busy_range (EMeetingTimeSelector *mts, EMeetingTime busy_periods_start; EMeetingTime busy_periods_end; - ia = e_meeting_model_find_attendee_at_row (mts->model, row); + ia = e_meeting_store_find_attendee_at_row (mts->model, row); busy_periods_start = e_meeting_attendee_get_start_busy_range (ia); busy_periods_end = e_meeting_attendee_get_end_busy_range (ia); diff --git a/calendar/gui/e-meeting-time-sel.c b/calendar/gui/e-meeting-time-sel.c index 18e8fd76fe..157320ef16 100644 --- a/calendar/gui/e-meeting-time-sel.c +++ b/calendar/gui/e-meeting-time-sel.c @@ -54,18 +54,13 @@ #include <gal/widgets/e-canvas.h> #include <gal/widgets/e-canvas-utils.h> -#include <gal/e-table/e-cell-combo.h> -#include <gal/e-table/e-cell-text.h> -#include <gal/e-table/e-table-simple.h> -#include <gal/e-table/e-table-scrolled.h> -#include <gal/e-table/e-table-header-item.h> -#include <gal/e-table/e-table-header-utils.h> #include <widgets/misc/e-dateedit.h> #include "calendar-component.h" #include "calendar-config.h" #include "e-meeting-utils.h" +#include "e-meeting-list-view.h" #include "e-meeting-time-sel-item.h" /* An array of hour strings for 24 hour time, "0:00" .. "23:00". */ @@ -132,9 +127,6 @@ static void e_meeting_time_selector_hadjustment_changed (GtkAdjustment *adjustme EMeetingTimeSelector *mts); static void e_meeting_time_selector_vadjustment_changed (GtkAdjustment *adjustment, EMeetingTimeSelector *mts); -static void e_meeting_time_selector_table_vadjustment_changed (GtkAdjustment *adjustment, - EMeetingTimeSelector *mts); - static void e_meeting_time_selector_on_canvas_realized (GtkWidget *widget, EMeetingTimeSelector *mts); @@ -213,10 +205,9 @@ static void e_meeting_time_selector_update_end_date_edit (EMeetingTimeSelector * static void e_meeting_time_selector_ensure_meeting_time_shown (EMeetingTimeSelector *mts); static void e_meeting_time_selector_update_dates_shown (EMeetingTimeSelector *mts); -static void rows_inserted_cb (ETableModel *etm, int row, int count, gpointer data); -static void cell_changed_cb (ETableModel *etm, int row, int col, gpointer data); -static void rows_deleted_cb (ETableModel *etm, int row, int count, gpointer data); -static void sort_info_changed_cb (ETableSortInfo *info, gpointer data); +static void row_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); +static void row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); +static void row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data); static GtkTableClass *parent_class; @@ -267,12 +258,12 @@ e_meeting_time_selector_init (EMeetingTimeSelector * mts) mts->zoomed_out = TRUE; mts->dragging_position = E_MEETING_TIME_SELECTOR_POS_NONE; - mts->etable = NULL; + mts->list_view = NULL; } void -e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingModel *emm) +e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingStore *ems) { GtkWidget *hbox, *vbox, *separator, *button, *label, *table; GtkWidget *alignment, *child_hbox, *arrow, *menuitem; @@ -282,8 +273,6 @@ e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingModel *em guint accel_key; time_t meeting_start_time; struct tm *meeting_start_tm; - char *filename; - ETable *real_table; guchar stipple_bits[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, }; @@ -324,27 +313,25 @@ e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingModel *em gtk_box_pack_start (GTK_BOX (vbox), mts->attendees_vbox, TRUE, TRUE, 0); gtk_widget_show (mts->attendees_vbox); - /* build the etable */ - filename = g_strdup_printf ("%s/config/et-header-meeting-time-sel", evolution_dir); - mts->model = emm; + mts->model = ems; if (mts->model) g_object_ref (mts->model); - g_signal_connect (mts->model, "model_rows_inserted", G_CALLBACK (rows_inserted_cb), mts); - g_signal_connect (mts->model, "model_cell_changed", G_CALLBACK (cell_changed_cb), mts); - g_signal_connect (mts->model, "model_rows_deleted", G_CALLBACK (rows_deleted_cb), mts); + g_signal_connect (mts->model, "row_inserted", G_CALLBACK (row_inserted_cb), mts); + g_signal_connect (mts->model, "row_changed", G_CALLBACK (row_changed_cb), mts); + g_signal_connect (mts->model, "row_deleted", G_CALLBACK (row_deleted_cb), mts); - mts->etable = GTK_WIDGET (e_meeting_model_etable_from_model (mts->model, - EVOLUTION_ETSPECDIR "/e-meeting-time-sel.etspec", - filename)); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (mts->etable), GTK_POLICY_NEVER, GTK_POLICY_NEVER); + mts->list_view = e_meeting_list_view_new (mts->model); + e_meeting_list_view_column_set_visible (mts->list_view, "Role", FALSE); + e_meeting_list_view_column_set_visible (mts->list_view, "RSVP", FALSE); + gtk_widget_show (GTK_WIDGET (mts->list_view)); - real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (mts->etable)); +#if 0 + /* FIXME: do we need sorting here */ g_signal_connect (real_table->sort_info, "sort_info_changed", G_CALLBACK (sort_info_changed_cb), mts); +#endif - gtk_box_pack_start (GTK_BOX (mts->attendees_vbox), mts->etable, TRUE, TRUE, 6); - gtk_widget_show (mts->etable); - g_free (filename); + gtk_box_pack_start (GTK_BOX (mts->attendees_vbox), GTK_WIDGET (mts->list_view), TRUE, TRUE, 6); /* The free/busy information */ mts->display_top = gnome_canvas_new (); @@ -666,11 +653,6 @@ e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingModel *em g_signal_connect (GTK_LAYOUT (mts->display_main)->vadjustment, "changed", G_CALLBACK (e_meeting_time_selector_vadjustment_changed), mts); - g_signal_connect (gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (mts->etable)), "value_changed", - G_CALLBACK (e_meeting_time_selector_table_vadjustment_changed), mts); - g_signal_connect (gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (mts->etable)), "changed", - G_CALLBACK (e_meeting_time_selector_table_vadjustment_changed), mts); - e_meeting_time_selector_recalc_grid (mts); e_meeting_time_selector_ensure_meeting_time_shown (mts); e_meeting_time_selector_update_start_date_edit (mts); @@ -794,13 +776,13 @@ e_meeting_time_selector_autopick_menu_detacher (GtkWidget *widget, GtkWidget * -e_meeting_time_selector_new (EMeetingModel *emm) +e_meeting_time_selector_new (EMeetingStore *ems) { GtkWidget *mts; mts = GTK_WIDGET (g_object_new (e_meeting_time_selector_get_type (), NULL)); - e_meeting_time_selector_construct (E_MEETING_TIME_SELECTOR (mts), emm); + e_meeting_time_selector_construct (E_MEETING_TIME_SELECTOR (mts), ems); return mts; } @@ -867,15 +849,14 @@ e_meeting_time_selector_style_set (GtkWidget *widget, { EMeetingTimeSelector *mts; EMeetingTime saved_time; - ETable *real_table; - ETableHeader *eth; int hour, max_hour_width; - int numcols, col; int maxheight; PangoFontDescription *font_desc; PangoContext *pango_context; PangoFontMetrics *font_metrics; PangoLayout *layout; + GtkTreePath *path; + GdkRectangle cell_area; if (GTK_WIDGET_CLASS (parent_class)->style_set) (*GTK_WIDGET_CLASS (parent_class)->style_set)(widget, previous_style); @@ -901,9 +882,7 @@ e_meeting_time_selector_style_set (GtkWidget *widget, max_hour_width = MAX (max_hour_width, mts->hour_widths[hour]); } - /* FIXME the 3 is for the padding etable adds on */ pango_layout_get_pixel_size (layout, NULL, &mts->row_height); - mts->row_height += 3; mts->col_width = max_hour_width + 6; e_meeting_time_selector_save_position (mts, &saved_time); @@ -913,21 +892,16 @@ e_meeting_time_selector_style_set (GtkWidget *widget, gtk_widget_set_usize (mts->display_top, -1, mts->row_height * 3 + 4); /* Calculate header height */ - real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (mts->etable)); - eth = real_table->full_header; - numcols = e_table_header_count (eth); - maxheight = 0; - for (col = 0; col < numcols; col++) { - ETableCol *ecol = e_table_header_get_column (eth, col); - int height; - - height = e_table_header_compute_height (ecol, widget); - - if (height > maxheight) - maxheight = height; - } - /* FIXME the 5 is for the padding etable adds on */ - gtk_widget_set_usize (mts->attendees_vbox_spacer, 1, mts->row_height * 3 - maxheight - 5); + if (GTK_WIDGET_REALIZED (mts->list_view)) { + path = gtk_tree_path_new (); + gtk_tree_path_append_index (path, 0); + gtk_tree_view_get_cell_area (GTK_TREE_VIEW (mts->list_view), path, NULL, &cell_area); + gtk_tree_path_free (path); + maxheight = cell_area.y; + } else + maxheight = 10; + + gtk_widget_set_usize (mts->attendees_vbox_spacer, 1, mts->row_height * 3 - maxheight); GTK_LAYOUT (mts->display_main)->hadjustment->step_increment = mts->col_width; GTK_LAYOUT (mts->display_main)->vadjustment->step_increment = mts->row_height; @@ -993,27 +967,13 @@ e_meeting_time_selector_vadjustment_changed (GtkAdjustment *adjustment, { GtkAdjustment *adj; - adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (mts->etable)); - if (adj->value != adjustment->value) { - adj->value = adjustment->value; - gtk_adjustment_value_changed (adj); - } -} - -static void -e_meeting_time_selector_table_vadjustment_changed (GtkAdjustment *adjustment, - EMeetingTimeSelector *mts) -{ - GtkAdjustment *adj; - - adj = GTK_LAYOUT (mts->display_main)->vadjustment; + adj = gtk_tree_view_get_vadjustment (GTK_TREE_VIEW (mts->list_view)); if (adj->value != adjustment->value) { adj->value = adjustment->value; gtk_adjustment_value_changed (adj); } } - void e_meeting_time_selector_get_meeting_time (EMeetingTimeSelector *mts, gint *start_year, @@ -1229,17 +1189,17 @@ e_meeting_time_selector_refresh_free_busy (EMeetingTimeSelector *mts, int row, g if (all) { int i; - for (i = 0; i < e_meeting_model_count_actual_attendees (mts->model); i++) + for (i = 0; i < e_meeting_store_count_actual_attendees (mts->model); i++) gtk_object_ref (GTK_OBJECT (mts)); } else { gtk_object_ref (GTK_OBJECT (mts)); } if (all) - e_meeting_model_refresh_all_busy_periods (mts->model, &start, &end, + e_meeting_store_refresh_all_busy_periods (mts->model, &start, &end, e_meeting_time_selector_refresh_cb, mts); else - e_meeting_model_refresh_busy_periods (mts->model, row, &start, &end, + e_meeting_store_refresh_busy_periods (mts->model, row, &start, &end, e_meeting_time_selector_refresh_cb, mts); } @@ -1387,12 +1347,8 @@ e_meeting_time_selector_on_invite_others_button_expose (GtkWidget *button, GdkEventExpose *event, EMeetingTimeSelector *mts) { - ETable *real_table; gboolean click_to_add = TRUE; - real_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (mts->etable)); - g_object_get (G_OBJECT (real_table), "use_click_to_add", &click_to_add, NULL); - gtk_widget_set_sensitive (button, click_to_add); return FALSE; } @@ -1401,7 +1357,7 @@ static void e_meeting_time_selector_on_invite_others_button_clicked (GtkWidget *button, EMeetingTimeSelector *mts) { - e_meeting_model_invite_others_dialog (mts->model); + e_meeting_list_view_invite_others_dialog (mts->list_view); } @@ -1561,8 +1517,8 @@ e_meeting_time_selector_autopick (EMeetingTimeSelector *mts, /* Step through each attendee, checking if the meeting time intersects one of the attendees busy periods. */ - for (row = 0; row < e_meeting_model_count_actual_attendees (mts->model); row++) { - attendee = e_meeting_model_find_attendee_at_row (mts->model, row); + for (row = 0; row < e_meeting_store_count_actual_attendees (mts->model); row++) { + attendee = e_meeting_store_find_attendee_at_row (mts->model, row); /* Skip optional people if they don't matter. */ if (skip_optional && e_meeting_attendee_get_atype (attendee) == E_MEETING_ATTENDEE_OPTIONAL_PERSON) @@ -2296,7 +2252,7 @@ e_meeting_time_selector_update_main_canvas_scroll_region (EMeetingTimeSelector * { gint height, canvas_height; - height = mts->row_height * (e_meeting_model_count_actual_attendees (mts->model) + 2); + height = mts->row_height * (e_meeting_store_count_actual_attendees (mts->model) + 2); canvas_height = GTK_WIDGET (mts->display_main)->allocation.height; height = MAX (height, canvas_height); @@ -2848,11 +2804,10 @@ e_meeting_time_selector_calculate_time_position (EMeetingTimeSelector *mts, } static void -rows_inserted_cb (ETableModel *etm, int row, int count, gpointer data) +row_inserted_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { EMeetingTimeSelector *mts = E_MEETING_TIME_SELECTOR (data); - int i; - + int row = gtk_tree_path_get_indices (path) [0]; /* Update the scroll region. */ e_meeting_time_selector_update_main_canvas_scroll_region (mts); @@ -2861,24 +2816,21 @@ rows_inserted_cb (ETableModel *etm, int row, int count, gpointer data) gtk_widget_queue_draw (mts->display_main); /* Get the latest free/busy info */ - for (i = 0; i < count; i++) - e_meeting_time_selector_refresh_free_busy (mts, row + i, FALSE); + e_meeting_time_selector_refresh_free_busy (mts, row, FALSE); } static void -cell_changed_cb (ETableModel *etm, int col, int row, gpointer data) +row_changed_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { EMeetingTimeSelector *mts = E_MEETING_TIME_SELECTOR (data); - - if (col != E_MEETING_MODEL_ADDRESS_COL) - return; + int row = gtk_tree_path_get_indices (path) [0]; /* Get the latest free/busy info */ e_meeting_time_selector_refresh_free_busy (mts, row, FALSE); } static void -rows_deleted_cb (ETableModel *etm, int row, int count, gpointer data) +row_deleted_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data) { EMeetingTimeSelector *mts = E_MEETING_TIME_SELECTOR (data); @@ -2890,12 +2842,3 @@ rows_deleted_cb (ETableModel *etm, int row, int count, gpointer data) gtk_widget_queue_draw (mts->display_main); } -static void -sort_info_changed_cb (ETableSortInfo *info, gpointer data) -{ - EMeetingTimeSelector *mts = E_MEETING_TIME_SELECTOR (data); - - /* Redraw */ - gtk_widget_queue_draw (mts->display_top); - gtk_widget_queue_draw (mts->display_main); -} diff --git a/calendar/gui/e-meeting-time-sel.h b/calendar/gui/e-meeting-time-sel.h index cbfffb6a25..ee23dbe672 100644 --- a/calendar/gui/e-meeting-time-sel.h +++ b/calendar/gui/e-meeting-time-sel.h @@ -31,11 +31,10 @@ #include <gal/e-text/e-text.h> #include <gal/e-table/e-table-model.h> #include <gal/e-table/e-table.h> -#include "e-meeting-model.h" +#include "e-meeting-store.h" +#include "e-meeting-list-view.h" -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ +G_BEGIN_DECLS /* * EMeetingTimeSelector displays a list of attendees for a meeting and a @@ -121,8 +120,8 @@ struct _EMeetingTimeSelector GtkWidget *attendees_vbox_spacer; /* The etable and model */ - EMeetingModel *model; - GtkWidget *etable; + EMeetingStore *model; + EMeetingListView *list_view; /* The canvas displaying the dates, times, and the summary 'All Attendees' free/busy display. */ @@ -265,8 +264,8 @@ struct _EMeetingTimeSelectorClass */ GtkType e_meeting_time_selector_get_type (void); -GtkWidget* e_meeting_time_selector_new (EMeetingModel *emm); -void e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingModel *emm); +GtkWidget* e_meeting_time_selector_new (EMeetingStore *ems); +void e_meeting_time_selector_construct (EMeetingTimeSelector * mts, EMeetingStore *ems); /* This returns the currently selected meeting time. Note that months are 1-12 and days are 1-31. The start time is guaranteed to @@ -375,9 +374,6 @@ gchar* e_meeting_time_selector_dump_time (EMeetingTime*mtstime); gchar* e_meeting_time_selector_dump_date (GDate *date); #endif /* E_MEETING_TIME_SELECTOR_DEBUG */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ +G_END_DECLS #endif /* _E_MEETING_TIME_SELECTOR_H_ */ diff --git a/calendar/gui/e-select-names-editable.c b/calendar/gui/e-select-names-editable.c new file mode 100644 index 0000000000..2fcc93397a --- /dev/null +++ b/calendar/gui/e-select-names-editable.c @@ -0,0 +1,228 @@ +/* + * e-select-names-editable.c + * + * Author: Mike Kestner <mkestner@ximian.com> + * + * Copyright (C) 2003 Ximian Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtkcelleditable.h> +#include <bonobo/bonobo-exception.h> +#include <bonobo/bonobo-widget.h> +#include <ebook/e-destination.h> + +#include "e-select-names-editable.h" +#include "Evolution-Addressbook-SelectNames.h" + +#define SELECT_NAMES_OAFIID "OAFIID:GNOME_Evolution_Addressbook_SelectNames" + +struct _ESelectNamesEditablePriv { + GNOME_Evolution_Addressbook_SelectNames select_names; + Bonobo_Control control; + Bonobo_PropertyBag bag; +}; + +static BonoboWidgetClass *parent_class; + +static void +esne_start_editing (GtkCellEditable *cell_editable, GdkEvent *event) +{ + ESelectNamesEditable *esne = E_SELECT_NAMES_EDITABLE (cell_editable); + +} + +static void +esne_cell_editable_init (GtkCellEditableIface *iface) +{ + iface->start_editing = esne_start_editing; +} + +static void +esne_finalize (GObject *obj) +{ + ESelectNamesEditable *esne = (ESelectNamesEditable *) obj; + + if (esne->priv->select_names != CORBA_OBJECT_NIL) + bonobo_object_release_unref (esne->priv->select_names, NULL); + esne->priv->select_names = CORBA_OBJECT_NIL; + + if (esne->priv->bag != CORBA_OBJECT_NIL) + bonobo_object_release_unref (esne->priv->bag, NULL); + esne->priv->bag = CORBA_OBJECT_NIL; + + g_free (esne->priv); + + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +esne_init (ESelectNamesEditable *esne) +{ + esne->priv = g_new0 (ESelectNamesEditablePriv, 1); + + esne->priv->select_names = CORBA_OBJECT_NIL; + esne->priv->control = CORBA_OBJECT_NIL; + esne->priv->bag = CORBA_OBJECT_NIL; +} + +static void +esne_class_init (GObjectClass *klass) +{ + klass->finalize = esne_finalize; + + parent_class = BONOBO_WIDGET_CLASS (g_type_class_peek_parent (klass)); +} + +GType +e_select_names_editable_get_type (void) +{ + static GType esne_type = 0; + + if (!esne_type) { + static const GTypeInfo esne_info = { + sizeof (ESelectNamesEditableClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) esne_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (ESelectNamesEditable), + 0, /* n_preallocs */ + (GInstanceInitFunc) esne_init, + }; + + static const GInterfaceInfo cell_editable_info = { + (GInterfaceInitFunc) esne_cell_editable_init, + NULL, + NULL + }; + + esne_type = g_type_register_static (BONOBO_TYPE_WIDGET, "ESelectNamesEditable", &esne_info, 0); + + g_type_add_interface_static (esne_type, GTK_TYPE_CELL_EDITABLE, &cell_editable_info); + } + + return esne_type; +} + +static void +entry_activate (BonoboListener *listener, const char *event_name, const CORBA_any *arg, CORBA_Environment *ev, gpointer esne) +{ + gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (esne)); + gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (esne)); +} + +ESelectNamesEditable * +e_select_names_editable_construct (ESelectNamesEditable *esne) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + esne->priv->select_names = bonobo_activation_activate_from_id (SELECT_NAMES_OAFIID, 0, NULL, &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return NULL; + } + + GNOME_Evolution_Addressbook_SelectNames_addSection (esne->priv->select_names, "A", "A", &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return NULL; + } + + esne->priv->control = GNOME_Evolution_Addressbook_SelectNames_getEntryBySection ( + esne->priv->select_names, "A", &ev); + if (BONOBO_EX (&ev)) { + CORBA_exception_free (&ev); + return NULL; + } + + bonobo_widget_construct_control_from_objref (BONOBO_WIDGET (esne), esne->priv->control, CORBA_OBJECT_NIL, &ev); + + CORBA_exception_free (&ev); + + esne->priv->bag = bonobo_control_frame_get_control_property_bag ( + bonobo_widget_get_control_frame (BONOBO_WIDGET (esne)), NULL); + bonobo_event_source_client_add_listener (esne->priv->bag, entry_activate, + "GNOME/Evolution/Addressbook/SelectNames:activate:entry", + NULL, esne); + + return esne; +} + +ESelectNamesEditable * +e_select_names_editable_new () +{ + ESelectNamesEditable *esne = g_object_new (E_TYPE_SELECT_NAMES_EDITABLE, NULL); + + if (!esne) + return NULL; + + if (!e_select_names_editable_construct (esne)) { + g_object_unref (esne); + return NULL; + } + + return esne; +} + +gchar * +e_select_names_editable_get_address (ESelectNamesEditable *esne) +{ + EDestination **dest; + gchar *dest_str; + gchar *result; + + g_return_val_if_fail (E_SELECT_NAMES_EDITABLE (esne), NULL); + + dest_str = bonobo_pbclient_get_string (esne->priv->bag, "destinations", NULL); + dest = e_destination_importv (dest_str); + result = g_strdup (e_destination_get_email (*dest)); + e_destination_freev (dest); + + return result; +} + +gchar * +e_select_names_editable_get_name (ESelectNamesEditable *esne) +{ + EDestination **dest; + gchar *dest_str; + gchar *result; + + g_return_val_if_fail (E_SELECT_NAMES_EDITABLE (esne), NULL); + + dest_str = bonobo_pbclient_get_string (esne->priv->bag, "destinations", NULL); + dest = e_destination_importv (dest_str); + result = g_strdup (e_destination_get_name (*dest)); + e_destination_freev (dest); + + return result; +} + +void +e_select_names_editable_set_address (ESelectNamesEditable *esne, const gchar *text) +{ + g_return_if_fail (E_IS_SELECT_NAMES_EDITABLE (esne)); + + bonobo_pbclient_set_string (esne->priv->bag, "addresses", text, NULL); +} + diff --git a/calendar/gui/e-select-names-editable.h b/calendar/gui/e-select-names-editable.h new file mode 100644 index 0000000000..867118fe9e --- /dev/null +++ b/calendar/gui/e-select-names-editable.h @@ -0,0 +1,64 @@ +/* + * e-select-names-editable.h + * + * Author: Mike Kestner <mkestner@ximian.com> + * + * Copyright (C) 2003 Ximian Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __E_SELECT_NAMES_EDITABLE_H__ +#define __E_SELECT_NAMES_EDITABLE_H__ + +#include <bonobo/bonobo-widget.h> + +G_BEGIN_DECLS + +#define E_TYPE_SELECT_NAMES_EDITABLE (e_select_names_editable_get_type ()) +#define E_SELECT_NAMES_EDITABLE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_SELECT_NAMES_EDITABLE, ESelectNamesEditable)) +#define E_SELECT_NAMES_EDITABLE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), E_TYPE_SELECT_NAMES_EDITABLE, ESelectNamesEditableClass)) +#define E_IS_SELECT_NAMES_EDITABLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_SELECT_NAMES_EDITABLE)) +#define E_IS_SELECT_NAMES_EDITABLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((o), E_TYPE_SELECT_NAMES_EDITABLE)) +#define E_SELECT_NAMES_EDITABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_TYPE_SELECT_NAMES_EDITABLE, ESelectNamesEditableClass)) + +typedef struct _ESelectNamesEditable ESelectNamesEditable; +typedef struct _ESelectNamesEditableClass ESelectNamesEditableClass; +typedef struct _ESelectNamesEditablePriv ESelectNamesEditablePriv; + +struct _ESelectNamesEditable +{ + BonoboWidget parent; + + ESelectNamesEditablePriv *priv; +}; + +struct _ESelectNamesEditableClass +{ + BonoboWidgetClass parent_class; +}; + +GType e_select_names_editable_get_type (void); + +ESelectNamesEditable *e_select_names_editable_construct (ESelectNamesEditable *esne); +ESelectNamesEditable *e_select_names_editable_new (void); + +gchar *e_select_names_editable_get_address (ESelectNamesEditable *esne); +void e_select_names_editable_set_address (ESelectNamesEditable *esne, const gchar *text); + +gchar *e_select_names_editable_get_name (ESelectNamesEditable *esne); +G_END_DECLS + +#endif /* __E_SELECT_NAMES_EDITABLE_H__ */ diff --git a/calendar/gui/e-select-names-renderer.c b/calendar/gui/e-select-names-renderer.c new file mode 100644 index 0000000000..9b325d6ac0 --- /dev/null +++ b/calendar/gui/e-select-names-renderer.c @@ -0,0 +1,176 @@ +/* + * e-select-names-renderer.c + * + * Author: Mike Kestner <mkestner@ximian.com> + * + * Copyright (C) 2003 Ximian Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <gtk/gtkcellrenderertext.h> +#include <gal/util/e-util.h> + +#include "cal-util/cal-util-marshal.h" + +#include "e-select-names-editable.h" +#include "e-select-names-renderer.h" + + +struct _ESelectNamesRendererPriv { + ESelectNamesEditable *editable; + gchar *path; + gchar *address; +}; + +enum { + PROP_0, + PROP_ADDRESS +}; + +enum { + CELL_EDITED, + LAST_SIGNAL +}; + +static GtkCellRendererTextClass *parent_class; +static gint signals [LAST_SIGNAL]; + +static void +esnr_editing_done (GtkCellEditable *editable, ESelectNamesRenderer *cell) +{ + gchar *new_address, *new_name; + + new_address = e_select_names_editable_get_address (E_SELECT_NAMES_EDITABLE (editable)); + new_name = e_select_names_editable_get_name (E_SELECT_NAMES_EDITABLE (editable)); + + g_signal_emit (cell, signals [CELL_EDITED], 0, cell->priv->path, new_address, new_name); + g_free (new_address); + g_free (new_name); + g_free (cell->priv->path); + cell->priv->path = NULL; +} + +static GtkCellEditable * +esnr_start_editing (GtkCellRenderer *cell, GdkEvent *event, GtkWidget *widget, const gchar *path, + GdkRectangle *bg_area, GdkRectangle *cell_area, GtkCellRendererState flags) +{ + ESelectNamesRenderer *sn_cell = E_SELECT_NAMES_RENDERER (cell); + GtkCellRendererText *text_cell = GTK_CELL_RENDERER_TEXT (cell); + ESelectNamesEditable *editable; + + if (!text_cell->editable) + return NULL; + + editable = E_SELECT_NAMES_EDITABLE (e_select_names_editable_new ()); + e_select_names_editable_set_address (editable, sn_cell->priv->address); + gtk_widget_show (GTK_WIDGET (editable)); + + g_signal_connect (editable, "editing-done", G_CALLBACK (esnr_editing_done), sn_cell); + + sn_cell->priv->editable = g_object_ref (editable); + sn_cell->priv->path = g_strdup (path); + + return GTK_CELL_EDITABLE (editable); +} + +static void +esnr_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + ESelectNamesRenderer *esnr = E_SELECT_NAMES_RENDERER (object); + + switch (prop_id) { + case PROP_ADDRESS: + g_value_set_string (value, esnr->priv->address); + break; + default: + break; + } +} + +static void +esnr_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + ESelectNamesRenderer *esnr = E_SELECT_NAMES_RENDERER (object); + + switch (prop_id) { + case PROP_ADDRESS: + g_free (esnr->priv->address); + esnr->priv->address = g_strdup (g_value_get_string (value)); + break; + default: + break; + } +} + +static void +esnr_finalize (GObject *obj) +{ + ESelectNamesRenderer *cell = (ESelectNamesRenderer *) obj; + + if (cell->priv->editable) + g_object_unref (cell->priv->editable); + cell->priv->editable = NULL; + + g_free (cell->priv->path); + g_free (cell->priv->address); + g_free (cell->priv); + + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +esnr_init (ESelectNamesRenderer *cell) +{ + cell->priv = g_new0 (ESelectNamesRendererPriv, 1); +} + +static void +esnr_class_init (ESelectNamesRendererClass *class) +{ + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); + GObjectClass *obj_class = G_OBJECT_CLASS (class); + + parent_class = GTK_CELL_RENDERER_TEXT_CLASS (g_type_class_peek_parent (class)); + + obj_class->finalize = esnr_finalize; + obj_class->get_property = esnr_get_property; + obj_class->set_property = esnr_set_property; + + cell_class->start_editing = esnr_start_editing; + + g_object_class_install_property (obj_class, PROP_ADDRESS, + g_param_spec_string ("address", "Address", "Email address.", NULL, G_PARAM_READWRITE)); + + signals [CELL_EDITED] = g_signal_new ("cell_edited", + G_OBJECT_CLASS_TYPE (obj_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESelectNamesRendererClass, cell_edited), + NULL, NULL, + cal_util_marshal_VOID__STRING_STRING_STRING, + G_TYPE_NONE, 3, + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); +} + +E_MAKE_TYPE (e_select_names_renderer, "ESelectNamesRenderer", ESelectNamesRenderer, esnr_class_init, esnr_init, GTK_TYPE_CELL_RENDERER_TEXT) + +GtkCellRenderer * +e_select_names_renderer_new (void) +{ + return GTK_CELL_RENDERER (g_object_new (E_TYPE_SELECT_NAMES_RENDERER, NULL)); +} + diff --git a/calendar/gui/e-select-names-renderer.h b/calendar/gui/e-select-names-renderer.h new file mode 100644 index 0000000000..fb045d928f --- /dev/null +++ b/calendar/gui/e-select-names-renderer.h @@ -0,0 +1,63 @@ +/* + * e-select-names-renderer.h + * + * Author: Mike Kestner <mkestner@ximian.com> + * + * Copyright (C) 2003 Ximian Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __E_SELECT_NAMES_RENDERER_H__ +#define __E_SELECT_NAMES_RENDERER_H__ + +#include <gtk/gtkcellrenderertext.h> + +G_BEGIN_DECLS + +#define E_TYPE_SELECT_NAMES_RENDERER (e_select_names_renderer_get_type ()) +#define E_SELECT_NAMES_RENDERER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), E_TYPE_SELECT_NAMES_RENDERER, ESelectNamesRenderer)) +#define E_SELECT_NAMES_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), E_TYPE_SELECT_NAMES_RENDERER, ESelectNamesRendererClass)) +#define E_IS_SELECT_NAMES_RENDERER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_SELECT_NAMES_RENDERER)) +#define E_IS_SELECT_NAMES_RENDERER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((o), E_TYPE_SELECT_NAMES_RENDERER)) +#define E_SELECT_NAMES_RENDERER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), E_TYPE_SELECT_NAMES_RENDERER, ESelectNamesRendererClass)) + +typedef struct _ESelectNamesRenderer ESelectNamesRenderer; +typedef struct _ESelectNamesRendererClass ESelectNamesRendererClass; +typedef struct _ESelectNamesRendererPriv ESelectNamesRendererPriv; + +struct _ESelectNamesRenderer +{ + GtkCellRendererText parent; + + ESelectNamesRendererPriv *priv; +}; + +struct _ESelectNamesRendererClass +{ + GtkCellRendererTextClass parent_class; + + void (* cell_edited) (ESelectNamesRenderer *renderer, + const gchar *path, + const gchar *address, + const gchar *name); +}; + +GType e_select_names_renderer_get_type (void); +GtkCellRenderer *e_select_names_renderer_new (void); + +G_END_DECLS + +#endif /* __E_SELECT_NAMES_RENDERER_H__ */ |