1/*
2 *
3Copyright 1990, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 *
25 * Author:  Jim Fulton, MIT X Consortium
26 *
27 * This widget is a trivial clipping widget.  It is typically used with a
28 * panner or scrollbar to navigate.
29 */
30
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34#include <X11/IntrinsicP.h>
35#include <X11/StringDefs.h>
36#include <X11/Xmu/Misc.h>
37#include <X11/Xaw/PortholeP.h>
38#include <X11/Xaw/XawInit.h>
39#include "Private.h"
40
41/*
42 * Class Methods
43 */
44static void XawPortholeChangeManaged(Widget);
45static XtGeometryResult XawPortholeGeometryManager(Widget, XtWidgetGeometry*,
46						   XtWidgetGeometry*);
47static XtGeometryResult XawPortholeQueryGeometry(Widget, XtWidgetGeometry*,
48						 XtWidgetGeometry*);
49static void XawPortholeRealize(Widget, Mask*, XSetWindowAttributes*);
50static void XawPortholeResize(Widget);
51
52/*
53 * Prototypes
54 */
55static Widget find_child(PortholeWidget);
56static void layout_child(PortholeWidget, Widget, XtWidgetGeometry*,
57			 Position*, Position*, Dimension*, Dimension*);
58static void SendReport(PortholeWidget, unsigned int);
59
60/*
61 * Initialization
62 */
63#define offset(field)	XtOffsetOf(PortholeRec, porthole.field)
64static XtResource resources[] = {
65  {
66    XtNreportCallback,
67    XtCReportCallback,
68    XtRCallback,
69    sizeof(XtPointer),
70    offset(report_callbacks),
71    XtRCallback,
72    NULL
73  },
74};
75#undef offset
76
77#define Superclass	(&compositeClassRec)
78PortholeClassRec portholeClassRec = {
79  /* core */
80  {
81    (WidgetClass)Superclass,		/* superclass */
82    "Porthole",				/* class_name */
83    sizeof(PortholeRec),		/* widget_size */
84    XawInitializeWidgetSet,		/* class_initialize */
85    NULL,				/* class_part_initialize */
86    False,				/* class_inited */
87    NULL,				/* initialize */
88    NULL,				/* initialize_hook */
89    XawPortholeRealize,			/* realize */
90    NULL,				/* actions */
91    0,					/* num_actions */
92    resources,				/* resources */
93    XtNumber(resources),		/* num_resources */
94    NULLQUARK,				/* xrm_class */
95    True,				/* compress_motion */
96    True,				/* compress_exposure */
97    True,				/* compress_enterleave */
98    False,				/* visible_interest */
99    NULL,				/* destroy */
100    XawPortholeResize,			/* resize */
101    NULL,				/* expose */
102    NULL,				/* set_values */
103    NULL,				/* set_values_hook */
104    XtInheritSetValuesAlmost,		/* set_values_almost */
105    NULL,				/* get_values_hook */
106    NULL,				/* accept_focus */
107    XtVersion,				/* version */
108    NULL,				/* callback_private */
109    NULL,				/* tm_table */
110    XawPortholeQueryGeometry,		/* query_geometry */
111    XtInheritDisplayAccelerator,	/* display_accelerator */
112    NULL,				/* extension */
113  },
114  /* composite */
115  {
116    XawPortholeGeometryManager,		/* geometry_manager */
117    XawPortholeChangeManaged,		/* change_managed */
118    XtInheritInsertChild,		/* insert_child */
119    XtInheritDeleteChild,		/* delete_child */
120    NULL,				/* extension */
121  },
122  { /* porthole */
123    NULL,				/* extension */
124  },
125};
126
127WidgetClass portholeWidgetClass = (WidgetClass)&portholeClassRec;
128
129/*
130 * Implementation
131 */
132static Widget
133find_child(PortholeWidget pw)
134{
135    Widget *children;
136    unsigned int i;
137
138    /*
139     * Find the managed child on which we should operate.  Ignore multiple
140     * managed children
141     */
142    for (i = 0, children = pw->composite.children;
143	 i < pw->composite.num_children; i++, children++)
144	if (XtIsManaged(*children))
145	    return (*children);
146
147    return (NULL);
148}
149
150static void
151SendReport(PortholeWidget pw, unsigned int changed)
152{
153    Widget child = find_child(pw);
154
155    if (pw->porthole.report_callbacks && child) {
156	XawPannerReport prep = {
157	    .changed = changed,
158	    .slider_x = (Position)(-XtX(child)), /* porthole is "inner" */
159	    .slider_y = (Position)(-XtY(child)), /* child is outer since it is larger */
160	    .slider_width = XtWidth(pw),
161	    .slider_height = XtHeight(pw),
162	    .canvas_width = XtWidth(child),
163	    .canvas_height = XtHeight(child)
164	};
165	XtCallCallbackList((Widget)pw, pw->porthole.report_callbacks,
166			   (XtPointer)&prep);
167    }
168}
169
170static void
171layout_child(PortholeWidget pw, Widget child, XtWidgetGeometry *geomp,
172	     Position *xp, Position *yp, Dimension *widthp, Dimension *heightp)
173{
174    Position minx, miny;
175
176    *xp = XtX(child);			/* default to current values */
177    *yp = XtY(child);
178    *widthp = XtWidth(child);
179    *heightp = XtHeight(child);
180    if (geomp) {			/* mix in any requested changes */
181	if (geomp->request_mode & CWX)
182	    *xp = geomp->x;
183	if (geomp->request_mode & CWY)
184	    *yp = geomp->y;
185	if (geomp->request_mode & CWWidth)
186	    *widthp = geomp->width;
187	if (geomp->request_mode & CWHeight)
188	    *heightp = geomp->height;
189    }
190
191    /*
192     * Make sure that the child is at least as large as the porthole; there
193     * is no maximum size
194     */
195    if (*widthp < XtWidth(pw)) *widthp = XtWidth(pw);
196    if (*heightp < XtHeight(pw)) *heightp = XtHeight(pw);
197
198    /*
199     * Make sure that the child is still on the screen.  Note that this must
200     * be done *after* the size computation so that we know where to put it
201     */
202    minx = (Position)(XtWidth(pw) - *widthp);
203    miny = (Position)(XtHeight(pw) - *heightp);
204
205    if (*xp < minx)
206	*xp = minx;
207    if (*yp < miny)
208	*yp = miny;
209
210    if (*xp > 0)
211	*xp = 0;
212    if (*yp > 0)
213	*yp = 0;
214}
215
216static void
217XawPortholeRealize(Widget gw, Mask *valueMask, XSetWindowAttributes *attr)
218{
219    attr->bit_gravity = NorthWestGravity;
220    *valueMask |= CWBitGravity;
221
222    if (XtWidth(gw) < 1)
223	XtWidth(gw) = 1;
224    if (XtHeight(gw) < 1)
225	XtHeight(gw) = 1;
226    (*portholeWidgetClass->core_class.superclass->core_class.realize)
227	(gw, valueMask, attr);
228}
229
230static void
231XawPortholeResize(Widget gw)
232{
233    PortholeWidget pw = (PortholeWidget)gw;
234    Widget child = find_child(pw);
235
236    /*
237     * If we have a child, we need to make sure that it is at least as big
238     * as we are and in the right place
239     */
240    if (child) {
241	Position x, y;
242	Dimension width, height;
243
244	layout_child(pw, child, NULL, &x, &y, &width, &height);
245	XtConfigureWidget(child, x, y, width, height, 0);
246    }
247
248    SendReport(pw, XawPRCanvasWidth | XawPRCanvasHeight);
249}
250
251static XtGeometryResult
252XawPortholeQueryGeometry(Widget gw, XtWidgetGeometry *intended,
253			 XtWidgetGeometry *preferred)
254{
255    PortholeWidget pw = (PortholeWidget)gw;
256    Widget child = find_child(pw);
257
258    if (child) {
259#define SIZEONLY (CWWidth | CWHeight)
260	preferred->request_mode = SIZEONLY;
261	preferred->width = XtWidth(child);
262	preferred->height = XtHeight(child);
263
264	if ((intended->request_mode & SIZEONLY) == SIZEONLY &&
265	    intended->width == preferred->width &&
266	    intended->height == preferred->height)
267	    return (XtGeometryYes);
268	else if (preferred->width == XtWidth(pw) &&
269		 preferred->height == XtHeight(pw))
270	    return (XtGeometryNo);
271
272	return (XtGeometryAlmost);
273#undef SIZEONLY
274    }
275
276    return (XtGeometryNo);
277}
278
279static XtGeometryResult
280XawPortholeGeometryManager(Widget w, XtWidgetGeometry *req,
281			   XtWidgetGeometry *reply)
282{
283    PortholeWidget pw = (PortholeWidget) w->core.parent;
284    Widget child = find_child(pw);
285    Bool okay = True;
286
287    if (child != w)
288	return (XtGeometryNo);
289
290    *reply = *req;			/* assume we'll grant everything */
291
292    if ((req->request_mode & CWBorderWidth) && req->border_width != 0) {
293	reply->border_width = 0;
294	okay = False;
295    }
296
297    layout_child(pw, child, req, &reply->x, &reply->y,
298		 &reply->width, &reply->height);
299
300    if ((req->request_mode & CWX) && req->x != reply->x)
301	okay = False;
302    if ((req->request_mode & CWY) && req->x != reply->x)
303	okay = False;
304    if ((req->request_mode & CWWidth) && req->width != reply->width)
305	okay = False;
306    if ((req->request_mode & CWHeight) && req->height != reply->height)
307	okay = False;
308
309    /*
310     * If we failed on anything, simply return without touching widget
311     */
312    if (!okay)
313	return (XtGeometryAlmost);
314
315    /*
316     * If not just doing a query, update widget and send report.  Note that
317     * we will often set fields that weren't requested because we want to keep
318     * the child visible
319     */
320    if (!(req->request_mode & XtCWQueryOnly)) {
321	unsigned int changed = 0;
322
323	if (XtX(child) != reply->x) {
324	    changed |= XawPRSliderX;
325	    XtX(child) = reply->x;
326	}
327	if (XtY(child) != reply->y) {
328	    changed |= XawPRSliderY;
329	    XtY(child) = reply->y;
330	}
331	if (XtWidth(child) != reply->width) {
332	    changed |= XawPRSliderWidth;
333	    XtWidth(child) = reply->width;
334	}
335	if (XtHeight(child) != reply->height) {
336	    changed |= XawPRSliderHeight;
337	    XtHeight(child) = reply->height;
338	}
339	if (changed)
340	    SendReport(pw, changed);
341    }
342
343    return (XtGeometryYes);		/* success! */
344}
345
346static void
347XawPortholeChangeManaged(Widget gw)
348{
349    PortholeWidget pw = (PortholeWidget)gw;
350    Widget child = find_child (pw);	/* ignore extra children */
351
352    if (child) {
353	if (!XtIsRealized (gw)) {
354	    XtWidgetGeometry geom = { .request_mode = 0 }, retgeom;
355
356	    if (XtWidth(pw) == 0) {
357		geom.width = XtWidth(child);
358		geom.request_mode |= CWWidth;
359	    }
360	    if (XtHeight(pw) == 0) {
361		geom.height = XtHeight(child);
362		geom.request_mode |= CWHeight;
363	    }
364	    if (geom.request_mode &&
365		XtMakeGeometryRequest (gw, &geom, &retgeom)
366		== XtGeometryAlmost)
367		(void)XtMakeGeometryRequest(gw, &retgeom, NULL);
368	}
369
370	XtResizeWidget(child, Max(XtWidth(child), XtWidth(pw)),
371		       Max(XtHeight(child), XtHeight(pw)), 0);
372
373	SendReport(pw, XawPRAll);
374    }
375}
376