Mercurial > audlegacy
annotate libaudacious/dirbrowser.c @ 2159:35bdfcd17ba7 trunk
[svn] - change the cleanup order in mainwin_quit_cb() to avoid freeze on quit.
- replace deprecated g_main_iteration() with g_main_context_iteration().
author | yaz |
---|---|
date | Mon, 18 Dec 2006 06:53:52 -0800 |
parents | f18a5b617c34 |
children | f363009b7410 |
rev | line source |
---|---|
2046 | 1 /* XMMS - Cross-platform multimedia player |
2 * Copyright (C) 1998-2002 Peter Alm, Mikael Alm, Olle Hallnas, | |
3 * Thomas Nilsson and 4Front Technologies | |
4 * Copyright (C) 1999-2002 Haavard Kvaalen | |
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 | |
2105
f18a5b617c34
[svn] - move to GPLv2-only. Based on my interpretation of the license, we are
nenolod
parents:
2058
diff
changeset
|
8 * the Free Software Foundation; under version 2 of the License. |
2046 | 9 * |
10 * This program is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 * GNU General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU General Public License | |
16 * along with this program; if not, write to the Free Software | |
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
18 */ | |
19 | |
20 #ifdef HAVE_CONFIG_H | |
21 # include "config.h" | |
22 #endif | |
23 | |
24 #define GETTEXT_PACKAGE PACKAGE | |
25 | |
26 #include <glib.h> | |
27 #include <glib/gi18n-lib.h> | |
28 #include <gtk/gtk.h> | |
29 #include <stdio.h> | |
30 #include <string.h> | |
31 | |
32 #include <unistd.h> | |
33 #include <dirent.h> | |
34 #include <sys/types.h> | |
35 #include <sys/stat.h> | |
36 | |
37 | |
38 /* XPM */ | |
39 static gchar *folder[] = { | |
40 "16 16 16 1", | |
41 " c None", | |
42 ". c #f4f7e4", | |
43 "X c #dee4b5", | |
44 "o c #e1e7b9", | |
45 "O c #c6cba4", | |
46 "+ c #dce2b8", | |
47 "@ c #e9e9ec", | |
48 "# c #d3d8ae", | |
49 "$ c #d8daca", | |
50 "% c #b2b2b5", | |
51 "& c #767862", | |
52 "* c #e3e6c3", | |
53 "= c #1b1b1a", | |
54 "- c #939684", | |
55 "; c #555555", | |
56 ": c #000000", | |
57 " ", | |
58 " ", | |
59 " :::: ", | |
60 " :.@@O: ", | |
61 ":-&&&&&::::: ", | |
62 ":.@@@@@*$O#O= ", | |
63 ":@*+XXXX+##O: ", | |
64 ":.*#oooXXXXX: ", | |
65 ":@+XoXXXXXX#: ", | |
66 ":@*ooXXXXXX#: ", | |
67 ":@**XXXXXXX#: ", | |
68 ":@*XXXXXXXX%: ", | |
69 ":$.*OOOOOO%-: ", | |
70 " ;::::::::::: ", | |
71 " ", | |
72 " " | |
73 }; | |
74 | |
75 /* Icon by Jakub Steiner <jimmac@ximian.com> */ | |
76 | |
77 /* XPM */ | |
78 static gchar *ofolder[] = { | |
79 "16 16 16 1", | |
80 " c None", | |
81 ". c #a9ad93", | |
82 "X c #60634d", | |
83 "o c #dee4b5", | |
84 "O c #9ca085", | |
85 "+ c #0c0d04", | |
86 "@ c #2f2f31", | |
87 "# c #3b3d2c", | |
88 "$ c #c8cda2", | |
89 "% c #e6e6e9", | |
90 "& c #b3b5a5", | |
91 "* c #80826d", | |
92 "= c #292a1c", | |
93 "- c #fefef6", | |
94 "; c #8f937b", | |
95 ": c #000000", | |
96 " ", | |
97 " ", | |
98 " :::: ", | |
99 " :-%%&: ", | |
100 ":-;;;OX::::: ", | |
101 ":-;;;;O;O;&.: ", | |
102 ":-*X##@@@@@=#: ", | |
103 ":%*+-%%ooooooO: ", | |
104 ":%X;%ooooooo.*: ", | |
105 ":.+-%oooooooO: ", | |
106 ":*O-oooooooo*: ", | |
107 ":O-oooooooo.: ", | |
108 ":*-%$$$$$$OX: ", | |
109 " ::::::::::: ", | |
110 " ", | |
111 " " | |
112 }; | |
113 | |
114 #define NODE_SPACING 4 | |
115 | |
116 typedef void (*db_handler_t) (char *); | |
117 | |
118 static GdkPixmap *folder_pixmap = NULL, *ofolder_pixmap; | |
119 static GdkBitmap *folder_mask, *ofolder_mask; | |
120 | |
121 struct dirnode { | |
122 guint scanned:1; | |
123 gchar *path; | |
124 }; | |
125 | |
126 static gboolean | |
127 check_for_subdir(gchar * path) | |
128 { | |
129 DIR *dir; | |
130 struct dirent *dirent; | |
131 struct stat statbuf; | |
132 gchar *npath; | |
133 | |
134 if ((dir = opendir(path)) != NULL) { | |
135 while ((dirent = readdir(dir)) != NULL) { | |
136 if (dirent->d_name[0] == '.') | |
137 continue; | |
138 | |
139 npath = g_strconcat(path, dirent->d_name, NULL); | |
140 if (stat(npath, &statbuf) != -1 && S_ISDIR(statbuf.st_mode)) { | |
141 g_free(npath); | |
142 closedir(dir); | |
143 return TRUE; | |
144 } | |
145 g_free(npath); | |
146 } | |
147 closedir(dir); | |
148 } | |
149 return FALSE; | |
150 } | |
151 | |
152 static void | |
153 destroy_cb(gpointer data) | |
154 { | |
155 struct dirnode *node = data; | |
156 | |
157 g_free(node->path); | |
158 g_free(node); | |
159 } | |
160 | |
161 static void | |
162 add_dir(GtkCTree * tree, GtkCTreeNode * pnode, gchar * parent, gchar * dir) | |
163 { | |
164 struct stat statbuf; | |
165 gchar *path; | |
166 | |
167 /* Don't show hidden dirs, nor . and .. */ | |
168 if (dir[0] == '.') | |
169 return; | |
170 | |
171 path = g_strconcat(parent, dir, NULL); | |
172 if (stat(path, &statbuf) != -1 && S_ISDIR(statbuf.st_mode)) { | |
173 gboolean has_subdir; | |
174 gchar *text = ""; | |
175 GtkCTreeNode *node; | |
176 struct dirnode *dirnode = g_new0(struct dirnode, 1); | |
177 dirnode->path = g_strconcat(path, "/", NULL); | |
178 has_subdir = check_for_subdir(dirnode->path); | |
179 node = gtk_ctree_insert_node(tree, pnode, NULL, &dir, | |
180 NODE_SPACING, folder_pixmap, | |
181 folder_mask, ofolder_pixmap, | |
182 ofolder_mask, !has_subdir, FALSE); | |
183 gtk_ctree_node_set_row_data_full(tree, node, dirnode, destroy_cb); | |
184 if (has_subdir) | |
185 gtk_ctree_insert_node(tree, node, NULL, &text, | |
186 NODE_SPACING, NULL, NULL, | |
187 NULL, NULL, FALSE, FALSE); | |
188 } | |
189 g_free(path); | |
190 } | |
191 | |
192 static void | |
193 expand_cb(GtkWidget * widget, GtkCTreeNode * parent_node) | |
194 { | |
195 struct dirent *dirent; | |
196 GtkCTree *tree = GTK_CTREE(widget); | |
197 struct dirnode *parent_dirnode; | |
198 | |
199 parent_dirnode = gtk_ctree_node_get_row_data(tree, parent_node); | |
200 if (!parent_dirnode->scanned) { | |
201 DIR *dir; | |
202 | |
203 gtk_clist_freeze(GTK_CLIST(widget)); | |
204 gtk_ctree_remove_node(tree, GTK_CTREE_ROW(parent_node)->children); | |
205 if ((dir = opendir(parent_dirnode->path)) != NULL) { | |
206 while ((dirent = readdir(dir)) != NULL) { | |
207 add_dir(tree, parent_node, | |
208 parent_dirnode->path, dirent->d_name); | |
209 } | |
210 closedir(dir); | |
211 gtk_ctree_sort_node(tree, parent_node); | |
212 } | |
213 gtk_clist_thaw(GTK_CLIST(widget)); | |
214 parent_dirnode->scanned = TRUE; | |
215 } | |
216 } | |
217 | |
218 static void | |
219 select_row_cb(GtkWidget * widget, gint row, gint column, | |
220 GdkEventButton * bevent, gpointer data) | |
221 { | |
222 struct dirnode *dirnode; | |
223 GtkCTreeNode *node; | |
224 db_handler_t handler; | |
225 | |
226 if (bevent && bevent->type == GDK_2BUTTON_PRESS) { | |
227 node = gtk_ctree_node_nth(GTK_CTREE(widget), row); | |
228 dirnode = gtk_ctree_node_get_row_data(GTK_CTREE(widget), node); | |
229 handler = (db_handler_t) gtk_object_get_user_data(GTK_OBJECT(widget)); | |
230 if (handler) | |
231 handler(dirnode->path); | |
232 } | |
233 } | |
234 | |
235 static void | |
236 show_cb(GtkWidget * widget, gpointer data) | |
237 { | |
238 GtkCTree *tree = GTK_CTREE(data); | |
239 GtkCTreeNode *node = gtk_object_get_data(GTK_OBJECT(tree), | |
240 "selected_node"); | |
241 | |
242 if (node) | |
243 gtk_ctree_node_moveto(tree, node, -1, 0.6, 0); | |
244 } | |
245 | |
246 static void | |
247 ok_clicked(GtkWidget * widget, GtkWidget * tree) | |
248 { | |
249 GtkCTreeNode *node; | |
250 struct dirnode *dirnode; | |
251 GList *list_node; | |
252 GtkWidget *window; | |
253 db_handler_t handler; | |
254 | |
255 window = gtk_object_get_user_data(GTK_OBJECT(widget)); | |
256 gtk_widget_hide(window); | |
257 list_node = GTK_CLIST(tree)->selection; | |
258 while (list_node) { | |
259 node = list_node->data; | |
260 dirnode = gtk_ctree_node_get_row_data(GTK_CTREE(tree), node); | |
261 handler = (db_handler_t) gtk_object_get_user_data(GTK_OBJECT(tree)); | |
262 if (handler) | |
263 handler(dirnode->path); | |
264 list_node = g_list_next(list_node); | |
265 } | |
266 gtk_widget_destroy(window); | |
267 | |
268 } | |
269 | |
2058 | 270 /** |
271 * xmms_create_dir_browser: | |
272 * @title: The title of the dir browser. | |
273 * @current_path: The path that the dir browser should represent. | |
274 * @mode: The GtkSelectionMode that should be used. | |
275 * @handler: A handler to execute upon a selection. | |
276 * | |
277 * Creates a directory browser. | |
278 * | |
279 * Return value: A GtkWidget containing the directory browser. | |
280 **/ | |
2046 | 281 GtkWidget * |
282 xmms_create_dir_browser(gchar * title, gchar * current_path, | |
283 GtkSelectionMode mode, void (*handler) (gchar *)) | |
284 { | |
285 GtkWidget *window, *scroll_win, *tree, *vbox, *bbox, *ok, *cancel, *sep; | |
286 gchar *root_text = "/", *text = ""; | |
287 GtkCTreeNode *root_node, *node, *selected_node = NULL; | |
288 GtkCTree *ctree; | |
289 struct dirnode *dirnode; | |
290 | |
291 window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
292 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | |
293 gtk_window_set_title(GTK_WINDOW(window), title); | |
294 gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG); | |
295 gtk_container_border_width(GTK_CONTAINER(window), 10); | |
296 | |
297 vbox = gtk_vbox_new(FALSE, 10); | |
298 gtk_container_add(GTK_CONTAINER(window), vbox); | |
299 | |
300 scroll_win = gtk_scrolled_window_new(NULL, NULL); | |
301 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win), | |
302 GTK_POLICY_AUTOMATIC, | |
303 GTK_POLICY_AUTOMATIC); | |
304 gtk_widget_set_usize(scroll_win, 450, 400); | |
305 gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0); | |
306 gtk_widget_show(scroll_win); | |
307 | |
308 gtk_widget_realize(window); | |
309 if (!folder_pixmap) { | |
310 folder_pixmap = gdk_pixmap_create_from_xpm_d(window->window, | |
311 &folder_mask, | |
312 NULL, folder); | |
313 ofolder_pixmap = gdk_pixmap_create_from_xpm_d(window->window, | |
314 &ofolder_mask, | |
315 NULL, ofolder); | |
316 } | |
317 | |
318 tree = gtk_ctree_new(1, 0); | |
319 ctree = GTK_CTREE(tree); | |
320 gtk_clist_set_column_auto_resize(GTK_CLIST(tree), 0, TRUE); | |
321 gtk_clist_set_selection_mode(GTK_CLIST(tree), mode); | |
322 gtk_ctree_set_line_style(ctree, GTK_CTREE_LINES_DOTTED); | |
323 g_signal_connect(G_OBJECT(tree), "tree_expand", | |
324 G_CALLBACK(expand_cb), NULL); | |
325 g_signal_connect(G_OBJECT(tree), "select_row", | |
326 G_CALLBACK(select_row_cb), NULL); | |
327 g_signal_connect(G_OBJECT(window), "show", G_CALLBACK(show_cb), tree); | |
328 gtk_container_add(GTK_CONTAINER(scroll_win), tree); | |
329 gtk_object_set_user_data(GTK_OBJECT(tree), (void *) handler); | |
330 | |
331 root_node = gtk_ctree_insert_node(ctree, NULL, NULL, | |
332 &root_text, NODE_SPACING, | |
333 folder_pixmap, folder_mask, | |
334 ofolder_pixmap, ofolder_mask, | |
335 FALSE, FALSE); | |
336 dirnode = g_new0(struct dirnode, 1); | |
337 dirnode->path = g_strdup(G_DIR_SEPARATOR_S); | |
338 gtk_ctree_node_set_row_data_full(ctree, root_node, dirnode, destroy_cb); | |
339 node = gtk_ctree_insert_node(ctree, root_node, NULL, | |
340 &text, 4, NULL, NULL, NULL, | |
341 NULL, TRUE, TRUE); | |
342 gtk_ctree_expand(ctree, root_node); | |
343 gtk_widget_show(tree); | |
344 | |
345 sep = gtk_hseparator_new(); | |
346 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); | |
347 gtk_widget_show(sep); | |
348 | |
349 bbox = gtk_hbutton_box_new(); | |
350 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); | |
351 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); | |
352 | |
353 ok = gtk_button_new_from_stock(GTK_STOCK_OK); | |
354 gtk_object_set_user_data(GTK_OBJECT(ok), window); | |
355 GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT); | |
356 gtk_window_set_default(GTK_WINDOW(window), ok); | |
357 gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0); | |
358 g_signal_connect(G_OBJECT(ok), "clicked", G_CALLBACK(ok_clicked), tree); | |
359 gtk_widget_show(ok); | |
360 | |
361 cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL); | |
362 GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); | |
363 gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); | |
364 g_signal_connect_swapped(G_OBJECT(cancel), "clicked", | |
365 G_CALLBACK(gtk_widget_destroy), | |
366 GTK_OBJECT(window)); | |
367 gtk_widget_show(cancel); | |
368 | |
369 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); | |
370 gtk_widget_show(bbox); | |
371 gtk_widget_show(vbox); | |
372 | |
373 if (current_path && *current_path) { | |
374 gchar **dir; | |
375 gint i; | |
376 | |
377 dir = g_strsplit(current_path, G_DIR_SEPARATOR_S, 0); | |
378 node = root_node; | |
379 for (i = 0; dir[i] != NULL; i++) { | |
380 if (dir[i][0] == '\0') | |
381 continue; | |
382 | |
383 for (node = GTK_CTREE_ROW(node)->children; node != NULL; | |
384 node = GTK_CTREE_ROW(node)->sibling) { | |
385 gchar *tmp; | |
386 if (gtk_ctree_node_get_pixtext(ctree, node, 0, | |
387 &tmp, NULL, NULL, NULL)) | |
388 if (!strcmp(dir[i], tmp)) | |
389 break; | |
390 } | |
391 if (!node) | |
392 break; | |
393 if (!GTK_CTREE_ROW(node)->is_leaf && dir[i + 1]) | |
394 gtk_ctree_expand(ctree, node); | |
395 else { | |
396 selected_node = node; | |
397 break; | |
398 } | |
399 } | |
400 g_strfreev(dir); | |
401 } | |
402 | |
403 if (!selected_node) | |
404 selected_node = root_node; | |
405 | |
406 gtk_ctree_select(ctree, selected_node); | |
407 gtk_object_set_data(GTK_OBJECT(tree), "selected_node", selected_node); | |
408 | |
409 return window; | |
410 } |