mc/gnome/gpopup.c

510 lines
13 KiB
C
Raw Normal View History

/* Popup menus for the Midnight Commander
*
* Copyright (C) 1998 The Free Software Foundation
*
* Authors: Federico Mena <federico@nuclecu.unam.mx>
* Miguel de Icaza <miguel@nuclecu.unam.mx>
*/
1998-12-01 09:23:50 +03:00
#include <config.h>
#include "util.h"
#include <gnome.h>
#include "panel.h"
#include "cmd.h"
#include "dialog.h"
#include "ext.h"
#include "gpageprop.h"
#include "gpopup.h"
#include "main.h"
#define CLIST_FROM_SW(panel_list) GTK_CLIST (GTK_BIN (panel_list)->child)
/* Flags for the popup menu entries. They specify to which kinds of files an entry is valid for. */
enum {
F_ALL = 1 << 0, /* Applies to all files */
F_REGULAR = 1 << 1, /* Applies only to regular files */
F_SYMLINK = 1 << 2, /* Applies only to symlinks */
F_SINGLE = 1 << 3, /* Applies only to a single file, not to a multiple selection */
F_NOTDIR = 1 << 4, /* Applies to non-directories */
F_DICON = 1 << 5, /* Applies to desktop icons */
F_PANEL = 1 << 6 /* Applies to files from a panel window */
};
struct action {
char *text; /* Menu item text */
int flags; /* Flags from the above enum */
GtkSignalFunc callback; /* Callback for menu item */
};
1998-12-01 09:23:50 +03:00
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);
if (!command)
return;
execute (command);
free (command);
}
static void
panel_action_open (GtkWidget *widget, WPanel *panel)
{
if (do_enter (panel))
return;
panel_action_open_with (widget, panel);
}
static void
1998-12-01 09:23:50 +03:00
panel_action_view (GtkWidget *widget, WPanel *panel)
{
view_cmd (panel);
}
static void
1998-12-01 09:23:50 +03:00
panel_action_view_unfiltered (GtkWidget *widget, WPanel *panel)
{
view_simple_cmd (panel);
}
static void
1998-12-01 09:23:50 +03:00
panel_action_edit (GtkWidget *widget, WPanel *panel)
{
edit_cmd (panel);
}
static void
1998-12-01 09:23:50 +03:00
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 (CLIST_FROM_SW (panel->list)), full_name, NULL) != 0)
reread_cmd ();
free (full_name);
}
static void
dicon_move (GtkWidget *widget, char *filename)
{
g_warning ("Implement this function!");
}
static void
dicon_copy (GtkWidget *widget, char *filename)
{
g_warning ("Implement this function!");
}
1998-12-01 09:23:50 +03:00
static void
dicon_delete (GtkWidget *widget, char *filename)
{
g_warning ("Implement this function!");
}
/* This is our custom signal connection function for popup menu items -- see below for the
* marshaller information. We pass the original callback function as the data pointer for the
* marshaller (uiinfo->moreinfo).
*/
static void
popup_connect_func (GnomeUIInfo *uiinfo, gchar *signal_name, GnomeUIBuilderData *uibdata)
{
g_assert (uibdata->is_interp);
if (uiinfo->moreinfo) {
gtk_object_set_data (GTK_OBJECT (uiinfo->widget), "popup_user_data", uiinfo->user_data);
gtk_signal_connect_full (GTK_OBJECT (uiinfo->widget), signal_name,
NULL,
uibdata->relay_func,
uiinfo->moreinfo,
uibdata->destroy_func,
FALSE,
FALSE);
}
}
/* Our custom marshaller for menu items. We need it so that it can extract the per-attachment
* user_data pointer from the parent menu shell and pass it to the callback. This overrides the
* user-specified data from the GnomeUIInfo structures.
*/
typedef void (* ActivateFunc) (GtkObject *object, gpointer data);
static void
popup_marshal_func (GtkObject *object, gpointer data, guint n_args, GtkArg *args)
{
ActivateFunc func;
gpointer user_data;
func = (ActivateFunc) data;
user_data = gtk_object_get_data (object, "popup_user_data");
gtk_object_set_data (GTK_OBJECT (GTK_WIDGET (object)->parent), "popup_active_item", object);
(* func) (object, user_data);
}
/* Fills the menu with the specified uiinfo at the specified position, using our magic marshallers
* to be able to fetch the active item. The code is shamelessly ripped from gnome-popup-menu.
*/
static void
fill_menu (GtkMenuShell *menu_shell, GnomeUIInfo *uiinfo, int pos)
{
GnomeUIBuilderData uibdata;
/* We use our own callback marshaller so that it can fetch the popup user data
* from the popup menu and pass it on to the user-defined callbacks.
*/
uibdata.connect_func = popup_connect_func;
uibdata.data = NULL;
uibdata.is_interp = TRUE;
uibdata.relay_func = popup_marshal_func;
uibdata.destroy_func = NULL;
gnome_app_fill_menu_custom (menu_shell, uiinfo, &uibdata, NULL, FALSE, pos);
}
/* The context menu: text displayed, condition that must be met and the routine that gets invoked
* upon activation.
1998-12-01 09:23:50 +03:00
*/
static struct action file_actions[] = {
{ N_("Properties"), F_SINGLE | F_PANEL, (GtkSignalFunc) panel_action_properties },
1998-12-01 09:23:50 +03:00
#if 0
{ N_("Properties"), F_SINGLE | F_DICON, (GtkSignalFunc) desktop_icon_properties },
1998-12-01 09:23:50 +03:00
#endif
{ "", F_SINGLE, NULL },
{ N_("Open"), F_PANEL | F_ALL, (GtkSignalFunc) panel_action_open },
1998-12-01 09:23:50 +03:00
#if 0
{ N_("Open"), F_DICON | F_ALL, (GtkSignalFunc) desktop_icon_execute },
1998-12-01 09:23:50 +03:00
#endif
{ N_("Open with"), F_PANEL | F_ALL, (GtkSignalFunc) panel_action_open_with },
{ N_("View"), F_PANEL | F_NOTDIR, (GtkSignalFunc) panel_action_view },
{ N_("View unfiltered"), F_PANEL | F_NOTDIR, (GtkSignalFunc) panel_action_view_unfiltered },
{ N_("Edit"), F_PANEL | F_NOTDIR, (GtkSignalFunc) panel_action_edit },
1998-12-01 09:23:50 +03:00
{ "", 0, NULL },
{ N_("Link..."), F_PANEL | F_REGULAR | F_SINGLE, (GtkSignalFunc) link_cmd },
{ N_("Symlink..."), F_PANEL | F_SINGLE, (GtkSignalFunc) symlink_cmd },
{ N_("Edit symlink..."), F_PANEL | F_SYMLINK, (GtkSignalFunc) edit_symlink_cmd },
1998-12-01 09:23:50 +03:00
{ NULL, 0, NULL },
};
/* Menu entries for files from a panel window */
static GnomeUIInfo panel_actions[] = {
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("Move/rename..."), NULL, ren_cmd),
GNOMEUIINFO_ITEM_NONE (N_("Copy..."), NULL, copy_cmd),
GNOMEUIINFO_ITEM_NONE (N_("Delete"), NULL, delete_cmd),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_END
1998-12-01 09:23:50 +03:00
};
/* Menu entries for files from desktop icons */
static GnomeUIInfo desktop_icon_actions[] = {
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_ITEM_NONE (N_("Move/rename..."), NULL, dicon_move),
GNOMEUIINFO_ITEM_NONE (N_("Copy..."), NULL, dicon_copy),
GNOMEUIINFO_ITEM_NONE (N_("Delete"), NULL, dicon_delete),
GNOMEUIINFO_SEPARATOR,
GNOMEUIINFO_END
1998-12-01 09:23:50 +03:00
};
/* Creates the menu items for the standard actions. Returns the position at which additional menu
* items should be inserted.
*/
static int
create_actions (GtkWidget *menu, WPanel *panel, int panel_row, char *filename)
1998-12-01 09:23:50 +03:00
{
gpointer closure;
struct action *action;
int pos;
GnomeUIInfo uiinfo[] = {
{ 0 },
GNOMEUIINFO_END
};
1998-12-01 09:23:50 +03:00
closure = panel ? (gpointer) panel : (gpointer) filename;
1998-12-01 09:23:50 +03:00
pos = 0;
for (action = file_actions; action->text; action++) {
1998-12-01 09:23:50 +03:00
/* First, try F_PANEL and F_DICON flags */
if (!panel && (action->flags & F_PANEL))
1998-12-01 09:23:50 +03:00
continue;
if (panel && (action->flags & F_DICON))
1998-12-01 09:23:50 +03:00
continue;
1998-12-01 09:23:50 +03:00
/* Items with F_ALL bypass any other condition */
if (!(action->flags & F_ALL)) {
1998-12-01 09:23:50 +03:00
/* Items with F_SINGLE require that ONLY ONE marked files exist */
if (panel && (action->flags & F_SINGLE))
1998-12-01 09:23:50 +03:00
if (panel->marked > 1)
continue;
/* Items with F_NOTDIR requiere that the selection is not a directory */
if (panel && (action->flags & F_NOTDIR)) {
struct stat *s = &panel->dir.list[panel_row].buf;
if (panel->dir.list [panel_row].f.link_to_dir)
1998-12-01 09:23:50 +03:00
continue;
1998-12-01 09:23:50 +03:00
if (S_ISDIR (s->st_mode))
continue;
}
1998-12-01 09:23:50 +03:00
/* Items with F_REGULAR do not accept any strange file types */
if (panel && (action->flags & F_REGULAR)) {
struct stat *s = &panel->dir.list[panel_row].buf;
if (S_ISLNK (s->st_mode) && panel->dir.list[panel_row].f.link_to_dir)
1998-12-01 09:23:50 +03:00
continue;
1998-12-01 09:23:50 +03:00
if (S_ISSOCK (s->st_mode) || S_ISCHR (s->st_mode) ||
S_ISFIFO (s->st_mode) || S_ISBLK (s->st_mode))
continue;
}
/* Items with F_SYMLINK only operate on symbolic links */
if (panel && action->flags & F_SYMLINK) {
struct stat *s = &panel->dir.list[panel_row].buf;
if (!S_ISLNK (s->st_mode))
1998-12-01 09:23:50 +03:00
continue;
}
}
/* Create the appropriate GnomeUIInfo structure for the menu item */
if (action->text[0]) {
uiinfo[0].type = GNOME_APP_UI_ITEM;
uiinfo[0].label = _(action->text);
uiinfo[0].hint = NULL;
uiinfo[0].moreinfo = action->callback;
uiinfo[0].user_data = closure;
uiinfo[0].unused_data = NULL;
uiinfo[0].pixmap_type = GNOME_APP_PIXMAP_NONE;
uiinfo[0].pixmap_info = NULL;
uiinfo[0].accelerator_key = 0;
uiinfo[0].ac_mods = 0;
uiinfo[0].widget = NULL;
} else
uiinfo[0].type = GNOME_APP_UI_SEPARATOR;
fill_menu (GTK_MENU_SHELL (menu), uiinfo, pos);
pos++;
1998-12-01 09:23:50 +03:00
}
return pos;
1998-12-01 09:23:50 +03:00
}
/* These functions activate a menu popup action for a filename in a hackish way, as they have to
* fetch the action from the menu item's label.
1998-12-01 09:23:50 +03:00
*/
static char *
get_label_text (GtkMenuItem *item)
{
GtkWidget *label;
/* THIS DEPENDS ON THE LATEST GNOME-LIBS! Keep this in sync with gnome-app-helper until we
* figure a way to do this in a non-hackish way.
*/
label = GTK_BIN (item)->child;
g_assert (label != NULL);
return GTK_LABEL (label)->label;
}
1998-12-01 09:23:50 +03:00
static void
regex_command_from_panel (GtkMenuItem *item, WPanel *panel)
1998-12-01 09:23:50 +03:00
{
char *filename;
1998-12-01 09:23:50 +03:00
char *action;
int movedir;
1998-12-16 05:32:31 +03:00
/* This is broken */
filename = panel->dir.list[panel->selected].fname;
action = get_label_text (item);
1998-12-01 09:23:50 +03:00
regex_command (filename, action, NULL, &movedir);
}
static void
regex_command_from_desktop_icon (GtkMenuItem *item, char *filename)
1998-12-01 09:23:50 +03:00
{
char *action;
int movedir;
action = get_label_text (item);
1998-12-01 09:23:50 +03:00
regex_command (filename, action, NULL, &movedir);
}
/* Create the menu items common to files from panel window and desktop icons, and also the items for
* regexp_command() actions.
*/
1998-12-01 09:23:50 +03:00
static void
create_regexp_actions (GtkWidget *menu, WPanel *panel, int panel_row, char *filename, int insert_pos)
1998-12-01 09:23:50 +03:00
{
gpointer closure, regex_closure;
GnomeUIInfo *a_uiinfo;
int i;
GtkSignalFunc regex_callback;
1998-12-01 09:23:50 +03:00
char *p, *q;
int c;
GnomeUIInfo uiinfo[] = {
{ 0 },
GNOMEUIINFO_END
};
if (panel) {
a_uiinfo = panel_actions;
closure = panel;
1998-12-16 05:32:31 +03:00
regex_callback = regex_command_from_desktop_icon;
regex_closure = filename;
1998-12-01 09:23:50 +03:00
} else {
a_uiinfo = desktop_icon_actions;
closure = filename;
regex_callback = regex_command_from_desktop_icon;
regex_closure = filename;
1998-12-01 09:23:50 +03:00
}
/* Fill in the common part of the menus */
for (i = 0; i < 5; i++)
a_uiinfo[i].user_data = closure;
fill_menu (GTK_MENU_SHELL (menu), a_uiinfo, insert_pos);
insert_pos += 5; /* the number of items from the common menus */
/* Fill in the regex command part */
1998-12-01 09:23:50 +03:00
p = regex_command (filename, NULL, NULL, NULL);
if (!p)
return;
for (;;) {
/* Strip off an entry */
1998-12-01 09:23:50 +03:00
while (*p == ' ' || *p == '\t')
p++;
1998-12-01 09:23:50 +03:00
if (!*p)
break;
1998-12-01 09:23:50 +03:00
q = p;
while (*q && *q != '=' && *q != '\t')
q++;
1998-12-01 09:23:50 +03:00
c = *q;
*q = 0;
/* Create the item for that entry */
uiinfo[0].type = GNOME_APP_UI_ITEM;
uiinfo[0].label = p;
uiinfo[0].hint = NULL;
uiinfo[0].moreinfo = regex_callback;
uiinfo[0].user_data = regex_closure;
uiinfo[0].unused_data = NULL;
uiinfo[0].pixmap_type = GNOME_APP_PIXMAP_NONE;
uiinfo[0].pixmap_info = NULL;
uiinfo[0].accelerator_key = 0;
uiinfo[0].ac_mods = 0;
uiinfo[0].widget = NULL;
fill_menu (GTK_MENU_SHELL (menu), uiinfo, insert_pos++);
/* Next! */
1998-12-01 09:23:50 +03:00
if (!c)
break;
1998-12-01 09:23:50 +03:00
p = q + 1;
}
}
/* Convenience callback to exit the main loop of a modal popup menu when it is deactivated*/
static void
menu_shell_deactivated (GtkMenuShell *menu_shell, gpointer data)
{
gtk_main_quit ();
}
/* Returns the index of the active item in the specified menu, or -1 if none is active */
static int
get_active_index (GtkMenu *menu)
{
GList *l;
GtkWidget *active;
int i;
active = gtk_object_get_data (GTK_OBJECT (menu), "popup_active_item");
for (i = 0, l = GTK_MENU_SHELL (menu)->children; l; l = l->next, i++)
if (active == l->data)
return i;
return -1;
}
1998-12-01 09:23:50 +03:00
/*
* Create a context menu
* It can take either a WPanel or a GnomeDesktopEntry. One of them should
* be set to NULL.
*/
int
gpopup_do_popup (GdkEventButton *event, WPanel *from_panel, int panel_row, char *filename)
1998-12-01 09:23:50 +03:00
{
GtkWidget *menu;
int pos;
guint id;
1998-12-01 09:23:50 +03:00
g_return_val_if_fail (event != NULL, -1);
g_return_val_if_fail (from_panel != NULL || filename != NULL, -1);
1998-12-01 09:23:50 +03:00
menu = gtk_menu_new ();
1998-12-01 09:23:50 +03:00
/* Connect to the deactivation signal to be able to quit our modal main loop */
1998-12-01 09:23:50 +03:00
id = gtk_signal_connect (GTK_OBJECT (menu), "deactivate",
(GtkSignalFunc) menu_shell_deactivated,
NULL);
1998-12-01 09:23:50 +03:00
/* Fill the menu */
1998-12-01 09:23:50 +03:00
pos = create_actions (menu, from_panel, panel_row, filename);
create_regexp_actions (menu, from_panel, panel_row, filename, pos);
1998-12-01 09:23:50 +03:00
/* Run it */
1998-12-01 09:23:50 +03:00
gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, event->time);
gtk_grab_add (menu);
gtk_main ();
gtk_grab_remove (menu);
1998-12-01 09:23:50 +03:00
gtk_signal_disconnect (GTK_OBJECT (menu), id);
1998-12-01 09:23:50 +03:00
return get_active_index (GTK_MENU (menu));
}
1998-12-16 05:32:31 +03:00