1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright (C) 2007 Ray Strode <rstrode@redhat.com>
4
* Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
5
* Copyright (C) 2008 Red Hat, Inc.
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
35
#include <glib/gi18n.h>
36
#include <glib/gstdio.h>
39
#include "gdm-chooser-widget.h"
40
#include "gdm-scrollable-widget.h"
41
#include "gdm-cell-renderer-timer.h"
42
#include "gdm-timer.h"
44
#define GDM_CHOOSER_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_CHOOSER_WIDGET, GdmChooserWidgetPrivate))
46
#ifndef GDM_CHOOSER_WIDGET_DEFAULT_ICON_SIZE
47
#define GDM_CHOOSER_WIDGET_DEFAULT_ICON_SIZE 64
51
GDM_CHOOSER_WIDGET_STATE_GROWN = 0,
52
GDM_CHOOSER_WIDGET_STATE_GROWING,
53
GDM_CHOOSER_WIDGET_STATE_SHRINKING,
54
GDM_CHOOSER_WIDGET_STATE_SHRUNK,
55
} GdmChooserWidgetState;
57
struct GdmChooserWidgetPrivate
60
GtkWidget *frame_alignment;
61
GtkWidget *scrollable_widget;
63
GtkWidget *items_view;
64
GtkListStore *list_store;
66
GtkTreeModelFilter *model_filter;
67
GtkTreeModelSort *model_sorter;
69
GdkPixbuf *is_in_use_pixbuf;
71
/* row for the list_store model */
72
GtkTreeRowReference *active_row;
73
GtkTreeRowReference *separator_row;
75
GHashTable *rows_with_timers;
77
GtkTreeViewColumn *status_column;
78
GtkTreeViewColumn *image_column;
84
gint number_of_normal_rows;
85
gint number_of_separated_rows;
86
gint number_of_rows_with_status;
87
gint number_of_rows_with_images;
88
gint number_of_active_timers;
91
guint timer_animation_timeout_id;
93
guint32 should_hide_inactive_items : 1;
94
guint32 emit_activated_after_resize_animation : 1;
96
GdmChooserWidgetPosition separator_position;
97
GdmChooserWidgetState state;
99
double active_row_normalized_position;
116
static guint signals[NUMBER_OF_SIGNALS];
118
static void gdm_chooser_widget_class_init (GdmChooserWidgetClass *klass);
119
static void gdm_chooser_widget_init (GdmChooserWidget *chooser_widget);
120
static void gdm_chooser_widget_finalize (GObject *object);
122
static void update_timer_from_time (GdmChooserWidget *widget,
123
GtkTreeRowReference *row,
126
G_DEFINE_TYPE (GdmChooserWidget, gdm_chooser_widget, GTK_TYPE_ALIGNMENT)
129
CHOOSER_IMAGE_COLUMN = 0,
131
CHOOSER_COMMENT_COLUMN,
132
CHOOSER_PRIORITY_COLUMN,
133
CHOOSER_ITEM_IS_IN_USE_COLUMN,
134
CHOOSER_ITEM_IS_SEPARATED_COLUMN,
135
CHOOSER_ITEM_IS_VISIBLE_COLUMN,
136
CHOOSER_TIMER_START_TIME_COLUMN,
137
CHOOSER_TIMER_DURATION_COLUMN,
138
CHOOSER_TIMER_VALUE_COLUMN,
140
NUMBER_OF_CHOOSER_COLUMNS
144
find_item (GdmChooserWidget *widget,
151
g_assert (GDM_IS_CHOOSER_WIDGET (widget));
152
g_assert (id != NULL);
155
model = GTK_TREE_MODEL (widget->priv->list_store);
157
if (!gtk_tree_model_get_iter_first (model, iter)) {
164
gtk_tree_model_get (model,
170
g_assert (item_id != NULL);
172
if (strcmp (id, item_id) == 0) {
177
} while (!found_item && gtk_tree_model_iter_next (model, iter));
183
GdmChooserWidget *widget;
184
GdmChooserUpdateForeachFunc func;
189
foreach_item (GtkTreeModel *model,
192
UpdateForeachData *data)
198
gboolean is_separate;
203
gtk_tree_model_get (model,
205
CHOOSER_ID_COLUMN, &id,
206
CHOOSER_IMAGE_COLUMN, &image,
207
CHOOSER_NAME_COLUMN, &name,
208
CHOOSER_COMMENT_COLUMN, &comment,
209
CHOOSER_PRIORITY_COLUMN, &priority,
210
CHOOSER_ITEM_IS_IN_USE_COLUMN, &in_use,
211
CHOOSER_ITEM_IS_SEPARATED_COLUMN, &is_separate,
213
res = data->func (data->widget,
223
gtk_list_store_set (GTK_LIST_STORE (model),
225
CHOOSER_ID_COLUMN, id,
226
CHOOSER_IMAGE_COLUMN, image,
227
CHOOSER_NAME_COLUMN, name,
228
CHOOSER_COMMENT_COLUMN, comment,
229
CHOOSER_PRIORITY_COLUMN, priority,
230
CHOOSER_ITEM_IS_IN_USE_COLUMN, in_use,
231
CHOOSER_ITEM_IS_SEPARATED_COLUMN, is_separate,
238
g_object_unref (image);
245
gdm_chooser_widget_update_foreach_item (GdmChooserWidget *widget,
246
GdmChooserUpdateForeachFunc func,
249
UpdateForeachData fdata;
251
fdata.widget = widget;
253
fdata.user_data = user_data;
254
gtk_tree_model_foreach (GTK_TREE_MODEL (widget->priv->list_store),
255
(GtkTreeModelForeachFunc) foreach_item,
260
translate_list_path_to_view_path (GdmChooserWidget *widget,
263
GtkTreePath *filtered_path;
264
GtkTreePath *sorted_path;
266
/* the child model is the source for the filter */
267
filtered_path = gtk_tree_model_filter_convert_child_path_to_path (widget->priv->model_filter,
269
sorted_path = gtk_tree_model_sort_convert_child_path_to_path (widget->priv->model_sorter,
271
gtk_tree_path_free (filtered_path);
273
gtk_tree_path_free (*path);
279
translate_view_path_to_list_path (GdmChooserWidget *widget,
282
GtkTreePath *filtered_path;
283
GtkTreePath *list_path;
285
/* the child model is the source for the filter */
286
filtered_path = gtk_tree_model_sort_convert_path_to_child_path (widget->priv->model_sorter,
289
list_path = gtk_tree_model_filter_convert_path_to_child_path (widget->priv->model_filter,
291
gtk_tree_path_free (filtered_path);
293
gtk_tree_path_free (*path);
298
get_list_path_to_active_row (GdmChooserWidget *widget)
302
if (widget->priv->active_row == NULL) {
306
path = gtk_tree_row_reference_get_path (widget->priv->active_row);
315
get_view_path_to_active_row (GdmChooserWidget *widget)
319
path = get_list_path_to_active_row (widget);
324
translate_list_path_to_view_path (widget, &path);
330
get_active_item_id (GdmChooserWidget *widget,
337
g_return_val_if_fail (GDM_IS_CHOOSER_WIDGET (widget), NULL);
339
model = GTK_TREE_MODEL (widget->priv->list_store);
342
if (widget->priv->active_row == NULL) {
346
path = get_list_path_to_active_row (widget);
351
if (gtk_tree_model_get_iter (model, iter, path)) {
352
gtk_tree_model_get (model,
358
gtk_tree_path_free (path);
364
gdm_chooser_widget_get_active_item (GdmChooserWidget *widget)
368
return get_active_item_id (widget, &iter);
372
get_selected_list_path (GdmChooserWidget *widget,
375
GtkTreeSelection *selection;
376
GtkTreeModel *sort_model;
377
GtkTreeIter sorted_iter;
382
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->items_view));
383
if (gtk_tree_selection_get_selected (selection, &sort_model, &sorted_iter)) {
385
g_assert (sort_model == GTK_TREE_MODEL (widget->priv->model_sorter));
387
path = gtk_tree_model_get_path (sort_model, &sorted_iter);
389
translate_view_path_to_list_path (widget, &path);
391
g_debug ("GdmChooserWidget: no rows selected");
398
gdm_chooser_widget_get_selected_item (GdmChooserWidget *widget)
407
get_selected_list_path (widget, &path);
413
model = GTK_TREE_MODEL (widget->priv->list_store);
415
if (gtk_tree_model_get_iter (model, &iter, path)) {
416
gtk_tree_model_get (model,
423
gtk_tree_path_free (path);
429
gdm_chooser_widget_set_selected_item (GdmChooserWidget *widget,
433
GtkTreeSelection *selection;
436
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
438
g_debug ("GdmChooserWidget: setting selected item '%s'",
441
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->items_view));
443
model = GTK_TREE_MODEL (widget->priv->list_store);
445
if (find_item (widget, id, &iter)) {
448
path = gtk_tree_model_get_path (model, &iter);
449
translate_list_path_to_view_path (widget, &path);
451
gtk_tree_selection_select_path (selection, path);
453
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget->priv->items_view),
460
gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget->priv->items_view),
464
gtk_tree_path_free (path);
466
gtk_tree_selection_unselect_all (selection);
471
activate_from_item_id (GdmChooserWidget *widget,
479
model = GTK_TREE_MODEL (widget->priv->list_store);
482
if (find_item (widget, item_id, &iter)) {
483
path = gtk_tree_model_get_path (model, &iter);
485
path_str = gtk_tree_path_to_string (path);
486
g_debug ("GdmChooserWidget: got list path '%s'", path_str);
489
translate_list_path_to_view_path (widget, &path);
491
path_str = gtk_tree_path_to_string (path);
492
g_debug ("GdmChooserWidget: translated to view path '%s'", path_str);
497
g_debug ("GdmChooserWidget: unable to activate - path for item '%s' not found", item_id);
501
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget->priv->items_view),
508
gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget->priv->items_view),
513
gtk_tree_view_row_activated (GTK_TREE_VIEW (widget->priv->items_view),
516
gtk_tree_path_free (path);
520
set_frame_text (GdmChooserWidget *widget,
525
label = gtk_frame_get_label_widget (GTK_FRAME (widget->priv->frame));
527
if (text == NULL && label != NULL) {
528
gtk_frame_set_label_widget (GTK_FRAME (widget->priv->frame),
530
gtk_alignment_set_padding (GTK_ALIGNMENT (widget->priv->frame_alignment),
532
} else if (text != NULL && label == NULL) {
533
label = gtk_label_new ("");
534
gtk_label_set_mnemonic_widget (GTK_LABEL (label),
535
widget->priv->items_view);
536
gtk_widget_show (label);
537
gtk_frame_set_label_widget (GTK_FRAME (widget->priv->frame),
539
gtk_alignment_set_padding (GTK_ALIGNMENT (widget->priv->frame_alignment),
543
if (label != NULL && text != NULL) {
545
markup = g_strdup_printf ("%s", text);
546
gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), markup);
552
on_shrink_animation_step (GdmScrollableWidget *scrollable_widget,
555
GdmChooserWidget *widget)
557
GtkTreePath *active_row_path;
558
const double final_alignment = 0.5;
559
double row_alignment;
561
active_row_path = get_view_path_to_active_row (widget);
562
row_alignment = widget->priv->active_row_normalized_position + progress * (final_alignment - widget->priv->active_row_normalized_position);
564
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget->priv->items_view),
565
active_row_path, NULL, TRUE, row_alignment, 0.0);
566
gtk_tree_path_free (active_row_path);
570
update_separator_visibility (GdmChooserWidget *widget)
572
GtkTreePath *separator_path;
576
separator_path = gtk_tree_row_reference_get_path (widget->priv->separator_row);
578
if (separator_path == NULL) {
582
gtk_tree_model_get_iter (GTK_TREE_MODEL (widget->priv->list_store),
583
&iter, separator_path);
585
if (widget->priv->number_of_normal_rows > 0 &&
586
widget->priv->number_of_separated_rows > 0 &&
587
widget->priv->state != GDM_CHOOSER_WIDGET_STATE_SHRUNK) {
593
gtk_list_store_set (widget->priv->list_store,
595
CHOOSER_ITEM_IS_VISIBLE_COLUMN, is_visible,
600
update_chooser_visibility (GdmChooserWidget *widget)
602
if (gdm_chooser_widget_get_number_of_items (widget) > 0) {
603
gtk_widget_show (widget->priv->frame);
605
gtk_widget_hide (widget->priv->frame);
607
g_object_notify (G_OBJECT (widget), "list-visible");
611
set_inactive_items_visible (GdmChooserWidget *widget,
612
gboolean should_show)
615
char *active_item_id;
616
GtkTreeIter active_item_iter;
619
model = GTK_TREE_MODEL (widget->priv->list_store);
621
if (!gtk_tree_model_get_iter_first (model, &iter)) {
625
active_item_id = get_active_item_id (widget, &active_item_iter);
631
if (active_item_id != NULL) {
634
gtk_tree_model_get (model, &iter,
635
CHOOSER_ID_COLUMN, &id, -1);
637
if (strcmp (active_item_id, id) == 0) {
639
g_free (active_item_id);
640
active_item_id = NULL;
646
gtk_list_store_set (widget->priv->list_store,
648
CHOOSER_ITEM_IS_VISIBLE_COLUMN, should_show,
651
gtk_list_store_set (widget->priv->list_store,
653
CHOOSER_ITEM_IS_VISIBLE_COLUMN, TRUE,
656
} while (gtk_tree_model_iter_next (model, &iter));
658
g_free (active_item_id);
660
update_separator_visibility (widget);
664
on_shrink_animation_complete (GdmScrollableWidget *scrollable_widget,
665
GdmChooserWidget *widget)
667
g_assert (widget->priv->state == GDM_CHOOSER_WIDGET_STATE_SHRINKING);
669
g_debug ("GdmChooserWidget: shrink complete");
671
widget->priv->active_row_normalized_position = 0.5;
672
set_inactive_items_visible (GDM_CHOOSER_WIDGET (widget), FALSE);
673
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (widget->priv->items_view), FALSE);
674
widget->priv->state = GDM_CHOOSER_WIDGET_STATE_SHRUNK;
676
update_separator_visibility (widget);
678
if (widget->priv->emit_activated_after_resize_animation) {
679
g_signal_emit (widget, signals[ACTIVATED], 0);
680
widget->priv->emit_activated_after_resize_animation = FALSE;
685
get_height_of_row_at_path (GdmChooserWidget *widget,
690
gtk_tree_view_get_background_area (GTK_TREE_VIEW (widget->priv->items_view),
697
get_normalized_position_of_row_at_path (GdmChooserWidget *widget,
700
GdkRectangle area_of_row_at_path;
701
GdkRectangle area_of_visible_rows;
703
gtk_tree_view_get_background_area (GTK_TREE_VIEW (widget->priv->items_view),
704
path, NULL, &area_of_row_at_path);
706
gtk_tree_view_convert_tree_to_widget_coords (GTK_TREE_VIEW (widget->priv->items_view),
707
area_of_visible_rows.x,
708
area_of_visible_rows.y,
709
&area_of_visible_rows.x,
710
&area_of_visible_rows.y);
711
return CLAMP (((double) area_of_row_at_path.y) / widget->priv->items_view->allocation.height, 0.0, 1.0);
715
start_shrink_animation (GdmChooserWidget *widget)
717
GtkTreePath *active_row_path;
718
int active_row_height;
719
int number_of_visible_rows;
721
g_assert (widget->priv->active_row != NULL);
723
number_of_visible_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (widget->priv->model_sorter), NULL);
725
if (number_of_visible_rows <= 1) {
726
on_shrink_animation_complete (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget),
731
active_row_path = get_view_path_to_active_row (widget);
732
active_row_height = get_height_of_row_at_path (widget, active_row_path);
733
widget->priv->active_row_normalized_position = get_normalized_position_of_row_at_path (widget, active_row_path);
734
gtk_tree_path_free (active_row_path);
736
gdm_scrollable_widget_slide_to_height (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget),
738
(GdmScrollableWidgetSlideStepFunc)
739
on_shrink_animation_step, widget,
740
(GdmScrollableWidgetSlideDoneFunc)
741
on_shrink_animation_complete, widget);
745
get_first_item (GdmChooserWidget *widget)
751
model = GTK_TREE_MODEL (widget->priv->list_store);
753
if (!gtk_tree_model_get_iter_first (model, &iter)) {
754
g_assert_not_reached ();
757
gtk_tree_model_get (model, &iter,
758
CHOOSER_ID_COLUMN, &id, -1);
763
activate_if_one_item (GdmChooserWidget *widget)
767
g_debug ("GdmChooserWidget: attempting to activate single item");
769
if (gdm_chooser_widget_get_number_of_items (widget) != 1) {
770
g_debug ("GdmChooserWidget: unable to activate single item - has %d items", gdm_chooser_widget_get_number_of_items (widget));
774
id = get_first_item (widget);
776
gdm_chooser_widget_set_active_item (widget, id);
784
_grab_focus (GtkWidget *widget)
786
GtkWidget *foc_widget;
788
foc_widget = GDM_CHOOSER_WIDGET (widget)->priv->items_view;
789
g_debug ("GdmChooserWidget: grabbing focus");
790
if (! GTK_WIDGET_REALIZED (foc_widget)) {
791
g_debug ("GdmChooserWidget: not grabbing focus - not realized");
795
if (GTK_WIDGET_HAS_FOCUS (foc_widget)) {
796
g_debug ("GdmChooserWidget: not grabbing focus - already has it");
800
gtk_widget_grab_focus (foc_widget);
804
on_grow_animation_complete (GdmScrollableWidget *scrollable_widget,
805
GdmChooserWidget *widget)
807
g_assert (widget->priv->state == GDM_CHOOSER_WIDGET_STATE_GROWING);
808
widget->priv->state = GDM_CHOOSER_WIDGET_STATE_GROWN;
809
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (widget->priv->items_view), TRUE);
811
_grab_focus (GTK_WIDGET (widget));
815
get_number_of_on_screen_rows (GdmChooserWidget *widget)
817
GtkTreePath *start_path;
818
GtkTreePath *end_path;
823
if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (widget->priv->items_view),
824
&start_path, &end_path)) {
828
start_index = gtk_tree_path_get_indices (start_path);
829
end_index = gtk_tree_path_get_indices (end_path);
831
number_of_rows = *end_index - *start_index + 1;
833
gtk_tree_path_free (start_path);
834
gtk_tree_path_free (end_path);
836
return number_of_rows;
840
on_grow_animation_step (GdmScrollableWidget *scrollable_widget,
843
GdmChooserWidget *widget)
845
int number_of_visible_rows;
846
int number_of_on_screen_rows;
848
number_of_visible_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (widget->priv->model_sorter), NULL);
849
number_of_on_screen_rows = get_number_of_on_screen_rows (widget);
851
*new_height = GTK_BIN (scrollable_widget)->child->requisition.height;
855
start_grow_animation (GdmChooserWidget *widget)
857
set_inactive_items_visible (widget, TRUE);
859
gdm_scrollable_widget_slide_to_height (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget),
860
GTK_BIN (widget->priv->scrollable_widget)->child->requisition.height,
861
(GdmScrollableWidgetSlideStepFunc)
862
on_grow_animation_step, widget,
863
(GdmScrollableWidgetSlideDoneFunc)
864
on_grow_animation_complete, widget);
868
skip_resize_animation (GdmChooserWidget *widget)
870
if (widget->priv->state == GDM_CHOOSER_WIDGET_STATE_SHRINKING) {
871
set_inactive_items_visible (GDM_CHOOSER_WIDGET (widget), FALSE);
872
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (widget->priv->items_view), FALSE);
873
widget->priv->state = GDM_CHOOSER_WIDGET_STATE_SHRUNK;
874
} else if (widget->priv->state == GDM_CHOOSER_WIDGET_STATE_GROWING) {
875
set_inactive_items_visible (GDM_CHOOSER_WIDGET (widget), TRUE);
876
gtk_tree_view_set_enable_search (GTK_TREE_VIEW (widget->priv->items_view), TRUE);
877
widget->priv->state = GDM_CHOOSER_WIDGET_STATE_GROWN;
878
_grab_focus (GTK_WIDGET (widget));
883
gdm_chooser_widget_grow (GdmChooserWidget *widget)
885
if (widget->priv->state == GDM_CHOOSER_WIDGET_STATE_SHRINKING) {
886
gdm_scrollable_widget_stop_sliding (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget));
889
gtk_alignment_set (GTK_ALIGNMENT (widget->priv->frame_alignment),
892
set_frame_text (widget, widget->priv->inactive_text);
894
widget->priv->state = GDM_CHOOSER_WIDGET_STATE_GROWING;
896
if (GTK_WIDGET_VISIBLE (widget)) {
897
start_grow_animation (widget);
899
skip_resize_animation (widget);
904
move_cursor_to_top (GdmChooserWidget *widget)
910
model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget->priv->items_view));
911
path = gtk_tree_path_new_first ();
912
if (gtk_tree_model_get_iter (model, &iter, path)) {
913
gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget->priv->items_view),
918
gtk_tree_path_free (path);
922
clear_selection (GdmChooserWidget *widget)
924
GtkTreeSelection *selection;
927
g_debug ("GdmChooserWidget: clearing selection");
929
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->items_view));
930
gtk_tree_selection_unselect_all (selection);
932
window = gtk_widget_get_ancestor (GTK_WIDGET (widget), GTK_TYPE_WINDOW);
934
if (window != NULL) {
935
gtk_window_set_focus (GTK_WINDOW (window), NULL);
942
gdm_chooser_widget_shrink (GdmChooserWidget *widget)
944
g_assert (widget->priv->should_hide_inactive_items == TRUE);
946
if (widget->priv->state == GDM_CHOOSER_WIDGET_STATE_GROWING) {
947
gdm_scrollable_widget_stop_sliding (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget));
950
set_frame_text (widget, widget->priv->active_text);
952
gtk_alignment_set (GTK_ALIGNMENT (widget->priv->frame_alignment),
955
clear_selection (widget);
957
widget->priv->state = GDM_CHOOSER_WIDGET_STATE_SHRINKING;
959
if (GTK_WIDGET_VISIBLE (widget)) {
960
start_shrink_animation (widget);
962
skip_resize_animation (widget);
967
activate_from_row (GdmChooserWidget *widget,
968
GtkTreeRowReference *row)
970
g_assert (row != NULL);
971
g_assert (gtk_tree_row_reference_valid (row));
973
if (widget->priv->active_row != NULL) {
974
gtk_tree_row_reference_free (widget->priv->active_row);
975
widget->priv->active_row = NULL;
978
widget->priv->active_row = gtk_tree_row_reference_copy (row);
980
if (widget->priv->should_hide_inactive_items) {
981
g_debug ("GdmChooserWidget: will emit activated after resize");
982
widget->priv->emit_activated_after_resize_animation = TRUE;
983
gdm_chooser_widget_shrink (widget);
985
g_debug ("GdmChooserWidget: emitting activated");
986
g_signal_emit (widget, signals[ACTIVATED], 0);
991
deactivate (GdmChooserWidget *widget)
995
if (widget->priv->active_row == NULL) {
999
path = get_view_path_to_active_row (widget);
1001
gtk_tree_row_reference_free (widget->priv->active_row);
1002
widget->priv->active_row = NULL;
1004
gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget->priv->items_view),
1006
gtk_tree_path_free (path);
1008
if (widget->priv->state != GDM_CHOOSER_WIDGET_STATE_GROWN) {
1009
gdm_chooser_widget_grow (widget);
1012
g_signal_emit (widget, signals[DEACTIVATED], 0);
1016
gdm_chooser_widget_activate_selected_item (GdmChooserWidget *widget)
1018
GtkTreeRowReference *row;
1019
gboolean is_already_active;
1021
GtkTreeModel *model;
1024
model = GTK_TREE_MODEL (widget->priv->list_store);
1025
is_already_active = FALSE;
1029
get_selected_list_path (widget, &path);
1031
g_debug ("GdmChooserWidget: no row selected");
1035
if (widget->priv->active_row != NULL) {
1036
GtkTreePath *active_path;
1038
active_path = gtk_tree_row_reference_get_path (widget->priv->active_row);
1040
if (gtk_tree_path_compare (path, active_path) == 0) {
1041
is_already_active = TRUE;
1043
gtk_tree_path_free (active_path);
1045
g_assert (path != NULL);
1046
row = gtk_tree_row_reference_new (model, path);
1047
gtk_tree_path_free (path);
1049
if (!is_already_active) {
1050
activate_from_row (widget, row);
1052
g_debug ("GdmChooserWidget: row is already active");
1054
gtk_tree_row_reference_free (row);
1058
gdm_chooser_widget_set_active_item (GdmChooserWidget *widget,
1061
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
1063
g_debug ("GdmChooserWidget: setting active item '%s'",
1064
id ? id : "(null)");
1067
activate_from_item_id (widget, id);
1069
deactivate (widget);
1074
gdm_chooser_widget_set_property (GObject *object,
1076
const GValue *value,
1079
GdmChooserWidget *self;
1081
self = GDM_CHOOSER_WIDGET (object);
1085
case PROP_INACTIVE_TEXT:
1086
g_free (self->priv->inactive_text);
1087
self->priv->inactive_text = g_value_dup_string (value);
1089
if (self->priv->active_row == NULL) {
1090
set_frame_text (self, self->priv->inactive_text);
1094
case PROP_ACTIVE_TEXT:
1095
g_free (self->priv->active_text);
1096
self->priv->active_text = g_value_dup_string (value);
1098
if (self->priv->active_row != NULL) {
1099
set_frame_text (self, self->priv->active_text);
1104
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1110
gdm_chooser_widget_get_property (GObject *object,
1115
GdmChooserWidget *self;
1117
self = GDM_CHOOSER_WIDGET (object);
1120
case PROP_INACTIVE_TEXT:
1121
g_value_set_string (value, self->priv->inactive_text);
1124
case PROP_ACTIVE_TEXT:
1125
g_value_set_string (value, self->priv->active_text);
1127
case PROP_LIST_VISIBLE:
1128
g_value_set_boolean (value, GTK_WIDGET_VISIBLE (self->priv->scrollable_widget));
1131
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1137
gdm_chooser_widget_dispose (GObject *object)
1139
GdmChooserWidget *widget;
1141
widget = GDM_CHOOSER_WIDGET (object);
1143
if (widget->priv->separator_row != NULL) {
1144
gtk_tree_row_reference_free (widget->priv->separator_row);
1145
widget->priv->separator_row = NULL;
1148
if (widget->priv->active_row != NULL) {
1149
gtk_tree_row_reference_free (widget->priv->active_row);
1150
widget->priv->active_row = NULL;
1153
if (widget->priv->inactive_text != NULL) {
1154
g_free (widget->priv->inactive_text);
1155
widget->priv->inactive_text = NULL;
1158
if (widget->priv->active_text != NULL) {
1159
g_free (widget->priv->active_text);
1160
widget->priv->active_text = NULL;
1163
if (widget->priv->in_use_message != NULL) {
1164
g_free (widget->priv->in_use_message);
1165
widget->priv->in_use_message = NULL;
1168
G_OBJECT_CLASS (gdm_chooser_widget_parent_class)->dispose (object);
1172
gdm_chooser_widget_hide (GtkWidget *widget)
1174
skip_resize_animation (GDM_CHOOSER_WIDGET (widget));
1175
GTK_WIDGET_CLASS (gdm_chooser_widget_parent_class)->hide (widget);
1179
gdm_chooser_widget_show (GtkWidget *widget)
1181
skip_resize_animation (GDM_CHOOSER_WIDGET (widget));
1183
GTK_WIDGET_CLASS (gdm_chooser_widget_parent_class)->show (widget);
1187
gdm_chooser_widget_size_allocate (GtkWidget *widget,
1188
GtkAllocation *allocation)
1190
GdmChooserWidget *chooser_widget;
1192
GTK_WIDGET_CLASS (gdm_chooser_widget_parent_class)->size_allocate (widget, allocation);
1194
chooser_widget = GDM_CHOOSER_WIDGET (widget);
1199
gdm_chooser_widget_focus (GtkWidget *widget,
1200
GtkDirectionType direction)
1202
/* Since we only have one focusable child (the tree view),
1203
* no matter which direction we're going the rules are the
1206
* 1) if it's aready got focus, return FALSE to surrender
1208
* 2) if it doesn't already have focus, then grab it
1210
if (GTK_CONTAINER (widget)->focus_child != NULL) {
1211
g_debug ("GdmChooserWidget: not focusing - focus child not null");
1215
_grab_focus (widget);
1221
gdm_chooser_widget_focus_in_event (GtkWidget *widget,
1222
GdkEventFocus *focus_event)
1224
/* We don't ever want the chooser widget itself to have focus.
1225
* Focus should always go to the tree view.
1227
_grab_focus (widget);
1233
gdm_chooser_widget_class_init (GdmChooserWidgetClass *klass)
1235
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1236
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1238
object_class->get_property = gdm_chooser_widget_get_property;
1239
object_class->set_property = gdm_chooser_widget_set_property;
1240
object_class->dispose = gdm_chooser_widget_dispose;
1241
object_class->finalize = gdm_chooser_widget_finalize;
1242
widget_class->size_allocate = gdm_chooser_widget_size_allocate;
1243
widget_class->hide = gdm_chooser_widget_hide;
1244
widget_class->show = gdm_chooser_widget_show;
1245
widget_class->focus = gdm_chooser_widget_focus;
1246
widget_class->focus_in_event = gdm_chooser_widget_focus_in_event;
1248
signals [LOADED] = g_signal_new ("loaded",
1249
G_TYPE_FROM_CLASS (object_class),
1251
G_STRUCT_OFFSET (GdmChooserWidgetClass, loaded),
1254
g_cclosure_marshal_VOID__VOID,
1258
signals [ACTIVATED] = g_signal_new ("activated",
1259
G_TYPE_FROM_CLASS (object_class),
1261
G_STRUCT_OFFSET (GdmChooserWidgetClass, activated),
1264
g_cclosure_marshal_VOID__VOID,
1268
signals [DEACTIVATED] = g_signal_new ("deactivated",
1269
G_TYPE_FROM_CLASS (object_class),
1271
G_STRUCT_OFFSET (GdmChooserWidgetClass, deactivated),
1274
g_cclosure_marshal_VOID__VOID,
1278
g_object_class_install_property (object_class,
1280
g_param_spec_string ("inactive-text",
1282
_("The text to use in the label if the "
1283
"user hasn't picked an item yet"),
1285
(G_PARAM_READWRITE |
1286
G_PARAM_CONSTRUCT)));
1287
g_object_class_install_property (object_class,
1289
g_param_spec_string ("active-text",
1291
_("The text to use in the label if the "
1292
"user has picked an item"),
1294
(G_PARAM_READWRITE |
1295
G_PARAM_CONSTRUCT)));
1297
g_object_class_install_property (object_class,
1299
g_param_spec_boolean ("list-visible",
1301
_("Whether the chooser list is visible"),
1305
g_type_class_add_private (klass, sizeof (GdmChooserWidgetPrivate));
1309
on_row_activated (GtkTreeView *tree_view,
1310
GtkTreePath *tree_path,
1311
GtkTreeViewColumn *tree_column,
1312
GdmChooserWidget *widget)
1316
path_str = gtk_tree_path_to_string (tree_path);
1317
g_debug ("GdmChooserWidget: row activated '%s'", path_str ? path_str : "(null)");
1319
gdm_chooser_widget_activate_selected_item (widget);
1323
path_is_separator (GdmChooserWidget *widget,
1324
GtkTreeModel *model,
1327
GtkTreePath *separator_path;
1328
GtkTreePath *translated_path;
1329
gboolean is_separator;
1331
separator_path = gtk_tree_row_reference_get_path (widget->priv->separator_row);
1333
if (separator_path == NULL) {
1337
if (model == GTK_TREE_MODEL (widget->priv->model_sorter)) {
1338
GtkTreePath *filtered_path;
1340
filtered_path = gtk_tree_model_sort_convert_path_to_child_path (widget->priv->model_sorter, path);
1342
translated_path = gtk_tree_model_filter_convert_path_to_child_path (widget->priv->model_filter, filtered_path);
1343
gtk_tree_path_free (filtered_path);
1344
} else if (model == GTK_TREE_MODEL (widget->priv->model_filter)) {
1345
translated_path = gtk_tree_model_filter_convert_path_to_child_path (widget->priv->model_filter, path);
1347
g_assert (model == GTK_TREE_MODEL (widget->priv->list_store));
1348
translated_path = gtk_tree_path_copy (path);
1351
if (gtk_tree_path_compare (separator_path, translated_path) == 0) {
1352
is_separator = TRUE;
1354
is_separator = FALSE;
1356
gtk_tree_path_free (translated_path);
1358
return is_separator;
1362
compare_item (GtkTreeModel *model,
1367
GdmChooserWidget *widget;
1374
gboolean is_separate_a;
1375
gboolean is_separate_b;
1378
GtkTreeIter *separator_iter;
1380
PangoAttrList *attrs;
1382
g_assert (GDM_IS_CHOOSER_WIDGET (data));
1384
widget = GDM_CHOOSER_WIDGET (data);
1386
separator_iter = NULL;
1387
if (widget->priv->separator_row != NULL) {
1389
GtkTreePath *path_a;
1390
GtkTreePath *path_b;
1392
path_a = gtk_tree_model_get_path (model, a);
1393
path_b = gtk_tree_model_get_path (model, b);
1395
if (path_is_separator (widget, model, path_a)) {
1397
} else if (path_is_separator (widget, model, path_b)) {
1401
gtk_tree_path_free (path_a);
1402
gtk_tree_path_free (path_b);
1406
is_separate_a = FALSE;
1407
if (separator_iter != a) {
1408
gtk_tree_model_get (model, a,
1409
CHOOSER_NAME_COLUMN, &name_a,
1410
CHOOSER_PRIORITY_COLUMN, &prio_a,
1411
CHOOSER_ITEM_IS_SEPARATED_COLUMN, &is_separate_a,
1416
is_separate_b = FALSE;
1417
if (separator_iter != b) {
1418
gtk_tree_model_get (model, b,
1419
CHOOSER_NAME_COLUMN, &name_b,
1420
CHOOSER_ID_COLUMN, &id,
1421
CHOOSER_PRIORITY_COLUMN, &prio_b,
1422
CHOOSER_ITEM_IS_SEPARATED_COLUMN, &is_separate_b,
1426
if (widget->priv->separator_position == GDM_CHOOSER_WIDGET_POSITION_TOP) {
1432
if (separator_iter == b) {
1433
result = is_separate_a? 1 : -1;
1434
result *= direction;
1435
} else if (separator_iter == a) {
1436
result = is_separate_b? -1 : 1;
1437
result *= direction;
1438
} else if (is_separate_b == is_separate_a) {
1439
if (prio_a == prio_b) {
1440
pango_parse_markup (name_a, -1, 0, &attrs, &text_a, NULL, NULL);
1441
pango_parse_markup (name_b, -1, 0, &attrs, &text_b, NULL, NULL);
1442
if (text_a && text_b)
1443
result = g_utf8_collate (text_a, text_b);
1445
result = g_utf8_collate (name_a, name_b);
1448
} else if (prio_a > prio_b) {
1454
result = is_separate_a - is_separate_b;
1455
result *= direction;
1465
name_cell_data_func (GtkTreeViewColumn *tree_column,
1466
GtkCellRenderer *cell,
1467
GtkTreeModel *model,
1469
GdmChooserWidget *widget)
1476
gtk_tree_model_get (model,
1478
CHOOSER_ITEM_IS_IN_USE_COLUMN, &is_in_use,
1479
CHOOSER_NAME_COLUMN, &name,
1483
markup = g_strdup_printf ("<b>%s</b>\n"
1484
"<i><span size=\"x-small\">%s</span></i>",
1485
name ? name : "(null)", widget->priv->in_use_message);
1487
markup = g_strdup_printf ("%s", name ? name : "(null)");
1491
g_object_set (cell, "markup", markup, NULL);
1496
check_cell_data_func (GtkTreeViewColumn *tree_column,
1497
GtkCellRenderer *cell,
1498
GtkTreeModel *model,
1500
GdmChooserWidget *widget)
1505
gtk_tree_model_get (model,
1507
CHOOSER_ITEM_IS_IN_USE_COLUMN, &is_in_use,
1511
pixbuf = widget->priv->is_in_use_pixbuf;
1516
g_object_set (cell, "pixbuf", pixbuf, NULL);
1520
get_is_in_use_pixbuf (GdmChooserWidget *widget)
1522
GtkIconTheme *theme;
1525
theme = gtk_icon_theme_get_default ();
1526
pixbuf = gtk_icon_theme_load_icon (theme,
1528
GDM_CHOOSER_WIDGET_DEFAULT_ICON_SIZE / 3,
1536
separator_func (GtkTreeModel *model,
1540
GdmChooserWidget *widget;
1542
gboolean is_separator;
1544
g_assert (GDM_IS_CHOOSER_WIDGET (data));
1546
widget = GDM_CHOOSER_WIDGET (data);
1548
g_assert (widget->priv->separator_row != NULL);
1550
path = gtk_tree_model_get_path (model, iter);
1552
is_separator = path_is_separator (widget, model, path);
1554
gtk_tree_path_free (path);
1556
return is_separator;
1560
add_separator (GdmChooserWidget *widget)
1563
GtkTreeModel *model;
1566
g_assert (widget->priv->separator_row == NULL);
1568
model = GTK_TREE_MODEL (widget->priv->list_store);
1570
gtk_list_store_insert_with_values (widget->priv->list_store,
1572
CHOOSER_ID_COLUMN, "-", -1);
1573
path = gtk_tree_model_get_path (model, &iter);
1574
widget->priv->separator_row = gtk_tree_row_reference_new (model, path);
1575
gtk_tree_path_free (path);
1579
update_column_visibility (GdmChooserWidget *widget)
1581
if (widget->priv->number_of_rows_with_images > 0) {
1582
gtk_tree_view_column_set_visible (widget->priv->image_column,
1585
gtk_tree_view_column_set_visible (widget->priv->image_column,
1588
if (widget->priv->number_of_rows_with_status > 0) {
1589
gtk_tree_view_column_set_visible (widget->priv->status_column,
1592
gtk_tree_view_column_set_visible (widget->priv->status_column,
1600
clear_canceled_visibility_update (GdmChooserWidget *widget)
1602
widget->priv->update_idle_id = 0;
1606
queue_column_visibility_update (GdmChooserWidget *widget)
1608
if (widget->priv->update_idle_id == 0) {
1609
widget->priv->update_idle_id =
1610
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1612
update_column_visibility, widget,
1614
clear_canceled_visibility_update);
1619
on_row_changed (GtkTreeModel *model,
1622
GdmChooserWidget *widget)
1624
queue_column_visibility_update (widget);
1628
add_frame (GdmChooserWidget *widget)
1630
widget->priv->frame = gtk_frame_new (NULL);
1631
gtk_frame_set_shadow_type (GTK_FRAME (widget->priv->frame),
1633
gtk_widget_show (widget->priv->frame);
1634
gtk_container_add (GTK_CONTAINER (widget), widget->priv->frame);
1636
widget->priv->frame_alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
1637
gtk_widget_show (widget->priv->frame_alignment);
1638
gtk_container_add (GTK_CONTAINER (widget->priv->frame),
1639
widget->priv->frame_alignment);
1643
on_button_release (GtkTreeView *items_view,
1644
GdkEventButton *event,
1645
GdmChooserWidget *widget)
1647
GtkTreeModel *model;
1649
GtkTreeSelection *selection;
1651
if (!widget->priv->should_hide_inactive_items) {
1655
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->items_view));
1656
if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1659
path = gtk_tree_model_get_path (model, &iter);
1660
gtk_tree_view_row_activated (GTK_TREE_VIEW (items_view),
1662
gtk_tree_path_free (path);
1669
search_equal_func (GtkTreeModel *model,
1673
GdmChooserWidget *widget)
1688
key_folded = g_utf8_casefold (key, -1);
1690
gtk_tree_model_get (model,
1692
CHOOSER_ID_COLUMN, &id,
1693
CHOOSER_NAME_COLUMN, &name,
1698
name_folded = g_utf8_casefold (name, -1);
1699
ret = !g_str_has_prefix (name_folded, key_folded);
1700
g_free (name_folded);
1711
id_folded = g_utf8_casefold (id, -1);
1712
ret = !g_str_has_prefix (id_folded, key_folded);
1722
g_free (key_folded);
1728
search_position_func (GtkTreeView *tree_view,
1729
GtkWidget *search_dialog,
1732
/* Move it outside the region viewable by
1734
* FIXME: This is pretty inelegant.
1736
* It might be nicer to make a GdmOffscreenBin
1737
* widget that we pack into the chooser widget below
1738
* the frame but gets redirected offscreen.
1740
* Then we would add a GtkEntry to the bin and set
1741
* that entry as the search entry for the tree view
1742
* instead of using a search position func.
1744
gtk_window_move (GTK_WINDOW (search_dialog), -24000, -24000);
1748
on_selection_changed (GtkTreeSelection *selection,
1749
GdmChooserWidget *widget)
1753
get_selected_list_path (widget, &path);
1756
path_str = gtk_tree_path_to_string (path);
1757
g_debug ("GdmChooserWidget: selection change to list path '%s'", path_str);
1760
g_debug ("GdmChooserWidget: selection cleared");
1765
gdm_chooser_widget_init (GdmChooserWidget *widget)
1767
GtkTreeViewColumn *column;
1768
GtkTreeSelection *selection;
1769
GtkCellRenderer *renderer;
1771
widget->priv = GDM_CHOOSER_WIDGET_GET_PRIVATE (widget);
1773
/* Even though, we're a container and also don't ever take
1774
* focus for ourselve, we set CAN_FOCUS so that gtk_widget_grab_focus
1775
* works on us. We then override grab_focus requests to
1776
* be redirected our internal tree view
1778
GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
1780
gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 0, 0);
1784
widget->priv->scrollable_widget = gdm_scrollable_widget_new ();
1785
gtk_widget_show (widget->priv->scrollable_widget);
1786
gtk_container_add (GTK_CONTAINER (widget->priv->frame_alignment),
1787
widget->priv->scrollable_widget);
1789
widget->priv->items_view = gtk_tree_view_new ();
1790
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget->priv->items_view),
1792
g_signal_connect (widget->priv->items_view,
1794
G_CALLBACK (on_row_activated),
1797
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (widget->priv->items_view),
1798
(GtkTreeViewSearchEqualFunc)search_equal_func,
1802
gtk_tree_view_set_search_position_func (GTK_TREE_VIEW (widget->priv->items_view),
1803
(GtkTreeViewSearchPositionFunc)search_position_func,
1807
/* hack to make single-click activate work
1809
g_signal_connect_after (widget->priv->items_view,
1810
"button-release-event",
1811
G_CALLBACK (on_button_release),
1814
gtk_widget_show (widget->priv->items_view);
1815
gtk_container_add (GTK_CONTAINER (widget->priv->scrollable_widget),
1816
widget->priv->items_view);
1818
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget->priv->items_view));
1819
gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1821
g_signal_connect (selection, "changed", G_CALLBACK (on_selection_changed), widget);
1823
g_assert (NUMBER_OF_CHOOSER_COLUMNS == 11);
1824
widget->priv->list_store = gtk_list_store_new (NUMBER_OF_CHOOSER_COLUMNS,
1837
widget->priv->model_filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (widget->priv->list_store), NULL));
1839
gtk_tree_model_filter_set_visible_column (widget->priv->model_filter,
1840
CHOOSER_ITEM_IS_VISIBLE_COLUMN);
1841
g_signal_connect (G_OBJECT (widget->priv->model_filter), "row-changed",
1842
G_CALLBACK (on_row_changed), widget);
1844
widget->priv->model_sorter = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (widget->priv->model_filter)));
1846
gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (widget->priv->model_sorter),
1851
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (widget->priv->model_sorter),
1853
GTK_SORT_ASCENDING);
1854
gtk_tree_view_set_model (GTK_TREE_VIEW (widget->priv->items_view),
1855
GTK_TREE_MODEL (widget->priv->model_sorter));
1856
gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (widget->priv->items_view),
1861
renderer = gtk_cell_renderer_pixbuf_new ();
1862
column = gtk_tree_view_column_new ();
1863
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1864
gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->items_view), column);
1865
widget->priv->image_column = column;
1867
gtk_tree_view_column_set_attributes (column,
1869
"pixbuf", CHOOSER_IMAGE_COLUMN,
1872
g_object_set (renderer,
1877
renderer = gtk_cell_renderer_text_new ();
1878
column = gtk_tree_view_column_new ();
1879
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1880
gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->items_view), column);
1881
gtk_tree_view_column_set_cell_data_func (column,
1883
(GtkTreeCellDataFunc) name_cell_data_func,
1887
gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (widget->priv->items_view),
1888
CHOOSER_COMMENT_COLUMN);
1891
renderer = gtk_cell_renderer_pixbuf_new ();
1892
column = gtk_tree_view_column_new ();
1893
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1894
gtk_tree_view_append_column (GTK_TREE_VIEW (widget->priv->items_view), column);
1895
widget->priv->status_column = column;
1897
gtk_tree_view_column_set_cell_data_func (column,
1899
(GtkTreeCellDataFunc) check_cell_data_func,
1902
widget->priv->is_in_use_pixbuf = get_is_in_use_pixbuf (widget);
1904
renderer = gdm_cell_renderer_timer_new ();
1905
gtk_tree_view_column_pack_start (column, renderer, FALSE);
1906
gtk_tree_view_column_add_attribute (column, renderer, "value",
1907
CHOOSER_TIMER_VALUE_COLUMN);
1909
widget->priv->rows_with_timers =
1910
g_hash_table_new_full (g_str_hash,
1912
(GDestroyNotify) g_free,
1914
gtk_tree_row_reference_free);
1916
add_separator (widget);
1917
queue_column_visibility_update (widget);
1921
gdm_chooser_widget_finalize (GObject *object)
1923
GdmChooserWidget *widget;
1925
g_return_if_fail (object != NULL);
1926
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (object));
1928
widget = GDM_CHOOSER_WIDGET (object);
1930
g_return_if_fail (widget->priv != NULL);
1932
g_hash_table_destroy (widget->priv->rows_with_timers);
1933
widget->priv->rows_with_timers = NULL;
1935
G_OBJECT_CLASS (gdm_chooser_widget_parent_class)->finalize (object);
1939
gdm_chooser_widget_new (const char *inactive_text,
1940
const char *active_text)
1944
object = g_object_new (GDM_TYPE_CHOOSER_WIDGET,
1945
"inactive-text", inactive_text,
1946
"active-text", active_text, NULL);
1948
return GTK_WIDGET (object);
1952
gdm_chooser_widget_update_item (GdmChooserWidget *widget,
1954
GdkPixbuf *new_image,
1955
const char *new_name,
1956
const char *new_comment,
1957
gulong new_priority,
1958
gboolean new_in_use,
1959
gboolean new_is_separate)
1961
GtkTreeModel *model;
1964
gboolean is_separate;
1967
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
1969
model = GTK_TREE_MODEL (widget->priv->list_store);
1971
if (!find_item (widget, id, &iter)) {
1972
g_critical ("Tried to remove non-existing item from chooser");
1976
is_separate = FALSE;
1977
gtk_tree_model_get (model, &iter,
1978
CHOOSER_IMAGE_COLUMN, &image,
1979
CHOOSER_ITEM_IS_IN_USE_COLUMN, &in_use,
1980
CHOOSER_ITEM_IS_SEPARATED_COLUMN, &is_separate,
1983
if (image != new_image) {
1984
if (image == NULL && new_image != NULL) {
1985
widget->priv->number_of_rows_with_images++;
1986
} else if (image != NULL && new_image == NULL) {
1987
widget->priv->number_of_rows_with_images--;
1989
queue_column_visibility_update (widget);
1991
if (image != NULL) {
1992
g_object_unref (image);
1995
if (in_use != new_in_use) {
1997
widget->priv->number_of_rows_with_status++;
1999
widget->priv->number_of_rows_with_status--;
2001
queue_column_visibility_update (widget);
2004
if (is_separate != new_is_separate) {
2005
if (new_is_separate) {
2006
widget->priv->number_of_separated_rows++;
2007
widget->priv->number_of_normal_rows--;
2009
widget->priv->number_of_separated_rows--;
2010
widget->priv->number_of_normal_rows++;
2012
update_separator_visibility (widget);
2015
gtk_list_store_set (widget->priv->list_store,
2017
CHOOSER_IMAGE_COLUMN, new_image,
2018
CHOOSER_NAME_COLUMN, new_name,
2019
CHOOSER_COMMENT_COLUMN, new_comment,
2020
CHOOSER_PRIORITY_COLUMN, new_priority,
2021
CHOOSER_ITEM_IS_IN_USE_COLUMN, new_in_use,
2022
CHOOSER_ITEM_IS_SEPARATED_COLUMN, new_is_separate,
2027
gdm_chooser_widget_add_item (GdmChooserWidget *widget,
2031
const char *comment,
2034
gboolean keep_separate)
2036
gboolean is_visible;
2038
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2040
if (keep_separate) {
2041
widget->priv->number_of_separated_rows++;
2043
widget->priv->number_of_normal_rows++;
2045
update_separator_visibility (widget);
2048
widget->priv->number_of_rows_with_status++;
2049
queue_column_visibility_update (widget);
2052
if (image != NULL) {
2053
widget->priv->number_of_rows_with_images++;
2054
queue_column_visibility_update (widget);
2057
is_visible = widget->priv->active_row == NULL;
2059
gtk_list_store_insert_with_values (widget->priv->list_store,
2061
CHOOSER_IMAGE_COLUMN, image,
2062
CHOOSER_NAME_COLUMN, name,
2063
CHOOSER_COMMENT_COLUMN, comment,
2064
CHOOSER_PRIORITY_COLUMN, priority,
2065
CHOOSER_ITEM_IS_IN_USE_COLUMN, in_use,
2066
CHOOSER_ITEM_IS_SEPARATED_COLUMN, keep_separate,
2067
CHOOSER_ITEM_IS_VISIBLE_COLUMN, is_visible,
2068
CHOOSER_ID_COLUMN, id,
2071
move_cursor_to_top (widget);
2072
update_chooser_visibility (widget);
2076
gdm_chooser_widget_remove_item (GdmChooserWidget *widget,
2079
GtkTreeModel *model;
2082
gboolean is_separate;
2085
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2087
model = GTK_TREE_MODEL (widget->priv->list_store);
2089
if (!find_item (widget, id, &iter)) {
2090
g_critical ("Tried to remove non-existing item from chooser");
2094
is_separate = FALSE;
2095
gtk_tree_model_get (model, &iter,
2096
CHOOSER_IMAGE_COLUMN, &image,
2097
CHOOSER_ITEM_IS_IN_USE_COLUMN, &is_in_use,
2098
CHOOSER_ITEM_IS_SEPARATED_COLUMN, &is_separate,
2101
if (image != NULL) {
2102
widget->priv->number_of_rows_with_images--;
2103
g_object_unref (image);
2107
widget->priv->number_of_rows_with_status--;
2108
queue_column_visibility_update (widget);
2112
widget->priv->number_of_separated_rows--;
2114
widget->priv->number_of_normal_rows--;
2116
update_separator_visibility (widget);
2118
gtk_list_store_remove (widget->priv->list_store, &iter);
2120
move_cursor_to_top (widget);
2121
update_chooser_visibility (widget);
2125
gdm_chooser_widget_lookup_item (GdmChooserWidget *widget,
2131
gboolean *is_in_use,
2132
gboolean *is_separate)
2135
char *active_item_id;
2137
g_return_val_if_fail (GDM_IS_CHOOSER_WIDGET (widget), FALSE);
2138
g_return_val_if_fail (id != NULL, FALSE);
2140
active_item_id = get_active_item_id (widget, &iter);
2142
if (active_item_id == NULL || strcmp (active_item_id, id) != 0) {
2143
g_free (active_item_id);
2145
if (!find_item (widget, id, &iter)) {
2149
g_free (active_item_id);
2151
if (image != NULL) {
2152
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2153
CHOOSER_IMAGE_COLUMN, image, -1);
2157
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2158
CHOOSER_NAME_COLUMN, name, -1);
2161
if (priority != NULL) {
2162
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2163
CHOOSER_PRIORITY_COLUMN, priority, -1);
2166
if (is_in_use != NULL) {
2167
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2168
CHOOSER_ITEM_IS_IN_USE_COLUMN, is_in_use, -1);
2171
if (is_separate != NULL) {
2172
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2173
CHOOSER_ITEM_IS_SEPARATED_COLUMN, is_separate, -1);
2180
gdm_chooser_widget_set_item_in_use (GdmChooserWidget *widget,
2185
gboolean was_in_use;
2187
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2189
if (!find_item (widget, id, &iter)) {
2193
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2194
CHOOSER_ITEM_IS_IN_USE_COLUMN, &was_in_use,
2197
if (was_in_use != is_in_use) {
2200
widget->priv->number_of_rows_with_status++;
2202
widget->priv->number_of_rows_with_status--;
2204
queue_column_visibility_update (widget);
2206
gtk_list_store_set (widget->priv->list_store,
2208
CHOOSER_ITEM_IS_IN_USE_COLUMN, is_in_use,
2215
gdm_chooser_widget_set_item_priority (GdmChooserWidget *widget,
2220
gulong was_priority;
2222
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2224
if (!find_item (widget, id, &iter)) {
2228
gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
2229
CHOOSER_PRIORITY_COLUMN, &was_priority,
2232
if (was_priority != priority) {
2234
gtk_list_store_set (widget->priv->list_store,
2236
CHOOSER_PRIORITY_COLUMN, priority,
2238
gtk_tree_model_filter_refilter (widget->priv->model_filter);
2243
get_current_time (void)
2245
const double microseconds_per_second = 1000000.0;
2249
g_get_current_time (&now);
2251
timestamp = ((microseconds_per_second * now.tv_sec) + now.tv_usec) /
2252
microseconds_per_second;
2258
on_timer_timeout (GdmChooserWidget *widget)
2260
GHashTableIter iter;
2268
g_hash_table_iter_init (&iter, widget->priv->rows_with_timers);
2269
while (g_hash_table_iter_next (&iter, &key, &value)) {
2270
list = g_slist_prepend (list, value);
2273
now = get_current_time ();
2274
for (tmp = list; tmp != NULL; tmp = tmp->next) {
2275
GtkTreeRowReference *row;
2277
row = (GtkTreeRowReference *) tmp->data;
2279
update_timer_from_time (widget, row, now);
2281
g_slist_free (list);
2287
start_timer (GdmChooserWidget *widget,
2288
GtkTreeRowReference *row,
2291
GtkTreeModel *model;
2295
model = GTK_TREE_MODEL (widget->priv->list_store);
2297
path = gtk_tree_row_reference_get_path (row);
2298
gtk_tree_model_get_iter (model, &iter, path);
2299
gtk_tree_path_free (path);
2301
gtk_list_store_set (widget->priv->list_store, &iter,
2302
CHOOSER_TIMER_START_TIME_COLUMN,
2303
get_current_time (), -1);
2304
gtk_list_store_set (widget->priv->list_store, &iter,
2305
CHOOSER_TIMER_DURATION_COLUMN,
2307
gtk_list_store_set (widget->priv->list_store, &iter,
2308
CHOOSER_TIMER_VALUE_COLUMN, 0.0, -1);
2310
widget->priv->number_of_active_timers++;
2311
if (widget->priv->timer_animation_timeout_id == 0) {
2312
g_assert (g_hash_table_size (widget->priv->rows_with_timers) == 1);
2314
widget->priv->timer_animation_timeout_id =
2315
g_timeout_add (1000 / 20,
2316
(GSourceFunc) on_timer_timeout,
2323
stop_timer (GdmChooserWidget *widget,
2324
GtkTreeRowReference *row)
2326
GtkTreeModel *model;
2330
model = GTK_TREE_MODEL (widget->priv->list_store);
2332
path = gtk_tree_row_reference_get_path (row);
2333
gtk_tree_model_get_iter (model, &iter, path);
2334
gtk_tree_path_free (path);
2336
gtk_list_store_set (widget->priv->list_store, &iter,
2337
CHOOSER_TIMER_START_TIME_COLUMN,
2339
gtk_list_store_set (widget->priv->list_store, &iter,
2340
CHOOSER_TIMER_DURATION_COLUMN,
2342
gtk_list_store_set (widget->priv->list_store, &iter,
2343
CHOOSER_TIMER_VALUE_COLUMN, 0.0, -1);
2345
widget->priv->number_of_active_timers--;
2346
if (widget->priv->number_of_active_timers == 0) {
2347
g_source_remove (widget->priv->timer_animation_timeout_id);
2348
widget->priv->timer_animation_timeout_id = 0;
2353
update_timer_from_time (GdmChooserWidget *widget,
2354
GtkTreeRowReference *row,
2357
GtkTreeModel *model;
2362
double elapsed_ratio;
2364
model = GTK_TREE_MODEL (widget->priv->list_store);
2366
path = gtk_tree_row_reference_get_path (row);
2367
gtk_tree_model_get_iter (model, &iter, path);
2368
gtk_tree_path_free (path);
2370
gtk_tree_model_get (model, &iter,
2371
CHOOSER_TIMER_START_TIME_COLUMN, &start_time,
2372
CHOOSER_TIMER_DURATION_COLUMN, &duration, -1);
2374
if (duration > G_MINDOUBLE) {
2375
elapsed_ratio = (now - start_time) / duration;
2377
elapsed_ratio = 0.0;
2380
gtk_list_store_set (widget->priv->list_store, &iter,
2381
CHOOSER_TIMER_VALUE_COLUMN,
2384
if (elapsed_ratio > .999) {
2387
stop_timer (widget, row);
2389
gtk_tree_model_get (model, &iter,
2390
CHOOSER_ID_COLUMN, &id, -1);
2391
g_hash_table_remove (widget->priv->rows_with_timers,
2395
widget->priv->number_of_rows_with_status--;
2396
queue_column_visibility_update (widget);
2401
gdm_chooser_widget_set_item_timer (GdmChooserWidget *widget,
2405
GtkTreeModel *model;
2406
GtkTreeRowReference *row;
2408
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2410
model = GTK_TREE_MODEL (widget->priv->list_store);
2412
row = g_hash_table_lookup (widget->priv->rows_with_timers,
2415
g_assert (row == NULL || gtk_tree_row_reference_valid (row));
2418
stop_timer (widget, row);
2423
g_warning ("could not find item with ID '%s' to "
2424
"remove timer", id);
2428
g_hash_table_remove (widget->priv->rows_with_timers,
2430
gtk_tree_model_filter_refilter (widget->priv->model_filter);
2438
if (!find_item (widget, id, &iter)) {
2439
g_warning ("could not find item with ID '%s' to "
2444
path = gtk_tree_model_get_path (model, &iter);
2445
row = gtk_tree_row_reference_new (model, path);
2447
g_hash_table_insert (widget->priv->rows_with_timers,
2448
g_strdup (id), row);
2450
widget->priv->number_of_rows_with_status++;
2451
queue_column_visibility_update (widget);
2454
start_timer (widget, row, timeout / 1000.0);
2458
gdm_chooser_widget_set_in_use_message (GdmChooserWidget *widget,
2459
const char *message)
2461
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2463
g_free (widget->priv->in_use_message);
2464
widget->priv->in_use_message = g_strdup (message);
2468
gdm_chooser_widget_set_separator_position (GdmChooserWidget *widget,
2469
GdmChooserWidgetPosition position)
2471
g_return_if_fail (GDM_IS_CHOOSER_WIDGET (widget));
2473
if (widget->priv->separator_position != position) {
2474
widget->priv->separator_position = position;
2477
gtk_tree_model_filter_refilter (widget->priv->model_filter);
2481
gdm_chooser_widget_set_hide_inactive_items (GdmChooserWidget *widget,
2482
gboolean should_hide)
2484
widget->priv->should_hide_inactive_items = should_hide;
2487
(widget->priv->state != GDM_CHOOSER_WIDGET_STATE_SHRUNK
2488
|| widget->priv->state != GDM_CHOOSER_WIDGET_STATE_SHRINKING) &&
2489
widget->priv->active_row != NULL) {
2490
gdm_chooser_widget_shrink (widget);
2491
} else if (!should_hide &&
2492
(widget->priv->state != GDM_CHOOSER_WIDGET_STATE_GROWN
2493
|| widget->priv->state != GDM_CHOOSER_WIDGET_STATE_GROWING)) {
2494
gdm_chooser_widget_grow (widget);
2499
gdm_chooser_widget_get_number_of_items (GdmChooserWidget *widget)
2501
return widget->priv->number_of_normal_rows +
2502
widget->priv->number_of_separated_rows;
2506
gdm_chooser_widget_activate_if_one_item (GdmChooserWidget *widget)
2508
activate_if_one_item (widget);
2512
gdm_chooser_widget_propagate_pending_key_events (GdmChooserWidget *widget)
2514
if (!gdm_scrollable_widget_has_queued_key_events (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget))) {
2518
gdm_scrollable_widget_replay_queued_key_events (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget));
2522
gdm_chooser_widget_loaded (GdmChooserWidget *widget)
2524
gdm_chooser_widget_grow (widget);
2525
g_signal_emit (widget, signals[LOADED], 0);