1 /*******************************************************************
2  FILE:		Control.c
3  CONTENTS:	Definitions for structures, methods, and actions of the
4 		Control widget.
5  AUTHOR:	Paul D. Johnston
6  HISTORY:
7  Date		Action
8  ---------	------------------------------------
9  5/23/92	Changed the widget class name so that it is preceded
10 		by 'xc' with the first major word capitalized.
11  5/18/92	Changed Label resource processing so that it copies a " "
12 		to the label pointer instead of "NO LABEL".
13  10/22/91	Created.
14 
15 ********************************************************************/
16 
17 #include <stdio.h>
18 
19 /* Xlib includes */
20 #include <X11/Xlib.h>
21 
22 /* Xt includes */
23 #include <X11/StringDefs.h>
24 #include <X11/IntrinsicP.h>
25 #ifdef X11R3
26 #include <X11/Xmu.h>
27 #else
28 #include <X11/Xmu/Xmu.h>
29 #endif
30 
31 /* Widget includes */
32 #include <X11/Xc/ControlP.h>	/* (includes Control.h also) */
33 
34 
35 /* Macro redefinition for offset. */
36 #define offset(field) XtOffset(ControlWidget, field)
37 
38 /* Constants used in 3D rectangle color generation. */
39 #define MAX_RGB	65280
40 #define SHADE_INTENSITY	((unsigned short)(MAX_RGB / 4))
41 
42 /* Data for the bitmap used for the 3D shading effect. */
43 #define shade_width 2
44 #define shade_height 2
45 static char shade_bits[] = { 0x02, 0x01};
46 
47 /* Declare widget methods */
48 static void Initialize();
49 static void Destroy();
50 static Boolean SetValues();
51 
52 
53 
54 /* Define the widget's resource list */
55 static XtResource resources[] =
56 {
57   {
58     XcNbackground,
59     XtCColor,
60     XtRPixel,
61     sizeof(Pixel),
62     offset(control.background_pixel),
63     XtRString,
64     "gray"
65   },
66   {
67     XcNlabelColor,
68     XtCColor,
69     XtRPixel,
70     sizeof(Pixel),
71     offset(control.label_pixel),
72     XtRString,
73     XtDefaultForeground
74   },
75   {
76     XcNlabel,
77     XcCLabel,
78     XtRString,
79     sizeof(String),
80     offset(control.label),
81     XtRString,
82     "  "
83   },
84   {
85     XtNfont,
86     XtCFont,
87     XtRFontStruct,
88     sizeof(XFontStruct *),
89     offset(control.font),
90     XtRString,
91     XtDefaultFont
92   },
93   {
94     XcNshadeDepth,
95     XcCShadeDepth,
96     XtRInt,
97     sizeof(int),
98     offset(control.shade_depth),
99     XtRString,
100     "3"
101   },
102 
103 };
104 
105 
106 
107 /* Widget Class Record initialization */
108 ControlClassRec controlClassRec =
109 {
110   {
111   /* core_class part */
112     (WidgetClass) &widgetClassRec,		/* superclass */
113     "Control",					/* class_name */
114     sizeof(ControlRec),				/* widget_size */
115     NULL,					/* class_initialize */
116     NULL,					/* class_part_initialize */
117     FALSE,					/* class_inited */
118     Initialize,					/* initialize */
119     NULL,					/* initialize_hook */
120     XtInheritRealize,				/* realize */
121     NULL,					/* actions */
122     0,						/* num_actions */
123     resources,					/* resources */
124     XtNumber(resources),			/* num_resources */
125     NULLQUARK,					/* xrm_class */
126     TRUE,					/* compress_motion */
127     TRUE,					/* compress_exposure */
128     TRUE,					/* compress_enterleave */
129     TRUE,					/* visible_interest */
130     Destroy,					/* destroy */
131     NULL,					/* resize */
132     NULL,					/* expose */
133     SetValues,					/* set_values */
134     NULL,					/* set_values_hook */
135     XtInheritSetValuesAlmost,			/* set_values_almost */
136     NULL,					/* get_values_hook */
137     NULL,					/* accept_focus */
138     XtVersion,					/* version */
139     NULL,					/* callback_private */
140     NULL,					/* tm_table */
141     NULL,					/* query_geometry */
142     NULL,					/* display_accelerator */
143     NULL,					/* extension */
144   },
145   {
146   /* Control class part */
147     0,						/* dummy_field */
148   },
149 };
150 
151 WidgetClass xcControlWidgetClass = (WidgetClass)&controlClassRec;
152 
153 
154 /* Widget method function definitions */
155 
156 /*******************************************************************
157  NAME:		Initialize.
158  DESCRIPTION:
159    This is the initialize method for the Control widget.  It
160 validates user-modifiable instance resources and initializes private
161 widget variables and structures.  This function also creates any server
162 resources (i.e., GCs, fonts, Pixmaps, etc.) used by this widget.  This
163 method is called by Xt when the application calls XtCreateWidget().
164 
165 *******************************************************************/
166 
Initialize(request,new)167 static void Initialize(request, new)
168 ControlWidget request, new;
169 {
170 /* Local variables */
171 XColor bg, exact;
172 Display *display = XtDisplay(new);
173 int scr = DefaultScreen(XtDisplay(new));
174 XGCValues values;
175 XtGCMask mask;
176 static char dash_list[2] = { 1, 1 };
177 
178 #ifdef NICE_SHADES
179 /* These macros are used for calculating the 3D shade colors. */
180 #define COLOR_ADD(member) \
181    if (((int)bg.member + (int)SHADE_INTENSITY) <= MAX_RGB) \
182       new->control.shade1.member = bg.member + SHADE_INTENSITY; \
183    else  new->control.shade1.member = MAX_RGB; \
184 
185 #define COLOR_SUBTRACT(member) \
186    if (((int)bg.member - (int)SHADE_INTENSITY) >= 0) \
187       new->control.shade2.member = bg.member - SHADE_INTENSITY; \
188    else  new->control.shade2.member = 0; \
189 
190 
191 /*
192  * Determine whether or not a backgroud color resource has been set for
193  * this widget, and if so, what its RGB values are. If no color has been
194  * established, set gray as its background by default.
195  */
196    if (new->control.background_pixel)
197    {
198       bg.pixel = new->control.background_pixel;
199       XQueryColor(display, DefaultColormap(display, scr), &bg);
200    }
201    else
202    {
203       if (XAllocNamedColor(display, DefaultColormap(display, scr),
204 		"gray", &bg, &exact))
205 	 new->control.background_pixel = bg.pixel;
206       else
207 	 XtWarning("Control: unable to alloc default bg\n");
208    }
209 
210 /*
211  * Calculate the 3D rectangle shades based on the background color RGB
212  * values, and allocate the corresponding colors.
213  */
214    COLOR_ADD(red);
215    COLOR_ADD(green);
216    COLOR_ADD(blue);
217    COLOR_SUBTRACT(red);
218    COLOR_SUBTRACT(green);
219    COLOR_SUBTRACT(blue);
220    if (XAllocColor(display, DefaultColormap(display, scr),
221 					&(new->control.shade1)) == 0)
222    {
223       XtWarning("Control: unable to alloc shade 1 color\n");
224       new->control.shade1.pixel = WhitePixel(display, DefaultScreen(display));
225    }
226    if (XAllocColor(display, DefaultColormap(display, scr),
227 					&(new->control.shade2)) == 0)
228    {
229       XtWarning("Control: unable to alloc shade 2 color\n");
230       new->control.shade2.pixel = BlackPixel(display, DefaultScreen(display));
231    }
232 #endif	/* NICE_SHADES */
233 
234 
235 /*
236  * Validate public instance variable settings.
237  */
238    if (strlen(new->control.label) == 0)
239    {
240       XtWarning("Control: invalid or missing label string.");
241       strcpy(new->control.label, " ");
242    }
243 
244    if ((new->control.shade_depth < MIN_SHADE_DEPTH) ||
245 			(new->control.shade_depth > MAX_SHADE_DEPTH))
246    {
247       XtWarning("Control: invalid shadeDepth specification.");
248       new->control.shade_depth = 3;
249    }
250 
251 
252 /* Create the GC used by all subclasses for drawing in this widget. */
253    values.graphics_exposures = False;
254    values.foreground = new->control.label_pixel;
255    values.background = new->control.background_pixel;
256    values.font = new->control.font->fid;
257    mask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
258    if ((new->control.gc = XCreateGC(display,
259 			RootWindowOfScreen(XtScreen(new)),
260 			mask, &values)) == NULL)
261       XtWarning("Control: couldn't create GC");
262 
263 /* Set basic line attributes used in drawing this widget. */
264    XSetLineAttributes(display, new->control.gc, 0, LineSolid,
265 						CapButt, JoinRound);
266    XSetDashes(display, new->control.gc, 0, dash_list, 2);
267 
268 
269 }  /* end of Initialize */
270 
271 
272 
273 
274 /*******************************************************************
275  NAME:		SetValues.
276  DESCRIPTION:
277    This is the set_values method for this widget. It validates resource
278 settings set with XtSetValues. If a resource is changed that would
279 require re-drawing the widget, return True.
280 
281 *******************************************************************/
282 
SetValues(cur,req,new)283 static Boolean SetValues(cur, req, new)
284 ControlWidget cur, req, new;
285 {
286 /* Local variables */
287 Boolean do_redisplay = False;
288 
289 
290 /* Validate new resource settings. */
291 /* Check to see if the labelColor has changed. */
292    if (new->control.label_pixel != cur->control.label_pixel)
293       do_redisplay = True;
294 
295 /* Check for valid shadeDepth setting. */
296    if (new->control.shade_depth != cur->control.shade_depth)
297    {
298       do_redisplay = True;
299       if ((new->control.shade_depth < MIN_SHADE_DEPTH) ||
300 			(new->control.shade_depth > MAX_SHADE_DEPTH))
301       {
302          XtWarning("Control: invalid shadeDepth setting.");
303          new->control.shade_depth = 3;
304       }
305    }
306 
307 /* Check for a valid label */
308    if (strcmp(new->control.label, cur->control.label) != 0)
309    {
310       if (strlen(new->control.label) == 0)
311       {
312          XtWarning("Control: invalid Label setting.");
313 	 if (new->control.label != NULL)
314             strcpy(new->control.label, " ");
315       }
316       do_redisplay = True;
317    }
318 
319    return do_redisplay;
320 
321 
322 }  /* end of SetValues */
323 
324 
325 
326 /*******************************************************************
327  NAME:		Destroy.
328  DESCRIPTION:
329    This function is the widget's destroy method.  It simply releases
330 any server resources acquired during the life of the widget.
331 
332 *******************************************************************/
333 
Destroy(w)334 static void Destroy(w)
335 ControlWidget w;
336 {
337 
338    if (w->control.gc)
339       XFreeGC(XtDisplay(w), w->control.gc);
340 
341 }  /* end of Destroy */
342 
343 
344 
345 
346 
347 /* Functions shared by widget subclasses. */
348 
349 /***************************************************************************
350    FUNCTION:	Point_In_Rect.
351    DESCRIPTION:
352    This function simply checks to see if the given point is within the
353 given rectangle and returns TRUE if it is, or FALSE if it isn't within
354 the rectangle.
355 
356 ***************************************************************************/
357 
Point_In_Rect(x,y,rect_x,rect_y,width,height)358 Boolean Point_In_Rect(		/* RETURN: True or False; identifies whether
359 				 * 	   or not the point is in the
360 				 * 	   rectangle.
361 				 */
362 
363 		x,		/* INPUT: The x coordinate of the point */
364 
365 		y,		/* INPUT: The y coordinate of the point */
366 
367 		rect_x,		/* INPUT: The x coordinate of the rectangle */
368 
369 		rect_y,		/* INPUT: The y coordinate of the rectangle */
370 
371 		width,		/* INPUT: The rectangle's width */
372 
373 		height)		/* INPUT: The rectangle's height */
374 
375 int 	x,
376 	y,
377 	rect_x,
378 	rect_y,
379 	width,
380 	height;
381 {
382 /* Check x coordinate first */
383    if ((x < rect_x) || (x > (rect_x + width)))
384       return False;
385 
386 /* Check y coordinate next. If its within the rectangle, return True. */
387    if ((y < rect_y) || (y > (rect_y + height)))
388       return False;
389 
390    return True;
391 
392 } /* end of Point_In_Rect */
393 
394 
395 
396 
397 
398 
399 /*******************************************************************
400  NAME:		Rect3d.
401  DESCRIPTION:
402    This function generates a filled rectangle with a 3D effect in
403 the given drawable.  It uses the widget's background color and the
404 derived shades (calculated in the Initialize method) to produce the
405 3D rectangle.
406 
407 *******************************************************************/
408 
Rect3d(w,display,drawable,gc,x,y,width,height,type)409 void Rect3d(w, display, drawable, gc, x, y, width, height, type)
410 ControlWidget w;
411 Display *display;
412 Drawable drawable;
413 GC gc;
414 int x, y;
415 unsigned int width, height;
416 Type3d type;
417 {
418 int j;
419 unsigned long shade1, shade2;
420 
421 /* Set basic GC attributes used in drawing the 3D rectangle. */
422 #ifdef NICE_SHADES
423    shade1 = w->control.shade1.pixel;
424    shade2 = w->control.shade2.pixel;
425 #else
426    shade1 = WhitePixel(display, DefaultScreen(display));
427    shade2 = BlackPixel(display, DefaultScreen(display));
428    XSetLineAttributes(display, gc, 0, LineOnOffDash, CapButt, JoinRound);
429 #endif
430 
431 /* Draw the shadow lines */
432    for (j = 0; j < w->control.shade_depth; j++)
433    {
434       if (type == RAISED)
435 	 XSetForeground(display, gc, shade1);
436       else
437 	 XSetForeground(display, gc, shade2);
438       XDrawLine(display, drawable, gc, x+j, y+j, x+j, y+height-j);
439       XDrawLine(display, drawable, gc, x+j, y+j, x+width-j, y+j);
440 
441       if (type == RAISED)
442 	 XSetForeground(display, gc, shade2);
443       else
444 	 XSetForeground(display, gc, shade1);
445       XDrawLine(display, drawable, gc, x+j, y+height-j, x+width-j, y+height-j);
446       XDrawLine(display, drawable, gc, x+width-j, y+j, x+width-j, y+height-j);
447    }
448 
449 /* Fill in the background. */
450    XSetFillStyle(display, gc, FillSolid);
451    XSetLineAttributes(display, gc, 0, LineSolid, CapButt, JoinRound);
452    XSetForeground(display, gc, w->control.background_pixel);
453    if ((width > (2 * w->control.shade_depth)) &&
454 			(height > (2 * w->control.shade_depth)))
455       XFillRectangle(display, drawable, gc, x+w->control.shade_depth,
456 	y+w->control.shade_depth, width-(2 * w->control.shade_depth),
457 				height-(2 * w->control.shade_depth));
458 
459 
460 }  /* end of Rect3d */
461 
462 
463 
464 /*******************************************************************
465  NAME:		VarRect3d.
466  DESCRIPTION:
467    This function generates a filled rectangle with a 3D effect in
468 the given drawable.  It uses the widget's background color and the
469 derived shades (calculated in the Initialize method) to produce the
470 3D rectangle.
471 
472 *******************************************************************/
473 
VarRect3d(w,display,drawable,gc,x,y,width,height,type,depth)474 void VarRect3d(w, display, drawable, gc, x, y, width, height, type, depth)
475 ControlWidget w;
476 Display *display;
477 Drawable drawable;
478 GC gc;
479 int x, y;
480 unsigned int width, height;
481 Type3d type;
482 int depth;
483 {
484 int j = ((depth < 1) ? 1 : depth);
485 unsigned long shade1, shade2;
486 
487 /* Set basic line attributes used in drawing the 3D rectangle. */
488 #ifdef NICE_SHADES
489    shade1 = w->control.shade1.pixel;
490    shade2 = w->control.shade2.pixel;
491 #else
492    shade1 = WhitePixel(display, DefaultScreen(display));
493    shade2 = BlackPixel(display, DefaultScreen(display));
494    XSetLineAttributes(display, gc, 0, LineOnOffDash, CapButt, JoinRound);
495 #endif
496 
497 /* Draw the shadow lines */
498    for (j = 0; j < w->control.shade_depth; j++)
499    {
500       if (type == RAISED)
501 	 XSetForeground(display, gc, shade1);
502       else
503 	 XSetForeground(display, gc, shade2);
504       XDrawLine(display, drawable, gc, x+j, y+j, x+j, y+height-j);
505       XDrawLine(display, drawable, gc, x+j, y+j, x+width-j, y+j);
506 
507       if (type == RAISED)
508 	 XSetForeground(display, gc, shade2);
509       else
510 	 XSetForeground(display, gc, shade1);
511       XDrawLine(display, drawable, gc, x+j, y+height-j, x+width-j, y+height-j);
512       XDrawLine(display, drawable, gc, x+width-j, y+j, x+width-j, y+height-j);
513    }
514 
515 /* Fill in the background. */
516    XSetLineAttributes(display, gc, 0, LineSolid, CapButt, JoinRound);
517    XSetFillStyle(display, gc, FillSolid);
518    XSetForeground(display, gc, w->control.background_pixel);
519    if ((width > (2 * depth)) && (height > (2 * depth)))
520       XFillRectangle(display, drawable, gc, x + depth,
521 	y + depth, width-(2 * depth), height-(2 * depth));
522 
523 
524 }  /* end of VarRect3d */
525 
526 
527 
528 /*******************************************************************
529  NAME:		ToLower.
530  DESCRIPTION:
531    Converts a character string to all lowercase for use in the
532 resource string conversion routines.
533 
534 *******************************************************************/
535 
ToLower(source,dest)536 void ToLower(source, dest)
537 char *source, *dest;
538 {
539 /* Local variables */
540 char ch;
541 
542    for (; (ch = *source) != 0; source++, dest++)
543    {
544       if ('A' <= ch && ch <= 'Z')
545 	 *dest = ch - 'A' + 'a';
546       else
547 	 *dest = ch;
548    }
549 
550    *dest = 0;
551 
552 }  /* end of ToLower */
553 
554 
555 
556 
557 /*******************************************************************
558  NAME:		CvtStringToOrient.
559  DESCRIPTION:
560    This function converts resource settings in string form to the
561 XcROrient representation type.
562 
563 *******************************************************************/
564 
CvtStringToOrient(args,num_args,fromVal,toVal)565 void CvtStringToOrient(args, num_args, fromVal, toVal)
566 XrmValuePtr args;		/* unused */
567 Cardinal *num_args;		/* unused */
568 XrmValuePtr fromVal;
569 XrmValuePtr toVal;
570 {
571 /* Local variables */
572 static XcOrient orient;
573 char lowerstring[100];
574 
575 
576 /* Convert the resource string to lower case for quick comparison */
577    ToLower((char *)fromVal->addr, lowerstring);
578 
579 
580 /*
581  * Compare resource string with valid XcOrient strings and assign to
582  * datatype.
583  */
584    if (strcmp(lowerstring, XcEvert) == 0)
585    {
586       orient = XcVert;
587       CvtDone(XcOrient, &orient);
588    }
589    else if (strcmp(lowerstring, XcEhoriz) == 0)
590    {
591       orient = XcHoriz;
592       CvtDone(XcOrient, &orient);
593    }
594 
595 /*
596  * If the string is not valid for this resource type, print a warning
597  * and do not make the conversion.
598  */
599    XtStringConversionWarning(fromVal->addr, "XcOrient");
600    toVal->addr = NULL;
601    toVal->size = 0;
602 
603 }  /* end of CvtStringToOrient */
604 
605 
606 
607 /*******************************************************************
608  NAME:		Arrow3d.
609  DESCRIPTION:
610    This function generates a filled arrow polygon with a 3D effect in
611 the given drawable.  It uses the widget's background color and the
612 derived shades (calculated in Control's Initialize method) to produce the
613 3D effect.
614 
615 *******************************************************************/
616 
Arrow3d(w,display,drawable,gc,bounds,orientation,type)617 void Arrow3d(w, display, drawable, gc, bounds, orientation, type)
618 ControlWidget w;
619 Display *display;
620 Drawable drawable;
621 GC gc;
622 XRectangle *bounds;
623 ArrowType orientation;
624 Type3d type;
625 {
626 /* Local variables */
627 int i, j, adjustment = w->control.shade_depth+1;
628 XPoint points[4];
629 unsigned long shade1, shade2;
630 
631 /* Set basic line attributes used in drawing the 3D arrow. */
632 #ifdef NICE_SHADES
633    shade1 = w->control.shade1.pixel;
634    shade2 = w->control.shade2.pixel;
635 #else
636    shade1 = WhitePixel(display, DefaultScreen(display));
637    shade2 = BlackPixel(display, DefaultScreen(display));
638    XSetLineAttributes(display, gc, 0, LineOnOffDash, CapButt, JoinRound);
639 #endif
640 
641 /* This macro is used to determine the shading color to use. */
642 #define SET_SHADE( sh1, sh2 ) \
643  if (type == RAISED) XSetForeground(display, gc, sh1); \
644  else XSetForeground(display, gc, sh2); \
645 
646 
647 /*
648  * Calculate the arrow vertices based on the bounding rectangle and
649  * orientation.
650  */
651    if (orientation == UP)
652    {
653    /* left corner */
654       points[0].x = bounds->x;
655       points[0].y = bounds->y + bounds->height;
656    /* arrow tip */
657       points[1].x = points[0].x + (bounds->width / 2 + 0.5);
658       points[1].y = bounds->y;
659    /* right corner */
660       points[2].x = points[0].x + bounds->width;
661       points[2].y = points[0].y;
662    }
663    else if (orientation == DOWN)
664    {
665    /* left corner */
666       points[0].x = bounds->x;
667       points[0].y = bounds->y;
668    /* arrow tip */
669       points[1].x = points[0].x + (bounds->width / 2 + 0.5);
670       points[1].y = bounds->y + bounds->height;
671    /* right corner */
672       points[2].x = points[0].x + bounds->width;
673       points[2].y = points[0].y;
674    }
675    else if (orientation == LEFT)
676    {
677    /* left corner */
678       points[0].x = bounds->x + bounds->width;
679       points[0].y = bounds->y + bounds->height;
680    /* arrow tip */
681       points[1].x = bounds->x;
682       points[1].y = bounds->y + (bounds->height / 2 + 0.5);
683    /* right corner */
684       points[2].x = points[0].x;
685       points[2].y = bounds->y;
686    }
687    else if (orientation == RIGHT)
688    {
689    /* left corner */
690       points[0].x = bounds->x;
691       points[0].y = bounds->y;
692    /* arrow tip */
693       points[1].x = bounds->x + bounds->width;
694       points[1].y = bounds->y + (bounds->height / 2 + 0.5);
695    /* right corner */
696       points[2].x = points[0].x;
697       points[2].y = bounds->y + bounds->height;
698    }
699 
700 /* left corner */
701    points[3].x = points[0].x;
702    points[3].y = points[0].y;
703 
704 
705 /* Draw the shadow lines */
706    for (j = 0; j < w->control.shade_depth; j++)
707    {
708       if (orientation == UP)
709       {
710 	 SET_SHADE(shade1, shade2);
711 	 XDrawLine(display, drawable, gc, points[0].x+j, points[0].y-j,
712 				points[1].x, points[1].y+j);
713 	 XDrawLine(display, drawable, gc, points[1].x, points[1].y+j,
714 				points[2].x-j, points[2].y-j);
715 	 SET_SHADE(shade2, shade1);
716 	 XDrawLine(display, drawable, gc, points[2].x-j, points[2].y-j,
717 				points[3].x+j, points[3].y-j);
718       }
719       else if (orientation == DOWN)
720       {
721 	 SET_SHADE(shade2, shade1);
722 	 XDrawLine(display, drawable, gc, points[0].x+j, points[0].y+j,
723 				points[1].x, points[1].y-j);
724 	 XDrawLine(display, drawable, gc, points[1].x, points[1].y-j,
725 				points[2].x-j, points[2].y+j);
726 	 SET_SHADE(shade1, shade2);
727 	 XDrawLine(display, drawable, gc, points[2].x-j, points[3].y+j,
728 				points[3].x+j, points[3].y+j);
729       }
730       else if (orientation == LEFT)
731       {
732 	 SET_SHADE(shade2, shade1);
733 	 XDrawLine(display, drawable, gc, points[0].x-j, points[0].y-j,
734 				points[1].x+j, points[1].y);
735 	 SET_SHADE(shade1, shade2);
736 	 XDrawLine(display, drawable, gc, points[1].x+j, points[1].y,
737 				points[2].x-j, points[2].y+j);
738 	 SET_SHADE(shade2, shade1);
739 	 XDrawLine(display, drawable, gc, points[2].x-j, points[2].y+j,
740 				points[3].x-j, points[3].y-j);
741       }
742       else if (orientation == RIGHT)
743       {
744 	 SET_SHADE(shade1, shade2);
745 	 XDrawLine(display, drawable, gc, points[0].x+j, points[0].y+j,
746 				points[1].x-j, points[1].y);
747 	 SET_SHADE(shade2, shade1);
748 	 XDrawLine(display, drawable, gc, points[1].x-j, points[1].y,
749 				points[2].x+j, points[2].y-j);
750 	 SET_SHADE(shade1, shade2);
751 	 XDrawLine(display, drawable, gc, points[2].x+j, points[2].y-j,
752 				points[3].x+j, points[3].y+j);
753       }
754    }
755 
756    XSetLineAttributes(display, gc, 0, LineSolid, CapButt, JoinRound);
757    XSetFillStyle(display, gc, FillSolid);
758 
759 }  /* end of Arrow3d */
760 
761 
762 /* end of Control.c */
763 
764