Git init
[framework/uifw/xorg/lib/libxaw.git] / src / Porthole.c
1 /*
2  *
3 Copyright 1990, 1994, 1998  The Open Group
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in 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  */
44 static void XawPortholeChangeManaged(Widget);
45 static XtGeometryResult XawPortholeGeometryManager(Widget, XtWidgetGeometry*,
46                                                    XtWidgetGeometry*);
47 static XtGeometryResult XawPortholeQueryGeometry(Widget, XtWidgetGeometry*,
48                                                  XtWidgetGeometry*);
49 static void XawPortholeRealize(Widget, Mask*, XSetWindowAttributes*);
50 static void XawPortholeResize(Widget);
51
52 /*
53  * Prototypes
54  */
55 static Widget find_child(PortholeWidget);
56 static void layout_child(PortholeWidget, Widget, XtWidgetGeometry*,
57                          Position*, Position*, Dimension*, Dimension*);
58 static void SendReport(PortholeWidget, unsigned int);
59
60 /*
61  * Initialization
62  */
63 #define offset(field)   XtOffsetOf(PortholeRec, porthole.field)
64 static 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)
78 PortholeClassRec 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
127 WidgetClass portholeWidgetClass = (WidgetClass)&portholeClassRec;
128
129 /*
130  * Implementation
131  */
132 static Widget
133 find_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
150 static void
151 SendReport(PortholeWidget pw, unsigned int changed)
152 {
153     Widget child = find_child(pw);
154
155     if (pw->porthole.report_callbacks && child) {
156         XawPannerReport prep;
157
158         prep.changed = changed;
159         prep.slider_x = -XtX(child);    /* porthole is "inner" */
160         prep.slider_y = -XtY(child);    /* child is outer since it is larger */
161         prep.slider_width = XtWidth(pw);
162         prep.slider_height = XtHeight(pw);
163         prep.canvas_width = XtWidth(child);
164         prep.canvas_height = XtHeight(child);
165         XtCallCallbackList((Widget)pw, pw->porthole.report_callbacks,
166                            (XtPointer)&prep);
167     }
168 }
169
170 static void
171 layout_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) - (Position)*widthp;
203     miny = (Position)XtHeight(pw) - (Position)*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
216 static void
217 XawPortholeRealize(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
230 static void
231 XawPortholeResize(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
251 static XtGeometryResult
252 XawPortholeQueryGeometry(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
279 static XtGeometryResult
280 XawPortholeGeometryManager(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
346 static void
347 XawPortholeChangeManaged(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, retgeom;
355
356             geom.request_mode = 0;
357             if (XtWidth(pw) == 0) {
358                 geom.width = XtWidth(child);
359                 geom.request_mode |= CWWidth;
360             }
361             if (XtHeight(pw) == 0) {
362                 geom.height = XtHeight(child);
363                 geom.request_mode |= CWHeight;
364             }
365             if (geom.request_mode &&
366                 XtMakeGeometryRequest (gw, &geom, &retgeom)
367                 == XtGeometryAlmost)
368                 (void)XtMakeGeometryRequest(gw, &retgeom, NULL);
369         }
370         
371         XtResizeWidget(child, Max(XtWidth(child), XtWidth(pw)),
372                        Max(XtHeight(child), XtHeight(pw)), 0);
373
374         SendReport(pw, XawPRAll);
375     }
376 }