mirror of https://github.com/MidnightCommander/mc
1998-12-11 Federico Mena Quintero <federico@nuclecu.unam.mx>
Wheeeee! Desktop icons can be selected by rubberbanding. You need Englightenment to do this, as so far it seems to be the only WM with support for the _WIN_DESKTOP_BUTTON_PROXY property. * gdesktop.c (click_proxy_filter): New event filter for button presses and releases on the root window. (click_proxy_button_press): New function to handle button presses on the desktop. (click_proxy_button_release): New function to handle button releases on the desktop. (click_proxy_motion): New function to handle rubberbanding motion on the desktop. (setup_xdnd_proxy): Flush the server after ungrabbing it. (find_click_proxy_window): Flush the server after ungrabbing it. (store_temp_selection): Stores the temporary selection to the master selection, or viceversa. This is needed for dynamic updating while the user rubberbands. (update_drag_selection): New function to update the selection while the user is rubberbanding on the desktop. (icon_is_in_area): New function to see if an icon is inside the rubberbanding area. * gdesktop-icon.h: Added fields for querying the icon and text position and size. * gdesktop-icon.c (desktop_icon_reshape): Set the position and size fields as computed for layout.
This commit is contained in:
parent
3c8529fac4
commit
741d37e4eb
|
@ -1,3 +1,33 @@
|
|||
1998-12-11 Federico Mena Quintero <federico@nuclecu.unam.mx>
|
||||
|
||||
Wheeeee! Desktop icons can be selected by rubberbanding. You
|
||||
need Englightenment to do this, as so far it seems to be the only
|
||||
WM with support for the _WIN_DESKTOP_BUTTON_PROXY property.
|
||||
|
||||
* gdesktop.c (click_proxy_filter): New event filter for button
|
||||
presses and releases on the root window.
|
||||
(click_proxy_button_press): New function to handle button presses
|
||||
on the desktop.
|
||||
(click_proxy_button_release): New function to handle button
|
||||
releases on the desktop.
|
||||
(click_proxy_motion): New function to handle rubberbanding motion
|
||||
on the desktop.
|
||||
(setup_xdnd_proxy): Flush the server after ungrabbing it.
|
||||
(find_click_proxy_window): Flush the server after ungrabbing it.
|
||||
(store_temp_selection): Stores the temporary selection to the
|
||||
master selection, or viceversa. This is needed for dynamic
|
||||
updating while the user rubberbands.
|
||||
(update_drag_selection): New function to update the selection
|
||||
while the user is rubberbanding on the desktop.
|
||||
(icon_is_in_area): New function to see if an icon is inside the
|
||||
rubberbanding area.
|
||||
|
||||
* gdesktop-icon.h: Added fields for querying the icon and text
|
||||
position and size.
|
||||
|
||||
* gdesktop-icon.c (desktop_icon_reshape): Set the position and
|
||||
size fields as computed for layout.
|
||||
|
||||
1998-12-11 Owen Taylor <otaylor@redhat.com>
|
||||
|
||||
* gdesktop.c (editing_started): Grab on the window for the
|
||||
|
|
|
@ -407,6 +407,16 @@ desktop_icon_reshape (DesktopIcon *dicon)
|
|||
|
||||
gtk_widget_set_usize (GTK_WIDGET (dicon), dicon->width, dicon->height);
|
||||
create_window_shape (dicon, icon_width, icon_height, text_width, text_height);
|
||||
|
||||
dicon->icon_x = (int) ((dicon->width - icon_width) / 2.0 + 0.5);
|
||||
dicon->icon_y = 0;
|
||||
dicon->icon_w = icon_width;
|
||||
dicon->icon_h = icon_height;
|
||||
|
||||
dicon->text_x = x1;
|
||||
dicon->text_y = y1;
|
||||
dicon->text_w = text_width;
|
||||
dicon->text_h = text_height;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,6 +40,11 @@ struct _DesktopIcon {
|
|||
|
||||
int width, height; /* Total size of the window */
|
||||
|
||||
int icon_x, icon_y; /* Icon offsets */
|
||||
int icon_w, icon_h; /* Icon size */
|
||||
int text_x, text_y; /* Text offsets */
|
||||
int text_w, text_h; /* Text size */
|
||||
|
||||
int w_changed_id; /* Signal connection ID for "width_changed" from the icon text item */
|
||||
int h_changed_id; /* Signal connection ID for "height_changed" from the icon text item */
|
||||
};
|
||||
|
|
341
gnome/gdesktop.c
341
gnome/gdesktop.c
|
@ -50,14 +50,15 @@
|
|||
|
||||
/* This structure defines the information carried by a desktop icon */
|
||||
struct desktop_icon_info {
|
||||
GtkWidget *dicon; /* The desktop icon widget */
|
||||
int x, y; /* Position in the desktop */
|
||||
int slot; /* Index of the slot the icon is in, or -1 for none */
|
||||
char *filename; /* The file this icon refers to (relative to the desktop_directory) */
|
||||
int selected : 1; /* Is the icon selected? */
|
||||
int finishing_selection : 1; /* Flag set while we are releasing button
|
||||
* after selecting in the text
|
||||
*/
|
||||
GtkWidget *dicon; /* The desktop icon widget */
|
||||
int x, y; /* Position in the desktop */
|
||||
int slot; /* Index of the slot the icon is in, or -1 for none */
|
||||
char *filename; /* The file this icon refers to (relative to the desktop_directory) */
|
||||
int selected : 1; /* Is the icon selected? */
|
||||
int tmp_selected : 1; /* Temp storage for original selection while rubberbanding */
|
||||
int finishing_selection : 1; /* Flag set while we are releasing
|
||||
* button after selecting in the text
|
||||
*/
|
||||
};
|
||||
|
||||
struct layout_slot {
|
||||
|
@ -124,6 +125,20 @@ static int dnd_select_icon_pending;
|
|||
static GdkWindow *click_proxy_gdk_window;
|
||||
static GtkWidget *click_proxy_invisible;
|
||||
|
||||
/* GC for drawing the rubberband rectangle */
|
||||
static GdkGC *click_gc;
|
||||
|
||||
/* Starting click position and event state for rubberbanding on the desktop */
|
||||
static int click_start_x;
|
||||
static int click_start_y;
|
||||
static int click_start_state;
|
||||
|
||||
/* Current mouse position for rubberbanding on the desktop */
|
||||
static int click_current_x;
|
||||
static int click_current_y;
|
||||
|
||||
static int click_dragging;
|
||||
|
||||
|
||||
static struct desktop_icon_info *desktop_icon_info_new (char *filename, int auto_pos, int xpos, int ypos);
|
||||
static void desktop_icon_info_free (struct desktop_icon_info *dii);
|
||||
|
@ -971,8 +986,6 @@ icon_drag_data_received (GtkWidget *widget, GdkDragContext *context, gint x, gin
|
|||
char *filename;
|
||||
file_entry *fe;
|
||||
|
||||
printf ("Here!\n");
|
||||
|
||||
dii = user_data;
|
||||
filename = g_concat_dir_and_file (desktop_directory, dii->filename);
|
||||
|
||||
|
@ -1234,6 +1247,7 @@ setup_xdnd_proxy (guint32 xid, GdkWindow *proxy_window)
|
|||
gdk_error_warnings = old_warnings;
|
||||
|
||||
XUngrabServer (GDK_DISPLAY ());
|
||||
gdk_flush ();
|
||||
|
||||
if (!proxy) {
|
||||
/* Mark our window as a valid proxy window with a XdndProxy
|
||||
|
@ -1446,6 +1460,7 @@ find_click_proxy_window (void)
|
|||
gdk_error_warnings = old_warnings;
|
||||
|
||||
XUngrabServer (GDK_DISPLAY ());
|
||||
gdk_flush ();
|
||||
|
||||
if (proxy)
|
||||
proxy_gdk_window = gdk_window_foreign_new (proxy);
|
||||
|
@ -1455,18 +1470,277 @@ find_click_proxy_window (void)
|
|||
return proxy_gdk_window;
|
||||
}
|
||||
|
||||
/* Handles events on the root window via the click_proxy_gdk_window */
|
||||
static gint
|
||||
click_proxy_event (GtkWidget *widget, GdkEvent *event, gpointer data)
|
||||
/* Executes the popup menu for the desktop */
|
||||
static void
|
||||
desktop_popup (GdkEventButton *event)
|
||||
{
|
||||
printf ("Click proxy event %d\n", event->type);
|
||||
printf ("FIXME: display desktop popup menu\n");
|
||||
}
|
||||
|
||||
/* Draws the rubberband rectangle for selecting icons on the desktop */
|
||||
static void
|
||||
draw_rubberband (int x, int y)
|
||||
{
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
if (click_start_x < x) {
|
||||
x1 = click_start_x;
|
||||
x2 = x;
|
||||
} else {
|
||||
x1 = x;
|
||||
x2 = click_start_x;
|
||||
}
|
||||
|
||||
if (click_start_y < y) {
|
||||
y1 = click_start_y;
|
||||
y2 = y;
|
||||
} else {
|
||||
y1 = y;
|
||||
y2 = click_start_y;
|
||||
}
|
||||
|
||||
gdk_draw_rectangle (GDK_ROOT_PARENT (), click_gc, FALSE, x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
/* Stores dii->selected into dii->tmp_selected to keep the original selection
|
||||
* around while the user is rubberbanding.
|
||||
*/
|
||||
static void
|
||||
store_temp_selection (void)
|
||||
{
|
||||
int i;
|
||||
GList *l;
|
||||
struct desktop_icon_info *dii;
|
||||
|
||||
for (i = 0; i < (layout_cols * layout_rows); i++)
|
||||
for (l = layout_slots[i].icons; l; l = l->next) {
|
||||
dii = l->data;
|
||||
|
||||
dii->tmp_selected = dii->selected;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns TRUE if the specified icon is at least partially inside the specified
|
||||
* area, or FALSE otherwise.
|
||||
*/
|
||||
static int
|
||||
icon_is_in_area (struct desktop_icon_info *dii, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
DesktopIcon *dicon;
|
||||
|
||||
dicon = DESKTOP_ICON (dii->dicon);
|
||||
|
||||
/* FIXME: this only intersects the rectangle with the icon image's
|
||||
* bounds. Doing the "hard" intersection with the actual shape of the
|
||||
* image is left as an exercise to the reader.
|
||||
*/
|
||||
|
||||
x1 -= dii->x;
|
||||
y1 -= dii->y;
|
||||
x2 -= dii->x;
|
||||
y2 -= dii->y;
|
||||
|
||||
if (x1 < dicon->icon_x + dicon->icon_w - 1
|
||||
&& x2 > dicon->icon_x
|
||||
&& y1 < dicon->icon_y + dicon->icon_h - 1
|
||||
&& y2 > dicon->icon_y)
|
||||
return TRUE;
|
||||
|
||||
if (x1 < dicon->text_x + dicon->text_w - 1
|
||||
&& x2 > dicon->text_x
|
||||
&& y1 < dicon->text_y + dicon->text_h - 1
|
||||
&& y2 > dicon->text_y)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Update the selection being rubberbanded. It selects or unselects the icons
|
||||
* as appropriate.
|
||||
*/
|
||||
static void
|
||||
update_drag_selection (int x, int y)
|
||||
{
|
||||
int x1, y1, x2, y2;
|
||||
int i;
|
||||
GList *l;
|
||||
struct desktop_icon_info *dii;
|
||||
int additive, invert, in_area;
|
||||
|
||||
if (click_start_x < x) {
|
||||
x1 = click_start_x;
|
||||
x2 = x;
|
||||
} else {
|
||||
x1 = x;
|
||||
x2 = click_start_x;
|
||||
}
|
||||
|
||||
if (click_start_y < y) {
|
||||
y1 = click_start_y;
|
||||
y2 = y;
|
||||
} else {
|
||||
y1 = y;
|
||||
y2 = click_start_y;
|
||||
}
|
||||
|
||||
/* Select or unselect icons as appropriate */
|
||||
|
||||
additive = click_start_state & GDK_SHIFT_MASK;
|
||||
invert = click_start_state & GDK_CONTROL_MASK;
|
||||
|
||||
for (i = 0; i < (layout_cols * layout_rows); i++)
|
||||
for (l = layout_slots[i].icons; l; l = l->next) {
|
||||
dii = l->data;
|
||||
|
||||
in_area = icon_is_in_area (dii, x1, y1, x2, y2);
|
||||
|
||||
if (in_area) {
|
||||
if (invert) {
|
||||
if (dii->selected == dii->tmp_selected) {
|
||||
desktop_icon_select (DESKTOP_ICON (dii->dicon), !dii->selected);
|
||||
dii->selected = !dii->selected;
|
||||
}
|
||||
} else if (additive) {
|
||||
if (!dii->selected) {
|
||||
desktop_icon_select (DESKTOP_ICON (dii->dicon), TRUE);
|
||||
dii->selected = TRUE;
|
||||
}
|
||||
} else {
|
||||
if (!dii->selected) {
|
||||
desktop_icon_select (DESKTOP_ICON (dii->dicon), TRUE);
|
||||
dii->selected = TRUE;
|
||||
}
|
||||
}
|
||||
} else if (dii->selected != dii->tmp_selected) {
|
||||
desktop_icon_select (DESKTOP_ICON (dii->dicon), dii->tmp_selected);
|
||||
dii->selected = dii->tmp_selected;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Handles button presses on the root window via the click_proxy_gdk_window */
|
||||
static gint
|
||||
click_proxy_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
|
||||
{
|
||||
if (event->button == 1) {
|
||||
click_start_x = event->x;
|
||||
click_start_y = event->y;
|
||||
click_start_state = event->state;
|
||||
|
||||
XGrabServer (GDK_DISPLAY ());
|
||||
|
||||
gdk_pointer_grab (GDK_ROOT_PARENT (),
|
||||
FALSE,
|
||||
GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
|
||||
NULL,
|
||||
NULL,
|
||||
event->time);
|
||||
|
||||
/* If no modifiers are pressed, we unselect all the icons */
|
||||
|
||||
if ((click_start_state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == 0)
|
||||
unselect_all (NULL);
|
||||
|
||||
store_temp_selection (); /* Save the original selection */
|
||||
|
||||
draw_rubberband (event->x, event->y);
|
||||
click_current_x = event->x;
|
||||
click_current_y = event->y;
|
||||
click_dragging = TRUE;
|
||||
|
||||
return TRUE;
|
||||
} else if (event->button == 3) {
|
||||
desktop_popup (event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Handles button releases on the root window via the click_proxy_gdk_window */
|
||||
static gint
|
||||
click_proxy_button_release (GtkWidget *widget, GdkEventButton *event, gpointer data)
|
||||
{
|
||||
if (!click_dragging || event->button != 1)
|
||||
return FALSE;
|
||||
|
||||
draw_rubberband (click_current_x, click_current_y);
|
||||
gdk_pointer_ungrab (event->time);
|
||||
click_dragging = FALSE;
|
||||
|
||||
update_drag_selection (event->x, event->y);
|
||||
|
||||
XUngrabServer (GDK_DISPLAY ());
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Handles motion events when dragging the icon-selection rubberband on the desktop */
|
||||
static gint
|
||||
click_proxy_motion (GtkWidget *widget, GdkEventMotion *event, gpointer data)
|
||||
{
|
||||
if (!click_dragging)
|
||||
return FALSE;
|
||||
|
||||
draw_rubberband (click_current_x, click_current_y);
|
||||
draw_rubberband (event->x, event->y);
|
||||
update_drag_selection (event->x, event->y);
|
||||
click_current_x = event->x;
|
||||
click_current_y = event->y;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Filter that translates proxied events from virtual root windows into normal
|
||||
* Gdk events for the click_proxy_invisible widget.
|
||||
*/
|
||||
static GdkFilterReturn
|
||||
click_proxy_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data)
|
||||
{
|
||||
XEvent *xev;
|
||||
|
||||
xev = xevent;
|
||||
|
||||
switch (xev->type) {
|
||||
case ButtonPress:
|
||||
case ButtonRelease:
|
||||
if (xev->type == ButtonPress)
|
||||
event->button.type = GDK_BUTTON_PRESS;
|
||||
else
|
||||
event->button.type = GDK_BUTTON_RELEASE;
|
||||
|
||||
gdk_window_ref (click_proxy_gdk_window);
|
||||
|
||||
event->button.window = click_proxy_gdk_window;
|
||||
event->button.send_event = xev->xbutton.send_event;
|
||||
event->button.time = xev->xbutton.time;
|
||||
event->button.x = xev->xbutton.x;
|
||||
event->button.y = xev->xbutton.y;
|
||||
event->button.state = xev->xbutton.state;
|
||||
event->button.button = xev->xbutton.button;
|
||||
break;
|
||||
|
||||
default:
|
||||
return GDK_FILTER_CONTINUE;
|
||||
}
|
||||
|
||||
return GDK_FILTER_TRANSLATE;
|
||||
}
|
||||
|
||||
#define gray50_width 2
|
||||
#define gray50_height 2
|
||||
static char gray50_bits[] = {
|
||||
0x02, 0x01, };
|
||||
|
||||
/* Sets up the window manager proxy window to receive clicks on the desktop root window */
|
||||
static void
|
||||
setup_desktop_clicks (void)
|
||||
{
|
||||
GdkColormap *cmap;
|
||||
GdkColor color;
|
||||
GdkBitmap *stipple;
|
||||
|
||||
click_proxy_gdk_window = find_click_proxy_window ();
|
||||
if (!click_proxy_gdk_window) {
|
||||
g_warning ("Root window clicks will not work as no GNOME-compliant window manager could be found!");
|
||||
|
@ -1475,15 +1749,48 @@ setup_desktop_clicks (void)
|
|||
|
||||
click_proxy_invisible = gtk_invisible_new ();
|
||||
gtk_widget_show (click_proxy_invisible);
|
||||
gdk_window_set_user_data (click_proxy_gdk_window, click_proxy_invisible); /* make it send events to us */
|
||||
|
||||
/* Make the proxy and the root windows send events to the invisible proxy widget */
|
||||
|
||||
gdk_window_set_user_data (click_proxy_gdk_window, click_proxy_invisible);
|
||||
gdk_window_set_user_data (GDK_ROOT_PARENT (), click_proxy_invisible);
|
||||
|
||||
/* Add our filter to translate virtual root window events into Gdk events */
|
||||
|
||||
gdk_window_add_filter (GDK_ROOT_PARENT (), click_proxy_filter, NULL);
|
||||
|
||||
/* The proxy window for clicks sends us events as SubstructureNotify things */
|
||||
|
||||
XSelectInput (GDK_DISPLAY (), GDK_WINDOW_XWINDOW (click_proxy_gdk_window), SubstructureNotifyMask);
|
||||
|
||||
gtk_signal_connect (GTK_OBJECT (click_proxy_invisible), "event",
|
||||
(GtkSignalFunc) click_proxy_event,
|
||||
gtk_signal_connect (GTK_OBJECT (click_proxy_invisible), "button_press_event",
|
||||
(GtkSignalFunc) click_proxy_button_press,
|
||||
NULL);
|
||||
gtk_signal_connect (GTK_OBJECT (click_proxy_invisible), "button_release_event",
|
||||
(GtkSignalFunc) click_proxy_button_release,
|
||||
NULL);
|
||||
gtk_signal_connect (GTK_OBJECT (click_proxy_invisible), "motion_notify_event",
|
||||
(GtkSignalFunc) click_proxy_motion,
|
||||
NULL);
|
||||
|
||||
/* Create the GC to paint the rubberband rectangle */
|
||||
|
||||
click_gc = gdk_gc_new (GDK_ROOT_PARENT ());
|
||||
|
||||
cmap = gdk_window_get_colormap (GDK_ROOT_PARENT ());
|
||||
|
||||
gdk_color_white (cmap, &color);
|
||||
if (color.pixel == 0)
|
||||
gdk_color_black (cmap, &color);
|
||||
|
||||
gdk_gc_set_foreground (click_gc, &color);
|
||||
gdk_gc_set_function (click_gc, GDK_XOR);
|
||||
|
||||
gdk_gc_set_fill (click_gc, GDK_STIPPLED);
|
||||
|
||||
stipple = gdk_bitmap_create_from_data (NULL, gray50_bits, gray50_width, gray50_height);
|
||||
gdk_gc_set_stipple (click_gc, stipple);
|
||||
gdk_bitmap_unref (stipple);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue