r135: Added new Directory object and put all the scanning and updating code
[rox-filer/dt.git] / ROX-Filer / src / filer.c
blob79b0d5b80b14e2b79846c698f0cf2602f5d3a0ff
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 1999, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* filer.c - code for handling filer windows */
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29 #include <time.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkx.h>
33 #include <gdk/gdkkeysyms.h>
34 #include "collection.h"
36 #include "main.h"
37 #include "support.h"
38 #include "gui_support.h"
39 #include "filer.h"
40 #include "pixmaps.h"
41 #include "menu.h"
42 #include "dnd.h"
43 #include "apps.h"
44 #include "mount.h"
45 #include "type.h"
46 #include "options.h"
48 #define ROW_HEIGHT_LARGE 64
49 #define ROW_HEIGHT_FULL_INFO 44
50 #define MAX_ICON_HEIGHT 42
51 #define PANEL_BORDER 2
52 #define MIN_ITEM_WIDTH 64
54 FilerWindow *window_with_focus = NULL;
55 GdkFont *fixed_font = NULL;
57 static FilerWindow *window_with_selection = NULL;
59 /* Options bits */
60 static GtkWidget *create_options();
61 static void update_options();
62 static void set_options();
63 static void save_options();
64 static char *filer_ro_bindings(char *data);
65 static char *filer_toolbar(char *data);
67 static OptionsSection options =
69 "Filer window options",
70 create_options,
71 update_options,
72 set_options,
73 save_options
75 static gboolean o_toolbar = TRUE;
76 static GtkWidget *toggle_toolbar;
77 static gboolean o_ro_bindings = FALSE;
78 static GtkWidget *toggle_ro_bindings;
80 /* Static prototypes */
81 static void detach(FilerWindow *filer_window);
82 static void filer_window_destroyed(GtkWidget *widget,
83 FilerWindow *filer_window);
84 void show_menu(Collection *collection, GdkEventButton *event,
85 int number_selected, gpointer user_data);
86 static int sort_by_name(const void *item1, const void *item2);
87 static gint focus_in(GtkWidget *widget,
88 GdkEventFocus *event,
89 FilerWindow *filer_window);
90 static gint focus_out(GtkWidget *widget,
91 GdkEventFocus *event,
92 FilerWindow *filer_window);
93 static void add_item(FilerWindow *filer_window, DirItem *item);
94 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
95 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
96 static void add_button(GtkContainer *box, int pixmap,
97 GtkSignalFunc cb, gpointer data);
98 static GtkWidget *create_toolbar(FilerWindow *filer_window);
99 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
100 FilerWindow *window);
101 static int calc_width(FilerWindow *filer_window, DirItem *item);
102 static void draw_large_icon(GtkWidget *widget,
103 GdkRectangle *area,
104 DirItem *item,
105 gboolean selected);
106 static void draw_string(GtkWidget *widget,
107 GdkFont *font,
108 char *string,
109 int x,
110 int y,
111 int width,
112 gboolean selected);
113 static void draw_item_large(GtkWidget *widget,
114 CollectionItem *item,
115 GdkRectangle *area);
116 static void draw_item_full_info(GtkWidget *widget,
117 CollectionItem *colitem,
118 GdkRectangle *area);
119 static gboolean test_point_large(Collection *collection,
120 int point_x, int point_y,
121 CollectionItem *item,
122 int width, int height);
123 static gboolean test_point_full_info(Collection *collection,
124 int point_x, int point_y,
125 CollectionItem *item,
126 int width, int height);
127 static void update_display(Directory *dir,
128 DirAction action,
129 GPtrArray *items,
130 FilerWindow *filer_window);
131 void filer_change_to(FilerWindow *filer_window, char *path);
132 static void shrink_width(FilerWindow *filer_window);
134 static GdkAtom xa_string;
135 enum
137 TARGET_STRING,
138 TARGET_URI_LIST,
141 void filer_init()
143 xa_string = gdk_atom_intern("STRING", FALSE);
145 options_sections = g_slist_prepend(options_sections, &options);
146 option_register("filer_ro_bindings", filer_ro_bindings);
147 option_register("filer_toolbar", filer_toolbar);
149 fixed_font = gdk_font_load("fixed");
152 static gboolean if_deleted(gpointer item, gpointer removed)
154 int i = ((GPtrArray *) removed)->len;
155 DirItem **r = (DirItem **) ((GPtrArray *) removed)->pdata;
156 char *leafname = ((DirItem *) item)->leafname;
158 while (i--)
160 if (strcmp(leafname, r[i]->leafname) == 0)
161 return TRUE;
164 return FALSE;
167 static void update_item(FilerWindow *filer_window, DirItem *item)
169 int i;
171 i = collection_find_item(filer_window->collection, item, dir_item_cmp);
172 collection_draw_item(filer_window->collection, i, TRUE);
175 static void update_display(Directory *dir,
176 DirAction action,
177 GPtrArray *items,
178 FilerWindow *filer_window)
180 int i;
182 switch (action)
184 case DIR_ADD:
185 for (i = 0; i < items->len; i++)
187 DirItem *item = (DirItem *) items->pdata[i];
189 add_item(filer_window, item);
192 collection_qsort(filer_window->collection,
193 filer_window->sort_fn);
194 break;
195 case DIR_REMOVE:
196 collection_delete_if(filer_window->collection,
197 if_deleted,
198 items);
199 break;
200 case DIR_END_SCAN:
201 if (filer_window->window->window)
202 gdk_window_set_cursor(
203 filer_window->window->window,
204 NULL);
205 shrink_width(filer_window);
206 break;
207 case DIR_UPDATE:
208 for (i = 0; i < items->len; i++)
210 DirItem *item = (DirItem *) items->pdata[i];
212 update_item(filer_window, item);
214 break;
218 static void detach(FilerWindow *filer_window)
220 g_return_if_fail(filer_window->directory != NULL);
222 dir_detach(filer_window->directory,
223 (DirCallback) update_display, filer_window);
224 g_fscache_data_unref(dir_cache, filer_window->directory);
225 filer_window->directory = NULL;
228 static void filer_window_destroyed(GtkWidget *widget,
229 FilerWindow *filer_window)
231 if (window_with_selection == filer_window)
232 window_with_selection = NULL;
233 if (window_with_focus == filer_window)
234 window_with_focus = NULL;
236 if (filer_window->directory)
237 detach(filer_window);
239 g_free(filer_window->path);
240 g_free(filer_window);
242 if (--number_of_windows < 1)
243 gtk_main_quit();
246 static int calc_width(FilerWindow *filer_window, DirItem *item)
248 int pix_width = item->image->width;
250 switch (filer_window->display_style)
252 case FULL_INFO:
253 return pix_width + item->details_width + 12;
254 break;
255 default:
256 return MAX(pix_width, item->name_width) + 4;
257 break;
261 /* Add a single object to a directory display */
262 static void add_item(FilerWindow *filer_window, DirItem *item)
264 char *leafname = item->leafname;
265 int item_width;
267 if (leafname[0] == '.')
269 if (filer_window->show_hidden == FALSE || leafname[1] == '\0'
270 || (leafname[1] == '.' && leafname[2] == '\0'))
271 return;
274 item_width = calc_width(filer_window, item);
275 if (item_width > filer_window->collection->item_width)
276 collection_set_item_size(filer_window->collection,
277 item_width,
278 filer_window->collection->item_height);
279 collection_insert(filer_window->collection, item);
282 /* Is a point inside an item? */
283 static gboolean test_point_large(Collection *collection,
284 int point_x, int point_y,
285 CollectionItem *colitem,
286 int width, int height)
288 DirItem *item = (DirItem *) colitem->data;
289 GdkFont *font = GTK_WIDGET(collection)->style->font;
290 int text_height = font->ascent + font->descent;
291 MaskedPixmap *image = item->image;
292 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
293 int image_width = (image->width >> 1) + 2;
294 int text_width = (item->name_width >> 1) + 2;
295 int x_limit;
297 if (point_y < image_y)
298 return FALSE; /* Too high up (don't worry about too low) */
300 if (point_y <= image_y + image->height + 2)
301 x_limit = image_width;
302 else if (point_y > height - text_height - 2)
303 x_limit = text_width;
304 else
305 x_limit = MIN(image_width, text_width);
307 return ABS(point_x - (width >> 1)) < x_limit;
310 static gboolean test_point_full_info(Collection *collection,
311 int point_x, int point_y,
312 CollectionItem *colitem,
313 int width, int height)
315 DirItem *item = (DirItem *) colitem->data;
316 GdkFont *font = GTK_WIDGET(collection)->style->font;
317 MaskedPixmap *image = item->image;
318 int image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
320 if (point_x < image->width + 2)
321 return point_x > 2 && point_y > image_y;
323 return point_x > image->width + 2 &&
324 point_y > (height >> 1) - font->ascent - font->descent &&
325 point_y < (height >> 1) + fixed_font->ascent + fixed_font->descent;
328 static void draw_large_icon(GtkWidget *widget,
329 GdkRectangle *area,
330 DirItem *item,
331 gboolean selected)
333 MaskedPixmap *image = item->image;
334 int image_x = area->x + ((area->width - image->width) >> 1);
335 int image_y;
336 GdkGC *gc = selected ? widget->style->white_gc
337 : widget->style->black_gc;
338 if (!item->image)
339 return;
341 gdk_gc_set_clip_mask(gc, item->image->mask);
343 image_y = MAX(0, MAX_ICON_HEIGHT - image->height);
344 gdk_gc_set_clip_origin(gc, image_x, area->y + image_y);
345 gdk_draw_pixmap(widget->window, gc,
346 item->image->pixmap,
347 0, 0, /* Source x,y */
348 image_x, area->y + image_y, /* Dest x,y */
349 -1, MIN(image->height, MAX_ICON_HEIGHT));
351 if (selected)
353 gdk_gc_set_function(gc, GDK_INVERT);
354 gdk_draw_rectangle(widget->window,
356 TRUE, image_x, area->y + image_y,
357 image->width, image->height);
358 gdk_gc_set_function(gc, GDK_COPY);
361 if (item->flags & ITEM_FLAG_SYMLINK)
363 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
364 gdk_gc_set_clip_mask(gc,
365 default_pixmap[TYPE_SYMLINK].mask);
366 gdk_draw_pixmap(widget->window, gc,
367 default_pixmap[TYPE_SYMLINK].pixmap,
368 0, 0, /* Source x,y */
369 image_x, area->y + 8, /* Dest x,y */
370 -1, -1);
372 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
374 int type = item->flags & ITEM_FLAG_MOUNTED
375 ? TYPE_MOUNTED
376 : TYPE_UNMOUNTED;
377 gdk_gc_set_clip_origin(gc, image_x, area->y + 8);
378 gdk_gc_set_clip_mask(gc,
379 default_pixmap[type].mask);
380 gdk_draw_pixmap(widget->window, gc,
381 default_pixmap[type].pixmap,
382 0, 0, /* Source x,y */
383 image_x, area->y + 8, /* Dest x,y */
384 -1, -1);
387 gdk_gc_set_clip_mask(gc, NULL);
388 gdk_gc_set_clip_origin(gc, 0, 0);
391 static void draw_string(GtkWidget *widget,
392 GdkFont *font,
393 char *string,
394 int x,
395 int y,
396 int width,
397 gboolean selected)
399 int text_height = font->ascent + font->descent;
401 if (selected)
402 gtk_paint_flat_box(widget->style, widget->window,
403 GTK_STATE_SELECTED, GTK_SHADOW_NONE,
404 NULL, widget, "text",
405 x, y - font->ascent,
406 width,
407 text_height);
409 gdk_draw_text(widget->window,
410 font,
411 selected ? widget->style->white_gc
412 : widget->style->black_gc,
413 x, y,
414 string, strlen(string));
417 /* Return a string (valid until next call) giving details
418 * of this item.
420 char *details(DirItem *item)
422 mode_t m = item->mode;
423 static GString *buf = NULL;
425 if (!buf)
426 buf = g_string_new(NULL);
428 g_string_sprintf(buf, "%s, %c%c%c:%c%c%c:%c%c%c, %s",
429 S_ISDIR(m) ? "Dir" :
430 S_ISCHR(m) ? "Char" :
431 S_ISBLK(m) ? "Blck" :
432 S_ISLNK(m) ? "Link" :
433 S_ISSOCK(m) ? "Sock" :
434 S_ISFIFO(m) ? "Pipe" : "File",
436 m & S_IRUSR ? 'r' : '-',
437 m & S_IWUSR ? 'w' : '-',
438 '-',
440 m & S_IRGRP ? 'r' : '-',
441 m & S_IWGRP ? 'w' : '-',
442 '-',
444 m & S_IROTH ? 'r' : '-',
445 m & S_IWOTH ? 'w' : '-',
446 '-',
448 format_size(item->size));
450 return buf->str;
453 static void draw_item_full_info(GtkWidget *widget,
454 CollectionItem *colitem,
455 GdkRectangle *area)
457 DirItem *item = (DirItem *) colitem->data;
458 MaskedPixmap *image = item->image;
459 GdkFont *font = widget->style->font;
460 int text_x = area->x + image->width + 8;
461 int mid_y = area->y + (area->height >> 1);
462 gboolean selected = colitem->selected;
463 GdkRectangle pic_area;
465 pic_area.x = area->x;
466 pic_area.y = area->y;
467 pic_area.width = image->width + 8;
468 pic_area.height = area->height;
470 draw_large_icon(widget, &pic_area, item, selected);
472 draw_string(widget,
473 widget->style->font,
474 item->leafname,
475 text_x, mid_y - font->descent,
476 item->name_width,
477 selected);
478 draw_string(widget,
479 fixed_font,
480 details(item),
481 text_x, mid_y + font->ascent,
482 item->name_width,
483 selected);
486 static void draw_item_large(GtkWidget *widget,
487 CollectionItem *colitem,
488 GdkRectangle *area)
490 DirItem *item = (DirItem *) colitem->data;
491 GdkFont *font = widget->style->font;
492 int text_x = area->x + ((area->width - item->name_width) >> 1);
493 int text_y = area->y + area->height - font->descent - 2;
494 gboolean selected = colitem->selected;
496 draw_large_icon(widget, area, item, selected);
498 draw_string(widget,
499 widget->style->font,
500 item->leafname,
501 text_x, text_y, item->name_width,
502 selected);
505 void show_menu(Collection *collection, GdkEventButton *event,
506 int item, gpointer user_data)
508 show_filer_menu((FilerWindow *) user_data, event, item);
511 static void may_rescan(FilerWindow *filer_window)
513 g_return_if_fail(filer_window != NULL);
515 // XXX: g_fscache_data_update(dir_cache, filer_window->directory);
518 /* Callback to collection_delete_if() */
519 #if 0
520 static gboolean remove_deleted(gpointer item_data, gpointer data)
522 DirItem *item = (DirItem *) item_data;
524 if (item->flags & ITEM_FLAG_MAY_DELETE)
526 free_item(item);
527 return TRUE;
530 return FALSE;
532 #endif
534 /* Another app has grabbed the selection */
535 static gint collection_lose_selection(GtkWidget *widget,
536 GdkEventSelection *event)
538 if (window_with_selection &&
539 window_with_selection->collection == COLLECTION(widget))
541 FilerWindow *filer_window = window_with_selection;
542 window_with_selection = NULL;
543 collection_clear_selection(filer_window->collection);
546 return TRUE;
549 /* Someone wants us to send them the selection */
550 static void selection_get(GtkWidget *widget,
551 GtkSelectionData *selection_data,
552 guint info,
553 guint time,
554 gpointer data)
556 GString *reply, *header;
557 FilerWindow *filer_window;
558 int i;
559 Collection *collection;
561 filer_window = gtk_object_get_data(GTK_OBJECT(widget), "filer_window");
563 reply = g_string_new(NULL);
564 header = g_string_new(NULL);
566 switch (info)
568 case TARGET_STRING:
569 g_string_sprintf(header, " %s",
570 make_path(filer_window->path, "")->str);
571 break;
572 case TARGET_URI_LIST:
573 g_string_sprintf(header, " file://%s%s",
574 our_host_name(),
575 make_path(filer_window->path, "")->str);
576 break;
579 collection = filer_window->collection;
580 for (i = 0; i < collection->number_of_items; i++)
582 if (collection->items[i].selected)
584 DirItem *item =
585 (DirItem *) collection->items[i].data;
587 g_string_append(reply, header->str);
588 g_string_append(reply, item->leafname);
591 /* This works, but I don't think I like it... */
592 /* g_string_append_c(reply, ' '); */
594 gtk_selection_data_set(selection_data, xa_string,
595 8, reply->str + 1, reply->len - 1);
596 g_string_free(reply, TRUE);
597 g_string_free(header, TRUE);
600 /* No items are now selected. This might be because another app claimed
601 * the selection or because the user unselected all the items.
603 static void lose_selection(Collection *collection,
604 guint time,
605 gpointer user_data)
607 FilerWindow *filer_window = (FilerWindow *) user_data;
609 if (window_with_selection == filer_window)
611 window_with_selection = NULL;
612 gtk_selection_owner_set(NULL,
613 GDK_SELECTION_PRIMARY,
614 time);
618 static void gain_selection(Collection *collection,
619 guint time,
620 gpointer user_data)
622 FilerWindow *filer_window = (FilerWindow *) user_data;
624 if (gtk_selection_owner_set(GTK_WIDGET(collection),
625 GDK_SELECTION_PRIMARY,
626 time))
628 window_with_selection = filer_window;
630 else
631 collection_clear_selection(filer_window->collection);
634 static int sort_by_name(const void *item1, const void *item2)
636 return strcmp((*((DirItem **)item1))->leafname,
637 (*((DirItem **)item2))->leafname);
640 static int sort_by_type(const void *item1, const void *item2)
642 const DirItem *i1 = (DirItem *) ((CollectionItem *) item1)->data;
643 const DirItem *i2 = (DirItem *) ((CollectionItem *) item2)->data;
644 MIME_type *m1, *m2;
646 int diff = i1->base_type - i2->base_type;
648 if (!diff)
649 diff = (i1->flags & ITEM_FLAG_APPDIR)
650 - (i2->flags & ITEM_FLAG_APPDIR);
651 if (diff)
652 return diff > 0 ? 1 : -1;
654 m1 = i1->mime_type;
655 m2 = i2->mime_type;
657 if (m1 && m2)
659 diff = strcmp(m1->media_type, m2->media_type);
660 if (!diff)
661 diff = strcmp(m1->subtype, m2->subtype);
663 else if (m1 || m2)
664 diff = m1 ? 1 : -1;
665 else
666 diff = 0;
668 if (diff)
669 return diff > 0 ? 1 : -1;
671 return sort_by_name(item1, item2);
674 void open_item(Collection *collection,
675 gpointer item_data, int item_number,
676 gpointer user_data)
678 FilerWindow *filer_window = (FilerWindow *) user_data;
679 DirItem *item = (DirItem *) item_data;
680 GdkEventButton *event;
681 char *full_path;
682 GtkWidget *widget;
683 gboolean shift;
684 gboolean adjust; /* do alternative action */
686 event = (GdkEventButton *) gtk_get_current_event();
687 full_path = make_path(filer_window->path,
688 item->leafname)->str;
690 collection_wink_item(filer_window->collection, item_number);
692 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_BUTTON_PRESS)
694 shift = event->state & GDK_SHIFT_MASK;
695 adjust = (event->button != 1)
696 ^ ((event->state & GDK_CONTROL_MASK) != 0);
698 else
700 shift = FALSE;
701 adjust = FALSE;
704 widget = filer_window->window;
706 switch (item->base_type)
708 case TYPE_DIRECTORY:
709 if (item->flags & ITEM_FLAG_APPDIR && !shift)
711 run_app(make_path(filer_window->path,
712 item->leafname)->str);
713 if (adjust && !filer_window->panel)
714 gtk_widget_destroy(widget);
715 break;
717 if ((adjust ^ o_ro_bindings) || filer_window->panel)
718 filer_opendir(full_path, FALSE, BOTTOM);
719 else
720 filer_change_to(filer_window, full_path);
721 break;
722 case TYPE_FILE:
723 if (item->flags & ITEM_FLAG_EXEC_FILE
724 && !shift)
726 char *argv[] = {NULL, NULL};
728 argv[0] = full_path;
730 if (spawn_full(argv, getenv("HOME"), 0))
732 if (adjust && !filer_window->panel)
733 gtk_widget_destroy(widget);
735 else
736 report_error("ROX-Filer",
737 "Failed to fork() child");
739 else
741 GString *message;
742 MIME_type *type = shift ? &text_plain
743 : item->mime_type;
745 g_return_if_fail(type != NULL);
747 if (type_open(full_path, type))
749 if (adjust && !filer_window->panel)
750 gtk_widget_destroy(widget);
752 else
754 message = g_string_new(NULL);
755 g_string_sprintf(message, "No open "
756 "action specified for files of "
757 "this type (%s/%s)",
758 type->media_type,
759 type->subtype);
760 report_error("ROX-Filer", message->str);
761 g_string_free(message, TRUE);
764 break;
765 default:
766 report_error("open_item",
767 "I don't know how to open that");
768 break;
772 static gint pointer_in(GtkWidget *widget,
773 GdkEventCrossing *event,
774 FilerWindow *filer_window)
776 may_rescan(filer_window);
777 return FALSE;
780 static gint focus_in(GtkWidget *widget,
781 GdkEventFocus *event,
782 FilerWindow *filer_window)
784 window_with_focus = filer_window;
786 return FALSE;
789 static gint focus_out(GtkWidget *widget,
790 GdkEventFocus *event,
791 FilerWindow *filer_window)
793 /* TODO: Shade the cursor */
795 return FALSE;
798 /* Handle keys that can't be bound with the menu */
799 static gint key_press_event(GtkWidget *widget,
800 GdkEventKey *event,
801 FilerWindow *filer_window)
803 switch (event->keyval)
806 case GDK_Left:
807 move_cursor(-1, 0);
808 break;
809 case GDK_Right:
810 move_cursor(1, 0);
811 break;
812 case GDK_Up:
813 move_cursor(0, -1);
814 break;
815 case GDK_Down:
816 move_cursor(0, 1);
817 break;
818 case GDK_Return:
820 case GDK_BackSpace:
821 change_to_parent(filer_window);
822 return TRUE;
825 return FALSE;
828 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
830 filer_change_to(filer_window, getenv("HOME"));
833 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
835 change_to_parent(filer_window);
838 void change_to_parent(FilerWindow *filer_window)
840 filer_change_to(filer_window, make_path(filer_window->path, "..")->str);
843 void filer_change_to(FilerWindow *filer_window, char *path)
845 detach(filer_window);
846 g_free(filer_window->path);
847 filer_window->path = pathdup(path);
849 filer_window->directory = g_fscache_lookup(dir_cache,
850 filer_window->path);
851 if (filer_window->directory)
853 collection_clear(filer_window->collection);
854 gtk_window_set_title(GTK_WINDOW(filer_window->window),
855 filer_window->path);
856 gdk_window_set_cursor(filer_window->window->window,
857 gdk_cursor_new(GDK_WATCH));
858 dir_attach(filer_window->directory,
859 (DirCallback) update_display,
860 filer_window);
862 else
864 char *error;
866 error = g_strdup_printf("Directory '%s' is not accessible.",
867 path);
868 delayed_error("ROX-Filer", error);
869 g_free(error);
870 gtk_widget_destroy(filer_window->window);
874 DirItem *selected_item(Collection *collection)
876 int i;
878 g_return_val_if_fail(collection != NULL, NULL);
879 g_return_val_if_fail(IS_COLLECTION(collection), NULL);
880 g_return_val_if_fail(collection->number_selected == 1, NULL);
882 for (i = 0; i < collection->number_of_items; i++)
883 if (collection->items[i].selected)
884 return (DirItem *) collection->items[i].data;
886 g_warning("selected_item: number_selected is wrong\n");
888 return NULL;
891 static int filer_confirm_close(GtkWidget *widget, GdkEvent *event,
892 FilerWindow *window)
894 /* TODO: We can open lots of these - very irritating! */
895 return get_choice("Close panel?",
896 "You have tried to close a panel via the window "
897 "manager - I usually find that this is accidental... "
898 "really close?",
899 2, "Remove", "Cancel") != 0;
902 /* Make the items as narrow as possible */
903 static void shrink_width(FilerWindow *filer_window)
905 int i;
906 Collection *col = filer_window->collection;
907 int width = MIN_ITEM_WIDTH;
908 int this_width;
910 for (i = 0; i < col->number_of_items; i++)
912 this_width = calc_width(filer_window,
913 (DirItem *) col->items[i].data);
914 if (this_width > width)
915 width = this_width;
918 collection_set_item_size(filer_window->collection,
919 width,
920 filer_window->display_style == FULL_INFO ? ROW_HEIGHT_FULL_INFO
921 : ROW_HEIGHT_LARGE);
924 void filer_style_set(FilerWindow *filer_window, DisplayStyle style)
926 if (filer_window->display_style == style)
927 return;
929 filer_window->display_style = style;
930 switch (style)
932 case FULL_INFO:
933 collection_set_functions(filer_window->collection,
934 draw_item_full_info, test_point_full_info);
935 break;
936 default:
937 collection_set_functions(filer_window->collection,
938 draw_item_large, test_point_large);
939 break;
942 shrink_width(filer_window);
945 void filer_opendir(char *path, gboolean panel, Side panel_side)
947 GtkWidget *hbox, *scrollbar, *collection;
948 FilerWindow *filer_window;
949 GtkTargetEntry target_table[] =
951 {"text/uri-list", 0, TARGET_URI_LIST},
952 {"STRING", 0, TARGET_STRING},
955 filer_window = g_new(FilerWindow, 1);
956 filer_window->path = pathdup(path);
958 filer_window->directory = g_fscache_lookup(dir_cache, path);
959 if (!filer_window->directory)
961 char *error;
963 error = g_strdup_printf("Directory '%s' not found.", path);
964 delayed_error("ROX-Filer", error);
965 g_free(error);
966 g_free(filer_window->path);
967 g_free(filer_window);
968 return;
971 filer_window->show_hidden = FALSE;
972 filer_window->panel = panel;
973 filer_window->panel_side = panel_side;
974 filer_window->temp_item_selected = FALSE;
975 filer_window->sort_fn = sort_by_type;
976 filer_window->flags = (FilerFlags) 0;
977 filer_window->display_style = UNKNOWN_STYLE;
979 filer_window->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
980 gtk_window_set_title(GTK_WINDOW(filer_window->window),
981 filer_window->path);
983 collection = collection_new(NULL);
984 gtk_object_set_data(GTK_OBJECT(collection),
985 "filer_window", filer_window);
986 filer_window->collection = COLLECTION(collection);
988 gtk_widget_add_events(filer_window->window, GDK_ENTER_NOTIFY);
989 gtk_signal_connect(GTK_OBJECT(filer_window->window),
990 "enter-notify-event",
991 GTK_SIGNAL_FUNC(pointer_in), filer_window);
992 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_in_event",
993 GTK_SIGNAL_FUNC(focus_in), filer_window);
994 gtk_signal_connect(GTK_OBJECT(filer_window->window), "focus_out_event",
995 GTK_SIGNAL_FUNC(focus_out), filer_window);
996 gtk_signal_connect(GTK_OBJECT(filer_window->window), "destroy",
997 filer_window_destroyed, filer_window);
999 gtk_signal_connect(GTK_OBJECT(filer_window->collection), "open_item",
1000 open_item, filer_window);
1001 gtk_signal_connect(GTK_OBJECT(collection), "show_menu",
1002 show_menu, filer_window);
1003 gtk_signal_connect(GTK_OBJECT(collection), "gain_selection",
1004 gain_selection, filer_window);
1005 gtk_signal_connect(GTK_OBJECT(collection), "lose_selection",
1006 lose_selection, filer_window);
1007 gtk_signal_connect(GTK_OBJECT(collection), "drag_selection",
1008 drag_selection, filer_window);
1009 gtk_signal_connect(GTK_OBJECT(collection), "drag_data_get",
1010 drag_data_get, filer_window);
1011 gtk_signal_connect(GTK_OBJECT(collection), "selection_clear_event",
1012 GTK_SIGNAL_FUNC(collection_lose_selection), NULL);
1013 gtk_signal_connect (GTK_OBJECT(collection), "selection_get",
1014 GTK_SIGNAL_FUNC(selection_get), NULL);
1015 gtk_selection_add_targets(collection, GDK_SELECTION_PRIMARY,
1016 target_table,
1017 sizeof(target_table) / sizeof(*target_table));
1019 filer_style_set(filer_window, LARGE);
1020 dir_attach(filer_window->directory, (DirCallback) update_display,
1021 filer_window);
1022 drag_set_dest(collection);
1024 if (panel)
1026 int swidth, sheight, iwidth, iheight;
1027 GtkWidget *frame, *win = filer_window->window;
1029 collection_set_panel(filer_window->collection, TRUE);
1030 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1031 "delete_event",
1032 GTK_SIGNAL_FUNC(filer_confirm_close),
1033 filer_window);
1035 gdk_window_get_size(GDK_ROOT_PARENT(), &swidth, &sheight);
1036 iwidth = filer_window->collection->item_width;
1037 iheight = filer_window->collection->item_height;
1039 if (panel_side == TOP || panel_side == BOTTOM)
1041 int height = iheight + PANEL_BORDER;
1042 int y = panel_side == TOP
1043 ? -PANEL_BORDER
1044 : sheight - height - PANEL_BORDER;
1046 gtk_widget_set_usize(collection, swidth, height);
1047 gtk_widget_set_uposition(win, 0, y);
1049 else
1051 int width = iwidth + PANEL_BORDER;
1052 int x = panel_side == LEFT
1053 ? -PANEL_BORDER
1054 : swidth - width - PANEL_BORDER;
1056 gtk_widget_set_usize(collection, width, sheight);
1057 gtk_widget_set_uposition(win, x, 0);
1060 frame = gtk_frame_new(NULL);
1061 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1062 gtk_container_add(GTK_CONTAINER(frame), collection);
1063 gtk_container_add(GTK_CONTAINER(win), frame);
1065 gtk_widget_realize(win);
1066 if (override_redirect)
1067 gdk_window_set_override_redirect(win->window, TRUE);
1068 make_panel_window(win->window);
1070 else
1072 hbox = gtk_hbox_new(FALSE, 0);
1074 gtk_signal_connect(GTK_OBJECT(filer_window->window),
1075 "key_press_event",
1076 GTK_SIGNAL_FUNC(key_press_event), filer_window);
1077 gtk_window_set_default_size(GTK_WINDOW(filer_window->window),
1078 filer_window->display_style == LARGE ? 400 : 512,
1079 o_toolbar ? 220 : 200);
1081 gtk_container_add(GTK_CONTAINER(filer_window->window),
1082 hbox);
1083 if (o_toolbar)
1085 GtkWidget *vbox, *toolbar;
1088 vbox = gtk_vbox_new(FALSE, 0);
1089 gtk_box_pack_start(GTK_BOX(hbox), vbox,
1090 TRUE, TRUE, 0);
1091 toolbar = create_toolbar(filer_window);
1092 gtk_box_pack_start(GTK_BOX(vbox), toolbar,
1093 FALSE, TRUE, 0);
1095 gtk_box_pack_start(GTK_BOX(vbox), collection,
1096 TRUE, TRUE, 0);
1098 else
1099 gtk_box_pack_start(GTK_BOX(hbox), collection,
1100 TRUE, TRUE, 0);
1102 scrollbar = gtk_vscrollbar_new(COLLECTION(collection)->vadj);
1103 gtk_box_pack_start(GTK_BOX(hbox), scrollbar, FALSE, TRUE, 0);
1104 gtk_accel_group_attach(filer_keys,
1105 GTK_OBJECT(filer_window->window));
1108 gtk_widget_show_all(filer_window->window);
1109 number_of_windows++;
1112 static GtkWidget *create_toolbar(FilerWindow *filer_window)
1114 GtkWidget *frame, *box;
1116 frame = gtk_frame_new(NULL);
1117 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
1119 box = gtk_hbutton_box_new();
1120 gtk_button_box_set_child_size_default(16, 16);
1121 gtk_hbutton_box_set_spacing_default(2);
1122 gtk_button_box_set_layout(GTK_BUTTON_BOX(box), GTK_BUTTONBOX_START);
1123 gtk_container_add(GTK_CONTAINER(frame), box);
1124 add_button(GTK_CONTAINER(box), TOOLBAR_UP_ICON,
1125 GTK_SIGNAL_FUNC(toolbar_up_clicked),
1126 filer_window);
1127 add_button(GTK_CONTAINER(box), TOOLBAR_HOME_ICON,
1128 GTK_SIGNAL_FUNC(toolbar_home_clicked),
1129 filer_window);
1131 return frame;
1134 static void add_button(GtkContainer *box, int pixmap,
1135 GtkSignalFunc cb, gpointer data)
1137 GtkWidget *button, *icon;
1139 button = gtk_button_new();
1140 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
1141 gtk_container_add(box, button);
1143 icon = gtk_pixmap_new(default_pixmap[pixmap].pixmap,
1144 default_pixmap[pixmap].mask);
1145 gtk_container_add(GTK_CONTAINER(button), icon);
1146 gtk_signal_connect(GTK_OBJECT(button), "clicked", cb, data);
1149 /* Build up some option widgets to go in the options dialog, but don't
1150 * fill them in yet.
1152 static GtkWidget *create_options()
1154 GtkWidget *vbox;
1156 vbox = gtk_vbox_new(FALSE, 0);
1157 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
1159 toggle_ro_bindings =
1160 gtk_check_button_new_with_label("Use RISC OS mouse bindings");
1161 gtk_box_pack_start(GTK_BOX(vbox), toggle_ro_bindings, FALSE, TRUE, 0);
1163 toggle_toolbar =
1164 gtk_check_button_new_with_label("Show toolbar on new windows");
1165 gtk_box_pack_start(GTK_BOX(vbox), toggle_toolbar, FALSE, TRUE, 0);
1167 return vbox;
1170 /* Reflect current state by changing the widgets in the options box */
1171 static void update_options()
1173 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_ro_bindings),
1174 o_ro_bindings);
1175 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_toolbar),
1176 o_toolbar);
1179 /* Set current values by reading the states of the widgets in the options box */
1180 static void set_options()
1182 o_ro_bindings = gtk_toggle_button_get_active(
1183 GTK_TOGGLE_BUTTON(toggle_ro_bindings));
1184 o_toolbar = gtk_toggle_button_get_active(
1185 GTK_TOGGLE_BUTTON(toggle_toolbar));
1188 static void save_options()
1190 option_write("filer_ro_bindings", o_ro_bindings ? "1" : "0");
1191 option_write("filer_toolbar", o_toolbar ? "1" : "0");
1194 static char *filer_ro_bindings(char *data)
1196 o_ro_bindings = atoi(data) != 0;
1197 return NULL;
1200 static char *filer_toolbar(char *data)
1202 o_toolbar = atoi(data) != 0;
1203 return NULL;
1206 void update_dir(FilerWindow *filer_window)
1208 dir_update(filer_window->directory, filer_window->path);