1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this program; if not, see <http://www.gnu.org/licenses/>.
13  *
14  *
15  * Authors:
16  *		Damon Chaplin <damon@ximian.com>
17  *		Rodrigo Moya <rodrigo@ximian.com>
18  *		Nathan Owens <pianocomp81@yahoo.com>
19  *
20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21  *
22  */
23 
24 /*
25  * EMemoTable - displays the ECalComponent objects in a table (an ETable).
26  */
27 
28 #include "evolution-config.h"
29 
30 #include "e-memo-table.h"
31 
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <glib/gi18n.h>
35 #include <glib/gstdio.h>
36 
37 #include "comp-util.h"
38 #include "e-cal-dialogs.h"
39 #include "e-cal-model-memos.h"
40 #include "e-cal-ops.h"
41 #include "e-calendar-view.h"
42 #include "e-cell-date-edit-text.h"
43 #include "itip-utils.h"
44 #include "print.h"
45 #include "misc.h"
46 
47 #define E_MEMO_TABLE_GET_PRIVATE(obj) \
48 	(G_TYPE_INSTANCE_GET_PRIVATE \
49 	((obj), E_TYPE_MEMO_TABLE, EMemoTablePrivate))
50 
51 struct _EMemoTablePrivate {
52 	gpointer shell_view;  /* weak pointer */
53 	ECalModel *model;
54 
55 	/* Fields used for cut/copy/paste */
56 	ICalComponent *tmp_vcal;
57 
58 	GtkTargetList *copy_target_list;
59 	GtkTargetList *paste_target_list;
60 };
61 
62 enum {
63 	PROP_0,
64 	PROP_COPY_TARGET_LIST,
65 	PROP_MODEL,
66 	PROP_PASTE_TARGET_LIST,
67 	PROP_SHELL_VIEW
68 };
69 
70 enum {
71 	OPEN_COMPONENT,
72 	POPUP_EVENT,
73 	LAST_SIGNAL
74 };
75 
76 static guint signals[LAST_SIGNAL];
77 
78 /* The icons to represent the memo. */
79 static const gchar *icon_names[] = {
80 	"stock_notes",
81 	"stock_insert-note"
82 };
83 
84 /* Forward Declarations */
85 static void	e_memo_table_selectable_init
86 					(ESelectableInterface *iface);
87 
G_DEFINE_TYPE_WITH_CODE(EMemoTable,e_memo_table,E_TYPE_TABLE,G_IMPLEMENT_INTERFACE (E_TYPE_SELECTABLE,e_memo_table_selectable_init))88 G_DEFINE_TYPE_WITH_CODE (
89 	EMemoTable,
90 	e_memo_table,
91 	E_TYPE_TABLE,
92 	G_IMPLEMENT_INTERFACE (
93 		E_TYPE_SELECTABLE,
94 		e_memo_table_selectable_init))
95 
96 static void
97 memo_table_emit_open_component (EMemoTable *memo_table,
98                                 ECalModelComponent *comp_data)
99 {
100 	guint signal_id = signals[OPEN_COMPONENT];
101 
102 	g_signal_emit (memo_table, signal_id, 0, comp_data);
103 }
104 
105 static void
memo_table_emit_popup_event(EMemoTable * memo_table,GdkEvent * event)106 memo_table_emit_popup_event (EMemoTable *memo_table,
107                              GdkEvent *event)
108 {
109 	guint signal_id = signals[POPUP_EVENT];
110 
111 	g_signal_emit (memo_table, signal_id, 0, event);
112 }
113 
114 /* Returns the current time, for the ECellDateEdit items.
115  * FIXME: Should probably use the timezone of the item rather than the
116  * current timezone, though that may be difficult to get from here. */
117 static struct tm
memo_table_get_current_time(ECellDateEdit * ecde,gpointer user_data)118 memo_table_get_current_time (ECellDateEdit *ecde,
119                              gpointer user_data)
120 {
121 	EMemoTable *memo_table = user_data;
122 	ECalModel *model;
123 	ICalTimezone *zone;
124 	ICalTime *tt;
125 	struct tm tmp_tm;
126 
127 	/* Get the current timezone. */
128 	model = e_memo_table_get_model (memo_table);
129 	zone = e_cal_model_get_timezone (model);
130 
131 	tt = i_cal_time_new_from_timet_with_zone (time (NULL), FALSE, zone);
132 
133 	/* Now copy it to the struct tm and return it. */
134 	tmp_tm = e_cal_util_icaltime_to_tm (tt);
135 
136 	g_clear_object (&tt);
137 
138 	return tmp_tm;
139 }
140 
141 static void
memo_table_date_edit_before_popup_cb(ECellDateEdit * cell_date_edit,gint row,gint view_col,gpointer user_data)142 memo_table_date_edit_before_popup_cb (ECellDateEdit *cell_date_edit,
143 				      gint row,
144 				      gint view_col,
145 				      gpointer user_data)
146 {
147 	EMemoTable *memo_table = user_data;
148 	ECellPopup *ecp;
149 	ETableItem *eti;
150 	ETableCol *ecol;
151 	gint col;
152 	gboolean date_only;
153 
154 	g_return_if_fail (E_IS_MEMO_TABLE (memo_table));
155 
156 	ecp = E_CELL_POPUP (cell_date_edit);
157 	eti = E_TABLE_ITEM (ecp->popup_cell_view->cell_view.e_table_item_view);
158 	ecol = e_table_header_get_column (eti->header, view_col);
159 	col = ecol->spec->model_col;
160 
161 	date_only = col == E_CAL_MODEL_FIELD_DTSTART;
162 
163 	if (date_only && e_table_get_cursor_row (E_TABLE (memo_table)) != -1) {
164 		ECalModel *model;
165 		ECalModelComponent *comp_data;
166 		ESelectionModel *esm;
167 
168 		esm = e_table_get_selection_model (E_TABLE (memo_table));
169 		if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
170 			row = e_sorter_sorted_to_model (esm->sorter, row);
171 
172 		model = e_memo_table_get_model (memo_table);
173 		comp_data = e_cal_model_get_component_at (model, row);
174 		if (comp_data && comp_data->icalcomp) {
175 			ICalProperty *prop;
176 
177 			prop = i_cal_component_get_first_property (comp_data->icalcomp, I_CAL_DTSTART_PROPERTY);
178 			if (prop) {
179 				ICalTime *dtstart;
180 
181 				dtstart = i_cal_property_get_dtstart (prop);
182 				date_only = !dtstart || i_cal_time_is_null_time (dtstart) || i_cal_time_is_date (dtstart);
183 				g_clear_object (&dtstart);
184 			}
185 
186 			g_clear_object (&prop);
187 		}
188 	}
189 
190 	g_object_set (G_OBJECT (cell_date_edit), "show-time", !date_only, NULL);
191 }
192 
193 /* Deletes all of the selected components in the table */
194 static void
delete_selected_components(EMemoTable * memo_table)195 delete_selected_components (EMemoTable *memo_table)
196 {
197 	GSList *objs;
198 
199 	objs = e_memo_table_get_selected (memo_table);
200 	e_cal_ops_delete_ecalmodel_components (memo_table->priv->model, objs);
201 	g_slist_free (objs);
202 }
203 
204 static void
memo_table_set_model(EMemoTable * memo_table,ECalModel * model)205 memo_table_set_model (EMemoTable *memo_table,
206                       ECalModel *model)
207 {
208 	g_return_if_fail (memo_table->priv->model == NULL);
209 
210 	memo_table->priv->model = g_object_ref (model);
211 }
212 
213 static void
memo_table_set_shell_view(EMemoTable * memo_table,EShellView * shell_view)214 memo_table_set_shell_view (EMemoTable *memo_table,
215                            EShellView *shell_view)
216 {
217 	g_return_if_fail (memo_table->priv->shell_view == NULL);
218 
219 	memo_table->priv->shell_view = shell_view;
220 
221 	g_object_add_weak_pointer (
222 		G_OBJECT (shell_view),
223 		&memo_table->priv->shell_view);
224 }
225 
226 static void
memo_table_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)227 memo_table_set_property (GObject *object,
228                          guint property_id,
229                          const GValue *value,
230                          GParamSpec *pspec)
231 {
232 	switch (property_id) {
233 		case PROP_MODEL:
234 			memo_table_set_model (
235 				E_MEMO_TABLE (object),
236 				g_value_get_object (value));
237 			return;
238 
239 		case PROP_SHELL_VIEW:
240 			memo_table_set_shell_view (
241 				E_MEMO_TABLE (object),
242 				g_value_get_object (value));
243 			return;
244 	}
245 
246 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
247 }
248 
249 static void
memo_table_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)250 memo_table_get_property (GObject *object,
251                          guint property_id,
252                          GValue *value,
253                          GParamSpec *pspec)
254 {
255 	switch (property_id) {
256 		case PROP_COPY_TARGET_LIST:
257 			g_value_set_boxed (
258 				value, e_memo_table_get_copy_target_list (
259 				E_MEMO_TABLE (object)));
260 			return;
261 
262 		case PROP_MODEL:
263 			g_value_set_object (
264 				value, e_memo_table_get_model (
265 				E_MEMO_TABLE (object)));
266 			return;
267 
268 		case PROP_PASTE_TARGET_LIST:
269 			g_value_set_boxed (
270 				value, e_memo_table_get_paste_target_list (
271 				E_MEMO_TABLE (object)));
272 			return;
273 
274 		case PROP_SHELL_VIEW:
275 			g_value_set_object (
276 				value, e_memo_table_get_shell_view (
277 				E_MEMO_TABLE (object)));
278 			return;
279 	}
280 
281 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
282 }
283 
284 static void
memo_table_dispose(GObject * object)285 memo_table_dispose (GObject *object)
286 {
287 	EMemoTablePrivate *priv;
288 
289 	priv = E_MEMO_TABLE_GET_PRIVATE (object);
290 
291 	if (priv->shell_view != NULL) {
292 		g_object_remove_weak_pointer (
293 			G_OBJECT (priv->shell_view), &priv->shell_view);
294 		priv->shell_view = NULL;
295 	}
296 
297 	g_clear_object (&priv->model);
298 	g_clear_pointer (&priv->copy_target_list, gtk_target_list_unref);
299 	g_clear_pointer (&priv->paste_target_list, gtk_target_list_unref);
300 
301 	/* Chain up to parent's dispose() method. */
302 	G_OBJECT_CLASS (e_memo_table_parent_class)->dispose (object);
303 }
304 
305 static void
memo_table_constructed(GObject * object)306 memo_table_constructed (GObject *object)
307 {
308 	EMemoTable *memo_table;
309 	ECalModel *model;
310 	ECell *cell, *popup_cell;
311 	ETableExtras *extras;
312 	ETableSpecification *specification;
313 	AtkObject *a11y;
314 	gchar *etspecfile;
315 	GList *strings;
316 	GError *local_error = NULL;
317 
318 	memo_table = E_MEMO_TABLE (object);
319 	model = e_memo_table_get_model (memo_table);
320 
321 	/* Create the header columns */
322 
323 	extras = e_table_extras_new ();
324 
325 	/*
326 	 * Normal string fields.
327 	 */
328 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
329 	g_object_set (cell,
330 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
331 		"strikeout_column", E_CAL_MODEL_FIELD_CANCELLED,
332 		NULL);
333 	e_table_extras_add_cell (extras, "calstring", cell);
334 	g_object_unref (cell);
335 
336 	/*
337 	 * Date fields.
338 	 */
339 	cell = e_cell_date_edit_text_new (NULL, GTK_JUSTIFY_LEFT);
340 	g_object_set (cell,
341 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
342 		"strikeout_column", E_CAL_MODEL_FIELD_CANCELLED,
343 		NULL);
344 
345 	e_binding_bind_property (
346 		model, "timezone",
347 		cell, "timezone",
348 		G_BINDING_BIDIRECTIONAL |
349 		G_BINDING_SYNC_CREATE);
350 
351 	e_binding_bind_property (
352 		model, "use-24-hour-format",
353 		cell, "use-24-hour-format",
354 		G_BINDING_BIDIRECTIONAL |
355 		G_BINDING_SYNC_CREATE);
356 
357 	popup_cell = e_cell_date_edit_new ();
358 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
359 	g_object_unref (cell);
360 
361 	e_binding_bind_property (
362 		model, "use-24-hour-format",
363 		popup_cell, "use-24-hour-format",
364 		G_BINDING_BIDIRECTIONAL |
365 		G_BINDING_SYNC_CREATE);
366 
367 	g_signal_connect (popup_cell, "before-popup",
368 		G_CALLBACK (memo_table_date_edit_before_popup_cb), memo_table);
369 
370 	e_table_extras_add_cell (extras, "dateedit", popup_cell);
371 	g_object_unref (popup_cell);
372 
373 	e_cell_date_edit_set_get_time_callback (
374 		E_CELL_DATE_EDIT (popup_cell),
375 		memo_table_get_current_time, memo_table, NULL);
376 
377 	/* Status field. */
378 	cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT);
379 	g_object_set (
380 		cell,
381 		"bg_color_column", E_CAL_MODEL_FIELD_COLOR,
382 		"strikeout_column", E_CAL_MODEL_FIELD_CANCELLED,
383 		NULL);
384 
385 	popup_cell = e_cell_combo_new ();
386 	e_cell_popup_set_child (E_CELL_POPUP (popup_cell), cell);
387 	g_object_unref (cell);
388 
389 	strings = cal_comp_util_get_status_list_for_kind (e_cal_model_get_component_kind (model));
390 	e_cell_combo_set_popdown_strings (
391 		E_CELL_COMBO (popup_cell),
392 		strings);
393 	g_list_free (strings);
394 
395 	e_table_extras_add_cell (extras, "calstatus", popup_cell);
396 	g_object_unref (popup_cell);
397 
398 	/* Sorting */
399 	e_table_extras_add_compare (
400 		extras, "date-compare", e_cell_date_edit_compare_cb);
401 	e_table_extras_add_compare (
402 		extras, "status-compare",
403 		e_cal_model_util_status_compare_cb);
404 
405 	/* Create pixmaps */
406 
407 	cell = e_cell_toggle_new (icon_names, G_N_ELEMENTS (icon_names));
408 	g_object_set (cell,
409 		"bg-color-column", E_CAL_MODEL_FIELD_COLOR,
410 		NULL);
411 	e_table_extras_add_cell (extras, "icon", cell);
412 	g_object_unref (cell);
413 
414 	e_table_extras_add_icon_name (extras, "icon", "stock_notes");
415 
416 	/* set proper format component for a default 'date' cell renderer */
417 	cell = e_table_extras_get_cell (extras, "date");
418 	e_cell_date_set_format_component (E_CELL_DATE (cell), "calendar");
419 
420 	/* Construct the table */
421 
422 	etspecfile = g_build_filename (
423 		EVOLUTION_ETSPECDIR, "e-memo-table.etspec", NULL);
424 	specification = e_table_specification_new (etspecfile, &local_error);
425 
426 	/* Failure here is fatal. */
427 	if (local_error != NULL) {
428 		g_error ("%s: %s", etspecfile, local_error->message);
429 		g_return_if_reached ();
430 	}
431 
432 	e_table_construct (
433 		E_TABLE (memo_table),
434 		E_TABLE_MODEL (model),
435 		extras, specification);
436 
437 	g_object_unref (specification);
438 	g_free (etspecfile);
439 
440 	gtk_widget_set_has_tooltip (GTK_WIDGET (memo_table), TRUE);
441 
442 	g_object_unref (extras);
443 
444 	a11y = gtk_widget_get_accessible (GTK_WIDGET (memo_table));
445 	if (a11y)
446 		atk_object_set_name (a11y, _("Memos"));
447 
448 	/* Chain up to parent's constructed() method. */
449 	G_OBJECT_CLASS (e_memo_table_parent_class)->constructed (object);
450 }
451 
452 static gboolean
memo_table_popup_menu(GtkWidget * widget)453 memo_table_popup_menu (GtkWidget *widget)
454 {
455 	EMemoTable *memo_table;
456 
457 	memo_table = E_MEMO_TABLE (widget);
458 	memo_table_emit_popup_event (memo_table, NULL);
459 
460 	return TRUE;
461 }
462 
463 static gboolean
memo_table_query_tooltip(GtkWidget * widget,gint x,gint y,gboolean keyboard_mode,GtkTooltip * tooltip)464 memo_table_query_tooltip (GtkWidget *widget,
465                           gint x,
466                           gint y,
467                           gboolean keyboard_mode,
468                           GtkTooltip *tooltip)
469 {
470 	EMemoTable *memo_table;
471 	ECalModel *model;
472 	ECalModelComponent *comp_data;
473 	gint row = -1, col = -1, row_y = -1, row_height = -1;
474 	GtkWidget *box, *l, *w;
475 	GdkRGBA sel_bg, sel_fg, norm_bg, norm_text;
476 	gchar *tmp, *summary;
477 	GString *tmp2;
478 	ECalComponent *new_comp;
479 	ECalComponentOrganizer *organizer;
480 	ECalComponentDateTime *dtstart, *dtdue;
481 	ICalTimezone *zone, *default_zone;
482 	GSList *desc, *p;
483 	gint len;
484 	ESelectionModel *esm;
485 	struct tm tmp_tm;
486 
487 	if (keyboard_mode)
488 		return FALSE;
489 
490 	memo_table = E_MEMO_TABLE (widget);
491 
492 	e_table_get_mouse_over_cell (E_TABLE (memo_table), &row, &col);
493 	if (row == -1)
494 		return FALSE;
495 
496 	/* Respect sorting option; the 'e_table_get_mouse_over_cell'
497 	 * returns sorted row, not the model one. */
498 	esm = e_table_get_selection_model (E_TABLE (memo_table));
499 	if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
500 		row = e_sorter_sorted_to_model (esm->sorter, row);
501 
502 	if (row < 0)
503 		return FALSE;
504 
505 	model = e_memo_table_get_model (memo_table);
506 	comp_data = e_cal_model_get_component_at (model, row);
507 	if (!comp_data || !comp_data->icalcomp)
508 		return FALSE;
509 
510 	new_comp = e_cal_component_new_from_icalcomponent (i_cal_component_clone (comp_data->icalcomp));
511 	if (!new_comp)
512 		return FALSE;
513 
514 	e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &sel_bg);
515 	e_utils_get_theme_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, &sel_fg);
516 	e_utils_get_theme_color (widget, "theme_bg_color", E_UTILS_DEFAULT_THEME_BG_COLOR, &norm_bg);
517 	e_utils_get_theme_color (widget, "theme_text_color,theme_fg_color", E_UTILS_DEFAULT_THEME_TEXT_COLOR, &norm_text);
518 
519 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
520 
521 	summary = e_calendar_view_dup_component_summary (comp_data->icalcomp);
522 	if (!(summary && *summary)) {
523 		g_free (summary);
524 		summary = g_strdup (_("* No Summary *"));
525 	}
526 
527 	l = gtk_label_new (NULL);
528 	tmp = g_markup_printf_escaped ("<b>%s</b>", summary);
529 	gtk_label_set_line_wrap (GTK_LABEL (l), TRUE);
530 	gtk_label_set_width_chars (GTK_LABEL (l), 20);
531 	gtk_label_set_markup (GTK_LABEL (l), tmp);
532 	gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
533 	w = gtk_event_box_new ();
534 
535 	gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &sel_bg);
536 	gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &sel_fg);
537 	gtk_container_add (GTK_CONTAINER (w), l);
538 	gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
539 	g_free (tmp);
540 
541 	g_free (summary);
542 
543 	w = gtk_event_box_new ();
544 	gtk_widget_override_background_color (w, GTK_STATE_FLAG_NORMAL, &norm_bg);
545 
546 	l = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
547 	gtk_container_add (GTK_CONTAINER (w), l);
548 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
549 	w = l;
550 
551 	organizer = e_cal_component_get_organizer (new_comp);
552 	if (organizer && e_cal_component_organizer_get_cn (organizer)) {
553 		const gchar *email;
554 
555 		email = itip_strip_mailto (e_cal_component_organizer_get_value (organizer));
556 
557 		if (email) {
558 			tmp = g_strdup_printf (
559 				/* Translators: It will display
560 				 * "Organizer: NameOfTheUser <email@ofuser.com>" */
561 				_("Organizer: %s <%s>"), e_cal_component_organizer_get_cn (organizer), email);
562 		} else {
563 			/* With SunOne accounts, there may be no ':' in
564 			 * organizer.value */
565 			tmp = g_strdup_printf (
566 				_("Organizer: %s"), e_cal_component_organizer_get_cn (organizer));
567 		}
568 
569 		l = gtk_label_new (tmp);
570 		gtk_label_set_line_wrap (GTK_LABEL (l), FALSE);
571 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
572 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
573 		g_free (tmp);
574 
575 		gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
576 	}
577 
578 	e_cal_component_organizer_free (organizer);
579 
580 	dtstart = e_cal_component_get_dtstart (new_comp);
581 	dtdue = e_cal_component_get_due (new_comp);
582 
583 	default_zone = e_cal_model_get_timezone (model);
584 
585 	if (dtstart && e_cal_component_datetime_get_tzid (dtstart)) {
586 		zone = i_cal_component_get_timezone (
587 			e_cal_component_get_icalcomponent (new_comp),
588 			e_cal_component_datetime_get_tzid (dtstart));
589 		if (!zone) {
590 			if (!e_cal_client_get_timezone_sync (comp_data->client, e_cal_component_datetime_get_tzid (dtstart), &zone, NULL, NULL))
591 				zone = NULL;
592 		}
593 		if (!zone)
594 			zone = default_zone;
595 	} else {
596 		zone = NULL;
597 	}
598 
599 	tmp2 = g_string_new ("");
600 
601 	if (dtstart && e_cal_component_datetime_get_value (dtstart)) {
602 		gchar *str;
603 
604 		tmp_tm = e_cal_util_icaltime_to_tm_with_zone (e_cal_component_datetime_get_value (dtstart), zone, default_zone);
605 		str = e_datetime_format_format_tm ("calendar", "table",
606 			i_cal_time_is_date (e_cal_component_datetime_get_value (dtstart)) ? DTFormatKindDate : DTFormatKindDateTime,
607 			&tmp_tm);
608 
609 		if (str && *str) {
610 			/* Translators: This is followed by an event's start date/time */
611 			g_string_append (tmp2, _("Start: "));
612 			g_string_append (tmp2, str);
613 		}
614 
615 		g_free (str);
616 	}
617 
618 	if (dtdue && e_cal_component_datetime_get_value (dtdue)) {
619 		gchar *str;
620 
621 		tmp_tm = e_cal_util_icaltime_to_tm_with_zone (e_cal_component_datetime_get_value (dtdue), zone, default_zone);
622 		str = e_datetime_format_format_tm ("calendar", "table",
623 			i_cal_time_is_date (e_cal_component_datetime_get_value (dtdue)) ? DTFormatKindDate : DTFormatKindDateTime,
624 			&tmp_tm);
625 
626 		if (str && *str) {
627 			if (tmp2->len)
628 				g_string_append (tmp2, "; ");
629 
630 			/* Translators: This is followed by an event's due date/time */
631 			g_string_append (tmp2, _("Due: "));
632 			g_string_append (tmp2, str);
633 		}
634 
635 		g_free (str);
636 	}
637 
638 	if (tmp2->len) {
639 		l = gtk_label_new (tmp2->str);
640 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
641 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
642 
643 		gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
644 	}
645 
646 	e_cal_component_datetime_free (dtstart);
647 	e_cal_component_datetime_free (dtdue);
648 
649 	g_string_set_size (tmp2, 0);
650 	desc = e_cal_component_get_descriptions (new_comp);
651 	for (len = 0, p = desc; p != NULL; p = p->next) {
652 		ECalComponentText *text = p->data;
653 
654 		if (text && e_cal_component_text_get_value (text)) {
655 			const gchar *value = e_cal_component_text_get_value (text);
656 			len += strlen (value);
657 			g_string_append (tmp2, value);
658 			if (len > 1024) {
659 				g_string_set_size (tmp2, 1020);
660 				g_string_append (tmp2, _("…"));
661 				break;
662 			}
663 		}
664 	}
665 	g_slist_free_full (desc, e_cal_component_text_free);
666 
667 	if (tmp2->len) {
668 		l = gtk_label_new (tmp2->str);
669 		gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
670 		gtk_box_pack_start (GTK_BOX (w), l, FALSE, FALSE, 0);
671 
672 		gtk_widget_override_color (l, GTK_STATE_FLAG_NORMAL, &norm_text);
673 	}
674 
675 	g_string_free (tmp2, TRUE);
676 
677 	gtk_widget_show_all (box);
678 	gtk_tooltip_set_custom (tooltip, box);
679 
680 	g_object_unref (new_comp);
681 
682 	if (esm && esm->sorter && e_sorter_needs_sorting (esm->sorter))
683 		row = e_sorter_model_to_sorted (esm->sorter, row);
684 
685 	e_table_get_cell_geometry (E_TABLE (memo_table), row, 0, NULL, &row_y, NULL, &row_height);
686 
687 	if (row_y != -1 && row_height != -1) {
688 		ETable *etable;
689 		GdkRectangle rect;
690 		GtkAllocation allocation;
691 
692 		etable = E_TABLE (memo_table);
693 
694 		if (etable && etable->table_canvas) {
695 			gtk_widget_get_allocation (GTK_WIDGET (etable->table_canvas), &allocation);
696 		} else {
697 			allocation.x = 0;
698 			allocation.y = 0;
699 			allocation.width = 0;
700 			allocation.height = 0;
701 		}
702 
703 		rect.x = allocation.x;
704 		rect.y = allocation.y + row_y - BUTTON_PADDING;
705 		rect.width = allocation.width;
706 		rect.height = row_height + 2 * BUTTON_PADDING;
707 
708 		if (etable && etable->header_canvas) {
709 			gtk_widget_get_allocation (GTK_WIDGET (etable->header_canvas), &allocation);
710 
711 			rect.y += allocation.height;
712 		}
713 
714 		gtk_tooltip_set_tip_area (tooltip, &rect);
715 	}
716 
717 	return TRUE;
718 }
719 
720 static void
memo_table_open_at_row(ETable * table,gint row)721 memo_table_open_at_row (ETable *table,
722 			gint row)
723 {
724 	EMemoTable *memo_table;
725 	ECalModel *model;
726 	ECalModelComponent *comp_data;
727 
728 	memo_table = E_MEMO_TABLE (table);
729 	model = e_memo_table_get_model (memo_table);
730 	comp_data = e_cal_model_get_component_at (model, row);
731 	memo_table_emit_open_component (memo_table, comp_data);
732 }
733 
734 static void
memo_table_double_click(ETable * table,gint row,gint col,GdkEvent * event)735 memo_table_double_click (ETable *table,
736                          gint row,
737                          gint col,
738                          GdkEvent *event)
739 {
740 	memo_table_open_at_row (table, row);
741 }
742 
743 static gint
memo_table_right_click(ETable * table,gint row,gint col,GdkEvent * event)744 memo_table_right_click (ETable *table,
745                         gint row,
746                         gint col,
747                         GdkEvent *event)
748 {
749 	EMemoTable *memo_table;
750 
751 	memo_table = E_MEMO_TABLE (table);
752 	memo_table_emit_popup_event (memo_table, event);
753 
754 	return TRUE;
755 }
756 
757 static gboolean
memo_table_key_press(ETable * table,gint row,gint col,GdkEvent * event)758 memo_table_key_press (ETable *table,
759 		      gint row,
760 		      gint col,
761 		      GdkEvent *event)
762 {
763 	if (event && event->type == GDK_KEY_PRESS &&
764 	    (event->key.keyval == GDK_KEY_Return || event->key.keyval == GDK_KEY_KP_Enter) &&
765 	    (event->key.state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK)) == 0 &&
766 	    !e_table_is_editing (table)) {
767 		memo_table_open_at_row (table, row);
768 		return TRUE;
769 	}
770 
771 	return FALSE;
772 }
773 
774 static gboolean
memo_table_white_space_event(ETable * table,GdkEvent * event)775 memo_table_white_space_event (ETable *table,
776 			      GdkEvent *event)
777 {
778 	guint event_button = 0;
779 
780 	g_return_val_if_fail (E_IS_MEMO_TABLE (table), FALSE);
781 	g_return_val_if_fail (event != NULL, FALSE);
782 
783 	if (event->type == GDK_BUTTON_PRESS &&
784 	    gdk_event_get_button (event, &event_button) &&
785 	    event_button == 3) {
786 		GtkWidget *table_canvas;
787 
788 		table_canvas = GTK_WIDGET (table->table_canvas);
789 
790 		if (!gtk_widget_has_focus (table_canvas))
791 			gtk_widget_grab_focus (table_canvas);
792 
793 		memo_table_emit_popup_event (E_MEMO_TABLE (table), event);
794 
795 		return TRUE;
796 	}
797 
798 	return FALSE;
799 }
800 
801 static void
memo_table_update_actions(ESelectable * selectable,EFocusTracker * focus_tracker,GdkAtom * clipboard_targets,gint n_clipboard_targets)802 memo_table_update_actions (ESelectable *selectable,
803                            EFocusTracker *focus_tracker,
804                            GdkAtom *clipboard_targets,
805                            gint n_clipboard_targets)
806 {
807 	EMemoTable *memo_table;
808 	GtkAction *action;
809 	GtkTargetList *target_list;
810 	GSList *list, *iter;
811 	gboolean can_paste = FALSE;
812 	gboolean sources_are_editable = TRUE;
813 	gboolean is_editing;
814 	gboolean sensitive;
815 	const gchar *tooltip;
816 	gint n_selected;
817 	gint ii;
818 
819 	memo_table = E_MEMO_TABLE (selectable);
820 	n_selected = e_table_selected_count (E_TABLE (memo_table));
821 	is_editing = e_table_is_editing (E_TABLE (memo_table));
822 
823 	list = e_memo_table_get_selected (memo_table);
824 	for (iter = list; iter != NULL && sources_are_editable; iter = iter->next) {
825 		ECalModelComponent *comp_data = iter->data;
826 
827 		sources_are_editable = sources_are_editable &&
828 			!e_client_is_readonly (E_CLIENT (comp_data->client));
829 	}
830 	g_slist_free (list);
831 
832 	target_list = e_selectable_get_paste_target_list (selectable);
833 	for (ii = 0; ii < n_clipboard_targets && !can_paste; ii++)
834 		can_paste = gtk_target_list_find (
835 			target_list, clipboard_targets[ii], NULL);
836 
837 	action = e_focus_tracker_get_cut_clipboard_action (focus_tracker);
838 	sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
839 	tooltip = _("Cut selected memos to the clipboard");
840 	gtk_action_set_sensitive (action, sensitive);
841 	gtk_action_set_tooltip (action, tooltip);
842 
843 	action = e_focus_tracker_get_copy_clipboard_action (focus_tracker);
844 	sensitive = (n_selected > 0) && !is_editing;
845 	tooltip = _("Copy selected memos to the clipboard");
846 	gtk_action_set_sensitive (action, sensitive);
847 	gtk_action_set_tooltip (action, tooltip);
848 
849 	action = e_focus_tracker_get_paste_clipboard_action (focus_tracker);
850 	sensitive = sources_are_editable && can_paste && !is_editing;
851 	tooltip = _("Paste memos from the clipboard");
852 	gtk_action_set_sensitive (action, sensitive);
853 	gtk_action_set_tooltip (action, tooltip);
854 
855 	action = e_focus_tracker_get_delete_selection_action (focus_tracker);
856 	sensitive = (n_selected > 0) && sources_are_editable && !is_editing;
857 	tooltip = _("Delete selected memos");
858 	gtk_action_set_sensitive (action, sensitive);
859 	gtk_action_set_tooltip (action, tooltip);
860 
861 	action = e_focus_tracker_get_select_all_action (focus_tracker);
862 	sensitive = TRUE;
863 	tooltip = _("Select all visible memos");
864 	gtk_action_set_sensitive (action, sensitive);
865 	gtk_action_set_tooltip (action, tooltip);
866 }
867 
868 static void
memo_table_cut_clipboard(ESelectable * selectable)869 memo_table_cut_clipboard (ESelectable *selectable)
870 {
871 	EMemoTable *memo_table;
872 
873 	memo_table = E_MEMO_TABLE (selectable);
874 
875 	e_selectable_copy_clipboard (selectable);
876 	delete_selected_components (memo_table);
877 }
878 
879 /* Helper for memo_table_copy_clipboard() */
880 static void
copy_row_cb(gint model_row,gpointer data)881 copy_row_cb (gint model_row,
882              gpointer data)
883 {
884 	EMemoTable *memo_table;
885 	ECalModelComponent *comp_data;
886 	ECalModel *model;
887 	ICalComponent *child;
888 
889 	memo_table = E_MEMO_TABLE (data);
890 
891 	g_return_if_fail (memo_table->priv->tmp_vcal != NULL);
892 
893 	model = e_memo_table_get_model (memo_table);
894 	comp_data = e_cal_model_get_component_at (model, model_row);
895 	if (comp_data == NULL)
896 		return;
897 
898 	/* Add timezones to the VCALENDAR component. */
899 	e_cal_util_add_timezones_from_component (
900 		memo_table->priv->tmp_vcal, comp_data->icalcomp);
901 
902 	/* Add the new component to the VCALENDAR component. */
903 	child = i_cal_component_clone (comp_data->icalcomp);
904 	if (child)
905 		i_cal_component_take_component (memo_table->priv->tmp_vcal, child);
906 }
907 
908 static void
memo_table_copy_clipboard(ESelectable * selectable)909 memo_table_copy_clipboard (ESelectable *selectable)
910 {
911 	EMemoTable *memo_table;
912 	GtkClipboard *clipboard;
913 	gchar *comp_str;
914 
915 	memo_table = E_MEMO_TABLE (selectable);
916 
917 	/* Create a temporary VCALENDAR object. */
918 	memo_table->priv->tmp_vcal = e_cal_util_new_top_level ();
919 
920 	e_table_selected_row_foreach (
921 		E_TABLE (memo_table), copy_row_cb, memo_table);
922 	comp_str = i_cal_component_as_ical_string (memo_table->priv->tmp_vcal);
923 
924 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
925 	e_clipboard_set_calendar (clipboard, comp_str, -1);
926 	gtk_clipboard_store (clipboard);
927 
928 	g_free (comp_str);
929 
930 	g_clear_object (&memo_table->priv->tmp_vcal);
931 }
932 
933 static void
memo_table_paste_clipboard(ESelectable * selectable)934 memo_table_paste_clipboard (ESelectable *selectable)
935 {
936 	EMemoTable *memo_table;
937 	GtkClipboard *clipboard;
938 	GnomeCanvasItem *item;
939 	GnomeCanvas *table_canvas;
940 
941 	memo_table = E_MEMO_TABLE (selectable);
942 
943 	clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
944 
945 	table_canvas = E_TABLE (memo_table)->table_canvas;
946 	item = table_canvas->focused_item;
947 
948 	/* XXX Should ECellText implement GtkEditable? */
949 
950 	/* Paste text into a cell being edited. */
951 	if (gtk_clipboard_wait_is_text_available (clipboard) &&
952 		gtk_widget_has_focus (GTK_WIDGET (table_canvas)) &&
953 		E_IS_TABLE_ITEM (item) &&
954 		E_TABLE_ITEM (item)->editing_col >= 0 &&
955 		E_TABLE_ITEM (item)->editing_row >= 0) {
956 
957 		ETableItem *etable_item = E_TABLE_ITEM (item);
958 
959 		e_cell_text_paste_clipboard (
960 			etable_item->cell_views[etable_item->editing_col],
961 			etable_item->editing_col,
962 			etable_item->editing_row);
963 
964 	/* Paste iCalendar data into the table. */
965 	} else if (e_clipboard_wait_is_calendar_available (clipboard)) {
966 		ECalModel *model;
967 		gchar *ical_str;
968 
969 		model = e_memo_table_get_model (memo_table);
970 
971 		ical_str = e_clipboard_wait_for_calendar (clipboard);
972 		e_cal_ops_paste_components (model, ical_str);
973 		g_free (ical_str);
974 	}
975 }
976 
977 /* Used from e_table_selected_row_foreach(); puts the selected row number in an
978  * gint pointed to by the closure data.
979  */
980 static void
get_selected_row_cb(gint model_row,gpointer data)981 get_selected_row_cb (gint model_row,
982                      gpointer data)
983 {
984 	gint *row;
985 
986 	row = data;
987 	*row = model_row;
988 }
989 
990 /*
991  * Returns the component that is selected in the table; only works if there is
992  * one and only one selected row.
993  */
994 static ECalModelComponent *
get_selected_comp(EMemoTable * memo_table)995 get_selected_comp (EMemoTable *memo_table)
996 {
997 	ECalModel *model;
998 	gint row;
999 
1000 	model = e_memo_table_get_model (memo_table);
1001 	if (e_table_selected_count (E_TABLE (memo_table)) != 1)
1002 		return NULL;
1003 
1004 	row = -1;
1005 	e_table_selected_row_foreach (
1006 		E_TABLE (memo_table), get_selected_row_cb, &row);
1007 	if (row < 0) {
1008 		g_warn_if_reached ();
1009 		return NULL;
1010 	}
1011 
1012 	return e_cal_model_get_component_at (model, row);
1013 }
1014 
1015 static void
memo_table_delete_selection(ESelectable * selectable)1016 memo_table_delete_selection (ESelectable *selectable)
1017 {
1018 	ECalModel *model;
1019 	EMemoTable *memo_table;
1020 	ECalComponent *comp = NULL;
1021 	ECalModelComponent *comp_data;
1022 	gboolean delete = TRUE;
1023 	gint n_selected;
1024 
1025 	memo_table = E_MEMO_TABLE (selectable);
1026 	model = e_memo_table_get_model (memo_table);
1027 
1028 	n_selected = e_table_selected_count (E_TABLE (memo_table));
1029 	if (n_selected <= 0)
1030 		return;
1031 
1032 	if (n_selected == 1)
1033 		comp_data = get_selected_comp (memo_table);
1034 	else
1035 		comp_data = NULL;
1036 
1037 	/* FIXME: this may be something other than a TODO component */
1038 
1039 	if (comp_data) {
1040 		comp = e_cal_component_new_from_icalcomponent (
1041 			i_cal_component_clone (comp_data->icalcomp));
1042 	}
1043 
1044 	if (e_cal_model_get_confirm_delete (model))
1045 		delete = e_cal_dialogs_delete_component (
1046 			comp, FALSE, n_selected,
1047 			E_CAL_COMPONENT_JOURNAL,
1048 			GTK_WIDGET (memo_table));
1049 
1050 	if (delete)
1051 		delete_selected_components (memo_table);
1052 
1053 	g_clear_object (&comp);
1054 }
1055 
1056 static void
memo_table_select_all(ESelectable * selectable)1057 memo_table_select_all (ESelectable *selectable)
1058 {
1059 	e_table_select_all (E_TABLE (selectable));
1060 }
1061 
1062 static void
e_memo_table_class_init(EMemoTableClass * class)1063 e_memo_table_class_init (EMemoTableClass *class)
1064 {
1065 	GObjectClass *object_class;
1066 	GtkWidgetClass *widget_class;
1067 	ETableClass *table_class;
1068 
1069 	g_type_class_add_private (class, sizeof (EMemoTablePrivate));
1070 
1071 	object_class = G_OBJECT_CLASS (class);
1072 	object_class->set_property = memo_table_set_property;
1073 	object_class->get_property = memo_table_get_property;
1074 	object_class->dispose = memo_table_dispose;
1075 	object_class->constructed = memo_table_constructed;
1076 
1077 	widget_class = GTK_WIDGET_CLASS (class);
1078 	widget_class->popup_menu = memo_table_popup_menu;
1079 	widget_class->query_tooltip = memo_table_query_tooltip;
1080 
1081 	table_class = E_TABLE_CLASS (class);
1082 	table_class->double_click = memo_table_double_click;
1083 	table_class->right_click = memo_table_right_click;
1084 	table_class->key_press = memo_table_key_press;
1085 	table_class->white_space_event = memo_table_white_space_event;
1086 
1087 	/* Inherited from ESelectableInterface */
1088 	g_object_class_override_property (
1089 		object_class,
1090 		PROP_COPY_TARGET_LIST,
1091 		"copy-target-list");
1092 
1093 	g_object_class_install_property (
1094 		object_class,
1095 		PROP_MODEL,
1096 		g_param_spec_object (
1097 			"model",
1098 			"Model",
1099 			NULL,
1100 			E_TYPE_CAL_MODEL,
1101 			G_PARAM_READWRITE |
1102 			G_PARAM_CONSTRUCT_ONLY));
1103 
1104 	/* Inherited from ESelectableInterface */
1105 	g_object_class_override_property (
1106 		object_class,
1107 		PROP_PASTE_TARGET_LIST,
1108 		"paste-target-list");
1109 
1110 	g_object_class_install_property (
1111 		object_class,
1112 		PROP_SHELL_VIEW,
1113 		g_param_spec_object (
1114 			"shell-view",
1115 			"Shell View",
1116 			NULL,
1117 			E_TYPE_SHELL_VIEW,
1118 			G_PARAM_READWRITE |
1119 			G_PARAM_CONSTRUCT_ONLY));
1120 
1121 	signals[OPEN_COMPONENT] = g_signal_new (
1122 		"open-component",
1123 		G_TYPE_FROM_CLASS (class),
1124 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1125 		G_STRUCT_OFFSET (EMemoTableClass, open_component),
1126 		NULL, NULL,
1127 		g_cclosure_marshal_VOID__OBJECT,
1128 		G_TYPE_NONE, 1,
1129 		E_TYPE_CAL_MODEL_COMPONENT);
1130 
1131 	signals[POPUP_EVENT] = g_signal_new (
1132 		"popup-event",
1133 		G_TYPE_FROM_CLASS (class),
1134 		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1135 		G_STRUCT_OFFSET (EMemoTableClass, popup_event),
1136 		NULL, NULL,
1137 		g_cclosure_marshal_VOID__BOXED,
1138 		G_TYPE_NONE, 1,
1139 		GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
1140 }
1141 
1142 static void
e_memo_table_init(EMemoTable * memo_table)1143 e_memo_table_init (EMemoTable *memo_table)
1144 {
1145 	GtkTargetList *target_list;
1146 
1147 	memo_table->priv = E_MEMO_TABLE_GET_PRIVATE (memo_table);
1148 
1149 	target_list = gtk_target_list_new (NULL, 0);
1150 	e_target_list_add_calendar_targets (target_list, 0);
1151 	memo_table->priv->copy_target_list = target_list;
1152 
1153 	target_list = gtk_target_list_new (NULL, 0);
1154 	e_target_list_add_calendar_targets (target_list, 0);
1155 	memo_table->priv->paste_target_list = target_list;
1156 }
1157 
1158 static void
e_memo_table_selectable_init(ESelectableInterface * iface)1159 e_memo_table_selectable_init (ESelectableInterface *iface)
1160 {
1161 	iface->update_actions = memo_table_update_actions;
1162 	iface->cut_clipboard = memo_table_cut_clipboard;
1163 	iface->copy_clipboard = memo_table_copy_clipboard;
1164 	iface->paste_clipboard = memo_table_paste_clipboard;
1165 	iface->delete_selection = memo_table_delete_selection;
1166 	iface->select_all = memo_table_select_all;
1167 }
1168 
1169 /**
1170  * e_memo_table_new:
1171  * @shell_view: an #EShellView
1172  * @model: an #ECalModel for the table
1173  *
1174  * Returns a new #EMemoTable.
1175  *
1176  * Returns: a new #EMemoTable
1177  **/
1178 GtkWidget *
e_memo_table_new(EShellView * shell_view,ECalModel * model)1179 e_memo_table_new (EShellView *shell_view,
1180                   ECalModel *model)
1181 {
1182 	g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1183 	g_return_val_if_fail (E_IS_CAL_MODEL (model), NULL);
1184 
1185 	return g_object_new (
1186 		E_TYPE_MEMO_TABLE,
1187 		"model", model, "shell-view", shell_view, NULL);
1188 }
1189 
1190 /**
1191  * e_memo_table_get_model:
1192  * @memo_table: A calendar table.
1193  *
1194  * Queries the calendar data model that a calendar table is using.
1195  *
1196  * Return value: A memo model.
1197  **/
1198 ECalModel *
e_memo_table_get_model(EMemoTable * memo_table)1199 e_memo_table_get_model (EMemoTable *memo_table)
1200 {
1201 	g_return_val_if_fail (memo_table != NULL, NULL);
1202 	g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL);
1203 
1204 	return memo_table->priv->model;
1205 }
1206 
1207 EShellView *
e_memo_table_get_shell_view(EMemoTable * memo_table)1208 e_memo_table_get_shell_view (EMemoTable *memo_table)
1209 {
1210 	g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL);
1211 
1212 	return memo_table->priv->shell_view;
1213 }
1214 
1215 struct get_selected_uids_closure {
1216 	EMemoTable *memo_table;
1217 	GSList *objects;
1218 };
1219 
1220 /* Used from e_table_selected_row_foreach(), builds a list of the selected UIDs */
1221 static void
add_uid_cb(gint model_row,gpointer data)1222 add_uid_cb (gint model_row,
1223             gpointer data)
1224 {
1225 	struct get_selected_uids_closure *closure;
1226 	ECalModelComponent *comp_data;
1227 	ECalModel *model;
1228 
1229 	closure = data;
1230 
1231 	model = e_memo_table_get_model (closure->memo_table);
1232 	comp_data = e_cal_model_get_component_at (model, model_row);
1233 
1234 	closure->objects = g_slist_prepend (closure->objects, comp_data);
1235 }
1236 
1237 /**
1238  * e_memo_table_get_selected:
1239  * @memo_table:
1240  *
1241  * Get the currently selected ECalModelComponent's on the table.
1242  *
1243  * Return value: A GSList of the components, which should be
1244  * g_slist_free'd when finished with.
1245  **/
1246 GSList *
e_memo_table_get_selected(EMemoTable * memo_table)1247 e_memo_table_get_selected (EMemoTable *memo_table)
1248 {
1249 	struct get_selected_uids_closure closure;
1250 
1251 	closure.memo_table = memo_table;
1252 	closure.objects = NULL;
1253 
1254 	e_table_selected_row_foreach (
1255 		E_TABLE (memo_table), add_uid_cb, &closure);
1256 
1257 	return closure.objects;
1258 }
1259 
1260 GtkTargetList *
e_memo_table_get_copy_target_list(EMemoTable * memo_table)1261 e_memo_table_get_copy_target_list (EMemoTable *memo_table)
1262 {
1263 	g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL);
1264 
1265 	return memo_table->priv->copy_target_list;
1266 }
1267 
1268 GtkTargetList *
e_memo_table_get_paste_target_list(EMemoTable * memo_table)1269 e_memo_table_get_paste_target_list (EMemoTable *memo_table)
1270 {
1271 	g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL);
1272 
1273 	return memo_table->priv->paste_target_list;
1274 }
1275