1 /*
2  * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by
6  * the Free Software Foundation.
7  *
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
11  * for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17 
18 #include "evolution-config.h"
19 
20 #include <glib-object.h>
21 #include <gtk/gtk.h>
22 
23 #include <e-util/e-util.h>
24 
25 #include "calendar-config.h"
26 #include "comp-util.h"
27 #include "e-comp-editor-property-part.h"
28 
29 struct _ECompEditorPropertyPartPrivate {
30 	GtkWidget *label_widget;
31 	GtkWidget *edit_widget;
32 	gboolean visible;
33 	gboolean sensitize_handled;
34 };
35 
36 enum {
37 	PROPERTY_PART_PROP_0,
38 	PROPERTY_PART_PROP_SENSITIZE_HANDLED,
39 	PROPERTY_PART_PROP_VISIBLE
40 };
41 
42 enum {
43 	PROPERTY_PART_CHANGED,
44 	PROPERTY_PART_LAST_SIGNAL
45 };
46 
47 static guint property_part_signals[PROPERTY_PART_LAST_SIGNAL];
48 
G_DEFINE_ABSTRACT_TYPE(ECompEditorPropertyPart,e_comp_editor_property_part,G_TYPE_OBJECT)49 G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPart, e_comp_editor_property_part, G_TYPE_OBJECT)
50 
51 static void
52 e_comp_editor_property_part_set_property (GObject *object,
53 					  guint property_id,
54 					  const GValue *value,
55 					  GParamSpec *pspec)
56 {
57 	switch (property_id) {
58 		case PROPERTY_PART_PROP_SENSITIZE_HANDLED:
59 			e_comp_editor_property_part_set_sensitize_handled (
60 				E_COMP_EDITOR_PROPERTY_PART (object),
61 				g_value_get_boolean (value));
62 			return;
63 
64 		case PROPERTY_PART_PROP_VISIBLE:
65 			e_comp_editor_property_part_set_visible (
66 				E_COMP_EDITOR_PROPERTY_PART (object),
67 				g_value_get_boolean (value));
68 			return;
69 	}
70 
71 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
72 }
73 
74 static void
e_comp_editor_property_part_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)75 e_comp_editor_property_part_get_property (GObject *object,
76 					  guint property_id,
77 					  GValue *value,
78 					  GParamSpec *pspec)
79 {
80 	switch (property_id) {
81 		case PROPERTY_PART_PROP_SENSITIZE_HANDLED:
82 			g_value_set_boolean (
83 				value,
84 				e_comp_editor_property_part_get_sensitize_handled (
85 				E_COMP_EDITOR_PROPERTY_PART (object)));
86 			return;
87 
88 		case PROPERTY_PART_PROP_VISIBLE:
89 			g_value_set_boolean (
90 				value,
91 				e_comp_editor_property_part_get_visible (
92 				E_COMP_EDITOR_PROPERTY_PART (object)));
93 			return;
94 	}
95 
96 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
97 }
98 
99 static void
e_comp_editor_property_part_constructed(GObject * object)100 e_comp_editor_property_part_constructed (GObject *object)
101 {
102 	ECompEditorPropertyPart *property_part;
103 	GtkWidget *label_widget = NULL, *edit_widget = NULL;
104 
105 	G_OBJECT_CLASS (e_comp_editor_property_part_parent_class)->constructed (object);
106 
107 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (object));
108 
109 	property_part = E_COMP_EDITOR_PROPERTY_PART (object);
110 
111 	e_comp_editor_property_part_create_widgets (property_part, &label_widget, &edit_widget);
112 
113 	if (label_widget) {
114 		property_part->priv->label_widget = g_object_ref_sink (label_widget);
115 
116 		e_binding_bind_property (
117 			property_part, "visible",
118 			label_widget, "visible",
119 			G_BINDING_SYNC_CREATE);
120 	}
121 
122 	if (edit_widget) {
123 		property_part->priv->edit_widget = g_object_ref_sink (edit_widget);
124 
125 		e_binding_bind_property (
126 			property_part, "visible",
127 			edit_widget, "visible",
128 			G_BINDING_SYNC_CREATE);
129 	}
130 }
131 
132 static void
e_comp_editor_property_part_dispose(GObject * object)133 e_comp_editor_property_part_dispose (GObject *object)
134 {
135 	ECompEditorPropertyPart *property_part;
136 
137 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (object));
138 
139 	property_part = E_COMP_EDITOR_PROPERTY_PART (object);
140 
141 	g_clear_object (&property_part->priv->label_widget);
142 	g_clear_object (&property_part->priv->edit_widget);
143 
144 	G_OBJECT_CLASS (e_comp_editor_property_part_parent_class)->dispose (object);
145 }
146 
147 static void
e_comp_editor_property_part_impl_sensitize_widgets(ECompEditorPropertyPart * property_part,gboolean force_insensitive)148 e_comp_editor_property_part_impl_sensitize_widgets (ECompEditorPropertyPart *property_part,
149 						    gboolean force_insensitive)
150 {
151 	GtkWidget *widget;
152 
153 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
154 
155 	widget = e_comp_editor_property_part_get_label_widget (property_part);
156 	if (widget)
157 		gtk_widget_set_sensitive (widget, !force_insensitive);
158 
159 	widget = e_comp_editor_property_part_get_edit_widget (property_part);
160 	if (widget) {
161 		if (GTK_IS_ENTRY (widget))
162 			g_object_set (G_OBJECT (widget), "editable", !force_insensitive, NULL);
163 		else
164 			gtk_widget_set_sensitive (widget, !force_insensitive);
165 	}
166 }
167 
168 static void
e_comp_editor_property_part_init(ECompEditorPropertyPart * property_part)169 e_comp_editor_property_part_init (ECompEditorPropertyPart *property_part)
170 {
171 	property_part->priv = G_TYPE_INSTANCE_GET_PRIVATE (property_part,
172 		E_TYPE_COMP_EDITOR_PROPERTY_PART,
173 		ECompEditorPropertyPartPrivate);
174 	property_part->priv->visible = TRUE;
175 	property_part->priv->sensitize_handled = FALSE;
176 }
177 
178 static void
e_comp_editor_property_part_class_init(ECompEditorPropertyPartClass * klass)179 e_comp_editor_property_part_class_init (ECompEditorPropertyPartClass *klass)
180 {
181 	GObjectClass *object_class;
182 
183 	g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartPrivate));
184 
185 	klass->sensitize_widgets = e_comp_editor_property_part_impl_sensitize_widgets;
186 
187 	object_class = G_OBJECT_CLASS (klass);
188 	object_class->set_property = e_comp_editor_property_part_set_property;
189 	object_class->get_property = e_comp_editor_property_part_get_property;
190 	object_class->constructed = e_comp_editor_property_part_constructed;
191 	object_class->dispose = e_comp_editor_property_part_dispose;
192 
193 	g_object_class_install_property (
194 		object_class,
195 		PROPERTY_PART_PROP_VISIBLE,
196 		g_param_spec_boolean (
197 			"visible",
198 			"Visible",
199 			"Whether the part is visible",
200 			TRUE,
201 			G_PARAM_READWRITE |
202 			G_PARAM_STATIC_STRINGS));
203 
204 	g_object_class_install_property (
205 		object_class,
206 		PROPERTY_PART_PROP_SENSITIZE_HANDLED,
207 		g_param_spec_boolean (
208 			"sensitize-handled",
209 			"Sensitize Handled",
210 			"Whether the part's sensitive property is handled by the owner of it",
211 			FALSE,
212 			G_PARAM_READWRITE |
213 			G_PARAM_STATIC_STRINGS));
214 
215 	property_part_signals[PROPERTY_PART_CHANGED] = g_signal_new (
216 		"changed",
217 		G_TYPE_FROM_CLASS (klass),
218 		G_SIGNAL_RUN_FIRST,
219 		G_STRUCT_OFFSET (ECompEditorPropertyPartClass, changed),
220 		NULL, NULL, NULL,
221 		G_TYPE_NONE, 0,
222 		G_TYPE_NONE);
223 }
224 
225 gboolean
e_comp_editor_property_part_get_visible(ECompEditorPropertyPart * property_part)226 e_comp_editor_property_part_get_visible (ECompEditorPropertyPart *property_part)
227 {
228 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), FALSE);
229 
230 	return property_part->priv->visible;
231 }
232 
233 void
e_comp_editor_property_part_set_visible(ECompEditorPropertyPart * property_part,gboolean visible)234 e_comp_editor_property_part_set_visible (ECompEditorPropertyPart *property_part,
235 					 gboolean visible)
236 {
237 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
238 
239 	if ((property_part->priv->visible ? 1 : 0) == (visible ? 1 : 0))
240 		return;
241 
242 	property_part->priv->visible = visible;
243 
244 	g_object_notify (G_OBJECT (property_part), "visible");
245 }
246 
247 gboolean
e_comp_editor_property_part_get_sensitize_handled(ECompEditorPropertyPart * property_part)248 e_comp_editor_property_part_get_sensitize_handled (ECompEditorPropertyPart *property_part)
249 {
250 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), FALSE);
251 
252 	return property_part->priv->sensitize_handled;
253 }
254 
255 void
e_comp_editor_property_part_set_sensitize_handled(ECompEditorPropertyPart * property_part,gboolean sensitize_handled)256 e_comp_editor_property_part_set_sensitize_handled (ECompEditorPropertyPart *property_part,
257 						   gboolean sensitize_handled)
258 {
259 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
260 
261 	if ((property_part->priv->sensitize_handled ? 1 : 0) == (sensitize_handled ? 1 : 0))
262 		return;
263 
264 	property_part->priv->sensitize_handled = sensitize_handled;
265 
266 	g_object_notify (G_OBJECT (property_part), "sensitize-handled");
267 }
268 
269 void
e_comp_editor_property_part_create_widgets(ECompEditorPropertyPart * property_part,GtkWidget ** out_label_widget,GtkWidget ** out_edit_widget)270 e_comp_editor_property_part_create_widgets (ECompEditorPropertyPart *property_part,
271 					    GtkWidget **out_label_widget,
272 					    GtkWidget **out_edit_widget)
273 {
274 	ECompEditorPropertyPartClass *klass;
275 
276 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
277 
278 	g_return_if_fail (property_part->priv->label_widget == NULL);
279 	g_return_if_fail (property_part->priv->edit_widget == NULL);
280 
281 	klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
282 	g_return_if_fail (klass != NULL);
283 	g_return_if_fail (klass->create_widgets != NULL);
284 
285 	klass->create_widgets (property_part, out_label_widget, out_edit_widget);
286 }
287 
288 GtkWidget *
e_comp_editor_property_part_get_label_widget(ECompEditorPropertyPart * property_part)289 e_comp_editor_property_part_get_label_widget (ECompEditorPropertyPart *property_part)
290 {
291 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), NULL);
292 
293 	return property_part->priv->label_widget;
294 }
295 
296 GtkWidget *
e_comp_editor_property_part_get_edit_widget(ECompEditorPropertyPart * property_part)297 e_comp_editor_property_part_get_edit_widget (ECompEditorPropertyPart *property_part)
298 {
299 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part), NULL);
300 
301 	return property_part->priv->edit_widget;
302 }
303 
304 void
e_comp_editor_property_part_fill_widget(ECompEditorPropertyPart * property_part,ICalComponent * component)305 e_comp_editor_property_part_fill_widget (ECompEditorPropertyPart *property_part,
306 					 ICalComponent *component)
307 {
308 	ECompEditorPropertyPartClass *klass;
309 
310 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
311 
312 	klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
313 	g_return_if_fail (klass != NULL);
314 	g_return_if_fail (klass->fill_widget != NULL);
315 
316 	klass->fill_widget (property_part, component);
317 }
318 
319 void
e_comp_editor_property_part_fill_component(ECompEditorPropertyPart * property_part,ICalComponent * component)320 e_comp_editor_property_part_fill_component (ECompEditorPropertyPart *property_part,
321 					    ICalComponent *component)
322 {
323 	ECompEditorPropertyPartClass *klass;
324 
325 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
326 
327 	klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
328 	g_return_if_fail (klass != NULL);
329 	g_return_if_fail (klass->fill_component != NULL);
330 
331 	klass->fill_component (property_part, component);
332 }
333 
334 void
e_comp_editor_property_part_sensitize_widgets(ECompEditorPropertyPart * property_part,gboolean force_insensitive)335 e_comp_editor_property_part_sensitize_widgets (ECompEditorPropertyPart *property_part,
336 					       gboolean force_insensitive)
337 {
338 	ECompEditorPropertyPartClass *klass;
339 
340 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
341 
342 	if (e_comp_editor_property_part_get_sensitize_handled (property_part))
343 		return;
344 
345 	klass = E_COMP_EDITOR_PROPERTY_PART_GET_CLASS (property_part);
346 	g_return_if_fail (klass != NULL);
347 
348 	if (klass->sensitize_widgets)
349 		klass->sensitize_widgets (property_part, force_insensitive);
350 }
351 
352 void
e_comp_editor_property_part_emit_changed(ECompEditorPropertyPart * property_part)353 e_comp_editor_property_part_emit_changed (ECompEditorPropertyPart *property_part)
354 {
355 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART (property_part));
356 
357 	g_signal_emit (property_part, property_part_signals[PROPERTY_PART_CHANGED], 0, NULL);
358 }
359 
360 /* ************************************************************************* */
361 
362 struct _ECompEditorPropertyPartStringPrivate {
363 	gboolean is_multivalue;
364 };
365 
G_DEFINE_ABSTRACT_TYPE(ECompEditorPropertyPartString,e_comp_editor_property_part_string,E_TYPE_COMP_EDITOR_PROPERTY_PART)366 G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartString, e_comp_editor_property_part_string, E_TYPE_COMP_EDITOR_PROPERTY_PART)
367 
368 static void
369 ecepp_string_create_widgets (ECompEditorPropertyPart *property_part,
370 			     GtkWidget **out_label_widget,
371 			     GtkWidget **out_edit_widget)
372 {
373 	ECompEditorPropertyPartStringClass *klass;
374 
375 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
376 	g_return_if_fail (out_label_widget != NULL);
377 	g_return_if_fail (out_edit_widget != NULL);
378 
379 	klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
380 	g_return_if_fail (klass != NULL);
381 	g_return_if_fail (g_type_is_a (klass->entry_type, GTK_TYPE_ENTRY) ||
382 			  g_type_is_a (klass->entry_type, GTK_TYPE_TEXT_VIEW));
383 
384 	/* The descendant sets the 'out_label_widget' parameter */
385 	*out_edit_widget = g_object_new (klass->entry_type, NULL);
386 	g_return_if_fail (*out_edit_widget != NULL);
387 
388 	g_object_set (G_OBJECT (*out_edit_widget),
389 		"hexpand", FALSE,
390 		"halign", GTK_ALIGN_FILL,
391 		"vexpand", FALSE,
392 		"valign", GTK_ALIGN_START,
393 		NULL);
394 
395 	gtk_widget_show (*out_edit_widget);
396 
397 	if (g_type_is_a (klass->entry_type, GTK_TYPE_TEXT_VIEW)) {
398 		GtkScrolledWindow *scrolled_window;
399 		GtkTextBuffer *text_buffer;
400 
401 		scrolled_window = GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
402 		gtk_scrolled_window_set_policy (scrolled_window, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
403 		gtk_scrolled_window_set_shadow_type (scrolled_window, GTK_SHADOW_IN);
404 		gtk_widget_show (GTK_WIDGET (scrolled_window));
405 
406 		gtk_container_add (GTK_CONTAINER (scrolled_window), *out_edit_widget);
407 
408 		g_object_set (G_OBJECT (*out_edit_widget),
409 			"hexpand", TRUE,
410 			"halign", GTK_ALIGN_FILL,
411 			"vexpand", TRUE,
412 			"valign", GTK_ALIGN_FILL,
413 			NULL);
414 
415 		g_object_set (G_OBJECT (scrolled_window),
416 			"hexpand", FALSE,
417 			"halign", GTK_ALIGN_FILL,
418 			"vexpand", FALSE,
419 			"valign", GTK_ALIGN_START,
420 			NULL);
421 
422 		text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (*out_edit_widget));
423 		g_signal_connect_swapped (text_buffer, "changed",
424 			G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
425 
426 		*out_edit_widget = GTK_WIDGET (scrolled_window);
427 	} else {
428 		g_signal_connect_swapped (*out_edit_widget, "changed",
429 			G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
430 	}
431 }
432 
433 static void
ecepp_string_fill_widget(ECompEditorPropertyPart * property_part,ICalComponent * component)434 ecepp_string_fill_widget (ECompEditorPropertyPart *property_part,
435 			  ICalComponent *component)
436 {
437 	ECompEditorPropertyPartStringClass *klass;
438 	GtkWidget *edit_widget;
439 	ICalProperty *prop;
440 	gchar *text = NULL;
441 
442 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
443 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
444 
445 	edit_widget = e_comp_editor_property_part_string_get_real_edit_widget (E_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
446 	g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_TEXT_VIEW (edit_widget));
447 
448 	klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
449 	g_return_if_fail (klass != NULL);
450 	g_return_if_fail (klass->prop_kind != I_CAL_NO_PROPERTY);
451 	g_return_if_fail (klass->i_cal_get_func != NULL);
452 
453 	if (e_comp_editor_property_part_string_is_multivalue (E_COMP_EDITOR_PROPERTY_PART_STRING (property_part))) {
454 		GString *multivalue = NULL;
455 
456 		for (prop = i_cal_component_get_first_property (component, klass->prop_kind);
457 		     prop;
458 		     g_object_unref (prop), prop = i_cal_component_get_next_property (component, klass->prop_kind)) {
459 			const gchar *value;
460 
461 			value = klass->i_cal_get_func (prop);
462 
463 			if (!value || !*value)
464 				continue;
465 
466 			if (!multivalue)
467 				multivalue = g_string_new ("");
468 			else if (multivalue->len)
469 				g_string_append_c (multivalue, ',');
470 
471 			g_string_append (multivalue, value);
472 		}
473 
474 		if (multivalue)
475 			text = g_string_free (multivalue, FALSE);
476 	} else {
477 		prop = i_cal_component_get_first_property (component, klass->prop_kind);
478 		if (prop) {
479 			text = g_strdup (klass->i_cal_get_func (prop));
480 			g_object_unref (prop);
481 		}
482 	}
483 
484 	if (GTK_IS_ENTRY (edit_widget)) {
485 		gtk_entry_set_text (GTK_ENTRY (edit_widget), text ? text : "");
486 	} else /* if (GTK_IS_TEXT_VIEW (edit_widget)) */ {
487 		GtkTextBuffer *buffer;
488 
489 		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit_widget));
490 		gtk_text_buffer_set_text (buffer, text ? text : "", -1);
491 	}
492 
493 	e_widget_undo_reset (edit_widget);
494 
495 	g_free (text);
496 }
497 
498 static void
ecepp_string_fill_component(ECompEditorPropertyPart * property_part,ICalComponent * component)499 ecepp_string_fill_component (ECompEditorPropertyPart *property_part,
500 			     ICalComponent *component)
501 {
502 	ECompEditorPropertyPartStringClass *klass;
503 	GtkWidget *edit_widget;
504 	ICalProperty *prop;
505 	gchar *value;
506 
507 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
508 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
509 
510 	edit_widget = e_comp_editor_property_part_string_get_real_edit_widget (E_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
511 	g_return_if_fail (GTK_IS_ENTRY (edit_widget) || GTK_IS_TEXT_VIEW (edit_widget));
512 
513 	klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (property_part);
514 	g_return_if_fail (klass != NULL);
515 	g_return_if_fail (klass->prop_kind != I_CAL_NO_PROPERTY);
516 	g_return_if_fail (klass->i_cal_new_func != NULL);
517 	g_return_if_fail (klass->i_cal_set_func != NULL);
518 
519 	if (GTK_IS_ENTRY (edit_widget)) {
520 		value = g_strdup (gtk_entry_get_text (GTK_ENTRY (edit_widget)));
521 	} else /* if (GTK_IS_TEXT_VIEW (edit_widget)) */ {
522 		GtkTextBuffer *buffer;
523 		GtkTextIter text_iter_start, text_iter_end;
524 
525 		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (edit_widget));
526 		gtk_text_buffer_get_start_iter (buffer, &text_iter_start);
527 		gtk_text_buffer_get_end_iter (buffer, &text_iter_end);
528 		value = gtk_text_buffer_get_text (buffer, &text_iter_start, &text_iter_end, FALSE);
529 	}
530 
531 	if (e_comp_editor_property_part_string_is_multivalue (E_COMP_EDITOR_PROPERTY_PART_STRING (property_part))) {
532 		/* Clear all multivalues first */
533 		e_cal_util_component_remove_property_by_kind (component, klass->prop_kind, TRUE);
534 
535 		if (value && *value) {
536 			gchar **split_value;
537 
538 			split_value = g_strsplit (value, ",", -1);
539 			if (split_value) {
540 				gint ii;
541 
542 				/* Store multivalue properties into multiple properties,
543 				   to workaround i_cal_component_clone() bug, which escapes
544 				   commas in such properties. */
545 				for (ii = 0; split_value[ii]; ii++) {
546 					const gchar *item = split_value[ii];
547 
548 					if (*item) {
549 						prop = klass->i_cal_new_func (item);
550 						i_cal_component_take_property (component, prop);
551 					}
552 				}
553 
554 				g_strfreev (split_value);
555 			}
556 		}
557 	} else {
558 		prop = i_cal_component_get_first_property (component, klass->prop_kind);
559 
560 		if (value && *value) {
561 			if (prop) {
562 				klass->i_cal_set_func (prop, value);
563 				g_object_unref (prop);
564 			} else {
565 				prop = klass->i_cal_new_func (value);
566 				i_cal_component_take_property (component, prop);
567 			}
568 		} else if (prop) {
569 			i_cal_component_remove_property (component, prop);
570 			g_object_unref (prop);
571 		}
572 	}
573 
574 	g_free (value);
575 }
576 
577 static void
ecepp_string_sensitize_widgets(ECompEditorPropertyPart * property_part,gboolean force_insensitive)578 ecepp_string_sensitize_widgets (ECompEditorPropertyPart *property_part,
579 				gboolean force_insensitive)
580 {
581 	GtkWidget *widget;
582 
583 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
584 
585 	widget = e_comp_editor_property_part_get_label_widget (property_part);
586 	if (widget)
587 		gtk_widget_set_sensitive (widget, !force_insensitive);
588 
589 	widget = e_comp_editor_property_part_string_get_real_edit_widget (E_COMP_EDITOR_PROPERTY_PART_STRING (property_part));
590 	g_return_if_fail (GTK_IS_ENTRY (widget) || GTK_IS_TEXT_VIEW (widget));
591 
592 	g_object_set (G_OBJECT (widget), "editable", !force_insensitive, NULL);
593 }
594 
595 static GtkWidget *
ecepp_string_get_real_edit_widget(ECompEditorPropertyPartString * part_string)596 ecepp_string_get_real_edit_widget (ECompEditorPropertyPartString *part_string)
597 {
598 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string), NULL);
599 
600 	return e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_string));
601 }
602 
603 static void
e_comp_editor_property_part_string_init(ECompEditorPropertyPartString * part_string)604 e_comp_editor_property_part_string_init (ECompEditorPropertyPartString *part_string)
605 {
606 	part_string->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_string,
607 		E_TYPE_COMP_EDITOR_PROPERTY_PART_STRING,
608 		ECompEditorPropertyPartStringPrivate);
609 	part_string->priv->is_multivalue = FALSE;
610 }
611 
612 static void
e_comp_editor_property_part_string_class_init(ECompEditorPropertyPartStringClass * klass)613 e_comp_editor_property_part_string_class_init (ECompEditorPropertyPartStringClass *klass)
614 {
615 	ECompEditorPropertyPartClass *part_class;
616 
617 	g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartStringPrivate));
618 
619 	klass->entry_type = GTK_TYPE_ENTRY;
620 
621 	klass->prop_kind = I_CAL_NO_PROPERTY;
622 	klass->i_cal_new_func = NULL;
623 	klass->i_cal_set_func = NULL;
624 	klass->i_cal_get_func = NULL;
625 	klass->get_real_edit_widget = ecepp_string_get_real_edit_widget;
626 
627 	part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
628 	part_class->create_widgets = ecepp_string_create_widgets;
629 	part_class->fill_widget = ecepp_string_fill_widget;
630 	part_class->fill_component = ecepp_string_fill_component;
631 	part_class->sensitize_widgets = ecepp_string_sensitize_widgets;
632 }
633 
634 void
e_comp_editor_property_part_string_attach_focus_tracker(ECompEditorPropertyPartString * part_string,EFocusTracker * focus_tracker)635 e_comp_editor_property_part_string_attach_focus_tracker (ECompEditorPropertyPartString *part_string,
636 							 EFocusTracker *focus_tracker)
637 {
638 	GtkWidget *edit_widget;
639 
640 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string));
641 
642 	if (!focus_tracker)
643 		return;
644 
645 	g_return_if_fail (E_IS_FOCUS_TRACKER (focus_tracker));
646 
647 	edit_widget = e_comp_editor_property_part_string_get_real_edit_widget (part_string);
648 
649 	if (edit_widget)
650 		e_widget_undo_attach (edit_widget, focus_tracker);
651 }
652 
653 void
e_comp_editor_property_part_string_set_is_multivalue(ECompEditorPropertyPartString * part_string,gboolean is_multivalue)654 e_comp_editor_property_part_string_set_is_multivalue (ECompEditorPropertyPartString *part_string,
655 						      gboolean is_multivalue)
656 {
657 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string));
658 
659 	part_string->priv->is_multivalue = is_multivalue;
660 }
661 
662 gboolean
e_comp_editor_property_part_string_is_multivalue(ECompEditorPropertyPartString * part_string)663 e_comp_editor_property_part_string_is_multivalue (ECompEditorPropertyPartString *part_string)
664 {
665 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string), FALSE);
666 
667 	return part_string->priv->is_multivalue;
668 }
669 
670 GtkWidget *
e_comp_editor_property_part_string_get_real_edit_widget(ECompEditorPropertyPartString * part_string)671 e_comp_editor_property_part_string_get_real_edit_widget (ECompEditorPropertyPartString *part_string)
672 {
673 	ECompEditorPropertyPartStringClass *klass;
674 	GtkWidget *edit_widget;
675 
676 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_STRING (part_string), NULL);
677 
678 	klass = E_COMP_EDITOR_PROPERTY_PART_STRING_GET_CLASS (part_string);
679 	g_return_val_if_fail (klass != NULL, NULL);
680 	g_return_val_if_fail (klass->get_real_edit_widget != NULL, NULL);
681 
682 	edit_widget = klass->get_real_edit_widget (part_string);
683 
684 	if (GTK_IS_SCROLLED_WINDOW (edit_widget))
685 		edit_widget = gtk_bin_get_child (GTK_BIN (edit_widget));
686 
687 	return edit_widget;
688 }
689 
690 /* ************************************************************************* */
691 
692 struct _ECompEditorPropertyPartDatetimePrivate {
693 	GWeakRef timezone_entry;
694 };
695 
696 enum {
697 	ECEPP_DATETIME_LOOKUP_TIMEZONE,
698 	ECEPP_DATETIME_LAST_SIGNAL
699 };
700 
701 static guint ecepp_datetime_signals[ECEPP_DATETIME_LAST_SIGNAL];
702 
G_DEFINE_ABSTRACT_TYPE(ECompEditorPropertyPartDatetime,e_comp_editor_property_part_datetime,E_TYPE_COMP_EDITOR_PROPERTY_PART)703 G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartDatetime, e_comp_editor_property_part_datetime, E_TYPE_COMP_EDITOR_PROPERTY_PART)
704 
705 static ICalTimezone *
706 ecepp_datetime_lookup_timezone (ECompEditorPropertyPartDatetime *part_datetime,
707 				const gchar *tzid)
708 {
709 	ICalTimezone *zone = NULL;
710 
711 	if (!tzid || !*tzid)
712 		return NULL;
713 
714 	g_signal_emit (part_datetime, ecepp_datetime_signals[ECEPP_DATETIME_LOOKUP_TIMEZONE], 0, tzid, &zone);
715 
716 	return zone;
717 }
718 
719 static struct tm
ecepp_datetime_get_current_time_cb(EDateEdit * date_edit,gpointer user_data)720 ecepp_datetime_get_current_time_cb (EDateEdit *date_edit,
721 				    gpointer user_data)
722 {
723 	GWeakRef *weakref = user_data;
724 	ECompEditorPropertyPartDatetime *part_datetime;
725 	ICalTime *today = NULL;
726 	struct tm tm;
727 
728 	memset (&tm, 0, sizeof (struct tm));
729 
730 	g_return_val_if_fail (weakref != NULL, tm);
731 
732 	part_datetime = g_weak_ref_get (weakref);
733 	if (part_datetime) {
734 		ETimezoneEntry *timezone_entry = g_weak_ref_get (&part_datetime->priv->timezone_entry);
735 		ICalTimezone *editor_zone = NULL;
736 
737 		if (timezone_entry)
738 			editor_zone = e_timezone_entry_get_timezone (timezone_entry);
739 
740 		if (editor_zone)
741 			today = i_cal_time_new_current_with_zone (editor_zone);
742 
743 		g_clear_object (&timezone_entry);
744 		g_object_unref (part_datetime);
745 	}
746 
747 	if (!today)
748 		today = i_cal_time_new_current_with_zone (calendar_config_get_icaltimezone ());
749 
750 	tm = e_cal_util_icaltime_to_tm (today);
751 
752 	g_clear_object (&today);
753 
754 	return tm;
755 }
756 
757 static void
ecepp_datetime_changed_cb(ECompEditorPropertyPart * property_part)758 ecepp_datetime_changed_cb (ECompEditorPropertyPart *property_part)
759 {
760 	GtkWidget *edit_widget;
761 
762 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
763 
764 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
765 
766 	if (!edit_widget || e_date_edit_has_focus (E_DATE_EDIT (edit_widget)) ||
767 	    !e_date_edit_date_is_valid (E_DATE_EDIT (edit_widget)) ||
768 	    !e_date_edit_time_is_valid (E_DATE_EDIT (edit_widget)))
769 		return;
770 
771 	e_comp_editor_property_part_emit_changed (property_part);
772 }
773 
774 static void
ecepp_datetime_create_widgets(ECompEditorPropertyPart * property_part,GtkWidget ** out_label_widget,GtkWidget ** out_edit_widget)775 ecepp_datetime_create_widgets (ECompEditorPropertyPart *property_part,
776 			       GtkWidget **out_label_widget,
777 			       GtkWidget **out_edit_widget)
778 {
779 	ECompEditorPropertyPartDatetimeClass *klass;
780 
781 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
782 	g_return_if_fail (out_label_widget != NULL);
783 	g_return_if_fail (out_edit_widget != NULL);
784 
785 	klass = E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS (property_part);
786 	g_return_if_fail (klass != NULL);
787 
788 	/* The descendant sets the 'out_label_widget' parameter */
789 	*out_edit_widget = e_date_edit_new ();
790 	g_return_if_fail (*out_edit_widget != NULL);
791 
792 	g_object_set (G_OBJECT (*out_edit_widget),
793 		"hexpand", FALSE,
794 		"halign", GTK_ALIGN_START,
795 		"vexpand", FALSE,
796 		"valign", GTK_ALIGN_START,
797 		NULL);
798 
799 	gtk_widget_show (*out_edit_widget);
800 
801 	e_date_edit_set_get_time_callback (E_DATE_EDIT (*out_edit_widget),
802 		ecepp_datetime_get_current_time_cb,
803 		e_weak_ref_new (property_part), (GDestroyNotify) e_weak_ref_free);
804 
805 	g_signal_connect_swapped (*out_edit_widget, "changed",
806 		G_CALLBACK (ecepp_datetime_changed_cb), property_part);
807 	g_signal_connect_swapped (*out_edit_widget, "notify::show-time",
808 		G_CALLBACK (ecepp_datetime_changed_cb), property_part);
809 }
810 
811 static void
ecepp_datetime_fill_widget(ECompEditorPropertyPart * property_part,ICalComponent * component)812 ecepp_datetime_fill_widget (ECompEditorPropertyPart *property_part,
813 			    ICalComponent *component)
814 {
815 	ECompEditorPropertyPartDatetime *part_datetime;
816 	ECompEditorPropertyPartDatetimeClass *klass;
817 	GtkWidget *edit_widget;
818 	ICalProperty *prop;
819 	ICalTime *value = NULL;
820 
821 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
822 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
823 
824 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
825 	g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
826 
827 	klass = E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS (property_part);
828 	g_return_if_fail (klass != NULL);
829 	g_return_if_fail (klass->prop_kind != I_CAL_NO_PROPERTY);
830 	g_return_if_fail (klass->i_cal_get_func != NULL);
831 
832 	part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part);
833 
834 	prop = i_cal_component_get_first_property (component, klass->prop_kind);
835 	if (prop) {
836 		ETimezoneEntry *timezone_entry = g_weak_ref_get (&part_datetime->priv->timezone_entry);
837 
838 		value = klass->i_cal_get_func (prop);
839 
840 		if (timezone_entry && value && !i_cal_time_is_date (value)) {
841 			ICalTimezone *editor_zone = e_timezone_entry_get_timezone (timezone_entry);
842 
843 			/* Attempt to convert time to the zone used in the editor */
844 			if (editor_zone && !i_cal_time_get_timezone (value) && !i_cal_time_is_utc (value)) {
845 				ICalParameter *param;
846 
847 				param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
848 				if (param) {
849 					const gchar *tzid;
850 
851 					tzid = i_cal_parameter_get_tzid (param);
852 
853 					if (tzid && *tzid) {
854 						if (editor_zone &&
855 						    (g_strcmp0 (i_cal_timezone_get_tzid (editor_zone), tzid) == 0 ||
856 						    g_strcmp0 (i_cal_timezone_get_location (editor_zone), tzid) == 0)) {
857 							i_cal_time_set_timezone (value, editor_zone);
858 						} else {
859 							i_cal_time_set_timezone (value, ecepp_datetime_lookup_timezone (part_datetime, tzid));
860 						}
861 					}
862 
863 					g_object_unref (param);
864 				}
865 			}
866 		}
867 
868 		g_clear_object (&timezone_entry);
869 		g_clear_object (&prop);
870 	}
871 
872 	if (!value)
873 		value = i_cal_time_new_null_time ();
874 
875 	e_comp_editor_property_part_datetime_set_value (part_datetime, value);
876 
877 	g_clear_object (&value);
878 }
879 
880 static void
ecepp_datetime_fill_component(ECompEditorPropertyPart * property_part,ICalComponent * component)881 ecepp_datetime_fill_component (ECompEditorPropertyPart *property_part,
882 			       ICalComponent *component)
883 {
884 	ECompEditorPropertyPartDatetime *part_datetime;
885 	ECompEditorPropertyPartDatetimeClass *klass;
886 	GtkWidget *edit_widget;
887 	EDateEdit *date_edit;
888 	ICalProperty *prop;
889 	ICalTime *value;
890 	time_t tt;
891 
892 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part));
893 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
894 
895 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
896 	g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
897 
898 	klass = E_COMP_EDITOR_PROPERTY_PART_DATETIME_GET_CLASS (property_part);
899 	g_return_if_fail (klass != NULL);
900 	g_return_if_fail (klass->prop_kind != I_CAL_NO_PROPERTY);
901 	g_return_if_fail (klass->i_cal_new_func != NULL);
902 	g_return_if_fail (klass->i_cal_get_func != NULL);
903 	g_return_if_fail (klass->i_cal_set_func != NULL);
904 
905 	part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (property_part);
906 	date_edit = E_DATE_EDIT (edit_widget);
907 	tt = e_date_edit_get_time (date_edit);
908 
909 	prop = i_cal_component_get_first_property (component, klass->prop_kind);
910 
911 	if (e_date_edit_get_allow_no_date_set (date_edit) && tt == (time_t) -1) {
912 		if (prop) {
913 			i_cal_component_remove_property (component, prop);
914 			g_object_unref (prop);
915 		}
916 	} else {
917 		value = e_comp_editor_property_part_datetime_get_value (part_datetime);
918 
919 		if (prop) {
920 			/* Remove the VALUE parameter, to correspond to the actual value being set */
921 			i_cal_property_remove_parameter_by_kind (prop, I_CAL_VALUE_PARAMETER);
922 
923 			klass->i_cal_set_func (prop, value);
924 
925 			/* Re-read the value, because it could be changed by the descendant */
926 			g_clear_object (&value);
927 			value = klass->i_cal_get_func (prop);
928 
929 			cal_comp_util_update_tzid_parameter (prop, value);
930 		} else {
931 			prop = klass->i_cal_new_func (value);
932 
933 			/* Re-read the value, because it could be changed by the descendant */
934 			g_clear_object (&value);
935 			value = klass->i_cal_get_func (prop);
936 
937 			cal_comp_util_update_tzid_parameter (prop, value);
938 			i_cal_component_add_property (component, prop);
939 		}
940 
941 		g_clear_object (&value);
942 		g_clear_object (&prop);
943 	}
944 }
945 
946 static void
ecepp_datetime_finalize(GObject * object)947 ecepp_datetime_finalize (GObject *object)
948 {
949 	ECompEditorPropertyPartDatetime *part_datetime = E_COMP_EDITOR_PROPERTY_PART_DATETIME (object);
950 
951 	g_weak_ref_clear (&part_datetime->priv->timezone_entry);
952 
953 	G_OBJECT_CLASS (e_comp_editor_property_part_datetime_parent_class)->finalize (object);
954 }
955 
956 static void
e_comp_editor_property_part_datetime_init(ECompEditorPropertyPartDatetime * part_datetime)957 e_comp_editor_property_part_datetime_init (ECompEditorPropertyPartDatetime *part_datetime)
958 {
959 	part_datetime->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_datetime,
960 		E_TYPE_COMP_EDITOR_PROPERTY_PART_DATETIME,
961 		ECompEditorPropertyPartDatetimePrivate);
962 
963 	g_weak_ref_init (&part_datetime->priv->timezone_entry, NULL);
964 }
965 
966 static void
e_comp_editor_property_part_datetime_class_init(ECompEditorPropertyPartDatetimeClass * klass)967 e_comp_editor_property_part_datetime_class_init (ECompEditorPropertyPartDatetimeClass *klass)
968 {
969 	ECompEditorPropertyPartClass *part_class;
970 	GObjectClass *object_class;
971 
972 	g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartDatetimePrivate));
973 
974 	klass->prop_kind = I_CAL_NO_PROPERTY;
975 	klass->i_cal_new_func = NULL;
976 	klass->i_cal_set_func = NULL;
977 	klass->i_cal_get_func = NULL;
978 
979 	part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
980 	part_class->create_widgets = ecepp_datetime_create_widgets;
981 	part_class->fill_widget = ecepp_datetime_fill_widget;
982 	part_class->fill_component = ecepp_datetime_fill_component;
983 
984 	object_class = G_OBJECT_CLASS (klass);
985 	object_class->finalize = ecepp_datetime_finalize;
986 
987 	/* ICalTimezone *lookup_timezone (datetime, const gchar *tzid); */
988 	ecepp_datetime_signals[ECEPP_DATETIME_LOOKUP_TIMEZONE] = g_signal_new (
989 		"lookup-timezone",
990 		G_OBJECT_CLASS_TYPE (object_class),
991 		G_SIGNAL_ACTION,
992 		0,
993 		NULL, NULL, NULL,
994 		G_TYPE_POINTER, 1,
995 		G_TYPE_STRING);
996 }
997 
998 void
e_comp_editor_property_part_datetime_attach_timezone_entry(ECompEditorPropertyPartDatetime * part_datetime,ETimezoneEntry * timezone_entry)999 e_comp_editor_property_part_datetime_attach_timezone_entry (ECompEditorPropertyPartDatetime *part_datetime,
1000 							    ETimezoneEntry *timezone_entry)
1001 {
1002 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
1003 	if (timezone_entry)
1004 		g_return_if_fail (E_IS_TIMEZONE_ENTRY (timezone_entry));
1005 
1006 	g_weak_ref_set (&part_datetime->priv->timezone_entry, timezone_entry);
1007 }
1008 
1009 void
e_comp_editor_property_part_datetime_set_date_only(ECompEditorPropertyPartDatetime * part_datetime,gboolean date_only)1010 e_comp_editor_property_part_datetime_set_date_only (ECompEditorPropertyPartDatetime *part_datetime,
1011 						    gboolean date_only)
1012 {
1013 	GtkWidget *edit_widget;
1014 
1015 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
1016 
1017 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1018 	g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
1019 
1020 	if ((e_date_edit_get_show_time (E_DATE_EDIT (edit_widget)) ? 1 : 0) == ((!date_only) ? 1 : 0))
1021 		return;
1022 
1023 	e_date_edit_set_show_time (E_DATE_EDIT (edit_widget), !date_only);
1024 }
1025 
1026 gboolean
e_comp_editor_property_part_datetime_get_date_only(ECompEditorPropertyPartDatetime * part_datetime)1027 e_comp_editor_property_part_datetime_get_date_only (ECompEditorPropertyPartDatetime *part_datetime)
1028 {
1029 	GtkWidget *edit_widget;
1030 
1031 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), FALSE);
1032 
1033 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1034 	g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), FALSE);
1035 
1036 	return !e_date_edit_get_show_time (E_DATE_EDIT (edit_widget));
1037 }
1038 
1039 void
e_comp_editor_property_part_datetime_set_allow_no_date_set(ECompEditorPropertyPartDatetime * part_datetime,gboolean allow_no_date_set)1040 e_comp_editor_property_part_datetime_set_allow_no_date_set (ECompEditorPropertyPartDatetime *part_datetime,
1041 							    gboolean allow_no_date_set)
1042 {
1043 	GtkWidget *edit_widget;
1044 
1045 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
1046 
1047 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1048 	g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
1049 
1050 	e_date_edit_set_allow_no_date_set (E_DATE_EDIT (edit_widget), allow_no_date_set);
1051 }
1052 
1053 gboolean
e_comp_editor_property_part_datetime_get_allow_no_date_set(ECompEditorPropertyPartDatetime * part_datetime)1054 e_comp_editor_property_part_datetime_get_allow_no_date_set (ECompEditorPropertyPartDatetime *part_datetime)
1055 {
1056 	GtkWidget *edit_widget;
1057 
1058 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), FALSE);
1059 
1060 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1061 	g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), FALSE);
1062 
1063 	return !e_date_edit_get_allow_no_date_set (E_DATE_EDIT (edit_widget));
1064 }
1065 
1066 void
e_comp_editor_property_part_datetime_set_value(ECompEditorPropertyPartDatetime * part_datetime,const ICalTime * value)1067 e_comp_editor_property_part_datetime_set_value (ECompEditorPropertyPartDatetime *part_datetime,
1068 						const ICalTime *value)
1069 {
1070 	GtkWidget *edit_widget;
1071 	EDateEdit *date_edit;
1072 	ICalTime *tmp_value = NULL;
1073 
1074 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime));
1075 
1076 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1077 	g_return_if_fail (E_IS_DATE_EDIT (edit_widget));
1078 
1079 	date_edit = E_DATE_EDIT (edit_widget);
1080 
1081 	if (!e_date_edit_get_allow_no_date_set (date_edit) && (!value || i_cal_time_is_null_time (value) ||
1082 	    !i_cal_time_is_valid_time (value))) {
1083 		tmp_value = i_cal_time_new_current_with_zone (i_cal_timezone_get_utc_timezone ());
1084 		value = tmp_value;
1085 	}
1086 
1087 	if (!value || i_cal_time_is_null_time (value) ||
1088 	    !i_cal_time_is_valid_time (value)) {
1089 		e_date_edit_set_time (date_edit, (time_t) -1);
1090 	} else {
1091 		ICalTimezone *zone;
1092 
1093 		zone = i_cal_time_get_timezone (value);
1094 
1095 		/* Convert to the same time zone as the editor uses, if different */
1096 		if (!i_cal_time_is_date (value) && zone) {
1097 			ETimezoneEntry *timezone_entry = g_weak_ref_get (&part_datetime->priv->timezone_entry);
1098 
1099 			if (timezone_entry) {
1100 				ICalTimezone *editor_zone = e_timezone_entry_get_timezone (timezone_entry);
1101 
1102 				if (editor_zone && zone != editor_zone &&
1103 				    g_strcmp0 (i_cal_timezone_get_tzid (editor_zone), i_cal_timezone_get_tzid (zone)) != 0 &&
1104 				    g_strcmp0 (i_cal_timezone_get_location (editor_zone), i_cal_timezone_get_location (zone)) != 0) {
1105 					if (tmp_value != value) {
1106 						tmp_value = i_cal_time_clone (value);
1107 						value = tmp_value;
1108 					}
1109 
1110 					i_cal_time_convert_timezone (tmp_value, zone, editor_zone);
1111 					i_cal_time_set_timezone (tmp_value, editor_zone);
1112 				}
1113 			}
1114 
1115 			g_clear_object (&timezone_entry);
1116 		}
1117 
1118 		e_date_edit_set_date (date_edit, i_cal_time_get_year (value), i_cal_time_get_month (value), i_cal_time_get_day (value));
1119 
1120 		if (!i_cal_time_is_date (value))
1121 			e_date_edit_set_time_of_day (date_edit, i_cal_time_get_hour (value), i_cal_time_get_minute (value));
1122 		else if (e_date_edit_get_show_time (date_edit))
1123 			e_date_edit_set_time_of_day (date_edit, 0, 0);
1124 		else if (e_date_edit_get_allow_no_date_set (date_edit))
1125 			e_date_edit_set_time_of_day (date_edit, -1, -1);
1126 
1127 		e_comp_editor_property_part_datetime_set_date_only (part_datetime, i_cal_time_is_date (value));
1128 	}
1129 
1130 	g_clear_object (&tmp_value);
1131 }
1132 
1133 ICalTime *
e_comp_editor_property_part_datetime_get_value(ECompEditorPropertyPartDatetime * part_datetime)1134 e_comp_editor_property_part_datetime_get_value (ECompEditorPropertyPartDatetime *part_datetime)
1135 {
1136 	ETimezoneEntry *timezone_entry = NULL;
1137 	GtkWidget *edit_widget;
1138 	EDateEdit *date_edit;
1139 	ICalTime *value = i_cal_time_new_null_time ();
1140 	gint year, month, day;
1141 
1142 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), value);
1143 
1144 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1145 	g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), value);
1146 
1147 	date_edit = E_DATE_EDIT (edit_widget);
1148 
1149 	if (!e_date_edit_get_date (date_edit, &year, &month, &day))
1150 		return value;
1151 
1152 	i_cal_time_set_date (value, year, month, day);
1153 
1154 	if (!e_date_edit_get_show_time (date_edit)) {
1155 		i_cal_time_set_is_date (value, TRUE);
1156 	} else {
1157 		gint hour, minute;
1158 
1159 		i_cal_time_set_timezone (value, NULL);
1160 		i_cal_time_set_is_date (value, !e_date_edit_get_time_of_day (date_edit, &hour, &minute));
1161 
1162 		if (!i_cal_time_is_date (value)) {
1163 			i_cal_time_set_time (value, hour, minute, 0);
1164 
1165 			timezone_entry = g_weak_ref_get (&part_datetime->priv->timezone_entry);
1166 			if (timezone_entry) {
1167 				ICalTimezone *zone, *utc_zone;
1168 
1169 				utc_zone = i_cal_timezone_get_utc_timezone ();
1170 				zone = e_timezone_entry_get_timezone (timezone_entry);
1171 
1172 				/* It's required to have set the built-in UTC zone, not its copy,
1173 				   thus libical knows that it's a UTC time, not a time with UTC TZID. */
1174 				if (zone && g_strcmp0 (i_cal_timezone_get_tzid (utc_zone), i_cal_timezone_get_tzid (zone)) == 0)
1175 					zone = utc_zone;
1176 
1177 				i_cal_time_set_timezone (value, zone);
1178 			}
1179 		}
1180 	}
1181 
1182 	g_clear_object (&timezone_entry);
1183 
1184 	return value;
1185 }
1186 
1187 gboolean
e_comp_editor_property_part_datetime_check_validity(ECompEditorPropertyPartDatetime * part_datetime,gboolean * out_date_is_valid,gboolean * out_time_is_valid)1188 e_comp_editor_property_part_datetime_check_validity (ECompEditorPropertyPartDatetime *part_datetime,
1189 						     gboolean *out_date_is_valid,
1190 						     gboolean *out_time_is_valid)
1191 {
1192 	GtkWidget *edit_widget;
1193 	EDateEdit *date_edit;
1194 	gboolean date_is_valid = TRUE, time_is_valid = TRUE;
1195 
1196 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_DATETIME (part_datetime), FALSE);
1197 
1198 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_datetime));
1199 	g_return_val_if_fail (E_IS_DATE_EDIT (edit_widget), FALSE);
1200 
1201 	date_edit = E_DATE_EDIT (edit_widget);
1202 
1203 	if (!e_date_edit_get_allow_no_date_set (date_edit) ||
1204 	    e_date_edit_get_time (date_edit) != (time_t) -1) {
1205 		date_is_valid = e_date_edit_date_is_valid (date_edit);
1206 
1207 		if (e_date_edit_get_show_time (date_edit))
1208 			time_is_valid = e_date_edit_time_is_valid (date_edit);
1209 	}
1210 
1211 	if (out_date_is_valid)
1212 		*out_date_is_valid = date_is_valid;
1213 	if (out_time_is_valid)
1214 		*out_time_is_valid = time_is_valid;
1215 
1216 	return date_is_valid && time_is_valid;
1217 }
1218 
1219 /* ************************************************************************* */
1220 
1221 struct _ECompEditorPropertyPartSpinPrivate {
1222 	gint dummy;
1223 };
1224 
G_DEFINE_ABSTRACT_TYPE(ECompEditorPropertyPartSpin,e_comp_editor_property_part_spin,E_TYPE_COMP_EDITOR_PROPERTY_PART)1225 G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartSpin, e_comp_editor_property_part_spin, E_TYPE_COMP_EDITOR_PROPERTY_PART)
1226 
1227 static void
1228 ecepp_spin_create_widgets (ECompEditorPropertyPart *property_part,
1229 			   GtkWidget **out_label_widget,
1230 			   GtkWidget **out_edit_widget)
1231 {
1232 	ECompEditorPropertyPartSpinClass *klass;
1233 
1234 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (property_part));
1235 	g_return_if_fail (out_label_widget != NULL);
1236 	g_return_if_fail (out_edit_widget != NULL);
1237 
1238 	klass = E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS (property_part);
1239 	g_return_if_fail (klass != NULL);
1240 
1241 	/* The descendant sets the 'out_label_widget' parameter */
1242 	*out_edit_widget = gtk_spin_button_new_with_range (-10, 10, 1);
1243 	g_return_if_fail (*out_edit_widget != NULL);
1244 
1245 	g_object_set (G_OBJECT (*out_edit_widget),
1246 		"hexpand", FALSE,
1247 		"halign", GTK_ALIGN_FILL,
1248 		"vexpand", FALSE,
1249 		"valign", GTK_ALIGN_START,
1250 		NULL);
1251 
1252 	gtk_spin_button_set_digits (GTK_SPIN_BUTTON (*out_edit_widget), 0);
1253 
1254 	gtk_widget_show (*out_edit_widget);
1255 
1256 	g_signal_connect_swapped (*out_edit_widget, "value-changed",
1257 		G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
1258 }
1259 
1260 static void
ecepp_spin_fill_widget(ECompEditorPropertyPart * property_part,ICalComponent * component)1261 ecepp_spin_fill_widget (ECompEditorPropertyPart *property_part,
1262 			ICalComponent *component)
1263 {
1264 	ECompEditorPropertyPartSpinClass *klass;
1265 	GtkWidget *edit_widget;
1266 	ICalProperty *prop;
1267 	gint value;
1268 
1269 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (property_part));
1270 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
1271 
1272 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
1273 	g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
1274 
1275 	klass = E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS (property_part);
1276 	g_return_if_fail (klass != NULL);
1277 	g_return_if_fail (klass->prop_kind != I_CAL_NO_PROPERTY);
1278 	g_return_if_fail (klass->i_cal_get_func != NULL);
1279 
1280 	prop = i_cal_component_get_first_property (component, klass->prop_kind);
1281 	if (prop) {
1282 		value = klass->i_cal_get_func (prop);
1283 		g_object_unref (prop);
1284 	} else {
1285 		gdouble d_min, d_max;
1286 
1287 		gtk_spin_button_get_range (GTK_SPIN_BUTTON (edit_widget), &d_min, &d_max);
1288 
1289 		value = (gint) d_min;
1290 	}
1291 
1292 	gtk_spin_button_set_value (GTK_SPIN_BUTTON (edit_widget), value);
1293 }
1294 
1295 static void
ecepp_spin_fill_component(ECompEditorPropertyPart * property_part,ICalComponent * component)1296 ecepp_spin_fill_component (ECompEditorPropertyPart *property_part,
1297 			   ICalComponent *component)
1298 {
1299 	ECompEditorPropertyPartSpinClass *klass;
1300 	GtkWidget *edit_widget;
1301 	ICalProperty *prop;
1302 	gint value;
1303 
1304 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (property_part));
1305 	g_return_if_fail (I_CAL_COMPONENT (component));
1306 
1307 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
1308 	g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
1309 
1310 	klass = E_COMP_EDITOR_PROPERTY_PART_SPIN_GET_CLASS (property_part);
1311 	g_return_if_fail (klass != NULL);
1312 	g_return_if_fail (klass->prop_kind != I_CAL_NO_PROPERTY);
1313 	g_return_if_fail (klass->i_cal_new_func != NULL);
1314 	g_return_if_fail (klass->i_cal_set_func != NULL);
1315 
1316 	value = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (edit_widget));
1317 	prop = i_cal_component_get_first_property (component, klass->prop_kind);
1318 
1319 	if (prop) {
1320 		klass->i_cal_set_func (prop, value);
1321 	} else {
1322 		prop = klass->i_cal_new_func (value);
1323 		i_cal_component_add_property (component, prop);
1324 	}
1325 
1326 	g_clear_object (&prop);
1327 }
1328 
1329 static void
e_comp_editor_property_part_spin_init(ECompEditorPropertyPartSpin * part_spin)1330 e_comp_editor_property_part_spin_init (ECompEditorPropertyPartSpin *part_spin)
1331 {
1332 	part_spin->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_spin,
1333 		E_TYPE_COMP_EDITOR_PROPERTY_PART_SPIN,
1334 		ECompEditorPropertyPartSpinPrivate);
1335 }
1336 
1337 static void
e_comp_editor_property_part_spin_class_init(ECompEditorPropertyPartSpinClass * klass)1338 e_comp_editor_property_part_spin_class_init (ECompEditorPropertyPartSpinClass *klass)
1339 {
1340 	ECompEditorPropertyPartClass *part_class;
1341 
1342 	g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartSpinPrivate));
1343 
1344 	klass->prop_kind = I_CAL_NO_PROPERTY;
1345 	klass->i_cal_new_func = NULL;
1346 	klass->i_cal_set_func = NULL;
1347 	klass->i_cal_get_func = NULL;
1348 
1349 	part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
1350 	part_class->create_widgets = ecepp_spin_create_widgets;
1351 	part_class->fill_widget = ecepp_spin_fill_widget;
1352 	part_class->fill_component = ecepp_spin_fill_component;
1353 }
1354 
1355 void
e_comp_editor_property_part_spin_set_range(ECompEditorPropertyPartSpin * part_spin,gint min_value,gint max_value)1356 e_comp_editor_property_part_spin_set_range (ECompEditorPropertyPartSpin *part_spin,
1357 					    gint min_value,
1358 					    gint max_value)
1359 {
1360 	GtkWidget *edit_widget;
1361 
1362 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (part_spin));
1363 
1364 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_spin));
1365 	g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
1366 
1367 	gtk_spin_button_set_range (GTK_SPIN_BUTTON (edit_widget), min_value, max_value);
1368 }
1369 
1370 void
e_comp_editor_property_part_spin_get_range(ECompEditorPropertyPartSpin * part_spin,gint * out_min_value,gint * out_max_value)1371 e_comp_editor_property_part_spin_get_range (ECompEditorPropertyPartSpin *part_spin,
1372 					    gint *out_min_value,
1373 					    gint *out_max_value)
1374 {
1375 	GtkWidget *edit_widget;
1376 	gdouble d_min = 0, d_max = 0;
1377 
1378 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_SPIN (part_spin));
1379 
1380 	edit_widget = e_comp_editor_property_part_get_edit_widget (E_COMP_EDITOR_PROPERTY_PART (part_spin));
1381 	g_return_if_fail (GTK_IS_SPIN_BUTTON (edit_widget));
1382 
1383 	gtk_spin_button_get_range (GTK_SPIN_BUTTON (edit_widget), &d_min, &d_max);
1384 
1385 	if (out_min_value)
1386 		*out_min_value = (gint) d_min;
1387 	if (out_max_value)
1388 		*out_max_value = (gint) d_max;
1389 }
1390 
1391 /* ************************************************************************* */
1392 
1393 struct _ECompEditorPropertyPartPickerPrivate {
1394 	gint dummy;
1395 };
1396 
G_DEFINE_ABSTRACT_TYPE(ECompEditorPropertyPartPicker,e_comp_editor_property_part_picker,E_TYPE_COMP_EDITOR_PROPERTY_PART)1397 G_DEFINE_ABSTRACT_TYPE (ECompEditorPropertyPartPicker, e_comp_editor_property_part_picker, E_TYPE_COMP_EDITOR_PROPERTY_PART)
1398 
1399 static void
1400 ecepp_picker_create_widgets (ECompEditorPropertyPart *property_part,
1401 			     GtkWidget **out_label_widget,
1402 			     GtkWidget **out_edit_widget)
1403 {
1404 	ECompEditorPropertyPartPickerClass *klass;
1405 	GtkComboBoxText *combo_box;
1406 	GSList *ids = NULL, *display_names = NULL, *i_link, *dn_link;
1407 
1408 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (property_part));
1409 	g_return_if_fail (out_label_widget != NULL);
1410 	g_return_if_fail (out_edit_widget != NULL);
1411 
1412 	klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (property_part);
1413 	g_return_if_fail (klass != NULL);
1414 
1415 	/* The descendant sets the 'out_label_widget' parameter */
1416 	*out_edit_widget = gtk_combo_box_text_new ();
1417 	g_return_if_fail (*out_edit_widget != NULL);
1418 
1419 	g_object_set (G_OBJECT (*out_edit_widget),
1420 		"hexpand", FALSE,
1421 		"halign", GTK_ALIGN_FILL,
1422 		"vexpand", FALSE,
1423 		"valign", GTK_ALIGN_START,
1424 		NULL);
1425 
1426 	gtk_widget_show (*out_edit_widget);
1427 
1428 	e_comp_editor_property_part_picker_get_values (E_COMP_EDITOR_PROPERTY_PART_PICKER (property_part),
1429 		&ids, &display_names);
1430 
1431 	g_warn_if_fail (g_slist_length (ids) == g_slist_length (display_names));
1432 
1433 	combo_box = GTK_COMBO_BOX_TEXT (*out_edit_widget);
1434 
1435 	for (i_link = ids, dn_link = display_names; i_link && dn_link; i_link = g_slist_next (i_link), dn_link = g_slist_next (dn_link)) {
1436 		const gchar *id, *display_name;
1437 
1438 		id = i_link->data;
1439 		display_name = dn_link->data;
1440 
1441 		g_warn_if_fail (id != NULL);
1442 		g_warn_if_fail (display_name != NULL);
1443 
1444 		if (!id || !display_name)
1445 			continue;
1446 
1447 		gtk_combo_box_text_append (combo_box, id, display_name);
1448 	}
1449 
1450 	g_slist_free_full (ids, g_free);
1451 	g_slist_free_full (display_names, g_free);
1452 
1453 	g_signal_connect_swapped (*out_edit_widget, "changed",
1454 		G_CALLBACK (e_comp_editor_property_part_emit_changed), property_part);
1455 }
1456 
1457 static void
ecepp_picker_fill_widget(ECompEditorPropertyPart * property_part,ICalComponent * component)1458 ecepp_picker_fill_widget (ECompEditorPropertyPart *property_part,
1459 			  ICalComponent *component)
1460 {
1461 	GtkWidget *edit_widget;
1462 	gchar *id = NULL;
1463 
1464 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (property_part));
1465 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
1466 
1467 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
1468 	g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget));
1469 
1470 	if (e_comp_editor_property_part_picker_get_from_component (
1471 		E_COMP_EDITOR_PROPERTY_PART_PICKER (property_part),
1472 		component, &id) && id) {
1473 		gtk_combo_box_set_active_id (GTK_COMBO_BOX (edit_widget), id);
1474 		g_free (id);
1475 	} else {
1476 		gtk_combo_box_set_active (GTK_COMBO_BOX (edit_widget), 0);
1477 	}
1478 }
1479 
1480 static void
ecepp_picker_fill_component(ECompEditorPropertyPart * property_part,ICalComponent * component)1481 ecepp_picker_fill_component (ECompEditorPropertyPart *property_part,
1482 			     ICalComponent *component)
1483 {
1484 	GtkWidget *edit_widget;
1485 	const gchar *id;
1486 
1487 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (property_part));
1488 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
1489 
1490 	edit_widget = e_comp_editor_property_part_get_edit_widget (property_part);
1491 	g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget));
1492 
1493 	id = gtk_combo_box_get_active_id (GTK_COMBO_BOX (edit_widget));
1494 	if (id) {
1495 		e_comp_editor_property_part_picker_set_to_component (
1496 			E_COMP_EDITOR_PROPERTY_PART_PICKER (property_part),
1497 			id, component);
1498 	}
1499 }
1500 
1501 static void
e_comp_editor_property_part_picker_init(ECompEditorPropertyPartPicker * part_picker)1502 e_comp_editor_property_part_picker_init (ECompEditorPropertyPartPicker *part_picker)
1503 {
1504 	part_picker->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_picker,
1505 		E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER,
1506 		ECompEditorPropertyPartPickerPrivate);
1507 }
1508 
1509 static void
e_comp_editor_property_part_picker_class_init(ECompEditorPropertyPartPickerClass * klass)1510 e_comp_editor_property_part_picker_class_init (ECompEditorPropertyPartPickerClass *klass)
1511 {
1512 	ECompEditorPropertyPartClass *part_class;
1513 
1514 	g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartPickerPrivate));
1515 
1516 	part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
1517 	part_class->create_widgets = ecepp_picker_create_widgets;
1518 	part_class->fill_widget = ecepp_picker_fill_widget;
1519 	part_class->fill_component = ecepp_picker_fill_component;
1520 }
1521 
1522 /* Both out_ids and out_display_names contain newly allocated strings,
1523    where also n-th element of out_uids corresponds to n-th element of
1524    the out_display_names. */
1525 void
e_comp_editor_property_part_picker_get_values(ECompEditorPropertyPartPicker * part_picker,GSList ** out_ids,GSList ** out_display_names)1526 e_comp_editor_property_part_picker_get_values (ECompEditorPropertyPartPicker *part_picker,
1527 					       GSList **out_ids,
1528 					       GSList **out_display_names)
1529 {
1530 	ECompEditorPropertyPartPickerClass *klass;
1531 
1532 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker));
1533 
1534 	klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (part_picker);
1535 	g_return_if_fail (klass != NULL);
1536 	g_return_if_fail (klass->get_values != NULL);
1537 
1538 	klass->get_values (part_picker, out_ids, out_display_names);
1539 }
1540 
1541 gboolean
e_comp_editor_property_part_picker_get_from_component(ECompEditorPropertyPartPicker * part_picker,ICalComponent * component,gchar ** out_id)1542 e_comp_editor_property_part_picker_get_from_component (ECompEditorPropertyPartPicker *part_picker,
1543 						       ICalComponent *component,
1544 						       gchar **out_id)
1545 {
1546 	ECompEditorPropertyPartPickerClass *klass;
1547 
1548 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker), FALSE);
1549 
1550 	klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (part_picker);
1551 	g_return_val_if_fail (klass != NULL, FALSE);
1552 	g_return_val_if_fail (klass->get_from_component != NULL, FALSE);
1553 
1554 	return klass->get_from_component (part_picker, component, out_id);
1555 }
1556 
1557 void
e_comp_editor_property_part_picker_set_to_component(ECompEditorPropertyPartPicker * part_picker,const gchar * id,ICalComponent * component)1558 e_comp_editor_property_part_picker_set_to_component (ECompEditorPropertyPartPicker *part_picker,
1559 						     const gchar *id,
1560 						     ICalComponent *component)
1561 {
1562 	ECompEditorPropertyPartPickerClass *klass;
1563 
1564 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker));
1565 
1566 	klass = E_COMP_EDITOR_PROPERTY_PART_PICKER_GET_CLASS (part_picker);
1567 	g_return_if_fail (klass != NULL);
1568 	g_return_if_fail (klass->set_to_component != NULL);
1569 
1570 	klass->set_to_component (part_picker, id, component);
1571 }
1572 
1573 const gchar *
e_comp_editor_property_part_picker_get_selected_id(ECompEditorPropertyPartPicker * part_picker)1574 e_comp_editor_property_part_picker_get_selected_id (ECompEditorPropertyPartPicker *part_picker)
1575 {
1576 	GtkWidget *edit_widget;
1577 
1578 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker), NULL);
1579 
1580 	edit_widget = e_comp_editor_property_part_get_edit_widget (
1581 		E_COMP_EDITOR_PROPERTY_PART (part_picker));
1582 	g_return_val_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget), NULL);
1583 
1584 	return gtk_combo_box_get_active_id (GTK_COMBO_BOX (edit_widget));
1585 }
1586 
1587 void
e_comp_editor_property_part_picker_set_selected_id(ECompEditorPropertyPartPicker * part_picker,const gchar * id)1588 e_comp_editor_property_part_picker_set_selected_id (ECompEditorPropertyPartPicker *part_picker,
1589 						    const gchar *id)
1590 {
1591 	GtkWidget *edit_widget;
1592 
1593 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker));
1594 	g_return_if_fail (id != NULL);
1595 
1596 	edit_widget = e_comp_editor_property_part_get_edit_widget (
1597 		E_COMP_EDITOR_PROPERTY_PART (part_picker));
1598 	g_return_if_fail (GTK_IS_COMBO_BOX_TEXT (edit_widget));
1599 
1600 	gtk_combo_box_set_active_id (GTK_COMBO_BOX (edit_widget), id);
1601 }
1602 
1603 /* ************************************************************************* */
1604 
1605 struct _ECompEditorPropertyPartPickerWithMapPrivate {
1606 	ECompEditorPropertyPartPickerMap *map;
1607 	gint n_map_elems;
1608 	gchar *label;
1609 
1610 	ICalPropertyKind prop_kind;
1611 	ECompEditorPropertyPartPickerMapICalNewFunc i_cal_new_func;
1612 	ECompEditorPropertyPartPickerMapICalSetFunc i_cal_set_func;
1613 	ECompEditorPropertyPartPickerMapICalGetFunc i_cal_get_func;
1614 };
1615 
1616 enum {
1617 	PICKER_WITH_MAP_PROP_0,
1618 	PICKER_WITH_MAP_PROP_MAP,
1619 	PICKER_WITH_MAP_PROP_LABEL
1620 };
1621 
G_DEFINE_TYPE(ECompEditorPropertyPartPickerWithMap,e_comp_editor_property_part_picker_with_map,E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER)1622 G_DEFINE_TYPE (ECompEditorPropertyPartPickerWithMap, e_comp_editor_property_part_picker_with_map, E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER)
1623 
1624 static void
1625 ecepp_picker_with_map_get_values (ECompEditorPropertyPartPicker *part_picker,
1626 				  GSList **out_ids,
1627 				  GSList **out_display_names)
1628 {
1629 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
1630 	gint ii;
1631 
1632 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker));
1633 	g_return_if_fail (out_ids != NULL);
1634 	g_return_if_fail (out_display_names != NULL);
1635 
1636 	part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker);
1637 	g_return_if_fail (part_picker_with_map->priv->map != NULL);
1638 	g_return_if_fail (part_picker_with_map->priv->n_map_elems > 0);
1639 
1640 	for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
1641 		*out_ids = g_slist_prepend (*out_ids, g_strdup_printf ("%d", ii));
1642 		*out_display_names = g_slist_prepend (*out_display_names,
1643 			g_strdup (part_picker_with_map->priv->map[ii].description));
1644 	}
1645 
1646 	*out_ids = g_slist_reverse (*out_ids);
1647 	*out_display_names = g_slist_reverse (*out_display_names);
1648 }
1649 
1650 static gboolean
ecepp_picker_with_map_get_from_component(ECompEditorPropertyPartPicker * part_picker,ICalComponent * component,gchar ** out_id)1651 ecepp_picker_with_map_get_from_component (ECompEditorPropertyPartPicker *part_picker,
1652 					  ICalComponent *component,
1653 					  gchar **out_id)
1654 {
1655 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
1656 	ICalProperty *prop;
1657 	gint ii, value;
1658 
1659 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker), FALSE);
1660 	g_return_val_if_fail (I_CAL_IS_COMPONENT (component), FALSE);
1661 	g_return_val_if_fail (out_id != NULL, FALSE);
1662 
1663 	part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker);
1664 	g_return_val_if_fail (part_picker_with_map->priv->map != NULL, FALSE);
1665 	g_return_val_if_fail (part_picker_with_map->priv->n_map_elems > 0, FALSE);
1666 	g_return_val_if_fail (part_picker_with_map->priv->prop_kind != I_CAL_NO_PROPERTY, FALSE);
1667 	g_return_val_if_fail (part_picker_with_map->priv->i_cal_get_func != NULL, FALSE);
1668 
1669 	prop = i_cal_component_get_first_property (component, part_picker_with_map->priv->prop_kind);
1670 	if (!prop)
1671 		return FALSE;
1672 
1673 	value = part_picker_with_map->priv->i_cal_get_func (prop);
1674 	g_clear_object (&prop);
1675 
1676 	for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
1677 		gboolean matches;
1678 
1679 		if (part_picker_with_map->priv->map[ii].matches_func) {
1680 			matches = part_picker_with_map->priv->map[ii].matches_func (part_picker_with_map->priv->map[ii].value, value);
1681 		} else {
1682 			matches = value == part_picker_with_map->priv->map[ii].value;
1683 		}
1684 
1685 		if (matches) {
1686 			*out_id = g_strdup_printf ("%d", ii);
1687 			return TRUE;
1688 		}
1689 	}
1690 
1691 	return FALSE;
1692 }
1693 
1694 static void
ecepp_picker_with_map_set_to_component(ECompEditorPropertyPartPicker * part_picker,const gchar * id,ICalComponent * component)1695 ecepp_picker_with_map_set_to_component (ECompEditorPropertyPartPicker *part_picker,
1696 					const gchar *id,
1697 					ICalComponent *component)
1698 {
1699 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
1700 	ICalProperty *prop;
1701 	gint ii, value;
1702 
1703 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker));
1704 	g_return_if_fail (id != NULL);
1705 	g_return_if_fail (I_CAL_IS_COMPONENT (component));
1706 
1707 	part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker);
1708 	g_return_if_fail (part_picker_with_map->priv->map != NULL);
1709 	g_return_if_fail (part_picker_with_map->priv->n_map_elems > 0);
1710 	g_return_if_fail (part_picker_with_map->priv->prop_kind != I_CAL_NO_PROPERTY);
1711 	g_return_if_fail (part_picker_with_map->priv->i_cal_new_func != NULL);
1712 	g_return_if_fail (part_picker_with_map->priv->i_cal_set_func != NULL);
1713 
1714 	ii = (gint) g_ascii_strtoll (id, NULL, 10);
1715 	g_return_if_fail (ii >= 0 && ii < part_picker_with_map->priv->n_map_elems);
1716 
1717 	prop = i_cal_component_get_first_property (component, part_picker_with_map->priv->prop_kind);
1718 	value = part_picker_with_map->priv->map[ii].value;
1719 
1720 	if (part_picker_with_map->priv->map[ii].delete_prop) {
1721 		if (prop)
1722 			i_cal_component_remove_property (component, prop);
1723 	} else if (prop) {
1724 		part_picker_with_map->priv->i_cal_set_func (prop, value);
1725 	} else {
1726 		prop = part_picker_with_map->priv->i_cal_new_func (value);
1727 		i_cal_component_add_property (component, prop);
1728 	}
1729 
1730 	g_clear_object (&prop);
1731 }
1732 
1733 static void
ecepp_picker_with_map_create_widgets(ECompEditorPropertyPart * property_part,GtkWidget ** out_label_widget,GtkWidget ** out_edit_widget)1734 ecepp_picker_with_map_create_widgets (ECompEditorPropertyPart *property_part,
1735 				      GtkWidget **out_label_widget,
1736 				      GtkWidget **out_edit_widget)
1737 {
1738 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
1739 	ECompEditorPropertyPartClass *part_class;
1740 
1741 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (property_part));
1742 	g_return_if_fail (out_label_widget != NULL);
1743 	g_return_if_fail (out_edit_widget != NULL);
1744 
1745 	part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (e_comp_editor_property_part_picker_with_map_parent_class);
1746 	g_return_if_fail (part_class != NULL);
1747 	g_return_if_fail (part_class->create_widgets != NULL);
1748 
1749 	*out_label_widget = NULL;
1750 
1751 	part_class->create_widgets (property_part, out_label_widget, out_edit_widget);
1752 	g_return_if_fail (*out_label_widget == NULL);
1753 	g_return_if_fail (*out_edit_widget != NULL);
1754 
1755 	part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (property_part);
1756 
1757 	*out_label_widget = gtk_label_new_with_mnemonic (part_picker_with_map->priv->label);
1758 	gtk_label_set_mnemonic_widget (GTK_LABEL (*out_label_widget), *out_edit_widget);
1759 
1760 	g_object_set (G_OBJECT (*out_label_widget),
1761 		"hexpand", FALSE,
1762 		"halign", GTK_ALIGN_END,
1763 		"vexpand", FALSE,
1764 		"valign", GTK_ALIGN_CENTER,
1765 		NULL);
1766 
1767 	gtk_widget_show (*out_label_widget);
1768 }
1769 
1770 static void
ecepp_picker_with_map_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1771 ecepp_picker_with_map_set_property (GObject *object,
1772 				    guint property_id,
1773 				    const GValue *value,
1774 				    GParamSpec *pspec)
1775 {
1776 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
1777 	gint ii;
1778 
1779 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (object));
1780 
1781 	part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (object);
1782 
1783 	switch (property_id) {
1784 		case PICKER_WITH_MAP_PROP_MAP:
1785 			g_return_if_fail (part_picker_with_map->priv->map == NULL);
1786 
1787 			part_picker_with_map->priv->map = g_value_get_pointer (value);
1788 			for (ii = 0; part_picker_with_map->priv->map[ii].description; ii++) {
1789 				/* pre-count elements */
1790 			}
1791 
1792 			part_picker_with_map->priv->n_map_elems = ii;
1793 			return;
1794 
1795 		case PICKER_WITH_MAP_PROP_LABEL:
1796 			g_free (part_picker_with_map->priv->label);
1797 			part_picker_with_map->priv->label = g_value_dup_string (value);
1798 			return;
1799 	}
1800 
1801 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1802 }
1803 
1804 static void
ecepp_picker_with_map_finalize(GObject * object)1805 ecepp_picker_with_map_finalize (GObject *object)
1806 {
1807 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map =
1808 		E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (object);
1809 
1810 	if (part_picker_with_map->priv->map && part_picker_with_map->priv->n_map_elems > 0) {
1811 		gint ii;
1812 
1813 		for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
1814 			g_free ((gchar *) part_picker_with_map->priv->map[ii].description);
1815 		}
1816 
1817 		g_free (part_picker_with_map->priv->map);
1818 		part_picker_with_map->priv->map = NULL;
1819 	}
1820 
1821 	g_free (part_picker_with_map->priv->label);
1822 	part_picker_with_map->priv->label = NULL;
1823 
1824 	G_OBJECT_CLASS (e_comp_editor_property_part_picker_with_map_parent_class)->finalize (object);
1825 }
1826 
1827 static void
e_comp_editor_property_part_picker_with_map_init(ECompEditorPropertyPartPickerWithMap * part_picker_with_map)1828 e_comp_editor_property_part_picker_with_map_init (ECompEditorPropertyPartPickerWithMap *part_picker_with_map)
1829 {
1830 	part_picker_with_map->priv = G_TYPE_INSTANCE_GET_PRIVATE (part_picker_with_map,
1831 		E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP,
1832 		ECompEditorPropertyPartPickerWithMapPrivate);
1833 }
1834 
1835 static void
e_comp_editor_property_part_picker_with_map_class_init(ECompEditorPropertyPartPickerWithMapClass * klass)1836 e_comp_editor_property_part_picker_with_map_class_init (ECompEditorPropertyPartPickerWithMapClass *klass)
1837 {
1838 	ECompEditorPropertyPartPickerClass *part_picker_class;
1839 	ECompEditorPropertyPartClass *part_class;
1840 	GObjectClass *object_class;
1841 
1842 	g_type_class_add_private (klass, sizeof (ECompEditorPropertyPartPickerWithMapPrivate));
1843 
1844 	part_picker_class = E_COMP_EDITOR_PROPERTY_PART_PICKER_CLASS (klass);
1845 	part_picker_class->get_values = ecepp_picker_with_map_get_values;
1846 	part_picker_class->get_from_component = ecepp_picker_with_map_get_from_component;
1847 	part_picker_class->set_to_component = ecepp_picker_with_map_set_to_component;
1848 
1849 	part_class = E_COMP_EDITOR_PROPERTY_PART_CLASS (klass);
1850 	part_class->create_widgets = ecepp_picker_with_map_create_widgets;
1851 
1852 	object_class = G_OBJECT_CLASS (klass);
1853 	object_class->set_property = ecepp_picker_with_map_set_property;
1854 	object_class->finalize = ecepp_picker_with_map_finalize;
1855 
1856 	g_object_class_install_property (
1857 		object_class,
1858 		PICKER_WITH_MAP_PROP_MAP,
1859 		g_param_spec_pointer (
1860 			"map",
1861 			"Map",
1862 			"Map of values, .description-NULL-terminated",
1863 			G_PARAM_WRITABLE |
1864 			G_PARAM_CONSTRUCT_ONLY |
1865 			G_PARAM_STATIC_STRINGS));
1866 
1867 	g_object_class_install_property (
1868 		object_class,
1869 		PICKER_WITH_MAP_PROP_LABEL,
1870 		g_param_spec_string (
1871 			"label",
1872 			"Label",
1873 			"Label of the picker",
1874 			NULL,
1875 			G_PARAM_WRITABLE |
1876 			G_PARAM_CONSTRUCT_ONLY |
1877 			G_PARAM_STATIC_STRINGS));
1878 }
1879 
1880 ECompEditorPropertyPart *
e_comp_editor_property_part_picker_with_map_new(const ECompEditorPropertyPartPickerMap map[],gint n_map_elements,const gchar * label,ICalPropertyKind prop_kind,ECompEditorPropertyPartPickerMapICalNewFunc i_cal_new_func,ECompEditorPropertyPartPickerMapICalSetFunc i_cal_set_func,ECompEditorPropertyPartPickerMapICalGetFunc i_cal_get_func)1881 e_comp_editor_property_part_picker_with_map_new (const ECompEditorPropertyPartPickerMap map[],
1882 						 gint n_map_elements,
1883 						 const gchar *label,
1884 						 ICalPropertyKind prop_kind,
1885 						 ECompEditorPropertyPartPickerMapICalNewFunc i_cal_new_func,
1886 						 ECompEditorPropertyPartPickerMapICalSetFunc i_cal_set_func,
1887 						 ECompEditorPropertyPartPickerMapICalGetFunc i_cal_get_func)
1888 {
1889 	ECompEditorPropertyPartPickerWithMap *part_picker_with_map;
1890 	ECompEditorPropertyPartPickerMap *map_copy;
1891 	ECompEditorPropertyPart *property_part;
1892 	gint ii;
1893 
1894 	g_return_val_if_fail (map != NULL, NULL);
1895 	g_return_val_if_fail (n_map_elements > 0, NULL);
1896 	g_return_val_if_fail (label != NULL, NULL);
1897 	g_return_val_if_fail (prop_kind != I_CAL_NO_PROPERTY, NULL);
1898 	g_return_val_if_fail (i_cal_new_func != NULL, NULL);
1899 	g_return_val_if_fail (i_cal_set_func != NULL, NULL);
1900 	g_return_val_if_fail (i_cal_get_func != NULL, NULL);
1901 
1902 	map_copy = g_new0 (ECompEditorPropertyPartPickerMap, n_map_elements + 1);
1903 	for (ii = 0; ii < n_map_elements; ii++) {
1904 		map_copy[ii] = map[ii];
1905 		map_copy[ii].description = g_strdup (map[ii].description);
1906 	}
1907 
1908 	property_part = g_object_new (E_TYPE_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP,
1909 		"map", map_copy,
1910 		"label", label,
1911 		NULL);
1912 
1913 	part_picker_with_map = E_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (property_part);
1914 
1915 	part_picker_with_map->priv->prop_kind = prop_kind;
1916 	part_picker_with_map->priv->i_cal_new_func = i_cal_new_func;
1917 	part_picker_with_map->priv->i_cal_set_func = i_cal_set_func;
1918 	part_picker_with_map->priv->i_cal_get_func = i_cal_get_func;
1919 
1920 	return property_part;
1921 }
1922 
1923 gint
e_comp_editor_property_part_picker_with_map_get_selected(ECompEditorPropertyPartPickerWithMap * part_picker_with_map)1924 e_comp_editor_property_part_picker_with_map_get_selected (ECompEditorPropertyPartPickerWithMap *part_picker_with_map)
1925 {
1926 	gint ii;
1927 	const gchar *id;
1928 
1929 	g_return_val_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker_with_map), -1);
1930 	g_return_val_if_fail (part_picker_with_map->priv->map != NULL, -1);
1931 
1932 	id = e_comp_editor_property_part_picker_get_selected_id (E_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker_with_map));
1933 	if (!id)
1934 		return -1;
1935 
1936 	ii = (gint) g_ascii_strtoll (id, NULL, 10);
1937 	if (ii < 0 || ii >= part_picker_with_map->priv->n_map_elems)
1938 		return -1;
1939 
1940 	return part_picker_with_map->priv->map[ii].value;
1941 }
1942 
1943 void
e_comp_editor_property_part_picker_with_map_set_selected(ECompEditorPropertyPartPickerWithMap * part_picker_with_map,gint value)1944 e_comp_editor_property_part_picker_with_map_set_selected (ECompEditorPropertyPartPickerWithMap *part_picker_with_map,
1945 							  gint value)
1946 {
1947 	gint ii;
1948 
1949 	g_return_if_fail (E_IS_COMP_EDITOR_PROPERTY_PART_PICKER_WITH_MAP (part_picker_with_map));
1950 	g_return_if_fail (part_picker_with_map->priv->map != NULL);
1951 
1952 	for (ii = 0; ii < part_picker_with_map->priv->n_map_elems; ii++) {
1953 		if (part_picker_with_map->priv->map[ii].value == value) {
1954 			gchar *id;
1955 
1956 			id = g_strdup_printf ("%d", ii);
1957 			e_comp_editor_property_part_picker_set_selected_id (
1958 				E_COMP_EDITOR_PROPERTY_PART_PICKER (part_picker_with_map), id);
1959 			g_free (id);
1960 
1961 			return;
1962 		}
1963 	}
1964 
1965 	g_warn_if_reached ();
1966 }
1967 
1968 /* ************************************************************************* */
1969