2008-09-24 Claudio Saavedra <csaavedra@igalia.com>
[hildon] / src / hildon-sort-dialog.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /** 
26  * SECTION:hildon-sort-dialog
27  * @short_description: A widget for defining the sorting order of items.
28  * 
29  * HildonSortDialog is used to define an order (ascending/descending)
30  * and a field by which items are sorted in a list. The combo boxes
31  * display the current value when the dialog is opened.
32  *
33  * <example>
34  * <title>An example for using HildonSortDialog</title>
35  * <programlisting>
36  * HildonSortDialog *sort_dialog = HILDON_SORT_DIALOG (hildon_sort_dialog_new (parent));
37  * <!-- -->
38  * gint response_id, add_sort_index;
39  * <!-- -->
40  * sort_by[0] = STR_SORT_BY_DATE;
41  * sort_by[1] = STR_SORT_BY_NAME;
42  * sort_by[2] = STR_SORT_BY_SIZE;
43  * sort_by[3] = NULL;
44  * <!-- -->
45  * sorting_order[0] = STR_SORTING_ORDER_ASCENDING;
46  * sorting_order[1] = STR_SORTING_ORDER_DESCENDING;
47  * sorting_order[2] = NULL;
48  * <!-- -->
49  * add_sort_index = hildon_sort_dialog_add_sort_key (sort_dialog, STR_SORT_BY_DATE);
50  * <!-- -->
51  * hildon_sort_dialog_add_sort_key (sort_dialog, STR_SORT_BY_NAME);
52  * <!-- -->
53  * hildon_sort_dialog_add_sort_key (sort_dialog, STR_SORT_BY_SIZE);
54  * <!-- -->
55  * if (dialog.first_time_clicked == TRUE)
56  * {
57  *      hildon_sort_dialog_set_sort_key (sort_dialog, add_sort_index);
58  * }
59  * <!-- -->
60  * if (dialog.first_time_clicked == FALSE)
61  * {
62  *      hildon_sort_dialog_set_sort_key (sort_dialog, dialog.sort_key);
63  *      hildon_sort_dialog_set_sort_order (sort_dialog, dialog.sort_order);
64  * }
65  * <!-- -->
66  * gtk_widget_show (GTK_WIDGET (sort_dialog));
67  * <!-- -->
68  * response_id = gtk_dialog_run (GTK_DIALOG (sort_dialog));
69  * <!-- -->
70  * if (response_id == GTK_RESPONSE_OK)
71  * {
72  *      dialog.sort_key = hildon_sort_dialog_get_sort_key (sort_dialog);
73  * <!-- -->
74  *      gtk_label_set_text (GTK_LABEL (dialog.label1), sort_by [dialog.sort_key]);
75  * <!-- -->
76  *      dialog.sort_order = hildon_sort_dialog_get_sort_order (sort_dialog);
77  * <!-- -->
78  *      gtk_label_set_text (GTK_LABEL (dialog.label2), sorting_order [dialog.sort_order]);
79  * <!-- -->
80  *      dialog.first_time_clicked = FALSE;
81  * }
82  * </programlisting>
83  * </example>
84  */
85
86 #ifdef                                          HAVE_CONFIG_H
87 #include                                        <config.h>
88 #endif
89
90 #include                                        <stdio.h>
91 #include                                        <libintl.h>
92
93 #include                                        "hildon-sort-dialog.h"
94 #include                                        "hildon-caption.h"
95 #include                                        "hildon-sort-dialog-private.h"
96
97 #define                                         _(String) \
98                                                 dgettext("hildon-libs", String)
99
100 static GtkDialogClass*                          parent_class;
101
102 static void 
103 hildon_sort_dialog_class_init                   (HildonSortDialogClass *class);
104
105 static void
106 hildon_sort_dialog_init                         (HildonSortDialog *widget);
107
108 static void 
109 hildon_sort_dialog_set_property                 (GObject * object,
110                                                  guint prop_id,
111                                                  const GValue *value,
112                                                  GParamSpec * pspec);
113
114 static void 
115 hildon_sort_dialog_get_property                 (GObject *object,
116                                                  guint prop_id,
117                                                  GValue * value, 
118                                                  GParamSpec * pspec);
119
120 static void 
121 reconstruct_combo                               (HildonSortDialog *dialog, 
122                                                  gboolean remove, 
123                                                  gboolean reversed);
124
125 static void
126 sort_key_changed                                (GtkWidget *widget, 
127                                                  HildonSortDialog *dialog);
128
129 static void 
130 hildon_sort_dialog_finalize                     (GObject *object);
131
132 static gint 
133 hildon_sort_dialog_add_sort_key_with_sorting    (HildonSortDialog *dialog, 
134                                                  const gchar *sort_key, 
135                                                  gboolean sorting);
136
137 enum 
138 {
139     PROP_0,
140     PROP_SORT_KEY,
141     PROP_SORT_ORDER
142 };
143
144 static void 
145 sort_key_changed                                (GtkWidget *widget, 
146                                                  HildonSortDialog *dialog)
147 {
148     g_return_if_fail (HILDON_IS_SORT_DIALOG (dialog));
149
150     HildonSortDialogPrivate *priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
151     g_assert (priv);
152
153     gint index = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
154
155     if (priv->key_reversed [index] != priv->reversed) {
156         reconstruct_combo (dialog, TRUE, priv->key_reversed [index]);
157         gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_order), 0);
158     }
159
160     priv->reversed = priv->key_reversed [index];
161 }
162
163 /* Initialises the sort dialog class. */
164 static void
165 hildon_sort_dialog_class_init                   (HildonSortDialogClass *class)
166 {
167     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
168     parent_class = g_type_class_peek_parent (class);
169     g_type_class_add_private (class, sizeof (HildonSortDialogPrivate));
170     
171     gobject_class->set_property = hildon_sort_dialog_set_property;
172     gobject_class->get_property = hildon_sort_dialog_get_property;
173     gobject_class->finalize     = (gpointer) hildon_sort_dialog_finalize;
174
175     /**
176      * HildonSortDialog:sort-key:
177      *
178      * The currently active sort key.
179      */
180     g_object_class_install_property (gobject_class, PROP_SORT_KEY,
181         g_param_spec_int ("sort-key",
182                           "Sort Key",
183                           "The currently active sort key",
184                           G_MININT,
185                           G_MAXINT,
186                           0, G_PARAM_READWRITE));
187    
188     /**
189      * HildonSortDialog:sort-order:
190      *
191      * The sort order for the currently active sort key.
192      */
193     g_object_class_install_property (gobject_class, PROP_SORT_ORDER,
194         g_param_spec_enum ("sort-order",
195                           "Sort Order",
196                           "The current sorting order",
197                           GTK_TYPE_SORT_TYPE,
198                           GTK_SORT_ASCENDING,
199                           G_PARAM_READWRITE));
200 }
201
202 static gint 
203 hildon_sort_dialog_add_sort_key_with_sorting    (HildonSortDialog *dialog, 
204                                                  const gchar *sort_key, 
205                                                  gboolean sorting)
206 {
207     HildonSortDialogPrivate *priv;
208
209     g_return_val_if_fail (HILDON_IS_SORT_DIALOG (dialog), -1);
210
211     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
212     g_assert (priv);
213
214     gboolean *new_array = g_malloc (sizeof (gboolean) * (priv->index_counter + 1));
215
216     /* Rewrite the old values */
217     int i = 0;
218     for (i = 0; i < priv->index_counter; i++) 
219         new_array [i] = priv->key_reversed [i];
220
221     new_array [priv->index_counter] = sorting;
222     gtk_combo_box_append_text (GTK_COMBO_BOX (priv->combo_key), sort_key);
223
224     /* Free the old one and reassign */
225     if (priv->key_reversed != NULL)
226         g_free (priv->key_reversed);
227     priv->key_reversed = new_array;
228
229     return priv->index_counter++;
230 }
231
232 static void 
233 reconstruct_combo                               (HildonSortDialog *dialog, 
234                                                  gboolean remove, 
235                                                  gboolean reversed)
236 {
237     HildonSortDialogPrivate *priv;
238     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
239     g_assert (priv);
240
241     if (remove) {
242         gtk_combo_box_remove_text (GTK_COMBO_BOX (priv->combo_order), 1);
243         gtk_combo_box_remove_text (GTK_COMBO_BOX (priv->combo_order), 0);
244     }
245
246     if (reversed) {
247         gtk_combo_box_append_text (GTK_COMBO_BOX (priv->combo_order), _("ckdg_va_sort_descending"));
248         gtk_combo_box_append_text (GTK_COMBO_BOX (priv->combo_order), _("ckdg_va_sort_ascending"));
249     } else  {
250         gtk_combo_box_append_text (GTK_COMBO_BOX (priv->combo_order), _("ckdg_va_sort_ascending"));
251         gtk_combo_box_append_text (GTK_COMBO_BOX (priv->combo_order), _("ckdg_va_sort_descending"));
252     }
253 }
254
255 static void
256 hildon_sort_dialog_init                         (HildonSortDialog * dialog)
257 {
258     HildonSortDialogPrivate *priv;
259     GtkSizeGroup *group;
260
261     g_assert(HILDON_IS_SORT_DIALOG (dialog));
262
263     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
264     g_assert (priv);
265
266     priv->index_counter = 0;
267     priv->reversed = FALSE;
268     priv->key_reversed = NULL;
269
270     group = GTK_SIZE_GROUP (gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL));
271
272     gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
273     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
274     gtk_window_set_title (GTK_WINDOW (dialog), _("ckdg_ti_sort"));
275
276     /* Tab one */
277     priv->combo_key = gtk_combo_box_new_text ();
278     priv->caption_key = hildon_caption_new(group, _("ckdg_fi_sort_field"), priv->combo_key,
279             NULL, HILDON_CAPTION_OPTIONAL);
280     hildon_caption_set_separator(HILDON_CAPTION (priv->caption_key), "");
281     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
282             priv->caption_key, FALSE, FALSE, 0);
283
284     /* Tab two */
285     priv->combo_order = gtk_combo_box_new_text ();
286     reconstruct_combo (dialog, FALSE, FALSE);
287
288     priv->caption_order = hildon_caption_new (group, _("ckdg_fi_sort_order"),
289             priv->combo_order,
290             NULL, HILDON_CAPTION_OPTIONAL);
291     hildon_caption_set_separator(HILDON_CAPTION(priv->caption_order), "");
292     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
293             priv->caption_order, FALSE, FALSE, 0);
294
295     gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_key), 0);
296     gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo_order), 0);
297     g_signal_connect (G_OBJECT (priv->combo_key), "changed", (gpointer) sort_key_changed, dialog);
298
299     /* Create the OK/CANCEL buttons */
300     (void) gtk_dialog_add_button (GTK_DIALOG(dialog),
301             _("ckdg_bd_sort_dialog_ok"),
302             GTK_RESPONSE_OK);
303     (void) gtk_dialog_add_button (GTK_DIALOG(dialog),
304             _("ckdg_bd_sort_dialog_cancel"),
305             GTK_RESPONSE_CANCEL);
306     /* FIXME: Hardcoded sizes are bad */
307     gtk_window_resize (GTK_WINDOW (dialog), 370, 100);
308     gtk_widget_show_all (GTK_DIALOG (dialog)->vbox);
309
310     g_object_unref (group); /* Captions now own their references to sizegroup */
311 }
312
313 /**
314  * hildon_sort_dialog_get_type:
315  *
316  * Returns GType for HildonSortDialog as produced by 
317  * g_type_register_static().
318  *
319  * Returns: HildonSortDialog type
320  */
321 GType G_GNUC_CONST
322 hildon_sort_dialog_get_type                     (void)
323 {
324     static GType dialog_type = 0;
325
326     if (!dialog_type) {
327         static const GTypeInfo dialog_info = {
328             sizeof (HildonSortDialogClass),
329             NULL,       /* base_init */
330             NULL,       /* base_finalize */
331             (GClassInitFunc) hildon_sort_dialog_class_init,
332             NULL,       /* class_finalize */
333             NULL,       /* class_data */
334             sizeof (HildonSortDialog),
335             0,  /* n_preallocs */
336             (GInstanceInitFunc) hildon_sort_dialog_init
337         };
338
339         dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
340                 "HildonSortDialog",
341                 &dialog_info, 0);
342     }
343     return dialog_type;
344 }
345
346 /**
347  * hildon_sort_dialog_new:
348  * @parent: widget to be transient for, or NULL if none
349  *
350  * HildonSortDialog contains two HildonCaptions with combo boxes. 
351  *
352  * Returns: pointer to a new @HildonSortDialog widget
353  */
354 GtkWidget*
355 hildon_sort_dialog_new                          (GtkWindow * parent)
356 {
357     GtkWidget *sort_dialog = g_object_new (HILDON_TYPE_SORT_DIALOG, NULL);
358
359     if (parent)
360         gtk_window_set_transient_for (GTK_WINDOW (sort_dialog), parent);
361
362     return sort_dialog;
363 }
364
365 /**
366  * hildon_sort_dialog_get_sort_key:
367  * @dialog: the #HildonSortDialog widget
368  *
369  * Gets index to currently active sort key.
370  * 
371  * Returns: an integer which is the index value of the "Sort by" 
372  * field 
373  */
374 gint
375 hildon_sort_dialog_get_sort_key                 (HildonSortDialog *dialog)
376 {
377     GtkWidget *combo_key;
378     HildonSortDialogPrivate *priv;
379
380     g_return_val_if_fail (HILDON_IS_SORT_DIALOG (dialog), -1);
381
382     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
383     g_assert (priv);
384
385     combo_key = gtk_bin_get_child (GTK_BIN (priv->caption_key));
386
387     return gtk_combo_box_get_active (GTK_COMBO_BOX (combo_key));
388 }
389
390 /**
391  * hildon_sort_dialog_get_sort_order:
392  * @dialog: the #HildonSortDialog widget
393  *
394  * Gets current sorting order from "Sort order" field.
395  *
396  * Returns: current sorting order as #GtkSortType
397  */
398 GtkSortType 
399 hildon_sort_dialog_get_sort_order               (HildonSortDialog *dialog)
400 {
401     GtkWidget *combo_order;
402     HildonSortDialogPrivate *priv;
403
404     g_return_val_if_fail (HILDON_IS_SORT_DIALOG (dialog), 0);
405
406     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
407     g_assert (priv);
408
409     combo_order = gtk_bin_get_child (GTK_BIN (priv->caption_order));
410
411     gint sort_order = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_order));
412
413     if (priv->reversed)
414         return (sort_order == 0) ? 1 : 0;
415     else
416         return sort_order;
417 }
418
419 /**
420  * hildon_sort_dialog_set_sort_key:
421  * @dialog: the #HildonSortDialog widget
422  * @key: combo box's index value
423  *
424  * Sets the index value of the #HildonSortDialog widget.
425  */
426 void
427 hildon_sort_dialog_set_sort_key                 (HildonSortDialog * dialog, 
428                                                  gint key)
429 {
430     GtkWidget *combo_key;
431     HildonSortDialogPrivate *priv;
432
433     g_return_if_fail (HILDON_IS_SORT_DIALOG (dialog));
434
435     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
436     g_assert (priv);
437
438     combo_key = gtk_bin_get_child (GTK_BIN (priv->caption_key));
439     gtk_combo_box_set_active (GTK_COMBO_BOX (combo_key), key);
440
441     g_object_notify (G_OBJECT (dialog), "sort-key");
442 }
443
444 /**
445  * hildon_sort_dialog_set_sort_order:
446  * @dialog: the #HildonSortDialog widget
447  * @order: combo box's index value
448  *
449  * Sets the index value of the #HildonSortDialog widget.
450  */
451 void
452 hildon_sort_dialog_set_sort_order               (HildonSortDialog *dialog,
453                                                  GtkSortType order)
454 {
455     GtkWidget *combo_order;
456     HildonSortDialogPrivate *priv;
457
458     g_return_if_fail (HILDON_IS_SORT_DIALOG (dialog));
459
460     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
461     g_assert (priv);
462
463     combo_order = gtk_bin_get_child (GTK_BIN (priv->caption_order));
464
465     if (priv->reversed) 
466         order = (order == 0) ? 1 : 0;
467
468     gtk_combo_box_set_active (GTK_COMBO_BOX (combo_order), order);
469
470     g_object_notify (G_OBJECT (dialog), "sort-order");
471 }
472
473 /**
474  * hildon_sort_dialog_add_sort_key:
475  * @dialog: the #HildonSortDialog widget
476  * @sort_key: combo box's index value
477  *
478  * Adds a new sort key and returns the respective index in
479  * sort key combobox.
480  *
481  * Returns: an integer which is the index of the added combo box's
482  * item
483  */
484 gint
485 hildon_sort_dialog_add_sort_key                 (HildonSortDialog *dialog,
486                                                  const gchar *sort_key)
487 {
488     return hildon_sort_dialog_add_sort_key_with_sorting (dialog, sort_key, FALSE);
489 }
490
491 /**
492  * hildon_sort_dialog_add_sort_key_reversed:
493  * @dialog: the #HildonSortDialog widget
494  * @sort_key: combo box's index value
495  *
496  * Adds a new sort key and returns the respective index in
497  * sort key combobox. The default sort order for this key is reversed (Descending first).
498  *
499  * Returns: an integer which is the index of the added combo box's
500  * item
501  *
502  */
503 gint
504 hildon_sort_dialog_add_sort_key_reversed        (HildonSortDialog *dialog,
505                                                  const gchar *sort_key)
506 {
507     return hildon_sort_dialog_add_sort_key_with_sorting (dialog, sort_key, TRUE);
508 }
509
510 static void
511 hildon_sort_dialog_set_property                 (GObject *object,
512                                                  guint prop_id,
513                                                  const GValue *value, 
514                                                  GParamSpec *pspec)
515 {
516     HildonSortDialog *dialog;
517
518     dialog = HILDON_SORT_DIALOG(object);
519
520     switch (prop_id) {
521
522         case PROP_SORT_KEY:
523             hildon_sort_dialog_set_sort_key (dialog, g_value_get_int (value));
524             break;
525
526         case PROP_SORT_ORDER:
527             hildon_sort_dialog_set_sort_order (dialog, g_value_get_enum (value));
528             break;
529
530         default:
531             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
532             break;
533     }
534 }
535
536 static void
537 hildon_sort_dialog_get_property                 (GObject *object,
538                                                  guint prop_id, 
539                                                  GValue *value, 
540                                                  GParamSpec *pspec)
541 {
542     HildonSortDialog *dialog;
543
544     dialog = HILDON_SORT_DIALOG (object);
545
546     switch (prop_id) {
547
548         case PROP_SORT_KEY:
549             g_value_set_int (value, hildon_sort_dialog_get_sort_key (dialog));
550             break;
551
552         case PROP_SORT_ORDER:
553             g_value_set_enum (value, hildon_sort_dialog_get_sort_order (dialog));
554             break;
555
556         default:
557             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
558             break;
559
560     }
561 }
562
563 static void 
564 hildon_sort_dialog_finalize                     (GObject *object)
565 {
566     HildonSortDialogPrivate *priv;
567     HildonSortDialog *dialog;
568
569     dialog = HILDON_SORT_DIALOG (object);
570     priv = HILDON_SORT_DIALOG_GET_PRIVATE (dialog);
571     g_assert (priv);
572
573     if (priv != NULL && priv->key_reversed != NULL)
574         g_free (priv->key_reversed);
575
576     if (G_OBJECT_CLASS (parent_class)->finalize)
577         G_OBJECT_CLASS (parent_class)->finalize(object);
578 }
579
580