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 }