r69: Tidied up the options system a bit. Added drag-and-drop section (with
[rox-filer.git] / ROX-Filer / src / filer.c
blobe87a49d05d680a085b734eb4804a6a3aa6a61155
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 /* When a child process that changes a directory dies we need to know
40 * which filer window to update. Use this table.
42 GHashTable *child_to_filer = NULL;
44 /* Link paths to GLists of filer windows */
45 GHashTable *path_to_window_list = NULL;
47 static FilerWindow *window_with_selection = NULL;
48 static FilerWindow *panel_with_timeout = NULL;
49 static gint panel_timeout;
51 /* Static prototypes */
52 static void filer_window_destroyed(GtkWidget *widget,
53 FilerWindow *filer_window);
54 static gboolean idle_scan_dir(gpointer data);
55 static void draw_item(GtkWidget *widget,
56 CollectionItem *item,
57 GdkRectangle *area);
58 void show_menu(Collection *collection, GdkEventButton *event,
59 int number_selected, gpointer user_data);
60 static int sort_by_name(const void *item1, const void *item2);
61 static void add_item(FilerWindow *filer_window, char *leafname);
62 static gboolean test_point(Collection *collection,
63 int point_x, int point_y,
64 CollectionItem *item,
65 int width, int height);
66 static void stop_scanning(FilerWindow *filer_window);
67 static gint focus_in(GtkWidget *widget,
68 GdkEventFocus *event,
69 FilerWindow *filer_window);
70 static gint focus_out(GtkWidget *widget,
71 GdkEventFocus *event,
72 FilerWindow *filer_window);
73 static void add_view(FilerWindow *filer_window);
74 static void remove_view(FilerWindow *filer_window);
76 static GdkAtom xa_string;
77 enum
79 TARGET_STRING,
80 TARGET_URI_LIST,
83 void filer_init()
85 xa_string = gdk_atom_intern("STRING", FALSE);
87 child_to_filer = g_hash_table_new(NULL, NULL);
88 path_to_window_list = g_hash_table_new(g_str_hash, g_str_equal);
92 /* When a filer window shows a directory, use this function to add
93 * it to the list of directories to be updated when the contents
94 * change.
96 static void add_view(FilerWindow *filer_window)
98 GList *list, *newlist;
100 g_return_if_fail(filer_window != NULL);
102 list = g_hash_table_lookup(path_to_window_list, filer_window->path);
103 newlist = g_list_prepend(list, filer_window);
104 if (newlist != list)
105 g_hash_table_insert(path_to_window_list, filer_window->path,
106 newlist);
109 /* When a filer window no longer shows a directory, call this to reverse
110 * the effect of add_view().
112 static void remove_view(FilerWindow *filer_window)
114 GList *list, *newlist;
116 g_return_if_fail(filer_window != NULL);
118 list = g_hash_table_lookup(path_to_window_list, filer_window->path);
119 newlist = g_list_remove(list, filer_window);
120 if (newlist != list)
121 g_hash_table_insert(path_to_window_list, filer_window->path,
122 newlist);
125 /* When a filer window is destroyed we call this for each entry in the
126 * child_to_filer hash table to remove old entries.
128 static gboolean child_eq(gpointer key, gpointer data, gpointer filer_window)
130 return data == filer_window;
133 /* Go though all the FileItems in a collection, freeing all the temp
134 * icons.
135 * TODO: Maybe we should cache icons?
137 static void free_temp_icons(FilerWindow *filer_window)
139 int i;
140 Collection *collection = filer_window->collection;
142 for (i = 0; i < collection->number_of_items; i++)
144 FileItem *item = (FileItem *) collection->items[i].data;
145 if (item->flags & ITEM_FLAG_TEMP_ICON)
147 gdk_pixmap_unref(item->image->pixmap);
148 gdk_pixmap_unref(item->image->mask);
149 g_free(item->image);
150 item->image = default_pixmap + TYPE_ERROR;
155 static void filer_window_destroyed(GtkWidget *widget,
156 FilerWindow *filer_window)
158 if (window_with_selection == filer_window)
159 window_with_selection = NULL;
160 if (window_with_focus == filer_window)
161 window_with_focus = NULL;
162 if (panel_with_timeout == filer_window)
164 /* Can this happen? */
165 panel_with_timeout = NULL;
166 gtk_timeout_remove(panel_timeout);
169 remove_view(filer_window);
170 g_hash_table_foreach_remove(child_to_filer, child_eq, filer_window);
172 free_temp_icons(filer_window);
173 if (filer_window->dir)
174 stop_scanning(filer_window);
175 g_free(filer_window->path);
176 g_free(filer_window);
178 if (--number_of_windows < 1)
179 gtk_main_quit();
182 static void stop_scanning(FilerWindow *filer_window)
184 g_return_if_fail(filer_window->dir != NULL);
186 closedir(filer_window->dir);
187 gtk_idle_remove(filer_window->idle_scan_id);
188 filer_window->dir = NULL;
191 /* This is called while we are scanning the directory */
192 static gboolean idle_scan_dir(gpointer data)
194 struct dirent *next;
195 FilerWindow *filer_window = (FilerWindow *) data;
199 next = readdir(filer_window->dir);
200 if (!next)
202 closedir(filer_window->dir);
203 filer_window->dir = NULL;
205 collection_set_item_size(filer_window->collection,
206 filer_window->scan_min_width,
207 filer_window->collection->item_height);
208 collection_qsort(filer_window->collection,
209 sort_by_name);
210 return FALSE; /* Finished */
213 add_item(filer_window, next->d_name);
214 } while (!gtk_events_pending());
216 return TRUE;
219 /* Add a single object to a directory display */
220 static void add_item(FilerWindow *filer_window, char *leafname)
222 FileItem *item;
223 int item_width;
224 struct stat info;
225 int base_type;
226 GString *path;
228 if (leafname[0] == '.')
230 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
231 || (leafname[1] == '.' && leafname[2] == '\0'))
232 return;
235 item = g_malloc(sizeof(FileItem));
236 item->leafname = g_strdup(leafname);
237 item->flags = 0;
239 path = make_path(filer_window->path, leafname);
240 if (lstat(path->str, &info))
241 base_type = TYPE_ERROR;
242 else
244 if (S_ISREG(info.st_mode))
245 base_type = TYPE_FILE;
246 else if (S_ISDIR(info.st_mode))
248 base_type = TYPE_DIRECTORY;
250 if (g_hash_table_lookup(mtab_mounts, path->str))
251 item->flags |= ITEM_FLAG_MOUNT_POINT
252 | ITEM_FLAG_MOUNTED;
253 else if (g_hash_table_lookup(fstab_mounts, path->str))
254 item->flags |= ITEM_FLAG_MOUNT_POINT;
256 else if (S_ISBLK(info.st_mode))
257 base_type = TYPE_BLOCK_DEVICE;
258 else if (S_ISCHR(info.st_mode))
259 base_type = TYPE_CHAR_DEVICE;
260 else if (S_ISFIFO(info.st_mode))
261 base_type = TYPE_PIPE;
262 else if (S_ISSOCK(info.st_mode))
263 base_type = TYPE_SOCKET;
264 else if (S_ISLNK(info.st_mode))
266 if (stat(path->str, &info))
268 base_type = TYPE_ERROR;
270 else
272 if (S_ISREG(info.st_mode))
273 base_type = TYPE_FILE;
274 else if (S_ISDIR(info.st_mode))
275 base_type = TYPE_DIRECTORY;
276 else if (S_ISBLK(info.st_mode))
277 base_type = TYPE_BLOCK_DEVICE;
278 else if (S_ISCHR(info.st_mode))
279 base_type = TYPE_CHAR_DEVICE;
280 else if (S_ISFIFO(info.st_mode))
281 base_type = TYPE_PIPE;
282 else if (S_ISSOCK(info.st_mode))
283 base_type = TYPE_SOCKET;
284 else
285 base_type = TYPE_UNKNOWN;
288 item->flags |= ITEM_FLAG_SYMLINK;
290 else
291 base_type = TYPE_UNKNOWN;
294 item->base_type = base_type;
296 item->mime_type = type_from_path(path->str);
298 if (base_type == TYPE_DIRECTORY)
300 /* Might be an application directory - better check... */
301 g_string_append(path, "/AppRun");
302 if (!stat(path->str, &info))
304 item->flags |= ITEM_FLAG_APPDIR;
308 if (item->flags & ITEM_FLAG_APPDIR) /* path still ends /AppRun */
310 MaskedPixmap *app_icon;
312 g_string_truncate(path, path->len - 3);
313 g_string_append(path, "Icon.xpm");
314 app_icon = load_pixmap_from(filer_window->window, path->str);
315 if (app_icon)
317 item->image = app_icon;
318 item->flags |= ITEM_FLAG_TEMP_ICON;
320 else
321 item->image = default_pixmap + TYPE_APPDIR;
323 else
325 if (base_type == TYPE_FILE &&
326 (info.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
328 item->image = default_pixmap + TYPE_EXEC_FILE;
329 item->flags |= ITEM_FLAG_EXEC_FILE;
331 else if (base_type == TYPE_FILE)
333 item->image = type_to_icon(filer_window->window,
334 item->mime_type);
336 else
337 item->image = default_pixmap + base_type;
340 item->text_width = gdk_string_width(filer_window->window->style->font,
341 leafname);
343 /* XXX: Must be a better way... */
344 item->pix_width = ((GdkPixmapPrivate *) item->image->pixmap)->width;
345 item->pix_height = ((GdkPixmapPrivate *) item->image->pixmap)->height;
347 item_width = MAX(item->pix_width, item->text_width) + 4;
349 if (item_width > filer_window->scan_min_width)
350 filer_window->scan_min_width = item_width;
352 if (item_width > filer_window->collection->item_width)
353 collection_set_item_size(filer_window->collection,
354 item_width,
355 filer_window->collection->item_height);
357 collection_insert(filer_window->collection, item);
360 static gboolean test_point(Collection *collection,
361 int point_x, int point_y,
362 CollectionItem *colitem,
363 int width, int height)
365 FileItem *item = (FileItem *) colitem->data;
366 GdkFont *font = GTK_WIDGET(collection)->style->font;
367 int text_height = font->ascent + font->descent;
368 int x_off = ABS(point_x - (width >> 1));
369 int image_y = MAX(0, MAX_ICON_HEIGHT - item->pix_height);
371 if (x_off <= (item->pix_width >> 1) + 2 &&
372 point_y >= image_y && point_y <= image_y + item->pix_height)
373 return TRUE;
375 if (x_off <= (item->text_width >> 1) + 2 &&
376 point_y > height - text_height - 2)
377 return TRUE;
379 return FALSE;
382 static void draw_item(GtkWidget *widget,
383 CollectionItem *colitem,
384 GdkRectangle *area)
386 FileItem *item = (FileItem *) colitem->data;
387 GdkGC *gc = colitem->selected ? widget->style->white_gc
388 : widget->style->black_gc;
389 int image_x = area->x + ((area->width - item->pix_width) >> 1);
390 GdkFont *font = widget->style->font;
391 int text_x = area->x + ((area->width - item->text_width) >> 1);
392 int text_y = area->y + area->height - font->descent - 2;
393 int text_height = font->ascent + font->descent;
395 if (item->image)
397 int image_y;
399 gdk_gc_set_clip_mask(gc, item->image->mask);
401 image_y = MAX(0, MAX_ICON_HEIGHT - item->pix_height);
402 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
403 gdk_draw_pixmap(widget->window, gc,
404 item->image->pixmap,
405 0, 0, /* Source x,y */
406 image_x, area->y + image_y, /* Dest x,y */
407 -1, MIN(item->pix_height, MAX_ICON_HEIGHT));
409 if (item->flags & ITEM_FLAG_SYMLINK)
411 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
412 gdk_gc_set_clip_mask(gc,
413 default_pixmap[TYPE_SYMLINK].mask);
414 gdk_draw_pixmap(widget->window, gc,
415 default_pixmap[TYPE_SYMLINK].pixmap,
416 0, 0, /* Source x,y */
417 image_x, area->y + 8, /* Dest x,y */
418 -1, -1);
420 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
422 int type = item->flags & ITEM_FLAG_MOUNTED
423 ? TYPE_MOUNTED
424 : TYPE_UNMOUNTED;
425 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
426 gdk_gc_set_clip_mask(gc,
427 default_pixmap[type].mask);
428 gdk_draw_pixmap(widget->window, gc,
429 default_pixmap[type].pixmap,
430 0, 0, /* Source x,y */
431 image_x, area->y + 8, /* Dest x,y */
432 -1, -1);
435 gdk_gc_set_clip_mask(gc, NULL);
436 gdk_gc_set_clip_origin(gc, 0, 0);
439 if (colitem->selected)
440 gtk_paint_flat_box(widget->style, widget->window,
441 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
442 NULL, widget, "text",
443 text_x, text_y - font->ascent,
444 item->text_width,
445 text_height);
447 gdk_draw_text(widget->window,
448 widget->style->font,
449 colitem->selected ? widget->style->white_gc
450 : widget->style->black_gc,
451 text_x, text_y,
452 item->leafname, strlen(item->leafname));
455 void show_menu(Collection *collection, GdkEventButton *event,
456 int item, gpointer user_data)
458 show_filer_menu((FilerWindow *) user_data, event, item);
461 static void may_rescan(FilerWindow *filer_window)
463 struct stat info;
465 g_return_if_fail(filer_window != NULL);
467 if (stat(filer_window->path, &info))
469 delayed_error("ROX-Filer", "Directory deleted");
470 gtk_widget_destroy(filer_window->window);
472 else if (info.st_mtime > filer_window->m_time)
474 if (filer_window->dir)
475 filer_window->flags |= FILER_NEEDS_RESCAN;
476 else
477 scan_dir(filer_window);
481 void scan_dir(FilerWindow *filer_window)
483 struct stat info;
485 if (filer_window->dir)
486 stop_scanning(filer_window);
487 if (panel_with_timeout == filer_window)
489 panel_with_timeout = NULL;
490 gtk_timeout_remove(panel_timeout);
493 mount_update();
495 free_temp_icons(filer_window);
496 collection_clear(filer_window->collection);
497 gtk_window_set_title(GTK_WINDOW(filer_window->window),
498 filer_window->path);
500 if (stat(filer_window->path, &info))
502 report_error("Error statting directory", g_strerror(errno));
503 return;
505 filer_window->m_time = info.st_mtime;
507 filer_window->dir = opendir(filer_window->path);
508 if (!filer_window->dir)
510 report_error("Error scanning directory", g_strerror(errno));
511 return;
514 filer_window->scan_min_width = 64;
516 filer_window->idle_scan_id = gtk_idle_add(idle_scan_dir, filer_window);
519 /* Another app has grabbed the selection */
520 static gint collection_lose_selection(GtkWidget *widget,
521 GdkEventSelection *event)
523 if (window_with_selection &&
524 window_with_selection->collection == COLLECTION(widget))
526 FilerWindow *filer_window = window_with_selection;
527 window_with_selection = NULL;
528 collection_clear_selection(filer_window->collection);
531 return TRUE;
534 /* Someone wants us to send them the selection */
535 static void selection_get(GtkWidget *widget,
536 GtkSelectionData *selection_data,
537 guint info,
538 guint time,
539 gpointer data)
541 GString *reply, *header;
542 FilerWindow *filer_window;
543 int i;
544 Collection *collection;
546 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
548 reply = g_string_new(NULL);
549 header = g_string_new(NULL);
551 switch (info)
553 case TARGET_STRING:
554 g_string_sprintf(header, " %s",
555 make_path(filer_window->path, "")->str);
556 break;
557 case TARGET_URI_LIST:
558 g_string_sprintf(header, " file://%s%s",
559 our_host_name(),
560 make_path(filer_window->path, "")->str);
561 break;
564 collection = filer_window->collection;
565 for (i = 0; i < collection->number_of_items; i++)
567 if (collection->items[i].selected)
569 FileItem *item =
570 (FileItem *) collection->items[i].data;
572 g_string_append(reply, header->str);
573 g_string_append(reply, item->leafname);
576 g_string_append_c(reply, ' ');
578 gtk_selection_data_set(selection_data, xa_string,
579 8, reply->str + 1, reply->len - 1);
580 g_string_free(reply, TRUE);
581 g_string_free(header, TRUE);
584 /* No items are now selected. This might be because another app claimed
585 * the selection or because the user unselected all the items.
587 static void lose_selection(Collection *collection,
588 guint time,
589 gpointer user_data)
591 FilerWindow *filer_window = (FilerWindow *) user_data;
593 if (window_with_selection == filer_window)
595 window_with_selection = NULL;
596 gtk_selection_owner_set(NULL,
597 GDK_SELECTION_PRIMARY,
598 time);
602 static void gain_selection(Collection *collection,
603 guint time,
604 gpointer user_data)
606 FilerWindow *filer_window = (FilerWindow *) user_data;
608 if (gtk_selection_owner_set(GTK_WIDGET(collection),
609 GDK_SELECTION_PRIMARY,
610 time))
612 window_with_selection = filer_window;
614 else
615 collection_clear_selection(filer_window->collection);
618 static int sort_by_name(const void *item1, const void *item2)
620 return strcmp((*((FileItem **)item1))->leafname,
621 (*((FileItem **)item2))->leafname);
624 static gint clear_panel_hilight(gpointer data)
626 collection_set_cursor_item(panel_with_timeout->collection, -1);
627 panel_with_timeout = NULL;
629 return FALSE;
632 /* It is possible to highlight an item briefly on a panel by calling this
633 * function.
635 void panel_set_timeout(FilerWindow *filer_window, gulong msec)
637 if (panel_with_timeout)
639 /* Can't have two timeouts at once */
640 gtk_timeout_remove(panel_timeout);
641 clear_panel_hilight(NULL);
644 if (filer_window)
646 panel_with_timeout = filer_window;
647 panel_timeout = gtk_timeout_add(msec,
648 clear_panel_hilight, NULL);
652 void open_item(Collection *collection,
653 gpointer item_data, int item_number,
654 gpointer user_data)
656 FilerWindow *filer_window = (FilerWindow *) user_data;
657 FileItem *item = (FileItem *) item_data;
658 GdkEventButton *event;
659 char *full_path;
660 GtkWidget *widget;
661 gboolean shift, adjust;
663 event = (GdkEventButton *) gtk_get_current_event();
664 full_path = make_path(filer_window->path, item->leafname)->str;
666 if (filer_window->panel)
668 panel_set_timeout(NULL, 0);
669 collection_set_cursor_item(collection, item_number);
670 gdk_flush();
671 panel_set_timeout(filer_window, 200);
674 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_BUTTON_PRESS)
676 shift = event->state & GDK_SHIFT_MASK;
677 adjust = event->button != 1 || event->state & GDK_CONTROL_MASK;
679 else
681 shift = FALSE;
682 adjust = FALSE;
685 widget = filer_window->window;
687 switch (item->base_type)
689 case TYPE_DIRECTORY:
690 if (item->flags & ITEM_FLAG_APPDIR && !shift)
692 run_app(make_path(filer_window->path,
693 item->leafname)->str);
694 if (adjust && !filer_window->panel)
695 gtk_widget_destroy(widget);
696 break;
698 if (adjust || filer_window->panel)
699 filer_opendir(full_path, FALSE, BOTTOM);
700 else
702 remove_view(filer_window);
703 filer_window->path = pathdup(full_path);
704 add_view(filer_window);
705 scan_dir(filer_window);
707 break;
708 case TYPE_FILE:
709 if (item->flags & ITEM_FLAG_EXEC_FILE)
711 char *argv[] = {full_path, NULL};
713 if (spawn_full(argv, getenv("HOME"), 0))
715 if (adjust && !filer_window->panel)
716 gtk_widget_destroy(widget);
718 else
719 report_error("ROX-Filer",
720 "Failed to fork() child");
722 else
724 GString *message;
725 MIME_type *type = item->mime_type;
727 if (type && type_open(full_path, type))
729 if (adjust && !filer_window->panel)
730 gtk_widget_destroy(widget);
732 else
734 message = g_string_new(NULL);
735 g_string_sprintf(message, "No open "
736 "action specified for files of "
737 "this type (%s/%s)",
738 type->media_type,
739 type->subtype);
740 report_error("ROX-Filer", message->str);
741 g_string_free(message, TRUE);
744 break;
745 default:
746 report_error("open_item",
747 "I don't know how to open that");
748 break;
752 static gint pointer_in(GtkWidget *widget,
753 GdkEventCrossing *event,
754 FilerWindow *filer_window)
756 may_rescan(filer_window);
757 return FALSE;
760 static gint focus_in(GtkWidget *widget,
761 GdkEventFocus *event,
762 FilerWindow *filer_window)
764 window_with_focus = filer_window;
766 return FALSE;
769 static gint focus_out(GtkWidget *widget,
770 GdkEventFocus *event,
771 FilerWindow *filer_window)
773 /* TODO: Shade the cursor */
775 return FALSE;
778 /* Handle keys that can't be bound with the menu */
779 static gint key_press_event(GtkWidget *widget,
780 GdkEventKey *event,
781 FilerWindow *filer_window)
783 switch (event->keyval)
786 case GDK_Left:
787 move_cursor(-1, 0);
788 break;
789 case GDK_Right:
790 move_cursor(1, 0);
791 break;
792 case GDK_Up:
793 move_cursor(0, -1);
794 break;
795 case GDK_Down:
796 move_cursor(0, 1);
797 break;
798 case GDK_Return:
800 case GDK_BackSpace:
801 remove_view(filer_window);
802 filer_window->path = pathdup(make_path(
803 filer_window->path,
804 "..")->str);
805 add_view(filer_window);
806 scan_dir(filer_window);
807 return TRUE;
810 return FALSE;
813 FileItem *selected_item(Collection *collection)
815 int i;
817 g_return_val_if_fail(collection != NULL, NULL);
818 g_return_val_if_fail(IS_COLLECTION(collection), NULL);
819 g_return_val_if_fail(collection->number_selected == 1, NULL);
821 for (i = 0; i < collection->number_of_items; i++)
822 if (collection->items[i].selected)
823 return (FileItem *) collection->items[i].data;
825 g_warning("selected_item: number_selected is wrong\n");
827 return NULL;
830 /* Refresh all windows onto this directory */
831 void refresh_dirs(char *path)
833 char *real;
834 GList *list, *next;
836 real = pathdup(path);
837 list = g_hash_table_lookup(path_to_window_list, real);
838 g_free(real);
840 while (list)
842 next = list->next;
843 may_rescan((FilerWindow *) list->data);
844 list = next;
848 void filer_opendir(char *path, gboolean panel, Side panel_side)
850 GtkWidget *hbox, *scrollbar, *collection;
851 FilerWindow *filer_window;
852 GtkTargetEntry target_table[] =
854 {"text/uri-list", 0, TARGET_URI_LIST},
855 {"STRING", 0, TARGET_STRING},
858 filer_window = g_malloc(sizeof(FilerWindow));
859 filer_window->path = pathdup(path);
860 filer_window->dir = NULL; /* Not scanning */
861 filer_window->show_hidden = FALSE;
862 filer_window->panel = panel;
863 filer_window->panel_side = panel_side;
864 filer_window->temp_item_selected = FALSE;
866 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
868 collection = collection_new(NULL);
869 gtk_object_set_data(GTK_OBJECT(collection),
870 "filer_window", filer_window);
871 filer_window->collection = COLLECTION(collection);
872 collection_set_item_size(filer_window->collection, 64, 64);
873 collection_set_functions(filer_window->collection,
874 draw_item, test_point);
876 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
877 gtk_signal_connect(GTK_OBJECT(filer_window->window),
878 "enter-notify-event",
879 GTK_SIGNAL_FUNC(pointer_in), filer_window);
880 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
881 GTK_SIGNAL_FUNC(focus_in), filer_window);
882 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
883 GTK_SIGNAL_FUNC(focus_out), filer_window);
884 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
885 filer_window_destroyed, filer_window);
887 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
888 open_item, filer_window);
889 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
890 show_menu, filer_window);
891 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
892 gain_selection, filer_window);
893 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
894 lose_selection, filer_window);
895 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
896 drag_selection, filer_window);
897 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
898 drag_data_get, filer_window);
899 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
900 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
901 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
902 GTK_SIGNAL_FUNC(selection_get), NULL);
903 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
904 target_table,
905 sizeof(target_table) / sizeof(*target_table));
907 drag_set_dest(collection);
909 if (panel)
911 int swidth, sheight, iwidth, iheight;
912 GtkWidget *frame, *win = filer_window->window;
914 collection_set_panel(filer_window->collection, TRUE);
916 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
917 iwidth = filer_window->collection->item_width;
918 iheight = filer_window->collection->item_height;
920 if (panel_side == TOP || panel_side == BOTTOM)
922 int height = iheight + PANEL_BORDER;
923 int y = panel_side == TOP
924 ? -PANEL_BORDER
925 : sheight - height - PANEL_BORDER;
927 gtk_widget_set_usize(collection, swidth, height);
928 gtk_widget_set_uposition(win, 0, y);
930 else
932 int width = iwidth + PANEL_BORDER;
933 int x = panel_side == LEFT
934 ? -PANEL_BORDER
935 : swidth - width - PANEL_BORDER;
937 gtk_widget_set_usize(collection, width, sheight);
938 gtk_widget_set_uposition(win, x, 0);
941 frame = gtk_frame_new(NULL);
942 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
943 gtk_container_add(GTK_CONTAINER(frame), collection);
944 gtk_container_add(GTK_CONTAINER(win), frame);
946 gtk_widget_realize(win);
947 make_panel_window(win->window);
949 else
951 gtk_signal_connect(GTK_OBJECT(filer_window->window),
952 "key_press_event",
953 GTK_SIGNAL_FUNC(key_press_event), filer_window);
954 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
955 400, 200);
957 hbox = gtk_hbox_new(FALSE, 0);
958 gtk_container_add(GTK_CONTAINER(filer_window->window), hbox);
960 gtk_box_pack_start(GTK_BOX(hbox), collection, TRUE, TRUE, 0);
962 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
963 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
964 gtk_accel_group_attach(filer_keys,
965 GTK_OBJECT(filer_window->window));
968 gtk_widget_show_all(filer_window->window);
969 number_of_windows++;
971 load_default_pixmaps(collection->window);
973 add_view(filer_window);
974 scan_dir(filer_window);