1 | /* testiconview-keynav.c |
2 | * Copyright (C) 2010 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | * |
17 | * Author: Matthias Clasen |
18 | */ |
19 | |
20 | /* |
21 | * This example demonstrates how to use the keynav-failed signal to |
22 | * extend arrow keynav over adjacent icon views. This can be used when |
23 | * grouping items. |
24 | */ |
25 | |
26 | #include <gtk/gtk.h> |
27 | |
28 | static GtkTreeModel * |
29 | get_model (void) |
30 | { |
31 | static GtkListStore *store; |
32 | GtkTreeIter iter; |
33 | |
34 | if (store) |
35 | return (GtkTreeModel *) g_object_ref (store); |
36 | |
37 | store = gtk_list_store_new (n_columns: 1, G_TYPE_STRING); |
38 | |
39 | gtk_list_store_append (list_store: store, iter: &iter); |
40 | gtk_list_store_set (list_store: store, iter: &iter, 0, "One" , -1); |
41 | gtk_list_store_append (list_store: store, iter: &iter); |
42 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Two" , -1); |
43 | gtk_list_store_append (list_store: store, iter: &iter); |
44 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Three" , -1); |
45 | gtk_list_store_append (list_store: store, iter: &iter); |
46 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Four" , -1); |
47 | gtk_list_store_append (list_store: store, iter: &iter); |
48 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Five" , -1); |
49 | gtk_list_store_append (list_store: store, iter: &iter); |
50 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Six" , -1); |
51 | gtk_list_store_append (list_store: store, iter: &iter); |
52 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Seven" , -1); |
53 | gtk_list_store_append (list_store: store, iter: &iter); |
54 | gtk_list_store_set (list_store: store, iter: &iter, 0, "Eight" , -1); |
55 | |
56 | return (GtkTreeModel *) store; |
57 | } |
58 | |
59 | static gboolean |
60 | visible_func (GtkTreeModel *model, |
61 | GtkTreeIter *iter, |
62 | gpointer data) |
63 | { |
64 | gboolean first = GPOINTER_TO_INT (data); |
65 | gboolean visible; |
66 | GtkTreePath *path; |
67 | |
68 | path = gtk_tree_model_get_path (tree_model: model, iter); |
69 | |
70 | if (gtk_tree_path_get_indices (path)[0] < 4) |
71 | visible = first; |
72 | else |
73 | visible = !first; |
74 | |
75 | gtk_tree_path_free (path); |
76 | |
77 | return visible; |
78 | } |
79 | |
80 | static GtkTreeModel * |
81 | get_filter_model (gboolean first) |
82 | { |
83 | GtkTreeModelFilter *model; |
84 | |
85 | model = (GtkTreeModelFilter *)gtk_tree_model_filter_new (child_model: get_model (), NULL); |
86 | |
87 | gtk_tree_model_filter_set_visible_func (filter: model, func: visible_func, GINT_TO_POINTER (first), NULL); |
88 | |
89 | return (GtkTreeModel *) model; |
90 | } |
91 | |
92 | static GtkWidget * |
93 | get_view (gboolean first) |
94 | { |
95 | GtkWidget *view; |
96 | |
97 | view = gtk_icon_view_new_with_model (model: get_filter_model (first)); |
98 | gtk_icon_view_set_text_column (GTK_ICON_VIEW (view), column: 0); |
99 | gtk_widget_set_size_request (widget: view, width: 0, height: -1); |
100 | |
101 | return view; |
102 | } |
103 | |
104 | typedef struct |
105 | { |
106 | GtkWidget *; |
107 | GtkWidget *view1; |
108 | GtkWidget *; |
109 | GtkWidget *view2; |
110 | } Views; |
111 | |
112 | static gboolean |
113 | keynav_failed (GtkWidget *view, |
114 | GtkDirectionType direction, |
115 | Views *views) |
116 | { |
117 | GtkTreePath *path; |
118 | GtkTreeModel *model; |
119 | GtkTreeIter iter; |
120 | int col; |
121 | GtkTreePath *sel; |
122 | |
123 | if (view == views->view1 && direction == GTK_DIR_DOWN) |
124 | { |
125 | if (gtk_icon_view_get_cursor (GTK_ICON_VIEW (views->view1), path: &path, NULL)) |
126 | { |
127 | col = gtk_icon_view_get_item_column (GTK_ICON_VIEW (views->view1), path); |
128 | gtk_tree_path_free (path); |
129 | |
130 | sel = NULL; |
131 | model = gtk_icon_view_get_model (GTK_ICON_VIEW (views->view2)); |
132 | gtk_tree_model_get_iter_first (tree_model: model, iter: &iter); |
133 | do { |
134 | path = gtk_tree_model_get_path (tree_model: model, iter: &iter); |
135 | if (gtk_icon_view_get_item_column (GTK_ICON_VIEW (views->view2), path) == col) |
136 | { |
137 | sel = path; |
138 | break; |
139 | } |
140 | } while (gtk_tree_model_iter_next (tree_model: model, iter: &iter)); |
141 | |
142 | gtk_icon_view_set_cursor (GTK_ICON_VIEW (views->view2), path: sel, NULL, FALSE); |
143 | gtk_tree_path_free (path: sel); |
144 | } |
145 | gtk_widget_grab_focus (widget: views->view2); |
146 | return TRUE; |
147 | } |
148 | |
149 | if (view == views->view2 && direction == GTK_DIR_UP) |
150 | { |
151 | if (gtk_icon_view_get_cursor (GTK_ICON_VIEW (views->view2), path: &path, NULL)) |
152 | { |
153 | col = gtk_icon_view_get_item_column (GTK_ICON_VIEW (views->view2), path); |
154 | gtk_tree_path_free (path); |
155 | |
156 | sel = NULL; |
157 | model = gtk_icon_view_get_model (GTK_ICON_VIEW (views->view1)); |
158 | gtk_tree_model_get_iter_first (tree_model: model, iter: &iter); |
159 | do { |
160 | path = gtk_tree_model_get_path (tree_model: model, iter: &iter); |
161 | if (gtk_icon_view_get_item_column (GTK_ICON_VIEW (views->view1), path) == col) |
162 | { |
163 | if (sel) |
164 | gtk_tree_path_free (path: sel); |
165 | sel = path; |
166 | } |
167 | else |
168 | gtk_tree_path_free (path); |
169 | } while (gtk_tree_model_iter_next (tree_model: model, iter: &iter)); |
170 | |
171 | gtk_icon_view_set_cursor (GTK_ICON_VIEW (views->view1), path: sel, NULL, FALSE); |
172 | gtk_tree_path_free (path: sel); |
173 | } |
174 | gtk_widget_grab_focus (widget: views->view1); |
175 | return TRUE; |
176 | } |
177 | |
178 | return FALSE; |
179 | } |
180 | |
181 | static void |
182 | focus_changed (GtkWidget *view, |
183 | GParamSpec *pspec, |
184 | gpointer data) |
185 | { |
186 | if (gtk_widget_has_focus (widget: view)) |
187 | { |
188 | GtkTreePath *path; |
189 | |
190 | if (!gtk_icon_view_get_cursor (GTK_ICON_VIEW (view), path: &path, NULL)) |
191 | { |
192 | path = gtk_tree_path_new_from_indices (first_index: 0, -1); |
193 | gtk_icon_view_set_cursor (GTK_ICON_VIEW (view), path, NULL, FALSE); |
194 | } |
195 | |
196 | gtk_icon_view_select_path (GTK_ICON_VIEW (view), path); |
197 | gtk_tree_path_free (path); |
198 | } |
199 | else |
200 | { |
201 | gtk_icon_view_unselect_all (GTK_ICON_VIEW (view)); |
202 | } |
203 | } |
204 | |
205 | #define CSS \ |
206 | "GtkWindow {\n" \ |
207 | " background-color: @base_color;\n" \ |
208 | "}\n" |
209 | |
210 | static void |
211 | set_styles (void) |
212 | { |
213 | GtkCssProvider *provider; |
214 | |
215 | provider = gtk_css_provider_new (); |
216 | |
217 | gtk_css_provider_load_from_data (css_provider: provider, CSS, length: -1); |
218 | |
219 | gtk_style_context_add_provider_for_display (display: gdk_display_get_default (), |
220 | GTK_STYLE_PROVIDER (provider), |
221 | GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); |
222 | } |
223 | |
224 | int |
225 | main (int argc, char *argv[]) |
226 | { |
227 | GtkWidget *window; |
228 | GtkWidget *vbox; |
229 | Views views; |
230 | |
231 | gtk_init (); |
232 | |
233 | set_styles (); |
234 | |
235 | window = gtk_window_new (); |
236 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
237 | gtk_window_set_child (GTK_WINDOW (window), child: vbox); |
238 | |
239 | views.header1 = g_object_new (GTK_TYPE_LABEL, |
240 | first_property_name: "label" , "<b>Group 1</b>" , |
241 | "use-markup" , TRUE, |
242 | "xalign" , 0.0, |
243 | NULL); |
244 | views.view1 = get_view (TRUE); |
245 | views.header2 = g_object_new (GTK_TYPE_LABEL, |
246 | first_property_name: "label" , "<b>Group 2</b>" , |
247 | "use-markup" , TRUE, |
248 | "xalign" , 0.0, |
249 | NULL); |
250 | views.view2 = get_view (FALSE); |
251 | |
252 | g_signal_connect (views.view1, "keynav-failed" , G_CALLBACK (keynav_failed), &views); |
253 | g_signal_connect (views.view2, "keynav-failed" , G_CALLBACK (keynav_failed), &views); |
254 | g_signal_connect (views.view1, "notify::has-focus" , G_CALLBACK (focus_changed), &views); |
255 | g_signal_connect (views.view2, "notify::has-focus" , G_CALLBACK (focus_changed), &views); |
256 | |
257 | gtk_box_append (GTK_BOX (vbox), child: views.header1); |
258 | gtk_box_append (GTK_BOX (vbox), child: views.view1); |
259 | gtk_box_append (GTK_BOX (vbox), child: views.header2); |
260 | gtk_box_append (GTK_BOX (vbox), child: views.view2); |
261 | |
262 | gtk_widget_show (widget: window); |
263 | |
264 | while (TRUE) |
265 | g_main_context_iteration (NULL, TRUE); |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | |