2 * ECalListView - display calendar events in an ETable.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Hans Petter Jansson <hpj@ximian.com>
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 #include "evolution-config.h"
24 #include "e-cal-list-view.h"
25 #include "ea-calendar.h"
31 #include <glib/gi18n.h>
32 #include <glib/gstdio.h>
33 #include <gdk/gdkkeysyms.h>
35 #include "e-cal-model-calendar.h"
36 #include "e-cell-date-edit-text.h"
37 #include "comp-util.h"
38 #include "itip-utils.h"
39 #include "calendar-config.h"
47 static void e_cal_list_view_dispose (GObject
*object
);
49 static GList
*e_cal_list_view_get_selected_events (ECalendarView
*cal_view
);
50 static gboolean
e_cal_list_view_get_selected_time_range (ECalendarView
*cal_view
, time_t *start_time
, time_t *end_time
);
51 static gboolean
e_cal_list_view_get_visible_time_range (ECalendarView
*cal_view
, time_t *start_time
,
54 static gboolean
e_cal_list_view_popup_menu (GtkWidget
*widget
);
56 static gboolean
e_cal_list_view_on_table_double_click (GtkWidget
*table
, gint row
, gint col
,
57 GdkEvent
*event
, gpointer data
);
58 static gboolean
e_cal_list_view_on_table_right_click (GtkWidget
*table
, gint row
, gint col
,
59 GdkEvent
*event
, gpointer data
);
60 static gboolean
e_cal_list_view_on_table_white_space_event (ETable
*table
, GdkEvent
*event
, gpointer data
);
61 static void e_cal_list_view_cursor_change_cb (ETable
*etable
, gint row
, gpointer data
);
63 G_DEFINE_TYPE (ECalListView
, e_cal_list_view
, E_TYPE_CALENDAR_VIEW
)
66 e_cal_list_view_get_property (GObject
*object
,
71 ECalListView
*eclv
= E_CAL_LIST_VIEW (object
);
73 switch (property_id
) {
75 g_value_set_boolean (value
, e_cal_list_view_is_editing (eclv
));
78 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
84 e_cal_list_view_class_init (ECalListViewClass
*class)
86 GObjectClass
*object_class
;
87 GtkWidgetClass
*widget_class
;
88 ECalendarViewClass
*view_class
;
90 object_class
= (GObjectClass
*) class;
91 widget_class
= (GtkWidgetClass
*) class;
92 view_class
= (ECalendarViewClass
*) class;
95 object_class
->dispose
= e_cal_list_view_dispose
;
96 object_class
->get_property
= e_cal_list_view_get_property
;
98 widget_class
->popup_menu
= e_cal_list_view_popup_menu
;
100 view_class
->get_selected_events
= e_cal_list_view_get_selected_events
;
101 view_class
->get_selected_time_range
= e_cal_list_view_get_selected_time_range
;
102 view_class
->get_visible_time_range
= e_cal_list_view_get_visible_time_range
;
104 g_object_class_override_property (
111 e_cal_list_view_init (ECalListView
*cal_list_view
)
113 cal_list_view
->table
= NULL
;
114 cal_list_view
->cursor_event
= NULL
;
115 cal_list_view
->set_table_id
= 0;
118 /* Returns the current time, for the ECellDateEdit items. */
120 get_current_time_cb (ECellDateEdit
*ecde
,
123 ECalListView
*cal_list_view
= data
;
125 struct tm tmp_tm
= { 0 };
126 struct icaltimetype tt
;
128 zone
= e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view
));
129 tt
= icaltime_from_timet_with_zone (time (NULL
), FALSE
, zone
);
131 /* Now copy it to the struct tm and return it. */
132 tmp_tm
.tm_year
= tt
.year
- 1900;
133 tmp_tm
.tm_mon
= tt
.month
- 1;
134 tmp_tm
.tm_mday
= tt
.day
;
135 tmp_tm
.tm_hour
= tt
.hour
;
136 tmp_tm
.tm_min
= tt
.minute
;
137 tmp_tm
.tm_sec
= tt
.second
;
138 tmp_tm
.tm_isdst
= -1;
144 e_cal_list_view_table_editing_changed_cb (ETable
*table
,
148 g_return_if_fail (E_IS_CAL_LIST_VIEW (eclv
));
150 g_object_notify (G_OBJECT (eclv
), "is-editing");
154 setup_e_table (ECalListView
*cal_list_view
)
157 ETableExtras
*extras
;
158 ETableSpecification
*specification
;
160 ECell
*cell
, *popup_cell
;
161 GtkWidget
*container
;
164 GError
*local_error
= NULL
;
166 model
= e_calendar_view_get_model (E_CALENDAR_VIEW (cal_list_view
));
168 /* Create the header columns */
170 extras
= e_table_extras_new ();
172 /* Normal string fields */
174 cell
= e_cell_text_new (NULL
, GTK_JUSTIFY_LEFT
);
177 "bg_color_column", E_CAL_MODEL_FIELD_COLOR
,
180 e_table_extras_add_cell (extras
, "calstring", cell
);
181 g_object_unref (cell
);
185 cell
= e_cell_date_edit_text_new (NULL
, GTK_JUSTIFY_LEFT
);
188 "bg_color_column", E_CAL_MODEL_FIELD_COLOR
,
191 e_binding_bind_property (
194 G_BINDING_BIDIRECTIONAL
|
195 G_BINDING_SYNC_CREATE
);
197 e_binding_bind_property (
198 model
, "use-24-hour-format",
199 cell
, "use-24-hour-format",
200 G_BINDING_BIDIRECTIONAL
|
201 G_BINDING_SYNC_CREATE
);
203 popup_cell
= e_cell_date_edit_new ();
204 e_cell_popup_set_child (E_CELL_POPUP (popup_cell
), cell
);
205 g_object_unref (cell
);
207 e_binding_bind_property (
208 model
, "use-24-hour-format",
209 popup_cell
, "use-24-hour-format",
210 G_BINDING_BIDIRECTIONAL
|
211 G_BINDING_SYNC_CREATE
);
213 e_table_extras_add_cell (extras
, "dateedit", popup_cell
);
214 g_object_unref (popup_cell
);
215 cal_list_view
->dates_cell
= E_CELL_DATE_EDIT (popup_cell
);
217 gtk_widget_hide (E_CELL_DATE_EDIT (popup_cell
)->none_button
);
219 e_cell_date_edit_set_get_time_callback (
220 E_CELL_DATE_EDIT (popup_cell
),
222 cal_list_view
, NULL
);
226 cell
= e_cell_text_new (NULL
, GTK_JUSTIFY_LEFT
);
229 "bg_color_column", E_CAL_MODEL_FIELD_COLOR
,
233 popup_cell
= e_cell_combo_new ();
234 e_cell_popup_set_child (E_CELL_POPUP (popup_cell
), cell
);
235 g_object_unref (cell
);
238 strings
= g_list_append (strings
, (gchar
*) _("Public"));
239 strings
= g_list_append (strings
, (gchar
*) _("Private"));
240 strings
= g_list_append (strings
, (gchar
*) _("Confidential"));
241 e_cell_combo_set_popdown_strings (
242 E_CELL_COMBO (popup_cell
),
244 g_list_free (strings
);
246 e_table_extras_add_cell (extras
, "classification", popup_cell
);
247 g_object_unref (popup_cell
);
251 e_table_extras_add_compare (
252 extras
, "date-compare",
253 e_cell_date_edit_compare_cb
);
255 /* set proper format component for a default 'date' cell renderer */
256 cell
= e_table_extras_get_cell (extras
, "date");
257 e_cell_date_set_format_component (E_CELL_DATE (cell
), "calendar");
259 /* Create table view */
261 container
= GTK_WIDGET (cal_list_view
);
263 widget
= gtk_scrolled_window_new (NULL
, NULL
);
264 gtk_scrolled_window_set_policy (
265 GTK_SCROLLED_WINDOW (widget
),
266 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
267 gtk_grid_attach (GTK_GRID (container
), widget
, 0, 0, 2, 2);
268 g_object_set (G_OBJECT (widget
),
271 "halign", GTK_ALIGN_FILL
,
272 "valign", GTK_ALIGN_FILL
,
274 gtk_widget_show (widget
);
278 etspecfile
= g_build_filename (
279 EVOLUTION_ETSPECDIR
, "e-cal-list-view.etspec", NULL
);
280 specification
= e_table_specification_new (etspecfile
, &local_error
);
282 /* Failure here is fatal. */
283 if (local_error
!= NULL
) {
284 g_error ("%s: %s", etspecfile
, local_error
->message
);
285 g_return_if_reached ();
288 widget
= e_table_new (E_TABLE_MODEL (model
), extras
, specification
);
289 gtk_container_add (GTK_CONTAINER (container
), widget
);
290 cal_list_view
->table
= E_TABLE (widget
);
291 gtk_widget_show (widget
);
293 g_object_unref (specification
);
294 g_object_unref (extras
);
297 /* Connect signals */
299 cal_list_view
->table
, "double_click",
300 G_CALLBACK (e_cal_list_view_on_table_double_click
),
303 cal_list_view
->table
, "right-click",
304 G_CALLBACK (e_cal_list_view_on_table_right_click
),
307 cal_list_view
->table
, "white-space-event",
308 G_CALLBACK (e_cal_list_view_on_table_white_space_event
),
310 g_signal_connect_after (
311 cal_list_view
->table
, "cursor_change",
312 G_CALLBACK (e_cal_list_view_cursor_change_cb
),
314 e_signal_connect_notify_after (
315 cal_list_view
->table
, "notify::is-editing",
316 G_CALLBACK (e_cal_list_view_table_editing_changed_cb
),
321 * e_cal_list_view_new:
322 * @Returns: a new #ECalListView.
324 * Creates a new #ECalListView.
327 e_cal_list_view_new (ECalModel
*model
)
329 ECalendarView
*cal_list_view
;
331 cal_list_view
= g_object_new (
332 E_TYPE_CAL_LIST_VIEW
, "model", model
, NULL
);
333 setup_e_table (E_CAL_LIST_VIEW (cal_list_view
));
335 return cal_list_view
;
339 e_cal_list_view_dispose (GObject
*object
)
341 ECalListView
*cal_list_view
;
343 cal_list_view
= E_CAL_LIST_VIEW (object
);
345 if (cal_list_view
->set_table_id
) {
346 g_source_remove (cal_list_view
->set_table_id
);
347 cal_list_view
->set_table_id
= 0;
350 if (cal_list_view
->cursor_event
) {
351 g_free (cal_list_view
->cursor_event
);
352 cal_list_view
->cursor_event
= NULL
;
355 if (cal_list_view
->table
) {
356 gtk_widget_destroy (GTK_WIDGET (cal_list_view
->table
));
357 cal_list_view
->table
= NULL
;
360 /* Chain up to parent's dispose() method. */
361 G_OBJECT_CLASS (e_cal_list_view_parent_class
)->dispose (object
);
365 e_cal_list_view_show_popup_menu (ECalListView
*cal_list_view
,
368 e_calendar_view_popup_event (E_CALENDAR_VIEW (cal_list_view
), event
);
372 e_cal_list_view_popup_menu (GtkWidget
*widget
)
374 ECalListView
*cal_list_view
= E_CAL_LIST_VIEW (widget
);
376 e_cal_list_view_show_popup_menu (cal_list_view
, NULL
);
381 e_cal_list_view_on_table_double_click (GtkWidget
*table
,
387 ECalListView
*cal_list_view
= E_CAL_LIST_VIEW (data
);
388 ECalModelComponent
*comp_data
;
390 comp_data
= e_cal_model_get_component_at (e_calendar_view_get_model (E_CALENDAR_VIEW (cal_list_view
)), row
);
391 e_calendar_view_edit_appointment (E_CALENDAR_VIEW (cal_list_view
), comp_data
->client
, comp_data
->icalcomp
, EDIT_EVENT_AUTODETECT
);
397 e_cal_list_view_on_table_right_click (GtkWidget
*table
,
403 ECalListView
*cal_list_view
= E_CAL_LIST_VIEW (data
);
405 e_cal_list_view_show_popup_menu (cal_list_view
, event
);
411 e_cal_list_view_on_table_white_space_event (ETable
*table
,
415 ECalListView
*cal_list_view
= user_data
;
416 guint event_button
= 0;
418 g_return_val_if_fail (E_IS_CAL_LIST_VIEW (cal_list_view
), FALSE
);
419 g_return_val_if_fail (event
!= NULL
, FALSE
);
421 if (event
->type
== GDK_BUTTON_PRESS
&&
422 gdk_event_get_button (event
, &event_button
) &&
424 GtkWidget
*table_canvas
;
426 table_canvas
= GTK_WIDGET (table
->table_canvas
);
428 if (!gtk_widget_has_focus (table_canvas
))
429 gtk_widget_grab_focus (table_canvas
);
431 e_cal_list_view_show_popup_menu (cal_list_view
, event
);
440 e_cal_list_view_cursor_change_cb (ETable
*etable
,
444 ECalListView
*cal_list_view
= E_CAL_LIST_VIEW (data
);
446 g_signal_emit_by_name (cal_list_view
, "selection_changed");
450 e_cal_list_view_get_selected_time_range (ECalendarView
*cal_view
,
457 selected
= e_calendar_view_get_selected_events (cal_view
);
459 ECalendarViewEvent
*event
= (ECalendarViewEvent
*) selected
->data
;
460 ECalComponentDateTime dtstart
, dtend
;
463 if (!is_comp_data_valid (event
))
466 comp
= e_cal_component_new ();
467 e_cal_component_set_icalcomponent (comp
, icalcomponent_new_clone (event
->comp_data
->icalcomp
));
469 e_cal_component_get_dtstart (comp
, &dtstart
);
471 zone
= icalcomponent_get_timezone (e_cal_component_get_icalcomponent (comp
), dtstart
.tzid
);
475 *start_time
= icaltime_as_timet_with_zone (*dtstart
.value
, zone
);
476 e_cal_component_free_datetime (&dtstart
);
479 e_cal_component_get_dtend (comp
, &dtend
);
481 zone
= icalcomponent_get_timezone (e_cal_component_get_icalcomponent (comp
), dtend
.tzid
);
485 *end_time
= icaltime_as_timet_with_zone (*dtend
.value
, zone
);
486 e_cal_component_free_datetime (&dtend
);
489 g_object_unref (comp
);
490 g_list_free (selected
);
499 e_cal_list_view_get_selected_events (ECalendarView
*cal_view
)
501 GList
*event_list
= NULL
;
504 if (E_CAL_LIST_VIEW (cal_view
)->cursor_event
) {
505 g_free (E_CAL_LIST_VIEW (cal_view
)->cursor_event
);
506 E_CAL_LIST_VIEW (cal_view
)->cursor_event
= NULL
;
509 cursor_row
= e_table_get_cursor_row (
510 E_CAL_LIST_VIEW (cal_view
)->table
);
512 if (cursor_row
>= 0) {
513 ECalendarViewEvent
*event
;
515 event
= E_CAL_LIST_VIEW (cal_view
)->cursor_event
= g_new0 (ECalendarViewEvent
, 1);
517 e_cal_model_get_component_at (
518 e_calendar_view_get_model (cal_view
),
520 event_list
= g_list_prepend (event_list
, event
);
527 adjust_range (icaltimetype icaltime
,
534 if (!icaltime_is_valid_time (icaltime
))
537 t
= icaltime_as_timet (icaltime
);
538 *earliest
= MIN (*earliest
, t
);
539 *latest
= MAX (*latest
, t
);
544 /* NOTE: Time use for this function increases linearly with number of events.
545 * This is not ideal, since it's used in a couple of places. We could probably
546 * be smarter about it, and use do it less frequently... */
548 e_cal_list_view_get_visible_time_range (ECalendarView
*cal_view
,
552 time_t earliest
= G_MAXINT
, latest
= 0;
553 gboolean set
= FALSE
;
556 n_rows
= e_table_model_row_count (E_TABLE_MODEL (e_calendar_view_get_model (cal_view
)));
558 for (i
= 0; i
< n_rows
; i
++) {
559 ECalModelComponent
*comp
;
560 icalcomponent
*icalcomp
;
562 comp
= e_cal_model_get_component_at (e_calendar_view_get_model (cal_view
), i
);
566 icalcomp
= comp
->icalcomp
;
570 adjust_range (icalcomponent_get_dtstart (icalcomp
), &earliest
, &latest
, &set
);
571 adjust_range (icalcomponent_get_dtend (icalcomp
), &earliest
, &latest
, &set
);
575 *start_time
= earliest
;
581 ECalModel
*model
= e_calendar_view_get_model (cal_view
);
583 /* Use time range set in the model when nothing shown in the list view */
584 e_cal_model_get_time_range (model
, start_time
, end_time
);
593 e_cal_list_view_get_range_shown (ECalListView
*cal_list_view
,
600 if (!e_cal_list_view_get_visible_time_range (E_CALENDAR_VIEW (cal_list_view
), &first
, &last
))
603 time_to_gdate_with_zone (start_date
, first
, e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view
)));
604 time_to_gdate_with_zone (&end_date
, last
, e_calendar_view_get_timezone (E_CALENDAR_VIEW (cal_list_view
)));
606 *days_shown
= g_date_days_between (start_date
, &end_date
);
611 e_cal_list_view_is_editing (ECalListView
*eclv
)
613 g_return_val_if_fail (E_IS_CAL_LIST_VIEW (eclv
), FALSE
);
615 return eclv
->table
&& e_table_is_editing (eclv
->table
);