Defuzzied one entry
[midnight-commander.git] / gnome / gscreen.c
blob0847386f8472d82480589377b71519f2d2111b4b
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 *cur_dir = g_get_current_dir ();
333 gtk_dtree_select_dir (GTK_DTREE (panel->tree), cur_dir);
334 g_free (cur_dir);
335 } else
336 gtk_dtree_select_dir (GTK_DTREE (panel->tree), panel->cwd);
338 gtk_signal_handler_unblock_by_data (GTK_OBJECT (panel->tree), panel);
341 static void
342 gmc_panel_set_size (int index, int boot)
344 Widget *w;
345 WPanel *p;
347 w = (Widget *) get_panel_widget (index);
348 p = (WPanel *) w;
349 w->cols = 40;
350 w->lines = 25;
351 set_panel_formats (p);
352 paint_panel (p);
354 if (!boot)
355 paint_frame (p);
357 x_fill_panel (p);
360 void
361 x_panel_set_size (int index)
363 printf ("WARNING: set size called\n");
364 gmc_panel_set_size (index, 1);
368 * Invoked when the f.mark field of a file item changes
370 void
371 x_panel_select_item (WPanel *panel, int index, int value)
373 int color;
375 color = file_compute_color (NORMAL, &panel->dir.list[index]);
376 panel_file_list_set_row_colors (CLIST_FROM_SW (panel->list), index, color);
379 void
380 x_select_item (WPanel *panel)
382 if (is_a_desktop_panel (panel))
383 return;
385 do_file_mark (panel, panel->selected, 1);
386 display_mini_info (panel);
388 if (panel->list_type == list_icons) {
389 GnomeIconList *list = ILIST_FROM_SW (panel->icons);
391 gnome_icon_list_select_icon (list, panel->selected);
392 if (GTK_WIDGET (list)->allocation.x != -1)
393 if (gnome_icon_list_icon_is_visible (list, panel->selected) != GTK_VISIBILITY_FULL)
394 gnome_icon_list_moveto (list, panel->selected, 0.5);
395 } else {
396 GtkCList *clist = CLIST_FROM_SW (panel->list);
398 gtk_clist_select_row (clist, panel->selected, 0);
400 /* Make it visible */
401 if (gtk_clist_row_is_visible (clist, panel->selected) != GTK_VISIBILITY_FULL)
402 gtk_clist_moveto (clist, panel->selected, 0, 0.5, 0.0);
406 void
407 x_unselect_item (WPanel *panel)
409 int selected = panel->selected;
411 if (panel->list_type == list_icons)
412 gnome_icon_list_unselect_all (ILIST_FROM_SW (panel->icons), NULL, NULL);
413 else
414 gtk_clist_unselect_all (CLIST_FROM_SW (panel->list));
416 panel->selected = selected;
419 void
420 x_filter_changed (WPanel *panel)
424 void
425 x_adjust_top_file (WPanel *panel)
427 /* gtk_clist_moveto (GTK_CLIST (panel->list), panel->top_file, 0, 0.0, 0.0); */
430 static void
431 panel_file_list_select_row (GtkWidget *file_list, gint row, gint column,
432 GdkEvent *event, gpointer data)
434 WPanel *panel;
436 panel = data;
438 panel->selected = row;
439 do_file_mark (panel, row, 1);
440 display_mini_info (panel);
441 execute_hooks (select_file_hook);
444 static void
445 panel_file_list_unselect_row (GtkWidget *widget, int row, int columns, GdkEvent *event, gpointer data)
447 WPanel *panel;
449 panel = data;
450 do_file_mark (panel, row, 0);
451 display_mini_info (panel);
453 if (panel->marked == 0)
454 panel->selected = 0;
457 static void
458 panel_file_list_resize_callback (GtkCList *clist, gint column, gint width, WPanel *panel)
460 int p;
462 p = column_width_pos[panel->list_type]; /* offset in column_width */
463 g_assert (p >= 0);
465 panel->column_width[p + column] = width;
467 /* make this default */
468 memcpy (default_column_width, panel->column_width, sizeof (default_column_width));
471 static void
472 panel_file_list_column_callback (GtkWidget *widget, int col, WPanel *panel)
474 format_e *format;
475 int i;
477 for (i = 0, format = panel->format; format; format = format->next){
478 if (!format->use_in_gui)
479 continue;
480 if (i == col){
481 sortfn *sorting_routine;
483 sorting_routine = get_sort_fn (format->id);
484 if (!sorting_routine)
485 return;
487 if (sorting_routine == panel->sort_type)
488 panel->reverse = !panel->reverse;
489 panel->sort_type = sorting_routine;
491 do_re_sort (panel);
492 return;
494 i++;
498 /* Convenience function to load a pixmap and mask from xpm data */
499 static void
500 create_pixmap (char **data, GdkPixmap **pixmap, GdkBitmap **mask)
502 GdkImlibImage *im;
504 im = gdk_imlib_create_image_from_xpm_data (data);
505 gdk_imlib_render (im, im->rgb_width, im->rgb_height);
506 *pixmap = gdk_imlib_copy_image (im);
507 *mask = gdk_imlib_copy_mask (im);
508 gdk_imlib_destroy_image (im);
511 static void
512 panel_create_pixmaps (void)
514 pixmaps_ready = TRUE;
516 create_pixmap (DIRECTORY_CLOSE_XPM, &icon_directory_pixmap, &icon_directory_mask);
517 create_pixmap (link_xpm, &icon_link_pixmap, &icon_link_mask);
518 create_pixmap (dev_xpm, &icon_dev_pixmap, &icon_dev_mask);
521 typedef gboolean (*desirable_fn)(WPanel *p, int x, int y);
522 typedef gboolean (*scroll_fn)(gpointer data);
524 /* Sets up a timer to scroll a panel component when something is being dragged
525 * over it. The `desirable' function should return whether it is desirable to
526 * scroll at the specified coordinates; and the `scroll' function should
527 * actually scroll the view.
529 static void
530 panel_setup_drag_scroll (WPanel *panel, int x, int y, desirable_fn desirable, scroll_fn scroll)
532 panel_cancel_drag_scroll (panel);
534 panel->drag_motion_x = x;
535 panel->drag_motion_y = y;
537 if ((* desirable) (panel, x, y))
538 panel->timer_id = gtk_timeout_add (SCROLL_TIMEOUT, scroll, panel);
541 static void
542 panel_file_list_configure (WPanel *panel, GtkWidget *sw, GtkWidget *file_list)
544 format_e *format = panel->format;
545 int i;
547 /* Set sorting callback */
548 gtk_signal_connect (GTK_OBJECT (file_list), "click_column",
549 GTK_SIGNAL_FUNC (panel_file_list_column_callback), panel);
551 /* Set column resize callback */
552 gtk_signal_connect (GTK_OBJECT (file_list), "resize_column",
553 GTK_SIGNAL_FUNC (panel_file_list_resize_callback), panel);
555 /* Avoid clist's broken focusing behavior */
556 GTK_WIDGET_UNSET_FLAGS (file_list, GTK_CAN_FOCUS);
558 /* Semi-sane selection mode */
559 gtk_clist_set_selection_mode (GTK_CLIST (file_list), GTK_SELECTION_EXTENDED);
561 for (i = 0, format = panel->format; format; format = format->next) {
562 GtkJustification just = GTK_JUSTIFY_LEFT;
564 if (!format->use_in_gui)
565 continue;
567 /* Set desired justification */
568 switch (HIDE_FIT (format->just_mode)) {
569 case J_LEFT:
570 just = GTK_JUSTIFY_LEFT;
571 break;
573 case J_RIGHT:
574 just = GTK_JUSTIFY_RIGHT;
575 break;
577 case J_CENTER:
578 just = GTK_JUSTIFY_CENTER;
579 break;
582 gtk_clist_set_column_justification (GTK_CLIST (file_list), i, just);
583 i++;
587 /* Creates an URI list to be transferred during a drop operation */
588 static char *
589 panel_build_selected_file_list (WPanel *panel, int *file_list_len)
591 if (panel->marked){
592 char *sep = "\r\n";
593 char *data, *copy;
594 int i, total_len;
595 int cwdlen = strlen (panel->cwd) + 1;
596 int filelen = strlen ("file:");
597 int seplen = strlen ("\r\n");
599 /* first pass, compute the length */
600 total_len = 0;
601 for (i = 0; i < panel->count; i++)
602 if (panel->dir.list [i].f.marked)
603 total_len += (filelen + cwdlen
604 + panel->dir.list [i].fnamelen
605 + seplen);
607 total_len++;
608 data = copy = g_malloc (total_len+1);
609 for (i = 0; i < panel->count; i++)
610 if (panel->dir.list [i].f.marked){
611 strcpy (copy, "file:");
612 strcpy (&copy [filelen], panel->cwd);
613 copy [filelen+cwdlen-1] = '/';
614 strcpy (&copy [filelen + cwdlen], panel->dir.list [i].fname);
615 strcpy (&copy [filelen + cwdlen + panel->dir.list [i].fnamelen], sep);
616 copy += filelen + cwdlen + panel->dir.list [i].fnamelen + seplen;
618 data [total_len] = 0;
619 *file_list_len = total_len;
620 return data;
621 } else {
622 char *fullname, *uri;
624 fullname = concat_dir_and_file (panel->cwd, panel->dir.list [panel->selected].fname);
626 uri = g_strconcat ("file:", fullname, NULL);
627 g_free (fullname);
629 *file_list_len = strlen (uri) + 1;
630 return uri;
635 * panel_drag_data_get:
637 * Invoked when a drag operation has been performed, this routine
638 * provides the data to be transfered
640 static void
641 panel_drag_data_get (GtkWidget *widget,
642 GdkDragContext *context,
643 GtkSelectionData *selection_data,
644 guint info,
645 guint32 time,
646 WPanel *panel)
648 int len;
649 char *data;
650 GList *files;
652 panel_cancel_drag_scroll (panel);
653 data = panel_build_selected_file_list (panel, &len);
655 switch (info){
656 case TARGET_URI_LIST:
657 case TARGET_TEXT_PLAIN:
658 gtk_selection_data_set (selection_data, selection_data->target, 8, data, len);
659 break;
661 case TARGET_URL:
662 files = gnome_uri_list_extract_uris (data);
663 if (files) {
664 gtk_selection_data_set (selection_data,
665 selection_data->target,
667 files->data,
668 strlen (files->data));
670 gnome_uri_list_free_strings (files);
671 break;
673 default:
674 g_assert_not_reached ();
677 g_free (data);
681 * panel_drag_data_delete:
683 * Invoked when the destination requests the information to be deleted
684 * possibly because the operation was MOVE.
686 static void
687 panel_drag_data_delete (GtkWidget *widget, GdkDragContext *context, WPanel *panel)
689 /* Things is: The File manager already handles file moving */
692 /* Performs a drop on a panel. If idx is -1, then drops on the panel's cwd
693 * itself.
695 static void
696 drop_on_panel (WPanel *panel, int idx, GdkDragContext *context, GtkSelectionData *selection_data)
698 int drop_on_dir;
699 int reload;
700 char *file = NULL;
701 file_entry *fe = NULL;
703 g_assert (panel != NULL);
704 g_assert (idx == -1 || idx < panel->count);
705 g_assert (context != NULL);
706 g_assert (selection_data != NULL);
708 drop_on_dir = FALSE;
710 if (idx == -1)
711 drop_on_dir = TRUE;
712 else {
713 fe = &panel->dir.list[idx];
714 file = g_concat_dir_and_file (panel->cwd, fe->fname);
716 if (!((S_ISDIR (fe->buf.st_mode) || fe->f.link_to_dir)
717 || gdnd_can_drop_on_file (file, fe))) {
718 g_free (file);
719 drop_on_dir = TRUE;
723 if (drop_on_dir) {
724 file = panel->cwd;
725 fe = file_entry_from_file (file);
726 if (!fe)
727 return; /* eeeek */
730 reload = gdnd_perform_drop (context, selection_data, file, fe);
732 if (file != panel->cwd)
733 g_free (file);
735 if (drop_on_dir)
736 file_entry_free (fe);
738 if (reload) {
739 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
740 repaint_screen ();
745 * panel_icon_list_drag_data_received:
747 * Invoked on the target side of a Drag and Drop operation when data has been
748 * dropped.
750 static void
751 panel_icon_list_drag_data_received (GtkWidget *widget,
752 GdkDragContext *context,
753 gint x,
754 gint y,
755 GtkSelectionData *selection_data,
756 guint info,
757 guint32 time,
758 WPanel *panel)
760 int idx;
762 idx = gnome_icon_list_get_icon_at (GNOME_ICON_LIST (widget), x, y);
763 drop_on_panel (panel, idx, context, selection_data);
767 * panel_clist_drag_data_received:
769 * Invoked on the target side of a Drag and Drop operation when data has been
770 * dropped.
772 static void
773 panel_clist_drag_data_received (GtkWidget *widget,
774 GdkDragContext *context,
775 gint x,
776 gint y,
777 GtkSelectionData *selection_data,
778 guint info,
779 guint32 time,
780 WPanel *panel)
782 GtkCList *clist;
783 int row;
785 clist = GTK_CLIST (widget);
787 /* Normalize the y coordinate to the clist_window */
789 y -= (GTK_CONTAINER (clist)->border_width
790 + clist->column_title_area.y
791 + clist->column_title_area.height);
793 if (gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &row, NULL) == 0)
794 row = -1;
796 drop_on_panel (panel, row, context, selection_data);
800 * panel_tree_drag_data_received:
802 * Invoked on the target side when a drop has been received in the Tree
804 static void
805 panel_tree_drag_data_received (GtkWidget *widget,
806 GdkDragContext *context,
807 gint x,
808 gint y,
809 GtkSelectionData *selection_data,
810 guint info,
811 guint32 time,
812 WPanel *panel)
814 GtkDTree *dtree = GTK_DTREE (widget);
815 GtkCTreeNode *node;
816 int row, col;
817 file_entry *fe;
818 char *path;
819 int reload;
821 if (!gtk_clist_get_selection_info (GTK_CLIST (dtree), x, y, &row, &col))
822 return;
824 node = gtk_ctree_node_nth (GTK_CTREE (dtree), row);
825 if (!node)
826 return;
828 path = gtk_dtree_get_row_path (dtree, node);
829 fe = file_entry_from_file (path);
830 if (!fe) {
831 g_free (path);
832 return; /* eeeek */
835 reload = gdnd_perform_drop (context, selection_data, path, fe);
837 file_entry_free (fe);
838 g_free (path);
840 if (reload) {
841 update_panels (UP_OPTIMIZE, UP_KEEPSEL);
842 repaint_screen ();
846 static void
847 load_dnd_icons (void)
849 if (!drag_directory)
850 drag_directory = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_NOT, NULL);
852 if (!drag_directory_ok)
853 drag_directory_ok = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_NEW, NULL);
855 if (!drag_multiple)
856 drag_multiple = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_NOT, NULL);
858 if (!drag_multiple_ok)
859 drag_multiple_ok = gnome_stock_transparent_window (GNOME_STOCK_PIXMAP_MULTIPLE, NULL);
862 /* Convenience function to start a drag operation for the icon and file lists */
863 static void
864 start_drag (GtkWidget *widget, int button, GdkEvent *event)
866 GtkTargetList *list;
867 GdkDragContext *context;
869 list = gtk_target_list_new (drag_types, ELEMENTS (drag_types));
871 context = gtk_drag_begin (widget, list,
872 (GDK_ACTION_COPY | GDK_ACTION_MOVE
873 | GDK_ACTION_LINK | GDK_ACTION_ASK),
874 button, event);
875 gtk_drag_set_icon_default (context);
878 static int
879 panel_widget_motion (GtkWidget *widget, GdkEventMotion *event, WPanel *panel)
881 if (!panel->maybe_start_drag)
882 return FALSE;
884 if (!((panel->maybe_start_drag == 1 && (event->state & GDK_BUTTON1_MASK))
885 || (panel->maybe_start_drag == 2 && (event->state & GDK_BUTTON2_MASK))))
886 return FALSE;
888 /* This is the same threshold value that is used in gtkdnd.c */
890 if (MAX (abs (panel->click_x - event->x),
891 abs (panel->click_y - event->y)) <= 3)
892 return FALSE;
894 start_drag (widget, panel->maybe_start_drag, (GdkEvent *) event);
895 return FALSE;
899 * panel_drag_begin:
901 * Invoked when a drag is starting in the List view or the Icon view
903 static void
904 panel_drag_begin (GtkWidget *widget, GdkDragContext *context, WPanel *panel)
906 panel->dragging = 1;
910 * panel_drag_end:
912 * Invoked when a drag has finished in the List view or the Icon view
914 static void
915 panel_drag_end (GtkWidget *widget, GdkDragContext *context, WPanel *panel)
917 panel_cancel_drag_scroll (panel);
918 panel->dragging = 0;
922 * panel_clist_scrolling_is_desirable:
924 * If the cursor is in a position close to either edge (top or bottom)
925 * and there is possible to scroll the window, this routine returns
926 * true.
928 static gboolean
929 panel_clist_scrolling_is_desirable (WPanel *panel, int x, int y)
931 GtkAdjustment *va;
933 va = scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (panel->list));
935 if (y < 10) {
936 if (va->value > va->lower)
937 return TRUE;
938 } else {
939 if (y > (CLIST_FROM_SW (panel->list)->clist_window_height - 10)) {
940 if (va->value < va->upper - va->page_size)
941 return TRUE;
945 return FALSE;
950 * panel_clist_scroll:
952 * Timer callback invoked to scroll the clist window
954 static gboolean
955 panel_clist_scroll (gpointer data)
957 WPanel *panel = data;
958 GtkAdjustment *va;
959 double v;
961 va = scrolled_window_get_vadjustment (panel->list);
963 if (panel->drag_motion_y < 10) {
964 v = va->value - va->step_increment;
965 if (v < va->lower)
966 v = va->lower;
968 gtk_adjustment_set_value (va, v);
969 } else {
970 v = va->value + va->step_increment;
971 if (v > va->upper - va->page_size)
972 v = va->upper - va->page_size;
974 gtk_adjustment_set_value (va, v);
977 return TRUE;
980 /* Callback used for drag motion events over the clist. We set up
981 * auto-scrolling and validate the drop to present the user with the correct
982 * feedback.
984 static gboolean
985 panel_clist_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time,
986 gpointer data)
988 WPanel *panel;
989 GtkCList *clist;
990 GdkDragAction action;
991 GtkWidget *source_widget;
992 gint idx;
993 file_entry *fe;
994 char *full_name;
996 panel = data;
998 /* Normalize the y coordinate to the clist_window */
1000 clist = CLIST_FROM_SW (panel->list);
1001 y -= (GTK_CONTAINER (clist)->border_width
1002 + clist->column_title_area.y
1003 + clist->column_title_area.height);
1005 if (y < 0) {
1006 gdk_drag_status (context, 0, time);
1007 goto out;
1010 /* Set up auto-scrolling */
1012 panel_setup_drag_scroll (panel, x, y,
1013 panel_clist_scrolling_is_desirable,
1014 panel_clist_scroll);
1016 /* Validate the drop */
1018 gdnd_find_panel_by_drag_context (context, &source_widget);
1020 if (!gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &idx, NULL))
1021 fe = NULL;
1022 else
1023 fe = &panel->dir.list[idx];
1025 full_name = fe ? g_concat_dir_and_file (panel->cwd, fe->fname) : panel->cwd;
1027 action = gdnd_validate_action (context,
1028 FALSE,
1029 source_widget != NULL,
1030 source_widget == widget,
1031 full_name,
1033 fe ? fe->f.marked : FALSE);
1035 if (full_name != panel->cwd)
1036 g_free (full_name);
1038 gdk_drag_status (context, action, time);
1040 out:
1041 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "drag_motion");
1042 return TRUE;
1046 * panel_clist_drag_leave
1048 * Invoked when the dragged object has left our region
1050 static void
1051 panel_clist_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
1053 WPanel *panel;
1055 panel = data;
1056 panel_cancel_drag_scroll (panel);
1060 * panel_icon_list_scrolling_is_desirable:
1062 * If the cursor is in a position close to either edge (top or bottom)
1063 * and there is possible to scroll the window, this routine returns
1064 * true.
1066 static gboolean
1067 panel_icon_list_scrolling_is_desirable (WPanel *panel, int x, int y)
1069 GtkAdjustment *va;
1071 va = scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (panel->icons));
1073 if (y < 10) {
1074 if (va->value > va->lower)
1075 return TRUE;
1076 } else {
1077 if (y > (GTK_WIDGET (ILIST_FROM_SW (panel->icons))->allocation.height - 10)) {
1078 if (va->value < va->upper - va->page_size)
1079 return TRUE;
1083 return FALSE;
1088 * panel_icon_list_scroll:
1090 * Timer callback invoked to scroll the clist window
1092 static gboolean
1093 panel_icon_list_scroll (gpointer data)
1095 WPanel *panel = data;
1096 GtkAdjustment *va;
1097 double v;
1099 va = scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (panel->icons));
1101 if (panel->drag_motion_y < 10) {
1102 v = va->value - va->step_increment;
1103 if (v < va->lower)
1104 v = va->lower;
1106 gtk_adjustment_set_value (va, v);
1107 } else {
1108 v = va->value + va->step_increment;
1109 if (v > va->upper - va->page_size)
1110 v = va->upper - va->page_size;
1112 gtk_adjustment_set_value (va, v);
1115 return TRUE;
1118 /* Callback used for drag motion events in the icon list. We need to set up
1119 * auto-scrolling and validate the drop to present the user with the correct
1120 * feedback.
1122 static gboolean
1123 panel_icon_list_drag_motion (GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time,
1124 gpointer data)
1126 WPanel *panel;
1127 GdkDragAction action;
1128 GtkWidget *source_widget;
1129 int idx;
1130 file_entry *fe;
1131 char *full_name;
1133 panel = data;
1135 /* Set up auto-scrolling */
1137 panel_setup_drag_scroll (panel, x, y,
1138 panel_icon_list_scrolling_is_desirable,
1139 panel_icon_list_scroll);
1141 /* Validate the drop */
1143 gdnd_find_panel_by_drag_context (context, &source_widget);
1145 idx = gnome_icon_list_get_icon_at (GNOME_ICON_LIST (widget), x, y);
1146 fe = (idx == -1) ? NULL : &panel->dir.list[idx];
1148 full_name = fe ? g_concat_dir_and_file (panel->cwd, fe->fname) : panel->cwd;
1150 action = gdnd_validate_action (context,
1151 FALSE,
1152 source_widget != NULL,
1153 source_widget == widget,
1154 full_name,
1156 fe ? fe->f.marked : FALSE);
1158 if (full_name != panel->cwd)
1159 g_free (full_name);
1161 gdk_drag_status (context, action, time);
1162 return TRUE;
1166 * panel_icon_list_drag_leave:
1168 * Invoked when the dragged object has left our region
1170 static void
1171 panel_icon_list_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
1173 WPanel *panel = data;
1175 panel_cancel_drag_scroll (panel);
1178 /* Handler for the row_popup_menu signal of the file list. */
1179 static void
1180 panel_file_list_row_popup_menu (GtkFList *flist, GdkEventButton *event, gpointer data)
1182 WPanel *panel;
1184 panel = data;
1185 gpopup_do_popup2 (event, panel, NULL);
1188 /* Handler for the empty_popup_menu signal of the file list. */
1189 static void
1190 panel_file_list_empty_popup_menu (GtkFList *flist, GdkEventButton *event, gpointer data)
1192 WPanel *panel;
1194 panel = data;
1195 file_list_popup (event, panel);
1198 /* Handler for the open_row signal of the file list */
1199 static void
1200 panel_file_list_open_row (GtkFList *flist, gpointer data)
1202 WPanel *panel;
1204 panel = data;
1205 do_enter (panel);
1208 /* Handler for the start_drag signal of the file list */
1209 static void
1210 panel_file_list_start_drag (GtkFList *flist, gint button, GdkEvent *event, gpointer data)
1212 start_drag (GTK_WIDGET (flist), button, event);
1216 * Create, setup the file listing display.
1218 static GtkWidget *
1219 panel_create_file_list (WPanel *panel)
1221 const int items = panel->format->items;
1222 format_e *format = panel->format;
1223 GtkWidget *file_list;
1224 GtkWidget *sw;
1225 gchar **titles;
1226 int i;
1228 titles = g_new (char *, items);
1229 for (i = 0; i < items; format = format->next)
1230 if (format->use_in_gui)
1231 titles [i++] = format->title;
1233 sw = gtk_scrolled_window_new (NULL, NULL);
1234 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1235 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1237 file_list = gtk_flist_new_with_titles (panel, items, titles);
1238 gtk_container_add (GTK_CONTAINER (sw), file_list);
1239 gtk_widget_show (file_list);
1241 panel_file_list_configure (panel, sw, file_list);
1242 g_free (titles);
1244 gtk_signal_connect (GTK_OBJECT (file_list), "select_row",
1245 GTK_SIGNAL_FUNC (panel_file_list_select_row),
1246 panel);
1247 gtk_signal_connect (GTK_OBJECT (file_list), "unselect_row",
1248 GTK_SIGNAL_FUNC (panel_file_list_unselect_row),
1249 panel);
1251 /* Connect to the flist signals */
1253 gtk_signal_connect (GTK_OBJECT (file_list), "row_popup_menu",
1254 GTK_SIGNAL_FUNC (panel_file_list_row_popup_menu),
1255 panel);
1256 gtk_signal_connect (GTK_OBJECT (file_list), "empty_popup_menu",
1257 GTK_SIGNAL_FUNC (panel_file_list_empty_popup_menu),
1258 panel);
1259 gtk_signal_connect (GTK_OBJECT (file_list), "open_row",
1260 GTK_SIGNAL_FUNC (panel_file_list_open_row),
1261 panel);
1262 gtk_signal_connect (GTK_OBJECT (file_list), "start_drag",
1263 GTK_SIGNAL_FUNC (panel_file_list_start_drag),
1264 panel);
1266 /* Set up drag and drop */
1268 load_dnd_icons ();
1270 gtk_drag_dest_set (GTK_WIDGET (file_list),
1271 GTK_DEST_DEFAULT_DROP,
1272 drop_types, ELEMENTS (drop_types),
1273 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
1275 gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_get",
1276 GTK_SIGNAL_FUNC (panel_drag_data_get), panel);
1277 gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_delete",
1278 GTK_SIGNAL_FUNC (panel_drag_data_delete), panel);
1279 gtk_signal_connect (GTK_OBJECT (file_list), "drag_data_received",
1280 GTK_SIGNAL_FUNC (panel_clist_drag_data_received), panel);
1283 * This signal is provided for scrolling the main window
1284 * if data is being dragged
1286 gtk_signal_connect (GTK_OBJECT (file_list), "drag_motion",
1287 GTK_SIGNAL_FUNC (panel_clist_drag_motion), panel);
1288 gtk_signal_connect (GTK_OBJECT (file_list), "drag_leave",
1289 GTK_SIGNAL_FUNC (panel_clist_drag_leave), panel);
1290 gtk_signal_connect (GTK_OBJECT (file_list), "drag_begin",
1291 GTK_SIGNAL_FUNC (panel_drag_begin), panel);
1292 gtk_signal_connect (GTK_OBJECT (file_list), "drag_end",
1293 GTK_SIGNAL_FUNC (panel_drag_end), panel);
1295 return sw;
1299 * Callback: icon selected
1301 static void
1302 panel_icon_list_select_icon (GtkWidget *widget, int index, GdkEvent *event, WPanel *panel)
1304 panel->selected = index;
1305 do_file_mark (panel, index, 1);
1306 display_mini_info (panel);
1307 execute_hooks (select_file_hook);
1309 /* Do not let the user select .. */
1310 if (strcmp (panel->dir.list[index].fname, "..") == 0)
1311 gnome_icon_list_unselect_icon (GNOME_ICON_LIST (widget), index);
1313 if (!event)
1314 return;
1316 switch (event->type){
1317 case GDK_BUTTON_PRESS:
1318 if (event->button.button == 3)
1319 gpopup_do_popup2 ((GdkEventButton *) event, panel, NULL);
1321 break;
1323 case GDK_2BUTTON_PRESS:
1324 if (event->button.button == 1) {
1325 do_enter (panel);
1327 break;
1329 default:
1330 break;
1334 static void
1335 panel_icon_list_unselect_icon (GtkWidget *widget, int index, GdkEvent *event, WPanel *panel)
1337 do_file_mark (panel, index, 0);
1338 display_mini_info (panel);
1339 if (panel->marked == 0)
1340 panel->selected = 0;
1343 static int
1344 queue_reread_cmd (gpointer data)
1346 reread_cmd ();
1347 return FALSE;
1350 /* Renames a file using a file operation context. Returns FILE_CONT on success. */
1352 rename_file_with_context (char *source, char *dest)
1354 FileOpContext *ctx;
1355 struct stat s;
1356 long count;
1357 double bytes;
1358 int retval;
1360 if (mc_lstat (source, &s) != 0)
1361 return FILE_ABORT;
1363 ctx = file_op_context_new ();
1364 file_op_context_create_ui (ctx, OP_MOVE, FALSE);
1366 count = 1;
1367 bytes = s.st_size;
1369 retval = move_file_file (ctx, source, dest, &count, &bytes);
1370 file_op_context_destroy (ctx);
1372 return retval;
1375 static int
1376 panel_icon_renamed (GtkWidget *widget, int index, char *dest, WPanel *panel)
1378 char *source;
1379 char *fullname;
1380 int retval;
1382 if (strcmp (dest, panel->dir.list[index].fname) == 0)
1383 return TRUE; /* do nothing if the name did not change */
1385 source = g_concat_dir_and_file (cpanel->cwd, panel->dir.list[index].fname);
1386 fullname = g_concat_dir_and_file (cpanel->cwd, dest);
1388 if (rename_file_with_context (source, dest) == FILE_CONT) {
1389 g_free (panel->dir.list [index].fname);
1390 panel->dir.list [index].fname = g_strdup (dest);
1392 gtk_idle_add (queue_reread_cmd, NULL);
1393 retval = TRUE;
1394 } else
1395 retval = FALSE;
1397 g_free (source);
1398 g_free (fullname);
1400 return retval;
1403 /* Callback for rescanning the cwd */
1404 static void
1405 handle_rescan_directory (GtkWidget *widget, gpointer data)
1407 reread_cmd ();
1410 /* The popup menu for file panels */
1411 static GnomeUIInfo file_list_popup_items[] = {
1412 GNOMEUIINFO_ITEM_NONE (N_("_Rescan Directory"), N_("Reloads the current directory"),
1413 handle_rescan_directory),
1414 GNOMEUIINFO_ITEM_NONE (N_("New _Directory..."), N_("Creates a new directory here"),
1415 gnome_mkdir_cmd),
1416 GNOMEUIINFO_END
1419 /* The popup menu for file panels */
1420 static GnomeUIInfo trash_file_list_popup_items[] = {
1421 GNOMEUIINFO_ITEM_NONE (N_("Empty _Trash"), N_("Empties the Trash"),
1422 gnome_empty_trash),
1423 GNOMEUIINFO_ITEM_NONE (N_("_Rescan Directory"), N_("Reloads the current directory"),
1424 handle_rescan_directory),
1425 GNOMEUIINFO_ITEM_NONE (N_("New _Directory..."), N_("Creates a new directory here"),
1426 gnome_mkdir_cmd),
1427 GNOMEUIINFO_END
1430 /* Creates the popup menu when the user clicks button 3 on the blank area of the
1431 * file panels.
1433 static void
1434 file_list_popup (GdkEventButton *event, WPanel *panel)
1436 GtkWidget *popup;
1437 gchar *trash_dir;
1438 GnomeUIInfo *items = file_list_popup_items;
1440 trash_dir = g_strconcat (gnome_user_home_dir, "/",
1441 DESKTOP_DIR_NAME, "/",
1442 "Trash.gmc",
1443 NULL);
1445 if ((strncmp (panel->cwd, trash_dir, strlen (trash_dir)) == 0) && (panel->count != 1))
1446 items = trash_file_list_popup_items;
1447 g_free (trash_dir);
1449 popup = gnome_popup_menu_new (items);
1450 gnome_popup_menu_do_popup_modal (popup, NULL, NULL, event, panel);
1451 gtk_widget_destroy (popup);
1455 /* Returns whether an icon in the icon list is being edited. FIXME: This
1456 * function uses a fantastically ugly hack to figure this out. It would be
1457 * saner to have a function provided by the icon list widget to do it, but we
1458 * can't break forwards compatibility at this point. It would be even saner to
1459 * have a good DnD API for the icon list.
1461 static int
1462 editing_icon_list (GnomeIconList *gil)
1464 GnomeCanvasItem *item;
1466 item = GNOME_CANVAS (gil)->focused_item;
1467 return (item && GNOME_IS_ICON_TEXT_ITEM (item) && GNOME_ICON_TEXT_ITEM (item)->editing);
1471 * Strategy for activaing the drags from the icon-list:
1473 * The icon-list uses the button-press/motion-notify events for
1474 * the banding selection. We catch the events and only if the
1475 * click happens in an icon and the user moves the mouse enough (a
1476 * threshold to give it a better feel) activa the drag and drop.
1479 static int
1480 panel_icon_list_button_press (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
1482 GnomeIconList *gil = GNOME_ICON_LIST (widget);
1483 int icon;
1485 if (editing_icon_list (gil))
1486 return FALSE;
1488 if (event->type != GDK_BUTTON_PRESS)
1489 return FALSE;
1491 icon = gnome_icon_list_get_icon_at (gil, event->x, event->y);
1493 if (icon == -1) {
1494 if (event->button == 3) {
1495 file_list_popup (event, panel);
1496 return TRUE;
1498 } else if (event->button != 3)
1499 panel->maybe_start_drag = event->button;
1501 panel->click_x = event->x;
1502 panel->click_y = event->y;
1503 return FALSE;
1506 static int
1507 panel_icon_list_button_release (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
1509 panel->maybe_start_drag = 0;
1510 if (event->button == 2){
1511 char *fullname;
1512 int icon;
1513 file_entry *fe;
1515 GnomeIconList *gil = GNOME_ICON_LIST (widget);
1516 icon = gnome_icon_list_get_icon_at (gil, event->x, event->y);
1517 fe = &panel->dir.list [icon];
1519 if (S_ISDIR (fe->buf.st_mode) || fe->f.link_to_dir){
1520 fullname = concat_dir_and_file (panel->cwd, fe->fname);
1521 new_panel_at (fullname);
1522 g_free (fullname);
1524 return TRUE;
1527 return FALSE;
1530 /* Create and setup the icon field display */
1531 static GtkWidget *
1532 panel_create_icon_display (WPanel *panel)
1534 GtkWidget *sw;
1535 GnomeIconList *ilist;
1537 sw = gtk_scrolled_window_new (NULL, NULL);
1538 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1539 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1541 ilist = GNOME_ICON_LIST (
1542 gnome_icon_list_new_flags (90, NULL,
1543 (GNOME_ICON_LIST_IS_EDITABLE
1544 | GNOME_ICON_LIST_STATIC_TEXT)));
1545 gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (ilist));
1546 gtk_widget_show (GTK_WIDGET (ilist));
1548 gnome_icon_list_set_separators (ilist, " /-_.");
1549 gnome_icon_list_set_row_spacing (ilist, 2);
1550 gnome_icon_list_set_col_spacing (ilist, 2);
1551 gnome_icon_list_set_icon_border (ilist, 2);
1552 gnome_icon_list_set_text_spacing (ilist, 2);
1554 gnome_icon_list_set_selection_mode (ilist, GTK_SELECTION_MULTIPLE);
1555 GTK_WIDGET_SET_FLAGS (ilist, GTK_CAN_FOCUS);
1557 gtk_signal_connect (GTK_OBJECT (ilist), "select_icon",
1558 GTK_SIGNAL_FUNC (panel_icon_list_select_icon),
1559 panel);
1560 gtk_signal_connect (GTK_OBJECT (ilist), "unselect_icon",
1561 GTK_SIGNAL_FUNC (panel_icon_list_unselect_icon),
1562 panel);
1563 gtk_signal_connect (GTK_OBJECT (ilist), "text_changed",
1564 GTK_SIGNAL_FUNC (panel_icon_renamed),
1565 panel);
1567 /* Setup the icons and DnD */
1569 load_dnd_icons ();
1571 gtk_drag_dest_set (GTK_WIDGET (ilist),
1572 GTK_DEST_DEFAULT_DROP,
1573 drop_types, ELEMENTS (drop_types),
1574 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
1576 gtk_signal_connect (GTK_OBJECT (ilist), "drag_data_get",
1577 GTK_SIGNAL_FUNC (panel_drag_data_get),
1578 panel);
1579 gtk_signal_connect (GTK_OBJECT (ilist), "drag_data_delete",
1580 GTK_SIGNAL_FUNC (panel_drag_data_delete),
1581 panel);
1582 gtk_signal_connect (GTK_OBJECT (ilist), "drag_data_received",
1583 GTK_SIGNAL_FUNC (panel_icon_list_drag_data_received),
1584 panel);
1585 gtk_signal_connect (GTK_OBJECT (ilist), "drag_begin",
1586 GTK_SIGNAL_FUNC (panel_drag_begin), panel);
1587 gtk_signal_connect (GTK_OBJECT (ilist), "drag_end",
1588 GTK_SIGNAL_FUNC (panel_drag_end), panel);
1590 /* These implement our drag-start activation code, as we have a pretty oveloaded widget */
1592 gtk_signal_connect (GTK_OBJECT (ilist), "button_press_event",
1593 GTK_SIGNAL_FUNC (panel_icon_list_button_press),
1594 panel);
1595 gtk_signal_connect (GTK_OBJECT (ilist), "button_release_event",
1596 GTK_SIGNAL_FUNC (panel_icon_list_button_release),
1597 panel);
1598 gtk_signal_connect (GTK_OBJECT (ilist), "motion_notify_event",
1599 GTK_SIGNAL_FUNC (panel_widget_motion),
1600 panel);
1602 /* This signal is provide for scrolling the main window if data is being
1603 * dragged.
1605 gtk_signal_connect (GTK_OBJECT (ilist), "drag_motion",
1606 GTK_SIGNAL_FUNC (panel_icon_list_drag_motion), panel);
1607 gtk_signal_connect (GTK_OBJECT (ilist), "drag_leave",
1608 GTK_SIGNAL_FUNC (panel_icon_list_drag_leave), panel);
1610 return sw;
1613 static void
1614 panel_switch_new_display_mode (WPanel *panel)
1616 GtkWidget *old_list = panel->list;
1618 if (!old_list)
1619 return;
1621 panel->list = panel_create_file_list (panel);
1622 gtk_widget_destroy (old_list);
1623 gtk_container_add (GTK_CONTAINER (panel->panel_listbox), panel->list);
1624 gtk_widget_show_all (panel->list);
1627 static GtkWidget *
1628 panel_create_cwd (Dlg_head *h, WPanel *panel, void **entry)
1630 WInput *in;
1632 in = input_new (0, 0, 0, 10, "", "cwd");
1633 add_widget (h, in);
1635 /* Force the creation of the gtk widget */
1636 send_message_to (h, (Widget *) in, WIDGET_INIT, 0);
1638 *entry = in;
1639 /* FIXME: for now, we set the usize. Ultimately, toolbar
1640 * will let you expand it, we hope.
1642 gtk_widget_set_usize (GTK_WIDGET (in->widget.wdata), 296, -1);
1643 return GTK_WIDGET (in->widget.wdata);
1646 void
1647 display_mini_info (WPanel *panel)
1649 GnomeAppBar *bar = GNOME_APPBAR (panel->ministatus);
1651 if (panel->searching) {
1652 char *buf;
1654 buf = g_strdup_printf (_("Search: %s"), panel->search_buffer);
1655 gnome_appbar_pop (bar);
1656 gnome_appbar_push (bar, buf);
1657 g_free (buf);
1658 return;
1661 if (panel->marked){
1662 char *buf;
1664 buf = g_strdup_printf ((panel->marked == 1) ? _("%s bytes in %d file") : _("%s bytes in %d files"),
1665 size_trunc_sep (panel->total),
1666 panel->marked);
1667 gnome_appbar_pop (bar);
1668 gnome_appbar_push (bar, buf);
1669 g_free (buf);
1670 return;
1673 if (S_ISLNK (panel->dir.list [panel->selected].buf.st_mode)){
1674 char *link, link_target [MC_MAXPATHLEN];
1675 int len;
1677 link = concat_dir_and_file (panel->cwd, panel->dir.list [panel->selected].fname);
1678 len = mc_readlink (link, link_target, MC_MAXPATHLEN);
1679 g_free (link);
1681 if (len > 0){
1682 link_target [len] = 0;
1683 /* FIXME: Links should be handled differently */
1684 /* str = g_strconcat ("-> ", link_target, NULL); */
1685 gnome_appbar_pop (bar);
1686 gnome_appbar_push (bar, " ");
1687 /* g_free (str); */
1688 } else {
1689 gnome_appbar_pop (bar);
1690 gnome_appbar_push (bar, _("<readlink failed>"));
1692 return;
1695 if (panel->estimated_total > 8){
1696 int len = panel->estimated_total;
1697 char *buffer;
1699 buffer = g_malloc (len + 2);
1700 format_file (buffer, panel, panel->selected, panel->estimated_total-2, 0, 1);
1701 buffer [len] = 0;
1702 gnome_appbar_pop (bar);
1703 gnome_appbar_push (bar, buffer);
1704 g_free (buffer);
1706 if (panel->list_type == list_icons){
1707 if (panel->marked == 0){
1708 gnome_appbar_pop (bar);
1709 gnome_appbar_push (bar, " ");
1714 /* Signal handler for DTree's "directory_changed" signal */
1715 static void
1716 panel_chdir (GtkDTree *dtree, char *path, WPanel *panel)
1718 if (panel->dragging)
1719 return;
1721 if (do_panel_cd (panel, path, cd_exact))
1722 return; /* success */
1724 if (panel->list_type == list_icons)
1725 gnome_icon_list_clear (ILIST_FROM_SW (panel->icons));
1726 else
1727 gtk_clist_clear (CLIST_FROM_SW (panel->list));
1729 strncpy (panel->cwd, path, sizeof (panel->cwd));
1730 show_dir (panel);
1733 static void
1734 panel_tree_scan_begin (GtkWidget *widget, gpointer data)
1736 WPanel *panel;
1738 panel = data;
1739 set_cursor_busy (panel);
1742 static void
1743 panel_tree_scan_end (GtkWidget *widget, gpointer data)
1745 WPanel *panel;
1747 panel = data;
1748 set_cursor_normal (panel);
1751 /* Handler for the possibly_ungrab signal of the dtree widget */
1752 static void
1753 panel_tree_possibly_ungrab (GtkWidget *widget, gpointer data)
1755 WPanel *panel;
1757 panel = data;
1759 /* The stupid clist button press handler grabs the mouse. We will get
1760 * called when the user presses the mouse on the tree, so we ungrab it.
1761 * Also, we have to make sure we don't knock away a DnD grab.
1763 if (!panel->drag_tree_dragging_over)
1764 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1767 /* Callback for the drag_begin signal of the tree */
1768 static void
1769 panel_tree_drag_begin (GtkWidget *widget, GdkDragContext *context, gpointer data)
1771 GtkDTree *dtree;
1772 WPanel *panel;
1774 dtree = GTK_DTREE (widget);
1775 panel = data;
1777 panel->dragging = TRUE;
1778 dtree->drag_dir = g_strdup (dtree->current_path);
1781 /* Callback for the drag_end signal of the tree */
1782 static void
1783 panel_tree_drag_end (GtkWidget *widget, GdkDragContext *context, gpointer data)
1785 GtkDTree *dtree;
1786 WPanel *panel;
1788 dtree = GTK_DTREE (widget);
1789 panel = data;
1791 panel->dragging = FALSE;
1792 g_free (dtree->drag_dir);
1793 dtree->drag_dir = NULL;
1796 /* Callback for the drag_data_get signal of the tree */
1797 static void
1798 panel_tree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
1799 GtkSelectionData *selection_data, guint info, guint time, gpointer data)
1801 GtkDTree *dtree;
1802 char *str;
1804 dtree = GTK_DTREE (widget);
1806 switch (info){
1807 case TARGET_URI_LIST:
1808 case TARGET_TEXT_PLAIN:
1809 str = g_strconcat ("file:", dtree->drag_dir, NULL);
1810 gtk_selection_data_set (selection_data, selection_data->target, 8,
1811 str, strlen (str) + 1);
1812 break;
1814 default:
1815 /* FIXME: handle TARGET_URL */
1816 break;
1821 * tree_drag_open_directory:
1823 * This routine is invoked in a delayed fashion if the user
1824 * keeps the drag cursor still over the widget.
1826 static gint
1827 tree_drag_open_directory (gpointer data)
1829 WPanel *panel;
1830 GtkDTree *dtree;
1831 GtkCTreeNode *node;
1833 panel = data;
1834 dtree = GTK_DTREE (panel->tree);
1836 node = gtk_ctree_node_nth (GTK_CTREE (panel->tree), panel->drag_tree_row);
1837 g_assert (node != NULL);
1839 if (!GTK_CTREE_ROW (node)->expanded) {
1840 #if 0
1841 /* FIXME: Disabled until fully debugged. Should also be configurable. */
1842 dtree->auto_expanded_nodes = g_list_append (dtree->auto_expanded_nodes, node);
1843 #endif
1844 gtk_ctree_expand (GTK_CTREE (panel->tree), node);
1847 return FALSE;
1850 /* Handles automatic collapsing of the tree nodes when doing drag and drop */
1851 static void
1852 panel_tree_check_auto_expand (WPanel *panel, GtkCTreeNode *current)
1854 GtkDTree *dtree;
1855 GtkCTree *ctree;
1856 GtkCList *clist;
1857 GList *free_list;
1858 GList *tmp_list;
1860 dtree = GTK_DTREE (panel->tree);
1861 ctree = GTK_CTREE (panel->tree);
1862 clist = GTK_CLIST (panel->tree);
1863 tmp_list = dtree->auto_expanded_nodes;
1865 if (current)
1866 for (; tmp_list; tmp_list = tmp_list->next)
1867 if (!gtk_dtree_is_ancestor (dtree, tmp_list->data, current))
1868 break;
1870 /* Collapse the rows as necessary. If possible, try to do so that the
1871 * "current" stays the same place on the screen.
1873 if (tmp_list) {
1874 gint row, new_y;
1875 gint old_y = 0;
1876 if (current) {
1877 row = g_list_position (clist->row_list, (GList *) current);
1878 old_y = row * clist->row_height - clist->vadjustment->value;
1881 free_list = tmp_list;
1883 for (; tmp_list; tmp_list = tmp_list->next)
1884 gtk_ctree_collapse (GTK_CTREE (panel->tree), tmp_list->data);
1886 /* We have to calculate the row position again because rows may
1887 * have shifted during the collapse.
1889 if (current) {
1890 row = g_list_position (clist->row_list, (GList *) current);
1891 new_y = row * clist->row_height - clist->vadjustment->value;
1893 if (new_y != old_y)
1894 gtk_adjustment_set_value (clist->vadjustment,
1895 clist->vadjustment->value + new_y - old_y);
1898 if (free_list->prev)
1899 free_list->prev->next = NULL;
1900 else
1901 dtree->auto_expanded_nodes = NULL;
1903 free_list->prev = NULL;
1904 g_list_free (free_list);
1909 * panel_tree_scrolling_is_desirable:
1911 * If the cursor is in a position close to either edge (top or bottom)
1912 * and there is possible to scroll the window, this routine returns
1913 * true.
1915 static gboolean
1916 panel_tree_scrolling_is_desirable (WPanel *panel, int x, int y)
1918 GtkDTree *dtree = GTK_DTREE (panel->tree);
1919 GtkAdjustment *va;
1921 va = scrolled_window_get_vadjustment (panel->tree_scrolled_window);
1923 if (y < 10) {
1924 if (va->value > va->lower)
1925 return TRUE;
1926 } else {
1927 if (y > (GTK_WIDGET (dtree)->allocation.height - 10)){
1928 if (va->value < va->upper - va->page_size)
1929 return TRUE;
1933 return FALSE;
1936 /* Timer callback to scroll the tree */
1937 static gboolean
1938 panel_tree_scroll (gpointer data)
1940 WPanel *panel = data;
1941 GtkAdjustment *va;
1942 double v;
1944 va = scrolled_window_get_vadjustment (panel->tree_scrolled_window);
1946 if (panel->drag_motion_y < 10) {
1947 v = va->value - va->step_increment;
1948 if (v < va->lower)
1949 v = va->lower;
1951 gtk_adjustment_set_value (va, v);
1952 } else {
1953 v = va->value + va->step_increment;
1954 if (v > va->upper - va->page_size)
1955 v = va->upper - va->page_size;
1957 gtk_adjustment_set_value (va, v);
1960 return TRUE;
1963 /* Callback for the drag_motion signal of the tree. We set up a timer to
1964 * automatically open nodes if the user hovers over them.
1966 static gboolean
1967 panel_tree_drag_motion (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time,
1968 gpointer data)
1970 GtkDTree *dtree;
1971 WPanel *panel;
1972 int on_row, row, col;
1973 GtkCTreeNode *node;
1974 GdkDragAction action;
1975 GtkWidget *source_widget;
1976 char *row_path;
1977 int on_drag_row;
1979 dtree = GTK_DTREE (widget);
1980 panel = data;
1982 panel->drag_tree_dragging_over = TRUE;
1983 panel_setup_drag_scroll (panel, x, y,
1984 panel_tree_scrolling_is_desirable,
1985 panel_tree_scroll);
1987 on_row = gtk_clist_get_selection_info (GTK_CLIST (widget), x, y, &row, &col);
1989 /* Remove the auto-expansion timeout if we are on the blank area of the
1990 * tree or on a row different from the previous one.
1992 if ((!on_row || row != panel->drag_tree_row) && panel->drag_tree_timeout_id != 0) {
1993 gtk_timeout_remove (panel->drag_tree_timeout_id);
1994 panel->drag_tree_timeout_id = 0;
1996 if (panel->drag_tree_fe) {
1997 file_entry_free (panel->drag_tree_fe);
1998 panel->drag_tree_fe = NULL;
2002 action = 0;
2004 if (on_row) {
2005 node = gtk_ctree_node_nth (GTK_CTREE (dtree), row);
2006 row_path = gtk_dtree_get_row_path (dtree, node);
2008 if (row != panel->drag_tree_row) {
2009 /* Highlight the row by selecting it */
2011 panel_tree_check_auto_expand (panel, node);
2013 dtree->internal = TRUE;
2014 gtk_clist_select_row (GTK_CLIST (widget), row, 0);
2015 dtree->internal = FALSE;
2017 /* Create the file entry to validate drops */
2019 panel->drag_tree_fe = file_entry_from_file (row_path);
2021 /* Install the timeout handler for auto-expansion */
2023 panel->drag_tree_timeout_id = gtk_timeout_add (TREE_OPEN_TIMEOUT,
2024 tree_drag_open_directory,
2025 panel);
2026 panel->drag_tree_row = row;
2029 /* Validate the action */
2031 gdnd_find_panel_by_drag_context (context, &source_widget);
2033 /* If this tree is the drag source, see if the user is trying to
2034 * drop on the row being dragged. Otherwise, consider all rows
2035 * as not selected.
2037 if (source_widget == widget) {
2038 g_assert (dtree->drag_dir != NULL);
2039 on_drag_row = strcmp (row_path, dtree->drag_dir) == 0;
2040 } else
2041 on_drag_row = FALSE;
2043 action = gdnd_validate_action (context,
2044 FALSE,
2045 source_widget != NULL,
2046 source_widget == widget,
2047 row_path,
2048 panel->drag_tree_fe,
2049 on_drag_row);
2051 g_free (row_path);
2052 } else {
2053 panel->drag_tree_row = -1;
2054 panel_tree_check_auto_expand (panel, NULL);
2057 gdk_drag_status (context, action, time);
2058 return TRUE;
2061 /* Callback for the drag_leave signal of the tree. We deactivate the timeout for
2062 * automatic branch expansion.
2064 static void
2065 panel_tree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data)
2067 WPanel *panel;
2069 panel = data;
2071 panel->drag_tree_dragging_over = FALSE;
2072 panel_cancel_drag_scroll (panel);
2074 if (panel->drag_tree_timeout_id != 0) {
2075 gtk_timeout_remove (panel->drag_tree_timeout_id);
2076 panel->drag_tree_timeout_id = 0;
2079 if (panel->drag_tree_fe) {
2080 file_entry_free (panel->drag_tree_fe);
2081 panel->drag_tree_fe = NULL;
2084 if (panel->drag_tree_row != -1)
2085 panel_tree_check_auto_expand (panel, NULL);
2087 panel->drag_tree_row = -1;
2090 #if CONTEXT_MENU_ON_TREE
2091 static void
2092 tree_do_op (GtkWidget *tree, WPanel *panel, int operation)
2096 static void
2097 tree_copy_cmd (GtkWidget *tree, WPanel *panel)
2099 tree_do_op (tree, panel, OP_COPY);
2102 static void
2103 tree_del_cmd (GtkWidget *tree, WPanel *panel)
2105 tree_do_op (tree, panel, OP_DELETE);
2108 static void
2109 tree_ren_cmd (GtkWidget *tree, WPanel *panel)
2111 tree_do_op (tree, panel, OP_MOVE);
2114 static GnomeUIInfo tree_popup_items[] = {
2115 GNOMEUIINFO_ITEM_STOCK(N_("_Copy..."), N_("Copy directory"), tree_copy_cmd, GNOME_STOCK_PIXMAP_COPY),
2116 GNOMEUIINFO_ITEM_STOCK(N_("_Delete..."), N_("Delete directory"), tree_del_cmd, GNOME_STOCK_PIXMAP_TRASH),
2117 GNOMEUIINFO_ITEM_NONE(N_("_Move..."), N_("Rename or move directory"), tree_ren_cmd),
2119 GNOMEUIINFO_END
2122 static void
2123 panel_tree_button_press (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
2125 GtkWidget *popup;
2127 if (event->type != GDK_BUTTON_PRESS)
2128 return;
2130 if (event->button != 3)
2131 return;
2133 popup = gnome_popup_menu_new (tree_popup_items);
2134 gnome_popup_menu_do_popup_modal (popup, NULL, NULL, event, panel);
2135 gtk_widget_destroy (popup);
2137 #endif
2140 * Middle click on the directory tree opens a new window.
2142 static void
2143 panel_tree_button_release (GtkWidget *widget, GdkEventButton *event, WPanel *panel)
2145 GtkDTree *dtree;
2146 GtkCTreeNode *node;
2147 int row;
2148 char *path;
2150 if (event->type != GDK_BUTTON_RELEASE)
2151 return;
2153 if (event->button != 2)
2154 return;
2156 dtree = GTK_DTREE (widget);
2157 if (!gtk_clist_get_selection_info (GTK_CLIST (dtree), event->x, event->y, &row, NULL))
2158 return;
2160 node = gtk_ctree_node_nth (GTK_CTREE (dtree), row);
2161 if (!node)
2162 return;
2164 path = gtk_dtree_get_row_path (dtree, node);
2165 if (!path)
2166 return;
2168 new_panel_at (path);
2172 * panel_create_tree_view:
2174 * Create and initializes the GtkDTree widget for being used in the
2175 * Panel
2177 static GtkWidget *
2178 panel_create_tree_view (WPanel *panel)
2180 GtkWidget *tree;
2182 tree = gtk_dtree_new ();
2183 /*gtk_ctree_set_line_style (GTK_CTREE (tree), GTK_CTREE_LINES_DOTTED);*/
2184 /*gtk_ctree_set_expander_style (GTK_CTREE (tree), GTK_CTREE_EXPANDER_SQUARE);*/
2185 gtk_ctree_set_indent (GTK_CTREE (tree), 10);
2187 /* DTree signals */
2189 gtk_signal_connect (GTK_OBJECT (tree), "directory_changed",
2190 GTK_SIGNAL_FUNC (panel_chdir), panel);
2191 gtk_signal_connect (GTK_OBJECT (tree), "scan_begin",
2192 GTK_SIGNAL_FUNC (panel_tree_scan_begin), panel);
2193 gtk_signal_connect (GTK_OBJECT (tree), "scan_end",
2194 GTK_SIGNAL_FUNC (panel_tree_scan_end), panel);
2195 gtk_signal_connect (GTK_OBJECT (tree), "possibly_ungrab",
2196 GTK_SIGNAL_FUNC (panel_tree_possibly_ungrab), panel);
2198 /* Set up drag source */
2200 gtk_drag_source_set (GTK_WIDGET (tree), GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
2201 drag_types, ELEMENTS (drag_types),
2202 (GDK_ACTION_LINK | GDK_ACTION_MOVE | GDK_ACTION_COPY
2203 | GDK_ACTION_ASK | GDK_ACTION_DEFAULT));
2205 gtk_signal_connect (GTK_OBJECT (tree), "drag_begin",
2206 GTK_SIGNAL_FUNC (panel_tree_drag_begin), panel);
2207 gtk_signal_connect (GTK_OBJECT (tree), "drag_end",
2208 GTK_SIGNAL_FUNC (panel_tree_drag_end), panel);
2209 gtk_signal_connect (GTK_OBJECT (tree), "drag_data_get",
2210 GTK_SIGNAL_FUNC (panel_tree_drag_data_get), panel);
2212 /* Set up drag destination */
2214 gtk_drag_dest_set (GTK_WIDGET (tree),
2215 GTK_DEST_DEFAULT_DROP,
2216 drop_types, ELEMENTS (drop_types),
2217 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK | GDK_ACTION_ASK);
2219 gtk_signal_connect (GTK_OBJECT (tree), "drag_motion",
2220 GTK_SIGNAL_FUNC (panel_tree_drag_motion), panel);
2221 gtk_signal_connect (GTK_OBJECT (tree), "drag_leave",
2222 GTK_SIGNAL_FUNC (panel_tree_drag_leave), panel);
2223 gtk_signal_connect (GTK_OBJECT (tree), "drag_data_received",
2224 GTK_SIGNAL_FUNC (panel_tree_drag_data_received), panel);
2226 #ifdef CONTEXT_MENU_ON_TREE
2227 /* Context sensitive menu */
2228 gtk_signal_connect_after (GTK_OBJECT (tree), "button_press_event",
2229 GTK_SIGNAL_FUNC (panel_tree_button_press), panel);
2230 gtk_clist_set_button_actions (GTK_CLIST (tree), 2, GTK_BUTTON_SELECTS);
2231 #endif
2233 gtk_signal_connect_after (GTK_OBJECT (tree), "button_release_event",
2234 GTK_SIGNAL_FUNC (panel_tree_button_release), panel);
2235 return tree;
2239 * create_and_setup_pane:
2241 * Creates the horizontal GtkPaned widget that holds the tree
2242 * and the listing/iconing displays
2244 static GtkWidget *
2245 create_and_setup_pane (WPanel *panel)
2247 GtkWidget *pane;
2248 GtkWidget *tree = panel->tree;
2249 GdkFont *tree_font = tree->style->font;
2250 int size;
2252 pane = gtk_hpaned_new ();
2254 if (tree_panel_visible == -1)
2255 size = 20 * gdk_string_width (tree_font, "W");
2256 else {
2257 if (tree_panel_visible)
2258 size = tree_panel_visible;
2259 else
2260 size = 0;
2262 #if 0
2263 gtk_paned_set_position (GTK_PANED (pane), size);
2264 #else
2266 * Hack: set the default startup size for the pane without
2267 * using _set_usize which would set the minimal size
2269 GTK_PANED (pane)->child1_size = size;
2270 GTK_PANED (pane)->position_set = TRUE;
2271 #endif
2272 gtk_widget_show (pane);
2274 return pane;
2277 static void
2278 panel_back (GtkWidget *button, WPanel *panel)
2280 directory_history_prev (panel);
2283 static void
2284 panel_fwd (GtkWidget *button, WPanel *panel)
2286 directory_history_next (panel);
2289 static void
2290 panel_up (GtkWidget *button, WPanel *panel)
2292 do_panel_cd (panel, "..", cd_exact);
2295 static void
2296 rescan_panel (GtkWidget *widget, gpointer data)
2298 reread_cmd ();
2301 static void
2302 go_home (GtkWidget *widget, WPanel *panel)
2304 do_panel_cd (panel, "~", cd_exact);
2307 /* The toolbar */
2309 static GnomeUIInfo toolbar[] = {
2310 GNOMEUIINFO_ITEM_STOCK (N_("Back"), N_("Go to the previously visited directory"),
2311 panel_back, GNOME_STOCK_PIXMAP_BACK),
2312 GNOMEUIINFO_ITEM_STOCK (N_("Up"), N_("Go up a level in the directory hierarchy"),
2313 panel_up, GNOME_STOCK_PIXMAP_UP),
2314 GNOMEUIINFO_ITEM_STOCK (N_("Forward"), N_("Go to the next directory"),
2315 panel_fwd, GNOME_STOCK_PIXMAP_FORWARD),
2316 GNOMEUIINFO_SEPARATOR,
2317 GNOMEUIINFO_ITEM_STOCK (N_("Rescan"), N_("Rescan the current directory"),
2318 rescan_panel, GNOME_STOCK_PIXMAP_REFRESH),
2319 GNOMEUIINFO_SEPARATOR,
2320 GNOMEUIINFO_ITEM_STOCK (N_("Home"), N_("Go to your home directory"),
2321 go_home, GNOME_STOCK_PIXMAP_HOME),
2322 GNOMEUIINFO_SEPARATOR,
2323 GNOMEUIINFO_RADIOLIST(panel_view_toolbar_uiinfo),
2324 GNOMEUIINFO_END
2327 static void
2328 do_ui_signal_connect (GnomeUIInfo *uiinfo, gchar *signal_name,
2329 GnomeUIBuilderData *uibdata)
2331 if (uiinfo->moreinfo)
2332 gtk_signal_connect (GTK_OBJECT (uiinfo->widget),
2333 signal_name, uiinfo->moreinfo, uibdata->data ?
2334 uibdata->data : uiinfo->user_data);
2337 static void
2338 tree_size_allocate (GtkWidget *widget, GtkAllocation *allocation, WPanel *panel)
2340 if (allocation->width <= 0){
2341 tree_panel_visible = 0;
2342 } else {
2343 tree_panel_visible = allocation->width;
2346 if (auto_save_setup) {
2347 save_setup ();
2351 void
2352 x_create_panel (Dlg_head *h, widget_data parent, WPanel *panel)
2354 GtkWidget *status_line, *vbox, *ministatus_box;
2355 GtkWidget *cwd;
2356 GtkWidget *dock;
2357 GnomeUIBuilderData uibdata;
2359 panel->xwindow = gtk_widget_get_toplevel (GTK_WIDGET (panel->widget.wdata));
2361 panel->table = gtk_table_new (2, 1, 0);
2364 * Tree View
2366 panel->tree_scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2367 gtk_scrolled_window_set_policy (
2368 GTK_SCROLLED_WINDOW (panel->tree_scrolled_window),
2369 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2370 panel->tree = panel_create_tree_view (panel);
2371 gtk_container_add (GTK_CONTAINER (panel->tree_scrolled_window), panel->tree);
2372 gtk_widget_show_all (panel->tree_scrolled_window);
2375 * Icon and Listing display
2377 panel->notebook = gtk_notebook_new ();
2378 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (panel->notebook), FALSE);
2379 gtk_widget_show (panel->notebook);
2381 panel->icons = panel_create_icon_display (panel);
2382 gtk_widget_show (panel->icons);
2384 memcpy (panel->column_width, default_column_width, sizeof (default_column_width));
2385 panel->list = panel_create_file_list (panel);
2386 gtk_widget_ref (panel->icons);
2387 gtk_widget_ref (panel->list);
2389 panel->panel_listbox = gtk_event_box_new ();
2390 gtk_widget_show (panel->panel_listbox);
2391 gtk_container_add (GTK_CONTAINER (panel->panel_listbox), panel->list);
2393 gtk_notebook_append_page (GTK_NOTEBOOK (panel->notebook), panel->icons, NULL);
2394 gtk_notebook_append_page (GTK_NOTEBOOK (panel->notebook), panel->panel_listbox, NULL);
2395 gtk_notebook_set_page (GTK_NOTEBOOK (panel->notebook),
2396 panel->list_type == list_icons ? 0 : 1);
2398 gtk_widget_show (panel->icons);
2399 gtk_widget_show (panel->list);
2400 gtk_widget_show (panel->notebook);
2403 * Pane
2405 panel->pane = create_and_setup_pane (panel);
2406 gtk_paned_add1 (GTK_PANED (panel->pane), panel->tree_scrolled_window);
2407 gtk_signal_connect (GTK_OBJECT (panel->tree_scrolled_window), "size_allocate",
2408 GTK_SIGNAL_FUNC (tree_size_allocate), panel);
2411 * Current Working directory
2414 cwd = panel_create_cwd (h, panel, &panel->current_dir);
2417 * We go through a lot of pain, wrestling with gnome_app* and gmc's @#$&*#$ internal structure and
2418 * make the #@$*&@#$ toolbars here...
2421 status_line = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
2422 uibdata.connect_func = do_ui_signal_connect;
2423 uibdata.data = panel;
2424 uibdata.is_interp = FALSE;
2425 uibdata.relay_func = NULL;
2426 uibdata.destroy_func = NULL;
2428 gnome_app_fill_toolbar_custom (GTK_TOOLBAR (status_line), toolbar, &uibdata, NULL);
2429 gnome_app_add_toolbar (GNOME_APP (panel->xwindow),
2430 GTK_TOOLBAR (status_line),
2431 "gmc-toolbar0",
2432 GNOME_DOCK_ITEM_BEH_EXCLUSIVE,
2433 GNOME_DOCK_TOP,
2434 2, 0, 0);
2435 panel->view_toolbar_items = copy_uiinfo_widgets (panel_view_toolbar_uiinfo);
2437 panel->back_b = toolbar[0].widget;
2438 panel->up_b = toolbar[1].widget;
2439 panel->fwd_b = toolbar[2].widget;
2440 panel_update_marks (panel);
2442 /* Set the list type by poking a toolbar item. Yes, this is hackish.
2443 * We fall back to icon view if a certain listing type is not supported.
2444 * Be sure to keep this in sync with the uiinfo arrays in glayout.c.
2447 if (panel->list_type == list_brief)
2448 gtk_toggle_button_set_active (
2449 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[1].widget), TRUE);
2450 else if (panel->list_type == list_full)
2451 gtk_toggle_button_set_active (
2452 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[2].widget), TRUE);
2453 else if (panel->list_type == list_user)
2454 gtk_toggle_button_set_active (
2455 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[3].widget), TRUE);
2456 else
2457 gtk_toggle_button_set_active (
2458 GTK_TOGGLE_BUTTON (panel_view_toolbar_uiinfo[0].widget), TRUE);
2460 status_line = gtk_hbox_new (FALSE, 2);
2461 gtk_container_set_border_width (GTK_CONTAINER (status_line), 3);
2462 gtk_box_pack_start (GTK_BOX (status_line),
2463 gtk_label_new (_("Location:")), FALSE, FALSE, 0);
2464 gtk_box_pack_start (GTK_BOX (status_line), cwd, TRUE, TRUE, 0);
2466 dock = gnome_dock_item_new ("gmc-toolbar1",
2467 (GNOME_DOCK_ITEM_BEH_EXCLUSIVE
2468 | GNOME_DOCK_ITEM_BEH_NEVER_VERTICAL));
2469 gtk_container_add (GTK_CONTAINER (dock), status_line);
2470 gnome_dock_add_item (GNOME_DOCK (GNOME_APP (panel->xwindow)->dock),
2471 GNOME_DOCK_ITEM (dock), GNOME_DOCK_TOP, 1, 0, 0, FALSE);
2473 gtk_widget_show_all (dock);
2475 panel->view_table = gtk_table_new (1, 1, 0);
2476 gtk_widget_show (panel->view_table);
2479 * The status bar.
2481 ministatus_box = gtk_frame_new (NULL);
2482 gtk_frame_set_shadow_type (GTK_FRAME (ministatus_box), GTK_SHADOW_IN);
2484 panel->status = gtk_label_new (_("Show all files"));
2485 gtk_misc_set_alignment (GTK_MISC (panel->status), 0.0, 0.0);
2486 gtk_misc_set_padding (GTK_MISC (panel->status), 2, 0);
2488 gtk_box_pack_start (GTK_BOX (panel->ministatus), ministatus_box, FALSE, FALSE, 0);
2489 gtk_container_add (GTK_CONTAINER (ministatus_box), panel->status);
2491 gtk_widget_show (ministatus_box);
2492 gtk_widget_show (panel->status);
2495 * Put the icon list and the file listing in a nice frame
2498 /* Add both the icon view and the listing view */
2499 gtk_table_attach (GTK_TABLE (panel->view_table), panel->notebook, 0, 1, 0, 1,
2500 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2501 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2502 0, 0);
2504 gtk_table_attach (GTK_TABLE (panel->table), panel->pane, 0, 1, 1, 2,
2505 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2506 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2507 0, 0);
2509 gtk_paned_add2 (GTK_PANED (panel->pane), panel->view_table);
2512 * ministatus_box is a container created just to put the
2513 * panel->ministatus inside.
2515 * Then the resize mode for ministatus_box is changed to stop
2516 * any size-changed messages to be propagated above.
2518 * This is required as the panel->ministatus Label is changed
2519 * very often (to display status information). If this hack
2520 * is not made, then the resize is queued and the whole window
2521 * flickers each time this changes
2523 #if 0
2524 ministatus_box = gtk_hbox_new (FALSE, 0);
2525 gtk_container_add (GTK_CONTAINER (ministatus_box), panel->ministatus);
2526 gtk_widget_show (ministatus_box);
2527 gtk_container_set_resize_mode (GTK_CONTAINER (ministatus_box), GTK_RESIZE_QUEUE);
2528 gtk_table_attach (GTK_TABLE (panel->table), ministatus_box, 0, 1, 2, 3,
2529 GTK_EXPAND | GTK_FILL | GTK_SHRINK,
2530 0, 0, 0);
2532 gtk_table_attach (GTK_TABLE (panel->table), frame, 0, 1, 3, 4,
2533 GTK_EXPAND | GTK_FILL,
2534 0, 0, 0);
2536 #endif
2537 /* Ultra nasty hack: pull the vbox from wdata */
2538 vbox = GTK_WIDGET (panel->widget.wdata);
2540 panel->widget.wdata = (widget_data) panel->table;
2542 /* Now, insert our table in our parent */
2543 gtk_container_add (GTK_CONTAINER (vbox), panel->table);
2544 gtk_widget_show (vbox);
2545 gtk_widget_show (panel->table);
2547 if (!(panel->widget.options & W_PANEL_HIDDEN) && !is_trash_panel)
2548 gtk_widget_show (gtk_widget_get_toplevel (panel->table));
2550 if (!pixmaps_ready)
2551 panel_create_pixmaps ();
2553 /* In GNOME the panel wants to have the cursor, to avoid "auto" focusing the
2554 * filter input line
2556 panel->widget.options |= W_WANT_CURSOR;
2557 panel->estimated_total = 0;
2559 panel->timer_id = -1;
2561 /* re-set the user_format explicitly */
2562 if (default_user_format != NULL) {
2563 g_free (panel->user_format);
2564 panel->user_format = g_strdup (default_user_format);
2568 void
2569 panel_update_cols (Widget *panel, int frame_size)
2571 panel->cols = 60;
2572 panel->lines = 20;
2575 char *
2576 get_nth_panel_name (int num)
2578 static char buffer [BUF_TINY];
2580 if (!num)
2581 return "New Left Panel";
2582 else if (num == 1)
2583 return "New Right Panel";
2584 else {
2585 g_snprintf (buffer, sizeof (buffer), "%ith Panel", num);
2586 return buffer;
2590 void
2591 load_hint (void)
2593 char *hint;
2595 if ((hint = get_random_hint ())){
2596 if (*hint)
2597 set_hintbar (hint);
2598 g_free (hint);
2599 } else
2600 set_hintbar ("The GNOME File Manager " VERSION);
2604 void
2605 paint_frame (WPanel *panel)
2609 void
2610 x_reset_sort_labels (WPanel *panel)
2612 int page;
2614 if (!panel->notebook)
2615 return;
2617 if (panel->list_type == list_icons){
2618 page = 0;
2619 } else {
2620 page = 1;
2621 panel_switch_new_display_mode (panel);
2623 gtk_notebook_set_page (GTK_NOTEBOOK (panel->notebook), page);
2626 /* Releases all of the X resources allocated */
2627 void
2628 x_panel_destroy (WPanel *panel)
2630 gtk_widget_destroy (GTK_WIDGET (panel->xwindow));