2 * This file is a part of hildon
4 * Copyright (C) 2005, 2008, 2009 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 struct _HildonPickerDialogPrivate
62 gulong signal_changed_id;
63 gulong signal_columns_changed_id;
65 gboolean center_on_show;
66 GSList *current_selection;
74 PROP_DONE_BUTTON_TEXT,
85 #define DEFAULT_DONE_BUTTON_TEXT _("wdgt_bd_done")
88 hildon_picker_dialog_set_property (GObject * object,
94 hildon_picker_dialog_get_property (GObject * object,
96 GValue * value, GParamSpec * pspec);
99 hildon_picker_dialog_finalize (GObject *object);
103 hildon_picker_dialog_show (GtkWidget *widget);
106 hildon_picker_dialog_realize (GtkWidget *widget);
109 hildon_picker_dialog_size_request (GtkWidget *widget,
110 GtkRequisition *requisition);
112 /* private functions */
114 requires_done_button (HildonPickerDialog * dialog);
117 prepare_action_area (HildonPickerDialog *dialog);
120 setup_interaction_mode (HildonPickerDialog * dialog);
123 _select_on_selector_changed_cb (HildonTouchSelector * dialog,
128 _hildon_picker_dialog_set_selector (HildonPickerDialog * dialog,
129 HildonTouchSelector * selector);
132 _on_dialog_response (GtkDialog *dialog,
137 _save_current_selection (HildonPickerDialog *dialog);
140 _restore_current_selection (HildonPickerDialog *dialog);
143 _clean_current_selection (HildonPickerDialog *dialog);
146 hildon_picker_dialog_get_max_height (HildonPickerDialog *dialog);
148 /**********************************************************************************/
151 hildon_picker_dialog_class_init (HildonPickerDialogClass * class)
153 GObjectClass *gobject_class;
154 GtkObjectClass *object_class;
155 GtkWidgetClass *widget_class;
156 GtkContainerClass *container_class;
158 gobject_class = (GObjectClass *) class;
159 object_class = (GtkObjectClass *) class;
160 widget_class = (GtkWidgetClass *) class;
161 container_class = (GtkContainerClass *) class;
164 gobject_class->set_property = hildon_picker_dialog_set_property;
165 gobject_class->get_property = hildon_picker_dialog_get_property;
166 gobject_class->finalize = hildon_picker_dialog_finalize;
169 widget_class->show = hildon_picker_dialog_show;
170 widget_class->realize = hildon_picker_dialog_realize;
171 widget_class->size_request = hildon_picker_dialog_size_request,
173 /* HildonPickerDialog */
174 class->set_selector = _hildon_picker_dialog_set_selector;
186 g_object_class_install_property (gobject_class,
187 PROP_DONE_BUTTON_TEXT,
188 g_param_spec_string ("done-button-text",
191 DEFAULT_DONE_BUTTON_TEXT,
196 g_object_class_install_property (gobject_class,
198 g_param_spec_boolean ("center-on-show",
200 "If the dialog should center"
201 " on the current selection"
202 " when it is showed",
207 /* Using the default height, we get 5 full rows. With the header it sums 404 pixels */
208 gtk_widget_class_install_style_property (widget_class,
210 ("max-height-landscape",
211 "Max dialog height on landscape mode",
212 "Maximum dialog height on landscape mode",
218 /* Using the default height, we get 9 full rows. With the header it sums 684 pixels */
219 gtk_widget_class_install_style_property (widget_class,
221 ("max-height-portrait",
222 "Max dialog height on portrait mode",
223 "Maximum dialog height on portrait mode",
229 g_type_class_add_private (object_class, sizeof (HildonPickerDialogPrivate));
234 hildon_picker_dialog_init (HildonPickerDialog * dialog)
236 dialog->priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
238 dialog->priv->selector = NULL;
239 dialog->priv->button =
240 gtk_dialog_add_button (GTK_DIALOG (dialog), "", GTK_RESPONSE_OK);
241 gtk_widget_grab_default (dialog->priv->button);
242 gtk_button_set_focus_on_click (GTK_BUTTON (dialog->priv->button), FALSE);
243 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
245 dialog->priv->signal_changed_id = 0;
246 dialog->priv->signal_columns_changed_id = 0;
247 dialog->priv->center_on_show = TRUE;
248 dialog->priv->current_selection = NULL;
249 dialog->priv->current_text = NULL;
251 g_signal_connect (G_OBJECT (dialog),
252 "response", G_CALLBACK (_on_dialog_response),
258 hildon_picker_dialog_set_property (GObject * object,
260 const GValue * value, GParamSpec * pspec)
262 HildonPickerDialog *dialog;
264 dialog = HILDON_PICKER_DIALOG (object);
267 case PROP_DONE_BUTTON_TEXT:
268 hildon_picker_dialog_set_done_label (HILDON_PICKER_DIALOG (object),
269 g_value_get_string (value));
271 case PROP_CENTER_ON_SHOW:
272 dialog->priv->center_on_show = g_value_get_boolean (value);
275 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
281 hildon_picker_dialog_get_property (GObject * object,
283 GValue * value, GParamSpec * pspec)
285 HildonPickerDialog *dialog;
287 dialog = HILDON_PICKER_DIALOG (object);
290 case PROP_DONE_BUTTON_TEXT:
291 g_value_set_string (value, hildon_picker_dialog_get_done_label (dialog));
293 case PROP_CENTER_ON_SHOW:
294 g_value_set_boolean (value, dialog->priv->center_on_show);
297 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
303 hildon_picker_dialog_finalize (GObject *object)
305 _clean_current_selection (HILDON_PICKER_DIALOG (object));
307 G_OBJECT_CLASS (hildon_picker_dialog_parent_class)->finalize (object);
311 hildon_picker_dialog_show (GtkWidget *widget)
313 HildonPickerDialog *dialog = HILDON_PICKER_DIALOG (widget);
314 HildonTouchSelector *selector;
316 GTK_WIDGET_CLASS (hildon_picker_dialog_parent_class)->show (widget);
318 if (dialog->priv->center_on_show) {
319 selector = hildon_picker_dialog_get_selector (dialog);
320 hildon_touch_selector_center_on_selected (selector);
323 _save_current_selection (dialog);
324 prepare_action_area (dialog);
329 hildon_picker_dialog_size_request (GtkWidget *widget,
330 GtkRequisition *requisition)
332 HildonTouchSelector *selector;
334 selector = hildon_picker_dialog_get_selector (HILDON_PICKER_DIALOG (widget));
337 GtkRequisition child_requisition;
338 GtkRequisition optimal_requisition;
342 bin = GTK_BIN (widget);
344 requisition->width = GTK_CONTAINER (widget)->border_width * 2;
345 /* Adding pannable container border using 4 instead of 2 */
346 requisition->height = GTK_CONTAINER (widget)->border_width * 4;
348 /* assure the requisition is done */
349 gtk_widget_size_request (bin->child, &child_requisition);
351 hildon_touch_selector_optimal_size_request (selector,
352 &optimal_requisition);
354 requisition->width += child_requisition.width;
356 max_height = hildon_picker_dialog_get_max_height (HILDON_PICKER_DIALOG (widget));
358 requisition->height = MIN (max_height,
359 requisition->height + optimal_requisition.height);
361 GTK_WIDGET_CLASS (hildon_picker_dialog_parent_class)->size_request
362 (widget, requisition);
366 hildon_picker_dialog_realize (GtkWidget *widget)
368 setup_interaction_mode (HILDON_PICKER_DIALOG (widget));
370 GTK_WIDGET_CLASS (hildon_picker_dialog_parent_class)->realize (widget);
373 /* ------------------------------ PRIVATE METHODS ---------------------------- */
376 hildon_picker_dialog_get_max_height (HildonPickerDialog *dialog)
378 gboolean landscape = TRUE;
380 GdkScreen *screen = NULL;
382 screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
383 if (screen != NULL) {
384 if (gdk_screen_get_width (screen) > gdk_screen_get_height (screen)) {
392 gtk_widget_style_get (GTK_WIDGET (dialog), "max-height-landscape",
395 gtk_widget_style_get (GTK_WIDGET (dialog), "max-height-portrait",
404 _select_on_selector_changed_cb (HildonTouchSelector * selector,
405 gint column, gpointer data)
407 g_return_if_fail (HILDON_IS_PICKER_DIALOG (data));
409 gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK);
413 selection_completed (HildonPickerDialog *dialog)
415 HildonPickerDialogPrivate *priv;
418 gboolean all_selected = TRUE;
419 HildonUIMode mode = HILDON_UI_MODE_NORMAL;
421 priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
423 mode = hildon_touch_selector_get_hildon_ui_mode (HILDON_TOUCH_SELECTOR (priv->selector));
424 if (mode == HILDON_UI_MODE_NORMAL) {
428 n_cols = hildon_touch_selector_get_num_columns (HILDON_TOUCH_SELECTOR (priv->selector));
429 for (i = 0; i < n_cols; i++) {
430 list = hildon_touch_selector_get_selected_rows (HILDON_TOUCH_SELECTOR (priv->selector), i);
432 all_selected = FALSE;
435 g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
443 _on_dialog_response (GtkDialog *dialog,
447 if (response_id == GTK_RESPONSE_OK) {
448 if (selection_completed (HILDON_PICKER_DIALOG (dialog)) == FALSE) {
449 g_signal_stop_emission_by_name (dialog, "response");
451 } else if (response_id == GTK_RESPONSE_DELETE_EVENT) {
452 _restore_current_selection (HILDON_PICKER_DIALOG (dialog));
457 on_selector_columns_changed (HildonTouchSelector * selector, gpointer userdata)
459 HildonPickerDialog * dialog;
461 dialog = HILDON_PICKER_DIALOG (userdata);
463 prepare_action_area (dialog);
464 if (GTK_WIDGET_REALIZED (dialog)) {
465 setup_interaction_mode (dialog);
470 * hildon_picker_dialog_set_done_label:
471 * @dialog: a #HildonPickerDialog
474 * Sets a custom string to be used as the 'Done' button label in @dialog.
479 hildon_picker_dialog_set_done_label (HildonPickerDialog * dialog,
482 HildonPickerDialogPrivate *priv;
484 g_return_if_fail (HILDON_IS_PICKER_DIALOG (dialog));
485 g_return_if_fail (label != NULL);
487 priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
489 gtk_button_set_label (GTK_BUTTON (priv->button), label);
493 * hildon_picker_dialog_get_done_label:
494 * @dialog: a #HildonPickerDialog
496 * Retrieves current 'Done' button label.
498 * Returns: the custom string to be used.
503 hildon_picker_dialog_get_done_label (HildonPickerDialog * dialog)
505 HildonPickerDialogPrivate *priv;
507 g_return_val_if_fail (HILDON_IS_PICKER_DIALOG (dialog), NULL);
509 priv = HILDON_PICKER_DIALOG_GET_PRIVATE (dialog);
511 return gtk_button_get_label (GTK_BUTTON (priv->button));
515 free_path_list (GList *list)
517 g_list_foreach (list, (GFunc) gtk_tree_path_free, NULL);
522 _clean_current_selection (HildonPickerDialog *dialog)
524 if (dialog->priv->current_selection) {
525 g_slist_foreach (dialog->priv->current_selection, (GFunc) free_path_list, NULL);
526 g_slist_free (dialog->priv->current_selection);
527 dialog->priv->current_selection = NULL;
529 if (dialog->priv->current_text) {
530 g_free (dialog->priv->current_text);
531 dialog->priv->current_text = NULL;
536 _save_current_selection (HildonPickerDialog *dialog)
538 HildonTouchSelector *selector;
541 selector = HILDON_TOUCH_SELECTOR (dialog->priv->selector);
543 _clean_current_selection (dialog);
545 columns = hildon_touch_selector_get_num_columns (selector);
546 for (i = 0; i < columns; i++) {
547 dialog->priv->current_selection
548 = g_slist_append (dialog->priv->current_selection,
549 hildon_touch_selector_get_selected_rows (selector, i));
551 if (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector)) {
552 HildonEntry *entry = hildon_touch_selector_entry_get_entry (HILDON_TOUCH_SELECTOR_ENTRY (selector));
553 dialog->priv->current_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
558 _restore_current_selection (HildonPickerDialog *dialog)
560 GSList *current_selection, *iter;
561 GList *selected, *selected_iter;
562 GtkTreePath *current_path;
563 HildonTouchSelector *selector;
565 GtkTreeIter tree_iter;
568 if (dialog->priv->current_selection == NULL)
571 current_selection = dialog->priv->current_selection;
572 selector = HILDON_TOUCH_SELECTOR (dialog->priv->selector);
574 if (hildon_touch_selector_get_num_columns (selector) !=
575 g_slist_length (current_selection)) {
576 /* We conclude that if the current selection has the same
577 numbers of columns that the selector, all this ok
578 Anyway this shouldn't happen. */
579 g_critical ("Trying to restore the selection on a selector after change"
580 " the number of columns. Are you removing columns while the"
585 if (dialog->priv->signal_changed_id)
586 g_signal_handler_block (selector, dialog->priv->signal_changed_id);
587 for (iter = current_selection, i = 0; iter; iter = g_slist_next (iter), i++) {
588 selected = (GList *) (iter->data);
589 model = hildon_touch_selector_get_model (selector, i);
590 hildon_touch_selector_unselect_all (selector, i);
591 for (selected_iter = selected; selected_iter; selected_iter = g_list_next (selected_iter)) {
592 current_path = (GtkTreePath *) selected_iter->data;
593 gtk_tree_model_get_iter (model, &tree_iter, current_path);
594 hildon_touch_selector_select_iter (selector, i, &tree_iter, FALSE);
597 if (HILDON_IS_TOUCH_SELECTOR_ENTRY (selector) && dialog->priv->current_text != NULL) {
598 HildonEntry *entry = hildon_touch_selector_entry_get_entry (HILDON_TOUCH_SELECTOR_ENTRY (selector));
599 gtk_entry_set_text (GTK_ENTRY (entry), dialog->priv->current_text);
601 if (dialog->priv->signal_changed_id)
602 g_signal_handler_unblock (selector, dialog->priv->signal_changed_id);
606 requires_done_button (HildonPickerDialog * dialog)
608 return hildon_touch_selector_has_multiple_selection
609 (HILDON_TOUCH_SELECTOR (dialog->priv->selector));
613 prepare_action_area (HildonPickerDialog *dialog)
615 if (requires_done_button (dialog)) {
616 gtk_widget_show (GTK_DIALOG (dialog)->action_area);
618 gtk_widget_hide (GTK_DIALOG (dialog)->action_area);
623 setup_interaction_mode (HildonPickerDialog * dialog)
625 if (dialog->priv->signal_changed_id) {
626 g_signal_handler_disconnect (dialog->priv->selector,
627 dialog->priv->signal_changed_id);
630 if (requires_done_button (dialog) == FALSE) {
631 dialog->priv->signal_changed_id =
632 g_signal_connect (G_OBJECT (dialog->priv->selector), "changed",
633 G_CALLBACK (_select_on_selector_changed_cb), dialog);
637 /*------------------------- PUBLIC METHODS ---------------------------- */
640 * hildon_picker_dialog_new:
641 * @parent: the parent window
643 * Creates a new #HildonPickerDialog
645 * Returns: a new #HildonPickerDialog
650 hildon_picker_dialog_new (GtkWindow * parent)
652 GtkDialog *dialog = NULL;
654 dialog = g_object_new (HILDON_TYPE_PICKER_DIALOG, NULL);
657 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
660 return GTK_WIDGET (dialog);
665 _hildon_picker_dialog_set_selector (HildonPickerDialog * dialog,
666 HildonTouchSelector * selector)
668 g_object_ref (selector);
670 /* Remove the old selector, if any */
671 if (dialog->priv->selector != NULL) {
672 gtk_container_remove (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
673 dialog->priv->selector);
674 if (dialog->priv->signal_columns_changed_id) {
675 g_signal_handler_disconnect (dialog->priv->selector,
676 dialog->priv->signal_columns_changed_id);
680 dialog->priv->selector = GTK_WIDGET (selector);
682 /* Pack the new selector */
683 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
684 dialog->priv->selector, TRUE, TRUE, 0);
686 g_object_unref (selector);
688 gtk_widget_show (dialog->priv->selector);
690 prepare_action_area (dialog);
691 if (GTK_WIDGET_REALIZED (dialog)) {
692 setup_interaction_mode (dialog);
695 dialog->priv->signal_columns_changed_id = g_signal_connect (G_OBJECT (dialog->priv->selector),
697 G_CALLBACK (on_selector_columns_changed), dialog);
702 * hildon_picker_dialog_set_selector:
703 * @dialog: a #HildonPickerDialog
704 * @selector: a #HildonTouchSelector
706 * Sets @selector as the #HildonTouchSelector to be shown in @dialog
708 * Returns: %TRUE if @selector was set, %FALSE otherwise
713 hildon_picker_dialog_set_selector (HildonPickerDialog * dialog,
714 HildonTouchSelector * selector)
716 g_return_val_if_fail (HILDON_IS_PICKER_DIALOG (dialog), FALSE);
717 g_return_val_if_fail (HILDON_IS_TOUCH_SELECTOR (selector), FALSE);
719 return HILDON_PICKER_DIALOG_GET_CLASS (dialog)->set_selector (dialog, selector);
723 * hildon_picker_dialog_get_selector:
724 * @dialog: a #HildonPickerDialog
726 * Retrieves the #HildonTouchSelector associated to @dialog.
728 * Returns: a #HildonTouchSelector
732 HildonTouchSelector *
733 hildon_picker_dialog_get_selector (HildonPickerDialog * dialog)
735 g_return_val_if_fail (HILDON_IS_PICKER_DIALOG (dialog), NULL);
737 return HILDON_TOUCH_SELECTOR (dialog->priv->selector);