r15: Application directories are now detected. Dragging from a filer windows
[rox-filer.git] / ROX-Filer / src / filer.c
blob9a6fd2a250f721f5bfaf4ab5b307fb501c637f51
1 /* vi: set cindent:
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * By Thomas Leonard, <tal197@ecs.soton.ac.uk>.
6 */
8 /* filer.c - code for handling filer windows */
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <errno.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
16 #include <gtk/gtk.h>
17 #include <gdk/gdkprivate.h> /* XXX - find another way to do this */
18 #include <collection.h>
20 #include "support.h"
21 #include "gui_support.h"
22 #include "filer.h"
23 #include "pixmaps.h"
24 #include "menu.h"
25 #include "dnd.h"
27 static int number_of_windows = 0;
28 static FilerWindow *window_with_selection = NULL;
30 /* Static prototypes */
31 static void filer_window_destroyed(GtkWidget *widget,
32 FilerWindow *filer_window);
33 static gboolean idle_scan_dir(gpointer data);
34 static void draw_item(GtkWidget *widget,
35 CollectionItem *item,
36 GdkRectangle *area);
37 void show_menu(Collection *collection, GdkEventButton *event,
38 int number_selected, gpointer user_data);
39 static int sort_by_name(const void *item1, const void *item2);
40 static void scan_dir(FilerWindow *filer_window);
41 static void add_item(FilerWindow *filer_window, char *leafname);
42 static gboolean test_point(Collection *collection,
43 int point_x, int point_y,
44 CollectionItem *item,
45 int width, int height);
48 static void filer_window_destroyed(GtkWidget *widget,
49 FilerWindow *filer_window)
51 if (window_with_selection == filer_window)
52 window_with_selection = NULL;
54 if (filer_window->dir)
56 closedir(filer_window->dir);
57 gtk_idle_remove(filer_window->idle_scan_id);
59 g_free(filer_window->path);
60 g_free(filer_window);
62 if (--number_of_windows < 1)
63 gtk_main_quit();
66 /* This is called while we are scanning the directory */
67 static gboolean idle_scan_dir(gpointer data)
69 struct dirent *next;
70 FilerWindow *filer_window = (FilerWindow *) data;
74 next = readdir(filer_window->dir);
75 if (!next)
77 closedir(filer_window->dir);
78 filer_window->dir = NULL;
80 collection_qsort(filer_window->collection,
81 sort_by_name);
82 return FALSE; /* Finished */
85 add_item(filer_window, next->d_name);
86 } while (!gtk_events_pending());
88 return TRUE;
91 /* Add a single object to a directory display */
92 static void add_item(FilerWindow *filer_window, char *leafname)
94 FileItem *item;
95 int item_width;
96 struct stat info;
97 int base_type;
98 GString *path;
100 /* Ignore dot files (should be an option) */
101 if (leafname[0] == '.')
102 return;
104 item = g_malloc(sizeof(FileItem));
105 item->leafname = g_strdup(leafname);
106 item->flags = 0;
108 path = make_path(filer_window->path, leafname);
109 if (lstat(path->str, &info))
110 base_type = TYPE_ERROR;
111 else
113 if (S_ISREG(info.st_mode))
114 base_type = TYPE_FILE;
115 else if (S_ISDIR(info.st_mode))
116 base_type = TYPE_DIRECTORY;
117 else if (S_ISBLK(info.st_mode))
118 base_type = TYPE_BLOCK_DEVICE;
119 else if (S_ISCHR(info.st_mode))
120 base_type = TYPE_CHAR_DEVICE;
121 else if (S_ISFIFO(info.st_mode))
122 base_type = TYPE_PIPE;
123 else if (S_ISSOCK(info.st_mode))
124 base_type = TYPE_SOCKET;
125 else if (S_ISLNK(info.st_mode))
127 if (stat(path->str, &info))
129 base_type = TYPE_ERROR;
131 else
133 if (S_ISREG(info.st_mode))
134 base_type = TYPE_FILE;
135 else if (S_ISDIR(info.st_mode))
136 base_type = TYPE_DIRECTORY;
137 else if (S_ISBLK(info.st_mode))
138 base_type = TYPE_BLOCK_DEVICE;
139 else if (S_ISCHR(info.st_mode))
140 base_type = TYPE_CHAR_DEVICE;
141 else if (S_ISFIFO(info.st_mode))
142 base_type = TYPE_PIPE;
143 else if (S_ISSOCK(info.st_mode))
144 base_type = TYPE_SOCKET;
145 else
146 base_type = TYPE_UNKNOWN;
149 item->flags |= ITEM_FLAG_SYMLINK;
151 else
152 base_type = TYPE_UNKNOWN;
155 if (base_type == TYPE_DIRECTORY)
157 /* Might be an application directory - better check... */
158 path = g_string_append(path, "/AppInfo");
159 if (!stat(path->str, &info))
161 base_type = TYPE_APPDIR;
164 item->base_type = base_type;
166 item->text_width = gdk_string_width(filer_window->window->style->font,
167 leafname);
168 item->image = default_pixmap + base_type;
170 /* XXX: Must be a better way... */
171 item->pix_width = ((GdkPixmapPrivate *) item->image->pixmap)->width;
173 item_width = MAX(item->pix_width, item->text_width) + 4;
175 if (item_width > filer_window->collection->item_width)
176 collection_set_item_size(filer_window->collection,
177 item_width,
178 filer_window->collection->item_height);
180 collection_insert(filer_window->collection, item);
183 static gboolean test_point(Collection *collection,
184 int point_x, int point_y,
185 CollectionItem *item,
186 int width, int height)
188 FileItem *fileitem = (FileItem *) item->data;
189 GdkFont *font = GTK_WIDGET(collection)->style->font;
190 int text_height = font->ascent + font->descent;
191 int x_off = ABS(point_x - (width >> 1));
193 if (x_off <= (fileitem->pix_width >> 1) + 2 &&
194 point_y < height - text_height - 2 &&
195 point_y > 6)
196 return TRUE;
198 if (x_off <= (fileitem->text_width >> 1) + 2 &&
199 point_y > height - text_height - 2)
200 return TRUE;
202 return FALSE;
205 static void draw_item(GtkWidget *widget,
206 CollectionItem *colitem,
207 GdkRectangle *area)
209 FileItem *item = (FileItem *) colitem->data;
210 GdkGC *gc = colitem->selected ? widget->style->white_gc
211 : widget->style->black_gc;
212 int image_x = area->x + ((area->width - item->pix_width) >> 1);
213 GdkFont *font = widget->style->font;
214 int text_x = area->x + ((area->width - item->text_width) >> 1);
215 int text_y = area->y + area->height - font->descent - 2;
216 int text_height = font->ascent + font->descent;
218 if (item->image)
220 gdk_gc_set_clip_mask(gc, item->image->mask);
221 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
222 gdk_draw_pixmap(widget->window, gc,
223 item->image->pixmap,
224 0, 0, /* Source x,y */
225 image_x, area->y + 8, /* Dest x,y */
226 -1, -1);
228 if (item->flags & ITEM_FLAG_SYMLINK)
230 gdk_gc_set_clip_mask(gc,
231 default_pixmap[TYPE_SYMLINK].mask);
232 gdk_draw_pixmap(widget->window, gc,
233 default_pixmap[TYPE_SYMLINK].pixmap,
234 0, 0, /* Source x,y */
235 image_x, area->y + 8, /* Dest x,y */
236 -1, -1);
239 gdk_gc_set_clip_mask(gc, NULL);
240 gdk_gc_set_clip_origin(gc, 0, 0);
243 if (colitem->selected)
244 gtk_paint_flat_box(widget->style, widget->window,
245 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
246 NULL, widget, "text",
247 text_x, text_y - font->ascent,
248 item->text_width,
249 text_height);
251 gdk_draw_text(widget->window,
252 widget->style->font,
253 colitem->selected ? widget->style->white_gc
254 : widget->style->black_gc,
255 text_x, text_y,
256 item->leafname, strlen(item->leafname));
259 void show_menu(Collection *collection, GdkEventButton *event,
260 int number_selected, gpointer user_data)
262 show_filer_menu((FilerWindow *) user_data, event);
265 static void scan_dir(FilerWindow *filer_window)
267 g_return_if_fail(filer_window->dir == NULL); /* XXX */
269 filer_window->dir = opendir(filer_window->path);
270 if (!filer_window->dir)
272 report_error("Error scanning directory:", g_strerror(errno));
273 return;
276 filer_window->idle_scan_id = gtk_idle_add(idle_scan_dir, filer_window);
279 static void gain_selection(Collection *collection,
280 gint number_selected,
281 gpointer user_data)
283 FilerWindow *filer_window = (FilerWindow *) user_data;
285 if (window_with_selection && window_with_selection != filer_window)
286 collection_clear_selection(window_with_selection->collection);
288 window_with_selection = filer_window;
291 static int sort_by_name(const void *item1, const void *item2)
293 return strcmp((*((FileItem **)item1))->leafname,
294 (*((FileItem **)item2))->leafname);
297 void open_item(Collection *collection,
298 gpointer item_data, int item_number,
299 gpointer user_data)
301 FilerWindow *filer_window = (FilerWindow *) user_data;
302 FileItem *item = (FileItem *) item_data;
304 if (item->base_type == TYPE_DIRECTORY)
305 filer_opendir(make_path(filer_window->path,
306 item->leafname)->str);
309 void filer_opendir(char *path)
311 GtkWidget *hbox, *scrollbar, *collection;
312 FilerWindow *filer_window;
314 filer_window = g_malloc(sizeof(FilerWindow));
315 filer_window->path = pathdup(path);
316 filer_window->dir = NULL; /* Not scanning */
318 collection = collection_new(NULL);
319 filer_window->collection = COLLECTION(collection);
320 collection_set_functions(filer_window->collection,
321 draw_item, test_point);
323 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
324 gtk_window_set_title(GTK_WINDOW(filer_window->window),
325 filer_window->path);
326 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
327 400, 200);
329 hbox = gtk_hbox_new(FALSE, 0);
330 gtk_container_add(GTK_CONTAINER(filer_window->window), hbox);
332 gtk_box_pack_start(GTK_BOX(hbox), collection, TRUE, TRUE, 0);
334 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
335 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
337 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
338 open_item, filer_window);
339 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
340 filer_window_destroyed, filer_window);
341 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
342 show_menu, filer_window);
343 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
344 gain_selection, filer_window);
345 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
346 drag_selection, filer_window);
347 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
348 drag_data_get, filer_window);
350 gtk_widget_show_all(filer_window->window);
351 number_of_windows++;
353 load_default_pixmaps(collection->window);
355 scan_dir(filer_window);