|
|
1.1 root 1: /* $Header: ButtonBox.c,v 1.1 87/09/11 07:57:18 toddb Exp $ */
2: #ifndef lint
3: static char *sccsid = "@(#)ButtonBox.c 1.14 2/26/87";
4: #endif lint
5:
6: /*
7: * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
8: *
9: * All Rights Reserved
10: *
11: * Permission to use, copy, modify, and distribute this software and its
12: * documentation for any purpose and without fee is hereby granted,
13: * provided that the above copyright notice appear in all copies and that
14: * both that copyright notice and this permission notice appear in
15: * supporting documentation, and that the name of Digital Equipment
16: * Corporation not be used in advertising or publicity pertaining to
17: * distribution of the software without specific, written prior permission.
18: *
19: *
20: * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
21: * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
22: * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
23: * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
24: * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25: * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
26: * SOFTWARE.
27: */
28:
29:
30: /*
31: * ButtonBox.c - Button box composite widget
32: *
33: * Author: haynes
34: * Digital Equipment Corporation
35: * Western Research Laboratory
36: * Date: Sat Jan 24 1987
37: */
38:
39: #include "Xlib.h"
40: #include "Intrinsic.h"
41: #include "ButtonBox.h"
42: #include "Atoms.h"
43:
44: #define MAXHEIGHT ((1 << 31)-1)
45: #define MAXWIDTH ((1 << 31)-1)
46:
47: #define max(x, y) (((x) > (y)) ? (x) : (y))
48: #define min(x, y) (((x) < (y)) ? (x) : (y))
49: #define assignmax(x, y) if ((y) > (x)) x = (y)
50: #define assignmin(x, y) if ((y) < (x)) x = (y)
51:
52: /****************************************************************
53: *
54: * Private Types
55: *
56: ****************************************************************/
57:
58: typedef struct _WidgetDataRec {
59: Display *dpy; /* widget display connection */
60: Window w; /* widget window */
61: Position x, y; /* widget window location */
62: Dimension width, height; /* widget window width and height */
63: Dimension borderWidth; /* widget window border width */
64: Pixel borderpixel; /* widget window border color */
65: Pixel bgpixel; /* widget window background color */
66: Dimension hspace, vspace; /* spacing between buttons */
67: int numbuttons; /* number of managed buttons */
68: WindowLugPtr buttons; /* list of managed buttons */
69: Boolean allowresize; /* reconfigure on notify */
70: } WidgetDataRec, *WidgetData;
71:
72:
73: /****************************************************************
74: *
75: * Private Data
76: *
77: ****************************************************************/
78:
79: static int index; /* index into button list to add/delete window */
80:
81: static WidgetDataRec globaldata;
82: static WidgetDataRec globalinit = {
83: NULL, /* Display dpy; */
84: NULL, /* Window w; */
85: 0, 0, /* Position x, y; */
86: 0, 0, /* Dimension width, height; */
87: 1, /* Dimension borderWidth; */
88: NULL, /* Pixmap borderpixel; */
89: NULL, /* Pixmap bgpixel; */
90: 4, 4, /* int hspace, vspace; */
91: 0, /* int numbuttons; */
92: NULL, /* WindowLugPtr buttons; */
93: TRUE /* resizable */
94: };
95:
96: static Resource resources[] = {
97: {XtNwindow, XtCWindow, XrmRWindow, sizeof(Window),
98: (caddr_t)&globaldata.w, (caddr_t)NULL},
99: {XtNx, XtCX, XrmRInt, sizeof(int),
100: (caddr_t)&globaldata.x, (caddr_t)NULL},
101: {XtNy, XtCY, XrmRInt, sizeof(int),
102: (caddr_t)&globaldata.y, (caddr_t)NULL},
103: {XtNwidth, XtCWidth, XrmRInt, sizeof(int),
104: (caddr_t)&globaldata.width, (caddr_t)NULL},
105: {XtNheight, XtCHeight, XrmRInt, sizeof(int),
106: (caddr_t)&globaldata.height, (caddr_t)NULL},
107: {XtNborderWidth, XtCBorderWidth, XrmRInt, sizeof(int),
108: (caddr_t)&globaldata.borderWidth, (caddr_t)NULL},
109: {XtNborder, XtCColor, XrmRPixel, sizeof(int),
110: (caddr_t)&globaldata.borderpixel, (caddr_t)&XtDefaultFGPixel},
111: {XtNbackground, XtCColor, XrmRPixel, sizeof(int),
112: (caddr_t)&globaldata.bgpixel, (caddr_t)&XtDefaultBGPixel},
113: {XtNhSpace, XtCHSpace, XrmRInt, sizeof(int),
114: (caddr_t)&globaldata.hspace, (caddr_t)NULL},
115: {XtNvSpace, XtCVSpace, XrmRInt, sizeof(int),
116: (caddr_t)&globaldata.vspace, (caddr_t)NULL},
117: {XtNresizable, XtCBoolean, XrmRBoolean, sizeof(Boolean),
118: (caddr_t)&globaldata.allowresize, (caddr_t) NULL}
119: };
120:
121:
122: static int indexinit = -1;
123: static Resource parmResources[] = {
124: {XtNindex, XtCIndex, XrmRInt,
125: sizeof(int), (caddr_t)&index, (caddr_t)&indexinit},
126: };
127:
128: /****************************************************************
129: *
130: * Private Routines
131: *
132: ****************************************************************/
133:
134: static Boolean initialized = FALSE;
135:
136: static XContext widgetContext;
137:
138: static void ButtonBoxInitialize()
139: {
140: if (initialized)
141: return;
142: initialized = TRUE;
143:
144: widgetContext = XUniqueContext();
145: }
146:
147: /*
148: * Given a display and window, get the widget data.
149: */
150:
151: static WidgetData DataFromWindow(dpy, window)
152: Display *dpy;
153: Window window;
154: {
155: WidgetData result;
156: if (XFindContext(dpy, window, widgetContext, (caddr_t *)&result))
157: return NULL;
158: return result;
159: }
160:
161: /*
162: *
163: * Do a layout, either actually assigning positions, or just calculating size.
164: * Returns 1 on success; 0 if it couldn't make things fit.
165: *
166: */
167:
168: static int DoLayout(data, width, height, box, position)
169: WidgetData data;
170: Dimension width, height;
171: WindowBox *box; /* RETURN */
172: Boolean position; /* actually reposition the windows? */
173: {
174: int i;
175: int w, h, lw, lh, count;
176:
177: w = data->hspace;
178: if ((w > width) && !position) return (0);
179: h = data->vspace;
180: if ((h > height) && !position) return (0);
181:
182: for (i = 0; i < data->numbuttons; ) {
183: count = 0;
184: lh = 0;
185: lw = data->hspace;
186: /* compute one line worth */
187: for ( ; (i < data->numbuttons); i++) {
188: int tw, th;
189: tw = lw
190: + data->buttons[i].wb.width
191: + 2*data->buttons[i].wb.borderWidth
192: + data->hspace;
193: if (tw > width) {
194: if (!position) break;
195: if (count > 0) break;
196: }
197: if (position &&
198: (lw != data->buttons[i].wb.x || h != data->buttons[i].wb.y)) {
199: XMoveWindow(data->dpy, data->buttons[i].w, lw, h);
200: data->buttons[i].wb.x = lw;
201: data->buttons[i].wb.y = h;
202: (void) XtSendConfigureNotify(data->dpy, data->buttons[i].w,
203: &(data->buttons[i].wb));
204: }
205: lw = tw;
206: th = data->buttons[i].wb.height + 2*data->buttons[i].wb.borderWidth;
207: assignmax(lh, th);
208: count++;
209: }
210: if (count == 0) return (0);
211: assignmax(w, lw);
212: h += lh+data->vspace;
213: if ((h > height) && !position) return (0);
214: }
215:
216: if (box != NULL) {
217: box->width = w;
218: box->height = h;
219: }
220: return (1);
221: }
222:
223: /*
224: *
225: * Calculate preferred size, given constraining box
226: *
227: */
228:
229: static int PreferredSize(data, width, height, box)
230: WidgetData data;
231: Dimension width, height;
232: WindowBox *box; /* RETURN */
233: {
234: return DoLayout(data, width, height, box, FALSE);
235: }
236:
237: /*
238: *
239: * Compute the layout of the button box
240: *
241: */
242:
243: static void Layout(data)
244: WidgetData data;
245: {
246: (void) DoLayout(data, data->width, data->height, (WindowBox *)NULL, TRUE);
247: }
248:
249: /*
250: *
251: * Main buttonbox event handler
252: *
253: */
254:
255: static XtEventReturnCode EventHandler(event, eventdata)
256: XEvent *event;
257: caddr_t eventdata;
258: {
259: WidgetData data = (WidgetData) eventdata;
260: void Destroy();
261:
262: switch (event->type) {
263: case ConfigureNotify: {
264: data->x = event->xconfigure.x;
265: data->y = event->xconfigure.y;
266: data->borderWidth = event->xconfigure.border_width;
267: if (data->allowresize) {
268: if (data->height != event->xconfigure.height ||
269: data->width != event->xconfigure.width) {
270: data->height = event->xconfigure.height;
271: data->width = event->xconfigure.width;
272: (void) TryLayout(data, data->width, MAXHEIGHT);
273: Layout(data);
274: }
275: }
276: break;
277: }
278:
279: case DestroyNotify: Destroy(data); break;
280: }
281:
282: return (XteventHandled);
283: }
284:
285:
286: /*
287: *
288: * Handle events on subwidgets
289: *
290: */
291:
292: static XtEventReturnCode SubEventHandler(event, eventdata)
293: XEvent *event;
294: caddr_t eventdata;
295: {
296: WidgetData data = (WidgetData) eventdata;
297: int i;
298: if (event->type == DestroyNotify) {
299: for (i=0 ; i<data->numbuttons; i++)
300: if (data->buttons[i].w == event->xany.window) {
301: DeleteButton(data, i);
302: (void) TryNewLayout(data);
303: Layout(data);
304: break;
305: }
306: }
307: }
308:
309: /*
310: *
311: * Destroy the buttonbox
312: *
313: */
314:
315: static void Destroy(data)
316: WidgetData data;
317: {
318: int i;
319: /* send destroy messages to all my subwindows */
320: for (i=0; i < data->numbuttons; i++)
321: (void) XtSendDestroyNotify(data->dpy, data->buttons[i].w);
322: XtFree((char *) data->buttons);
323:
324: XtClearEventHandlers(data->dpy, data->w);
325: (void) XDeleteContext(data->dpy, data->w, widgetContext);
326: XtFree ((char *) data);
327: }
328:
329: /*
330: *
331: * Find Button
332: *
333: */
334:
335: static WindowLugPtr FindButton(data, w)
336: WidgetData data;
337: Window w;
338: {
339: int i;
340:
341: for (i=0; i<data->numbuttons; i++)
342: if (data->buttons[i].w == w) return &(data->buttons[i]);
343:
344: return NULL;
345: }
346:
347: /*
348: *
349: * Try to do a new layout within a particular width and height
350: *
351: */
352:
353: static int TryLayout(data, width, height)
354: WidgetData data;
355: Dimension width, height;
356: {
357: WindowBox box, rbox;
358:
359: if (!PreferredSize(data, width, height, &box)) return (0);
360:
361: /* let's see if our parent will go for it. */
362: switch (XtMakeGeometryRequest(
363: data->dpy, data->w, XtgeometryResize, &box, &rbox)) {
364:
365: case XtgeometryNoManager:
366: XResizeWindow(data->dpy, data->w, box.width, box.height);
367: /* fall through to "yes" */
368:
369: case XtgeometryYes:
370: data->width = box.width;
371: data->height = box.height;
372: return (1);
373:
374:
375: case XtgeometryNo:
376: return (0);
377:
378:
379: case XtgeometryAlmost:
380: if (! PreferredSize(data, rbox.width, rbox.height,
381: (WindowBox *) NULL))
382: return (0);
383: box = rbox;
384: (void) XtMakeGeometryRequest(data->dpy, data->w, XtgeometryResize, &box, &rbox);
385: data->width = box.width;
386: data->height = box.height;
387: return (1);
388:
389: }
390: return (0);
391: }
392:
393: /*
394: *
395: * Try to do a new layout
396: *
397: */
398:
399: static int TryNewLayout(data)
400: WidgetData data;
401: {
402: if (TryLayout(data, data->width, data->height)) return (1);
403: if (TryLayout(data, data->width, MAXHEIGHT)) return (1);
404: if (TryLayout(data, MAXWIDTH, MAXHEIGHT)) return(1);
405: return (0);
406: }
407:
408: /*
409: *
410: * Button Resize Request
411: *
412: */
413:
414: /*ARGSUSED*/
415: static XtGeometryReturnCode ResizeButtonRequest(data, w, reqBox, replBox)
416: WidgetData data;
417: Window w;
418: WindowBox *reqBox;
419: WindowBox *replBox; /* RETURN */
420:
421: {
422: WindowLugPtr b;
423: WindowLug oldb;
424:
425: b = FindButton(data, w);
426: if (b == NULL) return (XtgeometryNo);
427: oldb = *b;
428: b->wb = *reqBox;
429: b->wb.borderWidth = oldb.wb.borderWidth;
430: /* HACK -- maybe we need a "change borderWidth" command? */
431:
432: if ((reqBox->width <= oldb.wb.width && reqBox->height <= oldb.wb.height) ||
433: /* making the button smaller always works */
434: (PreferredSize(data, data->width, data->height, (WindowBox *) NULL)) ||
435: /* will it fit inside old dims? */
436: (TryNewLayout(data)))
437: /* can we make it fit at all? */
438: {
439: XResizeWindow(data->dpy, b->w, b->wb.width, b->wb.height);
440: (void) XtSendConfigureNotify(data->dpy, b->w, &(b->wb));
441: Layout(data);
442: return XtgeometryYes;
443: }
444: *b = oldb;
445: return (XtgeometryNo);
446: }
447:
448: /*
449: *
450: * Button Box Geometry Manager
451: *
452: */
453:
454: static XtGeometryReturnCode ButtonBoxGeometryManager(
455: dpy, w, req, reqBox, replBox)
456: Display *dpy;
457: Window w;
458: XtGeometryRequest req;
459: WindowBox *reqBox;
460: WindowBox *replBox; /* RETURN */
461: {
462: WidgetData data;
463:
464: if (XFindContext(dpy, w, widgetContext, (caddr_t *)&data) == XCNOENT)
465: return (XtgeometryYes);
466: /* requests: move, resize, top, bottom */
467: switch (req) {
468: case XtgeometryTop : return (XtgeometryYes);
469: case XtgeometryBottom : return (XtgeometryYes);
470: case XtgeometryMove : return (XtgeometryNo);
471: case XtgeometryResize :
472: return (ResizeButtonRequest(data, w, reqBox, replBox));
473: }
474: return (XtgeometryNo);
475: }
476:
477: static XtStatus AddButton(data, w, index)
478: WidgetData data;
479: Window w;
480: int index;
481: {
482: int i;
483: WindowLug b;
484:
485: b.w = w;
486: if (XtGetWindowSize(data->dpy, w, &b.wb.width, &b.wb.height,
487: &b.wb.borderWidth))
488: return (0);
489:
490: if (FindButton(data, b.w) != NULL) return (0);
491:
492: data->numbuttons++;
493: if (data->numbuttons == 1) {
494: data->buttons = (WindowLugPtr) XtCalloc(1, sizeof(WindowLug));
495: } else {
496: data->buttons = (WindowLugPtr) XtRealloc(
497: (char *)data->buttons,
498: (unsigned) data->numbuttons*sizeof(WindowLug));
499: }
500:
501: for (i=data->numbuttons-1; i > index; i--)
502: data->buttons[i] = data->buttons[i-1];
503:
504: b.wb.x = b.wb.y = -99;
505: data->buttons[index] = b;
506: (void) XSaveContext(data->dpy, b.w, widgetContext, (caddr_t)data);
507: (void) XtSetGeometryHandler(
508: data->dpy, b.w, (XtGeometryHandler) ButtonBoxGeometryManager);
509: XtSetEventHandler(data->dpy, b.w, SubEventHandler, StructureNotifyMask,
510: (caddr_t)data);
511:
512: return(1);
513: }
514:
515: static DeleteButton(data, index)
516: WidgetData data;
517: int index;
518: {
519: (void) XDeleteContext(data->dpy, data->buttons[index].w, widgetContext);
520: (void) XtClearGeometryHandler(data->dpy, data->buttons[index].w);
521: XtDeleteEventHandler(data->dpy, data->buttons[index].w, SubEventHandler);
522:
523: for (index++; index<data->numbuttons; index++)
524: data->buttons[index-1] = data->buttons[index];
525:
526: data->numbuttons--;
527: data->buttons = (WindowLugPtr) XtRealloc(
528: (char *)data->buttons,
529: (unsigned) data->numbuttons*sizeof(WindowLug));
530: }
531:
532: /****************************************************************
533: *
534: * Public Routines
535: *
536: ****************************************************************/
537:
538: Window XtButtonBoxCreate(dpy, parent, args, argCount)
539: Display *dpy;
540: Window parent;
541: ArgList args;
542: int argCount;
543: {
544: WidgetData data;
545: XrmNameList names;
546: XrmClassList classes;
547: unsigned long valuemask;
548: XSetWindowAttributes wvals;
549: Boolean found;
550:
551: if (!initialized) ButtonBoxInitialize();
552:
553: data = (WidgetData) XtMalloc(sizeof(WidgetDataRec));
554:
555: globaldata = globalinit;
556: globaldata.dpy = dpy;
557: XtGetResources(dpy, resources, XtNumber(resources), args, argCount, parent,
558: "buttonBox", "ButtonBox", &names, &classes);
559: *data = globaldata;
560: if (data->width == 0)
561: data->width = ((data->hspace != 0) ? data->hspace : 10);
562: if (data->height == 0)
563: data->height = ((data->vspace != 0) ? data->vspace : 10);
564:
565: wvals.background_pixel = data->bgpixel;
566: wvals.border_pixel = data->borderpixel;
567: wvals.bit_gravity = NorthWestGravity;
568: valuemask = CWBackPixel | CWBorderPixel | CWBitGravity;
569:
570: if (data->w != NULL) {
571: Drawable root;
572: Position x, y;
573: unsigned int depth;
574:
575: /* set global data from window parameters */
576: if (!XGetGeometry(data->dpy, data->w, &root, &x, &y,
577: &(data->width), &(data->height),
578: &(data->borderWidth), &depth)) {
579: data->w = NULL;
580: } else {
581: /* set window according to args */
582: XChangeWindowAttributes(data->dpy, data->w, valuemask, &wvals);
583: }
584: }
585: if (data->w == NULL) {
586: data->w = XCreateWindow(data->dpy, parent, data->x, data->y,
587: data->width, data->height, data->borderWidth,
588: 0, InputOutput, (Visual *)CopyFromParent,
589: valuemask, &wvals);
590: }
591:
592: XtSetNameAndClass(data->dpy, data->w, names, classes);
593: XrmFreeNameList(names);
594: XrmFreeClassList(classes);
595:
596: /* set handler for message and destroy events */
597: XtSetEventHandler(dpy,
598: data->w, (XtEventHandler)EventHandler, StructureNotifyMask,
599: (caddr_t) data);
600:
601: (void) XSaveContext(data->dpy, data->w, widgetContext, (caddr_t)data);
602:
603: /* batch add initial buttons */
604: if (argCount) {
605: found = FALSE;
606: for ( ; --argCount >= 0; args++) {
607: if (XrmAtomsEqual(args->name, XtNbutton)) {
608: (void) AddButton(
609: data, (Window)args->value, data->numbuttons);
610: found = TRUE;
611: }
612: }
613: if (found) {
614: (void) TryNewLayout(data);
615: Layout(data);
616: XMapSubwindows(data->dpy, data->w);
617: }
618: }
619:
620: return (data->w);
621: }
622:
623: XtStatus XtButtonBoxAddButton(dpy, parent, args, argCount)
624: Display *dpy;
625: Window parent;
626: ArgList args;
627: int argCount;
628: {
629: WidgetData data;
630:
631: (void) XFindContext(dpy, parent, widgetContext, (caddr_t *)&data);
632: index = -1;
633: XtSetValues(parmResources, XtNumber(parmResources), args, argCount);
634: if ((index < -1) || (index > data->numbuttons)) return (0);
635:
636: /* batch add buttons */
637: if (argCount) {
638: for ( ; --argCount >= 0; args++) {
639: if (XrmAtomsEqual(args->name, XtNwindow)) {
640: (void) AddButton(
641: data,
642: (Window)args->value,
643: ((index < 0) ? data->numbuttons : index));
644: if (index >= 0) index++;
645: }
646: }
647: (void) TryNewLayout(data);
648: Layout(data);
649: XMapSubwindows(data->dpy, data->w);
650: }
651: /*
652: if (!AddButton(data, parms.w, index)) return(0);
653:
654: if (!TryNewLayout(data)) return(0);
655: Layout(data);
656: */
657: return (1);
658: }
659:
660: XtStatus XtButtonBoxDeleteButton(dpy, parent, args, argCount)
661: Display *dpy;
662: Window parent;
663: ArgList args;
664: int argCount;
665: {
666: WidgetData data;
667: Boolean foundOne = FALSE;
668: int i;
669:
670: (void) XFindContext(dpy, parent, widgetContext, (caddr_t *) &data);
671:
672: index = -1;
673: XtSetValues(parmResources, XtNumber(parmResources), args, argCount);
674: if ((index < -1) || (index >= data->numbuttons)) return(0);
675:
676: if (index >= 0) {
677: DeleteButton(data, index);
678: foundOne = TRUE;
679: } else if (argCount) {
680: for ( ; --argCount >= 0; args++) {
681: if (XrmAtomsEqual(args->name, XtNwindow)) {
682: for (i=0; i<data->numbuttons; i++) {
683: if (data->buttons[i].w == (Window)args->value) {
684: DeleteButton(data, i);
685: foundOne = TRUE;
686: break;
687: }
688: }
689: }
690: }
691: }
692:
693: if (! foundOne) return(0);
694:
695: (void) TryNewLayout(data); /* We may want to SHRINK things! */
696: Layout(data);
697: return (1);
698: }
699:
700: /*
701: *
702: * Get Attributes
703: *
704: */
705:
706: void XtButtonBoxGetValues (dpy, window, args, argCount)
707: Display *dpy;
708: Window window;
709: ArgList args;
710: int argCount;
711: {
712: WidgetData data;
713: data = DataFromWindow(dpy, window);
714: if (data == NULL) return;
715: globaldata = *data;
716: XtGetValues(resources, XtNumber(resources), args, argCount);
717: }
718:
719: /*
720: *
721: * Set Attributes
722: *
723: */
724:
725: void XtButtonBoxSetValues (dpy, window, args, argCount)
726: Display *dpy;
727: Window window;
728: ArgList args;
729: int argCount;
730: {
731: WidgetData data;
732: data = DataFromWindow(dpy, window);
733: if (data == NULL) return;
734: globaldata = *data;
735: XtSetValues(resources, XtNumber(resources), args, argCount);
736: *data = globaldata;
737: }
738:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.