| 1 | /* Icon View/Icon View Basics |
| 2 | * |
| 3 | * The GtkIconView widget is used to display and manipulate icons. |
| 4 | * It uses a GtkTreeModel for data storage, so the list store |
| 5 | * example might be helpful. |
| 6 | */ |
| 7 | |
| 8 | #include <glib/gi18n.h> |
| 9 | #include <gtk/gtk.h> |
| 10 | #include <string.h> |
| 11 | |
| 12 | static GtkWidget *window = NULL; |
| 13 | |
| 14 | #define FOLDER_NAME "/iconview/gnome-fs-directory.png" |
| 15 | #define FILE_NAME "/iconview/gnome-fs-regular.png" |
| 16 | |
| 17 | enum |
| 18 | { |
| 19 | COL_PATH, |
| 20 | COL_DISPLAY_NAME, |
| 21 | COL_PIXBUF, |
| 22 | COL_IS_DIRECTORY, |
| 23 | NUM_COLS |
| 24 | }; |
| 25 | |
| 26 | |
| 27 | static GdkPixbuf *file_pixbuf, *folder_pixbuf; |
| 28 | char *parent; |
| 29 | GtkWidget *up_button; |
| 30 | |
| 31 | /* Loads the images for the demo and returns whether the operation succeeded */ |
| 32 | static void |
| 33 | load_pixbufs (void) |
| 34 | { |
| 35 | if (file_pixbuf) |
| 36 | return; /* already loaded earlier */ |
| 37 | |
| 38 | file_pixbuf = gdk_pixbuf_new_from_resource (FILE_NAME, NULL); |
| 39 | /* resources must load successfully */ |
| 40 | g_assert (file_pixbuf); |
| 41 | |
| 42 | folder_pixbuf = gdk_pixbuf_new_from_resource (FOLDER_NAME, NULL); |
| 43 | g_assert (folder_pixbuf); |
| 44 | } |
| 45 | |
| 46 | static void |
| 47 | fill_store (GtkListStore *store) |
| 48 | { |
| 49 | GDir *dir; |
| 50 | const char *name; |
| 51 | GtkTreeIter iter; |
| 52 | |
| 53 | /* First clear the store */ |
| 54 | gtk_list_store_clear (list_store: store); |
| 55 | |
| 56 | /* Now go through the directory and extract all the file |
| 57 | * information */ |
| 58 | dir = g_dir_open (path: parent, flags: 0, NULL); |
| 59 | if (!dir) |
| 60 | return; |
| 61 | |
| 62 | name = g_dir_read_name (dir); |
| 63 | while (name != NULL) |
| 64 | { |
| 65 | char *path, *display_name; |
| 66 | gboolean is_dir; |
| 67 | |
| 68 | /* We ignore hidden files that start with a '.' */ |
| 69 | if (name[0] != '.') |
| 70 | { |
| 71 | path = g_build_filename (first_element: parent, name, NULL); |
| 72 | |
| 73 | is_dir = g_file_test (filename: path, test: G_FILE_TEST_IS_DIR); |
| 74 | |
| 75 | display_name = g_filename_to_utf8 (opsysstring: name, len: -1, NULL, NULL, NULL); |
| 76 | |
| 77 | gtk_list_store_append (list_store: store, iter: &iter); |
| 78 | gtk_list_store_set (list_store: store, iter: &iter, |
| 79 | COL_PATH, path, |
| 80 | COL_DISPLAY_NAME, display_name, |
| 81 | COL_IS_DIRECTORY, is_dir, |
| 82 | COL_PIXBUF, is_dir ? folder_pixbuf : file_pixbuf, |
| 83 | -1); |
| 84 | g_free (mem: path); |
| 85 | g_free (mem: display_name); |
| 86 | } |
| 87 | |
| 88 | name = g_dir_read_name (dir); |
| 89 | } |
| 90 | g_dir_close (dir); |
| 91 | } |
| 92 | |
| 93 | static int |
| 94 | sort_func (GtkTreeModel *model, |
| 95 | GtkTreeIter *a, |
| 96 | GtkTreeIter *b, |
| 97 | gpointer user_data) |
| 98 | { |
| 99 | gboolean is_dir_a, is_dir_b; |
| 100 | char *name_a, *name_b; |
| 101 | int ret; |
| 102 | |
| 103 | /* We need this function because we want to sort |
| 104 | * folders before files. |
| 105 | */ |
| 106 | |
| 107 | |
| 108 | gtk_tree_model_get (tree_model: model, iter: a, |
| 109 | COL_IS_DIRECTORY, &is_dir_a, |
| 110 | COL_DISPLAY_NAME, &name_a, |
| 111 | -1); |
| 112 | |
| 113 | gtk_tree_model_get (tree_model: model, iter: b, |
| 114 | COL_IS_DIRECTORY, &is_dir_b, |
| 115 | COL_DISPLAY_NAME, &name_b, |
| 116 | -1); |
| 117 | |
| 118 | if (!is_dir_a && is_dir_b) |
| 119 | ret = 1; |
| 120 | else if (is_dir_a && !is_dir_b) |
| 121 | ret = -1; |
| 122 | else |
| 123 | { |
| 124 | ret = g_utf8_collate (str1: name_a, str2: name_b); |
| 125 | } |
| 126 | |
| 127 | g_free (mem: name_a); |
| 128 | g_free (mem: name_b); |
| 129 | |
| 130 | return ret; |
| 131 | } |
| 132 | |
| 133 | static GtkListStore * |
| 134 | create_store (void) |
| 135 | { |
| 136 | GtkListStore *store; |
| 137 | |
| 138 | store = gtk_list_store_new (n_columns: NUM_COLS, |
| 139 | G_TYPE_STRING, |
| 140 | G_TYPE_STRING, |
| 141 | GDK_TYPE_PIXBUF, |
| 142 | G_TYPE_BOOLEAN); |
| 143 | |
| 144 | /* Set sort column and function */ |
| 145 | gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (store), |
| 146 | sort_func, |
| 147 | NULL, NULL); |
| 148 | gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), |
| 149 | GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, |
| 150 | order: GTK_SORT_ASCENDING); |
| 151 | |
| 152 | return store; |
| 153 | } |
| 154 | |
| 155 | static void |
| 156 | item_activated (GtkIconView *icon_view, |
| 157 | GtkTreePath *tree_path, |
| 158 | gpointer user_data) |
| 159 | { |
| 160 | GtkListStore *store; |
| 161 | char *path; |
| 162 | GtkTreeIter iter; |
| 163 | gboolean is_dir; |
| 164 | |
| 165 | store = GTK_LIST_STORE (user_data); |
| 166 | |
| 167 | gtk_tree_model_get_iter (GTK_TREE_MODEL (store), |
| 168 | iter: &iter, path: tree_path); |
| 169 | gtk_tree_model_get (GTK_TREE_MODEL (store), iter: &iter, |
| 170 | COL_PATH, &path, |
| 171 | COL_IS_DIRECTORY, &is_dir, |
| 172 | -1); |
| 173 | |
| 174 | if (!is_dir) |
| 175 | { |
| 176 | g_free (mem: path); |
| 177 | return; |
| 178 | } |
| 179 | |
| 180 | /* Replace parent with path and re-fill the model*/ |
| 181 | g_free (mem: parent); |
| 182 | parent = path; |
| 183 | |
| 184 | fill_store (store); |
| 185 | |
| 186 | /* Sensitize the up button */ |
| 187 | gtk_widget_set_sensitive (GTK_WIDGET (up_button), TRUE); |
| 188 | } |
| 189 | |
| 190 | static void |
| 191 | up_clicked (GtkButton *item, |
| 192 | gpointer user_data) |
| 193 | { |
| 194 | GtkListStore *store; |
| 195 | char *dir_name; |
| 196 | |
| 197 | store = GTK_LIST_STORE (user_data); |
| 198 | |
| 199 | dir_name = g_path_get_dirname (file_name: parent); |
| 200 | g_free (mem: parent); |
| 201 | |
| 202 | parent = dir_name; |
| 203 | |
| 204 | fill_store (store); |
| 205 | |
| 206 | /* Maybe de-sensitize the up button */ |
| 207 | gtk_widget_set_sensitive (GTK_WIDGET (up_button), |
| 208 | sensitive: strcmp (s1: parent, s2: "/" ) != 0); |
| 209 | } |
| 210 | |
| 211 | static void |
| 212 | home_clicked (GtkButton *item, |
| 213 | gpointer user_data) |
| 214 | { |
| 215 | GtkListStore *store; |
| 216 | |
| 217 | store = GTK_LIST_STORE (user_data); |
| 218 | |
| 219 | g_free (mem: parent); |
| 220 | parent = g_strdup (str: g_get_home_dir ()); |
| 221 | |
| 222 | fill_store (store); |
| 223 | |
| 224 | /* Sensitize the up button */ |
| 225 | gtk_widget_set_sensitive (GTK_WIDGET (up_button), |
| 226 | TRUE); |
| 227 | } |
| 228 | |
| 229 | static void close_window(void) |
| 230 | { |
| 231 | gtk_window_destroy (GTK_WINDOW (window)); |
| 232 | window = NULL; |
| 233 | |
| 234 | g_object_unref (object: file_pixbuf); |
| 235 | file_pixbuf = NULL; |
| 236 | |
| 237 | g_object_unref (object: folder_pixbuf); |
| 238 | folder_pixbuf = NULL; |
| 239 | } |
| 240 | |
| 241 | GtkWidget * |
| 242 | do_iconview (GtkWidget *do_widget) |
| 243 | { |
| 244 | if (!window) |
| 245 | { |
| 246 | GtkWidget *sw; |
| 247 | GtkWidget *icon_view; |
| 248 | GtkListStore *store; |
| 249 | GtkWidget *vbox; |
| 250 | GtkWidget *tool_bar; |
| 251 | GtkWidget *home_button; |
| 252 | |
| 253 | window = gtk_window_new (); |
| 254 | gtk_window_set_default_size (GTK_WINDOW (window), width: 650, height: 400); |
| 255 | |
| 256 | gtk_window_set_display (GTK_WINDOW (window), |
| 257 | display: gtk_widget_get_display (widget: do_widget)); |
| 258 | gtk_window_set_title (GTK_WINDOW (window), title: "Icon View Basics" ); |
| 259 | |
| 260 | g_signal_connect (window, "destroy" , |
| 261 | G_CALLBACK (close_window), NULL); |
| 262 | |
| 263 | load_pixbufs (); |
| 264 | |
| 265 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
| 266 | gtk_window_set_child (GTK_WINDOW (window), child: vbox); |
| 267 | |
| 268 | tool_bar = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
| 269 | gtk_box_append (GTK_BOX (vbox), child: tool_bar); |
| 270 | |
| 271 | up_button = gtk_button_new_with_mnemonic (label: "_Up" ); |
| 272 | gtk_widget_set_sensitive (GTK_WIDGET (up_button), FALSE); |
| 273 | gtk_box_append (GTK_BOX (tool_bar), child: up_button); |
| 274 | |
| 275 | home_button = gtk_button_new_with_mnemonic (label: "_Home" ); |
| 276 | gtk_box_append (GTK_BOX (tool_bar), child: home_button); |
| 277 | |
| 278 | |
| 279 | sw = gtk_scrolled_window_new (); |
| 280 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (sw), TRUE); |
| 281 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), |
| 282 | hscrollbar_policy: GTK_POLICY_AUTOMATIC, |
| 283 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
| 284 | gtk_widget_set_vexpand (widget: sw, TRUE); |
| 285 | |
| 286 | gtk_box_append (GTK_BOX (vbox), child: sw); |
| 287 | |
| 288 | /* Create the store and fill it with the contents of '/' */ |
| 289 | parent = g_strdup (str: "/" ); |
| 290 | store = create_store (); |
| 291 | fill_store (store); |
| 292 | |
| 293 | icon_view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (store)); |
| 294 | gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (icon_view), |
| 295 | mode: GTK_SELECTION_MULTIPLE); |
| 296 | g_object_unref (object: store); |
| 297 | |
| 298 | /* Connect to the "clicked" signal of the "Up" tool button */ |
| 299 | g_signal_connect (up_button, "clicked" , |
| 300 | G_CALLBACK (up_clicked), store); |
| 301 | |
| 302 | /* Connect to the "clicked" signal of the "Home" tool button */ |
| 303 | g_signal_connect (home_button, "clicked" , |
| 304 | G_CALLBACK (home_clicked), store); |
| 305 | |
| 306 | /* We now set which model columns that correspond to the text |
| 307 | * and pixbuf of each item |
| 308 | */ |
| 309 | gtk_icon_view_set_text_column (GTK_ICON_VIEW (icon_view), column: COL_DISPLAY_NAME); |
| 310 | gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (icon_view), column: COL_PIXBUF); |
| 311 | |
| 312 | /* Connect to the "item-activated" signal */ |
| 313 | g_signal_connect (icon_view, "item-activated" , |
| 314 | G_CALLBACK (item_activated), store); |
| 315 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), child: icon_view); |
| 316 | |
| 317 | gtk_widget_grab_focus (widget: icon_view); |
| 318 | } |
| 319 | |
| 320 | if (!gtk_widget_get_visible (widget: window)) |
| 321 | gtk_widget_show (widget: window); |
| 322 | else |
| 323 | gtk_window_destroy (GTK_WINDOW (window)); |
| 324 | |
| 325 | return window; |
| 326 | } |
| 327 | |