No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-meeting-list-view.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-meeting-list-view.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Mike Kestner <mkestner@ximian.com>
18 * JP Rosevear <jpr@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <libecal/libecal.h>
32 #include <libebook/libebook.h>
33 #include <libedataserverui/libedataserverui.h>
34
35 #include "calendar-config.h"
36 #include "e-meeting-list-view.h"
37 #include "itip-utils.h"
38 #include <shell/e-shell.h>
39 #include "e-select-names-renderer.h"
40
41 #define E_MEETING_LIST_VIEW_GET_PRIVATE(obj) \
42 (G_TYPE_INSTANCE_GET_PRIVATE \
43 ((obj), E_TYPE_MEETING_LIST_VIEW, EMeetingListViewPrivate))
44
45 struct _EMeetingListViewPrivate {
46 EMeetingStore *store;
47
48 ENameSelector *name_selector;
49
50 GHashTable *renderers;
51 };
52
53 #define BUF_SIZE 1024
54
55 /* Signal IDs */
56 enum {
57 ATTENDEE_ADDED,
58 LAST_SIGNAL
59 };
60
61 static guint e_meeting_list_view_signals[LAST_SIGNAL] = { 0 };
62
63 static void name_selector_dialog_close_cb (ENameSelectorDialog *dialog, gint response, gpointer data);
64
65 static const gchar *sections[] = {N_("Chair Persons"),
66 N_("Required Participants"),
67 N_("Optional Participants"),
68 N_("Resources"),
69 NULL};
70
71 static icalparameter_role roles[] = {ICAL_ROLE_CHAIR,
72 ICAL_ROLE_REQPARTICIPANT,
73 ICAL_ROLE_OPTPARTICIPANT,
74 ICAL_ROLE_NONPARTICIPANT,
75 ICAL_ROLE_NONE};
76
77 G_DEFINE_TYPE (EMeetingListView, e_meeting_list_view, GTK_TYPE_TREE_VIEW)
78
79 static void
80 e_meeting_list_view_finalize (GObject *object)
81 {
82 EMeetingListViewPrivate *priv;
83
84 priv = E_MEETING_LIST_VIEW_GET_PRIVATE (object);
85
86 if (priv->name_selector) {
87 e_name_selector_cancel_loading (priv->name_selector);
88 g_object_unref (priv->name_selector);
89 priv->name_selector = NULL;
90 }
91
92 if (priv->renderers) {
93 g_hash_table_destroy (priv->renderers);
94 priv->renderers = NULL;
95 }
96
97 /* Chain up to parent's finalize() method. */
98 G_OBJECT_CLASS (e_meeting_list_view_parent_class)->finalize (object);
99 }
100
101 static void
102 e_meeting_list_view_class_init (EMeetingListViewClass *class)
103 {
104 GObjectClass *object_class;
105
106 g_type_class_add_private (class, sizeof (EMeetingListViewPrivate));
107
108 object_class = G_OBJECT_CLASS (class);
109 object_class->finalize = e_meeting_list_view_finalize;
110
111 e_meeting_list_view_signals[ATTENDEE_ADDED] = g_signal_new (
112 "attendee_added",
113 G_TYPE_FROM_CLASS (class),
114 G_SIGNAL_RUN_LAST,
115 G_STRUCT_OFFSET (EMeetingListViewClass, attendee_added),
116 NULL, NULL,
117 g_cclosure_marshal_VOID__POINTER,
118 G_TYPE_NONE, 1,
119 G_TYPE_POINTER);
120 }
121
122 static void
123 add_section (ENameSelector *name_selector,
124 const gchar *name)
125 {
126 ENameSelectorModel *name_selector_model;
127
128 name_selector_model = e_name_selector_peek_model (name_selector);
129 e_name_selector_model_add_section (name_selector_model, name, gettext (name), NULL);
130 }
131
132 static void
133 meeting_list_view_realize_cb (EMeetingListView *view)
134 {
135 g_return_if_fail (view != NULL);
136 g_return_if_fail (view->priv != NULL);
137
138 g_signal_handlers_disconnect_by_func (view, meeting_list_view_realize_cb, NULL);
139
140 e_name_selector_load_books (view->priv->name_selector);
141 }
142
143 static void
144 e_meeting_list_view_init (EMeetingListView *view)
145 {
146 ENameSelectorDialog *name_selector_dialog;
147 ESourceRegistry *registry;
148 EShell *shell;
149 gint i;
150
151 view->priv = E_MEETING_LIST_VIEW_GET_PRIVATE (view);
152
153 view->priv->renderers = g_hash_table_new (g_direct_hash, g_int_equal);
154
155 /* FIXME Refactor this so we don't need e_shell_get_default(). */
156 shell = e_shell_get_default ();
157 registry = e_shell_get_registry (shell);
158
159 view->priv->name_selector = e_name_selector_new (registry);
160
161 for (i = 0; sections[i]; i++)
162 add_section (view->priv->name_selector, sections[i]);
163
164 name_selector_dialog =
165 e_name_selector_peek_dialog (view->priv->name_selector);
166 gtk_window_set_title (GTK_WINDOW (name_selector_dialog), _("Attendees"));
167 g_signal_connect (
168 name_selector_dialog, "response",
169 G_CALLBACK (name_selector_dialog_close_cb), view);
170
171 /* postpone name_selector loading, do that only when really needed */
172 g_signal_connect (
173 view, "realize",
174 G_CALLBACK (meeting_list_view_realize_cb), NULL);
175 }
176
177 static GList *
178 get_type_strings (void)
179 {
180 GList *strings = NULL;
181
182 strings = g_list_append (strings, (gchar *) _("Individual"));
183 strings = g_list_append (strings, (gchar *) _("Group"));
184 strings = g_list_append (strings, (gchar *) _("Resource"));
185 strings = g_list_append (strings, (gchar *) _("Room"));
186 strings = g_list_append (strings, (gchar *) _("Unknown"));
187
188 return strings;
189 }
190
191 static GList *
192 get_role_strings (void)
193 {
194 GList *strings = NULL;
195
196 strings = g_list_append (strings, (gchar *) _("Chair"));
197 strings = g_list_append (strings, (gchar *) _("Required Participant"));
198 strings = g_list_append (strings, (gchar *) _("Optional Participant"));
199 strings = g_list_append (strings, (gchar *) _("Non-Participant"));
200 strings = g_list_append (strings, (gchar *) _("Unknown"));
201
202 return strings;
203 }
204
205 static GList *
206 get_rsvp_strings (void)
207 {
208 GList *strings = NULL;
209
210 strings = g_list_append (strings, (gchar *) _("Yes"));
211 strings = g_list_append (strings, (gchar *) _("No"));
212
213 return strings;
214 }
215
216 static GList *
217 get_status_strings (void)
218 {
219 GList *strings = NULL;
220
221 strings = g_list_append (strings, (gchar *) _("Needs Action"));
222 strings = g_list_append (strings, (gchar *) _("Accepted"));
223 strings = g_list_append (strings, (gchar *) _("Declined"));
224 strings = g_list_append (strings, (gchar *) _("Tentative"));
225 strings = g_list_append (strings, (gchar *) _("Delegated"));
226
227 return strings;
228 }
229
230 static void
231 value_edited (GtkTreeView *view,
232 gint col,
233 const gchar *path,
234 const gchar *text)
235 {
236 EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
237 GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
238 gint row = gtk_tree_path_get_indices (treepath)[0];
239
240 e_meeting_store_set_value (model, row, col, text);
241 gtk_tree_path_free (treepath);
242 }
243
244 static guint
245 get_index_from_role (icalparameter_role role)
246 {
247 switch (role) {
248 case ICAL_ROLE_CHAIR:
249 return 0;
250 case ICAL_ROLE_REQPARTICIPANT:
251 return 1;
252 case ICAL_ROLE_OPTPARTICIPANT:
253 return 2;
254 case ICAL_ROLE_NONPARTICIPANT:
255 return 3;
256 default:
257 return 1;
258 }
259 }
260
261 void
262 e_meeting_list_view_add_attendee_to_name_selector (EMeetingListView *view,
263 EMeetingAttendee *ma)
264 {
265 EDestinationStore *destination_store;
266 ENameSelectorModel *name_selector_model;
267 EDestination *des;
268 EMeetingListViewPrivate *priv;
269 guint i = 1;
270
271 priv = view->priv;
272
273 name_selector_model = e_name_selector_peek_model (priv->name_selector);
274 i = get_index_from_role (e_meeting_attendee_get_role (ma));
275 e_name_selector_model_peek_section (
276 name_selector_model, sections[i],
277 NULL, &destination_store);
278 des = e_destination_new ();
279 e_destination_set_email (des, itip_strip_mailto (e_meeting_attendee_get_address (ma)));
280 e_destination_set_name (des, e_meeting_attendee_get_cn (ma));
281 e_destination_store_append_destination (destination_store, des);
282 g_object_unref (des);
283 }
284
285 void
286 e_meeting_list_view_remove_attendee_from_name_selector (EMeetingListView *view,
287 EMeetingAttendee *ma)
288 {
289 GList *destinations, *l;
290 EDestinationStore *destination_store;
291 ENameSelectorModel *name_selector_model;
292 const gchar *madd = NULL;
293 EMeetingListViewPrivate *priv;
294 guint i = 1;
295
296 priv = view->priv;
297
298 name_selector_model = e_name_selector_peek_model (priv->name_selector);
299 i = get_index_from_role (e_meeting_attendee_get_role (ma));
300 e_name_selector_model_peek_section (
301 name_selector_model, sections[i],
302 NULL, &destination_store);
303 destinations = e_destination_store_list_destinations (destination_store);
304 madd = itip_strip_mailto (e_meeting_attendee_get_address (ma));
305
306 for (l = destinations; l; l = g_list_next (l)) {
307 const gchar *attendee = NULL;
308 EDestination *des = l->data;
309
310 if (e_destination_is_evolution_list (des)) {
311 GList *l, *dl;
312
313 dl = (GList *) e_destination_list_get_dests (des);
314
315 for (l = dl; l; l = l->next) {
316 attendee = e_destination_get_email (l->data);
317 if (madd && attendee && g_str_equal (madd, attendee)) {
318 g_object_unref (l->data);
319 l = g_list_remove (l, l->data);
320 break;
321 }
322 }
323 } else {
324 attendee = e_destination_get_email (des);
325 if (madd && attendee && g_str_equal (madd, attendee)) {
326 e_destination_store_remove_destination (destination_store, des);
327 }
328 }
329 }
330
331 g_list_free (destinations);
332 }
333
334 void
335 e_meeting_list_view_remove_all_attendees_from_name_selector (EMeetingListView *view)
336 {
337 ENameSelectorModel *name_selector_model;
338 EMeetingListViewPrivate *priv;
339 guint i;
340
341 priv = view->priv;
342
343 name_selector_model = e_name_selector_peek_model (priv->name_selector);
344
345 for (i = 0; sections[i] != NULL; i++) {
346 EDestinationStore *destination_store = NULL;
347 GList *destinations = NULL, *l = NULL;
348
349 e_name_selector_model_peek_section (
350 name_selector_model, sections[i],
351 NULL, &destination_store);
352 if (!destination_store) {
353 g_warning ("destination store is NULL\n");
354 continue;
355 }
356
357 destinations = e_destination_store_list_destinations (destination_store);
358 for (l = destinations; l; l = g_list_next (l)) {
359 EDestination *des = l->data;
360
361 if (e_destination_is_evolution_list (des)) {
362 GList *m, *dl;
363
364 dl = (GList *) e_destination_list_get_dests (des);
365
366 for (m = dl; m; m = m->next) {
367 g_object_unref (m->data);
368 m = g_list_remove (m, l->data);
369 }
370 } else {
371 e_destination_store_remove_destination (destination_store, des);
372 }
373 }
374 g_list_free (destinations);
375 }
376 }
377
378 static void
379 attendee_edited_cb (GtkCellRenderer *renderer,
380 const gchar *path,
381 GList *addresses,
382 GList *names,
383 GtkTreeView *view)
384 {
385 EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
386 GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
387 gint row = gtk_tree_path_get_indices (treepath)[0];
388 EMeetingAttendee *existing_attendee;
389
390 existing_attendee = e_meeting_store_find_attendee_at_row (model, row);
391
392 if (g_list_length (addresses) > 1) {
393 EMeetingAttendee *attendee;
394 GList *l, *m;
395 gboolean can_remove = TRUE;
396
397 for (l = addresses, m = names; l && m; l = l->next, m = m->next) {
398 gchar *name = m->data, *email = l->data;
399
400 if (!((name && *name) || (email && *email)))
401 continue;
402
403 attendee = e_meeting_store_find_attendee (model, email, NULL);
404 if (attendee != NULL) {
405 if (attendee == existing_attendee)
406 can_remove = FALSE;
407 continue;
408 }
409
410 attendee = e_meeting_store_add_attendee_with_defaults (model);
411 e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", (gchar *) l->data));
412 e_meeting_attendee_set_cn (attendee, g_strdup (m->data));
413 if (existing_attendee) {
414 /* FIXME Should we copy anything else? */
415 e_meeting_attendee_set_cutype (attendee, e_meeting_attendee_get_cutype (existing_attendee));
416 e_meeting_attendee_set_role (attendee, e_meeting_attendee_get_role (existing_attendee));
417 e_meeting_attendee_set_rsvp (attendee, e_meeting_attendee_get_rsvp (existing_attendee));
418 e_meeting_attendee_set_status (attendee, ICAL_PARTSTAT_NEEDSACTION);
419 e_meeting_attendee_set_delfrom (attendee, (gchar *) e_meeting_attendee_get_delfrom (existing_attendee));
420 }
421 e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee);
422 g_signal_emit_by_name (view, "attendee_added", (gpointer) attendee);
423 }
424
425 if (existing_attendee && can_remove) {
426 e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
427 e_meeting_store_remove_attendee (model, existing_attendee);
428 }
429 } else if (g_list_length (addresses) == 1) {
430 gchar *name = names->data, *email = addresses->data;
431 gint existing_row;
432
433 if (!((name && *name) || (email && *email)) || ((e_meeting_store_find_attendee (model, email, &existing_row) != NULL) && existing_row != row)) {
434 if (existing_attendee) {
435 e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
436 e_meeting_store_remove_attendee (model, existing_attendee);
437 }
438 } else {
439 gboolean address_changed = FALSE;
440 EMeetingAttendee *attendee;
441
442 if (existing_attendee) {
443 const gchar *addr = e_meeting_attendee_get_address (existing_attendee);
444
445 if (addr && g_ascii_strncasecmp (addr, "MAILTO:", 7) == 0)
446 addr += 7;
447
448 address_changed = addr && g_ascii_strcasecmp (addr, email) != 0;
449
450 e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
451 attendee = existing_attendee;
452 } else {
453 attendee = e_meeting_store_add_attendee_with_defaults (model);
454 }
455
456 value_edited (view, E_MEETING_STORE_ADDRESS_COL, path, email);
457 value_edited (view, E_MEETING_STORE_CN_COL, path, name);
458
459 e_meeting_attendee_set_address (attendee, g_strdup_printf ("MAILTO:%s", email));
460 e_meeting_attendee_set_cn (attendee, g_strdup (name));
461 e_meeting_attendee_set_role (attendee, ICAL_ROLE_REQPARTICIPANT);
462 e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee);
463
464 if (address_changed)
465 e_meeting_attendee_set_status (attendee, ICAL_PARTSTAT_NEEDSACTION);
466
467 g_signal_emit_by_name (view, "attendee_added", (gpointer) attendee);
468 }
469 } else if (existing_attendee) {
470 const gchar *address = e_meeting_attendee_get_address (existing_attendee);
471
472 if (!(address && *address)) {
473 e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), existing_attendee);
474 e_meeting_store_remove_attendee (model, existing_attendee);
475 }
476 }
477
478 gtk_tree_path_free (treepath);
479 }
480
481 static void
482 attendee_editing_canceled_cb (GtkCellRenderer *renderer,
483 GtkTreeView *view)
484 {
485 EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
486 GtkTreePath *path;
487 EMeetingAttendee *existing_attendee;
488 gint row;
489
490 /* This is for newly added attendees when the editing is cancelled */
491 gtk_tree_view_get_cursor (view, &path, NULL);
492 if (!path)
493 return;
494
495 row = gtk_tree_path_get_indices (path)[0];
496 existing_attendee = e_meeting_store_find_attendee_at_row (model, row);
497 if (existing_attendee) {
498 if (!e_meeting_attendee_is_set_cn (existing_attendee) && !e_meeting_attendee_is_set_address (existing_attendee))
499 e_meeting_store_remove_attendee (model, existing_attendee);
500 }
501
502 gtk_tree_path_free (path);
503 }
504
505 static void
506 type_edited_cb (GtkCellRenderer *renderer,
507 const gchar *path,
508 const gchar *text,
509 GtkTreeView *view)
510 {
511 value_edited (view, E_MEETING_STORE_TYPE_COL, path, text);
512 }
513
514 static void
515 role_edited_cb (GtkCellRenderer *renderer,
516 const gchar *path,
517 const gchar *text,
518 GtkTreeView *view)
519 {
520 /* This is a little more complex than the other callbacks because
521 * we also need to update the "Required Participants" dialog. */
522
523 EMeetingStore *model = E_MEETING_STORE (gtk_tree_view_get_model (view));
524 GtkTreePath *treepath = gtk_tree_path_new_from_string (path);
525 gint row = gtk_tree_path_get_indices (treepath)[0];
526 EMeetingAttendee *attendee;
527
528 attendee = e_meeting_store_find_attendee_at_row (model, row);
529 e_meeting_list_view_remove_attendee_from_name_selector (E_MEETING_LIST_VIEW (view), attendee);
530 e_meeting_store_set_value (model, row, E_MEETING_STORE_ROLE_COL, text);
531 e_meeting_list_view_add_attendee_to_name_selector (E_MEETING_LIST_VIEW (view), attendee);
532
533 gtk_tree_path_free (treepath);
534 }
535
536 static void
537 rsvp_edited_cb (GtkCellRenderer *renderer,
538 const gchar *path,
539 const gchar *text,
540 GtkTreeView *view)
541 {
542 value_edited (view, E_MEETING_STORE_RSVP_COL, path, text);
543 }
544
545 static void
546 status_edited_cb (GtkCellRenderer *renderer,
547 const gchar *path,
548 const gchar *text,
549 GtkTreeView *view)
550 {
551 value_edited (view, E_MEETING_STORE_STATUS_COL, path, text);
552 }
553
554 static void
555 ense_update (GtkWidget *w,
556 gpointer data1,
557 gpointer user_data)
558 {
559 gtk_cell_editable_editing_done ((GtkCellEditable *) w);
560 }
561
562 static void
563 editing_started_cb (GtkCellRenderer *renderer,
564 GtkCellEditable *editable,
565 gchar *path,
566 gpointer user_data)
567 {
568 g_signal_connect (
569 editable, "updated",
570 G_CALLBACK (ense_update), NULL);
571 }
572
573 static GtkCellRenderer *
574 create_combo_cell_renderer (GList *strings)
575 {
576 GList *li;
577 GtkTreeIter iter;
578 GtkListStore *store;
579 GtkCellRenderer *renderer;
580
581 store = gtk_list_store_new (1, G_TYPE_STRING);
582 for (li = strings; li; li = li->next) {
583 const gchar *str = li->data;
584
585 gtk_list_store_append (store, &iter);
586 gtk_list_store_set (store, &iter, 0, str, -1);
587 }
588
589 renderer = gtk_cell_renderer_combo_new ();
590
591 g_object_set (
592 G_OBJECT (renderer),
593 "has-entry", FALSE,
594 "editable", TRUE,
595 "model", GTK_TREE_MODEL (store),
596 "text-column", 0,
597 NULL);
598
599 g_object_unref (store);
600 g_list_free (strings);
601
602 return renderer;
603 }
604
605 static void
606 build_table (EMeetingListView *lview)
607 {
608 GtkCellRenderer *renderer;
609 GtkTreeView *view = GTK_TREE_VIEW (lview);
610 EMeetingListViewPrivate *priv;
611 GHashTable *edit_table;
612 GtkTreeViewColumn *col;
613 gint pos;
614
615 priv = lview->priv;
616 edit_table = priv->renderers;
617 gtk_tree_view_set_headers_visible (view, TRUE);
618 gtk_tree_view_set_rules_hint (view, TRUE);
619
620 renderer = e_select_names_renderer_new ();
621 g_object_set (renderer, "editable", TRUE, NULL);
622 /* The extra space is just a hack to occupy more space for Attendee */
623 pos = gtk_tree_view_insert_column_with_attributes (
624 view, -1, _("Attendee "), renderer,
625 "text", E_MEETING_STORE_ATTENDEE_COL,
626 "name", E_MEETING_STORE_CN_COL,
627 "email", E_MEETING_STORE_ADDRESS_COL,
628 "underline", E_MEETING_STORE_ATTENDEE_UNDERLINE_COL,
629 NULL);
630 col = gtk_tree_view_get_column (view, pos -1);
631 gtk_tree_view_column_set_resizable (col, TRUE);
632 gtk_tree_view_column_set_reorderable (col, TRUE);
633 gtk_tree_view_column_set_expand (col, TRUE);
634 g_object_set (col, "min-width", 50, NULL);
635 g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_ATTENDEE_COL));
636 g_signal_connect (
637 renderer, "cell_edited",
638 G_CALLBACK (attendee_edited_cb), view);
639 g_signal_connect (
640 renderer, "editing-canceled",
641 G_CALLBACK (attendee_editing_canceled_cb), view);
642 g_signal_connect (
643 renderer, "editing-started",
644 G_CALLBACK (editing_started_cb), view);
645
646 g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_ATTENDEE_COL), renderer);
647
648 renderer = create_combo_cell_renderer (get_type_strings ());
649 pos = gtk_tree_view_insert_column_with_attributes (
650 view, -1, _("Type"), renderer,
651 "text", E_MEETING_STORE_TYPE_COL,
652 NULL);
653 col = gtk_tree_view_get_column (view, pos -1);
654 gtk_tree_view_column_set_resizable (col, TRUE);
655 gtk_tree_view_column_set_reorderable (col, TRUE);
656 g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_TYPE_COL));
657 g_signal_connect (
658 renderer, "edited",
659 G_CALLBACK (type_edited_cb), view);
660 g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_TYPE_COL), renderer);
661
662 renderer = create_combo_cell_renderer (get_role_strings ());
663 pos = gtk_tree_view_insert_column_with_attributes (
664 view, -1, _("Role"), renderer,
665 "text", E_MEETING_STORE_ROLE_COL,
666 NULL);
667 col = gtk_tree_view_get_column (view, pos -1);
668 gtk_tree_view_column_set_resizable (col, TRUE);
669 gtk_tree_view_column_set_reorderable (col, TRUE);
670 g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_ROLE_COL));
671 g_signal_connect (
672 renderer, "edited",
673 G_CALLBACK (role_edited_cb), view);
674 g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_ROLE_COL), renderer);
675
676 renderer = create_combo_cell_renderer (get_rsvp_strings ());
677 /* To translators: RSVP means "please reply" */
678 pos = gtk_tree_view_insert_column_with_attributes (
679 view, -1, _("RSVP"), renderer,
680 "text", E_MEETING_STORE_RSVP_COL,
681 NULL);
682 col = gtk_tree_view_get_column (view, pos -1);
683 gtk_tree_view_column_set_resizable (col, TRUE);
684 gtk_tree_view_column_set_reorderable (col, TRUE);
685 g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_RSVP_COL));
686 g_signal_connect (
687 renderer, "edited",
688 G_CALLBACK (rsvp_edited_cb), view);
689 g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_RSVP_COL), renderer);
690
691 renderer = create_combo_cell_renderer (get_status_strings ());
692 pos = gtk_tree_view_insert_column_with_attributes (
693 view, -1, _("Status"), renderer,
694 "text", E_MEETING_STORE_STATUS_COL,
695 NULL);
696 col = gtk_tree_view_get_column (view, pos -1);
697 gtk_tree_view_column_set_resizable (col, TRUE);
698 gtk_tree_view_column_set_reorderable (col, TRUE);
699 g_object_set_data (G_OBJECT (col), "mtg-store-col", GINT_TO_POINTER (E_MEETING_STORE_STATUS_COL));
700 g_signal_connect (
701 renderer, "edited",
702 G_CALLBACK (status_edited_cb), view);
703 g_hash_table_insert (edit_table, GINT_TO_POINTER (E_MEETING_STORE_STATUS_COL), renderer);
704
705 priv->renderers = edit_table;
706 }
707
708 static void
709 change_edit_cols_for_user (gpointer key,
710 gpointer value,
711 gpointer user_data)
712 {
713 GtkCellRenderer *renderer = (GtkCellRenderer *) value;
714 gint key_val = GPOINTER_TO_INT (key);
715
716 switch (key_val) {
717 case E_MEETING_STORE_ATTENDEE_COL:
718 g_object_set (renderer, "editable", FALSE, NULL);
719 break;
720 case E_MEETING_STORE_ROLE_COL:
721 g_object_set (renderer, "editable", FALSE, NULL);
722 break;
723 case E_MEETING_STORE_TYPE_COL:
724 g_object_set (renderer, "editable", FALSE, NULL);
725 break;
726 case E_MEETING_STORE_RSVP_COL:
727 g_object_set (renderer, "editable", TRUE, NULL);
728 break;
729 case E_MEETING_STORE_STATUS_COL:
730 g_object_set (renderer, "editable", TRUE, NULL);
731 break;
732 }
733 }
734
735 static void
736 change_edit_cols_for_organizer (gpointer key,
737 gpointer value,
738 gpointer user_data)
739 {
740 GtkCellRenderer *renderer = (GtkCellRenderer *) value;
741 guint edit_level = GPOINTER_TO_INT (user_data);
742
743 g_object_set (renderer, "editable", GINT_TO_POINTER (edit_level), NULL);
744 }
745
746 static void
747 row_activated_cb (GtkTreeSelection *selection,
748 EMeetingListView *view)
749 {
750 EMeetingAttendee *existing_attendee;
751 EMeetingListViewPrivate *priv;
752 gint row;
753 EMeetingAttendeeEditLevel el;
754 gint edit_level;
755 GtkTreeModel *model;
756 GtkTreePath *path = NULL;
757 GList *paths = NULL;
758
759 priv = view->priv;
760
761 if (!(paths = gtk_tree_selection_get_selected_rows (selection, &model)))
762 return;
763 if (g_list_length (paths) > 1)
764 return;
765 path = g_list_nth_data (paths, 0);
766 if (!path)
767 return;
768
769 row = gtk_tree_path_get_indices (path)[0];
770 existing_attendee = e_meeting_store_find_attendee_at_row (priv->store, row);
771 el = e_meeting_attendee_get_edit_level (existing_attendee);
772
773 switch (el) {
774 case E_MEETING_ATTENDEE_EDIT_NONE:
775 edit_level = FALSE;
776 g_hash_table_foreach (
777 priv->renderers,
778 change_edit_cols_for_organizer,
779 GINT_TO_POINTER (edit_level));
780 break;
781
782 case E_MEETING_ATTENDEE_EDIT_FULL:
783 edit_level = TRUE;
784 g_hash_table_foreach (
785 priv->renderers,
786 change_edit_cols_for_organizer,
787 GINT_TO_POINTER (edit_level));
788 break;
789
790 case E_MEETING_ATTENDEE_EDIT_STATUS:
791 edit_level = FALSE;
792 g_hash_table_foreach (
793 priv->renderers,
794 change_edit_cols_for_user,
795 GINT_TO_POINTER (edit_level));
796 break;
797 }
798 }
799
800 EMeetingListView *
801 e_meeting_list_view_new (EMeetingStore *store)
802 {
803 EMeetingListView *view = g_object_new (E_TYPE_MEETING_LIST_VIEW, NULL);
804 GtkTreeSelection *selection;
805
806 if (view) {
807 view->priv->store = store;
808 gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (store));
809 build_table (view);
810 }
811
812 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
813 g_signal_connect (
814 selection, "changed",
815 G_CALLBACK (row_activated_cb), view);
816
817 return view;
818 }
819
820 void
821 e_meeting_list_view_column_set_visible (EMeetingListView *view,
822 EMeetingStoreColumns column,
823 gboolean visible)
824 {
825 GList *cols, *l;
826
827 cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (view));
828
829 for (l = cols; l; l = l->next) {
830 GtkTreeViewColumn *col = (GtkTreeViewColumn *) l->data;
831 EMeetingStoreColumns store_colum = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (col), "mtg-store-col"));
832
833 if (store_colum == column) {
834 gtk_tree_view_column_set_visible (col, visible);
835 break;
836 }
837 }
838 }
839
840 void
841 e_meeting_list_view_edit (EMeetingListView *emlv,
842 EMeetingAttendee *attendee)
843 {
844 EMeetingListViewPrivate *priv;
845 GtkTreePath *path;
846 GtkTreeViewColumn *focus_col;
847
848 priv = emlv->priv;
849
850 g_return_if_fail (emlv != NULL);
851 g_return_if_fail (E_IS_MEETING_LIST_VIEW (emlv));
852 g_return_if_fail (attendee != NULL);
853
854 path = e_meeting_store_find_attendee_path (priv->store, attendee);
855 focus_col = gtk_tree_view_get_column (GTK_TREE_VIEW (emlv), 0);
856
857 if (path) {
858 gtk_tree_view_set_cursor (GTK_TREE_VIEW (emlv), path, focus_col, TRUE);
859
860 gtk_tree_path_free (path);
861 }
862 }
863
864 static void
865 process_section (EMeetingListView *view,
866 GList *destinations,
867 icalparameter_role role,
868 GSList **la)
869 {
870 EMeetingListViewPrivate *priv;
871 GList *l;
872
873 priv = view->priv;
874 for (l = destinations; l; l = g_list_next (l)) {
875 EDestination *destination = l->data, *des = NULL;
876 const GList *list_dests = NULL, *l;
877 GList card_dest;
878
879 if (e_destination_is_evolution_list (destination)) {
880 list_dests = e_destination_list_get_dests (destination);
881 } else {
882 EContact *contact = e_destination_get_contact (destination);
883 /* check if the contact is contact list which is not expanded yet */
884 /* we expand it by getting the list again from the server forming the query */
885 if (contact && e_contact_get (contact , E_CONTACT_IS_LIST)) {
886 EBookClient *book_client = NULL;
887 ENameSelectorDialog *dialog;
888 ENameSelectorModel *model;
889 EContactStore *c_store;
890 GSList *clients, *l;
891 gchar *uid = e_contact_get (contact, E_CONTACT_BOOK_UID);
892
893 dialog = e_name_selector_peek_dialog (view->priv->name_selector);
894 model = e_name_selector_dialog_peek_model (dialog);
895 c_store = e_name_selector_model_peek_contact_store (model);
896 clients = e_contact_store_get_clients (c_store);
897
898 for (l = clients; l; l = l->next) {
899 EBookClient *b = l->data;
900 ESource *source;
901
902 source = e_client_get_source (E_CLIENT (b));
903
904 if (g_strcmp0 (uid, e_source_get_uid (source)) == 0) {
905 book_client = b;
906 break;
907 }
908 }
909
910 if (book_client) {
911 GSList *contacts;
912 EContact *n_con = NULL;
913 gchar *query;
914
915 query = g_strdup_printf (
916 "(is \"full_name\" \"%s\")",
917 (gchar *) e_contact_get (contact, E_CONTACT_FULL_NAME));
918
919 if (!e_book_client_get_contacts_sync (book_client, query, &contacts, NULL, NULL)) {
920 g_warning ("Could not get contact from the book \n");
921 g_free (query);
922 g_slist_free (clients);
923 return;
924 } else {
925 des = e_destination_new ();
926 n_con = contacts->data;
927
928 e_destination_set_contact (des, n_con, 0);
929 e_destination_set_client (des, book_client);
930 list_dests = e_destination_list_get_dests (des);
931
932 g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
933 g_slist_free (contacts);
934 }
935
936 g_free (query);
937 }
938 g_slist_free (clients);
939 } else {
940 card_dest.next = NULL;
941 card_dest.prev = NULL;
942 card_dest.data = destination;
943 list_dests = &card_dest;
944 }
945 }
946
947 for (l = list_dests; l; l = l->next) {
948 EDestination *dest = l->data;
949 EContact *contact;
950 const gchar *name, *attendee = NULL;
951 gchar *fburi = NULL;
952
953 name = e_destination_get_name (dest);
954 attendee = e_destination_get_email (dest);
955
956 if (attendee == NULL || *attendee == '\0')
957 continue;
958
959 contact = e_destination_get_contact (dest);
960 if (contact)
961 fburi = e_contact_get (contact, E_CONTACT_FREEBUSY_URL);
962
963 if (e_meeting_store_find_attendee (priv->store, attendee, NULL) == NULL) {
964 EMeetingAttendee *ia = e_meeting_store_add_attendee_with_defaults (priv->store);
965
966 e_meeting_attendee_set_address (ia, g_strdup_printf ("MAILTO:%s", attendee));
967 e_meeting_attendee_set_role (ia, role);
968 if (role == ICAL_ROLE_NONPARTICIPANT)
969 e_meeting_attendee_set_cutype (ia, ICAL_CUTYPE_RESOURCE);
970 e_meeting_attendee_set_cn (ia, g_strdup (name));
971
972 if (fburi)
973 e_meeting_attendee_set_fburi (ia, fburi);
974 } else {
975 if (g_slist_length (*la) == 1) {
976 g_slist_free (*la);
977 *la = NULL;
978 } else
979 *la = g_slist_remove_link (*la, g_slist_find_custom (*la, attendee, (GCompareFunc)g_ascii_strcasecmp));
980 }
981 }
982
983 if (des) {
984 g_object_unref (des);
985 des = NULL;
986 }
987
988 }
989 }
990
991 static void
992 add_to_list (gpointer data,
993 gpointer u_data)
994 {
995 GSList **user_data = u_data;
996
997 *user_data = g_slist_append (*user_data, (gpointer)itip_strip_mailto (e_meeting_attendee_get_address (data)));
998 }
999
1000 static void
1001 name_selector_dialog_close_cb (ENameSelectorDialog *dialog,
1002 gint response,
1003 gpointer data)
1004 {
1005 EMeetingListView *view = E_MEETING_LIST_VIEW (data);
1006 ENameSelectorModel *name_selector_model;
1007 EMeetingStore *store;
1008 const GPtrArray *attendees;
1009 gint i;
1010 GSList *la = NULL, *l;
1011
1012 name_selector_model = e_name_selector_peek_model (view->priv->name_selector);
1013 store = E_MEETING_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
1014 attendees = e_meeting_store_get_attendees (store);
1015
1016 /* get all the email ids of the attendees */
1017 g_ptr_array_foreach ((GPtrArray *) attendees, (GFunc) add_to_list, &la);
1018
1019 for (i = 0; sections[i] != NULL; i++) {
1020 EDestinationStore *destination_store;
1021 GList *destinations;
1022
1023 e_name_selector_model_peek_section (
1024 name_selector_model, sections[i],
1025 NULL, &destination_store);
1026 if (!destination_store) {
1027 g_warning ("destination store is NULL\n");
1028 continue;
1029 }
1030
1031 destinations = e_destination_store_list_destinations (destination_store);
1032 process_section (view, destinations, roles[i], &la);
1033 g_list_free (destinations);
1034 }
1035
1036 /* remove the deleted attendees from name selector */
1037 for (l = la; l != NULL; l = l->next) {
1038 EMeetingAttendee *ma = NULL;
1039 const gchar *email = l->data;
1040 gint i;
1041
1042 ma = e_meeting_store_find_attendee (store, email, &i);
1043
1044 if (ma) {
1045 if (e_meeting_attendee_get_edit_level (ma) != E_MEETING_ATTENDEE_EDIT_FULL)
1046 g_warning ("Not enough rights to delete attendee: %s\n", e_meeting_attendee_get_address (ma));
1047 else
1048 e_meeting_store_remove_attendee (store, ma);
1049 }
1050 }
1051
1052 g_slist_free (la);
1053 gtk_widget_hide (GTK_WIDGET (dialog));
1054 }
1055
1056 void
1057 e_meeting_list_view_invite_others_dialog (EMeetingListView *view)
1058 {
1059 e_name_selector_show_dialog (
1060 view->priv->name_selector,
1061 GTK_WIDGET (view));
1062 }
1063
1064 void
1065 e_meeting_list_view_set_editable (EMeetingListView *lview,
1066 gboolean set)
1067 {
1068 EMeetingListViewPrivate *priv = lview->priv;
1069
1070 gint edit_level = set;
1071
1072 g_hash_table_foreach (priv->renderers, change_edit_cols_for_organizer, GINT_TO_POINTER (edit_level));
1073 }
1074
1075 ENameSelector *
1076 e_meeting_list_view_get_name_selector (EMeetingListView *lview)
1077 {
1078 EMeetingListViewPrivate *priv;
1079
1080 g_return_val_if_fail (lview != NULL, NULL);
1081 g_return_val_if_fail (E_IS_MEETING_LIST_VIEW (lview), NULL);
1082
1083 priv = lview->priv;
1084
1085 return priv->name_selector;
1086 }
1087
1088 void
1089 e_meeting_list_view_set_name_selector (EMeetingListView *lview,
1090 ENameSelector *name_selector)
1091 {
1092 EMeetingListViewPrivate *priv;
1093
1094 g_return_if_fail (lview != NULL);
1095 g_return_if_fail (E_IS_MEETING_LIST_VIEW (lview));
1096
1097 priv = lview->priv;
1098
1099 if (priv->name_selector) {
1100 g_object_unref (priv->name_selector);
1101 priv->name_selector = NULL;
1102 }
1103
1104 priv->name_selector = g_object_ref (name_selector);
1105 }