No issues found
1 /*
2 * ECalListView - display calendar events in an ETable.
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 * Authors:
18 * Hans Petter Jansson <hpj@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "e-cal-list-view.h"
28 #include "ea-calendar.h"
29
30 #include <math.h>
31 #include <time.h>
32 #include <sys/stat.h>
33 #include <gtk/gtk.h>
34 #include <glib/gi18n.h>
35 #include <glib/gstdio.h>
36 #include <gdk/gdkkeysyms.h>
37 #include <table/e-table-memory-store.h>
38 #include <table/e-cell-checkbox.h>
39 #include <table/e-cell-toggle.h>
40 #include <table/e-cell-text.h>
41 #include <table/e-cell-combo.h>
42 #include <table/e-cell-date.h>
43 #include <table/e-cell-date-edit.h>
44 #include <e-util/e-categories-config.h>
45 #include <e-util/e-dialog-utils.h>
46 #include <e-util/e-util-private.h>
47
48 #include "e-cal-model-calendar.h"
49 #include "e-cell-date-edit-text.h"
50 #include "dialogs/delete-comp.h"
51 #include "dialogs/delete-error.h"
52 #include "dialogs/goto-dialog.h"
53 #include "dialogs/send-comp.h"
54 #include "dialogs/cancel-comp.h"
55 #include "dialogs/recur-comp.h"
56 #include "comp-util.h"
57 #include "itip-utils.h"
58 #include "calendar-config.h"
59 #include "misc.h"
60
61 static void e_cal_list_view_dispose (GObject *object);
62
63 static GList *e_cal_list_view_get_selected_events (ECalendarView *cal_view);
64 static gboolean e_cal_list_view_get_selected_time_range (ECalendarView *cal_view, time_t *start_time, time_t *end_time);
65 static gboolean e_cal_list_view_get_visible_time_range (ECalendarView *cal_view, time_t *start_time,
66 time_t *end_time);
67
68 static gboolean e_cal_list_view_popup_menu (GtkWidget *widget);
69
70 static void e_cal_list_view_show_popup_menu (ECalListView *cal_list_view, gint row,
71 GdkEventButton *event);
72 static gboolean e_cal_list_view_on_table_double_click (GtkWidget *table, gint row, gint col,
73 GdkEvent *event, gpointer data);
74 static gboolean e_cal_list_view_on_table_right_click (GtkWidget *table, gint row, gint col,
75 GdkEvent *event, gpointer data);
76 static void e_cal_list_view_cursor_change_cb (ETable *etable, gint row, gpointer data);
77
78 G_DEFINE_TYPE (ECalListView, e_cal_list_view, E_TYPE_CALENDAR_VIEW)
79
80 static void
81 e_cal_list_view_class_init (ECalListViewClass *class)
82 {
83 GObjectClass *object_class;
84 GtkWidgetClass *widget_class;
85 ECalendarViewClass *view_class;
86
87 object_class = (GObjectClass *) class;
88 widget_class = (GtkWidgetClass *) class;
89 view_class = (ECalendarViewClass *) class;
90
91 /* Method override */
92 object_class->dispose = e_cal_list_view_dispose;
93
94 widget_class->popup_menu = e_cal_list_view_popup_menu;
95
96 view_class->get_selected_events = e_cal_list_view_get_selected_events;
97 view_class->get_selected_time_range = e_cal_list_view_get_selected_time_range;
98 view_class->get_visible_time_range = e_cal_list_view_get_visible_time_range;
99 }
100
101 static void
102 e_cal_list_view_init (ECalListView *cal_list_view)
103 {
104 cal_list_view->table = NULL;
105 cal_list_view->cursor_event = NULL;
106 cal_list_view->set_table_id = 0;
107 }
108
109 /* Returns the current time, for the ECellDateEdit items. */
110 static struct tm
111 get_current_time_cb (ECellDateEdit *ecde,
112 gpointer data)
113 {
114 ECalListView *cal_list_view = data;
115 icaltimezone *zone;
116 struct tm tmp_tm = { 0 };
117 struct icaltimetype tt;
118
119 zone = e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view));
120 tt = icaltime_from_timet_with_zone (time (NULL), FALSE, zone);
121
122 /* Now copy it to the struct tm and return it. */
123 tmp_tm.tm_year = tt.year - 1900;
124 tmp_tm.tm_mon = tt.month - 1;
125 tmp_tm.tm_mday = tt.day;
126 tmp_tm.tm_hour = tt.hour;
127 tmp_tm.tm_min = tt.minute;
128 tmp_tm.tm_sec = tt.second;
129 tmp_tm.tm_isdst = -1;
130
131 return tmp_tm;
132 }
133
134 void
135 e_cal_list_view_load_state (ECalListView *cal_list_view,
136 gchar *filename)
137 {
138 struct stat st;
139
140 g_return_if_fail (cal_list_view != NULL);
141 g_return_if_fail (E_IS_CAL_LIST_VIEW (cal_list_view));
142 g_return_if_fail (filename != NULL);
143
144 if (g_stat (filename, &st) == 0 && st.st_size > 0 && S_ISREG (st.st_mode))
145 e_table_load_state (cal_list_view->table, filename);
146 }
147
148 void
149 e_cal_list_view_save_state (ECalListView *cal_list_view,
150 gchar *filename)
151 {
152 g_return_if_fail (cal_list_view != NULL);
153 g_return_if_fail (E_IS_CAL_LIST_VIEW (cal_list_view));
154 g_return_if_fail (filename != NULL);
155
156 e_table_save_state (cal_list_view->table, filename);
157 }
158
159 static void
160 setup_e_table (ECalListView *cal_list_view)
161 {
162 ECalModel *model;
163 ETableExtras *extras;
164 GList *strings;
165 ECell *cell, *popup_cell;
166 GnomeCanvas *canvas;
167 GtkStyle *style;
168 GtkWidget *container;
169 GtkWidget *widget;
170 gchar *etspecfile;
171
172 model = e_calendar_view_get_model (E_CALENDAR_VIEW (cal_list_view));
173
174 /* Create the header columns */
175
176 extras = e_table_extras_new ();
177
178 /* Normal string fields */
179
180 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
181 g_object_set (
182 cell,
183 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
184 NULL);
185
186 e_table_extras_add_cell (extras, "calstring", cell);
187 g_object_unref (cell);
188
189 /* Date fields */
190
191 cell = e_cell_date_edit_text_new (NULL, GTK_JUSTIFY_LEFT);
192 g_object_set (
193 cell,
194 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
195 NULL);
196
197 g_object_bind_property (
198 model, "timezone",
199 cell, "timezone",
200 G_BINDING_BIDIRECTIONAL |
201 G_BINDING_SYNC_CREATE);
202
203 g_object_bind_property (
204 model, "use-24-hour-format",
205 cell, "use-24-hour-format",
206 G_BINDING_BIDIRECTIONAL |
207 G_BINDING_SYNC_CREATE);
208
209 popup_cell = e_cell_date_edit_new ();
210 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
211 g_object_unref (cell);
212
213 g_object_bind_property (
214 model, "use-24-hour-format",
215 popup_cell, "use-24-hour-format",
216 G_BINDING_BIDIRECTIONAL |
217 G_BINDING_SYNC_CREATE);
218
219 e_table_extras_add_cell (extras, "dateedit", popup_cell);
220 g_object_unref (popup_cell);
221 cal_list_view->dates_cell = E_CELL_DATE_EDIT (popup_cell);
222
223 gtk_widget_hide (E_CELL_DATE_EDIT (popup_cell)->none_button);
224
225 e_cell_date_edit_set_get_time_callback (
226 E_CELL_DATE_EDIT (popup_cell),
227 get_current_time_cb,
228 cal_list_view, NULL);
229
230 /* Combo fields */
231
232 cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
233 g_object_set (
234 cell,
235 "bg_color_column", E_CAL_MODEL_FIELD_COLOR,
236 "editable", FALSE,
237 NULL);
238
239 popup_cell = e_cell_combo_new ();
240 e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
241 g_object_unref (cell);
242
243 strings = NULL;
244 strings = g_list_append (strings, (gchar *) _("Public"));
245 strings = g_list_append (strings, (gchar *) _("Private"));
246 strings = g_list_append (strings, (gchar *) _("Confidential"));
247 e_cell_combo_set_popdown_strings (
248 E_CELL_COMBO (popup_cell),
249 strings);
250 g_list_free (strings);
251
252 e_table_extras_add_cell (extras, "classification", popup_cell);
253 g_object_unref (popup_cell);
254
255 /* Sorting */
256
257 e_table_extras_add_compare (
258 extras, "date-compare",
259 e_cell_date_edit_compare_cb);
260
261 /* set proper format component for a default 'date' cell renderer */
262 cell = e_table_extras_get_cell (extras, "date");
263 e_cell_date_set_format_component (E_CELL_DATE (cell), "calendar");
264
265 /* Create table view */
266
267 container = GTK_WIDGET (cal_list_view);
268
269 widget = gtk_scrolled_window_new (NULL, NULL);
270 gtk_scrolled_window_set_policy (
271 GTK_SCROLLED_WINDOW (widget),
272 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
273 gtk_scrolled_window_set_shadow_type (
274 GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
275 gtk_table_attach (
276 GTK_TABLE (container), widget, 0, 2, 0, 2,
277 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 1, 1);
278 gtk_widget_show (widget);
279
280 container = widget;
281
282 etspecfile = g_build_filename (
283 EVOLUTION_ETSPECDIR, "e-cal-list-view.etspec", NULL);
284 widget = e_table_new_from_spec_file (
285 E_TABLE_MODEL (model), extras, etspecfile, NULL);
286 gtk_container_add (GTK_CONTAINER (container), widget);
287 cal_list_view->table = E_TABLE (widget);
288 gtk_widget_show (widget);
289 g_free (etspecfile);
290
291 /* Make sure text is readable on top of our color coding */
292
293 canvas = GNOME_CANVAS (cal_list_view->table->table_canvas);
294 style = gtk_widget_get_style (GTK_WIDGET (canvas));
295
296 style->fg[GTK_STATE_SELECTED] = style->text[GTK_STATE_NORMAL];
297 style->fg[GTK_STATE_ACTIVE] = style->text[GTK_STATE_NORMAL];
298 gtk_widget_set_style (GTK_WIDGET (canvas), style);
299
300 /* Connect signals */
301 g_signal_connect (
302 cal_list_view->table, "double_click",
303 G_CALLBACK (e_cal_list_view_on_table_double_click),
304 cal_list_view);
305 g_signal_connect (
306 cal_list_view->table, "right-click",
307 G_CALLBACK (e_cal_list_view_on_table_right_click),
308 cal_list_view);
309 g_signal_connect_after (
310 cal_list_view->table, "cursor_change",
311 G_CALLBACK (e_cal_list_view_cursor_change_cb),
312 cal_list_view);
313 }
314
315 /**
316 * e_cal_list_view_new:
317 * @Returns: a new #ECalListView.
318 *
319 * Creates a new #ECalListView.
320 **/
321 ECalendarView *
322 e_cal_list_view_new (ECalModel *model)
323 {
324 ECalendarView *cal_list_view;
325
326 cal_list_view = g_object_new (
327 E_TYPE_CAL_LIST_VIEW, "model", model, NULL);
328 setup_e_table (E_CAL_LIST_VIEW (cal_list_view));
329
330 return cal_list_view;
331 }
332
333 static void
334 e_cal_list_view_dispose (GObject *object)
335 {
336 ECalListView *cal_list_view;
337
338 cal_list_view = E_CAL_LIST_VIEW (object);
339
340 if (cal_list_view->set_table_id) {
341 g_source_remove (cal_list_view->set_table_id);
342 cal_list_view->set_table_id = 0;
343 }
344
345 if (cal_list_view->cursor_event) {
346 g_free (cal_list_view->cursor_event);
347 cal_list_view->cursor_event = NULL;
348 }
349
350 if (cal_list_view->table) {
351 gtk_widget_destroy (GTK_WIDGET (cal_list_view->table));
352 cal_list_view->table = NULL;
353 }
354
355 /* Chain up to parent's dispose() method. */
356 G_OBJECT_CLASS (e_cal_list_view_parent_class)->dispose (object);
357 }
358
359 static void
360 e_cal_list_view_show_popup_menu (ECalListView *cal_list_view,
361 gint row,
362 GdkEventButton *event)
363 {
364 e_calendar_view_popup_event (E_CALENDAR_VIEW (cal_list_view), event);
365 }
366
367 static gboolean
368 e_cal_list_view_popup_menu (GtkWidget *widget)
369 {
370 ECalListView *cal_list_view = E_CAL_LIST_VIEW (widget);
371
372 e_cal_list_view_show_popup_menu (cal_list_view, -1, NULL);
373 return TRUE;
374 }
375
376 static gboolean
377 e_cal_list_view_on_table_double_click (GtkWidget *table,
378 gint row,
379 gint col,
380 GdkEvent *event,
381 gpointer data)
382 {
383 ECalListView *cal_list_view = E_CAL_LIST_VIEW (data);
384 ECalModelComponent *comp_data;
385
386 comp_data = e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (cal_list_view)), row);
387 e_calendar_view_edit_appointment (E_CALENDAR_VIEW (cal_list_view), comp_data->client, comp_data->icalcomp, EDIT_EVENT_AUTODETECT);
388
389 return TRUE;
390 }
391
392 static gboolean
393 e_cal_list_view_on_table_right_click (GtkWidget *table,
394 gint row,
395 gint col,
396 GdkEvent *event,
397 gpointer data)
398 {
399 ECalListView *cal_list_view = E_CAL_LIST_VIEW (data);
400
401 e_cal_list_view_show_popup_menu (cal_list_view, row, (GdkEventButton *) event);
402 return TRUE;
403 }
404
405 static void
406 e_cal_list_view_cursor_change_cb (ETable *etable,
407 gint row,
408 gpointer data)
409 {
410 ECalListView *cal_list_view = E_CAL_LIST_VIEW (data);
411
412 g_signal_emit_by_name (cal_list_view, "selection_changed");
413 }
414
415 static gboolean
416 e_cal_list_view_get_selected_time_range (ECalendarView *cal_view,
417 time_t *start_time,
418 time_t *end_time)
419 {
420 GList *selected;
421 icaltimezone *zone;
422
423 selected = e_calendar_view_get_selected_events (cal_view);
424 if (selected) {
425 ECalendarViewEvent *event = (ECalendarViewEvent *) selected->data;
426 ECalComponentDateTime dtstart, dtend;
427 ECalComponent *comp;
428
429 if (!is_comp_data_valid (event))
430 return FALSE;
431
432 comp = e_cal_component_new ();
433 e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (event->comp_data->icalcomp));
434 if (start_time) {
435 e_cal_component_get_dtstart (comp, &dtstart);
436 if (dtstart.tzid) {
437 zone = icalcomponent_get_timezone (e_cal_component_get_icalcomponent (comp), dtstart.tzid);
438 } else {
439 zone = NULL;
440 }
441 *start_time = icaltime_as_timet_with_zone (*dtstart.value, zone);
442 e_cal_component_free_datetime (&dtstart);
443 }
444 if (end_time) {
445 e_cal_component_get_dtend (comp, &dtend);
446 if (dtend.tzid) {
447 zone = icalcomponent_get_timezone (e_cal_component_get_icalcomponent (comp), dtend.tzid);
448 } else {
449 zone = NULL;
450 }
451 *end_time = icaltime_as_timet_with_zone (*dtend.value, zone);
452 e_cal_component_free_datetime (&dtend);
453 }
454
455 g_object_unref (comp);
456 g_list_free (selected);
457
458 return TRUE;
459 }
460
461 return FALSE;
462 }
463
464 static GList *
465 e_cal_list_view_get_selected_events (ECalendarView *cal_view)
466 {
467 GList *event_list = NULL;
468 gint cursor_row;
469
470 if (E_CAL_LIST_VIEW (cal_view)->cursor_event) {
471 g_free (E_CAL_LIST_VIEW (cal_view)->cursor_event);
472 E_CAL_LIST_VIEW (cal_view)->cursor_event = NULL;
473 }
474
475 cursor_row = e_table_get_cursor_row (
476 E_CAL_LIST_VIEW (cal_view)->table);
477
478 if (cursor_row >= 0) {
479 ECalendarViewEvent *event;
480
481 event = E_CAL_LIST_VIEW (cal_view)->cursor_event = g_new0 (ECalendarViewEvent, 1);
482 event->comp_data =
483 e_cal_model_get_component_at (
484 e_calendar_view_get_model (cal_view),
485 cursor_row);
486 event_list = g_list_prepend (event_list, event);
487 }
488
489 return event_list;
490 }
491
492 static void
493 adjust_range (icaltimetype icaltime,
494 time_t *earliest,
495 time_t *latest,
496 gboolean *set)
497 {
498 time_t t;
499
500 if (!icaltime_is_valid_time (icaltime))
501 return;
502
503 t = icaltime_as_timet (icaltime);
504 *earliest = MIN (*earliest, t);
505 *latest = MAX (*latest, t);
506
507 *set = TRUE;
508 }
509
510 /* NOTE: Time use for this function increases linearly with number of events.
511 * This is not ideal, since it's used in a couple of places. We could probably
512 * be smarter about it, and use do it less frequently... */
513 static gboolean
514 e_cal_list_view_get_visible_time_range (ECalendarView *cal_view,
515 time_t *start_time,
516 time_t *end_time)
517 {
518 time_t earliest = G_MAXINT, latest = 0;
519 gboolean set = FALSE;
520 gint n_rows, i;
521
522 n_rows = e_table_model_row_count (E_TABLE_MODEL (e_calendar_view_get_model (cal_view)));
523
524 for (i = 0; i < n_rows; i++) {
525 ECalModelComponent *comp;
526 icalcomponent *icalcomp;
527
528 comp = e_cal_model_get_component_at (e_calendar_view_get_model (cal_view), i);
529 if (!comp)
530 continue;
531
532 icalcomp = comp->icalcomp;
533 if (!icalcomp)
534 continue;
535
536 adjust_range (icalcomponent_get_dtstart (icalcomp), &earliest, &latest, &set);
537 adjust_range (icalcomponent_get_dtend (icalcomp), &earliest, &latest, &set);
538 }
539
540 if (set) {
541 *start_time = earliest;
542 *end_time = latest;
543 return TRUE;
544 }
545
546 if (!n_rows) {
547 ECalModel *model = e_calendar_view_get_model (cal_view);
548
549 /* Use time range set in the model when nothing shown in the list view */
550 e_cal_model_get_time_range (model, start_time, end_time);
551
552 return TRUE;
553 }
554
555 return FALSE;
556 }
557
558 gboolean
559 e_cal_list_view_get_range_shown (ECalListView *cal_list_view,
560 GDate *start_date,
561 gint *days_shown)
562 {
563 time_t first, last;
564 GDate end_date;
565
566 if (!e_cal_list_view_get_visible_time_range (E_CALENDAR_VIEW (cal_list_view), &first, &last))
567 return FALSE;
568
569 time_to_gdate_with_zone (start_date, first, e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view)));
570 time_to_gdate_with_zone (&end_date, last, e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view)));
571
572 *days_shown = g_date_days_between (start_date, &end_date);
573 return TRUE;
574 }