source: proiecte/PPPP/gdm/gui/simple-greeter/gdm-option-widget.c @ 134

Last change on this file since 134 was 134, checked in by (none), 14 years ago

gdm sources with the modifications for webcam

File size: 39.8 KB
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2008 Red Hat, Inc.
4 * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 *  Written by: Ray Strode <rstrode@redhat.com>
21 *              William Jon McCann <mccann@jhu.edu>
22 */
23
24#include "config.h"
25
26#include <stdlib.h>
27#include <stdio.h>
28#include <unistd.h>
29#include <string.h>
30#include <errno.h>
31#include <dirent.h>
32#include <sys/stat.h>
33
34#include <glib.h>
35#include <glib/gi18n.h>
36#include <glib/gstdio.h>
37#include <gtk/gtk.h>
38
39#include "gdm-option-widget.h"
40
41#define GDM_OPTION_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_OPTION_WIDGET, GdmOptionWidgetPrivate))
42
43#define GDM_OPTION_WIDGET_RC_STRING \
44"style \"gdm-option-widget-style\"" \
45"{" \
46"  GtkComboBox::appears-as-list = 1" \
47"}" \
48"widget_class \"*<GdmOptionWidget>.*.GtkComboBox\" style \"gdm-option-widget-style\""
49
50struct GdmOptionWidgetPrivate
51{
52        GtkWidget                *label;
53        GtkWidget                *image;
54        char                     *label_text;
55        char                     *icon_name;
56        char                     *default_item_id;
57
58        GtkWidget                *items_combo_box;
59        GtkListStore             *list_store;
60
61        GtkTreeModelFilter       *model_filter;
62        GtkTreeModelSort         *model_sorter;
63
64        GtkTreeRowReference      *active_row;
65        GtkTreeRowReference      *top_separator_row;
66        GtkTreeRowReference      *bottom_separator_row;
67
68        gint                     number_of_top_rows;
69        gint                     number_of_middle_rows;
70        gint                     number_of_bottom_rows;
71
72        guint                    check_idle_id;
73};
74
75enum {
76        PROP_0,
77        PROP_LABEL_TEXT,
78        PROP_ICON_NAME,
79        PROP_DEFAULT_ITEM
80};
81
82enum {
83        ACTIVATED = 0,
84        NUMBER_OF_SIGNALS
85};
86
87static guint    signals[NUMBER_OF_SIGNALS];
88
89static void     gdm_option_widget_class_init  (GdmOptionWidgetClass *klass);
90static void     gdm_option_widget_init        (GdmOptionWidget      *option_widget);
91static void     gdm_option_widget_finalize    (GObject              *object);
92
93G_DEFINE_TYPE (GdmOptionWidget, gdm_option_widget, GTK_TYPE_ALIGNMENT)
94enum {
95        OPTION_NAME_COLUMN = 0,
96        OPTION_COMMENT_COLUMN,
97        OPTION_POSITION_COLUMN,
98        OPTION_ID_COLUMN,
99        NUMBER_OF_OPTION_COLUMNS
100};
101
102static gboolean
103find_item (GdmOptionWidget *widget,
104           const char       *id,
105           GtkTreeIter      *iter)
106{
107        GtkTreeModel *model;
108        gboolean      found_item;
109
110        g_assert (GDM_IS_OPTION_WIDGET (widget));
111        g_assert (id != NULL);
112
113        found_item = FALSE;
114        model = GTK_TREE_MODEL (widget->priv->model_sorter);
115
116        if (!gtk_tree_model_get_iter_first (model, iter)) {
117                return FALSE;
118        }
119
120        do {
121                char *item_id;
122
123                gtk_tree_model_get (model, iter,
124                                    OPTION_ID_COLUMN, &item_id, -1);
125
126                g_assert (item_id != NULL);
127
128                if (strcmp (id, item_id) == 0) {
129                        found_item = TRUE;
130                }
131                g_free (item_id);
132
133        } while (!found_item && gtk_tree_model_iter_next (model, iter));
134
135        return found_item;
136}
137
138static char *
139get_active_item_id (GdmOptionWidget *widget,
140                    GtkTreeIter      *iter)
141{
142        char         *item_id;
143        GtkTreeModel *model;
144        GtkTreePath  *path;
145
146        g_return_val_if_fail (GDM_IS_OPTION_WIDGET (widget), NULL);
147
148        model = GTK_TREE_MODEL (widget->priv->list_store);
149        item_id = NULL;
150
151        if (widget->priv->active_row == NULL ||
152            !gtk_tree_row_reference_valid (widget->priv->active_row)) {
153                return NULL;
154        }
155
156        path = gtk_tree_row_reference_get_path (widget->priv->active_row);
157        if (gtk_tree_model_get_iter (model, iter, path)) {
158                gtk_tree_model_get (model, iter,
159                                    OPTION_ID_COLUMN, &item_id, -1);
160        };
161        gtk_tree_path_free (path);
162
163        return item_id;
164}
165
166char *
167gdm_option_widget_get_active_item (GdmOptionWidget *widget)
168{
169        GtkTreeIter iter;
170
171        return get_active_item_id (widget, &iter);
172}
173
174static void
175activate_from_item_id (GdmOptionWidget *widget,
176                       const char      *item_id)
177{
178        GtkTreeIter   iter;
179
180        if (item_id == NULL) {
181                if (widget->priv->active_row != NULL) {
182                    gtk_tree_row_reference_free (widget->priv->active_row);
183                    widget->priv->active_row = NULL;
184                }
185
186                gtk_combo_box_set_active (GTK_COMBO_BOX (widget->priv->items_combo_box), -1);
187                return;
188        }
189
190        if (!find_item (widget, item_id, &iter)) {
191                g_critical ("Tried to activate non-existing item from option widget");
192                return;
193        }
194
195        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget->priv->items_combo_box),
196                                       &iter);
197}
198
199static void
200activate_from_row (GdmOptionWidget    *widget,
201                   GtkTreeRowReference *row)
202{
203        g_assert (row != NULL);
204        g_assert (gtk_tree_row_reference_valid (row));
205
206        if (widget->priv->active_row != NULL) {
207                gtk_tree_row_reference_free (widget->priv->active_row);
208                widget->priv->active_row = NULL;
209        }
210
211        widget->priv->active_row = gtk_tree_row_reference_copy (row);
212
213        g_signal_emit (widget, signals[ACTIVATED], 0);
214
215}
216
217static void
218activate_selected_item (GdmOptionWidget *widget)
219{
220        GtkTreeModel        *model;
221        GtkTreeIter          sorted_iter;
222        gboolean             is_already_active;
223
224        model = GTK_TREE_MODEL (widget->priv->list_store);
225        is_already_active = FALSE;
226
227        if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget->priv->items_combo_box), &sorted_iter)) {
228                GtkTreeRowReference *row;
229                GtkTreePath *sorted_path;
230                GtkTreePath *base_path;
231
232                sorted_path = gtk_tree_model_get_path (GTK_TREE_MODEL (widget->priv->model_sorter),
233                                                       &sorted_iter);
234                base_path =
235                    gtk_tree_model_sort_convert_path_to_child_path (widget->priv->model_sorter,
236                                                                    sorted_path);
237                gtk_tree_path_free (sorted_path);
238
239                if (widget->priv->active_row != NULL) {
240                        GtkTreePath *active_path;
241
242                        active_path = gtk_tree_row_reference_get_path (widget->priv->active_row);
243
244                        if (active_path != NULL) {
245                                if (gtk_tree_path_compare  (base_path, active_path) == 0) {
246                                        is_already_active = TRUE;
247                                }
248                                gtk_tree_path_free (active_path);
249                        }
250                }
251                g_assert (base_path != NULL);
252                row = gtk_tree_row_reference_new (model, base_path);
253                gtk_tree_path_free (base_path);
254
255                if (!is_already_active) {
256                    activate_from_row (widget, row);
257                }
258
259                gtk_tree_row_reference_free (row);
260        }
261}
262
263void
264gdm_option_widget_set_active_item (GdmOptionWidget *widget,
265                                   const char      *id)
266{
267        g_return_if_fail (GDM_IS_OPTION_WIDGET (widget));
268
269        activate_from_item_id (widget, id);
270}
271
272char *
273gdm_option_widget_get_default_item (GdmOptionWidget *widget)
274{
275        g_return_val_if_fail (GDM_IS_OPTION_WIDGET (widget), NULL);
276
277        return g_strdup (widget->priv->default_item_id);
278}
279
280void
281gdm_option_widget_set_default_item (GdmOptionWidget *widget,
282                                    const char      *item)
283{
284        char *active;
285
286        g_return_if_fail (GDM_IS_OPTION_WIDGET (widget));
287        g_return_if_fail (item == NULL ||
288                          gdm_option_widget_lookup_item (widget, item,
289                                                         NULL, NULL, NULL));
290
291        if (widget->priv->default_item_id == NULL ||
292            item == NULL ||
293            strcmp (widget->priv->default_item_id, item) != 0) {
294                g_free (widget->priv->default_item_id);
295                widget->priv->default_item_id = NULL;
296
297                if (widget->priv->active_row == NULL || item != NULL) {
298                    activate_from_item_id (widget, item);
299                }
300
301                widget->priv->default_item_id = g_strdup (item);
302
303                g_object_notify (G_OBJECT (widget), "default-item");
304
305        } 
306
307        /* If a row has already been selected, then reset the selection to
308         * the active row.  This way when a user fails to authenticate, any
309         * previously selected value will still be selected.
310         */
311        active = gdm_option_widget_get_active_item (widget);
312
313        if (active != NULL && item != NULL &&
314            strcmp (gdm_option_widget_get_active_item (widget),
315                    item) != 0) {
316                GtkTreeRowReference *row;
317                GtkTreePath         *active_path;
318                GtkTreeModel        *model;
319
320                gdm_option_widget_set_active_item (widget, active);
321                active_path = gtk_tree_row_reference_get_path (widget->priv->active_row);
322                model = GTK_TREE_MODEL (widget->priv->list_store);
323                if (active_path != NULL) {
324                        row = gtk_tree_row_reference_new (model, active_path);
325                        activate_from_row (widget, row);
326                        gtk_tree_path_free (active_path);
327                        gtk_tree_row_reference_free (row);
328                }
329        }
330}
331
332static const char *
333gdm_option_widget_get_label_text (GdmOptionWidget *widget)
334{
335        return widget->priv->label_text;
336}
337
338static void
339gdm_option_widget_set_label_text (GdmOptionWidget *widget,
340                                  const char      *text)
341{
342        if (widget->priv->label_text == NULL ||
343            strcmp (widget->priv->label_text, text) != 0) {
344                g_free (widget->priv->label_text);
345                widget->priv->label_text = g_strdup (text);
346                gtk_label_set_markup_with_mnemonic (GTK_LABEL (widget->priv->label),
347                                      text);
348                g_object_notify (G_OBJECT (widget), "label-text");
349        }
350}
351
352static const char *
353gdm_option_widget_get_icon_name (GdmOptionWidget *widget)
354{
355        return widget->priv->icon_name;
356}
357
358static void
359gdm_option_widget_set_icon_name (GdmOptionWidget *widget,
360                                 const char      *name)
361{
362        if (name == NULL && widget->priv->icon_name != NULL) {
363                /* remove icon */
364                g_free (widget->priv->icon_name);
365                widget->priv->icon_name = NULL;
366                gtk_widget_hide (widget->priv->image);
367                gtk_image_clear (GTK_IMAGE (widget->priv->image));
368                g_object_notify (G_OBJECT (widget), "icon-name");
369        } else if (name != NULL && widget->priv->icon_name == NULL) {
370                /* add icon */
371                widget->priv->icon_name = g_strdup (name);
372                gtk_widget_show (widget->priv->image);
373                gtk_image_set_from_icon_name (GTK_IMAGE (widget->priv->image), name, GTK_ICON_SIZE_BUTTON);
374                g_object_notify (G_OBJECT (widget), "icon-name");
375        } else if (name != NULL
376                   && widget->priv->icon_name != NULL
377                   && strcmp (widget->priv->icon_name, name) != 0) {
378                /* changed icon */
379                g_free (widget->priv->icon_name);
380                widget->priv->icon_name = g_strdup (name);
381                gtk_image_set_from_icon_name (GTK_IMAGE (widget->priv->image), name, GTK_ICON_SIZE_BUTTON);
382                g_object_notify (G_OBJECT (widget), "icon-name");
383        }
384}
385
386static void
387gdm_option_widget_set_property (GObject        *object,
388                                guint           prop_id,
389                                const GValue   *value,
390                                GParamSpec     *pspec)
391{
392        GdmOptionWidget *self;
393
394        self = GDM_OPTION_WIDGET (object);
395
396        switch (prop_id) {
397        case PROP_LABEL_TEXT:
398                gdm_option_widget_set_label_text (self, g_value_get_string (value));
399                break;
400        case PROP_ICON_NAME:
401                gdm_option_widget_set_icon_name (self, g_value_get_string (value));
402                break;
403        case PROP_DEFAULT_ITEM:
404                gdm_option_widget_set_default_item (self, g_value_get_string (value));
405                break;
406        default:
407                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408                break;
409        }
410}
411
412static void
413gdm_option_widget_get_property (GObject        *object,
414                                guint           prop_id,
415                                GValue         *value,
416                                GParamSpec     *pspec)
417{
418        GdmOptionWidget *self;
419
420        self = GDM_OPTION_WIDGET (object);
421
422        switch (prop_id) {
423        case PROP_LABEL_TEXT:
424                g_value_set_string (value,
425                                    gdm_option_widget_get_label_text (self));
426                break;
427        case PROP_ICON_NAME:
428                g_value_set_string (value,
429                                    gdm_option_widget_get_icon_name (self));
430                break;
431        case PROP_DEFAULT_ITEM:
432                g_value_take_string (value,
433                                    gdm_option_widget_get_default_item (self));
434                break;
435        default:
436                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
437                break;
438        }
439}
440
441static GObject *
442gdm_option_widget_constructor (GType                  type,
443                               guint                  n_construct_properties,
444                               GObjectConstructParam *construct_properties)
445{
446        GdmOptionWidget      *option_widget;
447
448        option_widget = GDM_OPTION_WIDGET (G_OBJECT_CLASS (gdm_option_widget_parent_class)->constructor (type,
449                                                                                                         n_construct_properties,
450                                                                                                         construct_properties));
451
452        return G_OBJECT (option_widget);
453}
454
455static void
456gdm_option_widget_dispose (GObject *object)
457{
458        GdmOptionWidget *widget;
459
460        widget = GDM_OPTION_WIDGET (object);
461
462        if (widget->priv->top_separator_row != NULL) {
463                gtk_tree_row_reference_free (widget->priv->top_separator_row);
464                widget->priv->top_separator_row = NULL;
465        }
466
467        if (widget->priv->bottom_separator_row != NULL) {
468                gtk_tree_row_reference_free (widget->priv->bottom_separator_row);
469                widget->priv->bottom_separator_row = NULL;
470        }
471
472        if (widget->priv->active_row != NULL) {
473                gtk_tree_row_reference_free (widget->priv->active_row);
474                widget->priv->active_row = NULL;
475        }
476
477        G_OBJECT_CLASS (gdm_option_widget_parent_class)->dispose (object);
478}
479
480static gboolean
481gdm_option_widget_mnemonic_activate (GtkWidget *widget,
482                                     gboolean   group_cycling)
483{
484        GdmOptionWidget *option_widget;
485
486        option_widget = GDM_OPTION_WIDGET (widget);
487        gtk_widget_grab_focus (option_widget->priv->items_combo_box);
488        gtk_combo_box_popup (GTK_COMBO_BOX (option_widget->priv->items_combo_box));
489
490        return TRUE;
491}
492
493static void
494gdm_option_widget_class_init (GdmOptionWidgetClass *klass)
495{
496        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
497        GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
498
499        object_class->get_property = gdm_option_widget_get_property;
500        object_class->set_property = gdm_option_widget_set_property;
501        object_class->constructor = gdm_option_widget_constructor;
502        object_class->dispose = gdm_option_widget_dispose;
503        object_class->finalize = gdm_option_widget_finalize;
504        widget_class->mnemonic_activate = gdm_option_widget_mnemonic_activate;
505
506        gtk_rc_parse_string (GDM_OPTION_WIDGET_RC_STRING);
507
508        signals [ACTIVATED] = g_signal_new ("activated",
509                                            G_TYPE_FROM_CLASS (object_class),
510                                            G_SIGNAL_RUN_FIRST,
511                                            G_STRUCT_OFFSET (GdmOptionWidgetClass, activated),
512                                            NULL,
513                                            NULL,
514                                            g_cclosure_marshal_VOID__VOID,
515                                            G_TYPE_NONE,
516                                            0);
517
518        g_object_class_install_property (object_class,
519                                         PROP_LABEL_TEXT,
520                                         g_param_spec_string ("label-text",
521                                                              _("Label Text"),
522                                                              _("The text to use as a label"),
523                                                              NULL,
524                                                              (G_PARAM_READWRITE |
525                                                               G_PARAM_CONSTRUCT)));
526        g_object_class_install_property (object_class,
527                                         PROP_ICON_NAME,
528                                         g_param_spec_string ("icon-name",
529                                                              _("Icon name"),
530                                                              _("The icon to use with the label"),
531                                                              NULL,
532                                                              (G_PARAM_READWRITE |
533                                                               G_PARAM_CONSTRUCT)));
534
535        g_object_class_install_property (object_class,
536                                         PROP_DEFAULT_ITEM,
537                                         g_param_spec_string ("default-item",
538                                                              _("Default Item"),
539                                                              _("The id of the default item"),
540                                                              NULL,
541                                                              G_PARAM_READWRITE));
542
543        g_type_class_add_private (klass, sizeof (GdmOptionWidgetPrivate));
544}
545
546static void
547on_changed (GtkComboBox     *combo_box,
548            GdmOptionWidget *widget)
549{
550        if (widget->priv->default_item_id == NULL) {
551                return;
552        }
553
554        activate_selected_item (widget);
555}
556
557static void
558on_default_item_changed (GdmOptionWidget *widget)
559{
560        gtk_widget_set_sensitive (widget->priv->items_combo_box,
561                                  widget->priv->default_item_id != NULL);
562        gtk_tree_model_filter_refilter (widget->priv->model_filter);
563}
564
565static gboolean
566path_is_row (GdmOptionWidget     *widget,
567             GtkTreeModel        *model,
568             GtkTreePath         *path,
569             GtkTreeRowReference *row)
570{
571        GtkTreePath      *row_path;
572        GtkTreePath      *translated_path;
573        gboolean          is_row;
574
575        row_path = gtk_tree_row_reference_get_path (row);
576
577        if (row_path == NULL) {
578                return FALSE;
579        }
580
581        if (model == GTK_TREE_MODEL (widget->priv->model_sorter)) {
582                GtkTreePath *filtered_path;
583
584                filtered_path = gtk_tree_model_sort_convert_path_to_child_path (widget->priv->model_sorter, path);
585
586                translated_path = gtk_tree_model_filter_convert_path_to_child_path (widget->priv->model_filter, filtered_path);
587                gtk_tree_path_free (filtered_path);
588        } else if (model == GTK_TREE_MODEL (widget->priv->model_filter)) {
589                translated_path = gtk_tree_model_filter_convert_path_to_child_path (widget->priv->model_filter, path);
590        } else {
591                g_assert (model == GTK_TREE_MODEL (widget->priv->list_store));
592                translated_path = gtk_tree_path_copy (path);
593        }
594
595        if (gtk_tree_path_compare (row_path, translated_path) == 0) {
596                is_row = TRUE;
597        } else {
598                is_row = FALSE;
599        }
600        gtk_tree_path_free (translated_path);
601
602        return is_row;
603}
604
605static gboolean
606path_is_top_separator (GdmOptionWidget *widget,
607                       GtkTreeModel    *model,
608                       GtkTreePath     *path)
609{
610        if (widget->priv->top_separator_row != NULL) {
611                if (path_is_row (widget, model, path,
612                                 widget->priv->top_separator_row)) {
613                    return TRUE;
614                }
615        }
616
617        return FALSE;
618}
619
620static gboolean
621path_is_bottom_separator (GdmOptionWidget *widget,
622                          GtkTreeModel    *model,
623                          GtkTreePath     *path)
624{
625        if (widget->priv->bottom_separator_row != NULL) {
626
627                if (path_is_row (widget, model, path,
628                                 widget->priv->bottom_separator_row)) {
629                    return TRUE;
630                }
631        }
632
633        return FALSE;
634}
635
636static gboolean
637path_is_separator (GdmOptionWidget *widget,
638                   GtkTreeModel    *model,
639                   GtkTreePath     *path)
640{
641        return path_is_top_separator (widget, model, path) ||
642               path_is_bottom_separator (widget, model, path);
643}
644
645static gboolean
646gdm_option_widget_check_visibility (GdmOptionWidget *widget)
647{
648        if ((widget->priv->number_of_middle_rows != 0) &&
649            (widget->priv->number_of_top_rows > 0 ||
650             widget->priv->number_of_middle_rows > 1 || 
651             widget->priv->number_of_bottom_rows > 0)) {
652                gtk_widget_show (widget->priv->items_combo_box);
653                gtk_widget_show (widget->priv->label);
654
655                if (widget->priv->icon_name != NULL) {
656                        gtk_widget_show (widget->priv->image);
657                }
658        } else {
659                gtk_widget_hide (widget->priv->items_combo_box);
660                gtk_widget_hide (widget->priv->label);
661                gtk_widget_hide (widget->priv->image);
662        }
663
664        widget->priv->check_idle_id = 0;
665        return FALSE;
666}
667
668static void
669gdm_option_widget_queue_visibility_check (GdmOptionWidget *widget)
670{
671        if (widget->priv->check_idle_id == 0) {
672                widget->priv->check_idle_id = g_idle_add ((GSourceFunc) gdm_option_widget_check_visibility, widget);
673        }
674}
675
676static gboolean
677check_item_visibilty (GtkTreeModel *model,
678                      GtkTreeIter  *iter,
679                      gpointer      data)
680{
681        GdmOptionWidget *widget;
682        GtkTreePath     *path;
683        gboolean         is_top_separator;
684        gboolean         is_bottom_separator;
685        gboolean         is_visible;
686
687        g_assert (GDM_IS_OPTION_WIDGET (data));
688
689        widget = GDM_OPTION_WIDGET (data);
690
691        path = gtk_tree_model_get_path (model, iter);
692        is_top_separator = path_is_top_separator (widget, model, path);
693        is_bottom_separator = path_is_bottom_separator (widget, model, path);
694        gtk_tree_path_free (path);
695
696        if (is_top_separator) {
697                is_visible = widget->priv->number_of_top_rows > 0 &&
698                             widget->priv->number_of_middle_rows > 0;
699        } else if (is_bottom_separator) {
700                is_visible = widget->priv->number_of_bottom_rows > 0 &&
701                             widget->priv->number_of_middle_rows > 0;
702        } else {
703                is_visible = TRUE;
704        }
705
706        gdm_option_widget_queue_visibility_check (widget);
707
708        return is_visible;
709}
710
711static int
712compare_item (GtkTreeModel *model,
713              GtkTreeIter  *a,
714              GtkTreeIter  *b,
715              gpointer      data)
716{
717        GdmOptionWidget *widget;
718        GtkTreePath     *path;
719        gboolean         a_is_separator;
720        gboolean         b_is_separator;
721        char            *name_a;
722        char            *name_b;
723        int              position_a;
724        int              position_b;
725        int              result;
726
727        g_assert (GDM_IS_OPTION_WIDGET (data));
728
729        widget = GDM_OPTION_WIDGET (data);
730
731        gtk_tree_model_get (model, a,
732                            OPTION_NAME_COLUMN, &name_a,
733                            OPTION_POSITION_COLUMN, &position_a,
734                            -1);
735
736        gtk_tree_model_get (model, b,
737                            OPTION_NAME_COLUMN, &name_b,
738                            OPTION_POSITION_COLUMN, &position_b,
739                            -1);
740
741        if (position_a != position_b) {
742                result = position_a - position_b;
743                goto out;
744        }
745
746        if (position_a == GDM_OPTION_WIDGET_POSITION_MIDDLE) {
747                a_is_separator = FALSE;
748        } else {
749                path = gtk_tree_model_get_path (model, a);
750                a_is_separator = path_is_separator (widget, model, path);
751                gtk_tree_path_free (path);
752        }
753
754        if (position_b == GDM_OPTION_WIDGET_POSITION_MIDDLE) {
755                b_is_separator = FALSE;
756        } else {
757                path = gtk_tree_model_get_path (model, b);
758                b_is_separator = path_is_separator (widget, model, path);
759                gtk_tree_path_free (path);
760        }
761
762        if (a_is_separator && b_is_separator) {
763                result = 0;
764                goto out;
765        }
766
767        if (!a_is_separator && !b_is_separator) {
768            result = g_utf8_collate (name_a, name_b);
769            goto out;
770        }
771
772        g_assert (position_a == position_b);
773        g_assert (position_a != GDM_OPTION_WIDGET_POSITION_MIDDLE);
774
775        result = a_is_separator - b_is_separator;
776
777        if (position_a == GDM_OPTION_WIDGET_POSITION_BOTTOM) {
778                result *= -1;
779        }
780out:
781        g_free (name_a);
782        g_free (name_b);
783
784        return result;
785}
786
787static void
788name_cell_data_func (GtkTreeViewColumn  *tree_column,
789                     GtkCellRenderer    *cell,
790                     GtkTreeModel       *model,
791                     GtkTreeIter        *iter,
792                     GdmOptionWidget   *widget)
793{
794        char    *name;
795        char    *id;
796        char    *markup;
797        gboolean is_default;
798
799        name = NULL;
800        gtk_tree_model_get (model,
801                            iter,
802                            OPTION_ID_COLUMN, &id,
803                            OPTION_NAME_COLUMN, &name,
804                            -1);
805
806        if (widget->priv->default_item_id != NULL &&
807            id != NULL &&
808            strcmp (widget->priv->default_item_id, id) == 0) {
809                is_default = TRUE;
810        } else {
811                is_default = FALSE;
812        }
813        g_free (id);
814        id = NULL;
815
816        markup = g_strdup_printf ("<span size='small'>%s%s%s</span>",
817                                  is_default? "<i>" : "",
818                                  name ? name : "",
819                                  is_default? "</i>" : "");
820        g_free (name);
821
822        g_object_set (cell, "markup", markup, NULL);
823        g_free (markup);
824}
825
826static gboolean
827separator_func (GtkTreeModel *model,
828                GtkTreeIter  *iter,
829                gpointer      data)
830{
831        GdmOptionWidget *widget;
832        GtkTreePath     *path;
833        gboolean         is_separator;
834
835        g_assert (GDM_IS_OPTION_WIDGET (data));
836
837        widget = GDM_OPTION_WIDGET (data);
838
839        path = gtk_tree_model_get_path (model, iter);
840
841        is_separator = path_is_separator (widget, model, path);
842
843        gtk_tree_path_free (path);
844
845        return is_separator;
846}
847
848static void
849add_separators (GdmOptionWidget *widget)
850{
851        GtkTreeIter   iter;
852        GtkTreeModel *model;
853        GtkTreePath  *path;
854
855        g_assert (widget->priv->top_separator_row == NULL);
856        g_assert (widget->priv->bottom_separator_row == NULL);
857
858        model = GTK_TREE_MODEL (widget->priv->list_store);
859
860        gtk_list_store_insert_with_values (widget->priv->list_store,
861                                           &iter, 0,
862                                           OPTION_ID_COLUMN, "--",
863                                           OPTION_POSITION_COLUMN, GDM_OPTION_WIDGET_POSITION_BOTTOM,
864                                           -1);
865        path = gtk_tree_model_get_path (model, &iter);
866        widget->priv->bottom_separator_row =
867            gtk_tree_row_reference_new (model, path);
868        gtk_tree_path_free (path);
869
870        gtk_list_store_insert_with_values (widget->priv->list_store,
871                                           &iter, 0,
872                                           OPTION_ID_COLUMN, "-",
873                                           OPTION_POSITION_COLUMN, GDM_OPTION_WIDGET_POSITION_TOP,
874                                           -1);
875        path = gtk_tree_model_get_path (model, &iter);
876        widget->priv->top_separator_row =
877            gtk_tree_row_reference_new (model, path);
878        gtk_tree_path_free (path);
879}
880
881static void
882gdm_option_widget_init (GdmOptionWidget *widget)
883{
884        GtkWidget         *box;
885        GtkCellRenderer   *renderer;
886
887        widget->priv = GDM_OPTION_WIDGET_GET_PRIVATE (widget);
888
889        gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 0, 0);
890        gtk_alignment_set (GTK_ALIGNMENT (widget), 0.5, 0.5, 0, 0);
891
892        box = gtk_hbox_new (FALSE, 6);
893        gtk_widget_show (box);
894        gtk_container_add (GTK_CONTAINER (widget),
895                           box);
896
897        widget->priv->image = gtk_image_new ();
898        gtk_widget_set_no_show_all (widget->priv->image, TRUE);
899        gtk_box_pack_start (GTK_BOX (box), widget->priv->image, FALSE, FALSE, 0);
900
901        widget->priv->label = gtk_label_new ("");
902        gtk_label_set_use_underline (GTK_LABEL (widget->priv->label), TRUE);
903        gtk_label_set_use_markup (GTK_LABEL (widget->priv->label), TRUE);
904        gtk_widget_set_no_show_all (widget->priv->label, TRUE);
905        gtk_box_pack_start (GTK_BOX (box), widget->priv->label, FALSE, FALSE, 0);
906
907        widget->priv->items_combo_box = gtk_combo_box_new ();
908
909        g_signal_connect (widget->priv->items_combo_box,
910                          "changed",
911                          G_CALLBACK (on_changed),
912                          widget);
913
914        /* We disable the combo box until it has a default
915         */
916        gtk_widget_set_sensitive (widget->priv->items_combo_box, FALSE);
917        g_signal_connect (widget,
918                          "notify::default-item",
919                          G_CALLBACK (on_default_item_changed),
920                          NULL);
921
922        gtk_widget_set_no_show_all (widget->priv->items_combo_box, TRUE);
923        gtk_container_add (GTK_CONTAINER (box),
924                           widget->priv->items_combo_box);
925        gtk_label_set_mnemonic_widget (GTK_LABEL (widget->priv->label),
926                                       GTK_WIDGET (widget));
927
928        g_assert (NUMBER_OF_OPTION_COLUMNS == 4);
929        widget->priv->list_store = gtk_list_store_new (NUMBER_OF_OPTION_COLUMNS,
930                                                       G_TYPE_STRING,
931                                                       G_TYPE_STRING,
932                                                       G_TYPE_INT,
933                                                       G_TYPE_STRING);
934
935
936        widget->priv->model_filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (widget->priv->list_store), NULL));
937
938        gtk_tree_model_filter_set_visible_func (widget->priv->model_filter,
939                                                check_item_visibilty,
940                                                widget, NULL);
941
942        widget->priv->model_sorter = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (widget->priv->model_filter)));
943
944        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (widget->priv->model_sorter),
945                                         OPTION_ID_COLUMN,
946                                         compare_item,
947                                         widget, NULL);
948
949        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (widget->priv->model_sorter),
950                                              OPTION_ID_COLUMN,
951                                              GTK_SORT_ASCENDING);
952        gtk_combo_box_set_model (GTK_COMBO_BOX (widget->priv->items_combo_box),
953                                 GTK_TREE_MODEL (widget->priv->model_sorter));
954
955        add_separators (widget);
956        gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (widget->priv->items_combo_box),
957                                              separator_func, widget, NULL);
958
959        /* NAME COLUMN */
960        renderer = gtk_cell_renderer_text_new ();
961        gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget->priv->items_combo_box), renderer, FALSE);
962        gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (widget->priv->items_combo_box),
963                                            renderer,
964                                            (GtkCellLayoutDataFunc) name_cell_data_func,
965                                            widget,
966                                            NULL);
967}
968
969static void
970gdm_option_widget_finalize (GObject *object)
971{
972        GdmOptionWidget *widget;
973
974        g_return_if_fail (object != NULL);
975        g_return_if_fail (GDM_IS_OPTION_WIDGET (object));
976
977        widget = GDM_OPTION_WIDGET (object);
978
979        g_return_if_fail (widget->priv != NULL);
980
981        g_free (widget->priv->icon_name);
982        g_free (widget->priv->label_text);
983
984        G_OBJECT_CLASS (gdm_option_widget_parent_class)->finalize (object);
985}
986
987GtkWidget *
988gdm_option_widget_new (const char *label_text)
989{
990        GObject *object;
991
992        object = g_object_new (GDM_TYPE_OPTION_WIDGET,
993                               "label-text", label_text, NULL);
994
995        return GTK_WIDGET (object);
996}
997
998void
999gdm_option_widget_add_item (GdmOptionWidget         *widget,
1000                            const char              *id,
1001                            const char              *name,
1002                            const char              *comment,
1003                            GdmOptionWidgetPosition  position)
1004{
1005        GtkTreeIter iter;
1006
1007        g_return_if_fail (GDM_IS_OPTION_WIDGET (widget));
1008
1009        switch (position) {
1010            case GDM_OPTION_WIDGET_POSITION_BOTTOM:
1011                widget->priv->number_of_bottom_rows++;
1012                break;
1013
1014            case GDM_OPTION_WIDGET_POSITION_MIDDLE:
1015                widget->priv->number_of_middle_rows++;
1016                break;
1017
1018            case GDM_OPTION_WIDGET_POSITION_TOP:
1019                widget->priv->number_of_top_rows++;
1020                break;
1021        }
1022
1023        gtk_list_store_insert_with_values (widget->priv->list_store,
1024                                           &iter, 0,
1025                                           OPTION_NAME_COLUMN, name,
1026                                           OPTION_COMMENT_COLUMN, comment,
1027                                           OPTION_POSITION_COLUMN, (int) position,
1028                                           OPTION_ID_COLUMN, id,
1029                                           -1);
1030        gtk_tree_model_filter_refilter (widget->priv->model_filter);
1031}
1032
1033void
1034gdm_option_widget_remove_item (GdmOptionWidget *widget,
1035                               const char      *id)
1036{
1037        GtkTreeModel *model;
1038        GtkTreeIter   iter;
1039        int           position;
1040
1041        g_return_if_fail (GDM_IS_OPTION_WIDGET (widget));
1042
1043        model = GTK_TREE_MODEL (widget->priv->list_store);
1044
1045        if (!find_item (widget, id, &iter)) {
1046                g_critical ("Tried to remove non-existing item from option widget");
1047                return;
1048        }
1049
1050        if (widget->priv->default_item_id != NULL &&
1051            strcmp (widget->priv->default_item_id, id) == 0) {
1052                g_critical ("Tried to remove default item from option widget");
1053                return;
1054        }
1055
1056        gtk_tree_model_get (model, &iter,
1057                            OPTION_POSITION_COLUMN, &position,
1058                            -1);
1059
1060        switch ((GdmOptionWidgetPosition) position) {
1061            case GDM_OPTION_WIDGET_POSITION_BOTTOM:
1062                widget->priv->number_of_bottom_rows--;
1063                break;
1064
1065            case GDM_OPTION_WIDGET_POSITION_MIDDLE:
1066                widget->priv->number_of_middle_rows--;
1067                break;
1068
1069            case GDM_OPTION_WIDGET_POSITION_TOP:
1070                widget->priv->number_of_top_rows--;
1071                break;
1072        }
1073
1074        gtk_list_store_remove (widget->priv->list_store, &iter);
1075        gtk_tree_model_filter_refilter (widget->priv->model_filter);
1076}
1077
1078void
1079gdm_option_widget_remove_all_items (GdmOptionWidget *widget)
1080{
1081        GtkTreeIter   iter;
1082        GtkTreeModel *model;
1083        int           position;
1084        gboolean      is_valid;
1085
1086        g_assert (GDM_IS_OPTION_WIDGET (widget));
1087
1088        model = GTK_TREE_MODEL (widget->priv->list_store);
1089
1090        if (!gtk_tree_model_get_iter_first (model, &iter)) {
1091                return;
1092        }
1093
1094        do {
1095                gtk_tree_model_get (model, &iter,
1096                                    OPTION_POSITION_COLUMN, &position,
1097                                    -1);
1098
1099                if ((GdmOptionWidgetPosition) position == GDM_OPTION_WIDGET_POSITION_MIDDLE) {
1100                        is_valid = gtk_list_store_remove (widget->priv->list_store,
1101                                                          &iter);
1102                } else {
1103                        is_valid = gtk_tree_model_iter_next (model, &iter);
1104                }
1105
1106
1107        } while (is_valid);
1108}
1109
1110gboolean
1111gdm_option_widget_lookup_item (GdmOptionWidget          *widget,
1112                               const char               *id,
1113                               char                    **name,
1114                               char                    **comment,
1115                               GdmOptionWidgetPosition  *position)
1116{
1117        GtkTreeIter   iter;
1118        char         *active_item_id;
1119
1120        g_return_val_if_fail (GDM_IS_OPTION_WIDGET (widget), FALSE);
1121        g_return_val_if_fail (id != NULL, FALSE);
1122
1123        active_item_id = get_active_item_id (widget, &iter);
1124
1125        if (active_item_id == NULL || strcmp (active_item_id, id) != 0) {
1126                g_free (active_item_id);
1127
1128                if (!find_item (widget, id, &iter)) {
1129                        return FALSE;
1130                }
1131        } else {
1132                g_free (active_item_id);
1133        }
1134
1135        if (name != NULL) {
1136                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
1137                                    OPTION_NAME_COLUMN, name, -1);
1138        }
1139
1140        if (comment != NULL) {
1141                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
1142                                    OPTION_COMMENT_COLUMN, comment, -1);
1143        }
1144
1145        if (position != NULL) {
1146                int position_as_int;
1147
1148                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
1149                                    OPTION_POSITION_COLUMN, &position_as_int, -1);
1150
1151                *position = (GdmOptionWidgetPosition) position_as_int;
1152        }
1153
1154        return TRUE;
1155}
Note: See TracBrowser for help on using the repository browser.