/* GNU Midnight Commander -- GNOME edition * * Directory display routines * * Copyright (C) 1997 The Free Software Foundation * * Authors: Miguel de Icaza * Federico Mena */ #include #include #include /* atoi */ #include "fs.h" #include "x.h" #include "dir.h" #include "panel.h" #include "command.h" #include "panel.h" /* current_panel */ #include "command.h" /* cmdline */ #include "main.h" #include "color.h" #include "mouse.h" #include "layout.h" /* get_panel_widget */ #include "ext.h" /* regex_command */ #include "cmd.h" /* copy_cmd, ren_cmd, delete_cmd, ... */ #include "gscreen.h" #include "dir.h" #include "dialog.h" #include "gdesktop.h" #include "gpageprop.h" /* The pixmaps */ #include "directory.xpm" #include "link.xpm" #include "dev.xpm" /* This is used to initialize our pixmaps */ static int pixmaps_ready; GdkPixmap *icon_directory_pixmap; GdkBitmap *icon_directory_mask; GdkPixmap *icon_link_pixmap; GdkBitmap *icon_link_mask; GdkPixmap *icon_dev_pixmap; GdkBitmap *icon_dev_mask; static char *drag_types [] = { "text/plain", "url:ALL" }; static char *drop_types [] = { "url:ALL" }; #define ELEMENTS(x) (sizeof (x) / sizeof (x[0])) /* GtkWidgets with the shaped windows for dragging */ GtkWidget *drag_directory = NULL; GtkWidget *drag_directory_ok = NULL; GtkWidget *drag_multiple = NULL; GtkWidget *drag_multiple_ok = NULL; typedef void (*context_menu_callback)(GtkWidget *, WPanel *); #define F_ALL 1 #define F_REGULAR 2 #define F_SYMLINK 4 #define F_SINGLE 8 void repaint_file (WPanel *panel, int file_index, int move, int attr, int isstatus) { } void show_dir (WPanel *panel) { gtk_entry_set_text (GTK_ENTRY (panel->current_dir), panel->cwd); } static void panel_file_list_set_type_bitmap (GtkCList *cl, int row, int column, int color, file_entry *fe) { /* Here, add more icons */ switch (color){ case DIRECTORY_COLOR: gtk_clist_set_pixmap (cl, row, column, icon_directory_pixmap, icon_directory_mask); break; case LINK_COLOR: gtk_clist_set_pixmap (cl, row, column, icon_link_pixmap, icon_link_mask); break; case DEVICE_COLOR: gtk_clist_set_pixmap (cl, row, column, icon_dev_pixmap, icon_dev_mask); break; } } static void panel_file_list_set_row_colors (GtkCList *cl, int row, int color_pair) { gtk_clist_set_foreground (cl, row, gmc_color_pairs [color_pair].fore); gtk_clist_set_background (cl, row, gmc_color_pairs [color_pair].back); } void x_fill_panel (WPanel *panel) { const int top = panel->count; const int items = panel->format->items; const int selected = panel->selected; GtkCList *cl = GTK_CLIST (panel->list); int i, col, type_col, color; char **texts; texts = malloc (sizeof (char *) * items); gtk_clist_freeze (GTK_CLIST (cl)); gtk_clist_clear (GTK_CLIST (cl)); /* which column holds the type information */ type_col = -1; for (i = 0; i < top; i++){ file_entry *fe = &panel->dir.list [i]; format_e *format = panel->format; for (col = 0; format; format = format->next){ if (!format->use_in_gui) continue; if (type_col == -1) if (strcmp (format->id, "type") == 0) type_col = col; if (!format->string_fn) texts [col] = ""; else texts [col] = (*format->string_fn)(fe, 10); col++; } gtk_clist_append (cl, texts); color = file_entry_color (fe); panel_file_list_set_row_colors (cl, i, color); if (type_col != -1) panel_file_list_set_type_bitmap (cl, i, type_col, color, fe); } /* This is needed as the gtk_clist_append changes selected under us :-( */ panel->selected = selected; select_item (panel); gtk_clist_thaw (GTK_CLIST (cl)); free (texts); } static void gmc_panel_set_size (int index, int boot) { Widget *w; WPanel *p; w = (Widget *) get_panel_widget (index); p = (WPanel *) w; w->cols = 40; w->lines = 25; set_panel_formats (p); paint_panel (p); if (!boot) paint_frame (p); x_fill_panel (p); } void x_panel_set_size (int index) { printf ("WARNING: set size called\n"); gmc_panel_set_size (index, 1); } void x_panel_select_item (WPanel *panel, int index, int value) { int color; color = file_compute_color (value ? MARKED : NORMAL, &panel->dir.list[index]); panel_file_list_set_row_colors (GTK_CLIST (panel->list), index, color); } void x_select_item (WPanel *panel) { GtkCList *clist = GTK_CLIST (panel->list); gtk_clist_select_row (clist, panel->selected, 0); if (gtk_clist_row_is_visible (clist, panel->selected) != GTK_VISIBILITY_FULL){ printf ("No fue visible %d\n", panel->selected); gtk_clist_moveto (clist, panel->selected, 0, 0.5, 0.0); } } void x_unselect_item (WPanel *panel) { gtk_clist_unselect_row (GTK_CLIST (panel->list), panel->selected, 0); } void x_filter_changed (WPanel *panel) { gtk_entry_set_text (GTK_ENTRY (gnome_entry_gtk_entry (GNOME_ENTRY (panel->filter_w))), panel->filter ? panel->filter : ""); } void x_adjust_top_file (WPanel *panel) { /* gtk_clist_moveto (GTK_CLIST (panel->list), panel->top_file, 0, 0.0, 0.0); */ } #define COLUMN_INSET 3 #define CELL_SPACING 1 void panel_file_list_configure_contents (GtkWidget *file_list, WPanel *panel, int main_width, int height) { format_e *format = panel->format; int i, used_columns, expandables, items; int char_width, usable_pixels, extra_pixels, width; int total_columns, extra_columns; int expand_space, extra_space, shrink_space; int lost_pixels; /* Pass 1: Count minimum columns, * set field_len to default to the requested_field_len * and compute how much space we lost to the column decorations */ lost_pixels = used_columns = expandables = items = 0; for (format = panel->format; format; format = format->next){ format->field_len = format->requested_field_len; if (!format->use_in_gui) continue; used_columns += format->field_len; items++; if (format->expand) expandables++; lost_pixels += CELL_SPACING + (2 * COLUMN_INSET); } /* The left scrollbar might take some space from us, use this information */ if (GTK_WIDGET_VISIBLE (GTK_CLIST (file_list)->vscrollbar)){ int scrollbar_width = GTK_WIDGET (GTK_CLIST (file_list)->vscrollbar)->requisition.width; int scrollbar_space = GTK_CLIST_CLASS (GTK_OBJECT (file_list)->klass)->scrollbar_spacing; lost_pixels += scrollbar_space + scrollbar_width; } char_width = gdk_string_width (file_list->style->font, "xW") / 2; width = main_width - lost_pixels; extra_pixels = width % char_width; usable_pixels = width - extra_pixels; total_columns = usable_pixels / char_width; extra_columns = total_columns - used_columns; if (extra_columns > 0){ expand_space = extra_columns / expandables; extra_space = extra_columns % expandables; } else extra_space = expand_space = 0; panel->estimated_total = total_columns; /* If we dont have enough space, shorten the fields */ if (used_columns > total_columns){ expand_space = 0; shrink_space = (used_columns - total_columns) / items; } else shrink_space = 0; gtk_clist_freeze (GTK_CLIST (file_list)); for (i = 0, format = panel->format; format; format = format->next){ if (!format->use_in_gui) continue; format->field_len += (format->expand ? expand_space : 0) - shrink_space; gtk_clist_set_column_width (GTK_CLIST (file_list), i, format->field_len * char_width); i++; } gtk_clist_thaw (GTK_CLIST (file_list)); } static void panel_action_open (GtkWidget *widget, WPanel *panel) { do_enter (panel); } static void panel_action_open_with (GtkWidget *widget, WPanel *panel) { char *command; command = input_expand_dialog (" Open with...", "Enter extra arguments:", panel->dir.list [panel->selected].fname); execute (command); free (command); } void panel_action_view (GtkWidget *widget, WPanel *panel) { view_cmd (panel); } void panel_action_view_unfiltered (GtkWidget *widget, WPanel *panel) { view_simple_cmd (panel); } void panel_action_properties (GtkWidget *widget, WPanel *panel) { file_entry *fe = &panel->dir.list [panel->selected]; char *full_name = concat_dir_and_file (panel->cwd, fe->fname); if (item_properties (GTK_WIDGET (panel->list), full_name, NULL) != 0) reread_cmd (); free (full_name); } static struct { char *text; int flags; context_menu_callback callback; } file_actions [] = { { "Properties", F_SINGLE, panel_action_properties }, { "", F_SINGLE, NULL }, { "Open", F_ALL, panel_action_open }, { "Open with", F_ALL, panel_action_open_with }, { "View", F_ALL, panel_action_view }, { "View unfiltered", F_ALL, panel_action_view_unfiltered }, { "", 0, NULL }, { "Link...", F_REGULAR | F_SINGLE, (context_menu_callback) link_cmd }, { "Symlink...", F_SINGLE, (context_menu_callback) symlink_cmd }, { "Edit symlink...", F_SYMLINK, (context_menu_callback) edit_symlink_cmd }, { NULL, 0, NULL }, }; static struct { char *text; context_menu_callback callback; } common_actions [] = { { "Copy...", (context_menu_callback) copy_cmd }, { "Rename/move..", (context_menu_callback) ren_cmd }, { "Delete...", (context_menu_callback) delete_cmd }, { NULL, NULL } }; static GtkWidget * create_popup_submenu (WPanel *panel, int row, char *filename) { static int submenu_translated; GtkWidget *menu; int i; if (!submenu_translated){ /* FIXME translate it */ submenu_translated = 1; } menu = gtk_menu_new (); for (i = 0; file_actions [i].text; i++){ GtkWidget *item; /* Items with F_ALL bypass any other condition */ if (!(file_actions [i].flags & F_ALL)){ /* Items with F_SINGLE require that no marked files exist */ if (file_actions [i].flags & F_SINGLE){ if (panel->marked) break; } /* Items with F_REGULAR do not accept any strange file types */ if (file_actions [i].flags & F_REGULAR){ struct stat *s = &panel->dir.list [row].buf; if (S_ISLNK (panel->dir.list [row].f.link_to_dir)) break; if (S_ISSOCK (s->st_mode) || S_ISCHR (s->st_mode) || S_ISFIFO (s->st_mode) || S_ISBLK (s->st_mode)) break; } /* Items with F_SYMLINK only operate on symbolic links */ if (file_actions [i].flags & F_SYMLINK){ if (!S_ISLNK (panel->dir.list [row].buf.st_mode)) break; } } if (*file_actions [i].text) item = gtk_menu_item_new_with_label (file_actions [i].text); else item = gtk_menu_item_new (); gtk_widget_show (item); if (file_actions [i].callback){ gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC(file_actions [i].callback), panel); } gtk_menu_append (GTK_MENU (menu), item); } return menu; } /* * Ok, this activates a menu popup action for a filename * it is kind of hackish, it gets the desired action from the * item, so it has to peek inside the item to retrieve the label */ static void popup_activate_by_string (GtkMenuItem *item, WPanel *panel) { char *filename = panel->dir.list [panel->selected].fname; char *action; int movedir; g_return_if_fail (GTK_IS_MENU_ITEM (item)); g_return_if_fail (GTK_IS_LABEL (GTK_BIN (item)->child)); action = GTK_LABEL (GTK_BIN (item)->child)->label; regex_command (filename, action, NULL, &movedir); } static void file_popup_add_context (GtkMenu *menu, WPanel *panel, char *filename) { GtkWidget *item; char *p, *q; int c, i; for (i = 0; common_actions [i].text; i++){ GtkWidget *item; item = gtk_menu_item_new_with_label (common_actions [i].text); gtk_widget_show (item); gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (common_actions [i].callback), panel); gtk_menu_append (GTK_MENU (menu), item); } p = regex_command (filename, NULL, NULL, NULL); if (!p) return; item = gtk_menu_item_new (); gtk_widget_show (item); gtk_menu_append (menu, item); for (;;){ while (*p == ' ' || *p == '\t') p++; if (!*p) break; q = p; while (*q && *q != '=' && *q != '\t') q++; c = *q; *q = 0; item = gtk_menu_item_new_with_label (p); gtk_widget_show (item); gtk_signal_connect (GTK_OBJECT(item), "activate", GTK_SIGNAL_FUNC(popup_activate_by_string), panel); gtk_menu_append (menu, item); if (!c) break; p = q + 1; } } static void file_popup (GdkEvent *event, WPanel *panel, int row, char *filename) { GtkWidget *menu = gtk_menu_new (); GtkWidget *submenu; GtkWidget *item; item = gtk_menu_item_new_with_label (filename); gtk_widget_show (item); gtk_menu_append (GTK_MENU (menu), item); submenu = create_popup_submenu (panel, row, filename); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); file_popup_add_context (GTK_MENU (menu), panel, filename); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, event->button.time); } static void internal_select_item (GtkWidget *file_list, WPanel *panel, int row) { unselect_item (panel); panel->selected = row; gtk_signal_handler_block_by_data (GTK_OBJECT (file_list), panel); select_item (panel); gtk_signal_handler_unblock_by_data (GTK_OBJECT (file_list), panel); } static void panel_file_list_select_row (GtkWidget *file_list, int row, int column, GdkEvent *event, WPanel *panel) { if (!event) { internal_select_item (file_list, panel, row); return; } switch (event->type) { case GDK_BUTTON_PRESS: internal_select_item (file_list, panel, row); switch (event->button.button) { case 1: if (!(event->button.state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) break; /* fallback if shift-click is pressed */ case 2: do_file_mark (panel, row, !panel->dir.list[row].f.marked); break; case 3: file_popup (event, panel, row, panel->dir.list[row].fname); break; } break; case GDK_2BUTTON_PRESS: if (event->button.button == 1) do_enter (panel); break; default: break; } } /* Figure out the number of visible lines in the panel */ static void panel_file_list_compute_lines (GtkCList *file_list, WPanel *panel, int height) { int lost_pixels = 0; if (GTK_WIDGET_VISIBLE (file_list->hscrollbar)){ int scrollbar_width = GTK_WIDGET (file_list->hscrollbar)->requisition.width; int scrollbar_space = GTK_CLIST_CLASS (GTK_OBJECT (file_list)->klass)->scrollbar_spacing; lost_pixels = scrollbar_space + scrollbar_width; } panel->widget.lines = (height-lost_pixels) / (GTK_CLIST (file_list)->row_height + CELL_SPACING); } static void panel_file_list_size_allocate_hook (GtkWidget *file_list, GtkAllocation *allocation, WPanel *panel) { gtk_signal_handler_block_by_data (GTK_OBJECT (file_list), panel); panel_file_list_configure_contents (file_list, panel, allocation->width, allocation->height); gtk_signal_handler_unblock_by_data (GTK_OBJECT (file_list), panel); panel_file_list_compute_lines (GTK_CLIST (file_list), panel, allocation->height); } static void panel_file_list_column_callback (GtkWidget *widget, int col, WPanel *panel) { format_e *format; int i; for (i = 0, format = panel->format; format; format = format->next){ if (!format->use_in_gui) continue; if (i == col){ sortfn *sorting_routine; sorting_routine = get_sort_fn (format->id); if (!sorting_routine) return; if (sorting_routine == panel->sort_type) panel->reverse = !panel->reverse; panel->sort_type = sorting_routine; do_re_sort (panel); return; } i++; } } static void panel_create_pixmaps (GtkWidget *parent) { GdkColor color = gtk_widget_get_style (parent)->bg [GTK_STATE_NORMAL]; pixmaps_ready = 1; icon_directory_pixmap = gdk_pixmap_create_from_xpm_d (parent->window, &icon_directory_mask, &color, directory_xpm); icon_link_pixmap = gdk_pixmap_create_from_xpm_d (parent->window, &icon_link_mask, &color, link_xpm); icon_dev_pixmap = gdk_pixmap_create_from_xpm_d (parent->window, &icon_dev_mask, &color, dev_xpm); } static void panel_file_list_scrolled (GtkAdjustment *adj, WPanel *panel) { if (!GTK_IS_ADJUSTMENT (adj)) { fprintf (stderr, "file_list_is_scrolled is called and there are not enough boats!\n"); exit (1); } } static void panel_configure_file_list (WPanel *panel, GtkWidget *file_list) { format_e *format = panel->format; GtkCList *cl = GTK_CLIST (file_list); GtkObject *adjustment; int i; /* Set sorting callback */ gtk_signal_connect (GTK_OBJECT (file_list), "click_column", GTK_SIGNAL_FUNC (panel_file_list_column_callback), panel); /* Configure the CList */ gtk_clist_set_selection_mode (cl, GTK_SELECTION_BROWSE); gtk_clist_set_policy (cl, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); for (i = 0, format = panel->format; format; format = format->next){ GtkJustification just; if (!format->use_in_gui) continue; /* Set desired justification */ if (format->just_mode == J_LEFT) just = GTK_JUSTIFY_LEFT; else just = GTK_JUSTIFY_RIGHT; gtk_clist_set_column_justification (cl, i, just); i++; } /* Configure the scrolbars */ adjustment = GTK_OBJECT (gtk_range_get_adjustment (GTK_RANGE (cl->vscrollbar))); gtk_signal_connect_after (GTK_OBJECT(adjustment), "value_changed", GTK_SIGNAL_FUNC (panel_file_list_scrolled), panel); } static void * panel_build_selected_file_list (WPanel *panel, int *file_list_len) { if (panel->marked){ char *data, *copy; int i, total_len = 0; int cwdlen = strlen (panel->cwd) + 1; /* first pass, compute the length */ for (i = 0; i < panel->count; i++) if (panel->dir.list [i].f.marked) total_len += (cwdlen + panel->dir.list [i].fnamelen + 1); printf ("Total lenght: %d\n", total_len); data = copy = xmalloc (total_len, "build_selected_file_list"); for (i = 0; i < panel->count; i++) if (panel->dir.list [i].f.marked){ strcpy (copy, panel->cwd); copy [cwdlen-1] = '/'; strcpy (© [cwdlen], panel->dir.list [i].fname); copy += panel->dir.list [i].fnamelen + 1 + cwdlen; } *file_list_len = total_len; return data; } else { char *fullname = concat_dir_and_file (panel->cwd, panel->dir.list [panel->selected].fname); *file_list_len = strlen (fullname) + 1; return fullname; } } static void panel_drag_request (GtkWidget *widget, GdkEventDragRequest *event, WPanel *panel) { void *data; int len; GdkWindowPrivate *clist_window = (GdkWindowPrivate *) (GTK_WIDGET (widget)->window); GdkWindowPrivate *clist_areaw = (GdkWindowPrivate *) (GTK_CLIST (widget)->clist_window); if ((strcmp (event->data_type, "text/plain") == 0) || (strcmp (event->data_type, "url:ALL") == 0)){ data = panel_build_selected_file_list (panel, &len); if (clist_window->dnd_drag_accepted) gdk_window_dnd_data_set ((GdkWindow *)clist_window, (GdkEvent *) event, data, len); else gdk_window_dnd_data_set ((GdkWindow *)clist_areaw, (GdkEvent *) event, data, len); free (data); } } static void panel_drop_enter (GtkWidget *widget, GdkEvent *event) { printf ("%s\n", event->type == GDK_DROP_ENTER ? "DROP ENTER" : (event->type == GDK_DROP_LEAVE ? "DROP LEAVE" : "?")); } static void panel_drop_data_available (GtkWidget *widget, GdkEventDropDataAvailable *data, WPanel *panel) { gint winx, winy; gint dropx, dropy; gint row; char *drop_dir; gdk_window_get_origin (GTK_CLIST (widget)->clist_window, &winx, &winy); dropx = data->coords.x - winx; dropy = data->coords.y - winy; if (dropx < 0 || dropy < 0) return; if (gtk_clist_get_selection_info (GTK_CLIST (widget), dropx, dropy, &row, NULL) == 0) drop_dir = panel->cwd; else { g_assert (row < panel->count); if (S_ISDIR (panel->dir.list [row].buf.st_mode)) drop_dir = concat_dir_and_file (panel->cwd, panel->dir.list [row].fname); else drop_dir = panel->cwd; } drop_on_directory (data, drop_dir, 0); if (drop_dir != panel->cwd) free (drop_dir); } /* Workaround for the CList that is not adding its clist-window to the DND windows */ static void fixed_gtk_widget_dnd_drop_set (GtkCList *clist, int drop_enable, char **drop_types, int count, int is_destructive) { /* gtk_widget_dnd_drop_set (GTK_WIDGET (clist), drop_enable, drop_types, count, is_destructive); */ gdk_window_dnd_drop_set (clist->clist_window, drop_enable, drop_types, count, is_destructive); } static void fixed_gtk_widget_dnd_drag_set (GtkCList *clist, int drag_enable, gchar **type_accept_list, int numtypes) { /* gtk_widget_dnd_drag_set (GTK_WIDGET (clist), drag_enable, type_accept_list, numtypes); */ gdk_window_dnd_drag_set (clist->clist_window, drag_enable, type_accept_list, numtypes); } static void panel_drag_begin (GtkWidget *widget, GdkEvent *event, WPanel *panel) { GdkPoint hotspot = { 15, 15 }; if (panel->marked){ if (drag_multiple && drag_multiple_ok){ gdk_dnd_set_drag_shape (drag_multiple->window, &hotspot, drag_multiple_ok->window, &hotspot); gtk_widget_show (drag_multiple); gtk_widget_show (drag_multiple_ok); } } else { if (drag_directory && drag_directory_ok) gdk_dnd_set_drag_shape (drag_directory->window, &hotspot, drag_directory_ok->window, &hotspot); gtk_widget_show (drag_directory_ok); gtk_widget_show (drag_directory); } } static void panel_artificial_drag_start (GtkCList *window, GdkEventMotion *event) { artificial_drag_start (window->clist_window, event->x, event->y); } static GtkWidget * load_transparent_xpm (char *base) { char *f = concat_dir_and_file (ICONDIR, base); GtkWidget *w; w = make_transparent_window (f); g_free (f); return w; } static void panel_realized (GtkWidget *file_list, WPanel *panel) { GtkObject *obj = GTK_OBJECT (file_list); GdkPoint hotspot = { 5, 5 }; if (!drag_directory) drag_directory = load_transparent_xpm ("directory-ok.xpm"); if (!drag_directory_ok) drag_directory_ok = load_transparent_xpm ("directory.xpm"); if (!drag_multiple) drag_multiple = load_transparent_xpm ("multi.xpm"); if (!drag_multiple_ok) drag_multiple_ok = load_transparent_xpm ("multi-ok.xpm"); if (drag_directory && drag_directory_ok) gdk_dnd_set_drag_shape (drag_directory->window, &hotspot, drag_directory_ok->window, &hotspot); /* DND: Drag setup */ gtk_signal_connect (obj, "drag_request_event", GTK_SIGNAL_FUNC (panel_drag_request), panel); gtk_signal_connect (obj, "drag_begin_event", GTK_SIGNAL_FUNC (panel_drag_begin), panel); fixed_gtk_widget_dnd_drag_set (GTK_CLIST (file_list), TRUE, drag_types, ELEMENTS (drag_types)); /* DND: Drop setup */ gtk_signal_connect (obj, "drop_enter_event", GTK_SIGNAL_FUNC (panel_drop_enter), panel); gtk_signal_connect (obj, "drop_leave_event", GTK_SIGNAL_FUNC (panel_drop_enter), panel); gtk_signal_connect (obj, "drop_data_available_event", GTK_SIGNAL_FUNC (panel_drop_data_available), panel); /* Artificial way of getting drag to start without leaving the widget boundary */ gtk_signal_connect (obj, "motion_notify_event", GTK_SIGNAL_FUNC (panel_artificial_drag_start), panel); fixed_gtk_widget_dnd_drop_set (GTK_CLIST (file_list), TRUE, drop_types, ELEMENTS (drop_types), FALSE); } static GtkWidget * panel_create_file_list (WPanel *panel) { const int items = panel->format->items; format_e *format = panel->format; GtkWidget *file_list; GtkCList *clist; gchar **titles; int i; titles = g_new (char *, items); for (i = 0; i < items; format = format->next) if (format->use_in_gui) titles [i++] = format->title; file_list = gtk_clist_new_with_titles (items, titles); clist = GTK_CLIST (file_list); panel_configure_file_list (panel, file_list); free (titles); gtk_signal_connect_after (GTK_OBJECT (file_list), "size_allocate", GTK_SIGNAL_FUNC (panel_file_list_size_allocate_hook), panel); gtk_signal_connect (GTK_OBJECT (file_list), "realize", GTK_SIGNAL_FUNC (panel_realized), panel); gtk_signal_connect (GTK_OBJECT (file_list), "select_row", GTK_SIGNAL_FUNC (panel_file_list_select_row), panel); return file_list; } void panel_switch_new_display_mode (WPanel *panel) { GtkWidget *old_list = panel->list; if (!old_list) return; panel->list = panel_create_file_list (panel); gtk_widget_destroy (old_list); gtk_table_attach (GTK_TABLE (panel->table), panel->list, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0); gtk_widget_show (panel->list); panel_update_contents (panel); } static void change_cwd (GtkWidget *entry, WPanel *panel) { printf ("Cambiando a...%s\n", "xxx"); } static GtkWidget * panel_create_cwd (Dlg_head *h, WPanel *panel, GtkWidget **the_entry) { WInput *in; in = input_new (0, 0, 0, 10, "", "cwd"); add_widget (h, in); /* Force the creation of the gtk widget */ send_message_to (h, (Widget *) in, WIDGET_INIT, 0); *the_entry = gnome_entry_gtk_entry (GNOME_ENTRY (in->widget.wdata)); gtk_signal_connect (GTK_OBJECT (*the_entry), "activate", GTK_SIGNAL_FUNC (change_cwd), panel); return GTK_WIDGET (in->widget.wdata); } static void panel_change_filter (GtkWidget *entry, WPanel *panel) { char *reg_exp; reg_exp = gtk_entry_get_text (GTK_ENTRY (gnome_entry_gtk_entry (GNOME_ENTRY (entry)))); set_panel_filter_to (panel, strdup (reg_exp)); } /* FIXME: for now, this list is hardcoded. We want a way to let the user configure it. */ static struct filter_item { char *text; char *glob; } filter_items [] = { { "All files", "*" }, { "Archives and compressed files", "*.(tar|gz|tgz|taz|zip|lha|zoo|pak|sit|arc|arj|rar|huf|lzh)" }, { "RPM/DEB files", "*.(rpm|deb)" }, { "Text/Document files", "*.(txt|tex|doc|rtf)" }, { "HTML and SGML files", "*.(html|htm|sgml|sgm)" }, { "Postscript and PDF files", "*.(ps|pdf)" }, { "Spreadsheet files", "*.(xls|wks|wk1)" }, { "Image files", "*.(png|jpg|jpeg|xcf|gif|tif|tiff|xbm|xpm|pbm|pgm|ppm|tga|rgb|iff|lbm|ilbm|" "bmp|pcx|pic|pict|psd|gbr|pat|ico|fig|cgm|rle|fits)" }, { "Video/animation files", "*.(mpg|mpeg|mov|avi|fli|flc|flh|flx|dl)" }, { "Audio files", "*.(au|wav|mp3|snd|mod|s3m|ra)" }, { "C program files", "*.[ch]" }, { "C++ program files", "*.(cc|C|cpp|cxx|h|H)" }, { "Objective-C program files", "*.[mh]" }, { "Scheme program files", "*.scm" }, { "Assembler program files", "*.(s|S|asm)" }, { "Misc. program files", "*.(awk|sed|lex|l|y|sh|idl|pl|py|am|in|f|el|bas|pas|java|sl|p|m4|tcl|pov)" }, { "Font files", "*.(pfa|pfb|afm|ttf|fon|pcf|pcf.gz|spd)" } }; static GtkWidget *filter_menu; static void filter_item_select (GtkWidget *widget, gpointer data) { /* FIXME: the hintbar resizes horribly and screws the panel */ #if 0 struct filter_item *fi = gtk_object_get_user_data (GTK_OBJECT (widget)); set_hintbar (easy_patterns ? fi->glob : fi->regexp); #endif } static void filter_item_deselect (GtkWidget *widget, gpointer data) { /* set_hintbar (""); */ } static void filter_item_activate (GtkWidget *widget, gpointer data) { struct filter_item *fi = gtk_object_get_user_data (GTK_OBJECT (widget)); WPanel *panel = data; char *pattern; if (easy_patterns) pattern = g_strdup (fi->glob); else { /* This is sort of a hack to force convert_pattern() to actually convert the thing */ easy_patterns = 1; pattern = convert_pattern (fi->glob, match_file, 0); easy_patterns = 0; } set_panel_filter_to (panel, pattern); } static void build_filter_menu (WPanel *panel, GtkWidget *button) { GtkWidget *item; int i; if (filter_menu) return; /* FIXME: the filter menu is global, and it is never destroyed */ filter_menu = gtk_menu_new (); gtk_object_set_user_data (GTK_OBJECT (filter_menu), button); for (i = 0; i < ELEMENTS (filter_items); i++) { item = gtk_menu_item_new_with_label (filter_items[i].text); gtk_object_set_user_data (GTK_OBJECT (item), &filter_items[i]); gtk_signal_connect (GTK_OBJECT (item), "select", (GtkSignalFunc) filter_item_select, panel); gtk_signal_connect (GTK_OBJECT (item), "deselect", (GtkSignalFunc) filter_item_deselect, panel); gtk_signal_connect (GTK_OBJECT (item), "activate", (GtkSignalFunc) filter_item_activate, panel); gtk_widget_show (item); gtk_menu_append (GTK_MENU (filter_menu), item); } } static void position_filter_popup (GtkMenu *menu, gint *x, gint *y, gpointer data) { int screen_width, screen_height; GtkWidget *wmenu = GTK_WIDGET (menu); GtkWidget *button = GTK_WIDGET (data); /* This code is mostly ripped off from gtkmenu.c - Federico */ screen_width = gdk_screen_width (); screen_height = gdk_screen_height (); gdk_window_get_origin (button->window, x, y); *y += button->allocation.height; if ((*x + wmenu->requisition.width) > screen_width) *x -= (*x + wmenu->requisition.width) - screen_width; if ((*y + wmenu->requisition.height) > screen_height) *y -= (*y + wmenu->requisition.height) - screen_height; if (*y < 0) *y = 0; } static void show_filter_popup (GtkWidget *button, gpointer data) { WPanel *panel; panel = data; build_filter_menu (panel, button); gtk_menu_popup (GTK_MENU (filter_menu), NULL, NULL, position_filter_popup, button, 1, GDK_CURRENT_TIME); } void display_mini_info (WPanel *panel) { GtkLabel *label = GTK_LABEL (panel->ministatus); if (panel->searching){ char *str = copy_strings ("Search: ", panel->search_buffer, NULL); gtk_label_set (label, str); free (str); return; } if (panel->marked){ char buffer [120]; sprintf (buffer, " %s bytes in %d files%s", size_trunc_sep (panel->total), panel->marked, panel->marked == 1 ? "" : "s"); gtk_label_set (label, buffer); } if (S_ISLNK (panel->dir.list [panel->selected].buf.st_mode)){ char *link, link_target [MC_MAXPATHLEN]; int len; link = concat_dir_and_file (panel->cwd, panel->dir.list [panel->selected].fname); len = mc_readlink (link, link_target, MC_MAXPATHLEN); free (link); if (len > 0){ char *str; link_target [len] = 0; str = copy_strings ("-> ", link_target, NULL); gtk_label_set (label, str); free (str); } else gtk_label_set (label, ""); return; } if (panel->estimated_total){ int len = panel->estimated_total; char *buffer; buffer = xmalloc (len + 2, "display_mini_info"); format_file (buffer, panel, panel->selected, panel->estimated_total-2, 0, 1); buffer [len] = 0; gtk_label_set (label, buffer); free (buffer); } } static GtkWidget * panel_create_filter (Dlg_head *h, WPanel *panel, GtkWidget **filter_w) { GtkWidget *fhbox; GtkWidget *button; GtkWidget *arrow; GtkWidget *label; GtkWidget *ihbox; WInput *in; fhbox = gtk_hbox_new (FALSE, 0); /* Filter popup button */ button = gtk_button_new (); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) show_filter_popup, panel); GTK_WIDGET_UNSET_FLAGS (button, GTK_CAN_FOCUS); gtk_box_pack_start (GTK_BOX (fhbox), button, FALSE, FALSE, 0); gtk_widget_show (button); ihbox = gtk_hbox_new (FALSE, 3); gtk_container_add (GTK_CONTAINER (button), ihbox); gtk_widget_show (ihbox); arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT); gtk_box_pack_start (GTK_BOX (ihbox), arrow, TRUE, TRUE, 0); gtk_widget_show (arrow); label = gtk_label_new ("Filter"); gtk_box_pack_start (GTK_BOX (ihbox), label, TRUE, TRUE, 0); gtk_widget_show (label); /* Filter input line */ in = input_new (0, 0, 0, 10, "", "filter"); add_widget (h, in); /* Force the creation of the gtk widget */ send_message_to (h, (Widget *) in, WIDGET_INIT, 0); *filter_w = (GtkWidget *) in->widget.wdata; /* We do not want the focus by default (and the previos add_widget just gave it to us) */ h->current = h->current->prev; gtk_signal_connect (GTK_OBJECT (gnome_entry_gtk_entry (GNOME_ENTRY (*filter_w))), "activate", GTK_SIGNAL_FUNC (panel_change_filter), panel); gtk_box_pack_start (GTK_BOX (fhbox), *filter_w, TRUE, TRUE, 0); gtk_widget_show (*filter_w); return fhbox; } void x_create_panel (Dlg_head *h, widget_data parent, WPanel *panel) { GtkWidget *status_line, *filter, *vbox; GtkWidget *ministatus_align, *frame, *cwd; panel->table = gtk_table_new (2, 1, 0); panel->list = panel_create_file_list (panel); cwd = panel_create_cwd (h, panel, &panel->current_dir); filter = panel_create_filter (h, panel, (GtkWidget **) &panel->filter_w); /* ministatus */ ministatus_align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); panel->ministatus = gtk_label_new (""); gtk_container_add (GTK_CONTAINER (ministatus_align), panel->ministatus); status_line = gtk_hbox_new (0, 0); gtk_box_pack_start (GTK_BOX (status_line), cwd, 1, 1, 0); gtk_box_pack_end (GTK_BOX (status_line), filter, 0, 0, 0); /* The statusbar */ frame = gtk_frame_new (NULL); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); gtk_container_border_width (GTK_CONTAINER (frame), 3); panel->status = gtk_label_new (""); gtk_container_add (GTK_CONTAINER (frame), panel->status); gtk_table_attach (GTK_TABLE (panel->table), panel->list, 0, 1, 1, 2, GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0); gtk_table_attach (GTK_TABLE (panel->table), status_line, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0); gtk_table_attach (GTK_TABLE (panel->table), ministatus_align, 0, 1, 2, 3, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0); gtk_table_attach (GTK_TABLE (panel->table), frame, 0, 1, 3, 4, GTK_EXPAND | GTK_FILL, 0, 0, 0); /* Ultra nasty hack: pull the vbox from wdata */ vbox = GTK_WIDGET (panel->widget.wdata); panel->widget.wdata = (widget_data) panel->table; /* Now, insert our table in our parent */ gtk_container_add (GTK_CONTAINER (vbox), panel->table); gtk_widget_show_all (panel->table); if (!pixmaps_ready){ if (!GTK_WIDGET_REALIZED (panel->list)) gtk_widget_realize (panel->list); panel_create_pixmaps (panel->list); } /* In GNOME the panel wants to have the cursor, to avoid "auto" focusing the * filter input line */ panel->widget.options |= W_WANT_CURSOR; panel->estimated_total = 0; } void panel_update_cols (Widget *panel, int frame_size) { panel->cols = 60; panel->lines = 20; } char * get_nth_panel_name (int num) { static char buffer [20]; if (!num) return "New Left Panel"; else if (num == 1) return "New Right Panel"; else { sprintf (buffer, "%ith Panel", num); return buffer; } } void load_hint (void) { char *hint; if ((hint = get_random_hint ())){ if (*hint) set_hintbar (hint); free (hint); } else set_hintbar ("The GNOME File Manager " VERSION); } void paint_frame (WPanel *panel) { } void x_reset_sort_labels (WPanel *panel) { panel_switch_new_display_mode (panel); }