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