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