evolution-3.6.4/modules/calendar/e-task-shell-backend.c

No issues found

  1 /*
  2  * e-task-shell-backend.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-task-shell-backend.h"
 27 
 28 #include <string.h>
 29 #include <glib/gi18n.h>
 30 #include <libecal/libecal.h>
 31 #include <libedataserverui/libedataserverui.h>
 32 
 33 #include "shell/e-shell.h"
 34 #include "shell/e-shell-backend.h"
 35 #include "shell/e-shell-window.h"
 36 #include "widgets/misc/e-cal-source-config.h"
 37 #include "widgets/misc/e-source-config-dialog.h"
 38 
 39 #include "calendar/gui/comp-util.h"
 40 #include "calendar/gui/dialogs/task-editor.h"
 41 
 42 #include "e-task-shell-content.h"
 43 #include "e-task-shell-migrate.h"
 44 #include "e-task-shell-sidebar.h"
 45 #include "e-task-shell-view.h"
 46 
 47 #define E_TASK_SHELL_BACKEND_GET_PRIVATE(obj) \
 48 	(G_TYPE_INSTANCE_GET_PRIVATE \
 49 	((obj), E_TYPE_TASK_SHELL_BACKEND, ETaskShellBackendPrivate))
 50 
 51 struct _ETaskShellBackendPrivate {
 52 	gint placeholder;
 53 };
 54 
 55 G_DEFINE_DYNAMIC_TYPE (
 56 	ETaskShellBackend,
 57 	e_task_shell_backend,
 58 	E_TYPE_SHELL_BACKEND)
 59 
 60 static void
 61 task_shell_backend_new_task (ESource *source,
 62                              GAsyncResult *result,
 63                              EShell *shell,
 64                              CompEditorFlags flags)
 65 {
 66 	EClient *client = NULL;
 67 	ECalClient *cal_client;
 68 	ECalComponent *comp;
 69 	CompEditor *editor;
 70 	GError *error = NULL;
 71 
 72 	e_client_utils_open_new_finish (source, result, &client, &error);
 73 
 74 	/* XXX Handle errors better. */
 75 	if (error != NULL) {
 76 		g_warn_if_fail (client == NULL);
 77 		g_warning (
 78 			"%s: Failed to open '%s': %s",
 79 			G_STRFUNC, e_source_get_display_name (source),
 80 			error->message);
 81 		g_error_free (error);
 82 		return;
 83 	}
 84 
 85 	g_return_if_fail (E_IS_CAL_CLIENT (client));
 86 
 87 	cal_client = E_CAL_CLIENT (client);
 88 	editor = task_editor_new (cal_client, shell, flags);
 89 	comp = cal_comp_task_new_with_defaults (cal_client);
 90 	comp_editor_edit_comp (editor, comp);
 91 
 92 	gtk_window_present (GTK_WINDOW (editor));
 93 
 94 	g_object_unref (comp);
 95 	g_object_unref (client);
 96 }
 97 
 98 static void
 99 task_shell_backend_task_new_cb (GObject *source_object,
100                                 GAsyncResult *result,
101                                 gpointer shell)
102 {
103 	CompEditorFlags flags = 0;
104 
105 	flags |= COMP_EDITOR_NEW_ITEM;
106 
107 	task_shell_backend_new_task (
108 		E_SOURCE (source_object), result, shell, flags);
109 
110 	g_object_unref (shell);
111 }
112 
113 static void
114 task_shell_backend_task_assigned_new_cb (GObject *source_object,
115                                          GAsyncResult *result,
116                                          gpointer shell)
117 {
118 	CompEditorFlags flags = 0;
119 
120 	flags |= COMP_EDITOR_NEW_ITEM;
121 	flags |= COMP_EDITOR_IS_ASSIGNED;
122 	flags |= COMP_EDITOR_USER_ORG;
123 
124 	task_shell_backend_new_task (
125 		E_SOURCE (source_object), result, shell, flags);
126 
127 	g_object_unref (shell);
128 }
129 
130 static void
131 action_task_new_cb (GtkAction *action,
132                     EShellWindow *shell_window)
133 {
134 	EShell *shell;
135 	ESource *source;
136 	ESourceRegistry *registry;
137 	EClientSourceType source_type;
138 	const gchar *action_name;
139 
140 	/* This callback is used for both tasks and assigned tasks. */
141 
142 	shell = e_shell_window_get_shell (shell_window);
143 
144 	registry = e_shell_get_registry (shell);
145 	source_type = E_CLIENT_SOURCE_TYPE_TASKS;
146 	source = e_source_registry_ref_default_task_list (registry);
147 
148 	/* Use a callback function appropriate for the action.
149 	 * FIXME Need to obtain a better default time zone. */
150 	action_name = gtk_action_get_name (action);
151 	if (strcmp (action_name, "task-assigned-new") == 0)
152 		e_client_utils_open_new (
153 			source, source_type, FALSE, NULL,
154 			task_shell_backend_task_assigned_new_cb,
155 			g_object_ref (shell));
156 	else
157 		e_client_utils_open_new (
158 			source, source_type, FALSE, NULL,
159 			task_shell_backend_task_new_cb,
160 			g_object_ref (shell));
161 
162 	g_object_unref (source);
163 }
164 
165 static void
166 action_task_list_new_cb (GtkAction *action,
167                          EShellWindow *shell_window)
168 {
169 	EShell *shell;
170 	ESourceRegistry *registry;
171 	ECalClientSourceType source_type;
172 	GtkWidget *config;
173 	GtkWidget *dialog;
174 	const gchar *icon_name;
175 
176 	shell = e_shell_window_get_shell (shell_window);
177 
178 	registry = e_shell_get_registry (shell);
179 	source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
180 	config = e_cal_source_config_new (registry, NULL, source_type);
181 
182 	dialog = e_source_config_dialog_new (E_SOURCE_CONFIG (config));
183 
184 	gtk_window_set_transient_for (
185 		GTK_WINDOW (dialog), GTK_WINDOW (shell_window));
186 
187 	icon_name = gtk_action_get_icon_name (action);
188 	gtk_window_set_icon_name (GTK_WINDOW (dialog), icon_name);
189 
190 	gtk_window_set_title (GTK_WINDOW (dialog), _("New Task List"));
191 
192 	gtk_widget_show (dialog);
193 }
194 
195 static GtkActionEntry item_entries[] = {
196 
197 	{ "task-new",
198 	  "stock_task",
199 	  NC_("New", "_Task"),
200 	  "<Shift><Control>t",
201 	  N_("Create a new task"),
202 	  G_CALLBACK (action_task_new_cb) },
203 
204 	{ "task-assigned-new",
205 	  "stock_task",
206 	  NC_("New", "Assigne_d Task"),
207 	  NULL,
208 	  N_("Create a new assigned task"),
209 	  G_CALLBACK (action_task_new_cb) }
210 };
211 
212 static GtkActionEntry source_entries[] = {
213 
214 	{ "task-list-new",
215 	  "stock_todo",
216 	  NC_("New", "Tas_k List"),
217 	  NULL,
218 	  N_("Create a new task list"),
219 	  G_CALLBACK (action_task_list_new_cb) }
220 };
221 
222 static gboolean
223 task_shell_backend_handle_uri_cb (EShellBackend *shell_backend,
224                                   const gchar *uri)
225 {
226 	EShell *shell;
227 	CompEditor *editor;
228 	CompEditorFlags flags = 0;
229 	ECalClient *client;
230 	ECalComponent *comp;
231 	ESource *source;
232 	ESourceRegistry *registry;
233 	ECalClientSourceType source_type;
234 	SoupURI *soup_uri;
235 	icalcomponent *icalcomp;
236 	icalproperty *icalprop;
237 	const gchar *cp;
238 	gchar *source_uid = NULL;
239 	gchar *comp_uid = NULL;
240 	gchar *comp_rid = NULL;
241 	gboolean handled = FALSE;
242 	GError *error = NULL;
243 
244 	source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
245 	shell = e_shell_backend_get_shell (shell_backend);
246 
247 	if (strncmp (uri, "task:", 5) != 0)
248 		return FALSE;
249 
250 	soup_uri = soup_uri_new (uri);
251 
252 	if (soup_uri == NULL)
253 		return FALSE;
254 
255 	cp = soup_uri_get_query (soup_uri);
256 	if (cp == NULL)
257 		goto exit;
258 
259 	while (*cp != '\0') {
260 		gchar *header;
261 		gchar *content;
262 		gsize header_len;
263 		gsize content_len;
264 
265 		header_len = strcspn (cp, "=&");
266 
267 		/* If it's malformed, give up. */
268 		if (cp[header_len] != '=')
269 			break;
270 
271 		header = (gchar *) cp;
272 		header[header_len] = '\0';
273 		cp += header_len + 1;
274 
275 		content_len = strcspn (cp, "&");
276 
277 		content = g_strndup (cp, content_len);
278 		if (g_ascii_strcasecmp (header, "source-uid") == 0)
279 			source_uid = g_strdup (content);
280 		else if (g_ascii_strcasecmp (header, "comp-uid") == 0)
281 			comp_uid = g_strdup (content);
282 		else if (g_ascii_strcasecmp (header, "comp-rid") == 0)
283 			comp_rid = g_strdup (content);
284 		g_free (content);
285 
286 		cp += content_len;
287 		if (*cp == '&') {
288 			cp++;
289 			if (strcmp (cp, "amp;") == 0)
290 				cp += 4;
291 		}
292 	}
293 
294 	if (source_uid == NULL || comp_uid == NULL)
295 		goto exit;
296 
297 	/* URI is valid, so consider it handled.  Whether
298 	 * we successfully open it is another matter... */
299 	handled = TRUE;
300 
301 	registry = e_shell_get_registry (shell);
302 	source = e_source_registry_ref_source (registry, source_uid);
303 	if (source == NULL) {
304 		g_printerr ("No source for UID '%s'\n", source_uid);
305 		goto exit;
306 	}
307 
308 	client = e_cal_client_new (source, source_type, &error);
309 
310 	if (client != NULL)
311 		e_client_open_sync (E_CLIENT (client), TRUE, NULL, &error);
312 
313 	if (error != NULL) {
314 		g_warning (
315 			"%s: Failed to create/open client: %s",
316 			G_STRFUNC, error->message);
317 		if (client != NULL)
318 			g_object_unref (client);
319 		g_object_unref (source);
320 		g_error_free (error);
321 		goto exit;
322 	}
323 
324 	g_object_unref (source);
325 	source = NULL;
326 
327 	/* XXX Copied from e_task_shell_view_open_task().
328 	 *     Clearly a new utility function is needed. */
329 
330 	editor = comp_editor_find_instance (comp_uid);
331 
332 	if (editor != NULL)
333 		goto present;
334 
335 	e_cal_client_get_object_sync (
336 		client, comp_uid, comp_rid, &icalcomp, NULL, &error);
337 
338 	if (error != NULL) {
339 		g_warning (
340 			"%s: Failed to get object: %s",
341 			G_STRFUNC, error->message);
342 		g_object_unref (client);
343 		g_error_free (error);
344 		goto exit;
345 	}
346 
347 	comp = e_cal_component_new ();
348 	if (!e_cal_component_set_icalcomponent (comp, icalcomp)) {
349 		g_warning ("%s: Failed to set icalcomp to comp\n", G_STRFUNC);
350 		icalcomponent_free (icalcomp);
351 		icalcomp = NULL;
352 	}
353 
354 	icalprop = icalcomp ? icalcomponent_get_first_property (
355 		icalcomp, ICAL_ATTENDEE_PROPERTY) : NULL;
356 	if (icalprop != NULL)
357 		flags |= COMP_EDITOR_IS_ASSIGNED;
358 
359 	if (itip_organizer_is_user (registry, comp, client))
360 		flags |= COMP_EDITOR_USER_ORG;
361 
362 	if (!e_cal_component_has_attendees (comp))
363 		flags |= COMP_EDITOR_USER_ORG;
364 
365 	editor = task_editor_new (client, shell, flags);
366 	comp_editor_edit_comp (editor, comp);
367 
368 	g_object_unref (comp);
369 
370 present:
371 	gtk_window_present (GTK_WINDOW (editor));
372 
373 	g_object_unref (client);
374 
375 exit:
376 	g_free (source_uid);
377 	g_free (comp_uid);
378 	g_free (comp_rid);
379 
380 	soup_uri_free (soup_uri);
381 
382 	return handled;
383 }
384 
385 static void
386 task_shell_backend_window_added_cb (EShellBackend *shell_backend,
387                                     GtkWindow *window)
388 {
389 	const gchar *module_name;
390 
391 	if (!E_IS_SHELL_WINDOW (window))
392 		return;
393 
394 	module_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
395 
396 	e_shell_window_register_new_item_actions (
397 		E_SHELL_WINDOW (window), module_name,
398 		item_entries, G_N_ELEMENTS (item_entries));
399 
400 	e_shell_window_register_new_source_actions (
401 		E_SHELL_WINDOW (window), module_name,
402 		source_entries, G_N_ELEMENTS (source_entries));
403 }
404 
405 static void
406 task_shell_backend_constructed (GObject *object)
407 {
408 	EShell *shell;
409 	EShellBackend *shell_backend;
410 
411 	shell_backend = E_SHELL_BACKEND (object);
412 	shell = e_shell_backend_get_shell (shell_backend);
413 
414 	g_signal_connect_swapped (
415 		shell, "handle-uri",
416 		G_CALLBACK (task_shell_backend_handle_uri_cb),
417 		shell_backend);
418 
419 	g_signal_connect_swapped (
420 		shell, "window-added",
421 		G_CALLBACK (task_shell_backend_window_added_cb),
422 		shell_backend);
423 
424 	/* Chain up to parent's constructed() method. */
425 	G_OBJECT_CLASS (e_task_shell_backend_parent_class)->constructed (object);
426 }
427 
428 static void
429 e_task_shell_backend_class_init (ETaskShellBackendClass *class)
430 {
431 	GObjectClass *object_class;
432 	EShellBackendClass *shell_backend_class;
433 
434 	g_type_class_add_private (class, sizeof (ETaskShellBackendPrivate));
435 
436 	object_class = G_OBJECT_CLASS (class);
437 	object_class->constructed = task_shell_backend_constructed;
438 
439 	shell_backend_class = E_SHELL_BACKEND_CLASS (class);
440 	shell_backend_class->shell_view_type = E_TYPE_TASK_SHELL_VIEW;
441 	shell_backend_class->name = "tasks";
442 	shell_backend_class->aliases = "";
443 	shell_backend_class->schemes = "task";
444 	shell_backend_class->sort_order = 500;
445 	shell_backend_class->preferences_page = "calendar-and-tasks";
446 	shell_backend_class->start = NULL;
447 	shell_backend_class->migrate = e_task_shell_backend_migrate;
448 }
449 
450 static void
451 e_task_shell_backend_class_finalize (ETaskShellBackendClass *class)
452 {
453 }
454 
455 static void
456 e_task_shell_backend_init (ETaskShellBackend *task_shell_backend)
457 {
458 	task_shell_backend->priv =
459 		E_TASK_SHELL_BACKEND_GET_PRIVATE (task_shell_backend);
460 }
461 
462 void
463 e_task_shell_backend_type_register (GTypeModule *type_module)
464 {
465 	/* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration
466 	 *     function, so we have to wrap it with a public function in
467 	 *     order to register types from a separate compilation unit. */
468 	e_task_shell_backend_register_type (type_module);
469 }