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