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