evolution-3.6.4/calendar/gui/e-cal-list-view.c

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 }