evolution-3.6.4/modules/calendar/e-cal-shell-content.c

No issues found

  1 /*
  2  * e-cal-shell-content.c
  3  *
  4  * This program is free software; you can redistribute it and/or
  5  * modify it under the terms of the GNU Lesser General Public
  6  * License as published by the Free Software Foundation; either
  7  * version 2 of the License, or (at your option) version 3.
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 12  * Lesser General Public License for more details.
 13  *
 14  * You should have received a copy of the GNU Lesser General Public
 15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 19  *
 20  */
 21 
 22 #ifdef HAVE_CONFIG_H
 23 #include <config.h>
 24 #endif
 25 
 26 #include "e-cal-shell-content.h"
 27 
 28 #include <string.h>
 29 #include <glib/gi18n.h>
 30 
 31 #include "widgets/menus/gal-view-etable.h"
 32 #include "widgets/misc/e-paned.h"
 33 #include "widgets/misc/e-selectable.h"
 34 
 35 #include "calendar/gui/calendar-config.h"
 36 #include "calendar/gui/calendar-view.h"
 37 #include "calendar/gui/e-cal-list-view.h"
 38 #include "calendar/gui/e-cal-model-calendar.h"
 39 #include "calendar/gui/e-calendar-view.h"
 40 #include "calendar/gui/e-day-view.h"
 41 #include "calendar/gui/e-week-view.h"
 42 
 43 #include "e-cal-shell-view-private.h"
 44 
 45 #define E_CAL_SHELL_CONTENT_GET_PRIVATE(obj) \
 46 	(G_TYPE_INSTANCE_GET_PRIVATE \
 47 	((obj), E_TYPE_CAL_SHELL_CONTENT, ECalShellContentPrivate))
 48 
 49 struct _ECalShellContentPrivate {
 50 	GtkWidget *hpaned;
 51 	GtkWidget *notebook;
 52 	GtkWidget *vpaned;
 53 
 54 	GtkWidget *calendar;
 55 	GtkWidget *task_table;
 56 	GtkWidget *memo_table;
 57 
 58 	GalViewInstance *view_instance;
 59 };
 60 
 61 enum {
 62 	PROP_0,
 63 	PROP_CALENDAR,
 64 	PROP_MEMO_TABLE,
 65 	PROP_TASK_TABLE
 66 };
 67 
 68 /* Used to indicate who has the focus within the calendar view. */
 69 typedef enum {
 70 	FOCUS_CALENDAR,
 71 	FOCUS_MEMO_TABLE,
 72 	FOCUS_TASK_TABLE,
 73 	FOCUS_OTHER
 74 } FocusLocation;
 75 
 76 G_DEFINE_DYNAMIC_TYPE (
 77 	ECalShellContent,
 78 	e_cal_shell_content,
 79 	E_TYPE_SHELL_CONTENT)
 80 
 81 static void
 82 cal_shell_content_display_view_cb (ECalShellContent *cal_shell_content,
 83                                    GalView *gal_view)
 84 {
 85 	GnomeCalendar *calendar;
 86 	GnomeCalendarViewType view_type;
 87 
 88 	/* XXX This is confusing: we have CalendarView and ECalendarView.
 89 	 *     ECalendarView is an abstract base class for calendar view
 90 	 *     widgets (day view, week view, etc).  CalendarView is a
 91 	 *     simple GalView subclass that represents a calendar view. */
 92 
 93 	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
 94 
 95 	if (GAL_IS_VIEW_ETABLE (gal_view)) {
 96 		ECalendarView *calendar_view;
 97 
 98 		view_type = GNOME_CAL_LIST_VIEW;
 99 		calendar_view = gnome_calendar_get_calendar_view (
100 			calendar, view_type);
101 		gal_view_etable_attach_table (
102 			GAL_VIEW_ETABLE (gal_view),
103 			E_CAL_LIST_VIEW (calendar_view)->table);
104 	} else {
105 		view_type = calendar_view_get_view_type (
106 			CALENDAR_VIEW (gal_view));
107 	}
108 
109 	gnome_calendar_display_view (calendar, view_type);
110 }
111 
112 static void
113 cal_shell_content_notify_view_id_cb (ECalShellContent *cal_shell_content)
114 {
115 	EShellContent *shell_content;
116 	EShellView *shell_view;
117 	GSettings *settings;
118 	GtkWidget *paned;
119 	const gchar *key;
120 	const gchar *view_id;
121 
122 	settings = g_settings_new ("org.gnome.evolution.calendar");
123 	paned = cal_shell_content->priv->hpaned;
124 
125 	shell_content = E_SHELL_CONTENT (cal_shell_content);
126 	shell_view = e_shell_content_get_shell_view (shell_content);
127 	view_id = e_shell_view_get_view_id (shell_view);
128 
129 	if (view_id != NULL && strcmp (view_id, "Month_View") == 0)
130 		key = "month-hpane-position";
131 	else
132 		key = "hpane-position";
133 
134 	g_settings_unbind (paned, "hposition");
135 
136 	g_settings_bind (
137 		settings, key,
138 		paned, "hposition",
139 		G_SETTINGS_BIND_DEFAULT);
140 
141 	g_object_unref (settings);
142 }
143 
144 static gchar *
145 cal_shell_content_get_pad_state_filename (EShellContent *shell_content,
146                                           ETable *table)
147 {
148 	EShellBackend *shell_backend;
149 	EShellView *shell_view;
150 	const gchar *config_dir, *nick = NULL;
151 
152 	g_return_val_if_fail (shell_content != NULL, NULL);
153 	g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL);
154 	g_return_val_if_fail (table != NULL, NULL);
155 	g_return_val_if_fail (E_IS_TABLE (table), NULL);
156 
157 	if (E_IS_TASK_TABLE (table))
158 		nick = "TaskPad";
159 	else if (E_IS_MEMO_TABLE (table))
160 		nick = "MemoPad";
161 
162 	g_return_val_if_fail (nick != NULL, NULL);
163 
164 	shell_view = e_shell_content_get_shell_view (shell_content);
165 	shell_backend = e_shell_view_get_shell_backend (shell_view);
166 	config_dir = e_shell_backend_get_config_dir (shell_backend);
167 
168 	return g_build_filename (config_dir, nick, NULL);
169 }
170 
171 static void
172 cal_shell_content_save_table_state (EShellContent *shell_content,
173                                     ETable *table)
174 {
175 	gchar *filename;
176 
177 	filename = cal_shell_content_get_pad_state_filename (
178 		shell_content, table);
179 	g_return_if_fail (filename != NULL);
180 
181 	e_table_save_state (table, filename);
182 	g_free (filename);
183 }
184 
185 static void
186 cal_shell_content_load_table_state (EShellContent *shell_content,
187                                     ETable *table)
188 {
189 	gchar *filename;
190 
191 	filename = cal_shell_content_get_pad_state_filename (
192 		shell_content, table);
193 	g_return_if_fail (filename != NULL);
194 
195 	e_table_load_state (table, filename);
196 	g_free (filename);
197 }
198 
199 void
200 e_cal_shell_content_save_state (ECalShellContent *cal_shell_content)
201 {
202 	ECalShellContentPrivate *priv;
203 
204 	g_return_if_fail (cal_shell_content != NULL);
205 	g_return_if_fail (E_IS_CAL_SHELL_CONTENT (cal_shell_content));
206 
207 	priv = cal_shell_content->priv;
208 
209 	if (priv->task_table != NULL)
210 		cal_shell_content_save_table_state (
211 			E_SHELL_CONTENT (cal_shell_content),
212 			E_TABLE (priv->task_table));
213 
214 	if (priv->memo_table != NULL)
215 		cal_shell_content_save_table_state (
216 			E_SHELL_CONTENT (cal_shell_content),
217 			E_TABLE (priv->memo_table));
218 }
219 
220 static void
221 cal_shell_content_set_property (GObject *object,
222                                 guint property_id,
223                                 const GValue *value,
224                                 GParamSpec *pspec)
225 {
226 	switch (property_id) {
227 	}
228 
229 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
230 }
231 
232 static void
233 cal_shell_content_get_property (GObject *object,
234                                 guint property_id,
235                                 GValue *value,
236                                 GParamSpec *pspec)
237 {
238 	switch (property_id) {
239 		case PROP_CALENDAR:
240 			g_value_set_object (
241 				value, e_cal_shell_content_get_calendar (
242 				E_CAL_SHELL_CONTENT (object)));
243 			return;
244 
245 		case PROP_MEMO_TABLE:
246 			g_value_set_object (
247 				value, e_cal_shell_content_get_memo_table (
248 				E_CAL_SHELL_CONTENT (object)));
249 			return;
250 
251 		case PROP_TASK_TABLE:
252 			g_value_set_object (
253 				value, e_cal_shell_content_get_task_table (
254 				E_CAL_SHELL_CONTENT (object)));
255 			return;
256 	}
257 
258 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
259 }
260 
261 static void
262 cal_shell_content_dispose (GObject *object)
263 {
264 	ECalShellContentPrivate *priv;
265 
266 	priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object);
267 
268 	if (priv->hpaned != NULL) {
269 		g_object_unref (priv->hpaned);
270 		priv->hpaned = NULL;
271 	}
272 
273 	if (priv->notebook != NULL) {
274 		g_object_unref (priv->notebook);
275 		priv->notebook = NULL;
276 	}
277 
278 	if (priv->vpaned != NULL) {
279 		g_object_unref (priv->vpaned);
280 		priv->vpaned = NULL;
281 	}
282 
283 	if (priv->calendar != NULL) {
284 		g_object_unref (priv->calendar);
285 		priv->calendar = NULL;
286 	}
287 
288 	if (priv->task_table != NULL) {
289 		g_object_unref (priv->task_table);
290 		priv->task_table = NULL;
291 	}
292 
293 	if (priv->memo_table != NULL) {
294 		g_object_unref (priv->memo_table);
295 		priv->memo_table = NULL;
296 	}
297 
298 	if (priv->view_instance != NULL) {
299 		g_object_unref (priv->view_instance);
300 		priv->view_instance = NULL;
301 	}
302 
303 	/* Chain up to parent's dispose() method. */
304 	G_OBJECT_CLASS (e_cal_shell_content_parent_class)->dispose (object);
305 }
306 
307 static time_t
308 gc_get_default_time (ECalModel *model,
309                      gpointer user_data)
310 {
311 	GnomeCalendar *gcal = user_data;
312 	time_t res = 0, end;
313 
314 	g_return_val_if_fail (model != NULL, 0);
315 	g_return_val_if_fail (GNOME_IS_CALENDAR (user_data), 0);
316 
317 	gnome_calendar_get_current_time_range (gcal, &res, &end);
318 
319 	return res;
320 }
321 
322 static void
323 cal_shell_content_constructed (GObject *object)
324 {
325 	ECalShellContentPrivate *priv;
326 	ECalendarView *calendar_view;
327 	ECalModel *memo_model = NULL;
328 	ECalModel *task_model = NULL;
329 	EShell *shell;
330 	EShellContent *shell_content;
331 	EShellView *shell_view;
332 	EShellWindow *shell_window;
333 	EShellContent *foreign_content;
334 	EShellView *foreign_view;
335 	GnomeCalendar *calendar;
336 	ESourceRegistry *registry;
337 	GalViewInstance *view_instance;
338 	GSettings *settings;
339 	GtkWidget *container;
340 	GtkWidget *widget;
341 	gchar *markup;
342 	gint ii;
343 
344 	priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (object);
345 
346 	/* Chain up to parent's constructed() method. */
347 	G_OBJECT_CLASS (e_cal_shell_content_parent_class)->constructed (object);
348 
349 	shell_content = E_SHELL_CONTENT (object);
350 	shell_view = e_shell_content_get_shell_view (shell_content);
351 	shell_window = e_shell_view_get_shell_window (shell_view);
352 
353 	shell = e_shell_window_get_shell (shell_window);
354 
355 	/* We borrow the memopad and taskpad models from the memo
356 	 * and task views, loading the views if necessary. */
357 	if (!e_shell_get_express_mode (shell)) {
358 		foreign_view = e_shell_window_get_shell_view (shell_window, "memos");
359 		foreign_content = e_shell_view_get_shell_content (foreign_view);
360 		g_object_get (foreign_content, "model", &memo_model, NULL);
361 
362 		foreign_view = e_shell_window_get_shell_view (shell_window, "tasks");
363 		foreign_content = e_shell_view_get_shell_content (foreign_view);
364 		g_object_get (foreign_content, "model", &task_model, NULL);
365 	}
366 
367 	/* Build content widgets. */
368 
369 	container = GTK_WIDGET (object);
370 
371 	if (!e_shell_get_express_mode (shell)) {
372 		widget = e_paned_new (GTK_ORIENTATION_HORIZONTAL);
373 		gtk_container_add (GTK_CONTAINER (container), widget);
374 		priv->hpaned = g_object_ref (widget);
375 		gtk_widget_show (widget);
376 
377 		container = priv->hpaned;
378 	}
379 
380 	widget = gtk_notebook_new ();
381 	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
382 	gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
383 	if (!e_shell_get_express_mode (shell))
384 		gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, FALSE);
385 	else
386 		gtk_container_add (GTK_CONTAINER (container), widget);
387 	priv->notebook = g_object_ref (widget);
388 	gtk_widget_show (widget);
389 
390 	if (!e_shell_get_express_mode (shell)) {
391 		/* FIXME Need to deal with saving and restoring the position.
392 		 *       Month view has its own position. */
393 		widget = e_paned_new (GTK_ORIENTATION_VERTICAL);
394 		e_paned_set_fixed_resize (E_PANED (widget), FALSE);
395 		gtk_paned_pack2 (GTK_PANED (container), widget, FALSE, TRUE);
396 		priv->vpaned = g_object_ref (widget);
397 		gtk_widget_show (widget);
398 	}
399 
400 	container = priv->notebook;
401 
402 	/* Add views in the order defined by GnomeCalendarViewType, such
403 	 * that the notebook page number corresponds to the view type. */
404 
405 	registry = e_shell_get_registry (shell);
406 	priv->calendar = gnome_calendar_new (registry);
407 	calendar = GNOME_CALENDAR (priv->calendar);
408 
409 	for (ii = 0; ii < GNOME_CAL_LAST_VIEW; ii++) {
410 		calendar_view = gnome_calendar_get_calendar_view (calendar, ii);
411 
412 		gtk_notebook_append_page (
413 			GTK_NOTEBOOK (container),
414 			GTK_WIDGET (calendar_view), NULL);
415 		gtk_widget_show (GTK_WIDGET (calendar_view));
416 	}
417 
418 	g_object_bind_property (
419 		priv->calendar, "view",
420 		priv->notebook, "page",
421 		G_BINDING_SYNC_CREATE);
422 
423 	container = priv->vpaned;
424 
425 	if (!e_shell_get_express_mode (shell)) {
426 		widget = gtk_vbox_new (FALSE, 0);
427 		gtk_paned_pack1 (GTK_PANED (container), widget, TRUE, TRUE);
428 		gtk_widget_show (widget);
429 
430 		container = widget;
431 
432 		widget = gtk_hseparator_new ();
433 		gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
434 		gtk_widget_show (widget);
435 
436 		widget = gtk_label_new (NULL);
437 		markup = g_strdup_printf ("<b>%s</b>", _("Tasks"));
438 		gtk_label_set_markup (GTK_LABEL (widget), markup);
439 		gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
440 		gtk_widget_show (widget);
441 		g_free (markup);
442 
443 		widget = gtk_scrolled_window_new (NULL, NULL);
444 		gtk_scrolled_window_set_policy (
445 			GTK_SCROLLED_WINDOW (widget),
446 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
447 		gtk_scrolled_window_set_shadow_type (
448 			GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
449 		gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
450 		gtk_widget_show (widget);
451 
452 		container = widget;
453 
454 		widget = e_task_table_new (shell_view, task_model);
455 		gtk_container_add (GTK_CONTAINER (container), widget);
456 		priv->task_table = g_object_ref (widget);
457 		gtk_widget_show (widget);
458 
459 		cal_shell_content_load_table_state (
460 			shell_content, E_TABLE (widget));
461 
462 		g_signal_connect_swapped (
463 			widget, "open-component",
464 			G_CALLBACK (e_cal_shell_view_taskpad_open_task),
465 			shell_view);
466 
467 		container = priv->vpaned;
468 
469 		widget = gtk_vbox_new (FALSE, 0);
470 		gtk_paned_pack2 (GTK_PANED (container), widget, TRUE, TRUE);
471 		gtk_widget_show (widget);
472 
473 		container = widget;
474 
475 		widget = gtk_label_new (NULL);
476 		markup = g_strdup_printf ("<b>%s</b>", _("Memos"));
477 		gtk_label_set_markup (GTK_LABEL (widget), markup);
478 		gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
479 		gtk_widget_show (widget);
480 		g_free (markup);
481 
482 		widget = gtk_scrolled_window_new (NULL, NULL);
483 		gtk_scrolled_window_set_policy (
484 			GTK_SCROLLED_WINDOW (widget),
485 			GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
486 		gtk_scrolled_window_set_shadow_type (
487 			GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
488 		gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
489 		gtk_widget_show (widget);
490 
491 		container = widget;
492 
493 		widget = e_memo_table_new (shell_view, memo_model);
494 		gtk_container_add (GTK_CONTAINER (container), widget);
495 		priv->memo_table = g_object_ref (widget);
496 		gtk_widget_show (widget);
497 
498 		cal_shell_content_load_table_state (
499 			shell_content, E_TABLE (widget));
500 
501 		e_cal_model_set_default_time_func (
502 			memo_model, gc_get_default_time, calendar);
503 
504 		g_signal_connect_swapped (
505 			widget, "open-component",
506 			G_CALLBACK (e_cal_shell_view_memopad_open_memo),
507 			shell_view);
508 	}
509 
510 	/* Load the view instance. */
511 
512 	view_instance = e_shell_view_new_view_instance (shell_view, NULL);
513 	g_signal_connect_swapped (
514 		view_instance, "display-view",
515 		G_CALLBACK (cal_shell_content_display_view_cb),
516 		object);
517 	/* XXX Actually, don't load the view instance just yet.
518 	 *     The GtkWidget::map() callback below explains why. */
519 	priv->view_instance = view_instance;
520 
521 	if (!e_shell_get_express_mode (shell)) {
522 		g_signal_connect_swapped (
523 			shell_view, "notify::view-id",
524 			G_CALLBACK (cal_shell_content_notify_view_id_cb),
525 			object);
526 
527 		settings = g_settings_new ("org.gnome.evolution.calendar");
528 
529 		g_settings_bind (
530 			settings, "tag-vpane-position",
531 			priv->vpaned, "proportion",
532 			G_SETTINGS_BIND_DEFAULT);
533 
534 		g_object_unref (settings);
535 	}
536 
537 	if (memo_model)
538 		g_object_unref (memo_model);
539 	if (task_model)
540 		g_object_unref (task_model);
541 }
542 
543 static void
544 cal_shell_content_map (GtkWidget *widget)
545 {
546 	ECalShellContentPrivate *priv;
547 
548 	/* XXX Delay loading the GalViewInstance until after ECalShellView
549 	 *     has a chance to install the sidebar's date navigator into
550 	 *     GnomeCalendar, since loading the GalViewInstance triggers a
551 	 *     callback in GnomeCalendar that requires the date navigator.
552 	 *     Ordinarily we would do this at the end of constructed(), but
553 	 *     that's too soon in this case.  (This feels kind of kludgy.) */
554 	priv = E_CAL_SHELL_CONTENT_GET_PRIVATE (widget);
555 	gal_view_instance_load (priv->view_instance);
556 
557 	/* Chain up to parent's map() method. */
558 	GTK_WIDGET_CLASS (e_cal_shell_content_parent_class)->map (widget);
559 }
560 
561 /* Helper for cal_shell_content_check_state() */
562 static icalproperty *
563 cal_shell_content_get_attendee_prop (icalcomponent *icalcomp,
564                                      const gchar *address)
565 {
566 	icalproperty *prop;
567 
568 	if (address == NULL || *address == '\0')
569 		return NULL;
570 
571 	prop = icalcomponent_get_first_property (
572 		icalcomp, ICAL_ATTENDEE_PROPERTY);
573 
574 	while (prop != NULL) {
575 		const gchar *attendee;
576 
577 		attendee = icalproperty_get_attendee (prop);
578 
579 		if (g_str_equal (itip_strip_mailto (attendee), address))
580 			return prop;
581 
582 		prop = icalcomponent_get_next_property (
583 			icalcomp, ICAL_ATTENDEE_PROPERTY);
584 	}
585 
586 	return NULL;
587 }
588 
589 /* Helper for cal_shell_content_check_state() */
590 static gboolean
591 cal_shell_content_icalcomp_is_delegated (icalcomponent *icalcomp,
592                                          const gchar *user_email)
593 {
594 	icalproperty *prop;
595 	icalparameter *param;
596 	const gchar *delto = NULL;
597 	gboolean is_delegated = FALSE;
598 
599 	prop = cal_shell_content_get_attendee_prop (icalcomp, user_email);
600 
601 	if (prop != NULL) {
602 		param = icalproperty_get_first_parameter (
603 			prop, ICAL_DELEGATEDTO_PARAMETER);
604 		if (param != NULL) {
605 			delto = icalparameter_get_delegatedto (param);
606 			delto = itip_strip_mailto (delto);
607 		}
608 	} else
609 		return FALSE;
610 
611 	prop = cal_shell_content_get_attendee_prop (icalcomp, delto);
612 
613 	if (prop != NULL) {
614 		const gchar *delfrom = NULL;
615 		icalparameter_partstat status = ICAL_PARTSTAT_NONE;
616 
617 		param = icalproperty_get_first_parameter (
618 			prop, ICAL_DELEGATEDFROM_PARAMETER);
619 		if (param != NULL) {
620 			delfrom = icalparameter_get_delegatedfrom (param);
621 			delfrom = itip_strip_mailto (delfrom);
622 		}
623 		param = icalproperty_get_first_parameter (
624 			prop, ICAL_PARTSTAT_PARAMETER);
625 		if (param != NULL)
626 			status = icalparameter_get_partstat (param);
627 		is_delegated =
628 			(status != ICAL_PARTSTAT_DECLINED) &&
629 			(g_strcmp0 (delfrom, user_email) == 0);
630 	}
631 
632 	return is_delegated;
633 }
634 
635 static guint32
636 cal_shell_content_check_state (EShellContent *shell_content)
637 {
638 	EShell *shell;
639 	EShellView *shell_view;
640 	EShellBackend *shell_backend;
641 	ESourceRegistry *registry;
642 	ECalShellContent *cal_shell_content;
643 	GnomeCalendar *calendar;
644 	ECalendarView *calendar_view;
645 	GnomeCalendarViewType view_type;
646 	gboolean selection_is_editable = FALSE;
647 	gboolean selection_is_instance = FALSE;
648 	gboolean selection_is_meeting = FALSE;
649 	gboolean selection_is_organizer = FALSE;
650 	gboolean selection_is_recurring = FALSE;
651 	gboolean selection_can_delegate = FALSE;
652 	guint32 state = 0;
653 	GList *selected;
654 	GList *link;
655 	guint n_selected;
656 
657 	cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
658 
659 	shell_view = e_shell_content_get_shell_view (shell_content);
660 	shell_backend = e_shell_view_get_shell_backend (shell_view);
661 	shell = e_shell_backend_get_shell (shell_backend);
662 	registry = e_shell_get_registry (shell);
663 
664 	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
665 	view_type = gnome_calendar_get_view (calendar);
666 	calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
667 
668 	selected = e_calendar_view_get_selected_events (calendar_view);
669 	n_selected = g_list_length (selected);
670 
671 	/* If we have a selection, assume it's
672 	 * editable until we learn otherwise. */
673 	if (n_selected > 0)
674 		selection_is_editable = TRUE;
675 
676 	for (link = selected; link != NULL; link = g_list_next (link)) {
677 		ECalendarViewEvent *event = link->data;
678 		ECalClient *client;
679 		ECalComponent *comp;
680 		gchar *user_email;
681 		icalcomponent *icalcomp;
682 		const gchar *capability;
683 		gboolean cap_delegate_supported;
684 		gboolean cap_delegate_to_many;
685 		gboolean icalcomp_is_delegated;
686 		gboolean read_only;
687 
688 		if (!is_comp_data_valid (event))
689 			continue;
690 
691 		client = event->comp_data->client;
692 		icalcomp = event->comp_data->icalcomp;
693 
694 		read_only = e_client_is_readonly (E_CLIENT (client));
695 		selection_is_editable &= !read_only;
696 
697 		selection_is_instance |=
698 			e_cal_util_component_is_instance (icalcomp);
699 
700 		selection_is_meeting =
701 			(n_selected == 1) &&
702 			e_cal_util_component_has_attendee (icalcomp);
703 
704 		selection_is_recurring |=
705 			e_cal_util_component_is_instance (icalcomp) ||
706 			e_cal_util_component_has_recurrences (icalcomp);
707 
708 		/* XXX The rest of this is rather expensive and
709 		 *     only applies if a single event is selected,
710 		 *     so continue with the loop iteration if the
711 		 *     rest of this is not applicable. */
712 		if (n_selected > 1)
713 			continue;
714 
715 		/* XXX This probably belongs in comp-util.c. */
716 
717 		comp = e_cal_component_new ();
718 		e_cal_component_set_icalcomponent (
719 			comp, icalcomponent_new_clone (icalcomp));
720 		user_email = itip_get_comp_attendee (
721 			registry, comp, client);
722 
723 		selection_is_organizer =
724 			e_cal_util_component_has_organizer (icalcomp) &&
725 			itip_organizer_is_user (registry, comp, client);
726 
727 		capability = CAL_STATIC_CAPABILITY_DELEGATE_SUPPORTED;
728 		cap_delegate_supported =
729 			e_client_check_capability (
730 			E_CLIENT (client), capability);
731 
732 		capability = CAL_STATIC_CAPABILITY_DELEGATE_TO_MANY;
733 		cap_delegate_to_many =
734 			e_client_check_capability (
735 			E_CLIENT (client), capability);
736 
737 		icalcomp_is_delegated =
738 			(user_email != NULL) &&
739 			cal_shell_content_icalcomp_is_delegated (
740 			icalcomp, user_email);
741 
742 		selection_can_delegate =
743 			cap_delegate_supported &&
744 			(cap_delegate_to_many ||
745 			(!selection_is_organizer &&
746 			 !icalcomp_is_delegated));
747 
748 		g_free (user_email);
749 		g_object_unref (comp);
750 	}
751 
752 	g_list_free (selected);
753 
754 	if (n_selected == 1)
755 		state |= E_CAL_SHELL_CONTENT_SELECTION_SINGLE;
756 	if (n_selected > 1)
757 		state |= E_CAL_SHELL_CONTENT_SELECTION_MULTIPLE;
758 	if (selection_is_editable)
759 		state |= E_CAL_SHELL_CONTENT_SELECTION_IS_EDITABLE;
760 	if (selection_is_instance)
761 		state |= E_CAL_SHELL_CONTENT_SELECTION_IS_INSTANCE;
762 	if (selection_is_meeting)
763 		state |= E_CAL_SHELL_CONTENT_SELECTION_IS_MEETING;
764 	if (selection_is_organizer)
765 		state |= E_CAL_SHELL_CONTENT_SELECTION_IS_ORGANIZER;
766 	if (selection_is_recurring)
767 		state |= E_CAL_SHELL_CONTENT_SELECTION_IS_RECURRING;
768 	if (selection_can_delegate)
769 		state |= E_CAL_SHELL_CONTENT_SELECTION_CAN_DELEGATE;
770 
771 	return state;
772 }
773 
774 static void
775 cal_shell_content_focus_search_results (EShellContent *shell_content)
776 {
777 	ECalShellContent *cal_shell_content;
778 	GnomeCalendar *calendar;
779 	GnomeCalendarViewType view_type;
780 	ECalendarView *calendar_view;
781 
782 	cal_shell_content = E_CAL_SHELL_CONTENT (shell_content);
783 	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
784 	view_type = gnome_calendar_get_view (calendar);
785 	calendar_view = gnome_calendar_get_calendar_view (calendar, view_type);
786 
787 	gtk_widget_grab_focus (GTK_WIDGET (calendar_view));
788 }
789 
790 static void
791 e_cal_shell_content_class_init (ECalShellContentClass *class)
792 {
793 	GObjectClass *object_class;
794 	GtkWidgetClass *widget_class;
795 	EShellContentClass *shell_content_class;
796 
797 	g_type_class_add_private (class, sizeof (ECalShellContentPrivate));
798 
799 	object_class = G_OBJECT_CLASS (class);
800 	object_class->set_property = cal_shell_content_set_property;
801 	object_class->get_property = cal_shell_content_get_property;
802 	object_class->dispose = cal_shell_content_dispose;
803 	object_class->constructed = cal_shell_content_constructed;
804 
805 	widget_class = GTK_WIDGET_CLASS (class);
806 	widget_class->map = cal_shell_content_map;
807 
808 	shell_content_class = E_SHELL_CONTENT_CLASS (class);
809 	shell_content_class->check_state = cal_shell_content_check_state;
810 	shell_content_class->focus_search_results = cal_shell_content_focus_search_results;
811 
812 	g_object_class_install_property (
813 		object_class,
814 		PROP_CALENDAR,
815 		g_param_spec_object (
816 			"calendar",
817 			NULL,
818 			NULL,
819 			GNOME_TYPE_CALENDAR,
820 			G_PARAM_READABLE));
821 
822 	g_object_class_install_property (
823 		object_class,
824 		PROP_MEMO_TABLE,
825 		g_param_spec_object (
826 			"memo-table",
827 			NULL,
828 			NULL,
829 			E_TYPE_MEMO_TABLE,
830 			G_PARAM_READABLE));
831 
832 	g_object_class_install_property (
833 		object_class,
834 		PROP_TASK_TABLE,
835 		g_param_spec_object (
836 			"task-table",
837 			NULL,
838 			NULL,
839 			E_TYPE_TASK_TABLE,
840 			G_PARAM_READABLE));
841 }
842 
843 static void
844 e_cal_shell_content_class_finalize (ECalShellContentClass *class)
845 {
846 }
847 
848 static void
849 e_cal_shell_content_init (ECalShellContent *cal_shell_content)
850 {
851 	cal_shell_content->priv =
852 		E_CAL_SHELL_CONTENT_GET_PRIVATE (cal_shell_content);
853 
854 	/* Postpone widget construction until we have a shell view. */
855 }
856 
857 void
858 e_cal_shell_content_type_register (GTypeModule *type_module)
859 {
860 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
861 	 *     function, so we have to wrap it with a public function in
862 	 *     order to register types from a separate compilation unit. */
863 	e_cal_shell_content_register_type (type_module);
864 }
865 
866 GtkWidget *
867 e_cal_shell_content_new (EShellView *shell_view)
868 {
869 	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
870 
871 	return g_object_new (
872 		E_TYPE_CAL_SHELL_CONTENT,
873 		"shell-view", shell_view, NULL);
874 }
875 
876 ECalModel *
877 e_cal_shell_content_get_model (ECalShellContent *cal_shell_content)
878 {
879 	GnomeCalendar *calendar;
880 
881 	g_return_val_if_fail (
882 		E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
883 
884 	calendar = e_cal_shell_content_get_calendar (cal_shell_content);
885 
886 	return gnome_calendar_get_model (calendar);
887 }
888 
889 GnomeCalendar *
890 e_cal_shell_content_get_calendar (ECalShellContent *cal_shell_content)
891 {
892 	g_return_val_if_fail (
893 		E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
894 
895 	return GNOME_CALENDAR (cal_shell_content->priv->calendar);
896 }
897 
898 EMemoTable *
899 e_cal_shell_content_get_memo_table (ECalShellContent *cal_shell_content)
900 {
901 	g_return_val_if_fail (
902 		E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
903 
904 	return E_MEMO_TABLE (cal_shell_content->priv->memo_table);
905 }
906 
907 ETaskTable *
908 e_cal_shell_content_get_task_table (ECalShellContent *cal_shell_content)
909 {
910 	g_return_val_if_fail (
911 		E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
912 
913 	return E_TASK_TABLE (cal_shell_content->priv->task_table);
914 }
915 
916 EShellSearchbar *
917 e_cal_shell_content_get_searchbar (ECalShellContent *cal_shell_content)
918 {
919 	EShellView *shell_view;
920 	EShellContent *shell_content;
921 	GtkWidget *widget;
922 
923 	g_return_val_if_fail (
924 		E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
925 
926 	shell_content = E_SHELL_CONTENT (cal_shell_content);
927 	shell_view = e_shell_content_get_shell_view (shell_content);
928 	widget = e_shell_view_get_searchbar (shell_view);
929 
930 	return E_SHELL_SEARCHBAR (widget);
931 }
932 
933 GalViewInstance *
934 e_cal_shell_content_get_view_instance (ECalShellContent *cal_shell_content)
935 {
936 	g_return_val_if_fail (
937 		E_IS_CAL_SHELL_CONTENT (cal_shell_content), NULL);
938 
939 	return cal_shell_content->priv->view_instance;
940 }