comparison libaudacious/dirbrowser.c @ 2046:456c74b6880a trunk

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