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
|
|
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
|
2058
|
271 /**
|
|
272 * xmms_create_dir_browser:
|
|
273 * @title: The title of the dir browser.
|
|
274 * @current_path: The path that the dir browser should represent.
|
|
275 * @mode: The GtkSelectionMode that should be used.
|
|
276 * @handler: A handler to execute upon a selection.
|
|
277 *
|
|
278 * Creates a directory browser.
|
|
279 *
|
|
280 * Return value: A GtkWidget containing the directory browser.
|
|
281 **/
|
2046
|
282 GtkWidget *
|
|
283 xmms_create_dir_browser(gchar * title, gchar * current_path,
|
|
284 GtkSelectionMode mode, void (*handler) (gchar *))
|
|
285 {
|
|
286 GtkWidget *window, *scroll_win, *tree, *vbox, *bbox, *ok, *cancel, *sep;
|
|
287 gchar *root_text = "/", *text = "";
|
|
288 GtkCTreeNode *root_node, *node, *selected_node = NULL;
|
|
289 GtkCTree *ctree;
|
|
290 struct dirnode *dirnode;
|
|
291
|
|
292 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
293 gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
|
|
294 gtk_window_set_title(GTK_WINDOW(window), title);
|
|
295 gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
|
|
296 gtk_container_border_width(GTK_CONTAINER(window), 10);
|
|
297
|
|
298 vbox = gtk_vbox_new(FALSE, 10);
|
|
299 gtk_container_add(GTK_CONTAINER(window), vbox);
|
|
300
|
|
301 scroll_win = gtk_scrolled_window_new(NULL, NULL);
|
|
302 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
|
|
303 GTK_POLICY_AUTOMATIC,
|
|
304 GTK_POLICY_AUTOMATIC);
|
|
305 gtk_widget_set_usize(scroll_win, 450, 400);
|
|
306 gtk_box_pack_start(GTK_BOX(vbox), scroll_win, TRUE, TRUE, 0);
|
|
307 gtk_widget_show(scroll_win);
|
|
308
|
|
309 gtk_widget_realize(window);
|
|
310 if (!folder_pixmap) {
|
|
311 folder_pixmap = gdk_pixmap_create_from_xpm_d(window->window,
|
|
312 &folder_mask,
|
|
313 NULL, folder);
|
|
314 ofolder_pixmap = gdk_pixmap_create_from_xpm_d(window->window,
|
|
315 &ofolder_mask,
|
|
316 NULL, ofolder);
|
|
317 }
|
|
318
|
|
319 tree = gtk_ctree_new(1, 0);
|
|
320 ctree = GTK_CTREE(tree);
|
|
321 gtk_clist_set_column_auto_resize(GTK_CLIST(tree), 0, TRUE);
|
|
322 gtk_clist_set_selection_mode(GTK_CLIST(tree), mode);
|
|
323 gtk_ctree_set_line_style(ctree, GTK_CTREE_LINES_DOTTED);
|
|
324 g_signal_connect(G_OBJECT(tree), "tree_expand",
|
|
325 G_CALLBACK(expand_cb), NULL);
|
|
326 g_signal_connect(G_OBJECT(tree), "select_row",
|
|
327 G_CALLBACK(select_row_cb), NULL);
|
|
328 g_signal_connect(G_OBJECT(window), "show", G_CALLBACK(show_cb), tree);
|
|
329 gtk_container_add(GTK_CONTAINER(scroll_win), tree);
|
|
330 gtk_object_set_user_data(GTK_OBJECT(tree), (void *) handler);
|
|
331
|
|
332 root_node = gtk_ctree_insert_node(ctree, NULL, NULL,
|
|
333 &root_text, NODE_SPACING,
|
|
334 folder_pixmap, folder_mask,
|
|
335 ofolder_pixmap, ofolder_mask,
|
|
336 FALSE, FALSE);
|
|
337 dirnode = g_new0(struct dirnode, 1);
|
|
338 dirnode->path = g_strdup(G_DIR_SEPARATOR_S);
|
|
339 gtk_ctree_node_set_row_data_full(ctree, root_node, dirnode, destroy_cb);
|
|
340 node = gtk_ctree_insert_node(ctree, root_node, NULL,
|
|
341 &text, 4, NULL, NULL, NULL,
|
|
342 NULL, TRUE, TRUE);
|
|
343 gtk_ctree_expand(ctree, root_node);
|
|
344 gtk_widget_show(tree);
|
|
345
|
|
346 sep = gtk_hseparator_new();
|
|
347 gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
|
|
348 gtk_widget_show(sep);
|
|
349
|
|
350 bbox = gtk_hbutton_box_new();
|
|
351 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
|
|
352 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
|
|
353
|
|
354 ok = gtk_button_new_from_stock(GTK_STOCK_OK);
|
|
355 gtk_object_set_user_data(GTK_OBJECT(ok), window);
|
|
356 GTK_WIDGET_SET_FLAGS(ok, GTK_CAN_DEFAULT);
|
|
357 gtk_window_set_default(GTK_WINDOW(window), ok);
|
|
358 gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0);
|
|
359 g_signal_connect(G_OBJECT(ok), "clicked", G_CALLBACK(ok_clicked), tree);
|
|
360 gtk_widget_show(ok);
|
|
361
|
|
362 cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
|
|
363 GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
|
|
364 gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0);
|
|
365 g_signal_connect_swapped(G_OBJECT(cancel), "clicked",
|
|
366 G_CALLBACK(gtk_widget_destroy),
|
|
367 GTK_OBJECT(window));
|
|
368 gtk_widget_show(cancel);
|
|
369
|
|
370 gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
|
|
371 gtk_widget_show(bbox);
|
|
372 gtk_widget_show(vbox);
|
|
373
|
|
374 if (current_path && *current_path) {
|
|
375 gchar **dir;
|
|
376 gint i;
|
|
377
|
|
378 dir = g_strsplit(current_path, G_DIR_SEPARATOR_S, 0);
|
|
379 node = root_node;
|
|
380 for (i = 0; dir[i] != NULL; i++) {
|
|
381 if (dir[i][0] == '\0')
|
|
382 continue;
|
|
383
|
|
384 for (node = GTK_CTREE_ROW(node)->children; node != NULL;
|
|
385 node = GTK_CTREE_ROW(node)->sibling) {
|
|
386 gchar *tmp;
|
|
387 if (gtk_ctree_node_get_pixtext(ctree, node, 0,
|
|
388 &tmp, NULL, NULL, NULL))
|
|
389 if (!strcmp(dir[i], tmp))
|
|
390 break;
|
|
391 }
|
|
392 if (!node)
|
|
393 break;
|
|
394 if (!GTK_CTREE_ROW(node)->is_leaf && dir[i + 1])
|
|
395 gtk_ctree_expand(ctree, node);
|
|
396 else {
|
|
397 selected_node = node;
|
|
398 break;
|
|
399 }
|
|
400 }
|
|
401 g_strfreev(dir);
|
|
402 }
|
|
403
|
|
404 if (!selected_node)
|
|
405 selected_node = root_node;
|
|
406
|
|
407 gtk_ctree_select(ctree, selected_node);
|
|
408 gtk_object_set_data(GTK_OBJECT(tree), "selected_node", selected_node);
|
|
409
|
|
410 return window;
|
|
411 }
|