2001-05-30 Ludovic Drolez <ludovic.drolez@freealter.com>
[midnight-commander.git] / gnome / gscreen.c
blob9dbabdc09e266e6ea0f30cfcb2aa4cb00335430a
1 /* GNU Midnight Commander -- GNOME edition
3 * Directory display routines
5 * Copyright (C) 1997 The Free Software Foundation
7 * Authors: Miguel de Icaza
8 * Federico Mena
9 */
11 #include <config.h>
12 #include <string.h>
13 #include "x.h"
14 #include "util.h"
15 #include "global.h"
16 #include "dir.h"
17 #include "command.h"
18 #include "panel.h"
19 #define WANT_WIDGETS /* bleah */
20 #include "main.h"
21 #include "color.h"
22 #include "mouse.h"
23 #include "layout.h" /* get_panel_widget */
24 #include "ext.h" /* regex_command */
25 #include "cmd.h" /* copy_cmd, ren_cmd, delete_cmd, ... */
26 #include "gscreen.h"
27 #include "dir.h"
28 #include "dialog.h"
29 #include "setup.h"
30 #include "file.h"
31 #include "fileopctx.h"
32 #include "gdesktop.h"
33 #include "gdnd.h"
34 #include "gtkdtree.h"
35 #include "gpopup.h"
36 #include "gcmd.h"
37 #include "gicon.h"
38 #include "gtkflist.h"
39 #include "../vfs/vfs.h"
40 #include <gdk/gdkprivate.h>
42 #ifndef MAX
43 # define MAX(a,b) ((a) > (b) ? a : b)
44 #endif
46 /* Offsets within the default_column_width array for the different listing types */
47 static const int column_width_pos[LIST_TYPES] = {
48 GMC_COLUMNS_BRIEF,
50 -1,
51 GMC_COLUMNS_BRIEF + GMC_COLUMNS_DETAILED,
55 /* Default column widths for file listings */
56 int default_column_width[GMC_COLUMNS];
58 /* default format for custom view */
59 char* default_user_format = NULL;
61 /* Whether to display the tree view on the left */
62 int tree_panel_visible = -1;
64 /* The pixmaps */
65 #include "dir-close.xpm"
66 #include "link.xpm"
67 #include "dev.xpm"
69 /* Timeout for auto-scroll on drag */
70 #define SCROLL_TIMEOUT 100
72 /* Timeout for opening a tree branch on drag */
73 #define TREE_OPEN_TIMEOUT 1000
75 /* This is used to initialize our pixmaps */
76 static int pixmaps_ready;
77 GdkPixmap *icon_directory_pixmap;
78 GdkBitmap *icon_directory_mask;
79 GdkPixmap *icon_link_pixmap;
80 GdkBitmap *icon_link_mask;
81 GdkPixmap *icon_dev_pixmap;
82 GdkBitmap *icon_dev_mask;
84 static GtkTargetEntry drag_types [] = {
85 { TARGET_URI_LIST_TYPE, 0, TARGET_URI_LIST },
86 { TARGET_TEXT_PLAIN_TYPE, 0, TARGET_TEXT_PLAIN },
87 { TARGET_URL_TYPE, 0, TARGET_URL }
90 static GtkTargetEntry drop_types [] = {
91 { TARGET_URI_LIST_TYPE, 0, TARGET_URI_LIST },
92 { TARGET_URL_TYPE, 0, TARGET_URL }
95 #define ELEMENTS(x) (sizeof (x) / sizeof (x[0]))
97 /* GtkWidgets with the shaped windows for dragging */
98 GtkWidget *drag_directory = NULL;
99 GtkWidget *drag_directory_ok = NULL;
100 GtkWidget *drag_multiple = NULL;
101 GtkWidget *drag_multiple_ok = NULL;
103 static void file_list_popup (GdkEventButton *event, WPanel *panel);
106 void
107 repaint_file (WPanel *panel, int file_index, int move, int attr, int isstatus)
112 * Invoked by the generic code: show current working directory
114 void
115 show_dir (WPanel *panel)
117 assign_text (panel->current_dir, panel->cwd);
118 update_input (panel->current_dir, 1);
119 gtk_window_set_title (GTK_WINDOW (panel->xwindow), panel->cwd);
123 * Utility routine: Try to load a bitmap for a file_entry
125 static void
126 panel_file_list_set_type_bitmap (GtkCList *cl, int row, int column, int color, file_entry *fe)
128 /* Here, add more icons */
129 switch (color){
130 case DIRECTORY_COLOR:
131 gtk_clist_set_pixmap (cl, row, column, icon_directory_pixmap, icon_directory_mask);
132 break;
133 case LINK_COLOR:
134 gtk_clist_set_pixmap (cl, row, column, icon_link_pixmap, icon_link_mask);
135 break;
136 case DEVICE_COLOR:
137 gtk_clist_set_pixmap (cl, row, column, icon_dev_pixmap, icon_dev_mask);
138 break;
142 static void
143 panel_cancel_drag_scroll (WPanel *panel)
145 g_return_if_fail (panel != NULL);
147 if (panel->timer_id != -1){
148 gtk_timeout_remove (panel->timer_id);
149 panel->timer_id = -1;
154 * Sets the color attributes for a given row.
156 static void
157 panel_file_list_set_row_colors (GtkCList *cl, int row, int color_pair)
159 gtk_clist_set_foreground (cl, row, gmc_color_pairs [color_pair].fore);
160 gtk_clist_set_background (cl, row, gmc_color_pairs [color_pair].back);
164 * Update the status of the back and forward history buttons.
165 * Called from the generic code
167 void
168 x_panel_update_marks (WPanel *panel)
170 int ff = panel->dir_history->next ? 1 : 0;
171 int bf = panel->dir_history->prev ? 1 : 0;
173 if (!panel->fwd_b)
174 return;
176 gtk_widget_set_sensitive (panel->fwd_b, ff);
177 gtk_widget_set_sensitive (panel->back_b, bf);
180 static GtkAdjustment *
181 scrolled_window_get_vadjustment (GtkScrolledWindow *sw)
183 GtkRange *vsb = GTK_RANGE (sw->vscrollbar);
184 GtkAdjustment *va = vsb->adjustment;
186 return va;
190 * Listing view: Load the contents
192 static void
193 panel_fill_panel_list (WPanel *panel)
195 const int top = panel->count;
196 const int items = panel->format->items;
197 const int selected = panel->selected;
198 GtkCList *cl = CLIST_FROM_SW (panel->list);
199 int i, col, type_col, color;
200 int width, p;
201 char **texts;
202 GtkAdjustment *va;
203 double clist_v_pos;
205 texts = g_new (char *, items + 1);
207 /* CList doesn't restore its vertical scroll position when its cleared,
208 * so we need to do it manually.
211 /* Save the vertical position */
212 va = scrolled_window_get_vadjustment (panel->list);
213 clist_v_pos = va->value;
215 gtk_clist_freeze (GTK_CLIST (cl));
216 gtk_clist_clear (GTK_CLIST (cl));
218 /* which column holds the type information */
219 type_col = -1;
221 g_assert (items == cl->columns);
223 texts [items] = NULL;
224 for (i = 0; i < top; i++){
225 file_entry *fe = &panel->dir.list [i];
226 format_e *format = panel->format;
227 int n;
229 for (col = 0; format; format = format->next){
230 if (!format->use_in_gui)
231 continue;
233 if (type_col == -1)
234 if (strcmp (format->id, "type") == 0)
235 type_col = col;
237 if (!format->string_fn)
238 texts[col] = "";
239 else
240 texts[col] = (* format->string_fn) (fe, 10);
241 col++;
244 n = gtk_clist_append (cl, texts);
246 /* Do not let the user select .. */
247 if (strcmp (fe->fname, "..") == 0)
248 gtk_clist_set_selectable (cl, n, FALSE);
250 color = file_compute_color (NORMAL, fe);
251 panel_file_list_set_row_colors (cl, i, color);
253 if (type_col != -1)
254 panel_file_list_set_type_bitmap (cl, i, type_col, color, fe);
256 if (fe->f.marked)
257 gtk_clist_select_row (cl, i, 0);
258 else
259 gtk_clist_unselect_row (cl, i, 0);
263 g_free (texts);
265 /* This is needed as the gtk_clist_append changes selected under us :-( */
266 panel->selected = selected;
268 p = column_width_pos[panel->list_type]; /* offset in column_width */
269 g_assert (p >= 0);
270 for (i = 0; i < items; i++) {
271 width = panel->column_width[p + i];
272 if (width == 0)
273 width = gtk_clist_optimal_column_width (cl, i);
275 gtk_clist_set_column_width (cl, i, width);
278 /* Now restore the clist's vertical position */
279 gtk_adjustment_set_value (va, clist_v_pos);
281 gtk_clist_thaw (GTK_CLIST (cl));
285 * Icon view: load the panel contents
287 static void
288 panel_fill_panel_icons (WPanel *panel)
290 const int top = panel->count;
291 const int selected = panel->selected;
292 GnomeIconList *icons = ILIST_FROM_SW (panel->icons);
293 int i;
294 GdkImlibImage *image;
296 gnome_icon_list_freeze (icons);
297 gnome_icon_list_clear (icons);
299 for (i = 0; i < top; i++) {
300 file_entry *fe = &panel->dir.list [i];
301 int p;
303 image = gicon_get_icon_for_file (panel->cwd, fe, TRUE);
304 p = gnome_icon_list_append_imlib (icons, image, fe->fname);
306 if (fe->f.marked)
307 gnome_icon_list_select_icon (icons, p);
308 else
309 gnome_icon_list_unselect_icon (icons, p);
312 /* This is needed as the gtk_gnome_icon_list_append_imlib changes selected under us :-( */
313 panel->selected = selected;
315 gnome_icon_list_thaw (icons);
319 * Invoked from the generic code to fill the display
321 void
322 x_fill_panel (WPanel *panel)
324 if (panel->list_type == list_icons)
325 panel_fill_panel_icons (panel);
326 else
327 panel_fill_panel_list (panel);
329 gtk_signal_handler_block_by_data (GTK_OBJECT (panel->tree), panel);
331 if (vfs_current_is_local ()){
332 char buffer [MC_MAXPATHLEN];
334 get_current_wd (buffer, sizeof (buffer)-1);
335 gtk_dtree_select_dir (GTK_DTREE (panel->tree), buffer);
336 } else
337 gtk_dtree_select_dir (GTK_DTREE (panel->tree), panel->cwd);
339 gtk_signal_handler_unblock_by_data (GTK_OBJECT (panel->tree), panel);
342 static void
343 gmc_panel_set_size (int index, int boot)
345 Widget *w;
346 WPanel *p;
348 w = (Widget *) get_panel_widget (index);
349 p = (WPanel *) w;
350 w->cols = 40;
351 w->lines = 25;
352 set_panel_formats (p);
353 paint_panel (p);
355 if (!boot)
356 paint_frame (p);
358 x_fill_panel (p);
361 void
362 x_panel_set_size (int index)
364 printf ("WARNING: set size called\n");
365 gmc_panel_set_size (index, 1);
369 * Invoked when the f.mark field of a file item changes
371 void
372 x_panel_select_item (WPanel *panel, int index, int value)
374 int color;
376 color = file_compute_color (NORMAL, &panel->dir.list[index]);
377 panel_file_list_set_row_colors (CLIST_FROM_SW (panel->list), index, color);
380 void
381 x_select_item (WPanel *panel)
383 if (is_a_desktop_panel (panel))
384 return;
386 do_file_mark (panel, panel->selected, 1);
387 display_mini_info (panel);
389 if (panel->list_type == list_icons) {
390 GnomeIconList *list = ILIST_FROM_SW (panel->icons);
392 gnome_icon_list_select_icon (list, panel->selected);
393 if (GTK_WIDGET (list)->allocation.x != -1)
394 if (gnome_icon_list_icon_is_visible (list, panel->selected) != GTK_VISIBILITY_FULL)
395 gnome_icon_list_moveto (list, panel->selected, 0.5);
396 } else {
397 GtkCList *clist = CLIST_FROM_SW (panel->list);
399 gtk_clist_select_row (clist, panel->selected, 0);
401 /* Make it visible */
402 if (gtk_clist_row_is_visible (clist, panel->selected) != GTK_VISIBILITY_FULL)
403 gtk_clist_moveto (clist, panel->selected, 0, 0.5, 0.0);
407 void
408 x_unselect_item (WPanel *panel)
410 int selected = panel->selected;
412 if (panel->list_type == list_icons)
413 gnome_icon_list_unselect_all (ILIST_FROM_SW (panel->icons), NULL, NULL);
414 else
415 gtk_clist_unselect_all (CLIST_FROM_SW (panel->list));
417 panel->selected = selected;
420 void
421 x_filter_changed (WPanel *panel)
425 void
426 x_adjust_top_file (WPanel *panel)
428 /* gtk_clist_moveto (GTK_CLIST (panel->list), panel->top_file, 0, 0.0, 0.0); */
431 static void
432 panel_file_list_select_row (GtkWidget *file_list, gint row, gint column,
433 GdkEvent *event, gpointer data)
435 WPanel *panel;
437 panel = data;
439 panel->selected = row;
440 do_file_mark (panel, row, 1);
441 display_mini_info (panel);
442 execute_hooks (select_file_hook);
445 static void
446 panel_file_list_unselect_row (GtkWidget *widget, int row, int columns, GdkEvent *event, gpointer data)
448 WPanel *panel;
450 panel = data;
451 do_file_mark (panel, row, 0);
452 display_mini_info (panel);
454 if (panel->marked == 0)
455 panel->selected = 0;
458 static void
459 panel_file_list_resize_callback (GtkCList *clist, gint column, gint width, WPanel *panel)
461 int p;
463 p = column_width_pos[panel->list_type]; /* offset in column_width */
464 g_assert (p >= 0);
466 panel->column_width[p + column] = width;
468 /* make this default */
469 memcpy (default_column_width, panel->column_width, sizeof (default_column_width));
472 static void
473 panel_file_list_column_callback (GtkWidget *widget, int col, WPanel *panel)
475 format_e *format;
476 int i;
478 for (i = 0, format = panel->format; format; format = format->next){
479 if (!format->use_in_gui)
480 continue;
481 if (i == col){
482 sortfn *sorting_routine;
484 sorting_routine = get_sort_fn (format->id);
485 if (!sorting_routine)
486 return;
488 if (sorting_routine == panel->sort_type)
489 panel->reverse = !panel->reverse;
490 panel->sort_type = sorting_routine;
492 do_re_sort (panel);
493 return;
495 i++;
499 /* Convenience function to load a pixmap and mask from xpm data */
500 static void
501 create_pixmap (char **data, GdkPixmap **pixmap, GdkBitmap **mask)
503 GdkImlibImage *im;
505 im = gdk_imlib_create_image_from_xpm_data (data);
506 gdk_imlib_render (im, im->rgb_width, im->rgb_height);
507 *pixmap = gdk_imlib_copy_image (im);
508 *mask = gdk_imlib_copy_mask (im);
509 gdk_imlib_destroy_image (im);
512 static void
513 panel_create_pixmaps (void)
515 pixmaps_ready = TRUE;
517 create_pixmap (DIRECTORY_CLOSE_XPM, &icon_directory_pixmap, &icon_directory_mask);
518 create_pixmap (link_xpm, &icon_link_pixmap, &icon_link_mask);
519 create_pixmap (dev_xpm, &icon_dev_pixmap, &icon_dev_mask);
522 typedef gboolean (*desirable_fn)(WPanel *p, int x, int y);
523 typedef gboolean (*scroll_fn)(gpointer data);
525 /* Sets up a timer to scroll a panel component when something is being dragged
526 * over it. The `desirable' function should return whether it is desirable to
527 * scroll at the specified coordinates; and the `scroll' function should
528 * actually scroll the view.
530 static void
531 panel_setup_drag_scroll (WPanel *panel, int x, int y, desirable_fn desirable, scroll_fn scroll)
533 panel_cancel_drag_scroll (panel);
535 panel->drag_motion_x = x;
536 panel->drag_motion_y = y;
538 if ((* desirable) (panel, x, y))
539 panel->timer_id = gtk_timeout_add (SCROLL_TIMEOUT, scroll, panel);
542 static void
543 panel_file_list_configure (WPanel *panel, GtkWidget *sw, GtkWidget *file_list)
545 format_e *format = panel->format;
546 int i;
548 /* Set sorting callback */
549 gtk_signal_connect (GTK_OBJECT (file_list), "click_column",
550 GTK_SIGNAL_FUNC (panel_file_list_column_callback), panel);
552 /* Set column resize callback */
553 gtk_signal_connect (GTK_OBJECT (file_list), "resize_column",
554 GTK_SIGNAL_FUNC (panel_file_list_resize_callback), panel);
556 /* Avoid clist's broken focusing behavior */
557 GTK_WIDGET_UNSET_FLAGS (file_list, GTK_CAN_FOCUS);
559 /* Semi-sane selection mode */
560 gtk_clist_set_selection_mode (GTK_CLIST (file_list), GTK_SELECTION_EXTENDED);
562 for (i = 0, format = panel->format; format; format = format->next) {
563 GtkJustification just = GTK_JUSTIFY_LEFT;
565 if (!format->use_in_gui)
566 continue;
568 /* Set desired justification */
569 switch (HIDE_FIT (format->just_mode)) {
570 case J_LEFT:
571 just = GTK_JUSTIFY_LEFT;
572 break;
574 case J_RIGHT:
575 just = GTK_JUSTIFY_RIGHT;
576 break;
578 case J_CENTER:
579 just = GTK_JUSTIFY_CENTER;
580 break;
583 gtk_clist_set_column_justification (GTK_CLIST (file_list), i, just);
584 i++;
588 /* Creates an URI list to be transferred during a drop operation */
589 static char *
590 panel_build_selected_file_list (WPanel *panel, int *file_list_len)
592 if (panel->marked){
593 char *sep = "\r\n";
594 char *data, *copy;
595 int i, total_len;
596 int cwdlen = strlen (panel->cwd) + 1;
597 int filelen = strlen ("file:");
598 int seplen = strlen ("\r\n");
600 /* first pass, compute the length */
601 total_len = 0;
602 for (i = 0; i < panel->count; i++)
603 if (panel->dir.list [i].f.marked)
604 total_len += (filelen + cwdlen
605 + panel->dir.list [i].fnamelen
606 + seplen);
608 total_len++;
609 data = copy = g_malloc (total_len+1);
610 for (i = 0; i < panel->count; i++)
611 if (panel->dir.list [i].f.marked){
612 strcpy (copy, "file:");
613 strcpy (&copy [filelen], panel->cwd);
614 copy [filelen+cwdlen-1] = '/';
615 strcpy (&copy [filelen + cwdlen], panel->dir.list [i].fname);
616 strcpy (&copy [filelen + cwdlen + panel->dir.list [i].fnamelen], sep);
617 copy += filelen + cwdlen + panel->dir.list [i].fnamelen + seplen;
619 data [total_len] = 0;
620 *file_list_len = total_len;
621 return data;
622 } else {
623 char *fullname, *uri;
625 fullname = concat_dir_and_file (panel->cwd, panel->dir.list [panel->selected].fname);
627 uri = g_strconcat ("file:", fullname, NULL);
628 g_free (fullname);
630 *file_list_len = strlen (uri) + 1;
631 return uri;
636 * panel_drag_data_get:
638 * Invoked when a drag operation has been performed, this routine
639 * provides the data to be transfered
641 static void
642 panel_drag_data_get (GtkWidget *widget,
643 GdkDragContext *context,
644 GtkSelectionData *selection_data,
645 guint info,
646 guint32 time,
647 WPanel *panel)
649 int len;
650 char *data;
651 GList *files;
653 panel_cancel_drag_scroll (panel);
654 data = panel_build_selected_file_list (panel, &len);
656 switch (info){
657 case TARGET_URI_LIST:
658 case TARGET_TEXT_PLAIN:
659 gtk_selection_data_set (selection_data, selection_data->target, 8, data, len);
660 break;
662 case TARGET_URL:
663 files = gnome_uri_list_extract_uris (data);
664 if (files) {
665 gtk_selection_data_set (selection_data,
666 selection_data->target,
668 files->data,
669 strlen (files->data));
671 gnome_uri_list_free_strings (files);
672 break;
674 default:
675 g_assert_not_reached ();
678 g_free (data);
682 * panel_drag_data_delete:
684 * Invoked when the destination requests the information to be deleted
685 * possibly because the operation was MOVE.
687 static void
688 panel_drag_data_delete (GtkWidget *widget, GdkDragContext *context, WPanel *panel)
690 /* Things is: The File manager already handles file moving */
693 /* Performs a drop on a panel. If idx is -1, then drops on the panel's cwd
694 * itself.
696 static void
697 drop_on_panel (WPanel *panel, int idx, GdkDragContext *context, GtkSelectionData *selection_data)
699 char *file;
700 file_entry *fe;
701 int drop_on_dir;
702 int reload;
704 g_assert (panel != NULL);
705 g_assert (idx == -1 || idx < panel->count);
706 g_assert (context != NULL);
707 g_assert (selection_data != NULL);
709 drop_on_dir = FALSE;
711 if (idx == -1)
712 drop_on_dir = TRUE;
713 else {
714 fe = &panel->dir.list[idx];
715 file = g_concat_dir_and_file (panel->cwd, fe->fname);
717 if (!((S_ISDIR (fe->buf.st_mode) || fe->f.link_to_dir)
718 || gdnd_can_drop_on_file (file, fe))) {
719 g_free (file);
720 drop_on_dir = TRUE;
724 if (drop_on_dir) {
725 file = panel->cwd;
726 fe = file_entry_from_file (file);
727 if (!fe)
728 return; /* eeeek */
731 reload = gdnd_perform_drop (context, selection_data, file, fe);
733 if (file != panel->cwd)
734 g_free (file);
736 if (drop_on_dir)
737 file_entry_free (fe);
739 if (reload) {
740 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
741 repaint_screen ();
746 * panel_icon_list_drag_data_received:
748 * Invoked on the target side of a Drag and Drop operation when data has been
749 * dropped.
751 static void
752 panel_icon_list_drag_data_received (GtkWidget *widget,
753 GdkDragContext *context,
754 gint x,
755 gint y,
756 GtkSelectionData *selection_data,
757 guint info,
758 guint32 time,
759 WPanel *panel)
761 int idx;
763 idx = gnome_icon_list_get_icon_at (GNOME_ICON_LIST (widget), x, y);
764 drop_on_panel (panel, idx, context, selection_data);
768 * panel_clist_drag_data_received:
770 * Invoked on the target side of a Drag and Drop operation when data has been
771 * dropped.
773 static void
774 panel_clist_drag_data_received (GtkWidget *widget,
775 GdkDragContext *context,
776 gint x,
777 gint y,
778 GtkSelectionData *selection_data,
779 guint info,
780 guint32 time,
781 WPanel *panel)
783 GtkCList *clist;
784 int row;
786 clist = GTK_CLIST (widget);
788 /* Normalize the y coordinate to the clist_window */
790 y -= (GTK_CONTAINER (clist)->border_width
791 + clist->column_title_area.y
792 + clist->column_title_area.height);
794 if (gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &row, NULL) == 0)
795 row = -1;
797 drop_on_panel (panel, row, context, selection_data);
801 * panel_tree_drag_data_received:
803 * Invoked on the target side when a drop has been received in the Tree
805 static void
806 panel_tree_drag_data_received (GtkWidget *widget,
807 GdkDragContext *context,
808 gint x,
809 gint y,
810 GtkSelectionData *selection_data,
811 guint info,
812 guint32 time,
813 WPanel *panel)
815 GtkDTree *dtree = GTK_DTREE (widget);
816 GtkCTreeNode *node;
817 int row, col;
818 file_entry *fe;
819 char *path;
820 int reload;
822 if (!gtk_clist_get_selection_info (GTK_CLIST (dtree), x, y, &row, &col))
823 return;
825 node = gtk_ctree_node_nth (GTK_CTREE (dtree), row);
826 if (!node)
827 return;
829 path = gtk_dtree_get_row_path (dtree, node);
830 fe = file_entry_from_file (path);
831 if (!fe) {
832 g_free (path);
833 return; /* eeeek */
836 reload = gdnd_perform_drop (context, selection_data, path, fe);
838 file_entry_free (fe);
839 g_free (path);
841 if (reload) {
842 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
843 repaint_screen ();
847 static void
848 load_dnd_icons (void)
850 if (!drag_directory)
851 drag_directory = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_NOT, NULL);
853 if (!drag_directory_ok)
854 drag_directory_ok = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_NEW, NULL);
856 if (!drag_multiple)
857 drag_multiple = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_NOT, NULL);
859 if (!drag_multiple_ok)
860 drag_multiple_ok = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_MULTIPLE, NULL);
863 /* Convenience function to start a drag operation for the icon and file lists */
864 static void
865 start_drag (GtkWidget *widget, int button, GdkEvent *event)
867 GtkTargetList *list;
868 GdkDragContext *context;
870 list = gtk_target_list_new (drag_types, ELEMENTS (drag_types));
872 context = gtk_drag_begin (widget, list,
873 (GDK_ACTION_COPY | GDK_ACTION_MOVE
874 | GDK_ACTION_LINK | GDK_ACTION_ASK),
875 button, event);
876 gtk_drag_set_icon_default (context);
879 static int
880 panel_widget_motion (GtkWidget *widget, GdkEventMotion *event, WPanel *panel)
882 if (!panel->maybe_start_drag)
883 return FALSE;
885 if (!((panel->maybe_start_drag == 1 && (event->state & GDK_BUTTON1_MASK))
886 || (panel->maybe_start_drag == 2 && (event->state & GDK_BUTTON2_MASK))))
887 return FALSE;
889 /* This is the same threshold value that is used in gtkdnd.c */
891 if (MAX (abs (panel->click_x - event->x),
892 abs (panel->click_y - event->y)) <= 3)
893 return FALSE;
895 start_drag (widget, panel->maybe_start_drag, (GdkEvent *) event);
896 return FALSE;
900 * panel_drag_begin:
902 * Invoked when a drag is starting in the List view or the Icon view
904 static void
905 panel_drag_begin (GtkWidget *widget, GdkDragContext *context, WPanel *panel)
907 panel->dragging = 1;
911 * panel_drag_end:
913 * Invoked when a drag has finished in the List view or the Icon view
915 static void
916 panel_drag_end (GtkWidget *widget, GdkDragContext *context, WPanel *panel)
918 panel_cancel_drag_scroll (panel);
919 panel->dragging = 0;
923 * panel_clist_scrolling_is_desirable:
925 * If the cursor is in a position close to either edge (top or bottom)
926 * and there is possible to scroll the window, this routine returns
927 * true.
929 static gboolean
930 panel_clist_scrolling_is_desirable (WPanel *panel, int x, int y)
932 GtkAdjustment *va;
934 va = scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (panel->list));
936 if (y < 10) {
937 if (va->value > va->lower)
938 return TRUE;
939 } else {
940 if (y > (CLIST_FROM_SW (panel->list)->clist_window_height - 10)) {
941 if (va->value < va->upper - va->page_size)
942 return TRUE;
946 return FALSE;
951 * panel_clist_scroll:
953 * Timer callback invoked to scroll the clist window
955 static gboolean
956 panel_clist_scroll (gpointer data)
958 WPanel *panel = data;
959 GtkAdjustment *va;
960 double v;
962 va = scrolled_window_get_vadjustment (panel->list);
964 if (panel->drag_motion_y < 10) {
965 v = va->value - va->step_increment;
966 if (v < va->lower)
967 v = va->lower;
969 gtk_adjustment_set_value (va, v);
970 } else {
971 v = va->value + va->step_increment;
972 if (v > va->upper - va->page_size)
973 v = va->upper - va->page_size;
975 gtk_adjustment_set_value (va, v);
978 return TRUE;
981 /* Callback used for drag motion events over the clist. We set up
982 * auto-scrolling and validate the drop to present the user with the correct
983 * feedback.
985 static gboolean
986 panel_clist_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time,
987 gpointer data)
989 WPanel *panel;
990 GtkCList *clist;
991 GdkDragAction action;
992 GtkWidget *source_widget;
993 gint idx;
994 file_entry *fe;
995 char *full_name;
997 panel = data;
999 /* Normalize the y coordinate to the clist_window */
1001 clist = CLIST_FROM_SW (panel->list);
1002 y -= (GTK_CONTAINER (clist)->border_width
1003 + clist->column_title_area.y
1004 + clist->column_title_area.height);
1006 if (y < 0) {
1007 gdk_drag_status (context, 0, time);
1008 goto out;
1011 /* Set up auto-scrolling */
1013 panel_setup_drag_scroll (panel, x, y,
1014 panel_clist_scrolling_is_desirable,
1015 panel_clist_scroll);
1017 /* Validate the drop */
1019 gdnd_find_panel_by_drag_context (context, &source_widget);
1021 if (!gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &idx, NULL))
1022 fe = NULL;
1023 else
1024 fe = &panel->dir.list[idx];
1026 full_name = fe ? g_concat_dir_and_file (panel->cwd, fe->fname) : panel->cwd;
1028 action = gdnd_validate_action (context,
1029 FALSE,
1030 source_widget != NULL,
1031 source_widget == widget,
1032 full_name,
1034 fe ? fe->f.marked : FALSE);
1036 if (full_name != panel->cwd)
1037 g_free (full_name);
1039 gdk_drag_status (context, action, time);
1041 out:
1042 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "drag_motion");
1043 return TRUE;
1047 * panel_clist_drag_leave
1049 * Invoked when the dragged object has left our region
1051 static void
1052 panel_clist_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
1054 WPanel *panel;
1056 panel = data;
1057 panel_cancel_drag_scroll (panel);
1061 * panel_icon_list_scrolling_is_desirable:
1063 * If the cursor is in a position close to either edge (top or bottom)
1064 * and there is possible to scroll the window, this routine returns
1065 * true.
1067 static gboolean
1068 panel_icon_list_scrolling_is_desirable (WPanel *panel, int x, int y)
1070 GtkAdjustment *va;
1072 va = scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (panel->icons));
1074 if (y < 10) {
1075 if (va->value > va->lower)
1076 return TRUE;
1077 } else {
1078 if (y > (GTK_WIDGET (ILIST_FROM_SW (panel->icons))->allocation.height - 10)) {
1079 if (va->value < va->upper - va->page_size)
1080 return TRUE;
1084 return FALSE;
1089 * panel_icon_list_scroll:
1091 * Timer callback invoked to scroll the clist window
1093 static gboolean
1094 panel_icon_list_scroll (gpointer data)
1096 WPanel *panel = data;
1097 GtkAdjustment *va;
1098 double v;
1100 va = scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (panel->icons));
1102 if (panel->drag_motion_y < 10) {
1103 v = va->value - va->step_increment;
1104 if (v < va->lower)
1105 v = va->lower;
1107 gtk_adjustment_set_value (va, v);
1108 } else {
1109 v = va->value + va->step_increment;
1110 if (v > va->upper - va->page_size)
1111 v = va->upper - va->page_size;
1113 gtk_adjustment_set_value (va, v);
1116 return TRUE;
1119 /* Callback used for drag motion events in the icon list. We need to set up
1120 * auto-scrolling and validate the drop to present the user with the correct
1121 * feedback.
1123 static gboolean
1124 panel_icon_list_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time,
1125 gpointer data)
1127 WPanel *panel;
1128 GdkDragAction action;
1129 GtkWidget *source_widget;
1130 int idx;
1131 file_entry *fe;
1132 char *full_name;
1134 panel = data;
1136 /* Set up auto-scrolling */
1138 panel_setup_drag_scroll (panel, x, y,
1139 panel_icon_list_scrolling_is_desirable,
1140 panel_icon_list_scroll);
1142 /* Validate the drop */
1144 gdnd_find_panel_by_drag_context (context, &source_widget);
1146 idx = gnome_icon_list_get_icon_at (GNOME_ICON_LIST (widget), x, y);
1147 fe = (idx == -1) ? NULL : &panel->dir.list[idx];
1149 full_name = fe ? g_concat_dir_and_file (panel->cwd, fe->fname) : panel->cwd;
1151 action = gdnd_validate_action (context,
1152 FALSE,
1153 source_widget != NULL,
1154 source_widget == widget,
1155 full_name,
1157 fe ? fe->f.marked : FALSE);
1159 if (full_name != panel->cwd)
1160 g_free (full_name);
1162 gdk_drag_status (context, action, time);
1163 return TRUE;
1167 * panel_icon_list_drag_leave:
1169 * Invoked when the dragged object has left our region
1171 static void
1172 panel_icon_list_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
1174 WPanel *panel = data;
1176 panel_cancel_drag_scroll (panel);
1179 /* Handler for the row_popup_menu signal of the file list. */
1180 static void
1181 panel_file_list_row_popup_menu (GtkFList *flist, GdkEventButton *event, gpointer data)
1183 WPanel *panel;
1185 panel = data;
1186 gpopup_do_popup2 (event, panel, NULL);
1189 /* Handler for the empty_popup_menu signal of the file list. */
1190 static void
1191 panel_file_list_empty_popup_menu (GtkFList *flist, GdkEventButton *event, gpointer data)
1193 WPanel *panel;
1195 panel = data;
1196 file_list_popup (event, panel);
1199 /* Handler for the open_row signal of the file list */
1200 static void
1201 panel_file_list_open_row (GtkFList *flist, gpointer data)
1203 WPanel *panel;
1205 panel = data;
1206 do_enter (panel);
1209 /* Handler for the start_drag signal of the file list */
1210 static void
1211 panel_file_list_start_drag (GtkFList *flist, gint button, GdkEvent *event, gpointer data)
1213 start_drag (GTK_WIDGET (flist), button, event);
1217 * Create, setup the file listing display.
1219 static GtkWidget *
1220 panel_create_file_list (WPanel *panel)
1222 const int items = panel->format->items;
1223 format_e *format = panel->format;
1224 GtkWidget *file_list;
1225 GtkWidget *sw;
1226 gchar **titles;
1227 int i;
1229 titles = g_new (char *, items);
1230 for (i = 0; i < items; format = format->next)
1231 if (format->use_in_gui)
1232 titles [i++] = format->title;
1234 sw = gtk_scrolled_window_new (NULL, NULL);
1235 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1236 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1238 file_list = gtk_flist_new_with_titles (panel, items, titles);
1239 gtk_container_add (GTK_CONTAINER (sw), file_list);
1240 gtk_widget_show (file_list);
1242 panel_file_list_configure (panel, sw, file_list);
1243 g_free (titles);
1245 gtk_signal_connect (GTK_OBJECT (file_list), "select_row",
1246 GTK_SIGNAL_FUNC (panel_file_list_select_row),
1247 panel);
1248 gtk_signal_connect (GTK_OBJECT (file_list), "unselect_row",
1249 GTK_SIGNAL_FUNC (panel_file_list_unselect_row),
1250 panel);
1252 /* Connect to the flist signals */
1254 gtk_signal_connect (GTK_OBJECT (file_list), "row_popup_menu",
1255 GTK_SIGNAL_FUNC (panel_file_list_row_popup_menu),
1256 panel);
1257 gtk_signal_connect (GTK_OBJECT (file_list), "empty_popup_menu",
1258 GTK_SIGNAL_FUNC (panel_file_list_empty_popup_menu),
1259 panel);
1260 gtk_signal_connect (GTK_OBJECT (file_list), "open_row",
1261 GTK_SIGNAL_FUNC (panel_file_list_open_row),
1262 panel);
1263 gtk_signal_connect (GTK_OBJECT (file_list), "start_drag",
1264 GTK_SIGNAL_FUNC (panel_file_list_start_drag),
1265 panel);
1267 /* Set up drag and drop */
1269 load_dnd_icons ();
1271 gtk_drag_dest_set (GTK_WIDGET (file_list),
1272 GTK_DEST_DEFAULT_DROP,
1273 drop_types, ELEMENTS (drop_types),
1274 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
1276 gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_get",
1277 GTK_SIGNAL_FUNC (panel_drag_data_get), panel);
1278 gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_delete",
1279 GTK_SIGNAL_FUNC (panel_drag_data_delete), panel);
1280 gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_received",
1281 GTK_SIGNAL_FUNC (panel_clist_drag_data_received), panel);
1284 * This signal is provided for scrolling the main window
1285 * if data is being dragged
1287 gtk_signal_connect (GTK_OBJECT (file_list), "drag_motion",
1288 GTK_SIGNAL_FUNC (panel_clist_drag_motion), panel);
1289 gtk_signal_connect (GTK_OBJECT (file_list), "drag_leave",
1290 GTK_SIGNAL_FUNC (panel_clist_drag_leave), panel);
1291 gtk_signal_connect (GTK_OBJECT (file_list), "drag_begin",
1292 GTK_SIGNAL_FUNC (panel_drag_begin), panel);
1293 gtk_signal_connect (GTK_OBJECT (file_list), "drag_end",
1294 GTK_SIGNAL_FUNC (panel_drag_end), panel);
1296 return sw;
1300 * Callback: icon selected
1302 static void
1303 panel_icon_list_select_icon (GtkWidget *widget, int index, GdkEvent *event, WPanel *panel)
1305 panel->selected = index;
1306 do_file_mark (panel, index, 1);
1307 display_mini_info (panel);
1308 execute_hooks (select_file_hook);
1310 /* Do not let the user select .. */
1311 if (strcmp (panel->dir.list[index].fname, "..") == 0)
1312 gnome_icon_list_unselect_icon (GNOME_ICON_LIST (widget), index);
1314 if (!event)
1315 return;
1317 switch (event->type){
1318 case GDK_BUTTON_PRESS:
1319 if (event->button.button == 3)
1320 gpopup_do_popup2 ((GdkEventButton *) event, panel, NULL);
1322 break;
1324 case GDK_BUTTON_RELEASE:
1325 if (event->button.button == 2){
1326 char *fullname;
1328 if (S_ISDIR (panel->dir.list [index].buf.st_mode) ||
1329 panel->dir.list [index].f.link_to_dir){
1330 fullname = concat_dir_and_file (panel->cwd, panel->dir.list [index].fname);
1331 new_panel_at (fullname);
1332 g_free (fullname);
1335 break;
1337 case GDK_2BUTTON_PRESS:
1338 if (event->button.button == 1) {
1339 do_enter (panel);
1341 break;
1343 default:
1344 break;
1348 static void
1349 panel_icon_list_unselect_icon (GtkWidget *widget, int index, GdkEvent *event, WPanel *panel)
1351 do_file_mark (panel, index, 0);
1352 display_mini_info (panel);
1353 if (panel->marked == 0)
1354 panel->selected = 0;
1357 static int
1358 queue_reread_cmd (gpointer data)
1360 reread_cmd ();
1361 return FALSE;
1364 /* Renames a file using a file operation context. Returns FILE_CONT on success. */
1366 rename_file_with_context (char *source, char *dest)
1368 FileOpContext *ctx;
1369 struct stat s;
1370 long count;
1371 double bytes;
1372 int retval;
1374 if (mc_lstat (source, &s) != 0)
1375 return FILE_ABORT;
1377 ctx = file_op_context_new ();
1378 file_op_context_create_ui (ctx, OP_MOVE, FALSE);
1380 count = 1;
1381 bytes = s.st_size;
1383 retval = move_file_file (ctx, source, dest, &count, &bytes);
1384 file_op_context_destroy (ctx);
1386 return retval;
1389 static int
1390 panel_icon_renamed (GtkWidget *widget, int index, char *dest, WPanel *panel)
1392 char *source;
1393 char *fullname;
1394 int retval;
1396 if (strcmp (dest, panel->dir.list[index].fname) == 0)
1397 return TRUE; /* do nothing if the name did not change */
1399 source = g_concat_dir_and_file (cpanel->cwd, panel->dir.list[index].fname);
1400 fullname = g_concat_dir_and_file (cpanel->cwd, dest);
1402 if (rename_file_with_context (source, dest) == FILE_CONT) {
1403 g_free (panel->dir.list [index].fname);
1404 panel->dir.list [index].fname = g_strdup (dest);
1406 gtk_idle_add (queue_reread_cmd, NULL);
1407 retval = TRUE;
1408 } else
1409 retval = FALSE;
1411 g_free (source);
1412 g_free (fullname);
1414 return retval;
1417 /* Callback for rescanning the cwd */
1418 static void
1419 handle_rescan_directory (GtkWidget *widget, gpointer data)
1421 reread_cmd ();
1424 /* The popup menu for file panels */
1425 static GnomeUIInfo file_list_popup_items[] = {
1426 GNOMEUIINFO_ITEM_NONE (N_("_Rescan Directory"), N_("Reloads the current directory"),
1427 handle_rescan_directory),
1428 GNOMEUIINFO_ITEM_NONE (N_("New _Directory..."), N_("Creates a new directory here"),
1429 gnome_mkdir_cmd),
1430 GNOMEUIINFO_END
1433 /* The popup menu for file panels */
1434 static GnomeUIInfo trash_file_list_popup_items[] = {
1435 GNOMEUIINFO_ITEM_NONE (N_("Empty _Trash"), N_("Empties the Trash"),
1436 gnome_empty_trash),
1437 GNOMEUIINFO_ITEM_NONE (N_("_Rescan Directory"), N_("Reloads the current directory"),
1438 handle_rescan_directory),
1439 GNOMEUIINFO_ITEM_NONE (N_("New _Directory..."), N_("Creates a new directory here"),
1440 gnome_mkdir_cmd),
1441 GNOMEUIINFO_END
1444 /* Creates the popup menu when the user clicks button 3 on the blank area of the
1445 * file panels.
1447 static void
1448 file_list_popup (GdkEventButton *event, WPanel *panel)
1450 GtkWidget *popup;
1451 gchar *trash_dir;
1452 GnomeUIInfo *items = file_list_popup_items;
1454 trash_dir = g_strconcat (gnome_user_home_dir, "/",
1455 DESKTOP_DIR_NAME, "/",
1456 "Trash.gmc",
1457 NULL);
1459 if ((strncmp (panel->cwd, trash_dir, strlen (trash_dir)) == 0) && (panel->count != 1))
1460 items = trash_file_list_popup_items;
1461 g_free (trash_dir);
1463 popup = gnome_popup_menu_new (items);
1464 gnome_popup_menu_do_popup_modal (popup, NULL, NULL, event, panel);
1465 gtk_widget_destroy (popup);
1469 /* Returns whether an icon in the icon list is being edited. FIXME: This
1470 * function uses a fantastically ugly hack to figure this out. It would be
1471 * saner to have a function provided by the icon list widget to do it, but we
1472 * can't break forwards compatibility at this point. It would be even saner to
1473 * have a good DnD API for the icon list.
1475 static int
1476 editing_icon_list (GnomeIconList *gil)
1478 GnomeCanvasItem *item;
1480 item = GNOME_CANVAS (gil)->focused_item;
1481 return (item && GNOME_IS_ICON_TEXT_ITEM (item) && GNOME_ICON_TEXT_ITEM (item)->editing);
1485 * Strategy for activaing the drags from the icon-list:
1487 * The icon-list uses the button-press/motion-notify events for
1488 * the banding selection. We catch the events and only if the
1489 * click happens in an icon and the user moves the mouse enough (a
1490 * threshold to give it a better feel) activa the drag and drop.
1493 static int
1494 panel_icon_list_button_press (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
1496 GnomeIconList *gil = GNOME_ICON_LIST (widget);
1497 int icon;
1499 if (editing_icon_list (gil))
1500 return FALSE;
1502 if (event->type != GDK_BUTTON_PRESS)
1503 return FALSE;
1505 icon = gnome_icon_list_get_icon_at (gil, event->x, event->y);
1507 if (icon == -1) {
1508 if (event->button == 3) {
1509 file_list_popup (event, panel);
1510 return TRUE;
1512 } else if (event->button != 3)
1513 panel->maybe_start_drag = event->button;
1515 panel->click_x = event->x;
1516 panel->click_y = event->y;
1517 return FALSE;
1520 static int
1521 panel_icon_list_button_release (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
1523 panel->maybe_start_drag = 0;
1524 return FALSE;
1527 /* Create and setup the icon field display */
1528 static GtkWidget *
1529 panel_create_icon_display (WPanel *panel)
1531 GtkWidget *sw;
1532 GnomeIconList *ilist;
1534 sw = gtk_scrolled_window_new (NULL, NULL);
1535 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1536 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1538 ilist = GNOME_ICON_LIST (
1539 gnome_icon_list_new_flags (90, NULL,
1540 (GNOME_ICON_LIST_IS_EDITABLE
1541 | GNOME_ICON_LIST_STATIC_TEXT)));
1542 gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (ilist));
1543 gtk_widget_show (GTK_WIDGET (ilist));
1545 gnome_icon_list_set_separators (ilist, " /-_.");
1546 gnome_icon_list_set_row_spacing (ilist, 2);
1547 gnome_icon_list_set_col_spacing (ilist, 2);
1548 gnome_icon_list_set_icon_border (ilist, 2);
1549 gnome_icon_list_set_text_spacing (ilist, 2);
1551 gnome_icon_list_set_selection_mode (ilist, GTK_SELECTION_MULTIPLE);
1552 GTK_WIDGET_SET_FLAGS (ilist, GTK_CAN_FOCUS);
1554 gtk_signal_connect (GTK_OBJECT (ilist), "select_icon",
1555 GTK_SIGNAL_FUNC (panel_icon_list_select_icon),
1556 panel);
1557 gtk_signal_connect (GTK_OBJECT (ilist), "unselect_icon",
1558 GTK_SIGNAL_FUNC (panel_icon_list_unselect_icon),
1559 panel);
1560 gtk_signal_connect (GTK_OBJECT (ilist), "text_changed",
1561 GTK_SIGNAL_FUNC (panel_icon_renamed),
1562 panel);
1564 /* Setup the icons and DnD */
1566 load_dnd_icons ();
1568 gtk_drag_dest_set (GTK_WIDGET (ilist),
1569 GTK_DEST_DEFAULT_DROP,
1570 drop_types, ELEMENTS (drop_types),
1571 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
1573 gtk_signal_connect (GTK_OBJECT (ilist), "drag_data_get",
1574 GTK_SIGNAL_FUNC (panel_drag_data_get),
1575 panel);
1576 gtk_signal_connect (GTK_OBJECT (ilist), "drag_data_delete",
1577 GTK_SIGNAL_FUNC (panel_drag_data_delete),
1578 panel);
1579 gtk_signal_connect (GTK_OBJECT (ilist), "drag_data_received",
1580 GTK_SIGNAL_FUNC (panel_icon_list_drag_data_received),
1581 panel);
1582 gtk_signal_connect (GTK_OBJECT (ilist), "drag_begin",
1583 GTK_SIGNAL_FUNC (panel_drag_begin), panel);
1584 gtk_signal_connect (GTK_OBJECT (ilist), "drag_end",
1585 GTK_SIGNAL_FUNC (panel_drag_end), panel);
1587 /* These implement our drag-start activation code, as we have a pretty oveloaded widget */
1589 gtk_signal_connect (GTK_OBJECT (ilist), "button_press_event",
1590 GTK_SIGNAL_FUNC (panel_icon_list_button_press),
1591 panel);
1592 gtk_signal_connect (GTK_OBJECT (ilist), "button_release_event",
1593 GTK_SIGNAL_FUNC (panel_icon_list_button_release),
1594 panel);
1595 gtk_signal_connect (GTK_OBJECT (ilist), "motion_notify_event",
1596 GTK_SIGNAL_FUNC (panel_widget_motion),
1597 panel);
1599 /* This signal is provide for scrolling the main window if data is being
1600 * dragged.
1602 gtk_signal_connect (GTK_OBJECT (ilist), "drag_motion",
1603 GTK_SIGNAL_FUNC (panel_icon_list_drag_motion), panel);
1604 gtk_signal_connect (GTK_OBJECT (ilist), "drag_leave",
1605 GTK_SIGNAL_FUNC (panel_icon_list_drag_leave), panel);
1607 return sw;
1610 static void
1611 panel_switch_new_display_mode (WPanel *panel)
1613 GtkWidget *old_list = panel->list;
1615 if (!old_list)
1616 return;
1618 panel->list = panel_create_file_list (panel);
1619 gtk_widget_destroy (old_list);
1620 gtk_container_add (GTK_CONTAINER (panel->panel_listbox), panel->list);
1621 gtk_widget_show_all (panel->list);
1624 static GtkWidget *
1625 panel_create_cwd (Dlg_head *h, WPanel *panel, void **entry)
1627 WInput *in;
1629 in = input_new (0, 0, 0, 10, "", "cwd");
1630 add_widget (h, in);
1632 /* Force the creation of the gtk widget */
1633 send_message_to (h, (Widget *) in, WIDGET_INIT, 0);
1635 *entry = in;
1636 /* FIXME: for now, we set the usize. Ultimately, toolbar
1637 * will let you expand it, we hope.
1639 gtk_widget_set_usize (GTK_WIDGET (in->widget.wdata), 296, -1);
1640 return GTK_WIDGET (in->widget.wdata);
1643 void
1644 display_mini_info (WPanel *panel)
1646 GnomeAppBar *bar = GNOME_APPBAR (panel->ministatus);
1648 if (panel->searching) {
1649 char *buf;
1651 buf = g_strdup_printf (_("Search: %s"), panel->search_buffer);
1652 gnome_appbar_pop (bar);
1653 gnome_appbar_push (bar, buf);
1654 g_free (buf);
1655 return;
1658 if (panel->marked){
1659 char *buf;
1661 buf = g_strdup_printf ((panel->marked == 1) ? _("%s bytes in %d file") : _("%s bytes in %d files"),
1662 size_trunc_sep (panel->total),
1663 panel->marked);
1664 gnome_appbar_pop (bar);
1665 gnome_appbar_push (bar, buf);
1666 g_free (buf);
1667 return;
1670 if (S_ISLNK (panel->dir.list [panel->selected].buf.st_mode)){
1671 char *link, link_target [MC_MAXPATHLEN];
1672 int len;
1674 link = concat_dir_and_file (panel->cwd, panel->dir.list [panel->selected].fname);
1675 len = mc_readlink (link, link_target, MC_MAXPATHLEN);
1676 g_free (link);
1678 if (len > 0){
1679 link_target [len] = 0;
1680 /* FIXME: Links should be handled differently */
1681 /* str = g_strconcat ("-> ", link_target, NULL); */
1682 gnome_appbar_pop (bar);
1683 gnome_appbar_push (bar, " ");
1684 /* g_free (str); */
1685 } else {
1686 gnome_appbar_pop (bar);
1687 gnome_appbar_push (bar, _("<readlink failed>"));
1689 return;
1692 if (panel->estimated_total > 8){
1693 int len = panel->estimated_total;
1694 char *buffer;
1696 buffer = g_malloc (len + 2);
1697 format_file (buffer, panel, panel->selected, panel->estimated_total-2, 0, 1);
1698 buffer [len] = 0;
1699 gnome_appbar_pop (bar);
1700 gnome_appbar_push (bar, buffer);
1701 g_free (buffer);
1703 if (panel->list_type == list_icons){
1704 if (panel->marked == 0){
1705 gnome_appbar_pop (bar);
1706 gnome_appbar_push (bar, " ");
1711 /* Signal handler for DTree's "directory_changed" signal */
1712 static void
1713 panel_chdir (GtkDTree *dtree, char *path, WPanel *panel)
1715 if (panel->dragging)
1716 return;
1718 if (do_panel_cd (panel, path, cd_exact))
1719 return; /* success */
1721 if (panel->list_type == list_icons)
1722 gnome_icon_list_clear (ILIST_FROM_SW (panel->icons));
1723 else
1724 gtk_clist_clear (CLIST_FROM_SW (panel->list));
1726 strncpy (panel->cwd, path, sizeof (panel->cwd));
1727 show_dir (panel);
1730 static void
1731 panel_tree_scan_begin (GtkWidget *widget, gpointer data)
1733 WPanel *panel;
1735 panel = data;
1736 set_cursor_busy (panel);
1739 static void
1740 panel_tree_scan_end (GtkWidget *widget, gpointer data)
1742 WPanel *panel;
1744 panel = data;
1745 set_cursor_normal (panel);
1748 /* Handler for the possibly_ungrab signal of the dtree widget */
1749 static void
1750 panel_tree_possibly_ungrab (GtkWidget *widget, gpointer data)
1752 WPanel *panel;
1754 panel = data;
1756 /* The stupid clist button press handler grabs the mouse. We will get
1757 * called when the user presses the mouse on the tree, so we ungrab it.
1758 * Also, we have to make sure we don't knock away a DnD grab.
1760 if (!panel->drag_tree_dragging_over)
1761 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1764 /* Callback for the drag_begin signal of the tree */
1765 static void
1766 panel_tree_drag_begin (GtkWidget *widget, GdkDragContext *context, gpointer data)
1768 GtkDTree *dtree;
1769 WPanel *panel;
1771 dtree = GTK_DTREE (widget);
1772 panel = data;
1774 panel->dragging = TRUE;
1775 dtree->drag_dir = g_strdup (dtree->current_path);
1778 /* Callback for the drag_end signal of the tree */
1779 static void
1780 panel_tree_drag_end (GtkWidget *widget, GdkDragContext *context, gpointer data)
1782 GtkDTree *dtree;
1783 WPanel *panel;
1785 dtree = GTK_DTREE (widget);
1786 panel = data;
1788 panel->dragging = FALSE;
1789 g_free (dtree->drag_dir);
1790 dtree->drag_dir = NULL;
1793 /* Callback for the drag_data_get signal of the tree */
1794 static void
1795 panel_tree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
1796 GtkSelectionData *selection_data, guint info, guint time, gpointer data)
1798 GtkDTree *dtree;
1799 char *str;
1801 dtree = GTK_DTREE (widget);
1803 switch (info){
1804 case TARGET_URI_LIST:
1805 case TARGET_TEXT_PLAIN:
1806 str = g_strconcat ("file:", dtree->drag_dir, NULL);
1807 gtk_selection_data_set (selection_data, selection_data->target, 8,
1808 str, strlen (str) + 1);
1809 break;
1811 default:
1812 /* FIXME: handle TARGET_URL */
1813 break;
1818 * tree_drag_open_directory:
1820 * This routine is invoked in a delayed fashion if the user
1821 * keeps the drag cursor still over the widget.
1823 static gint
1824 tree_drag_open_directory (gpointer data)
1826 WPanel *panel;
1827 GtkDTree *dtree;
1828 GtkCTreeNode *node;
1830 panel = data;
1831 dtree = GTK_DTREE (panel->tree);
1833 node = gtk_ctree_node_nth (GTK_CTREE (panel->tree), panel->drag_tree_row);
1834 g_assert (node != NULL);
1836 if (!GTK_CTREE_ROW (node)->expanded) {
1837 #if 0
1838 /* FIXME: Disabled until fully debugged. Should also be configurable. */
1839 dtree->auto_expanded_nodes = g_list_append (dtree->auto_expanded_nodes, node);
1840 #endif
1841 gtk_ctree_expand (GTK_CTREE (panel->tree), node);
1844 return FALSE;
1847 /* Handles automatic collapsing of the tree nodes when doing drag and drop */
1848 static void
1849 panel_tree_check_auto_expand (WPanel *panel, GtkCTreeNode *current)
1851 GtkDTree *dtree;
1852 GtkCTree *ctree;
1853 GtkCList *clist;
1854 GList *free_list;
1855 GList *tmp_list;
1856 gint row, old_y, new_y;
1858 dtree = GTK_DTREE (panel->tree);
1859 ctree = GTK_CTREE (panel->tree);
1860 clist = GTK_CLIST (panel->tree);
1861 tmp_list = dtree->auto_expanded_nodes;
1863 if (current)
1864 for (; tmp_list; tmp_list = tmp_list->next)
1865 if (!gtk_dtree_is_ancestor (dtree, tmp_list->data, current))
1866 break;
1868 /* Collapse the rows as necessary. If possible, try to do so that the
1869 * "current" stays the same place on the screen.
1871 if (tmp_list) {
1872 if (current) {
1873 row = g_list_position (clist->row_list, (GList *) current);
1874 old_y = row * clist->row_height - clist->vadjustment->value;
1877 free_list = tmp_list;
1879 for (; tmp_list; tmp_list = tmp_list->next)
1880 gtk_ctree_collapse (GTK_CTREE (panel->tree), tmp_list->data);
1882 /* We have to calculate the row position again because rows may
1883 * have shifted during the collapse.
1885 if (current) {
1886 row = g_list_position (clist->row_list, (GList *) current);
1887 new_y = row * clist->row_height - clist->vadjustment->value;
1889 if (new_y != old_y)
1890 gtk_adjustment_set_value (clist->vadjustment,
1891 clist->vadjustment->value + new_y - old_y);
1894 if (free_list->prev)
1895 free_list->prev->next = NULL;
1896 else
1897 dtree->auto_expanded_nodes = NULL;
1899 free_list->prev = NULL;
1900 g_list_free (free_list);
1905 * panel_tree_scrolling_is_desirable:
1907 * If the cursor is in a position close to either edge (top or bottom)
1908 * and there is possible to scroll the window, this routine returns
1909 * true.
1911 static gboolean
1912 panel_tree_scrolling_is_desirable (WPanel *panel, int x, int y)
1914 GtkDTree *dtree = GTK_DTREE (panel->tree);
1915 GtkAdjustment *va;
1917 va = scrolled_window_get_vadjustment (panel->tree_scrolled_window);
1919 if (y < 10) {
1920 if (va->value > va->lower)
1921 return TRUE;
1922 } else {
1923 if (y > (GTK_WIDGET (dtree)->allocation.height - 10)){
1924 if (va->value < va->upper - va->page_size)
1925 return TRUE;
1929 return FALSE;
1932 /* Timer callback to scroll the tree */
1933 static gboolean
1934 panel_tree_scroll (gpointer data)
1936 WPanel *panel = data;
1937 GtkAdjustment *va;
1938 double v;
1940 va = scrolled_window_get_vadjustment (panel->tree_scrolled_window);
1942 if (panel->drag_motion_y < 10) {
1943 v = va->value - va->step_increment;
1944 if (v < va->lower)
1945 v = va->lower;
1947 gtk_adjustment_set_value (va, v);
1948 } else {
1949 v = va->value + va->step_increment;
1950 if (v > va->upper - va->page_size)
1951 v = va->upper - va->page_size;
1953 gtk_adjustment_set_value (va, v);
1956 return TRUE;
1959 /* Callback for the drag_motion signal of the tree. We set up a timer to
1960 * automatically open nodes if the user hovers over them.
1962 static gboolean
1963 panel_tree_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time,
1964 gpointer data)
1966 GtkDTree *dtree;
1967 WPanel *panel;
1968 int on_row, row, col;
1969 GtkCTreeNode *node;
1970 GdkDragAction action;
1971 GtkWidget *source_widget;
1972 char *row_path;
1973 int on_drag_row;
1975 dtree = GTK_DTREE (widget);
1976 panel = data;
1978 panel->drag_tree_dragging_over = TRUE;
1979 panel_setup_drag_scroll (panel, x, y,
1980 panel_tree_scrolling_is_desirable,
1981 panel_tree_scroll);
1983 on_row = gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &row, &col);
1985 /* Remove the auto-expansion timeout if we are on the blank area of the
1986 * tree or on a row different from the previous one.
1988 if ((!on_row || row != panel->drag_tree_row) && panel->drag_tree_timeout_id != 0) {
1989 gtk_timeout_remove (panel->drag_tree_timeout_id);
1990 panel->drag_tree_timeout_id = 0;
1992 if (panel->drag_tree_fe) {
1993 file_entry_free (panel->drag_tree_fe);
1994 panel->drag_tree_fe = NULL;
1998 action = 0;
2000 if (on_row) {
2001 node = gtk_ctree_node_nth (GTK_CTREE (dtree), row);
2002 row_path = gtk_dtree_get_row_path (dtree, node);
2004 if (row != panel->drag_tree_row) {
2005 /* Highlight the row by selecting it */
2007 panel_tree_check_auto_expand (panel, node);
2009 dtree->internal = TRUE;
2010 gtk_clist_select_row (GTK_CLIST (widget), row, 0);
2011 dtree->internal = FALSE;
2013 /* Create the file entry to validate drops */
2015 panel->drag_tree_fe = file_entry_from_file (row_path);
2017 /* Install the timeout handler for auto-expansion */
2019 panel->drag_tree_timeout_id = gtk_timeout_add (TREE_OPEN_TIMEOUT,
2020 tree_drag_open_directory,
2021 panel);
2022 panel->drag_tree_row = row;
2025 /* Validate the action */
2027 gdnd_find_panel_by_drag_context (context, &source_widget);
2029 /* If this tree is the drag source, see if the user is trying to
2030 * drop on the row being dragged. Otherwise, consider all rows
2031 * as not selected.
2033 if (source_widget == widget) {
2034 g_assert (dtree->drag_dir != NULL);
2035 on_drag_row = strcmp (row_path, dtree->drag_dir) == 0;
2036 } else
2037 on_drag_row = FALSE;
2039 action = gdnd_validate_action (context,
2040 FALSE,
2041 source_widget != NULL,
2042 source_widget == widget,
2043 row_path,
2044 panel->drag_tree_fe,
2045 on_drag_row);
2047 g_free (row_path);
2048 } else {
2049 panel->drag_tree_row = -1;
2050 panel_tree_check_auto_expand (panel, NULL);
2053 gdk_drag_status (context, action, time);
2054 return TRUE;
2057 /* Callback for the drag_leave signal of the tree. We deactivate the timeout for
2058 * automatic branch expansion.
2060 static void
2061 panel_tree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
2063 WPanel *panel;
2065 panel = data;
2067 panel->drag_tree_dragging_over = FALSE;
2068 panel_cancel_drag_scroll (panel);
2070 if (panel->drag_tree_timeout_id != 0) {
2071 gtk_timeout_remove (panel->drag_tree_timeout_id);
2072 panel->drag_tree_timeout_id = 0;
2075 if (panel->drag_tree_fe) {
2076 file_entry_free (panel->drag_tree_fe);
2077 panel->drag_tree_fe = NULL;
2080 if (panel->drag_tree_row != -1)
2081 panel_tree_check_auto_expand (panel, NULL);
2083 panel->drag_tree_row = -1;
2086 #if CONTEXT_MENU_ON_TREE
2087 static void
2088 tree_do_op (GtkWidget *tree, WPanel *panel, int operation)
2092 static void
2093 tree_copy_cmd (GtkWidget *tree, WPanel *panel)
2095 tree_do_op (tree, panel, OP_COPY);
2098 static void
2099 tree_del_cmd (GtkWidget *tree, WPanel *panel)
2101 tree_do_op (tree, panel, OP_DELETE);
2104 static void
2105 tree_ren_cmd (GtkWidget *tree, WPanel *panel)
2107 tree_do_op (tree, panel, OP_MOVE);
2110 static GnomeUIInfo tree_popup_items[] = {
2111 GNOMEUIINFO_ITEM_STOCK(N_("_Copy..."), N_("Copy directory"), tree_copy_cmd, GNOME_STOCK_PIXMAP_COPY),
2112 GNOMEUIINFO_ITEM_STOCK(N_("_Delete..."), N_("Delete directory"), tree_del_cmd, GNOME_STOCK_PIXMAP_TRASH),
2113 GNOMEUIINFO_ITEM_NONE(N_("_Move..."), N_("Rename or move directory"), tree_ren_cmd),
2115 GNOMEUIINFO_END
2118 static void
2119 panel_tree_button_press (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
2121 GtkWidget *popup;
2123 if (event->type != GDK_BUTTON_PRESS)
2124 return;
2126 if (event->button != 3)
2127 return;
2129 popup = gnome_popup_menu_new (tree_popup_items);
2130 gnome_popup_menu_do_popup_modal (popup, NULL, NULL, event, panel);
2131 gtk_widget_destroy (popup);
2133 #endif
2136 * panel_create_tree_view:
2138 * Create and initializes the GtkDTree widget for being used in the
2139 * Panel
2141 static GtkWidget *
2142 panel_create_tree_view (WPanel *panel)
2144 GtkWidget *tree;
2146 tree = gtk_dtree_new ();
2147 /*gtk_ctree_set_line_style (GTK_CTREE (tree), GTK_CTREE_LINES_DOTTED);*/
2148 /*gtk_ctree_set_expander_style (GTK_CTREE (tree), GTK_CTREE_EXPANDER_SQUARE);*/
2149 gtk_ctree_set_indent (GTK_CTREE (tree), 10);
2151 /* DTree signals */
2153 gtk_signal_connect (GTK_OBJECT (tree), "directory_changed",
2154 GTK_SIGNAL_FUNC (panel_chdir), panel);
2155 gtk_signal_connect (GTK_OBJECT (tree), "scan_begin",
2156 GTK_SIGNAL_FUNC (panel_tree_scan_begin), panel);
2157 gtk_signal_connect (GTK_OBJECT (tree), "scan_end",
2158 GTK_SIGNAL_FUNC (panel_tree_scan_end), panel);
2159 gtk_signal_connect (GTK_OBJECT (tree), "possibly_ungrab",
2160 GTK_SIGNAL_FUNC (panel_tree_possibly_ungrab), panel);
2162 /* Set up drag source */
2164 gtk_drag_source_set (GTK_WIDGET (tree), GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
2165 drag_types, ELEMENTS (drag_types),
2166 (GDK_ACTION_LINK | GDK_ACTION_MOVE | GDK_ACTION_COPY
2167 | GDK_ACTION_ASK | GDK_ACTION_DEFAULT));
2169 gtk_signal_connect (GTK_OBJECT (tree), "drag_begin",
2170 GTK_SIGNAL_FUNC (panel_tree_drag_begin), panel);
2171 gtk_signal_connect (GTK_OBJECT (tree), "drag_end",
2172 GTK_SIGNAL_FUNC (panel_tree_drag_end), panel);
2173 gtk_signal_connect (GTK_OBJECT (tree), "drag_data_get",
2174 GTK_SIGNAL_FUNC (panel_tree_drag_data_get), panel);
2176 /* Set up drag destination */
2178 gtk_drag_dest_set (GTK_WIDGET (tree),
2179 GTK_DEST_DEFAULT_DROP,
2180 drop_types, ELEMENTS (drop_types),
2181 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
2183 gtk_signal_connect (GTK_OBJECT (tree), "drag_motion",
2184 GTK_SIGNAL_FUNC (panel_tree_drag_motion), panel);
2185 gtk_signal_connect (GTK_OBJECT (tree), "drag_leave",
2186 GTK_SIGNAL_FUNC (panel_tree_drag_leave), panel);
2187 gtk_signal_connect (GTK_OBJECT (tree), "drag_data_received",
2188 GTK_SIGNAL_FUNC (panel_tree_drag_data_received), panel);
2190 #ifdef CONTEXT_MENU_ON_TREE
2191 /* Context sensitive menu */
2192 gtk_signal_connect_after (GTK_OBJECT (tree), "button_press_event",
2193 GTK_SIGNAL_FUNC (panel_tree_button_press), panel);
2194 gtk_clist_set_button_actions (GTK_CLIST (tree), 2, GTK_BUTTON_SELECTS);
2195 #endif
2196 return tree;
2200 * create_and_setup_pane:
2202 * Creates the horizontal GtkPaned widget that holds the tree
2203 * and the listing/iconing displays
2205 static GtkWidget *
2206 create_and_setup_pane (WPanel *panel)
2208 GtkWidget *pane;
2209 GtkWidget *tree = panel->tree;
2210 GdkFont *tree_font = tree->style->font;
2211 int size;
2213 pane = gtk_hpaned_new ();
2215 if (tree_panel_visible == -1)
2216 size = 20 * gdk_string_width (tree_font, "W");
2217 else {
2218 if (tree_panel_visible)
2219 size = tree_panel_visible;
2220 else
2221 size = 0;
2223 #if 0
2224 gtk_paned_set_position (GTK_PANED (pane), size);
2225 #else
2227 * Hack: set the default startup size for the pane without
2228 * using _set_usize which would set the minimal size
2230 GTK_PANED (pane)->child1_size = size;
2231 GTK_PANED (pane)->position_set = TRUE;
2232 #endif
2233 gtk_widget_show (pane);
2235 return pane;
2238 static void
2239 panel_back (GtkWidget *button, WPanel *panel)
2241 directory_history_prev (panel);
2244 static void
2245 panel_fwd (GtkWidget *button, WPanel *panel)
2247 directory_history_next (panel);
2250 static void
2251 panel_up (GtkWidget *button, WPanel *panel)
2253 do_panel_cd (panel, "..", cd_exact);
2256 static void
2257 rescan_panel (GtkWidget *widget, gpointer data)
2259 reread_cmd ();
2262 static void
2263 go_home (GtkWidget *widget, WPanel *panel)
2265 do_panel_cd (panel, "~", cd_exact);
2268 /* The toolbar */
2270 static GnomeUIInfo toolbar[] = {
2271 GNOMEUIINFO_ITEM_STOCK (N_("Back"), N_("Go to the previously visited directory"),
2272 panel_back, GNOME_STOCK_PIXMAP_BACK),
2273 GNOMEUIINFO_ITEM_STOCK (N_("Up"), N_("Go up a level in the directory hierarchy"),
2274 panel_up, GNOME_STOCK_PIXMAP_UP),
2275 GNOMEUIINFO_ITEM_STOCK (N_("Forward"), N_("Go to the next directory"),
2276 panel_fwd, GNOME_STOCK_PIXMAP_FORWARD),
2277 GNOMEUIINFO_SEPARATOR,
2278 GNOMEUIINFO_ITEM_STOCK (N_("Rescan"), N_("Rescan the current directory"),
2279 rescan_panel, GNOME_STOCK_PIXMAP_REFRESH),
2280 GNOMEUIINFO_SEPARATOR,
2281 GNOMEUIINFO_ITEM_STOCK (N_("Home"), N_("Go to your home directory"),
2282 go_home, GNOME_STOCK_PIXMAP_HOME),
2283 GNOMEUIINFO_SEPARATOR,
2284 GNOMEUIINFO_RADIOLIST(panel_view_toolbar_uiinfo),
2285 GNOMEUIINFO_END
2288 static void
2289 do_ui_signal_connect (GnomeUIInfo *uiinfo, gchar *signal_name,
2290 GnomeUIBuilderData *uibdata)
2292 if (uiinfo->moreinfo)
2293 gtk_signal_connect (GTK_OBJECT (uiinfo->widget),
2294 signal_name, uiinfo->moreinfo, uibdata->data ?
2295 uibdata->data : uiinfo->user_data);
2298 static void
2299 tree_size_allocate (GtkWidget *widget, GtkAllocation *allocation, WPanel *panel)
2301 if (allocation->width <= 0){
2302 tree_panel_visible = 0;
2303 } else {
2304 tree_panel_visible = allocation->width;
2307 if (auto_save_setup) {
2308 save_setup ();
2312 void
2313 x_create_panel (Dlg_head *h, widget_data parent, WPanel *panel)
2315 GtkWidget *status_line, *vbox, *ministatus_box;
2316 GtkWidget *cwd;
2317 GtkWidget *dock;
2318 GnomeUIBuilderData uibdata;
2320 panel->xwindow = gtk_widget_get_toplevel (GTK_WIDGET (panel->widget.wdata));
2322 panel->table = gtk_table_new (2, 1, 0);
2325 * Tree View
2327 panel->tree_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2328 gtk_scrolled_window_set_policy (
2329 GTK_SCROLLED_WINDOW (panel->tree_scrolled_window),
2330 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2331 panel->tree = panel_create_tree_view (panel);
2332 gtk_container_add (GTK_CONTAINER (panel->tree_scrolled_window), panel->tree);
2333 gtk_widget_show_all (panel->tree_scrolled_window);
2336 * Icon and Listing display
2338 panel->notebook = gtk_notebook_new ();
2339 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (panel->notebook), FALSE);
2340 gtk_widget_show (panel->notebook);
2342 panel->icons = panel_create_icon_display (panel);
2343 gtk_widget_show (panel->icons);
2345 memcpy (panel->column_width, default_column_width, sizeof (default_column_width));
2346 panel->list = panel_create_file_list (panel);
2347 gtk_widget_ref (panel->icons);
2348 gtk_widget_ref (panel->list);
2350 panel->panel_listbox = gtk_event_box_new ();
2351 gtk_widget_show (panel->panel_listbox);
2352 gtk_container_add (GTK_CONTAINER (panel->panel_listbox), panel->list);
2354 gtk_notebook_append_page (GTK_NOTEBOOK (panel->notebook), panel->icons, NULL);
2355 gtk_notebook_append_page (GTK_NOTEBOOK (panel->notebook), panel->panel_listbox, NULL);
2356 gtk_notebook_set_page (GTK_NOTEBOOK (panel->notebook),
2357 panel->list_type == list_icons ? 0 : 1);
2359 gtk_widget_show (panel->icons);
2360 gtk_widget_show (panel->list);
2361 gtk_widget_show (panel->notebook);
2364 * Pane
2366 panel->pane = create_and_setup_pane (panel);
2367 gtk_paned_add1 (GTK_PANED (panel->pane), panel->tree_scrolled_window);
2368 gtk_signal_connect (GTK_OBJECT (panel->tree_scrolled_window), "size_allocate",
2369 GTK_SIGNAL_FUNC (tree_size_allocate), panel);
2372 * Current Working directory
2375 cwd = panel_create_cwd (h, panel, &panel->current_dir);
2378 * We go through a lot of pain, wrestling with gnome_app* and gmc's @#$&*#$ internal structure and
2379 * make the #@$*&@#$ toolbars here...
2382 status_line = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
2383 uibdata.connect_func = do_ui_signal_connect;
2384 uibdata.data = panel;
2385 uibdata.is_interp = FALSE;
2386 uibdata.relay_func = NULL;
2387 uibdata.destroy_func = NULL;
2389 gnome_app_fill_toolbar_custom (GTK_TOOLBAR (status_line), toolbar, &uibdata, NULL);
2390 gnome_app_add_toolbar (GNOME_APP (panel->xwindow),
2391 GTK_TOOLBAR (status_line),
2392 "gmc-toolbar0",
2393 GNOME_DOCK_ITEM_BEH_EXCLUSIVE,
2394 GNOME_DOCK_TOP,
2395 2, 0, 0);
2396 panel->view_toolbar_items = copy_uiinfo_widgets (panel_view_toolbar_uiinfo);
2398 panel->back_b = toolbar[0].widget;
2399 panel->up_b = toolbar[1].widget;
2400 panel->fwd_b = toolbar[2].widget;
2401 panel_update_marks (panel);
2403 /* Set the list type by poking a toolbar item. Yes, this is hackish.
2404 * We fall back to icon view if a certain listing type is not supported.
2405 * Be sure to keep this in sync with the uiinfo arrays in glayout.c.
2408 if (panel->list_type == list_brief)
2409 gtk_toggle_button_set_active (
2410 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[1].widget), TRUE);
2411 else if (panel->list_type == list_full)
2412 gtk_toggle_button_set_active (
2413 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[2].widget), TRUE);
2414 else if (panel->list_type == list_user)
2415 gtk_toggle_button_set_active (
2416 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[3].widget), TRUE);
2417 else
2418 gtk_toggle_button_set_active (
2419 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[0].widget), TRUE);
2421 status_line = gtk_hbox_new (FALSE, 2);
2422 gtk_container_set_border_width (GTK_CONTAINER (status_line), 3);
2423 gtk_box_pack_start (GTK_BOX (status_line),
2424 gtk_label_new (_("Location:")), FALSE, FALSE, 0);
2425 gtk_box_pack_start (GTK_BOX (status_line), cwd, TRUE, TRUE, 0);
2427 dock = gnome_dock_item_new ("gmc-toolbar1",
2428 (GNOME_DOCK_ITEM_BEH_EXCLUSIVE
2429 | GNOME_DOCK_ITEM_BEH_NEVER_VERTICAL));
2430 gtk_container_add (GTK_CONTAINER (dock), status_line);
2431 gnome_dock_add_item (GNOME_DOCK (GNOME_APP (panel->xwindow)->dock),
2432 GNOME_DOCK_ITEM (dock), GNOME_DOCK_TOP, 1, 0, 0, FALSE);
2434 gtk_widget_show_all (dock);
2436 panel->view_table = gtk_table_new (1, 1, 0);
2437 gtk_widget_show (panel->view_table);
2440 * The status bar.
2442 ministatus_box = gtk_frame_new (NULL);
2443 gtk_frame_set_shadow_type (GTK_FRAME (ministatus_box), GTK_SHADOW_IN);
2445 panel->status = gtk_label_new (_("Show all files"));
2446 gtk_misc_set_alignment (GTK_MISC (panel->status), 0.0, 0.0);
2447 gtk_misc_set_padding (GTK_MISC (panel->status), 2, 0);
2449 gtk_box_pack_start (GTK_BOX (panel->ministatus), ministatus_box, FALSE, FALSE, 0);
2450 gtk_container_add (GTK_CONTAINER (ministatus_box), panel->status);
2452 gtk_widget_show (ministatus_box);
2453 gtk_widget_show (panel->status);
2456 * Put the icon list and the file listing in a nice frame
2459 /* Add both the icon view and the listing view */
2460 gtk_table_attach (GTK_TABLE (panel->view_table), panel->notebook, 0, 1, 0, 1,
2461 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2462 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2463 0, 0);
2465 gtk_table_attach (GTK_TABLE (panel->table), panel->pane, 0, 1, 1, 2,
2466 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2467 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2468 0, 0);
2470 gtk_paned_add2 (GTK_PANED (panel->pane), panel->view_table);
2473 * ministatus_box is a container created just to put the
2474 * panel->ministatus inside.
2476 * Then the resize mode for ministatus_box is changed to stop
2477 * any size-changed messages to be propagated above.
2479 * This is required as the panel->ministatus Label is changed
2480 * very often (to display status information). If this hack
2481 * is not made, then the resize is queued and the whole window
2482 * flickers each time this changes
2484 #if 0
2485 ministatus_box = gtk_hbox_new (FALSE, 0);
2486 gtk_container_add (GTK_CONTAINER (ministatus_box), panel->ministatus);
2487 gtk_widget_show (ministatus_box);
2488 gtk_container_set_resize_mode (GTK_CONTAINER (ministatus_box), GTK_RESIZE_QUEUE);
2489 gtk_table_attach (GTK_TABLE (panel->table), ministatus_box, 0, 1, 2, 3,
2490 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2491 0, 0, 0);
2493 gtk_table_attach (GTK_TABLE (panel->table), frame, 0, 1, 3, 4,
2494 GTK_EXPAND | GTK_FILL,
2495 0, 0, 0);
2497 #endif
2498 /* Ultra nasty hack: pull the vbox from wdata */
2499 vbox = GTK_WIDGET (panel->widget.wdata);
2501 panel->widget.wdata = (widget_data) panel->table;
2503 /* Now, insert our table in our parent */
2504 gtk_container_add (GTK_CONTAINER (vbox), panel->table);
2505 gtk_widget_show (vbox);
2506 gtk_widget_show (panel->table);
2508 if (!(panel->widget.options & W_PANEL_HIDDEN) && !is_trash_panel)
2509 gtk_widget_show (gtk_widget_get_toplevel (panel->table));
2511 if (!pixmaps_ready)
2512 panel_create_pixmaps ();
2514 /* In GNOME the panel wants to have the cursor, to avoid "auto" focusing the
2515 * filter input line
2517 panel->widget.options |= W_WANT_CURSOR;
2518 panel->estimated_total = 0;
2520 panel->timer_id = -1;
2522 /* re-set the user_format explicitly */
2523 if (default_user_format != NULL) {
2524 g_free (panel->user_format);
2525 panel->user_format = g_strdup (default_user_format);
2529 void
2530 panel_update_cols (Widget *panel, int frame_size)
2532 panel->cols = 60;
2533 panel->lines = 20;
2536 char *
2537 get_nth_panel_name (int num)
2539 static char buffer [BUF_TINY];
2541 if (!num)
2542 return "New Left Panel";
2543 else if (num == 1)
2544 return "New Right Panel";
2545 else {
2546 g_snprintf (buffer, sizeof (buffer), "%ith Panel", num);
2547 return buffer;
2551 void
2552 load_hint (void)
2554 char *hint;
2556 if ((hint = get_random_hint ())){
2557 if (*hint)
2558 set_hintbar (hint);
2559 g_free (hint);
2560 } else
2561 set_hintbar ("The GNOME File Manager " VERSION);
2565 void
2566 paint_frame (WPanel *panel)
2570 void
2571 x_reset_sort_labels (WPanel *panel)
2573 int page;
2575 if (!panel->notebook)
2576 return;
2578 if (panel->list_type == list_icons){
2579 page = 0;
2580 } else {
2581 page = 1;
2582 panel_switch_new_display_mode (panel);
2584 gtk_notebook_set_page (GTK_NOTEBOOK (panel->notebook), page);
2587 /* Releases all of the X resources allocated */
2588 void
2589 x_panel_destroy (WPanel *panel)
2591 gtk_widget_destroy (GTK_WIDGET (panel->xwindow));