r99: Bugfix: Closing a window while scanning caused an error about the cursor.
[rox-filer.git] / ROX-Filer / src / filer.c
blobc845cb395e86a56b035d62d8ab6eeab6f50b82e4
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>
15 #include <time.h>
17 #include <gtk/gtk.h>
18 #include <gdk/gdkx.h>
19 #include <gdk/gdkprivate.h> /* XXX - find another way to do this */
20 #include <gdk/gdkkeysyms.h>
21 #include "collection.h"
23 #include "main.h"
24 #include "support.h"
25 #include "gui_support.h"
26 #include "filer.h"
27 #include "pixmaps.h"
28 #include "menu.h"
29 #include "dnd.h"
30 #include "apps.h"
31 #include "mount.h"
32 #include "type.h"
34 #define MAX_ICON_HEIGHT 42
35 #define PANEL_BORDER 2
37 FilerWindow *window_with_focus = NULL;
39 /* Link paths to GLists of filer windows */
40 GHashTable *path_to_window_list = NULL;
42 static FilerWindow *window_with_selection = NULL;
44 /* Static prototypes */
45 static void filer_window_destroyed(GtkWidget *widget,
46 FilerWindow *filer_window);
47 static gboolean idle_scan_dir(gpointer data);
48 static void draw_item(GtkWidget *widget,
49 CollectionItem *item,
50 GdkRectangle *area);
51 void show_menu(Collection *collection, GdkEventButton *event,
52 int number_selected, gpointer user_data);
53 static int sort_by_name(const void *item1, const void *item2);
54 static void add_item(FilerWindow *filer_window, char *leafname);
55 static gboolean test_point(Collection *collection,
56 int point_x, int point_y,
57 CollectionItem *item,
58 int width, int height);
59 static void stop_scanning(FilerWindow *filer_window);
60 static gint focus_in(GtkWidget *widget,
61 GdkEventFocus *event,
62 FilerWindow *filer_window);
63 static gint focus_out(GtkWidget *widget,
64 GdkEventFocus *event,
65 FilerWindow *filer_window);
66 static void add_view(FilerWindow *filer_window);
67 static void remove_view(FilerWindow *filer_window);
68 static void free_item(FileItem *item);
69 static gboolean remove_deleted(gpointer item_data, gpointer data);
71 static GdkAtom xa_string;
72 enum
74 TARGET_STRING,
75 TARGET_URI_LIST,
78 void filer_init()
80 xa_string = gdk_atom_intern("STRING", FALSE);
82 path_to_window_list = g_hash_table_new(g_str_hash, g_str_equal);
86 /* When a filer window shows a directory, use this function to add
87 * it to the list of directories to be updated when the contents
88 * change.
90 static void add_view(FilerWindow *filer_window)
92 GList *list, *newlist;
94 g_return_if_fail(filer_window != NULL);
96 list = g_hash_table_lookup(path_to_window_list, filer_window->path);
97 newlist = g_list_prepend(list, filer_window);
98 if (newlist != list)
99 g_hash_table_insert(path_to_window_list, filer_window->path,
100 newlist);
103 /* When a filer window no longer shows a directory, call this to reverse
104 * the effect of add_view().
106 static void remove_view(FilerWindow *filer_window)
108 GList *list, *newlist;
110 g_return_if_fail(filer_window != NULL);
112 list = g_hash_table_lookup(path_to_window_list, filer_window->path);
113 newlist = g_list_remove(list, filer_window);
114 if (newlist != list)
115 g_hash_table_insert(path_to_window_list, filer_window->path,
116 newlist);
119 /* Go though all the FileItems in a collection, freeing all items.
120 * TODO: Maybe we should cache icons?
122 static void free_items(FilerWindow *filer_window)
124 guint i;
125 Collection *collection = filer_window->collection;
127 for (i = 0; i < collection->number_of_items; i++)
129 free_item((FileItem *) collection->items[i].data);
130 collection->items[i].data = NULL;
134 /* Set one item in a collection to NULL, freeing all data for it */
135 static void free_item(FileItem *item)
137 if (item->flags & ITEM_FLAG_TEMP_ICON)
139 gdk_pixmap_unref(item->image->pixmap);
140 gdk_pixmap_unref(item->image->mask);
141 g_free(item->image);
142 item->image = default_pixmap + TYPE_ERROR;
144 g_free(item->leafname);
145 g_free(item);
148 static void filer_window_destroyed(GtkWidget *widget,
149 FilerWindow *filer_window)
151 if (window_with_selection == filer_window)
152 window_with_selection = NULL;
153 if (window_with_focus == filer_window)
154 window_with_focus = NULL;
156 remove_view(filer_window);
158 free_items(filer_window);
159 if (filer_window->dir)
160 stop_scanning(filer_window);
161 g_free(filer_window->path);
162 g_free(filer_window);
164 if (--number_of_windows < 1)
165 gtk_main_quit();
168 static void stop_scanning(FilerWindow *filer_window)
170 g_return_if_fail(filer_window->dir != NULL);
172 closedir(filer_window->dir);
173 gtk_idle_remove(filer_window->idle_scan_id);
174 filer_window->dir = NULL;
175 if (filer_window->window->window)
176 gdk_window_set_cursor(filer_window->window->window, NULL);
179 /* This is called while we are scanning the directory */
180 static gboolean idle_scan_dir(gpointer data)
182 struct dirent *next;
183 FilerWindow *filer_window = (FilerWindow *) data;
187 next = readdir(filer_window->dir);
188 if (!next)
190 closedir(filer_window->dir);
191 filer_window->dir = NULL;
192 gdk_window_set_cursor(filer_window->window->window,
193 NULL);
195 collection_set_item_size(filer_window->collection,
196 filer_window->scan_min_width,
197 filer_window->collection->item_height);
198 collection_qsort(filer_window->collection,
199 filer_window->sort_fn);
200 if (filer_window->flags & FILER_UPDATING)
202 collection_delete_if(filer_window->collection,
203 remove_deleted, NULL);
205 if (filer_window->flags & FILER_NEEDS_RESCAN)
206 update_dir(filer_window);
208 return FALSE; /* Finished */
211 add_item(filer_window, next->d_name);
212 } while (!gtk_events_pending());
214 return TRUE;
217 /* Add a single object to a directory display */
218 static void add_item(FilerWindow *filer_window, char *leafname)
220 FileItem *item;
221 int old_item;
222 int item_width;
223 struct stat info;
224 int base_type;
225 GString *path;
227 if (leafname[0] == '.')
229 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
230 || (leafname[1] == '.' && leafname[2] == '\0'))
231 return;
234 item = g_malloc(sizeof(FileItem));
235 item->leafname = g_strdup(leafname);
236 item->flags = (ItemFlags) 0;
238 path = make_path(filer_window->path, leafname);
239 if (lstat(path->str, &info))
240 base_type = TYPE_ERROR;
241 else
243 if (S_ISREG(info.st_mode))
244 base_type = TYPE_FILE;
245 else if (S_ISDIR(info.st_mode))
247 base_type = TYPE_DIRECTORY;
249 if (g_hash_table_lookup(mtab_mounts, path->str))
250 item->flags |= ITEM_FLAG_MOUNT_POINT
251 | ITEM_FLAG_MOUNTED;
252 else if (g_hash_table_lookup(fstab_mounts, path->str))
253 item->flags |= ITEM_FLAG_MOUNT_POINT;
255 else if (S_ISBLK(info.st_mode))
256 base_type = TYPE_BLOCK_DEVICE;
257 else if (S_ISCHR(info.st_mode))
258 base_type = TYPE_CHAR_DEVICE;
259 else if (S_ISFIFO(info.st_mode))
260 base_type = TYPE_PIPE;
261 else if (S_ISSOCK(info.st_mode))
262 base_type = TYPE_SOCKET;
263 else if (S_ISLNK(info.st_mode))
265 if (stat(path->str, &info))
267 base_type = TYPE_ERROR;
269 else
271 if (S_ISREG(info.st_mode))
272 base_type = TYPE_FILE;
273 else if (S_ISDIR(info.st_mode))
274 base_type = TYPE_DIRECTORY;
275 else if (S_ISBLK(info.st_mode))
276 base_type = TYPE_BLOCK_DEVICE;
277 else if (S_ISCHR(info.st_mode))
278 base_type = TYPE_CHAR_DEVICE;
279 else if (S_ISFIFO(info.st_mode))
280 base_type = TYPE_PIPE;
281 else if (S_ISSOCK(info.st_mode))
282 base_type = TYPE_SOCKET;
283 else
284 base_type = TYPE_UNKNOWN;
287 item->flags |= ITEM_FLAG_SYMLINK;
289 else
290 base_type = TYPE_UNKNOWN;
293 item->base_type = base_type;
294 item->mime_type = NULL;
296 if (base_type == TYPE_DIRECTORY &&
297 !(item->flags & ITEM_FLAG_MOUNT_POINT))
299 uid_t uid = info.st_uid;
301 /* Might be an application directory - better check...
302 * AppRun must have the same owner as the directory
303 * (to stop people putting an AppRun in, eg, /tmp)
305 g_string_append(path, "/AppRun");
306 if (!stat(path->str, &info) && info.st_uid == uid)
308 item->flags |= ITEM_FLAG_APPDIR;
312 if (item->flags & ITEM_FLAG_APPDIR) /* path still ends /AppRun */
314 MaskedPixmap *app_icon;
316 g_string_truncate(path, path->len - 3);
317 g_string_append(path, "Icon.xpm");
318 app_icon = load_pixmap_from(filer_window->window, path->str);
319 if (app_icon)
321 item->image = app_icon;
322 item->flags |= ITEM_FLAG_TEMP_ICON;
324 else
325 item->image = default_pixmap + TYPE_APPDIR;
327 else
329 if (base_type == TYPE_FILE &&
330 (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
332 item->image = default_pixmap + TYPE_EXEC_FILE;
333 item->flags |= ITEM_FLAG_EXEC_FILE;
335 else if (base_type == TYPE_FILE)
337 item->mime_type = type_from_path(path->str);
338 item->image = type_to_icon(filer_window->window,
339 item->mime_type);
341 else
342 item->image = default_pixmap + base_type;
345 item->text_width = gdk_string_width(filer_window->window->style->font,
346 leafname);
348 /* XXX: Must be a better way... */
349 item->pix_width = ((GdkPixmapPrivate *) item->image->pixmap)->width;
350 item->pix_height = ((GdkPixmapPrivate *) item->image->pixmap)->height;
352 item_width = MAX(item->pix_width, item->text_width) + 4;
354 if (item_width > filer_window->scan_min_width)
355 filer_window->scan_min_width = item_width;
357 if (item_width > filer_window->collection->item_width)
358 collection_set_item_size(filer_window->collection,
359 item_width,
360 filer_window->collection->item_height);
362 old_item = collection_find_item(filer_window->collection, item,
363 sort_by_name);
365 if (old_item >= 0)
367 CollectionItem *old =
368 &filer_window->collection->items[old_item];
370 free_item((FileItem *) old->data);
371 old->data = item;
372 collection_draw_item(filer_window->collection, old_item, TRUE);
374 else
375 collection_insert(filer_window->collection, item);
378 /* Is a point inside an item? */
379 static gboolean test_point(Collection *collection,
380 int point_x, int point_y,
381 CollectionItem *colitem,
382 int width, int height)
384 FileItem *item = (FileItem *) colitem->data;
385 GdkFont *font = GTK_WIDGET(collection)->style->font;
386 int text_height = font->ascent + font->descent;
387 int image_y = MAX(0, MAX_ICON_HEIGHT - item->pix_height);
388 int image_width = (item->pix_width >> 1) + 2;
389 int text_width = (item->text_width >> 1) + 2;
390 int x_limit;
392 if (point_y < image_y)
393 return FALSE; /* Too high up (don't worry about too low) */
395 if (point_y <= image_y + item->pix_height + 2)
396 x_limit = image_width;
397 else if (point_y > height - text_height - 2)
398 x_limit = text_width;
399 else
400 x_limit = MIN(image_width, text_width);
402 return ABS(point_x - (width >> 1)) < x_limit;
405 static void draw_item(GtkWidget *widget,
406 CollectionItem *colitem,
407 GdkRectangle *area)
409 FileItem *item = (FileItem *) colitem->data;
410 GdkGC *gc = colitem->selected ? widget->style->white_gc
411 : widget->style->black_gc;
412 int image_x = area->x + ((area->width - item->pix_width) >> 1);
413 GdkFont *font = widget->style->font;
414 int text_x = area->x + ((area->width - item->text_width) >> 1);
415 int text_y = area->y + area->height - font->descent - 2;
416 int text_height = font->ascent + font->descent;
418 if (item->image)
420 int image_y;
422 gdk_gc_set_clip_mask(gc, item->image->mask);
424 image_y = MAX(0, MAX_ICON_HEIGHT - item->pix_height);
425 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
426 gdk_draw_pixmap(widget->window, gc,
427 item->image->pixmap,
428 0, 0, /* Source x,y */
429 image_x, area->y + image_y, /* Dest x,y */
430 -1, MIN(item->pix_height, MAX_ICON_HEIGHT));
432 if (item->flags & ITEM_FLAG_SYMLINK)
434 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
435 gdk_gc_set_clip_mask(gc,
436 default_pixmap[TYPE_SYMLINK].mask);
437 gdk_draw_pixmap(widget->window, gc,
438 default_pixmap[TYPE_SYMLINK].pixmap,
439 0, 0, /* Source x,y */
440 image_x, area->y + 8, /* Dest x,y */
441 -1, -1);
443 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
445 int type = item->flags & ITEM_FLAG_MOUNTED
446 ? TYPE_MOUNTED
447 : TYPE_UNMOUNTED;
448 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
449 gdk_gc_set_clip_mask(gc,
450 default_pixmap[type].mask);
451 gdk_draw_pixmap(widget->window, gc,
452 default_pixmap[type].pixmap,
453 0, 0, /* Source x,y */
454 image_x, area->y + 8, /* Dest x,y */
455 -1, -1);
458 gdk_gc_set_clip_mask(gc, NULL);
459 gdk_gc_set_clip_origin(gc, 0, 0);
462 if (colitem->selected)
463 gtk_paint_flat_box(widget->style, widget->window,
464 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
465 NULL, widget, "text",
466 text_x, text_y - font->ascent,
467 item->text_width,
468 text_height);
470 gdk_draw_text(widget->window,
471 widget->style->font,
472 colitem->selected ? widget->style->white_gc
473 : widget->style->black_gc,
474 text_x, text_y,
475 item->leafname, strlen(item->leafname));
478 void show_menu(Collection *collection, GdkEventButton *event,
479 int item, gpointer user_data)
481 show_filer_menu((FilerWindow *) user_data, event, item);
484 static void may_rescan(FilerWindow *filer_window)
486 struct stat info;
488 g_return_if_fail(filer_window != NULL);
490 if (stat(filer_window->path, &info))
492 delayed_error("ROX-Filer", "Directory deleted");
493 gtk_widget_destroy(filer_window->window);
495 else if (info.st_mtime > filer_window->m_time)
497 if (filer_window->dir)
498 filer_window->flags |= FILER_NEEDS_RESCAN;
499 else
500 update_dir(filer_window);
504 /* Callback to collection_delete_if() */
505 static gboolean remove_deleted(gpointer item_data, gpointer data)
507 FileItem *item = (FileItem *) item_data;
509 if (item->flags & ITEM_FLAG_MAY_DELETE)
511 free_item(item);
512 return TRUE;
515 return FALSE;
518 /* Forget the old contents of a filer window and scan the directory
519 * from the start. If we are already scanning then rescan later.
521 void scan_dir(FilerWindow *filer_window)
523 if (filer_window->dir)
524 stop_scanning(filer_window);
526 free_items(filer_window);
527 collection_clear(filer_window->collection);
529 update_dir(filer_window);
530 filer_window->flags &= ~FILER_UPDATING;
532 gdk_window_set_cursor(filer_window->window->window,
533 gdk_cursor_new(GDK_WATCH));
536 /* Like scan_dir(), but assume new display will be similar to the old
537 * one (less flicker and doesn't lose the selection).
539 void update_dir(FilerWindow *filer_window)
541 Collection *collection = filer_window->collection;
542 struct stat info;
543 guint i;
545 if (filer_window->dir)
547 /* Already scanning - start again when we finish */
548 filer_window->flags |= FILER_NEEDS_RESCAN;
549 return;
551 filer_window->flags &= ~FILER_NEEDS_RESCAN;
552 filer_window->flags |= FILER_UPDATING;
554 for (i = 0; i < collection->number_of_items; i++)
556 FileItem *item = (FileItem *) collection->items[i].data;
557 item->flags |= ITEM_FLAG_MAY_DELETE;
560 mount_update();
562 gtk_window_set_title(GTK_WINDOW(filer_window->window),
563 filer_window->path);
565 if (stat(filer_window->path, &info))
567 report_error("Error statting directory", g_strerror(errno));
568 return;
570 filer_window->m_time = info.st_mtime;
572 filer_window->dir = opendir(filer_window->path);
573 if (!filer_window->dir)
575 report_error("Error scanning directory", g_strerror(errno));
576 return;
579 filer_window->scan_min_width = 64;
581 filer_window->idle_scan_id = gtk_idle_add(idle_scan_dir, filer_window);
584 /* Another app has grabbed the selection */
585 static gint collection_lose_selection(GtkWidget *widget,
586 GdkEventSelection *event)
588 if (window_with_selection &&
589 window_with_selection->collection == COLLECTION(widget))
591 FilerWindow *filer_window = window_with_selection;
592 window_with_selection = NULL;
593 collection_clear_selection(filer_window->collection);
596 return TRUE;
599 /* Someone wants us to send them the selection */
600 static void selection_get(GtkWidget *widget,
601 GtkSelectionData *selection_data,
602 guint info,
603 guint time,
604 gpointer data)
606 GString *reply, *header;
607 FilerWindow *filer_window;
608 int i;
609 Collection *collection;
611 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
613 reply = g_string_new(NULL);
614 header = g_string_new(NULL);
616 switch (info)
618 case TARGET_STRING:
619 g_string_sprintf(header, " %s",
620 make_path(filer_window->path, "")->str);
621 break;
622 case TARGET_URI_LIST:
623 g_string_sprintf(header, " file://%s%s",
624 our_host_name(),
625 make_path(filer_window->path, "")->str);
626 break;
629 collection = filer_window->collection;
630 for (i = 0; i < collection->number_of_items; i++)
632 if (collection->items[i].selected)
634 FileItem *item =
635 (FileItem *) collection->items[i].data;
637 g_string_append(reply, header->str);
638 g_string_append(reply, item->leafname);
641 /* This works, but I don't think I like it... */
642 /* g_string_append_c(reply, ' '); */
644 gtk_selection_data_set(selection_data, xa_string,
645 8, reply->str + 1, reply->len - 1);
646 g_string_free(reply, TRUE);
647 g_string_free(header, TRUE);
650 /* No items are now selected. This might be because another app claimed
651 * the selection or because the user unselected all the items.
653 static void lose_selection(Collection *collection,
654 guint time,
655 gpointer user_data)
657 FilerWindow *filer_window = (FilerWindow *) user_data;
659 if (window_with_selection == filer_window)
661 window_with_selection = NULL;
662 gtk_selection_owner_set(NULL,
663 GDK_SELECTION_PRIMARY,
664 time);
668 static void gain_selection(Collection *collection,
669 guint time,
670 gpointer user_data)
672 FilerWindow *filer_window = (FilerWindow *) user_data;
674 if (gtk_selection_owner_set(GTK_WIDGET(collection),
675 GDK_SELECTION_PRIMARY,
676 time))
678 window_with_selection = filer_window;
680 else
681 collection_clear_selection(filer_window->collection);
684 static int sort_by_name(const void *item1, const void *item2)
686 return strcmp((*((FileItem **)item1))->leafname,
687 (*((FileItem **)item2))->leafname);
690 static int sort_by_type(const void *item1, const void *item2)
692 const FileItem *i1 = (FileItem *) ((CollectionItem *) item1)->data;
693 const FileItem *i2 = (FileItem *) ((CollectionItem *) item2)->data;
694 MIME_type *m1, *m2;
696 int diff = i1->base_type - i2->base_type;
698 if (!diff)
699 diff = (i1->flags & ITEM_FLAG_APPDIR)
700 - (i2->flags & ITEM_FLAG_APPDIR);
701 if (diff)
702 return diff > 0 ? 1 : -1;
704 m1 = i1->mime_type;
705 m2 = i2->mime_type;
707 if (m1 && m2)
709 diff = strcmp(m1->media_type, m2->media_type);
710 if (!diff)
711 diff = strcmp(m1->subtype, m2->subtype);
713 else if (m1 || m2)
714 diff = m1 ? 1 : -1;
715 else
716 diff = 0;
718 if (diff)
719 return diff > 0 ? 1 : -1;
721 return sort_by_name(item1, item2);
724 void open_item(Collection *collection,
725 gpointer item_data, int item_number,
726 gpointer user_data)
728 FilerWindow *filer_window = (FilerWindow *) user_data;
729 FileItem *item = (FileItem *) item_data;
730 GdkEventButton *event;
731 char *full_path;
732 GtkWidget *widget;
733 gboolean shift, adjust;
735 event = (GdkEventButton *) gtk_get_current_event();
736 full_path = make_path(filer_window->path, item->leafname)->str;
738 collection_wink_item(filer_window->collection, item_number);
740 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_BUTTON_PRESS)
742 shift = event->state & GDK_SHIFT_MASK;
743 adjust = event->button != 1 || event->state & GDK_CONTROL_MASK;
745 else
747 shift = FALSE;
748 adjust = FALSE;
751 widget = filer_window->window;
753 switch (item->base_type)
755 case TYPE_DIRECTORY:
756 if (item->flags & ITEM_FLAG_APPDIR && !shift)
758 run_app(make_path(filer_window->path,
759 item->leafname)->str);
760 if (adjust && !filer_window->panel)
761 gtk_widget_destroy(widget);
762 break;
764 if (adjust || filer_window->panel)
765 filer_opendir(full_path, FALSE, BOTTOM);
766 else
768 remove_view(filer_window);
769 filer_window->path = pathdup(full_path);
770 add_view(filer_window);
771 scan_dir(filer_window);
773 break;
774 case TYPE_FILE:
775 if (item->flags & ITEM_FLAG_EXEC_FILE && !shift)
777 char *argv[] = {NULL, NULL};
779 argv[0] = full_path;
781 if (spawn_full(argv, getenv("HOME"), 0))
783 if (adjust && !filer_window->panel)
784 gtk_widget_destroy(widget);
786 else
787 report_error("ROX-Filer",
788 "Failed to fork() child");
790 else
792 GString *message;
793 MIME_type *type = shift ? &text_plain
794 : item->mime_type;
796 g_return_if_fail(type != NULL);
798 if (type_open(full_path, type))
800 if (adjust && !filer_window->panel)
801 gtk_widget_destroy(widget);
803 else
805 message = g_string_new(NULL);
806 g_string_sprintf(message, "No open "
807 "action specified for files of "
808 "this type (%s/%s)",
809 type->media_type,
810 type->subtype);
811 report_error("ROX-Filer", message->str);
812 g_string_free(message, TRUE);
815 break;
816 default:
817 report_error("open_item",
818 "I don't know how to open that");
819 break;
823 static gint pointer_in(GtkWidget *widget,
824 GdkEventCrossing *event,
825 FilerWindow *filer_window)
827 may_rescan(filer_window);
828 return FALSE;
831 static gint focus_in(GtkWidget *widget,
832 GdkEventFocus *event,
833 FilerWindow *filer_window)
835 window_with_focus = filer_window;
837 return FALSE;
840 static gint focus_out(GtkWidget *widget,
841 GdkEventFocus *event,
842 FilerWindow *filer_window)
844 /* TODO: Shade the cursor */
846 return FALSE;
849 /* Handle keys that can't be bound with the menu */
850 static gint key_press_event(GtkWidget *widget,
851 GdkEventKey *event,
852 FilerWindow *filer_window)
854 switch (event->keyval)
857 case GDK_Left:
858 move_cursor(-1, 0);
859 break;
860 case GDK_Right:
861 move_cursor(1, 0);
862 break;
863 case GDK_Up:
864 move_cursor(0, -1);
865 break;
866 case GDK_Down:
867 move_cursor(0, 1);
868 break;
869 case GDK_Return:
871 case GDK_BackSpace:
872 remove_view(filer_window);
873 filer_window->path = pathdup(make_path(
874 filer_window->path,
875 "..")->str);
876 add_view(filer_window);
877 scan_dir(filer_window);
878 return TRUE;
881 return FALSE;
884 FileItem *selected_item(Collection *collection)
886 int i;
888 g_return_val_if_fail(collection != NULL, NULL);
889 g_return_val_if_fail(IS_COLLECTION(collection), NULL);
890 g_return_val_if_fail(collection->number_selected == 1, NULL);
892 for (i = 0; i < collection->number_of_items; i++)
893 if (collection->items[i].selected)
894 return (FileItem *) collection->items[i].data;
896 g_warning("selected_item: number_selected is wrong\n");
898 return NULL;
901 /* Refresh all windows onto this directory */
902 void refresh_dirs(char *path)
904 char *real;
905 GList *list, *next;
907 real = pathdup(path);
908 list = g_hash_table_lookup(path_to_window_list, real);
909 g_free(real);
911 while (list)
913 next = list->next;
914 update_dir((FilerWindow *) list->data);
915 list = next;
919 void filer_opendir(char *path, gboolean panel, Side panel_side)
921 GtkWidget *hbox, *scrollbar, *collection;
922 FilerWindow *filer_window;
923 GtkTargetEntry target_table[] =
925 {"text/uri-list", 0, TARGET_URI_LIST},
926 {"STRING", 0, TARGET_STRING},
929 filer_window = g_malloc(sizeof(FilerWindow));
930 filer_window->path = pathdup(path);
931 filer_window->dir = NULL; /* Not scanning */
932 filer_window->show_hidden = FALSE;
933 filer_window->panel = panel;
934 filer_window->panel_side = panel_side;
935 filer_window->temp_item_selected = FALSE;
936 filer_window->sort_fn = sort_by_type;
937 filer_window->flags = (FilerFlags) 0;
939 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
941 collection = collection_new(NULL);
942 gtk_object_set_data(GTK_OBJECT(collection),
943 "filer_window", filer_window);
944 filer_window->collection = COLLECTION(collection);
945 collection_set_item_size(filer_window->collection, 64, 64);
946 collection_set_functions(filer_window->collection,
947 draw_item, test_point);
949 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
950 gtk_signal_connect(GTK_OBJECT(filer_window->window),
951 "enter-notify-event",
952 GTK_SIGNAL_FUNC(pointer_in), filer_window);
953 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
954 GTK_SIGNAL_FUNC(focus_in), filer_window);
955 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
956 GTK_SIGNAL_FUNC(focus_out), filer_window);
957 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
958 filer_window_destroyed, filer_window);
960 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
961 open_item, filer_window);
962 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
963 show_menu, filer_window);
964 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
965 gain_selection, filer_window);
966 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
967 lose_selection, filer_window);
968 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
969 drag_selection, filer_window);
970 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
971 drag_data_get, filer_window);
972 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
973 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
974 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
975 GTK_SIGNAL_FUNC(selection_get), NULL);
976 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
977 target_table,
978 sizeof(target_table) / sizeof(*target_table));
980 drag_set_dest(collection);
982 if (panel)
984 int swidth, sheight, iwidth, iheight;
985 GtkWidget *frame, *win = filer_window->window;
987 collection_set_panel(filer_window->collection, TRUE);
989 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
990 iwidth = filer_window->collection->item_width;
991 iheight = filer_window->collection->item_height;
993 if (panel_side == TOP || panel_side == BOTTOM)
995 int height = iheight + PANEL_BORDER;
996 int y = panel_side == TOP
997 ? -PANEL_BORDER
998 : sheight - height - PANEL_BORDER;
1000 gtk_widget_set_usize(collection, swidth, height);
1001 gtk_widget_set_uposition(win, 0, y);
1003 else
1005 int width = iwidth + PANEL_BORDER;
1006 int x = panel_side == LEFT
1007 ? -PANEL_BORDER
1008 : swidth - width - PANEL_BORDER;
1010 gtk_widget_set_usize(collection, width, sheight);
1011 gtk_widget_set_uposition(win, x, 0);
1014 frame = gtk_frame_new(NULL);
1015 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1016 gtk_container_add(GTK_CONTAINER(frame), collection);
1017 gtk_container_add(GTK_CONTAINER(win), frame);
1019 gtk_widget_realize(win);
1020 make_panel_window(win->window);
1022 else
1024 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1025 "key_press_event",
1026 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1027 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1028 400, 200);
1030 hbox = gtk_hbox_new(FALSE, 0);
1031 gtk_container_add(GTK_CONTAINER(filer_window->window), hbox);
1033 gtk_box_pack_start(GTK_BOX(hbox), collection, TRUE, TRUE, 0);
1035 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1036 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1037 gtk_accel_group_attach(filer_keys,
1038 GTK_OBJECT(filer_window->window));
1041 gtk_widget_show_all(filer_window->window);
1042 number_of_windows++;
1044 load_default_pixmaps(collection->window);
1046 add_view(filer_window);
1047 scan_dir(filer_window);