Drag and drop is dead, long live drab and throw :)



The idea:

Drag and drop is the best thing that ever happenned to GUIs. But, it's often
tedious and awkward due to the small dropsites and the inaccuracies of the
average mouse. I was watching a friend sorting 800 files in the Win98
explorer last night, he talked about 'throwing' this file and that file into
folders. So, why not extend the concept of drag and drop to include throwing.

What is this:

This is the code for a very simple mock up of the general idea. The buttons
at the top are draggable (using gtk's XDND code), these are thrown down the
screen at the two black boxes in the corners which could represent anything
from a script to a folder.

I think that this idea would bring needed functionality to gnome and
gtk. I would be happy to help code such an idea (but I will need help)
if others are interested.

-- 
(o_   | Toby Jaffey : www.nott.ac.uk/~psystrj/
//\   | "You're bound to be unhappy if you optimize everything." -- 
V_/_  | Donald E. Knuth                                             
/*
Thu Jun 17 01:17:37 BST 1999
This code is Copyright Toby Jaffey 1999 and released under the GNU Public License.

I wrote this in about an hour to aid communication of an idea. It is
hurrendous code, with sections grabbed from other things, I am amazed that
it actually works.

The idea:

Drag and drop is the best thing that ever happenned to GUIs. But, it's often
tedious and awkward due to the small dropsites and the inaccuracies of the
average mouse. I was watching a friend sorting 800 files in the Win98
explorer last night, he talked about 'throwing' this file and that file into
folders. So, why not extend the concept of drag and drop to include throwing.

What is this:

This is the code for a very simple mock up of the general idea. The buttons
at the top are draggable (using gtk's XDND code), these are thrown down the
screen at the two black boxes in the corners which could represent anything
from a script to a folder.

What's wrong with it:

I know *nothing* (of any use) about XDND. As you can tell from the following
code, I also have very little understanding of the gtk DND API. To pick up
on aborted drags I have made the background of the window ('drawbox') into a
dropsite. It then continues the line on which the data was thrown until it
hits the screen edge. In reality, it should continue across the screen until
it hits a screen boundary or a potential drop site.

Applications:

File browsers - Users could throw items from a regular filetree widget into
boxes that represent filesystem actions.

Sorting bookmarks - I am forever meaning to sort my NS bookmarks into
folders, but, it's too fiddly.

Possible ideas for way ahead in the future:

Have the flying data bounce off screen boundaries, so that a skilled user
can set up more drop sites (if they have the skill to avoid others and
cunningly bounce data around the screen).
*/

/* simple Makefile for this */

/*
CC = gcc -Wall -O6 -g

SRCS = grabnthrow.c
OBJS = $(SRCS:.c=.o)

all: dothis

.c.o:
	$(CC) `gtk-config --cflags` -c $*.c -o $*.o

dothis: $(OBJS)
	$(CC) `gtk-config --libs` $(OBJS)  -o $@

clean: 
	rm -f *~ *.o grabnthrow
*/


#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

enum
{
TARGET_URL,
TARGET_NETSCAPE_URL
};

static GtkTargetEntry contact_list_drop_types[]=
	{
	{ "text/plain",0,TARGET_NETSCAPE_URL }
	};

int indrag=0;
int startx,starty,endx,endy;

GtkWidget *drawbox;

void calcEndpoint(gdouble xsize,gdouble  ysize,gdouble sx,gdouble  sy,gdouble  endx,gdouble  endy,int *realendx,int  *realendy);

void draw_line (GtkWidget *widget, gdouble x1, gdouble y1, gdouble x2, gdouble y2, GdkColor *colour);
void draw_rect(GtkWidget *widget, int fill, gdouble x1, gdouble y1, gdouble x2, gdouble y2, GdkColor *colour);

GtkWidget *createDrawArea(GtkWidget *window,GtkWidget *vbox,int xsize,int ysize);
gint configure_event (GtkWidget *widget, GdkEventConfigure *event);
gint expose_event (GtkWidget *widget, GdkEventExpose *event);

gboolean drag_end(GtkWidget *widget,GdkDragContext *context);
void drag_data_get(GtkWidget *widget,GdkDragContext *context,GtkSelectionData *selection_data,guint info,guint32 time);
gboolean drag_drop(GtkWidget *widget,GdkDragContext *context,gint x,gint y,guint time);
void drag_data_delete(GtkWidget *widget,GdkDragContext *context);
void drag_begin(GtkWidget *widget,GdkDragContext *context);

void handle_buttons(GtkWidget *w, gpointer d);
GtkWidget *addButton(GtkWidget *vbox,GtkWidget *window,char *label,int show,GtkSignalFunc func,gpointer data);
GtkWidget *createDrawWindow(int xsize,int ysize,int xoff,int yoff,char *title);
GtkWidget *createHBox(GtkWidget *window);
GtkWidget *createVBox(GtkWidget *window);
void quit(GtkWidget *w);
GtkWidget *addText(GtkWidget *box,GtkWidget *window,gchar *starttext,GtkSignalFunc func);
void text_done(GtkWidget *w,GtkWidget *e);
void savePos(char *name,int x,int y);
void getPos(char *name,int *x,int *y);

char *options=NULL,*command;
GtkWidget *entry,*mainwindow;


int main(int argc, char *argv[])
{
GtkWidget *hbox,*hbox2,*vbox;
int x=100,y=101;

gtk_init(&argc, &argv);

mainwindow = createDrawWindow(0,0,x,y,argv[1]);
vbox = createVBox(mainwindow);
hbox = createHBox(vbox);
hbox2 = createHBox(vbox);

addButton(hbox2,mainwindow,"1",1,GTK_SIGNAL_FUNC(handle_buttons),(gpointer)1);
addButton(hbox2,mainwindow,"2",1,GTK_SIGNAL_FUNC(handle_buttons),(gpointer)1);
addButton(hbox2,mainwindow,"3",1,GTK_SIGNAL_FUNC(handle_buttons),(gpointer)1);
addButton(hbox2,mainwindow,"4",1,GTK_SIGNAL_FUNC(handle_buttons),(gpointer)1);

drawbox = createDrawArea(mainwindow,vbox,500,500);

gtk_widget_show(mainwindow);

gtk_main();

return 0;
} // end main

void handle_buttons(GtkWidget *w,gpointer d)
{
switch((int)d)
	{
	case 1:
	fprintf(stderr,"\nClickety click");
	break;
	}
} // end handle button presses


GtkWidget *addButton(GtkWidget *vbox,GtkWidget *window,char *label,int show,GtkSignalFunc func,gpointer data)
{

GtkWidget *button;
button = gtk_button_new_with_label(label);
gtk_box_pack_start(GTK_BOX(vbox),button,TRUE,TRUE,0);
gtk_signal_connect(GTK_OBJECT(button),"clicked",GTK_SIGNAL_FUNC(func),data);
gtk_widget_show(button);

gtk_drag_source_set(button,
		256,
		contact_list_drop_types, 1,
		GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK|GDK_ACTION_ASK);

gtk_signal_connect(GTK_OBJECT(button),"drag_data_get",GTK_SIGNAL_FUNC(drag_data_get),NULL);
gtk_signal_connect(GTK_OBJECT(button),"drag_begin",GTK_SIGNAL_FUNC(drag_begin),NULL);
gtk_signal_connect(GTK_OBJECT(button),"drag_end",GTK_SIGNAL_FUNC(drag_end),NULL);

gtk_signal_connect(GTK_OBJECT(button),"drag_data_delete",GTK_SIGNAL_FUNC(drag_data_delete),NULL);

return button;
} // end add a button to the vbox


GtkWidget *createDrawWindow(int xsize,int ysize,int xoff,int yoff,char *title)
{
GtkWidget *window;  
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_policy((GtkWindow *)window,0,0,1);
gtk_widget_set_uposition(window,xoff,yoff);
gtk_window_set_title((GtkWindow *)window,title);
gtk_signal_connect(GTK_OBJECT(window),"destroy",GTK_SIGNAL_FUNC (quit), NULL);
return window;
} // end create window to draw in.


GtkWidget *createHBox(GtkWidget *window)
{
GtkWidget *hbox;
hbox = gtk_hbox_new(FALSE, 1);
gtk_container_add(GTK_CONTAINER(window),hbox);
gtk_widget_show(hbox);
return hbox;
} // end create hbox for packing


GtkWidget *createVBox(GtkWidget *window)
{
GtkWidget *hbox;
hbox = gtk_vbox_new(FALSE, 1);
gtk_container_add(GTK_CONTAINER(window),hbox);
gtk_widget_show(hbox);
return hbox;
} // end create hbox for packing


void quit(GtkWidget *w)
{
gtk_widget_hide(w);
gtk_exit(0);
} // end prog

gboolean drag_end(GtkWidget *widget,GdkDragContext *context)
{
//fprintf(stderr,"\ndrag_end");
return TRUE;
}

void drag_data_get(GtkWidget *widget,GdkDragContext *context,GtkSelectionData *selection_data,guint info,guint32 time)
{
//fprintf(stderr,"\ndrag_data_get");
gtk_selection_data_set(selection_data,selection_data->type,8,"http://www.www.com",19);
}

gboolean drag_drop(GtkWidget *widget,GdkDragContext *context,gint x,gint y,guint time)
{
GdkColor black={0,0,0},white={65535,65535,65535};
int realendx,realendy;
GdkColormap *colourmap;

colourmap = gdk_window_get_colormap(widget->window);
gdk_color_alloc(colourmap,&white);
gdk_color_alloc(colourmap,&black);

//gdk_color_black(gtk_widget_get_colormap(widget), &black);
//gdk_color_white(gtk_widget_get_colormap(widget), &white);

//gdk_color_alloc(colourmap,&white);

endx = x;
endy = y;
indrag=0;
//fprintf(stderr,"\ndrag_drop: %d %d",x,y);

//fprintf(stderr,"\n\ndrag from: %d,%d -> %d,%d",startx,starty,endx,endy);

//draw_line(drawbox,startx,starty,endx,endy,&black);

if (startx==endx && starty==endy)
	fprintf(stderr,"\nDrag wasn't big enough");
else
	{
	calcEndpoint(500,500,startx,starty,endx,endy,&realendx,&realendy);
	draw_rect(widget,1,startx-5,starty-5,startx+5,starty+5,&black);
	draw_line(drawbox,startx,starty,realendx,realendy,&black);
	}

if (realendx>=0 && realendx<=100)
	{
	fprintf(stderr,"\nLEFT");
//	draw_rect(drawbox,1,0,490,100,500,&white);
//	sleep(1);
	draw_rect(drawbox,1,0,490,100,500,&black);
	}

if (realendx>=400 && realendx<=500)
	{
	fprintf(stderr,"\nRIGHT");
//	draw_rect(drawbox,1,400,490,500,500,&white);
//	sleep(1);
	draw_rect(drawbox,1,400,490,500,500,&black);
	}
	
return TRUE;
}


void calcEndpoint(gdouble xsize,gdouble ysize,gdouble sx,gdouble sy,gdouble  endx,gdouble  endy,int *realendx,int *realendy)
{
// follow through with line until his border
double m;
double c;

m = (double)(endy-sy)/(double)(endx-sx); // gradient

//y=mx+c
//y-mx = c;
c = (double)sy - (double)(m*sx);

// y=mx+c;
//*realendy = (int)(m * xsize + c);
*realendy = ysize;
// x=(y-c)/m
*realendx = (int)((ysize-c)/m);
}


gboolean drag_motion(GtkWidget *widget,GdkDragContext *context,gint x,gint y,guint time)
{
GdkColor black;

gdk_color_black(gtk_widget_get_colormap(widget), &black);

if (indrag==0)
	{
	startx=x;
	starty=y;
//	fprintf(stderr,"\ndrag_motion: %d %d",x,y);
	draw_rect(widget,1,x-5,y-5,x+5,y+5,&black);
	indrag=1;
	}

// else do nothing, ignore motion
return TRUE;
}


void drag_data_delete(GtkWidget *widget,GdkDragContext *context)
{
//fprintf(stderr,"\ndrag_data_delete");
}

void drag_begin(GtkWidget *widget,GdkDragContext *context)
{
indrag=0; // prepare for start of motion
//fprintf(stderr,"\ndrag_begin");
}

GtkWidget *createDrawArea(GtkWidget *window,GtkWidget *vbox,int xsize,int ysize)
{
GtkWidget *drawing_area;
drawing_area = gtk_drawing_area_new ();
gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area), xsize, ysize);
gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
// Signals used to handle backing pixmap
gtk_signal_connect(GTK_OBJECT (drawing_area), "expose_event",
		  (GtkSignalFunc) expose_event, NULL);
gtk_signal_connect(GTK_OBJECT(drawing_area),"configure_event",
		  (GtkSignalFunc) configure_event, NULL);

gtk_signal_connect(GTK_OBJECT(drawing_area),"drag_drop",GTK_SIGNAL_FUNC(drag_drop),NULL);
gtk_signal_connect(GTK_OBJECT(drawing_area),"drag_motion",GTK_SIGNAL_FUNC(drag_motion),NULL);

//gtk_signal_connect(GTK_OBJECT (drawing_area), "motion_notify_event",
//		  (GtkSignalFunc) motion_notify_event, NULL);


gtk_drag_dest_set(drawing_area,GTK_DEST_DEFAULT_ALL,contact_list_drop_types,1,GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK|GDK_ACTION_ASK);

gtk_widget_set_events(drawing_area, GDK_EXPOSURE_MASK
		      | GDK_LEAVE_NOTIFY_MASK
		      | GDK_POINTER_MOTION_MASK
		      | GDK_POINTER_MOTION_HINT_MASK);
gtk_widget_show(drawing_area);
return drawing_area;
} // end create drawing area to draw in.

gint configure_event (GtkWidget *widget, GdkEventConfigure *event)
{
return TRUE;
}

gint expose_event (GtkWidget *widget, GdkEventExpose *event)
{
GdkColor black;
GdkColor white;

gdk_color_black(gtk_widget_get_colormap(widget), &black);
gdk_color_white(gtk_widget_get_colormap(widget), &white);

//draw_rect(widget,1,0,0,500,500,&white);
draw_rect(widget,1,0,490,100,500,&black);
draw_rect(widget,1,400,490,500,500,&black);

return FALSE;
}

void draw_line(GtkWidget *widget, gdouble x1, gdouble y1, gdouble x2, gdouble y2, GdkColor *colour)
{
GdkGC *gc;

gc = gdk_gc_new(widget->window);
gdk_gc_set_foreground(gc,colour);

gdk_draw_line(widget->window,gc,x1,y1,x2,y2);
gdk_gc_destroy(gc);
} // end draw line


void draw_rect(GtkWidget *widget, int fill, gdouble x1, gdouble y1, gdouble x2, gdouble y2, GdkColor *colour)
{
GdkRectangle up;
GdkGC *gc;

up.x = x1;
up.y = y1;
up.width = x2-x1;
up.height = y2-y1;

gc = gdk_gc_new(widget->window);
gdk_gc_set_foreground(gc,colour);

gdk_draw_rectangle(widget->window,gc,fill,up.x,up.y,up.width,up.height);
gdk_gc_destroy(gc);
} // end draw rect



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]