1 /* mousemark.c - mouse stuff marking dragging
2 
3    Copyright (C) 1996-2000 the Free Software Foundation
4 
5    Authors: 1996, 1997 Paul Sheer
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20    02111-1307, USA.
21 */
22 
23 #include <config.h>
24 #ifndef GTK
25 #include "coolwidget.h"
26 #endif
27 
28 int just_dropped_something = 0;
29 
30 #include "edit.h"
31 #include <X11/Xmd.h>
32 #include <X11/Xatom.h>
33 #ifndef GTK
34 #include "app_glob.c"
35 #include "coollocal.h"
36 #endif
37 #include "editcmddef.h"
38 #include "xdnd.h"
39 #ifdef GTK
40 #include "src/mad.h"
41 #else
42 #include "mad.h"
43 #endif
44 
45 #ifndef HAVE_DND
46 
47 Atom **xdnd_typelist_send = 0;
48 Atom **xdnd_typelist_receive = 0;
49 
50 #define NUM_SIMPLE_TYPES 10
51 
52 /* each entry is listed in order of what we would like the RECEIVE */
53 static char *mime_type_send[NUM_SIMPLE_TYPES][10] =
54 {
55     {0},			/* DndUnknown 0 */
56     {0},			/* DndRawData 1 */
57     {"url/url", "text/plain", 0},	/* DndFile    2 (input widget) */
58     {"text/plain", "text/uri-list", 0},	/* DndFiles   3 (fielded text box) */
59     {"text/plain", "application/octet-stream", 0},	/* DndText    4 (editor widget) */
60 /* don't use these: */
61     {0},			/* DndLink    5 */
62     {0},			/* DndDir     6 */
63     {0},			/* DndExe     7 */
64     {0},			/* DndURL     8 */
65     {0},			/* DndMIME    9 */
66 };
67 
68 static char *mime_type_recieve[NUM_SIMPLE_TYPES][10] =
69 {
70     {0},	/* DndUnknown 0 */
71     {0},	/* DndRawData 1 */
72     {"url/url", "text/uri-list", "text/plain", 0},	/* DndFile    2 (input widget) */
73     {0},	/* DndFiles   3 */
74     {"url/url", "text/plain", "application/octet-stream", "text/uri-list", 0},	/* DndText    4 (editor widget) */
75 /* don't use these: */
76     {0},			/* DndLink    5 */
77     {0},			/* DndDir     6 */
78     {0},			/* DndExe     7 */
79     {0},			/* DndURL     8 */
80     {0},			/* DndMIME    9 */
81 };
82 
83 #ifndef GTK
84 
85 static char dnd_directory[MAX_PATH_LEN] = "/";
86 
striptrailing(char * s,int c)87 char *striptrailing (char *s, int c)
88 {
89     int i;
90     i = strlen (s) - 1;
91 
92     while (i >= 0) {
93 	if (s[i] == c) {
94 	    s[i--] = 0;
95 	    continue;
96 	}
97 	break;
98     }
99     return s;
100 }
101 
102 /* return the prepending directory (see CDndFileList() below) */
CDndDirectory(void)103 char *CDndDirectory (void)
104 {
105     return dnd_directory;
106 }
107 
108 /*
109    Sets the directory, must be a null terminated complete path.
110    Strips trailing slashes.
111  */
CSetDndDirectory(char * d)112 void CSetDndDirectory (char *d)
113 {
114     if (!d)
115 	return;
116     strcpy (dnd_directory, d);
117     striptrailing (dnd_directory, '/');
118     if (*dnd_directory)
119 	return;
120     *dnd_directory = '/';
121 }
122 
123 /*
124    Takes a newline separate list of files,
125    returns a newline separate list of complete `file:' path names
126    by prepending dnd_directory to each file name.
127    returns 0 if no files in list.
128    result must always be free'd.
129    returns l as the total length of data.
130    returns num_files as the number of files in the list.
131    Alters t
132  */
CDndFileList(char * t,int * l,int * num_files)133 char *CDndFileList (char *t, int *l, int *num_files)
134 {
135     char *p, *q, *r, *result;
136     int i, len, done = 0;
137 
138 /* strip leading newlines */
139     while (*t == '\n')
140 	t++;
141 
142 /* strip trailing newlines */
143     striptrailing (t, '\n');
144 
145     if (!*t)
146 	return 0;
147 
148 /* count files */
149     for (i = 1, p = t; *p; p++)
150 	if (*p == '\n')
151 	    i++;
152 
153     *num_files = i;
154 
155     len = (unsigned long) p - (unsigned long) t;
156     result = CMalloc ((strlen (dnd_directory) + strlen ("file:") + 2) * i + len + 2);
157 
158     r = result;
159     p = t;
160     while (!done) {
161 	q = strchr (p, '\n');
162 	if (q)
163 	    *q = 0;
164 	else
165 	    done = 1;
166 	strcpy (r, "file:");
167 	if (*p != '/') {
168 	    strcat (r, dnd_directory);
169 	    strcat (r, "/");
170 	}
171 	strcat (r, p);
172 	r += strlen (r);
173 	*r++ = '\n';
174 	p = ++q;
175     }
176     *r = 0;
177     *l = (unsigned long) r - (unsigned long) result;
178     return result;
179 }
180 
181 Window get_focus_border_widget (void);
182 
widget_apply_leave(DndClass * dnd,Window widgets_window)183 static void widget_apply_leave (DndClass * dnd, Window widgets_window)
184 {
185     CWidget *w;
186     w = CWidgetOfWindow (widgets_window);
187     if (get_focus_border_widget () == widgets_window)
188 	destroy_focus_border ();
189     if (w)
190 	CExpose (w->ident);
191 }
192 
widget_insert_drop(DndClass * dnd,unsigned char * data,int length,int remaining,Window into,Window from,Atom type)193 static int widget_insert_drop (DndClass * dnd, unsigned char *data, int length, int remaining, Window into, Window from, Atom type)
194 {
195     CWidget *w;
196     char *p;
197     w = CWidgetOfWindow (into);
198     if (!w)
199 	return 1;
200     if (w->funcs->insert_drop) {
201 	int r;
202 	Window child_return;
203 	int xd, yd;
204 	if (!dnd->user_hook1)
205 	    dnd->user_hook2 = dnd->user_hook1 = CMalloc (length + remaining + 1);
206 	memcpy (dnd->user_hook2, data, length);
207 	p = dnd->user_hook2;
208 	p += length;	/* avoids ansi warning */
209 	dnd->user_hook2 = p;
210 	if (remaining)
211 	    return 0;
212 	XTranslateCoordinates (CDisplay, CRoot, into, dnd->x, dnd->y, &xd, &yd, &child_return);
213 	r = (*w->funcs->insert_drop) (w->funcs->data, from, dnd->user_hook1, (unsigned long) dnd->user_hook2 - (unsigned long) dnd->user_hook1, xd, yd, type, dnd->supported_action);
214 	free (dnd->user_hook1);
215 	dnd->user_hook1 = dnd->user_hook2 = 0;
216 	if (get_focus_border_widget () == into)
217 	    destroy_focus_border ();
218 	CExpose (w->ident);
219 	return r;
220     }
221     return 1;
222 }
223 
array_length(Atom * a)224 static int array_length (Atom * a)
225 {
226     int n;
227     for (n = 0; a[n]; n++);
228     return n;
229 }
230 
231 /* X11R5 does not have XGetAtomNames function: */
my_XGetAtomNames(Display * display,Atom * atoms,int count,char ** names_return)232 static Status my_XGetAtomNames (Display * display, Atom * atoms, int count, char **names_return)
233 {
234     int i;
235     for (i = 0; i < count; i++)
236 	if (!(names_return[i] = XGetAtomName (display, atoms[i])))
237 	    return 0;
238     return 1;
239 }
240 
widget_apply_position(DndClass * dnd,Window widgets_window,Window from,Atom action,int x,int y,Time t,Atom * typelist,int * want_position,Atom * supported_action,Atom * desired_type,XRectangle * rectangle)241 static int widget_apply_position (DndClass * dnd, Window widgets_window, Window from,
242 		      Atom action, int x, int y, Time t, Atom * typelist,
243 	int *want_position, Atom * supported_action, Atom * desired_type,
244 				  XRectangle * rectangle)
245 {
246     CWidget *w;
247     Window child_return;
248     int xt, yt;
249     int xd, yd;
250     long click;
251     int i, j;
252     Atom result = 0;
253 
254     w = CWidgetOfWindow (widgets_window);
255     if (!w)
256 	return 0;
257 
258 /* doesn't have mouse funcs - not a drop widget?? */
259     if (!w->funcs)
260         return 0;
261 
262 /* input widgets can't drop to themselves :-( */
263     if (w->kind == C_TEXTINPUT_WIDGET && widgets_window == from)
264 	return 0;
265 
266 /* look up mime types from my own list of supported types */
267     for (j = 0; xdnd_typelist_receive[w->funcs->types][j]; j++) {
268 	for (i = 0; typelist[i]; i++) {
269 	    if (typelist[i] == xdnd_typelist_receive[w->funcs->types][j]) {
270 		result = typelist[i];
271 		break;
272 	    }
273 	}
274 	if (result)
275 	    break;
276     }
277 
278     if (!result && w->funcs->mime_majors) {
279 	char **names_return;
280 	names_return = CMalloc ((array_length (typelist) + 1) * sizeof (char *));
281 	memset (names_return, 0, (array_length (typelist) + 1) * sizeof (char *));
282 	if (my_XGetAtomNames (CDisplay, typelist, array_length (typelist), names_return)) {
283 	    for (i = 0; i < array_length (typelist); i++) {
284 		for (j = 0; w->funcs->mime_majors[j]; j++) {
285 		    if (!strncmp (w->funcs->mime_majors[j], names_return[i], strlen (w->funcs->mime_majors[j]))) {
286 			result = typelist[i];
287 			break;
288 		    }
289 		}
290 		if (result)
291 		    break;
292 	    }
293 	    for (i = 0; i < array_length (typelist); i++)
294 		if (names_return[i])
295 		    XFree (names_return[i]);
296 	}
297     }
298 /* not supported, so return false */
299     if (!result)
300 	return 0;
301 
302     XTranslateCoordinates (CDisplay, CRoot, widgets_window, x, y, &xd, &yd, &child_return);
303     if (xd < -40 || yd < -40 || xd >= CWidthOf (w) + 40 || yd >= CHeightOf (w) + 40)
304 	return 0;
305 
306 /* things that can receive a drop are in the editor font by definition. */
307     CPushFont ("editor", 0);
308     if (w->funcs->xy && w->funcs->cp && w->funcs->move) {
309 	(*w->funcs->xy) (xd, yd, &xt, &yt);
310 	click = (*w->funcs->cp) (w->funcs->data, xt, yt);
311 	if (w->funcs->fin_mark)
312 	    (*w->funcs->fin_mark) (w->funcs->data);
313 	if (w->funcs->move)
314 	    (*w->funcs->move) (w->funcs->data, click, yt);
315 	if (w->funcs->redraw)
316 	    (*w->funcs->redraw) (w->funcs->data, click);
317     }
318     CPopFont ();
319 
320 /* we want more position messages */
321     *want_position = 1;
322 
323 /* we only support copy and move */
324     if (action == dnd->XdndActionMove) {
325 	*supported_action = dnd->XdndActionMove;
326     } else {
327 	*supported_action = dnd->XdndActionCopy;
328     }
329     *desired_type = result;
330     rectangle->x = x - 1;
331     rectangle->y = y - 1;
332     rectangle->width = rectangle->height = 2;
333     if (get_focus_border_widget () != widgets_window) {
334 	destroy_focus_border ();
335 	create_focus_border (w, 4);
336     }
337     CExpose (w->ident);
338     return 1;
339 }
340 
widget_get_data(DndClass * dnd,Window window,unsigned char ** data,int * length,Atom type)341 static void widget_get_data (DndClass * dnd, Window window, unsigned char **data, int *length, Atom type)
342 {
343     int t = DndText;
344     long start_mark, end_mark;
345     CWidget *w;
346     w = CWidgetOfWindow (window);
347     if (!w) {
348 	return;
349     }
350     if (!w->funcs)
351 	return;
352     if ((*w->funcs->marks) (w->funcs->data, &start_mark, &end_mark)) {
353 	return;
354     }
355     if (type == XInternAtom (dnd->display, "url/url", False))
356 	t = DndFile;
357     else if (type == XInternAtom (dnd->display, "text/uri-list", False))
358 	t = DndFiles;
359     *data = (unsigned char *) (*w->funcs->get_block) (w->funcs->data, start_mark, end_mark, &t, length);
360 }
361 
widget_exists(DndClass * dnd,Window window)362 static int widget_exists (DndClass * dnd, Window window)
363 {
364     return (CWidgetOfWindow (window) != 0);
365 }
366 
handle_expose_events(DndClass * dnd,XEvent * xevent)367 static void handle_expose_events (DndClass * dnd, XEvent * xevent)
368 {
369     if (!xevent->xexpose.count)
370 	render_focus_border (xevent->xexpose.window);
371     return;
372 }
373 
mouse_init(void)374 void mouse_init (void)
375 {
376     CDndClass->handle_expose_events = handle_expose_events;
377     CDndClass->widget_insert_drop = widget_insert_drop;
378     CDndClass->widget_exists = widget_exists;
379     CDndClass->widget_apply_position = widget_apply_position;
380     CDndClass->widget_get_data = widget_get_data;
381     CDndClass->widget_apply_leave = widget_apply_leave;
382     CDndClass->options |= XDND_OPTION_NO_HYSTERESIS;
383     CDndClass->user_hook1 = CDndClass->user_hook2 = 0;
384     if (!xdnd_typelist_receive) {
385 	int i;
386 	xdnd_typelist_receive = malloc ((NUM_SIMPLE_TYPES + 1) * sizeof (Atom *));
387 	xdnd_typelist_send = malloc ((NUM_SIMPLE_TYPES + 1) * sizeof (Atom *));
388 	for (i = 0; i < NUM_SIMPLE_TYPES; i++) {
389 	    int j;
390 	    xdnd_typelist_receive[i] = CMalloc (32 * sizeof (Atom));
391 	    for (j = 0; mime_type_recieve[i][j]; j++) {
392 		xdnd_typelist_receive[i][j] = XInternAtom (CDndClass->display, mime_type_recieve[i][j], False);
393 		xdnd_typelist_receive[i][j + 1] = 0;
394 	    }
395 	    xdnd_typelist_receive[i + 1] = 0;
396 	    xdnd_typelist_send[i] = CMalloc (32 * sizeof (Atom));
397 	    for (j = 0; mime_type_send[i][j]; j++) {
398 		xdnd_typelist_send[i][j] = XInternAtom (CDndClass->display, mime_type_send[i][j], False);
399 		xdnd_typelist_send[i][j + 1] = 0;
400 	    }
401 	    xdnd_typelist_send[i + 1] = 0;
402 	}
403     }
404 }
405 
mouse_shut(void)406 void mouse_shut (void)
407 {
408     if (xdnd_typelist_receive) {
409 	int i;
410 	for (i = 0; xdnd_typelist_send[i]; i++)
411 	    free (xdnd_typelist_send[i]);
412 	free (xdnd_typelist_send);
413 	xdnd_typelist_send = 0;
414 	for (i = 0; xdnd_typelist_receive[i]; i++)
415 	    free (xdnd_typelist_receive[i]);
416 	free (xdnd_typelist_receive);
417 	xdnd_typelist_receive = 0;
418     }
419 }
420 
421 #endif  /* !GTK */
422 
mouse_funcs_new(void * data,struct mouse_funcs * m)423 struct mouse_funcs *mouse_funcs_new (void *data, struct mouse_funcs *m)
424 {
425     struct mouse_funcs *p;
426     p = CMalloc (sizeof (*m));
427     memcpy (p, m, sizeof (*m));
428     p->data = data;
429     return p;
430 }
431 
432 #endif /* HAVE_DND */
433 
mouse_mark(XEvent * event,int double_click,struct mouse_funcs * funcs)434 void mouse_mark (XEvent * event, int double_click, struct mouse_funcs *funcs)
435 {
436     void *data;
437     static unsigned long win_press = 0;
438     static int state = 0;	/* 0 = button up, 1 = button pressed, 2 = button pressed and dragging */
439     static int x_last, y_last;
440     long click;
441     data = funcs->data;
442 
443     if (event->type == ButtonPress) {
444 	long start_mark, end_mark;
445 	state = 1;
446 	win_press = (unsigned long) event->xbutton.window;
447 	(*funcs->xy) (event->xbutton.x, event->xbutton.y, &x_last, &y_last);
448 	click = (*funcs->cp) (data, x_last, y_last);
449 	if (!(*funcs->marks) (data, &start_mark, &end_mark)) {
450 	    if ((*funcs->range) (data, start_mark, end_mark, click)) {		/* if clicked on highlighted text */
451 		unsigned char *t;
452 		int l;
453 #ifdef HAVE_DND
454 		int type;
455 		t = (unsigned char *) (*funcs->get_block) (data, start_mark, end_mark, &type, &l);
456 		if (t) {
457 		    just_dropped_something = 1;
458 		    CDrag (event->xbutton.window, type, t, l, event->xbutton.button == Button1 ? Button1Mask : 0);
459 		    free (t);
460 		}
461 #else
462 #ifndef GTK
463 		/* this is just to get the type which depends on the number of lines selected for
464 		    the fileselection box: */
465 		t = (unsigned char *) (*funcs->get_block) (data, start_mark, end_mark, &funcs->types, &l);
466 		if (t) {
467 		    free (t);
468 		    set_cursor_visible ();
469 		    if (xdnd_drag (CDndClass, event->xbutton.window,
470 				event->xbutton.button == Button1 ? CDndClass->XdndActionCopy : CDndClass->XdndActionMove,
471 				xdnd_typelist_send[funcs->types]) == CDndClass->XdndActionMove) {
472 			if (funcs->delete_block)
473 			    (*funcs->delete_block) (data);
474 		    }
475 		}
476 #endif
477 #endif
478 		if (funcs->fin_mark)
479 		    (*funcs->fin_mark) (data);
480 		return;
481 	    }
482 	}
483 	just_dropped_something = 0;
484 	if (funcs->fin_mark)
485 	    (*funcs->fin_mark) (data);
486 	(*funcs->move) (data, click, y_last);
487 	if (double_click && funcs->dclick) {
488 	    (*funcs->dclick) (data, event);
489 	    state = 0;
490 	}
491 	if (funcs->redraw)
492 	    (*funcs->redraw) (data, click);
493     } else if (event->type == ButtonRelease && state > 0 && win_press == (unsigned long) event->xbutton.window && !double_click) {
494 	int x, y;
495 	long start_mark, end_mark;
496 	(*funcs->xy) (event->xbutton.x, event->xbutton.y, &x, &y);
497 	click = (*funcs->cp) (data, x, y);
498 	(*funcs->move) (data, click, y);
499 	if (state == 2)
500 	    goto unhighlight;
501 	if (!(*funcs->marks) (data, &start_mark, &end_mark)) {
502 	    if ((*funcs->range) (data, start_mark, end_mark, click)) {		/* if clicked on highlighted text */
503 	      unhighlight:
504 		if (funcs->release_mark)
505 		    (*funcs->release_mark) (data, event);
506 	    }
507 	}
508 	state = 0;
509 	if (funcs->redraw)
510 	    (*funcs->redraw) (data, click);
511     } else if (event->type == MotionNotify && state > 0 && win_press == (unsigned long) event->xmotion.window && (event->xmotion.state & Button12345Mask)) {
512 	int x, y;
513 	if (!(event->xmotion.state & Button12345Mask))
514 	    return;
515 	(*funcs->xy) (event->xmotion.x, event->xmotion.y, &x, &y);
516 	if (x == x_last && y == y_last && state == 1)
517 	    return;
518 	click = (*funcs->cp) (data, x, y);
519 	if (state == 1) {
520 	    state = 2;
521 	    if (funcs->move_mark)
522 		(funcs->move_mark) (data);
523 	}
524 	(*funcs->move) (data, click, y);
525 	if (funcs->motion)
526 	    (*funcs->motion) (data, click);
527 	if (funcs->redraw)
528 	    (*funcs->redraw) (data, click);
529     }
530 }
531 
532