2006-09-12 Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
[hildon] / hildon-widgets / hildon-sort-dialog.c
1 /*
2  * This file is part of hildon-libs
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 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
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include <stdio.h>
39 #include <libintl.h>
40 #include <gtk/gtkcombobox.h>
41 #include <gtk/gtkbox.h>
42 #include <hildon-widgets/hildon-caption.h>
43 #include "hildon-sort-dialog.h"
44
45
46 #define _(String) dgettext(PACKAGE, String)
47
48 static GtkDialogClass *parent_class;
49
50 #define HILDON_SORT_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE \
51                 ((obj), HILDON_TYPE_SORT_DIALOG, HildonSortDialogPrivate));
52
53 typedef struct _HildonSortDialogPrivate HildonSortDialogPrivate;
54
55 static void hildon_sort_dialog_class_init(HildonSortDialogClass * class);
56 static void hildon_sort_dialog_init(HildonSortDialog * widget);
57 static void hildon_sort_dialog_set_property(GObject * object,
58                                      guint prop_id,
59                                      const GValue * value,
60                                      GParamSpec * pspec);
61 static void hildon_sort_dialog_get_property(GObject * object,
62                                      guint prop_id,
63                                      GValue * value, GParamSpec * pspec);
64 static void reconstruct_combo (HildonSortDialog * dialog, 
65                                      gboolean remove, 
66                                      gboolean reversed);
67 static gint hildon_sort_dialog_add_sort_key_with_sorting(HildonSortDialog * dialog, 
68                                      const gchar * sort_key, 
69                                      gboolean sorting);
70 static void sort_key_changed(GtkWidget * widget, 
71                              HildonSortDialog * dialog);
72 static void hildon_sort_dialog_finalize(GObject * object);
73
74 enum {
75     PROP_0,
76     PROP_SORT_KEY,
77     PROP_SORT_ORDER
78 };
79
80 /* private data */
81 struct _HildonSortDialogPrivate {
82     /* Sort category widgets */
83     GtkWidget *combo_key;
84     GtkWidget *caption_key;
85
86     /* Sort order widgets */
87     GtkWidget *combo_order;
88     GtkWidget *caption_order;
89
90     /* Index value counter */
91     gint index_counter;
92
93     /* If the current order displayed is reversed */
94     gboolean reversed;
95
96     /* An array for each key representing if a key should be reverse-sorted */
97     gboolean *key_reversed;
98 };
99
100 /* Private functions */
101
102 static void sort_key_changed(GtkWidget * widget, HildonSortDialog * dialog)
103 {
104     g_return_if_fail(HILDON_IS_SORT_DIALOG(dialog));
105
106     HildonSortDialogPrivate *priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
107
108     gint index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
109
110     if (priv->key_reversed [index] != priv->reversed) {
111         reconstruct_combo (dialog, TRUE, priv->key_reversed [index]);
112         gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combo_order), 0);
113     }
114
115     priv->reversed = priv->key_reversed [index];
116 }
117
118 /*
119  * Initialises the sort dialog class.
120  */
121 static void hildon_sort_dialog_class_init(HildonSortDialogClass * class)
122 {
123     GObjectClass *gobject_class = G_OBJECT_CLASS(class);
124     parent_class = g_type_class_peek_parent(class);
125     g_type_class_add_private(class, sizeof(HildonSortDialogPrivate));
126     
127     gobject_class->set_property = hildon_sort_dialog_set_property;
128     gobject_class->get_property = hildon_sort_dialog_get_property;
129     gobject_class->finalize = (gpointer) hildon_sort_dialog_finalize;
130     
131     g_object_class_install_property(gobject_class, PROP_SORT_KEY,
132         g_param_spec_int("sort-key",
133                          "Sort Key",
134                          "The currently active sort key",
135                          G_MININT,
136                          G_MAXINT,
137                          0, G_PARAM_READWRITE));
138     
139     g_object_class_install_property(gobject_class, PROP_SORT_ORDER,
140         g_param_spec_enum("sort-order",
141                          "Sort Order",
142                          "The current sorting order",
143                          GTK_TYPE_SORT_TYPE,
144                          GTK_SORT_ASCENDING,
145                          G_PARAM_READWRITE));
146 }
147
148 static gint hildon_sort_dialog_add_sort_key_with_sorting(HildonSortDialog * dialog, const gchar * sort_key, gboolean sorting)
149 {
150     HildonSortDialogPrivate *priv;
151
152     g_return_val_if_fail(HILDON_IS_SORT_DIALOG(dialog), -1);
153
154     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
155     gboolean *new_array = g_malloc (sizeof (gboolean) * (priv->index_counter + 1));
156
157     /* Rewrite the old values */
158     int i = 0;
159     for (i = 0; i < priv->index_counter; i++) 
160         new_array [i] = priv->key_reversed [i];
161
162     new_array [priv->index_counter] = sorting;
163     gtk_combo_box_append_text(GTK_COMBO_BOX(priv->combo_key), sort_key);
164
165     /* Free the old one and reassign */
166     if (priv->key_reversed != NULL)
167         g_free (priv->key_reversed);
168     priv->key_reversed = new_array;
169
170     return priv->index_counter++;
171 }
172
173 static void reconstruct_combo (HildonSortDialog * dialog, gboolean remove, gboolean reversed)
174 {
175     HildonSortDialogPrivate *priv;
176     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
177
178     if (remove) {
179         gtk_combo_box_remove_text(GTK_COMBO_BOX(priv->combo_order), 1);
180         gtk_combo_box_remove_text(GTK_COMBO_BOX(priv->combo_order), 0);
181     }
182
183     if (reversed) {
184         gtk_combo_box_append_text(GTK_COMBO_BOX(priv->combo_order), _("ckdg_va_sort_descending"));
185         gtk_combo_box_append_text(GTK_COMBO_BOX(priv->combo_order), _("ckdg_va_sort_ascending"));
186     } else {
187         gtk_combo_box_append_text(GTK_COMBO_BOX(priv->combo_order), _("ckdg_va_sort_ascending"));
188         gtk_combo_box_append_text(GTK_COMBO_BOX(priv->combo_order), _("ckdg_va_sort_descending"));
189     }
190 }
191
192 static void hildon_sort_dialog_init(HildonSortDialog * dialog)
193 {
194     HildonSortDialogPrivate *priv;
195     GtkSizeGroup *group;
196
197     g_assert(HILDON_IS_SORT_DIALOG(dialog));
198
199     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
200
201     priv->index_counter = 0;
202     priv->reversed = FALSE;
203     priv->key_reversed = NULL;
204
205     group = GTK_SIZE_GROUP(gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL));
206
207     gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
208     gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
209     gtk_window_set_title(GTK_WINDOW(dialog), _("ckdg_ti_sort"));
210
211     /* Tab one */
212     priv->combo_key = gtk_combo_box_new_text();
213     priv->caption_key = hildon_caption_new(group, _("ckdg_fi_sort_field"), priv->combo_key,
214                                         NULL, HILDON_CAPTION_OPTIONAL);
215     hildon_caption_set_separator(HILDON_CAPTION(priv->caption_key), "");
216     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
217                        priv->caption_key, FALSE, FALSE, 0);
218
219     /* Tab two */
220     priv->combo_order = gtk_combo_box_new_text();
221     reconstruct_combo (dialog, FALSE, FALSE);
222     
223     priv->caption_order = hildon_caption_new(group, _("ckdg_fi_sort_order"),
224                                         priv->combo_order,
225                                         NULL, HILDON_CAPTION_OPTIONAL);
226     hildon_caption_set_separator(HILDON_CAPTION(priv->caption_order), "");
227     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
228                        priv->caption_order, FALSE, FALSE, 0);
229
230     gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combo_key), 0);
231     gtk_combo_box_set_active(GTK_COMBO_BOX(priv->combo_order), 0);
232     g_signal_connect (G_OBJECT (priv->combo_key), "changed", (gpointer) sort_key_changed, dialog);
233
234     /* Create the OK/CANCEL buttons */
235     (void) gtk_dialog_add_button(GTK_DIALOG(dialog),
236                                            _("ckdg_bd_sort_dialog_ok"),
237                                            GTK_RESPONSE_OK);
238     (void) gtk_dialog_add_button(GTK_DIALOG(dialog),
239                                                _("ckdg_bd_sort_dialog_cancel"),
240                                                GTK_RESPONSE_CANCEL);
241     /* FIXME: Hardcoded sizes are bad */
242     gtk_window_resize(GTK_WINDOW(dialog), 370, 100);
243     gtk_widget_show_all(GTK_DIALOG(dialog)->vbox);
244
245     g_object_unref(group); /* Captions now own their references to sizegroup */
246 }
247
248 /* Public functions */
249
250 /**
251  * hildon_sort_dialog_get_type:
252  *
253  * Returns GType for HildonSortDialog as produced by 
254  * g_type_register_static().
255  *
256  * Returns: HildonSortDialog type
257  */
258 GType hildon_sort_dialog_get_type()
259 {
260     static GType dialog_type = 0;
261
262     if (!dialog_type) {
263         static const GTypeInfo dialog_info = {
264             sizeof(HildonSortDialogClass),
265             NULL,       /* base_init */
266             NULL,       /* base_finalize */
267             (GClassInitFunc) hildon_sort_dialog_class_init,
268             NULL,       /* class_finalize */
269             NULL,       /* class_data */
270             sizeof(HildonSortDialog),
271             0,  /* n_preallocs */
272             (GInstanceInitFunc) hildon_sort_dialog_init
273         };
274
275         dialog_type = g_type_register_static(GTK_TYPE_DIALOG,
276                                              "HildonSortDialog",
277                                              &dialog_info, 0);
278     }
279     return dialog_type;
280 }
281
282 /**
283  * hildon_sort_dialog_new:
284  * @parent: widget to be transient for, or NULL if none
285  *
286  * HildonSortDialog contains two HildonCaptions with combo boxes. 
287  *
288  * Returns: pointer to a new @HildonSortDialog widget
289  */
290 GtkWidget *hildon_sort_dialog_new(GtkWindow * parent)
291 {
292     GtkWidget *sort_dialog = g_object_new(HILDON_TYPE_SORT_DIALOG, NULL);
293
294     if (parent)
295         gtk_window_set_transient_for(GTK_WINDOW(sort_dialog), parent);
296
297     return sort_dialog;
298 }
299
300 /**
301  * hildon_sort_dialog_get_sort_key:
302  * @dialog: the #HildonSortDialog widget
303  *
304  * Gets index to currently active sort key.
305  * 
306  * Returns: an integer which is the index value of the "Sort by" 
307  * field 
308  */
309 gint hildon_sort_dialog_get_sort_key(HildonSortDialog * dialog)
310 {
311     GtkWidget *combo_key;
312     HildonSortDialogPrivate *priv;
313
314     g_return_val_if_fail(HILDON_IS_SORT_DIALOG(dialog), -1);
315
316     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
317     
318     combo_key = gtk_bin_get_child(GTK_BIN(priv->caption_key));
319
320     return gtk_combo_box_get_active(GTK_COMBO_BOX(combo_key));
321 }
322
323 /**
324  * hildon_sort_dialog_get_sort_order:
325  * @dialog: the #HildonSortDialog widget
326  *
327  * Gets current sorting order from "Sort order" field.
328  *
329  * Returns: current sorting order as #GtkSortType
330  */
331 GtkSortType hildon_sort_dialog_get_sort_order(HildonSortDialog * dialog)
332 {
333     GtkWidget *combo_order;
334     HildonSortDialogPrivate *priv;
335
336     g_return_val_if_fail(HILDON_IS_SORT_DIALOG(dialog), 0);
337
338     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
339     combo_order = gtk_bin_get_child(GTK_BIN(priv->caption_order));
340
341     gint sort_order = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_order));
342
343     if (priv->reversed)
344         return (sort_order == 0) ? 1 : 0;
345     else
346         return sort_order;
347 }
348
349 /**
350  * hildon_sort_dialog_set_sort_key:
351  * @dialog: the #HildonSortDialog widget
352  * @key: combo box's index value
353  *
354  * Sets the index value of the #HildonSortDialog widget.
355  */
356 void hildon_sort_dialog_set_sort_key(HildonSortDialog * dialog, gint key)
357 {
358     GtkWidget *combo_key;
359     HildonSortDialogPrivate *priv;
360
361     g_return_if_fail(HILDON_IS_SORT_DIALOG(dialog));
362
363     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
364     combo_key = gtk_bin_get_child(GTK_BIN(priv->caption_key));
365     gtk_combo_box_set_active(GTK_COMBO_BOX(combo_key), key);
366
367     g_object_notify (G_OBJECT (dialog), "sort-key");
368 }
369
370 /**
371  * hildon_sort_dialog_set_sort_order:
372  * @dialog: the #HildonSortDialog widget
373  * @order: combo box's index value
374  *
375  * Sets the index value of the #HildonSortDialog widget.
376  */
377 void
378 hildon_sort_dialog_set_sort_order(HildonSortDialog * dialog,
379                                   GtkSortType order)
380 {
381     GtkWidget *combo_order;
382     HildonSortDialogPrivate *priv;
383
384     g_return_if_fail(HILDON_IS_SORT_DIALOG(dialog));
385
386     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
387     combo_order = gtk_bin_get_child(GTK_BIN(priv->caption_order));
388     
389     if (priv->reversed) 
390         order = (order == 0) ? 1 : 0;
391     
392     gtk_combo_box_set_active(GTK_COMBO_BOX(combo_order), order);
393
394     g_object_notify (G_OBJECT (dialog), "sort-order");
395 }
396
397 /**
398  * hildon_sort_dialog_add_sort_key:
399  * @dialog: the #HildonSortDialog widget
400  * @sort_key: combo box's index value
401  *
402  * Adds a new sort key and returns the respective index in
403  * sort key combobox.
404  *
405  * Returns: an integer which is the index of the added combo box's
406  * item
407  */
408 gint
409 hildon_sort_dialog_add_sort_key(HildonSortDialog * dialog,
410                                 const gchar * sort_key)
411 {
412     return hildon_sort_dialog_add_sort_key_with_sorting (dialog, sort_key, FALSE);
413 }
414
415 /**
416  * hildon_sort_dialog_add_sort_key_reversed:
417  * @dialog: the #HildonSortDialog widget
418  * @sort_key: combo box's index value
419  *
420  * Adds a new sort key and returns the respective index in
421  * sort key combobox. The default sort order for this key is reversed (Descending first).
422  *
423  * Returns: an integer which is the index of the added combo box's
424  * item
425  *
426  * Since: 0.14.1
427  */
428 gint
429 hildon_sort_dialog_add_sort_key_reversed(HildonSortDialog * dialog,
430                                 const gchar * sort_key)
431 {
432     return hildon_sort_dialog_add_sort_key_with_sorting (dialog, sort_key, TRUE);
433 }
434
435 static void
436 hildon_sort_dialog_set_property(GObject * object,
437                          guint prop_id,
438                          const GValue * value, GParamSpec * pspec)
439 {
440     HildonSortDialog *dialog;
441
442     dialog = HILDON_SORT_DIALOG(object);
443
444     switch (prop_id) {
445     case PROP_SORT_KEY:
446         hildon_sort_dialog_set_sort_key(dialog, g_value_get_int(value));
447         break;
448     case PROP_SORT_ORDER:
449         hildon_sort_dialog_set_sort_order(dialog, g_value_get_enum(value));
450         break;
451     default:
452         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
453         break;
454     }
455 }
456
457 static void
458 hildon_sort_dialog_get_property(GObject * object,
459                          guint prop_id, GValue * value, GParamSpec * pspec)
460 {
461     HildonSortDialog *dialog;
462
463     dialog = HILDON_SORT_DIALOG(object);
464
465     switch (prop_id) {
466     case PROP_SORT_KEY:
467         g_value_set_int(value, hildon_sort_dialog_get_sort_key(dialog));
468         break;
469     case PROP_SORT_ORDER:
470         g_value_set_enum(value, hildon_sort_dialog_get_sort_order(dialog));
471         break;
472     default:
473         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
474         break;
475     }
476 }
477
478 static void 
479 hildon_sort_dialog_finalize(GObject * object)
480 {
481     HildonSortDialogPrivate *priv;
482     HildonSortDialog *dialog;
483
484     g_return_if_fail (HILDON_IS_SORT_DIALOG (object));
485     dialog = HILDON_SORT_DIALOG(object);
486
487     priv = HILDON_SORT_DIALOG_GET_PRIVATE(dialog);
488     if (priv != NULL && priv->key_reversed != NULL)
489         g_free(priv->key_reversed);
490
491     if (G_OBJECT_CLASS(parent_class)->finalize)
492         G_OBJECT_CLASS(parent_class)->finalize(object);
493 }
494
495