No issues found
1 /*
2 * Evolution calendar - Task editor dialog
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 * Authors:
19 * Miguel de Icaza <miguel@ximian.com>
20 * Federico Mena-Quintero <federico@ximian.com>
21 * Seth Alves <alves@hungry.com>
22 * JP Rosevear <jpr@ximian.com>
23 *
24 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
25 *
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <string.h>
33 #include <glib/gi18n.h>
34
35 #include "e-util/e-plugin-ui.h"
36 #include "e-util/e-util-private.h"
37
38 #include "task-page.h"
39 #include "task-details-page.h"
40 #include "cancel-comp.h"
41 #include "task-editor.h"
42
43 #define TASK_EDITOR_GET_PRIVATE(obj) \
44 (G_TYPE_INSTANCE_GET_PRIVATE \
45 ((obj), TYPE_TASK_EDITOR, TaskEditorPrivate))
46
47 struct _TaskEditorPrivate {
48 TaskPage *task_page;
49 TaskDetailsPage *task_details_page;
50 GtkWidget *task_details_window;
51
52 EMeetingStore *model;
53 gboolean assignment_shown;
54 gboolean updating;
55 };
56
57 /* Extends the UI definition in CompEditor */
58 static const gchar *ui =
59 "<ui>"
60 " <menubar action='main-menu'>"
61 " <menu action='view-menu'>"
62 " <menuitem action='view-type'/>"
63 " <menuitem action='view-status'/>"
64 " <menuitem action='view-role'/>"
65 " <menuitem action='view-rsvp'/>"
66 " <separator/>"
67 " <menuitem action='view-time-zone'/>"
68 " <menuitem action='view-categories'/>"
69 " </menu>"
70 " <menu action='insert-menu'>"
71 " <menuitem action='send-options'/>"
72 " </menu>"
73 " <menu action='options-menu'>"
74 " <menu action='classification-menu'>"
75 " <menuitem action='classify-public'/>"
76 " <menuitem action='classify-private'/>"
77 " <menuitem action='classify-confidential'/>"
78 " </menu>"
79 " <menuitem action='option-status'/>"
80 " </menu>"
81 " </menubar>"
82 " <toolbar name='main-toolbar'>"
83 " <placeholder name='content'>"
84 " <toolitem action='view-time-zone'/>"
85 " <toolitem action='option-status'/>"
86 " </placeholder>"
87 " </toolbar>"
88 "</ui>";
89
90 static void task_editor_edit_comp (CompEditor *editor,
91 ECalComponent *comp);
92 static gboolean task_editor_send_comp (CompEditor *editor,
93 ECalComponentItipMethod method,
94 gboolean strip_alarms);
95
96 G_DEFINE_TYPE (TaskEditor, task_editor, TYPE_COMP_EDITOR)
97
98 static void
99 action_option_status_cb (GtkAction *action,
100 TaskEditor *editor)
101 {
102 gtk_widget_show (editor->priv->task_details_window);
103 }
104
105 static void
106 action_send_options_cb (GtkAction *action,
107 TaskEditor *editor)
108 {
109 task_page_send_options_clicked_cb (editor->priv->task_page);
110 }
111
112 static GtkActionEntry task_entries[] = {
113
114 { "option-status",
115 "stock_view-details",
116 N_("_Status Details"),
117 "<Control>t",
118 N_("Click to change or view the status details of the task"),
119 G_CALLBACK (action_option_status_cb) }
120 };
121
122 static GtkActionEntry assigned_task_entries[] = {
123
124 { "send-options",
125 NULL,
126 N_("_Send Options"),
127 NULL,
128 N_("Insert advanced send options"),
129 G_CALLBACK (action_send_options_cb) }
130 };
131
132 static void
133 task_editor_model_changed_cb (TaskEditor *te)
134 {
135 if (!te->priv->updating) {
136 comp_editor_set_changed (COMP_EDITOR (te), TRUE);
137 comp_editor_set_needs_send (COMP_EDITOR (te), TRUE);
138 }
139 }
140
141 static void
142 task_editor_dispose (GObject *object)
143 {
144 TaskEditorPrivate *priv;
145
146 priv = TASK_EDITOR_GET_PRIVATE (object);
147
148 if (priv->task_page) {
149 g_object_unref (priv->task_page);
150 priv->task_page = NULL;
151 }
152
153 if (priv->task_details_page) {
154 g_object_unref (priv->task_details_page);
155 priv->task_details_page = NULL;
156 }
157
158 if (priv->model) {
159 g_object_unref (priv->model);
160 priv->model = NULL;
161 }
162
163 /* Chain up to parent's dispose() method. */
164 G_OBJECT_CLASS (task_editor_parent_class)->dispose (object);
165 }
166
167 static void
168 task_editor_constructed (GObject *object)
169 {
170 TaskEditorPrivate *priv;
171 CompEditor *editor;
172 CompEditorFlags flags;
173 GtkActionGroup *action_group;
174 gboolean is_assigned;
175
176 priv = TASK_EDITOR_GET_PRIVATE (object);
177 editor = COMP_EDITOR (object);
178
179 flags = comp_editor_get_flags (editor);
180 is_assigned = flags & COMP_EDITOR_IS_ASSIGNED;
181
182 priv->task_page = task_page_new (priv->model, editor);
183 task_page_set_assignment (priv->task_page, is_assigned);
184 comp_editor_append_page (
185 editor, COMP_EDITOR_PAGE (priv->task_page),
186 _("Task"), TRUE);
187
188 action_group = comp_editor_get_action_group (editor, "coordinated");
189 gtk_action_group_set_visible (action_group, is_assigned);
190
191 if (is_assigned) {
192 ECalClient *client;
193
194 client = comp_editor_get_client (editor);
195
196 if (e_client_check_capability (
197 E_CLIENT (client),
198 CAL_STATIC_CAPABILITY_REQ_SEND_OPTIONS))
199 task_page_show_options (priv->task_page);
200 comp_editor_set_group_item (editor, TRUE);
201 }
202
203 g_object_bind_property (
204 object, "client",
205 priv->model, "client",
206 G_BINDING_SYNC_CREATE);
207
208 /* Chain up to parent's constructed() method. */
209 G_OBJECT_CLASS (task_editor_parent_class)->constructed (object);
210 }
211
212 static void
213 task_editor_show_categories (CompEditor *editor,
214 gboolean visible)
215 {
216 TaskEditor *task_editor = TASK_EDITOR (editor);
217
218 task_page_set_show_categories (task_editor->priv->task_page, visible);
219 }
220
221 static void
222 task_editor_show_role (CompEditor *editor,
223 gboolean visible)
224 {
225 TaskEditor *task_editor = TASK_EDITOR (editor);
226
227 task_page_set_view_role (task_editor->priv->task_page, visible);
228 }
229
230 static void
231 task_editor_show_rsvp (CompEditor *editor,
232 gboolean visible)
233 {
234 TaskEditor *task_editor = TASK_EDITOR (editor);
235
236 task_page_set_view_rsvp (task_editor->priv->task_page, visible);
237 }
238
239 static void
240 task_editor_show_status (CompEditor *editor,
241 gboolean visible)
242 {
243 TaskEditor *task_editor = TASK_EDITOR (editor);
244
245 task_page_set_view_status (task_editor->priv->task_page, visible);
246 }
247
248 static void
249 task_editor_show_time_zone (CompEditor *editor,
250 gboolean visible)
251 {
252 TaskEditor *task_editor = TASK_EDITOR (editor);
253
254 task_page_set_show_timezone (task_editor->priv->task_page, visible);
255 }
256
257 static void
258 task_editor_show_type (CompEditor *editor,
259 gboolean visible)
260 {
261 TaskEditor *task_editor = TASK_EDITOR (editor);
262
263 task_page_set_view_type (task_editor->priv->task_page, visible);
264 }
265
266 static void
267 task_editor_class_init (TaskEditorClass *class)
268 {
269 GObjectClass *object_class;
270 CompEditorClass *editor_class;
271
272 g_type_class_add_private (class, sizeof (TaskEditorPrivate));
273
274 object_class = G_OBJECT_CLASS (class);
275 object_class->dispose = task_editor_dispose;
276 object_class->constructed = task_editor_constructed;
277
278 editor_class = COMP_EDITOR_CLASS (class);
279 editor_class->help_section = "tasks-usage";
280 editor_class->edit_comp = task_editor_edit_comp;
281 editor_class->send_comp = task_editor_send_comp;
282 editor_class->show_categories = task_editor_show_categories;
283 editor_class->show_role = task_editor_show_role;
284 editor_class->show_rsvp = task_editor_show_rsvp;
285 editor_class->show_status = task_editor_show_status;
286 editor_class->show_time_zone = task_editor_show_time_zone;
287 editor_class->show_type = task_editor_show_type;
288 }
289
290 static void
291 task_editor_init (TaskEditor *te)
292 {
293 CompEditor *editor = COMP_EDITOR (te);
294 GtkUIManager *ui_manager;
295 GtkActionGroup *action_group;
296 GtkWidget *content_area;
297 GtkAction *action;
298 const gchar *id;
299 GError *error = NULL;
300
301 te->priv = TASK_EDITOR_GET_PRIVATE (te);
302 te->priv->model = E_MEETING_STORE (e_meeting_store_new ());
303 te->priv->assignment_shown = TRUE;
304 te->priv->updating = FALSE;
305
306 te->priv->task_details_window = gtk_dialog_new_with_buttons (
307 _("Task Details"), GTK_WINDOW (te), GTK_DIALOG_MODAL,
308 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
309 g_signal_connect (
310 te->priv->task_details_window, "response",
311 G_CALLBACK (gtk_widget_hide), NULL);
312 g_signal_connect (
313 te->priv->task_details_window, "delete-event",
314 G_CALLBACK (gtk_widget_hide), NULL);
315
316 te->priv->task_details_page = task_details_page_new (editor);
317 content_area = gtk_dialog_get_content_area (
318 GTK_DIALOG (te->priv->task_details_window));
319 gtk_container_add (
320 GTK_CONTAINER (content_area),
321 comp_editor_page_get_widget (
322 (CompEditorPage *) te->priv->task_details_page));
323 gtk_widget_show_all (
324 gtk_bin_get_child (GTK_BIN (te->priv->task_details_window)));
325 comp_editor_append_page (
326 editor, COMP_EDITOR_PAGE (te->priv->task_details_page), NULL, FALSE);
327
328 action_group = comp_editor_get_action_group (editor, "individual");
329 gtk_action_group_add_actions (
330 action_group, task_entries,
331 G_N_ELEMENTS (task_entries), te);
332
333 action_group = comp_editor_get_action_group (editor, "coordinated");
334 gtk_action_group_add_actions (
335 action_group, assigned_task_entries,
336 G_N_ELEMENTS (assigned_task_entries), te);
337
338 ui_manager = comp_editor_get_ui_manager (editor);
339 gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, &error);
340
341 id = "org.gnome.evolution.task-editor";
342 e_plugin_ui_register_manager (ui_manager, id, te);
343 e_plugin_ui_enable_manager (ui_manager, id);
344
345 if (error != NULL) {
346 g_critical ("%s: %s", G_STRFUNC, error->message);
347 g_error_free (error);
348 }
349
350 action = comp_editor_get_action (editor, "print");
351 gtk_action_set_tooltip (action, _("Print this task"));
352
353 g_signal_connect_swapped (
354 te->priv->model, "row_changed",
355 G_CALLBACK (task_editor_model_changed_cb), te);
356 g_signal_connect_swapped (
357 te->priv->model, "row_inserted",
358 G_CALLBACK (task_editor_model_changed_cb), te);
359 g_signal_connect_swapped (
360 te->priv->model, "row_deleted",
361 G_CALLBACK (task_editor_model_changed_cb), te);
362 }
363
364 static void
365 task_editor_edit_comp (CompEditor *editor,
366 ECalComponent *comp)
367 {
368 TaskEditorPrivate *priv;
369 ECalComponentOrganizer organizer;
370 ECalClient *client;
371 GSList *attendees = NULL;
372 ESourceRegistry *registry;
373 EShell *shell;
374
375 priv = TASK_EDITOR_GET_PRIVATE (editor);
376
377 priv->updating = TRUE;
378
379 if (COMP_EDITOR_CLASS (task_editor_parent_class)->edit_comp)
380 COMP_EDITOR_CLASS (task_editor_parent_class)->edit_comp (editor, comp);
381
382 shell = comp_editor_get_shell (editor);
383 client = comp_editor_get_client (editor);
384
385 registry = e_shell_get_registry (shell);
386
387 /* Get meeting related stuff */
388 e_cal_component_get_organizer (comp, &organizer);
389 e_cal_component_get_attendee_list (comp, &attendees);
390
391 if (attendees != NULL) {
392 GSList *l;
393 gint row;
394
395 task_page_hide_options (priv->task_page);
396 task_page_set_assignment (priv->task_page, TRUE);
397
398 for (l = attendees; l != NULL; l = l->next) {
399 ECalComponentAttendee *ca = l->data;
400 EMeetingAttendee *ia;
401
402 ia = E_MEETING_ATTENDEE (
403 e_meeting_attendee_new_from_e_cal_component_attendee (ca));
404 /* If we aren't the organizer or the attendee is just
405 * delegating, don't allow editing. */
406 if (!comp_editor_get_user_org (editor) ||
407 e_meeting_attendee_is_set_delto (ia))
408 e_meeting_attendee_set_edit_level (
409 ia, E_MEETING_ATTENDEE_EDIT_NONE);
410 comp_editor_page_add_attendee (
411 COMP_EDITOR_PAGE (priv->task_page), ia);
412
413 g_object_unref (ia);
414 }
415
416 /* If we aren't the organizer we can still change our own status */
417 if (!comp_editor_get_user_org (editor)) {
418 EMeetingAttendee *ia;
419
420 ia = e_meeting_store_find_self (priv->model, &row);
421
422 if (ia != NULL)
423 e_meeting_attendee_set_edit_level (
424 ia, E_MEETING_ATTENDEE_EDIT_STATUS);
425 } else if (e_cal_client_check_organizer_must_attend (client)) {
426 EMeetingAttendee *ia;
427
428 ia = e_meeting_store_find_attendee (priv->model, organizer.value, &row);
429 if (ia != NULL)
430 e_meeting_attendee_set_edit_level (
431 ia, E_MEETING_ATTENDEE_EDIT_NONE);
432 }
433
434 comp_editor_set_group_item (editor, TRUE);
435 priv->assignment_shown = TRUE;
436 }
437 e_cal_component_free_attendee_list (attendees);
438
439 comp_editor_set_needs_send (
440 editor, priv->assignment_shown &&
441 itip_organizer_is_user (registry, comp, client));
442
443 priv->updating = FALSE;
444 }
445
446 static gboolean
447 task_editor_send_comp (CompEditor *editor,
448 ECalComponentItipMethod method,
449 gboolean strip_alarms)
450 {
451 TaskEditorPrivate *priv;
452 EShell *shell;
453 ESourceRegistry *registry;
454 ECalComponent *comp = NULL;
455
456 priv = TASK_EDITOR_GET_PRIVATE (editor);
457
458 /* Don't cancel more than once or when just publishing */
459 if (method == E_CAL_COMPONENT_METHOD_PUBLISH ||
460 method == E_CAL_COMPONENT_METHOD_CANCEL)
461 goto parent;
462
463 shell = comp_editor_get_shell (editor);
464 registry = e_shell_get_registry (shell);
465
466 comp = task_page_get_cancel_comp (priv->task_page);
467 if (comp != NULL) {
468 ECalClient *client;
469 gboolean result;
470
471 client = e_meeting_store_get_client (priv->model);
472 result = itip_send_comp (
473 registry, E_CAL_COMPONENT_METHOD_CANCEL, comp,
474 client, NULL, NULL, NULL, strip_alarms, FALSE);
475 g_object_unref (comp);
476
477 if (!result)
478 return FALSE;
479 }
480
481 parent:
482 if (COMP_EDITOR_CLASS (task_editor_parent_class)->send_comp)
483 return COMP_EDITOR_CLASS (task_editor_parent_class)->
484 send_comp (editor, method, strip_alarms);
485
486 return FALSE;
487 }
488
489 /**
490 * task_editor_new:
491 * @client: a ECalClient
492 *
493 * Creates a new event editor dialog.
494 *
495 * Return value: A newly-created event editor dialog, or NULL if the event
496 * editor could not be created.
497 **/
498 CompEditor *
499 task_editor_new (ECalClient *client,
500 EShell *shell,
501 CompEditorFlags flags)
502 {
503 g_return_val_if_fail (E_IS_CAL_CLIENT (client), NULL);
504 g_return_val_if_fail (E_IS_SHELL (shell), NULL);
505
506 return g_object_new (
507 TYPE_TASK_EDITOR,
508 "client", client, "flags", flags, "shell", shell, NULL);
509 }
510
511 void
512 task_editor_show_assignment (TaskEditor *te)
513 {
514 CompEditor *editor;
515
516 g_return_if_fail (IS_TASK_EDITOR (te));
517
518 editor = COMP_EDITOR (te);
519
520 task_page_set_assignment (te->priv->task_page, TRUE);
521 if (!te->priv->assignment_shown) {
522 te->priv->assignment_shown = TRUE;
523 comp_editor_set_needs_send (editor, TRUE);
524 comp_editor_set_changed (editor, FALSE);
525 }
526
527 }