2 * This file is a part of hildon
4 * Copyright (C) 2005, 2008 Nokia Corporation.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version. or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * SECTION:hildon-picker-dialog
23 * @short_description: A utility widget that shows a #HildonTouchSelector widget
25 * #HildonPickerDialog is a dialog that is used to show a
26 * #HildonTouchSelector widget and a 'Done' button to allow users to
27 * finish their selections.
29 * The #HildonPickerDialog will show a 'Done' button in case the
30 * #HildonTouchSelector allows multiple selection. The label of the
31 * button can be set using hildon_picker_dialog_set_done_label() and
32 * retrieved using hildon_picker_dialog_get_done_label()
34 * Note that in most cases developers don't need to deal directly with
35 * this widget. #HildonPickerButton is designed to pop up a
36 * #HildonPickerDialog and manage the interaction with it.
47 #include "hildon-touch-selector.h"
48 #include "hildon-touch-selector-entry.h"
49 #include "hildon-picker-dialog.h"
51 #define _(String) dgettext("hildon-libs", String)
53 #define HILDON_PICKER_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_PICKER_DIALOG, HildonPickerDialogPrivate))
55 G_DEFINE_TYPE (HildonPickerDialog, hildon_picker_dialog, HILDON_TYPE_DIALOG)
57 #define HILDON_TOUCH_SELECTOR_HEIGHT 320
59 struct _HildonPickerDialogPrivate
64 gulong signal_changed_id;
65 gulong signal_columns_changed_id;
67 gboolean center_on_show;
68 GSList *current_selection;
76 PROP_DONE_BUTTON_TEXT,
87 #define DEFAULT_DONE_BUTTON_TEXT _("wdgt_bd_done")
90 hildon_picker_dialog_set_property (GObject * object,
96 hildon_picker_dialog_get_property (GObject * object,
98 GValue * value, GParamSpec * pspec);
101 hildon_picker_dialog_finalize (GObject *object);
105 hildon_picker_dialog_show (GtkWidget *widget);
108 hildon_picker_dialog_realize (GtkWidget *widget);
110 /* private functions */
112 requires_done_button (HildonPickerDialog * dialog);
115 prepare_action_area (HildonPickerDialog *dialog);
118 setup_interaction_mode (HildonPickerDialog * dialog);
121 _select_on_selector_changed_cb (HildonTouchSelector * dialog,
126 _hildon_picker_dialog_set_selector (HildonPickerDialog * dialog,
127 HildonTouchSelector * selector);
130 _on_dialog_response (GtkDialog *dialog,
135 _save_current_selection (HildonPickerDialog *dialog);
138 _restore_current_selection (HildonPickerDialog *dialog);
141 _clean_current_selection (HildonPickerDialog *dialog);
143 /**********************************************************************************/
146 hildon_picker_dialog_class_init (HildonPickerDialogClass * class)
148 GObjectClass *gobject_class;
149 GtkObjectClass *object_class;
150 GtkWidgetClass *widget_class;
151 GtkContainerClass *container_class;
153 gobject_class = (GObjectClass *) class;
154 object_class = (GtkObjectClass *) class;
155 widget_class = (GtkWidgetClass *) class;
156 container_class = (GtkContainerClass *) class;
159 gobject_class->set_property = hildon_picker_dialog_set_property;
160 gobject_class->get_property = hildon_picker_dialog_get_property;
161 gobject_class->finalize = hildon_picker_dialog_finalize;
164 widget_class->show = hildon_picker_dialog_show;
165 widget_class->realize = hildon_picker_dialog_realize;
167 /* HildonPickerDialog */
168 class->set_selector = _hildon_picker_dialog_set_selector;
180 g_object_class_install_property (gobject_class,
181 PROP_DONE_BUTTON_TEXT,
182 g_param_spec_string ("done-button-text",
185 DEFAULT_DONE_BUTTON_TEXT,
190 g_object_class_install_property (gobject_class,
192 g_param_spec_boolean ("center-on-show",
194 "If the dialog should center"
195 " on the current selection"
196 " when it is showed",
201 g_type_class_add_private (object_class, sizeof (HildonPickerDialogPrivate));
206 hildon_picker_dialog_init (HildonPickerDialog * dialog)
208 dialog->priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
210 dialog->priv->selector = NULL;
211 dialog->priv->button =
212 gtk_dialog_add_button (GTK_DIALOG (dialog), "", GTK_RESPONSE_OK);
213 gtk_widget_grab_default (dialog->priv->button);
215 dialog->priv->signal_changed_id = 0;
216 dialog->priv->signal_columns_changed_id = 0;
217 dialog->priv->center_on_show = TRUE;
218 dialog->priv->current_selection = NULL;
219 dialog->priv->current_text = NULL;
221 g_signal_connect (G_OBJECT (dialog),
222 "response", G_CALLBACK (_on_dialog_response),
228 hildon_picker_dialog_set_property (GObject * object,
230 const GValue * value, GParamSpec * pspec)
232 HildonPickerDialog *dialog;
234 dialog = HILDON_PICKER_DIALOG (object);
237 case PROP_DONE_BUTTON_TEXT:
238 hildon_picker_dialog_set_done_label (HILDON_PICKER_DIALOG (object),
239 g_value_get_string (value));
241 case PROP_CENTER_ON_SHOW:
242 dialog->priv->center_on_show = g_value_get_boolean (value);
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
251 hildon_picker_dialog_get_property (GObject * object,
253 GValue * value, GParamSpec * pspec)
255 HildonPickerDialog *dialog;
257 dialog = HILDON_PICKER_DIALOG (object);
260 case PROP_DONE_BUTTON_TEXT:
261 g_value_set_string (value, hildon_picker_dialog_get_done_label (dialog));
263 case PROP_CENTER_ON_SHOW:
264 g_value_set_boolean (value, dialog->priv->center_on_show);
267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
273 hildon_picker_dialog_finalize (GObject *object)
275 _clean_current_selection (HILDON_PICKER_DIALOG (object));
277 G_OBJECT_CLASS (hildon_picker_dialog_parent_class)->finalize (object);
281 hildon_picker_dialog_show (GtkWidget *widget)
283 HildonPickerDialog *dialog = HILDON_PICKER_DIALOG (widget);
284 HildonTouchSelector *selector;
286 GTK_WIDGET_CLASS (hildon_picker_dialog_parent_class)->show (widget);
288 if (dialog->priv->center_on_show) {
289 selector = hildon_picker_dialog_get_selector (dialog);
290 hildon_touch_selector_center_on_selected (selector);
293 _save_current_selection (dialog);
294 prepare_action_area (dialog);
299 hildon_picker_dialog_realize (GtkWidget *widget)
301 setup_interaction_mode (HILDON_PICKER_DIALOG (widget));
303 GTK_WIDGET_CLASS (hildon_picker_dialog_parent_class)->realize (widget);
306 /* ------------------------------ PRIVATE METHODS ---------------------------- */
309 _select_on_selector_changed_cb (HildonTouchSelector * selector,
310 gint column, gpointer data)
312 g_return_if_fail (HILDON_IS_PICKER_DIALOG (data));
314 gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK);
318 selection_completed (HildonPickerDialog *dialog)
320 HildonPickerDialogPrivate *priv;
323 gboolean all_selected = TRUE;
325 priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
327 n_cols = hildon_touch_selector_get_num_columns (HILDON_TOUCH_SELECTOR (priv->selector));
328 for (i = 0; i < n_cols; i++) {
329 list = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (priv->selector), i);
331 all_selected = FALSE;
334 g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
342 _on_dialog_response (GtkDialog *dialog,
346 if (response_id == GTK_RESPONSE_OK) {
347 if (selection_completed (HILDON_PICKER_DIALOG (dialog)) == FALSE) {
348 g_signal_stop_emission_by_name (dialog, "response");
350 } else if (response_id == GTK_RESPONSE_DELETE_EVENT) {
351 _restore_current_selection (HILDON_PICKER_DIALOG (dialog));
356 on_selector_columns_changed (HildonTouchSelector * selector, gpointer userdata)
358 HildonPickerDialog * dialog;
360 dialog = HILDON_PICKER_DIALOG (userdata);
362 prepare_action_area (dialog);
363 if (GTK_WIDGET_REALIZED (dialog)) {
364 setup_interaction_mode (dialog);
369 * hildon_picker_dialog_set_done_label:
370 * @dialog: a #HildonPickerDialog
373 * Sets a custom string to be used as the 'Done' button label in @dialog.
378 hildon_picker_dialog_set_done_label (HildonPickerDialog * dialog,
381 HildonPickerDialogPrivate *priv;
383 g_return_if_fail (HILDON_IS_PICKER_DIALOG (dialog));
384 g_return_if_fail (label != NULL);
386 priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
388 gtk_button_set_label (GTK_BUTTON (priv->button), label);
392 * hildon_picker_dialog_get_done_label:
393 * @dialog: a #HildonPickerDialog
395 * Retrieves current 'Done' button label.
397 * Returns: the custom string to be used.
402 hildon_picker_dialog_get_done_label (HildonPickerDialog * dialog)
404 HildonPickerDialogPrivate *priv;
406 g_return_val_if_fail (HILDON_IS_PICKER_DIALOG (dialog), NULL);
408 priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
410 return gtk_button_get_label (GTK_BUTTON (priv->button));
414 free_path_list (GList *list)
416 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
421 _clean_current_selection (HildonPickerDialog *dialog)
423 if (dialog->priv->current_selection) {
424 g_slist_foreach (dialog->priv->current_selection, (GFunc) free_path_list, NULL);
425 g_slist_free (dialog->priv->current_selection);
426 dialog->priv->current_selection = NULL;
428 if (dialog->priv->current_text) {
429 g_free (dialog->priv->current_text);
430 dialog->priv->current_text = NULL;
435 _save_current_selection (HildonPickerDialog *dialog)
437 HildonTouchSelector *selector;
440 selector = HILDON_TOUCH_SELECTOR (dialog->priv->selector);
442 _clean_current_selection (dialog);
444 columns = hildon_touch_selector_get_num_columns (selector);
445 for (i = 0; i < columns; i++) {
446 dialog->priv->current_selection
447 = g_slist_append (dialog->priv->current_selection,
448 hildon_touch_selector_get_selected_rows (selector, i));
450 if (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector)) {
451 HildonEntry *entry = hildon_touch_selector_entry_get_entry (HILDON_TOUCH_SELECTOR_ENTRY (selector));
452 dialog->priv->current_text = g_strdup (hildon_entry_get_text (entry));
457 _restore_current_selection (HildonPickerDialog *dialog)
459 GSList *current_selection, *iter;
460 GList *selected, *selected_iter;
461 GtkTreePath *current_path;
462 HildonTouchSelector *selector;
464 GtkTreeIter tree_iter;
467 if (dialog->priv->current_selection == NULL)
470 current_selection = dialog->priv->current_selection;
471 selector = HILDON_TOUCH_SELECTOR (dialog->priv->selector);
473 if (hildon_touch_selector_get_num_columns (selector) !=
474 g_slist_length (current_selection)) {
475 /* We conclude that if the current selection has the same
476 numbers of columns that the selector, all this ok
477 Anyway this shouldn't happen. */
478 g_critical ("Trying to restore the selection on a selector after change"
479 " the number of columns. Are you removing columns while the"
484 if (dialog->priv->signal_changed_id)
485 g_signal_handler_block (selector, dialog->priv->signal_changed_id);
486 for (iter = current_selection, i = 0; iter; iter = g_slist_next (iter), i++) {
487 selected = (GList *) (iter->data);
488 model = hildon_touch_selector_get_model (selector, i);
489 hildon_touch_selector_unselect_all (selector, i);
490 for (selected_iter = selected; selected_iter; selected_iter = g_list_next (selected_iter)) {
491 current_path = (GtkTreePath *) selected_iter->data;
492 gtk_tree_model_get_iter (model, &tree_iter, current_path);
493 hildon_touch_selector_select_iter (selector, i, &tree_iter, FALSE);
496 if (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector) && dialog->priv->current_text != NULL) {
497 HildonEntry *entry = hildon_touch_selector_entry_get_entry (HILDON_TOUCH_SELECTOR_ENTRY (selector));
498 hildon_entry_set_text (entry, dialog->priv->current_text);
500 if (dialog->priv->signal_changed_id)
501 g_signal_handler_unblock (selector, dialog->priv->signal_changed_id);
505 requires_done_button (HildonPickerDialog * dialog)
507 return hildon_touch_selector_has_multiple_selection
508 (HILDON_TOUCH_SELECTOR (dialog->priv->selector));
512 prepare_action_area (HildonPickerDialog *dialog)
514 if (requires_done_button (dialog)) {
515 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), TRUE);
516 gtk_widget_show (GTK_DIALOG (dialog)->action_area);
518 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
519 gtk_widget_hide (GTK_DIALOG (dialog)->action_area);
524 setup_interaction_mode (HildonPickerDialog * dialog)
526 if (dialog->priv->signal_changed_id) {
527 g_signal_handler_disconnect (dialog->priv->selector,
528 dialog->priv->signal_changed_id);
531 if (requires_done_button (dialog) == FALSE) {
532 dialog->priv->signal_changed_id =
533 g_signal_connect (G_OBJECT (dialog->priv->selector), "changed",
534 G_CALLBACK (_select_on_selector_changed_cb), dialog);
538 /*------------------------- PUBLIC METHODS ---------------------------- */
541 * hildon_picker_dialog_new:
542 * @parent: the parent window
544 * Creates a new #HildonPickerDialog
546 * Returns: a new #HildonPickerDialog
551 hildon_picker_dialog_new (GtkWindow * parent)
553 GtkDialog *dialog = NULL;
555 dialog = g_object_new (HILDON_TYPE_PICKER_DIALOG, NULL);
558 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
561 return GTK_WIDGET (dialog);
566 _hildon_picker_dialog_set_selector (HildonPickerDialog * dialog,
567 HildonTouchSelector * selector)
569 g_object_ref (selector);
571 /* Remove the old selector, if any */
572 if (dialog->priv->selector != NULL) {
573 gtk_container_remove (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
574 dialog->priv->selector);
575 if (dialog->priv->signal_columns_changed_id) {
576 g_signal_handler_disconnect (dialog->priv->selector,
577 dialog->priv->signal_columns_changed_id);
581 dialog->priv->selector = GTK_WIDGET (selector);
583 /* Pack the new selector */
584 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
585 dialog->priv->selector, TRUE, TRUE, 0);
587 g_object_unref (selector);
589 /* Ensure that the dialog's height is correct */
590 gtk_widget_set_size_request (GTK_WIDGET (dialog->priv->selector), -1,
591 HILDON_TOUCH_SELECTOR_HEIGHT);
593 gtk_widget_show (dialog->priv->selector);
595 prepare_action_area (dialog);
596 if (GTK_WIDGET_REALIZED (dialog)) {
597 setup_interaction_mode (dialog);
600 dialog->priv->signal_columns_changed_id = g_signal_connect (G_OBJECT (dialog->priv->selector),
602 G_CALLBACK (on_selector_columns_changed), dialog);
607 * hildon_picker_dialog_set_selector:
608 * @dialog: a #HildonPickerDialog
609 * @selector: a #HildonTouchSelector
611 * Sets @selector as the #HildonTouchSelector to be shown in @dialog
613 * Returns: %TRUE if @selector was set, %FALSE otherwise
618 hildon_picker_dialog_set_selector (HildonPickerDialog * dialog,
619 HildonTouchSelector * selector)
621 g_return_val_if_fail (HILDON_IS_PICKER_DIALOG (dialog), FALSE);
622 g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
624 return HILDON_PICKER_DIALOG_GET_CLASS (dialog)->set_selector (dialog, selector);
628 * hildon_picker_dialog_get_selector:
629 * @dialog: a #HildonPickerDialog
631 * Retrieves the #HildonTouchSelector associated to @dialog.
633 * Returns: a #HildonTouchSelector
637 HildonTouchSelector *
638 hildon_picker_dialog_get_selector (HildonPickerDialog * dialog)
640 g_return_val_if_fail (HILDON_IS_PICKER_DIALOG (dialog), NULL);
642 return HILDON_TOUCH_SELECTOR (dialog->priv->selector);