1/*
2 * tkTreeDrag.c --
3 *
4 *	This module implements outline dragging for treectrl widgets.
5 *
6 * Copyright (c) 2002-2009 Tim Baker
7 *
8 * RCS: @(#) $Id: tkTreeDrag.c,v 1.34 2010/03/21 20:47:06 treectrl Exp $
9 */
10
11#include "tkTreeCtrl.h"
12
13typedef struct TreeDragImage_ TreeDragImage_;
14typedef struct DragElem DragElem;
15
16/*
17 * The following structure holds info about a single element of the drag
18 * image.
19 */
20struct DragElem
21{
22    int x, y, width, height;
23    DragElem *next;
24};
25
26/*
27 * The following structure holds info about the drag image. There is one of
28 * these per TreeCtrl.
29 */
30struct TreeDragImage_
31{
32    TreeCtrl *tree;
33    Tk_OptionTable optionTable;
34    int visible;
35    int x, y; /* offset to draw at in canvas coords */
36    int bounds[4]; /* bounds of all DragElems */
37    DragElem *elem;
38    int onScreen; /* TRUE if is displayed */
39    int sx, sy; /* Window coords where displayed */
40    int sw, sh; /* Width/height of previously-displayed image */
41#ifdef DRAG_PIXMAP
42    int pixmapW, pixmapH;
43    Pixmap pixmap;
44    Tk_Image image;
45#endif /* DRAG_PIXMAP */
46#ifdef DRAGIMAGE_STYLE
47    TreeStyle masterStyle; /* Style to create the drag image from. */
48    TreeStyle instanceStyle; /* Style to create the drag image from. */
49    int styleX, styleY; /* Mouse cursor hotspot offset into dragimage. */
50    int styleW, styleH; /* Width/Height of dragimage style. */
51    int pixmapW, pixmapH; /* Width/Height of 'pixmap'. */
52    Pixmap pixmap; /* Pixmap -> Tk_Image. */
53    Tk_Image tkimage; /* Transparent image that is drawn in the widget. */
54#endif /* DRAGIMAGE_STYLE */
55};
56
57#define DRAG_CONF_VISIBLE		0x0001
58
59static Tk_OptionSpec optionSpecs[] = {
60#ifdef DRAGIMAGE_STYLE
61    {TK_OPTION_CUSTOM, "-style", (char *) NULL, (char *) NULL,
62     (char *) NULL, -1, Tk_Offset(TreeDragImage_, masterStyle),
63     TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_style, 0},
64#endif /* DRAGIMAGE_STYLE */
65    {TK_OPTION_BOOLEAN, "-visible", (char *) NULL, (char *) NULL,
66	"0", -1, Tk_Offset(TreeDragImage_, visible),
67	0, (ClientData) NULL, DRAG_CONF_VISIBLE},
68    {TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
69	(char *) NULL, 0, -1, 0, 0, 0}
70};
71
72#ifdef DRAG_PIXMAP
73static void
74UpdateImage(
75    TreeDragImage dragImage	/* Drag image record. */
76    )
77{
78    TreeCtrl *tree = dragImage->tree;
79    Tk_PhotoHandle photoH;
80    XImage *ximage;
81    int width = dragImage->bounds[2] - dragImage->bounds[0];
82    int height = dragImage->bounds[3] - dragImage->bounds[1];
83    int alpha = 128;
84    XColor *colorPtr;
85
86    if (dragImage->image != NULL) {
87	Tk_FreeImage(dragImage->image);
88	dragImage->image = NULL;
89    }
90
91    photoH = Tk_FindPhoto(tree->interp, "::TreeCtrl::ImageDrag");
92    if (photoH == NULL) {
93	Tcl_GlobalEval(tree->interp, "image create photo ::TreeCtrl::ImageDrag");
94	photoH = Tk_FindPhoto(tree->interp, "::TreeCtrl::ImageDrag");
95	if (photoH == NULL)
96	    return;
97    }
98
99    /* Pixmap -> XImage */
100    ximage = XGetImage(tree->display, dragImage->pixmap, 0, 0,
101	    (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap);
102    if (ximage == NULL)
103	panic("tkTreeDrag.c:UpdateImage() ximage is NULL");
104
105    /* XImage -> Tk_Image */
106    colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "pink");
107    Tree_XImage2Photo(tree->interp, photoH, ximage, colorPtr->pixel, alpha);
108
109    XDestroyImage(ximage);
110
111    dragImage->image = Tk_GetImage(tree->interp, tree->tkwin,
112	"::TreeCtrl::ImageDrag", NULL, (ClientData) NULL);
113}
114
115static void
116UpdatePixmap(
117    TreeDragImage dragImage	/* Drag image record. */
118    )
119{
120    TreeCtrl *tree = dragImage->tree;
121    int w, h;
122    XColor *colorPtr;
123    GC gc;
124    DragElem *elem;
125    unsigned long trans;
126
127    w = dragImage->bounds[2] - dragImage->bounds[0];
128    h = dragImage->bounds[3] - dragImage->bounds[1];
129    if (w > dragImage->pixmapW || h > dragImage->pixmapH)
130    {
131
132	if (dragImage->pixmap != None)
133	    Tk_FreePixmap(tree->display, dragImage->pixmap);
134	dragImage->pixmap = Tk_GetPixmap(tree->display,
135	    Tk_WindowId(tree->tkwin),
136	    w, h, Tk_Depth(tree->tkwin));
137
138	dragImage->pixmapW = w;
139	dragImage->pixmapH = h;
140    }
141
142    colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "pink");
143    gc = Tk_GCForColor(colorPtr, Tk_WindowId(tree->tkwin));
144    XFillRectangle(tree->display, dragImage->pixmap, gc,
145	0, 0, w, h);
146
147    trans = colorPtr->pixel;
148
149    colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "gray50");
150    gc = Tk_GCForColor(colorPtr, Tk_WindowId(tree->tkwin));
151
152    for (elem = dragImage->elem; elem != NULL; elem = elem->next) {
153	XFillRectangle(tree->display, dragImage->pixmap, gc,
154	    elem->x - dragImage->bounds[0],
155	    elem->y - dragImage->bounds[1],
156	    elem->width, elem->height);
157    }
158
159    if (dragImage->image != NULL) {
160	Tk_FreeImage(dragImage->image);
161	dragImage->image = NULL;
162    }
163}
164
165static void
166DrawPixmap(
167    TreeDragImage dragImage,	/* Drag image record. */
168    TreeDrawable td)
169{
170    TreeCtrl *tree = dragImage->tree;
171    int ix, iy, iw, ih;
172
173    if (!dragImage->visible)
174	return;
175
176    if (dragImage->image == NULL)
177	UpdateImage(dragImage);
178
179    if (dragImage->image == NULL)
180	return;
181
182    ix = iy = 0;
183    iw = dragImage->bounds[2] - dragImage->bounds[0];
184    ih = dragImage->bounds[3] - dragImage->bounds[1];
185
186    /* FIXME: clip src image to area to be redrawn */
187
188    Tree_RedrawImage(dragImage->image, ix, iy, iw, ih, td,
189	dragImage->x + dragImage->bounds[0] - tree->drawableXOrigin,
190	dragImage->y + dragImage->bounds[1] - tree->drawableYOrigin);
191}
192#endif /* DRAG_PIXMAP */
193
194#ifdef DRAGIMAGE_STYLE
195void
196TreeDragImage_StyleDeleted(
197    TreeDragImage dragImage,	/* Drag image record. */
198    TreeStyle style)		/* Style that was deleted. */
199{
200    TreeCtrl *tree = dragImage->tree;
201
202    if (dragImage->masterStyle == style) {
203	TreeStyle_FreeResources(tree, dragImage->instanceStyle);
204	dragImage->masterStyle = NULL;
205	dragImage->instanceStyle = NULL;
206    }
207}
208
209static void
210DragImage_UpdateStyleTkImage(
211    TreeDragImage dragImage)	/* Drag image record. */
212{
213    TreeCtrl *tree = dragImage->tree;
214    Tk_PhotoHandle photoH;
215    XImage *ximage;
216    int width = dragImage->styleW;
217    int height = dragImage->styleH;
218    int alpha = 128;
219    XColor *colorPtr;
220
221    if (dragImage->tkimage != NULL) {
222	Tk_FreeImage(dragImage->tkimage);
223	dragImage->tkimage = NULL;
224    }
225
226    photoH = Tk_FindPhoto(tree->interp, "::TreeCtrl::ImageDrag");
227    if (photoH == NULL) {
228	Tcl_GlobalEval(tree->interp, "image create photo ::TreeCtrl::ImageDrag");
229	photoH = Tk_FindPhoto(tree->interp, "::TreeCtrl::ImageDrag");
230	if (photoH == NULL)
231	    return;
232    }
233
234    /* Pixmap -> XImage */
235    ximage = XGetImage(tree->display, dragImage->pixmap, 0, 0,
236	    (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap);
237    if (ximage == NULL)
238	panic("tkTreeDrag.c:DragImage_UpdateStyleTkImage() ximage is NULL");
239
240    /* XImage -> Tk_Image */
241    colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "pink");
242    Tree_XImage2Photo(tree->interp, photoH, ximage, colorPtr->pixel, alpha);
243
244    XDestroyImage(ximage);
245
246    dragImage->tkimage = Tk_GetImage(tree->interp, tree->tkwin,
247	"::TreeCtrl::ImageDrag", NULL, (ClientData) NULL);
248}
249
250static void
251DragImage_UpdateStylePixmap(
252    TreeDragImage dragImage)	/* Drag image record. */
253{
254    TreeCtrl *tree = dragImage->tree;
255    int w, h, state = 0;
256    XColor *colorPtr;
257    GC gc;
258    StyleDrawArgs drawArgs;
259
260    w = dragImage->styleW = TreeStyle_NeededWidth(tree, dragImage->instanceStyle, state);
261    h = dragImage->styleH = TreeStyle_NeededHeight(tree, dragImage->instanceStyle, state);
262    if (w > dragImage->pixmapW || h > dragImage->pixmapH)
263    {
264
265	if (dragImage->pixmap != None)
266	    Tk_FreePixmap(tree->display, dragImage->pixmap);
267	dragImage->pixmap = Tk_GetPixmap(tree->display,
268	    Tk_WindowId(tree->tkwin),
269	    w, h, Tk_Depth(tree->tkwin));
270
271	dragImage->pixmapW = w;
272	dragImage->pixmapH = h;
273    }
274
275    colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "pink");
276    gc = Tk_GCForColor(colorPtr, Tk_WindowId(tree->tkwin));
277    XFillRectangle(tree->display, dragImage->pixmap, gc,
278	0, 0, w, h);
279
280    drawArgs.tree = tree;
281
282    drawArgs.td.drawable = dragImage->pixmap;
283    drawArgs.td.width = w; drawArgs.td.height = h;
284
285    drawArgs.bounds[0] = drawArgs.bounds[1] = 0;
286    drawArgs.bounds[2] = w; drawArgs.bounds[3] = h;
287
288    drawArgs.state = state;
289    drawArgs.style = dragImage->instanceStyle;
290
291    drawArgs.indent = 0;
292
293    drawArgs.x = drawArgs.y = 0;
294    drawArgs.width = w; drawArgs.height = h;
295
296    drawArgs.justify = TK_JUSTIFY_LEFT;
297
298    TreeStyle_Draw(&drawArgs);
299
300    if (dragImage->tkimage != NULL) {
301	Tk_FreeImage(dragImage->tkimage);
302	dragImage->tkimage = NULL;
303    }
304}
305
306static void
307DragImage_DrawStyle(
308    TreeDragImage dragImage,	/* Drag image record. */
309    TreeDrawable td)		/* Where to draw. */
310{
311    TreeCtrl *tree = dragImage->tree;
312    int ix, iy, iw, ih;
313
314    if (dragImage->tkimage == NULL)
315	DragImage_UpdateStyleTkImage(dragImage);
316
317    if (dragImage->tkimage == NULL)
318	return;
319
320    ix = iy = 0;
321    iw = dragImage->styleW; ih = dragImage->styleH;
322
323    Tree_RedrawImage(dragImage->tkimage, ix, iy, iw, ih, td,
324	dragImage->x + -dragImage->styleX - tree->drawableXOrigin,
325	dragImage->y + -dragImage->styleY - tree->drawableYOrigin);
326}
327#endif /* DRAGIMAGE_STYLE */
328
329/*
330 *----------------------------------------------------------------------
331 *
332 * TreeDragImage_Draw --
333 *
334 *	Draw the elements that make up the drag image if it is visible.
335 *
336 * Results:
337 *	None.
338 *
339 * Side effects:
340 *	Stuff is drawn.
341 *
342 *----------------------------------------------------------------------
343 */
344
345void
346TreeDragImage_Draw(
347    TreeDragImage dragImage,	/* Drag image record. */
348    TreeDrawable td)		/* Where to draw. */
349{
350#ifdef DRAG_PIXMAP
351    DrawPixmap(tree->dragImage, tdrawable);
352
353#elif 1 /* Use XOR dotted rectangles where possible. */
354    TreeCtrl *tree = dragImage->tree;
355
356    if (!dragImage->visible)
357	return;
358
359#ifdef DRAGIMAGE_STYLE
360    if (dragImage->instanceStyle != NULL) {
361	DragImage_DrawStyle(dragImage, td);
362	return;
363    }
364#endif /* DRAGIMAGE_STYLE */
365
366    /* Yes this is XOR drawing but we aren't erasing the previous
367     * dragimage as when TreeDragImage_IsXOR() returns TRUE. */
368    TreeDragImage_DrawXOR(dragImage, td.drawable,
369	0 - tree->xOrigin, 0 - tree->yOrigin);
370#else /* */
371    TreeCtrl *tree = dragImage->tree;
372    GC gc;
373    DragElem *elem;
374#if 1 /* Stippled rectangles: BUG not clipped to contentbox. */
375    XGCValues gcValues;
376    unsigned long mask;
377    XPoint points[5];
378
379    if (!dragImage->visible)
380	return;
381
382    gcValues.stipple = Tk_GetBitmap(tree->interp, tree->tkwin, "gray50");
383    gcValues.fill_style = FillStippled;
384    mask = GCStipple|GCFillStyle;
385    gc = Tk_GetGC(tree->tkwin, mask, &gcValues);
386
387    for (elem = dragImage->elem; elem != NULL; elem = elem->next) {
388	XRectangle rect;
389	rect.x = dragImage->x + elem->x /*- dragImage->bounds[0]*/ - tree->drawableXOrigin;
390	rect.y = dragImage->y + elem->y /*- dragImage->bounds[1]*/ - tree->drawableYOrigin;
391	rect.width = elem->width;
392	rect.height = elem->height;
393
394#ifdef WIN32
395	/* XDrawRectangle ignores the stipple pattern. */
396	points[0].x = rect.x, points[0].y = rect.y;
397	points[1].x = rect.x + rect.width - 1, points[1].y = rect.y;
398	points[2].x = rect.x + rect.width - 1, points[2].y = rect.y + rect.height - 1;
399	points[3].x = rect.x, points[3].y = rect.y + rect.height - 1;
400	points[4] = points[0];
401	XDrawLines(tree->display, td.drawable, gc, points, 5, CoordModeOrigin);
402#else /* !WIN32 */
403	XDrawRectangle(tree->display, td.drawable, gc, rect.x, rect.y,
404	    rect.width - 1, rect.height - 1);
405#endif
406    }
407
408    Tk_FreeGC(tree->display, gc);
409#else /* Debug/test: gray rectangles */
410    XColor *colorPtr;
411    TkRegion rgn;
412
413    if (!dragImage->visible)
414	return;
415
416    colorPtr = Tk_GetColor(tree->interp, tree->tkwin, "gray50");
417    gc = Tk_GCForColor(colorPtr, Tk_WindowId(tree->tkwin));
418
419    rgn = Tree_GetRegion(tree);
420
421    for (elem = dragImage->elem; elem != NULL; elem = elem->next) {
422	XRectangle rect;
423	rect.x = dragImage->x + elem->x /*- dragImage->bounds[0]*/ - tree->drawableXOrigin;
424	rect.y = dragImage->y + elem->y /*- dragImage->bounds[1]*/ - tree->drawableYOrigin;
425	rect.width = elem->width;
426	rect.height = elem->height;
427	TkUnionRectWithRegion(&rect, rgn, rgn);
428    }
429
430    Tree_FillRegion(tree->display, td.drawable, gc, rgn);
431
432    Tree_FreeRegion(tree, rgn);
433#endif /* Debug/test: gray rectangles */
434#endif /* XOR */
435}
436
437/*
438 *----------------------------------------------------------------------
439 *
440 * DragElem_Alloc --
441 *
442 *	Allocate and initialize a new DragElem record. Add the record
443 *	to the list of records for the drag image.
444 *
445 * Results:
446 *	Pointer to allocated DragElem.
447 *
448 * Side effects:
449 *	Memory is allocated.
450 *
451 *----------------------------------------------------------------------
452 */
453
454static DragElem *
455DragElem_Alloc(
456    TreeDragImage dragImage	/* Drag image record. */
457    )
458{
459    DragElem *elem = (DragElem *) ckalloc(sizeof(DragElem));
460    DragElem *walk = dragImage->elem;
461    memset(elem, '\0', sizeof(DragElem));
462    if (dragImage->elem == NULL)
463	dragImage->elem = elem;
464    else {
465	while (walk->next != NULL)
466	    walk = walk->next;
467	walk->next = elem;
468    }
469    return elem;
470}
471
472/*
473 *----------------------------------------------------------------------
474 *
475 * DragElem_Free --
476 *
477 *	Free a DragElem.
478 *
479 * Results:
480 *	Pointer to the next DragElem.
481 *
482 * Side effects:
483 *	Memory is deallocated.
484 *
485 *----------------------------------------------------------------------
486 */
487
488static DragElem *
489DragElem_Free(
490    TreeDragImage dragImage,	/* Drag image record. */
491    DragElem *elem		/* Drag element to free. */
492    )
493{
494    DragElem *next = elem->next;
495    WFREE(elem, DragElem);
496    return next;
497}
498
499/*
500 *----------------------------------------------------------------------
501 *
502 * TreeDragImage_Init --
503 *
504 *	Perform drag-image-related initialization when a new TreeCtrl is
505 *	created.
506 *
507 * Results:
508 *	A standard Tcl result.
509 *
510 * Side effects:
511 *	Memory is allocated.
512 *
513 *----------------------------------------------------------------------
514 */
515
516int
517TreeDragImage_Init(
518    TreeCtrl *tree		/* Widget info. */
519    )
520{
521    TreeDragImage dragImage;
522
523    dragImage = (TreeDragImage) ckalloc(sizeof(TreeDragImage_));
524    memset(dragImage, '\0', sizeof(TreeDragImage_));
525    dragImage->tree = tree;
526    dragImage->optionTable = Tk_CreateOptionTable(tree->interp, optionSpecs);
527    if (Tk_InitOptions(tree->interp, (char *) dragImage, dragImage->optionTable,
528	tree->tkwin) != TCL_OK) {
529	WFREE(dragImage, TreeDragImage_);
530	return TCL_ERROR;
531    }
532    tree->dragImage = (TreeDragImage) dragImage;
533    return TCL_OK;
534}
535
536/*
537 *----------------------------------------------------------------------
538 *
539 * TreeDragImage_Free --
540 *
541 *	Free drag-image-related resources when a TreeCtrl is deleted.
542 *
543 * Results:
544 *	None.
545 *
546 * Side effects:
547 *	Memory is deallocated.
548 *
549 *----------------------------------------------------------------------
550 */
551
552void
553TreeDragImage_Free(
554    TreeDragImage dragImage	/* Drag image token. */
555    )
556{
557    DragElem *elem = dragImage->elem;
558
559    while (elem != NULL)
560	elem = DragElem_Free(dragImage, elem);
561#ifdef DRAG_PIXMAP
562    if (dragImage->image != NULL)
563	Tk_FreeImage(dragImage->image);
564    if (dragImage->pixmap != None)
565	Tk_FreePixmap(dragImage->tree->display, dragImage->pixmap);
566#endif /* DRAG_PIXMAP */
567    Tk_FreeConfigOptions((char *) dragImage, dragImage->optionTable,
568	dragImage->tree->tkwin);
569    WFREE(dragImage, TreeDragImage_);
570}
571
572/*
573 *----------------------------------------------------------------------
574 *
575 * TreeDragImage_IsXOR --
576 *
577 *	Return true if the dragimage is being drawn with XOR.
578 *
579 * Results:
580 *	None.
581 *
582 * Side effects:
583 *	None.
584 *
585 *----------------------------------------------------------------------
586 */
587
588int TreeDragImage_IsXOR(TreeDragImage dragImage)
589{
590#if defined(WIN32)
591    return FALSE; /* TRUE on XP, FALSE on Win7 (lots of flickering) */
592#elif defined(MAC_TK_CARBON)
593    return TRUE;
594#elif defined(MAC_TK_COCOA)
595    return FALSE; /* Cocoa doesn't have XOR */
596#else /* X11 */
597    /* With VirtualBox+Ubuntu get extreme lag if TRUE with Compiz. */
598    /* With VirtualBox+Ubuntu get lots of flickering if TRUE without Compiz. */
599    return FALSE;
600#endif
601}
602
603/*
604 *----------------------------------------------------------------------
605 *
606 * TreeDragImage_IsVisible --
607 *
608 *	Return true if the dragimage is being drawn.
609 *
610 * Results:
611 *	None.
612 *
613 * Side effects:
614 *	None.
615 *
616 *----------------------------------------------------------------------
617 */
618
619int TreeDragImage_IsVisible(TreeDragImage dragImage)
620{
621    return dragImage->visible;
622}
623
624/*
625 *----------------------------------------------------------------------
626 *
627 * TreeDragImage_Display --
628 *
629 *	Draw the drag image if it is not already displayed and if
630 *	it's -visible option is TRUE.
631 *
632 * Results:
633 *	None.
634 *
635 * Side effects:
636 *	Stuff is drawn.
637 *
638 *----------------------------------------------------------------------
639 */
640
641void
642TreeDragImage_Display(
643    TreeDragImage dragImage	/* Drag image token. */
644    )
645{
646    TreeCtrl *tree = dragImage->tree;
647
648    if (!dragImage->onScreen && dragImage->visible) {
649	if (TreeDragImage_IsXOR(dragImage) == FALSE) {
650	    dragImage->sx = dragImage->x + dragImage->bounds[0] - tree->xOrigin;
651	    dragImage->sy = dragImage->y + dragImage->bounds[1] - tree->yOrigin;
652	    dragImage->sw = dragImage->bounds[2] - dragImage->bounds[0];
653	    dragImage->sh = dragImage->bounds[3] - dragImage->bounds[1];
654/*	    Tree_InvalidateItemArea(tree, dragImage->sx, dragImage->sy,
655		dragImage->sx + dragImage->sw, dragImage->sy + dragImage->sh);*/
656	    Tree_EventuallyRedraw(tree);
657	} else {
658	    dragImage->sx = 0 - tree->xOrigin;
659	    dragImage->sy = 0 - tree->yOrigin;
660	    TreeDragImage_DrawXOR(dragImage, Tk_WindowId(tree->tkwin), dragImage->sx, dragImage->sy);
661	}
662	dragImage->onScreen = TRUE;
663    }
664}
665
666/*
667 *----------------------------------------------------------------------
668 *
669 * TreeDragImage_Undisplay --
670 *
671 *	Erase the drag image if it is displayed.
672 *
673 * Results:
674 *	None.
675 *
676 * Side effects:
677 *	Stuff is drawn.
678 *
679 *----------------------------------------------------------------------
680 */
681
682void
683TreeDragImage_Undisplay(
684    TreeDragImage dragImage	/* Drag image token. */
685    )
686{
687    TreeCtrl *tree = dragImage->tree;
688
689    if (dragImage->onScreen) {
690	if (TreeDragImage_IsXOR(dragImage) == FALSE) {
691/*	    Tree_InvalidateItemArea(tree, dragImage->sx, dragImage->sy,
692		dragImage->sx + dragImage->sw, dragImage->sy + dragImage->sh);*/
693	    Tree_EventuallyRedraw(tree);
694	} else {
695	    TreeDragImage_DrawXOR(dragImage, Tk_WindowId(tree->tkwin),
696		dragImage->sx, dragImage->sy);
697	}
698	dragImage->onScreen = FALSE;
699    }
700}
701
702/*
703 *----------------------------------------------------------------------
704 *
705 * DragImage_Config --
706 *
707 *	This procedure is called to process an objc/objv list to set
708 *	configuration options for a DragImage.
709 *
710 * Results:
711 *	The return value is a standard Tcl result.  If TCL_ERROR is
712 *	returned, then an error message is left in interp's result.
713 *
714 * Side effects:
715 *	Configuration information, such as text string, colors, font,
716 *	etc. get set for dragImage;  old resources get freed, if there
717 *	were any.  Display changes may occur.
718 *
719 *----------------------------------------------------------------------
720 */
721
722static int
723DragImage_Config(
724    TreeDragImage dragImage,	/* Drag image record. */
725    int objc,			/* Number of arguments. */
726    Tcl_Obj *CONST objv[]	/* Argument values. */
727    )
728{
729    TreeCtrl *tree = dragImage->tree;
730    Tk_SavedOptions savedOptions;
731    int error;
732    Tcl_Obj *errorResult = NULL;
733    int mask;
734
735    for (error = 0; error <= 1; error++) {
736	if (error == 0) {
737	    if (Tk_SetOptions(tree->interp, (char *) dragImage, dragImage->optionTable,
738		objc, objv, tree->tkwin, &savedOptions, &mask) != TCL_OK) {
739		mask = 0;
740		continue;
741	    }
742
743	    /* xxx */
744
745	    Tk_FreeSavedOptions(&savedOptions);
746	    break;
747	} else {
748	    errorResult = Tcl_GetObjResult(tree->interp);
749	    Tcl_IncrRefCount(errorResult);
750	    Tk_RestoreSavedOptions(&savedOptions);
751
752		/* xxx */
753
754	    Tcl_SetObjResult(tree->interp, errorResult);
755	    Tcl_DecrRefCount(errorResult);
756	    return TCL_ERROR;
757	}
758    }
759
760    if (mask & DRAG_CONF_VISIBLE) {
761	TreeDragImage_Undisplay((TreeDragImage) dragImage);
762	TreeDragImage_Display((TreeDragImage) dragImage);
763    }
764
765#ifdef DRAGIMAGE_STYLE
766    if (dragImage->instanceStyle != NULL) {
767	TreeStyle_FreeResources(tree, dragImage->instanceStyle);
768	dragImage->instanceStyle = NULL;
769    }
770    if (dragImage->masterStyle != NULL) {
771	dragImage->instanceStyle = TreeStyle_NewInstance(tree, dragImage->masterStyle);
772	DragImage_UpdateStylePixmap(dragImage);
773    }
774#endif /* DRAGIMAGE_STYLE */
775
776    return TCL_OK;
777}
778
779/*
780 *----------------------------------------------------------------------
781 *
782 * TreeDragImage_DrawXOR --
783 *
784 *	Draw (or erase) the elements that make up the drag image.
785 *
786 * Results:
787 *	None.
788 *
789 * Side effects:
790 *	Stuff is drawn (or erased, since this is XOR drawing).
791 *
792 *----------------------------------------------------------------------
793 */
794
795void TreeDragImage_DrawXOR(TreeDragImage dragImage, Drawable drawable, int x, int y)
796{
797    TreeCtrl *tree = dragImage->tree;
798    DragElem *elem = dragImage->elem;
799    DotState dotState;
800
801/*	if (!dragImage->visible)
802	return; */
803    if (elem == NULL)
804	return;
805
806    TreeDotRect_Setup(tree, drawable, &dotState);
807
808    while (elem != NULL) {
809	TreeDotRect_Draw(&dotState,
810	    x + dragImage->x + elem->x,
811	    y + dragImage->y + elem->y,
812	    elem->width, elem->height);
813	elem = elem->next;
814    }
815
816    TreeDotRect_Restore(&dotState);
817}
818
819/*
820 *----------------------------------------------------------------------
821 *
822 * TreeDragImageCmd --
823 *
824 *	This procedure is invoked to process the [dragimage] widget
825 *	command.  See the user documentation for details on what it
826 *	does.
827 *
828 * Results:
829 *	A standard Tcl result.
830 *
831 * Side effects:
832 *	See the user documentation.
833 *
834 *----------------------------------------------------------------------
835 */
836
837int
838TreeDragImageCmd(
839    ClientData clientData,	/* Widget info. */
840    Tcl_Interp *interp,		/* Current interpreter. */
841    int objc,			/* Number of arguments. */
842    Tcl_Obj *CONST objv[]	/* Argument values. */
843    )
844{
845    TreeCtrl *tree = clientData;
846    TreeDragImage dragImage = tree->dragImage;
847    static CONST char *commandNames[] = { "add", "cget", "clear", "configure",
848	"offset",
849#ifdef DRAGIMAGE_STYLE
850	"stylehotspot",
851#endif /* DRAGIMAGE_STYLE */
852	(char *) NULL };
853    enum { COMMAND_ADD, COMMAND_CGET, COMMAND_CLEAR, COMMAND_CONFIGURE,
854	COMMAND_OFFSET
855#ifdef DRAGIMAGE_STYLE
856	, COMMAND_STYLEHOTSPOT
857#endif /* DRAGIMAGE_STYLE */
858    };
859    int index;
860
861    if (objc < 3) {
862	Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
863	return TCL_ERROR;
864    }
865
866    if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
867	&index) != TCL_OK) {
868	return TCL_ERROR;
869    }
870
871    switch (index) {
872	/* T dragimage add I ?C? ?E ...? */
873	case COMMAND_ADD: {
874	    TreeItem item;
875	    TreeItemColumn itemColumn;
876	    TreeColumn treeColumn;
877	    TreeRectangle rects[128];
878	    DragElem *elem;
879	    int i, count, result = TCL_OK;
880
881	    if (objc < 4) {
882		Tcl_WrongNumArgs(interp, 3, objv, "item ?column? ?element ...?");
883		return TCL_ERROR;
884	    }
885
886	    if (TreeItem_FromObj(tree, objv[3], &item, IFO_NOT_NULL) != TCL_OK)
887		return TCL_ERROR;
888
889	    TreeDragImage_Undisplay(tree->dragImage);
890
891	    /* Every element in every column. */
892	    if (objc == 4) {
893
894		treeColumn = tree->columns;
895		itemColumn = TreeItem_GetFirstColumn(tree, item);
896		while (itemColumn != NULL) {
897		    if (TreeItemColumn_GetStyle(tree, itemColumn) != NULL) {
898			count = TreeItem_GetRects(tree, item, treeColumn,
899			    -1, NULL, rects);
900			if (count == -1) {
901			    result = TCL_ERROR;
902			    goto doneADD;
903			}
904			for (i = 0; i < count; i++) {
905			    elem = DragElem_Alloc(dragImage);
906			    elem->x = rects[i].x;
907			    elem->y = rects[i].y;
908			    elem->width = rects[i].width;
909			    elem->height = rects[i].height;
910			}
911		    }
912		    treeColumn = TreeColumn_Next(treeColumn);
913		    itemColumn = TreeItemColumn_GetNext(tree, itemColumn);
914		}
915
916	    } else {
917
918		if (TreeColumn_FromObj(tree, objv[4], &treeColumn,
919			CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
920		    result = TCL_ERROR;
921		    goto doneADD;
922		}
923
924		/* Every element in a column. */
925		if (objc == 5) {
926		    objc = -1;
927		    objv = NULL;
928
929		/* List of elements in a column. */
930		} else {
931		    objc -= 5;
932		    objv += 5;
933		}
934
935		count = TreeItem_GetRects(tree, item, treeColumn, objc, objv,
936			rects);
937		if (count == -1) {
938		    result = TCL_ERROR;
939		    goto doneADD;
940		}
941
942		for (i = 0; i < count; i++) {
943		    elem = DragElem_Alloc(dragImage);
944		    elem->x = rects[i].x;
945		    elem->y = rects[i].y;
946		    elem->width = rects[i].width;
947		    elem->height = rects[i].height;
948		}
949	    }
950
951doneADD:
952	    dragImage->bounds[0] = 100000;
953	    dragImage->bounds[1] = 100000;
954	    dragImage->bounds[2] = -100000;
955	    dragImage->bounds[3] = -100000;
956	    for (elem = dragImage->elem; elem != NULL; elem = elem->next) {
957		if (elem->x < dragImage->bounds[0])
958		    dragImage->bounds[0] = elem->x;
959		if (elem->y < dragImage->bounds[1])
960		    dragImage->bounds[1] = elem->y;
961		if (elem->x + elem->width > dragImage->bounds[2])
962		    dragImage->bounds[2] = elem->x + elem->width;
963		if (elem->y + elem->height > dragImage->bounds[3])
964		    dragImage->bounds[3] = elem->y + elem->height;
965	    }
966#ifdef DRAG_PIXMAP
967	    UpdatePixmap(dragImage);
968#endif /* DRAG_PIXMAP */
969	    TreeDragImage_Display(tree->dragImage);
970	    return result;
971	}
972
973	/* T dragimage cget option */
974	case COMMAND_CGET: {
975	    Tcl_Obj *resultObjPtr;
976
977	    if (objc != 4) {
978		Tcl_WrongNumArgs(interp, 3, objv, "option");
979		return TCL_ERROR;
980	    }
981	    resultObjPtr = Tk_GetOptionValue(interp, (char *) dragImage,
982		dragImage->optionTable, objv[3], tree->tkwin);
983	    if (resultObjPtr == NULL)
984		return TCL_ERROR;
985	    Tcl_SetObjResult(interp, resultObjPtr);
986	    break;
987	}
988
989	/* T dragimage clear */
990	case COMMAND_CLEAR: {
991	    if (objc != 3) {
992		Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
993		return TCL_ERROR;
994	    }
995	    if (dragImage->elem != NULL) {
996		DragElem *elem = dragImage->elem;
997		TreeDragImage_Undisplay(tree->dragImage);
998/*				if (dragImage->visible)
999		    DragImage_Redraw(dragImage); */
1000		while (elem != NULL)
1001		    elem = DragElem_Free(dragImage, elem);
1002		dragImage->elem = NULL;
1003	    }
1004	    break;
1005	}
1006
1007	/* T dragimage configure ?option? ?value? ?option value ...? */
1008	case COMMAND_CONFIGURE: {
1009	    Tcl_Obj *resultObjPtr;
1010
1011	    if (objc < 3) {
1012		Tcl_WrongNumArgs(interp, 3, objv, "?option? ?value?");
1013		return TCL_ERROR;
1014	    }
1015	    if (objc <= 4) {
1016		resultObjPtr = Tk_GetOptionInfo(interp, (char *) dragImage,
1017		    dragImage->optionTable,
1018		    (objc == 3) ? (Tcl_Obj *) NULL : objv[3],
1019		    tree->tkwin);
1020		if (resultObjPtr == NULL)
1021		    return TCL_ERROR;
1022		Tcl_SetObjResult(interp, resultObjPtr);
1023		break;
1024	    }
1025	    return DragImage_Config(dragImage, objc - 3, objv + 3);
1026	}
1027
1028	/* T dragimage offset ?x y? */
1029	case COMMAND_OFFSET: {
1030	    int x, y;
1031
1032	    if (objc != 3 && objc != 5) {
1033		Tcl_WrongNumArgs(interp, 3, objv, "?x y?");
1034		return TCL_ERROR;
1035	    }
1036	    if (objc == 3) {
1037		FormatResult(interp, "%d %d", dragImage->x, dragImage->y);
1038		break;
1039	    }
1040	    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK)
1041		return TCL_ERROR;
1042	    if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)
1043		return TCL_ERROR;
1044	    TreeDragImage_Undisplay(tree->dragImage);
1045/*			if (dragImage->visible)
1046		DragImage_Redraw(dragImage); */
1047	    dragImage->x = x;
1048	    dragImage->y = y;
1049	    TreeDragImage_Display(tree->dragImage);
1050	    break;
1051	}
1052
1053#ifdef DRAGIMAGE_STYLE
1054	/* T dragimage stylehotspot ?x y? */
1055	case COMMAND_STYLEHOTSPOT: {
1056	    int x, y;
1057
1058	    if (objc != 3 && objc != 5) {
1059		Tcl_WrongNumArgs(interp, 3, objv, "?x y?");
1060		return TCL_ERROR;
1061	    }
1062	    if (objc == 3) {
1063		FormatResult(interp, "%d %d", dragImage->styleX, dragImage->styleY);
1064		break;
1065	    }
1066	    if (Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK)
1067		return TCL_ERROR;
1068	    if (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)
1069		return TCL_ERROR;
1070	    TreeDragImage_Undisplay(tree->dragImage);
1071	    dragImage->styleX = x;
1072	    dragImage->styleY = y;
1073	    TreeDragImage_Display(tree->dragImage);
1074	    break;
1075	}
1076#endif /* DRAGIMAGE_STYLE */
1077    }
1078
1079    return TCL_OK;
1080}
1081