1 /* Copyright (C) 1993 Nathan Sidwell */ 2 /* RCS $Id: Drag.c,v 4.14 1995/12/21 15:55:04 nathan Exp $ */ 3 /* the drag widget is an override shell to be popped up 4 * and follow the pointer until released. 5 * A pixmap is displayed in the widget. 6 * This should probably just be a composite widget whose child 7 * does the painting. Ho hum 8 */ 9 /*{{{ includes*/ 10 #include "ansiknr.h" 11 #include <X11/X.h> 12 #include <X11/Xlib.h> 13 #include <X11/IntrinsicP.h> 14 #include <X11/StringDefs.h> 15 #include <X11/Xaw/SimpleP.h> 16 #include <X11/ShellP.h> 17 #include "Drag.h" 18 /*}}}*/ 19 /*{{{ structs*/ 20 /*{{{ typedef struct _DragClass*/ 21 typedef struct _DragClass 22 { 23 int ansi_compliance; /* not used */ 24 } DragClassPart; 25 /*}}}*/ 26 /*{{{ typedef struct _DragClassRec*/ 27 typedef struct _DragClassRec 28 { 29 CoreClassPart core_class; 30 CompositeClassPart composite_class; 31 ShellClassPart shell_class; 32 OverrideShellClassPart override_shell_class; 33 DragClassPart drag_class; 34 } DragClassRec; 35 /*}}}*/ 36 /*{{{ typedef struct _DragPart*/ 37 typedef struct 38 { 39 /* resources */ 40 Pixmap pixmap; /* the icon to display */ 41 XtCallbackList callbacks; /* callbacks to notify */ 42 WidgetList choices; /* choices of selection */ 43 Cardinal num_choices; /* number of choices */ 44 Cursor cursor; /* cursor to use */ 45 46 /* private state */ 47 unsigned width; /* pixmap width */ 48 unsigned height; /* pixmap height */ 49 GC gc; /* GC to draw */ 50 Position x; /* corner of pixmap */ 51 Position y; /* corner of pixmap */ 52 Position offset_x; /* x offset */ 53 Position offset_y; /* y offset */ 54 Widget widget; /* invoker */ 55 } DragPart; 56 /*}}}*/ 57 /*{{{ typedef struct _DragRec*/ 58 typedef struct _DragRec 59 { 60 CorePart core; 61 CompositePart composite; 62 ShellPart shell; 63 OverrideShellPart override; 64 DragPart drag; 65 } DragRec; 66 /*}}}*/ 67 /*}}}*/ 68 /*{{{ resources*/ 69 static XtResource resources[] = 70 { 71 {XtNpixmap, XtCPixmap, XtRBitmap, sizeof(Pixmap), 72 XtOffsetOf(DragRec, drag.pixmap), XtRImmediate, (XtPointer)None}, 73 {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), 74 XtOffsetOf(DragRec, drag.callbacks), XtRCallback, (XtPointer)NULL}, 75 {MredNwidgetChoices, MredCWidgetList, XtRWidgetList, sizeof(WidgetList), 76 XtOffsetOf(DragRec, drag.choices), XtRImmediate, (XtPointer)0}, 77 {MredNnumWidgetChoices, XtCIndex, XtRCardinal, sizeof(Cardinal), 78 XtOffsetOf(DragRec, drag.num_choices), XtRImmediate, (XtPointer)0}, 79 {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), 80 XtOffsetOf(DragRec, drag.cursor), XtRString, (XtPointer)"left_ptr"}, 81 }; 82 /*}}}*/ 83 /*{{{ prototypes*/ 84 static VOIDFUNC Drag PROTOARG((Widget, XEvent *, String *, Cardinal *)); 85 static VOIDFUNC Notify PROTOARG((Widget, XEvent *, String *, Cardinal *)); 86 87 static VOIDFUNC ClassPartInitialize PROTOARG((WidgetClass)); 88 static VOIDFUNC Destroy PROTOARG((Widget)); 89 static VOIDFUNC Initialize PROTOARG((Widget, Widget, ArgList, Cardinal *)); 90 static XtGeometryResult QueryGeometry 91 PROTOARG((Widget, XtWidgetGeometry *, XtWidgetGeometry *)); 92 static VOIDFUNC Redisplay PROTOARG((Widget, XEvent *, Region)); 93 static VOIDFUNC Resize PROTOARG((Widget)); 94 static Boolean SetValues 95 PROTOARG((Widget, Widget, Widget, ArgList, Cardinal *)); 96 97 static VOIDFUNC GetGC PROTOARG((DragWidget)); 98 static VOIDFUNC GetPixmapSize PROTOARG((DragWidget)); 99 static unsigned GetPointerPosition 100 PROTOARG((XEvent *, Position *, Position *)); 101 /*}}}*/ 102 /*{{{ translations*/ 103 static char translations[] = "\ 104 <BtnUp>:notify()\n\ 105 <BtnMotion>:drag()\n\ 106 "; 107 /*}}}*/ 108 /*{{{ actions*/ 109 static XtActionsRec actions[] = 110 { 111 {"drag", Drag}, 112 {"notify", Notify}, 113 }; 114 /*}}}*/ 115 /*{{{ static CompositeClassExtensionRec compositeExtension =*/ 116 static CompositeClassExtensionRec compositeExtension = 117 { 118 NULL, 119 NULLQUARK, 120 XtCompositeExtensionVersion, 121 sizeof(CompositeClassExtensionRec), 122 False 123 }; 124 /*}}}*/ 125 #define SuperClass (WidgetClass)&overrideShellClassRec 126 /*{{{ DragClassRec dragClassRec =*/ 127 DragClassRec dragClassRec = 128 { 129 /*{{{ core class part*/ 130 { 131 SuperClass, /* superclass */ 132 "Drag", /* class_name */ 133 sizeof(DragRec), /* size */ 134 NULL, /* class_initialize */ 135 ClassPartInitialize, /* class_part_initialize */ 136 False, /* class_inited */ 137 Initialize, /* initialize */ 138 NULL, /* initialize_hook */ 139 XtInheritRealize, /* realize */ 140 actions, /* actions */ 141 XtNumber(actions), /* num_actions */ 142 resources, /* resources */ 143 XtNumber(resources), /* num_resources */ 144 NULLQUARK, /* xrm_class */ 145 True, /* compress_motion */ 146 XtExposeCompressMultiple, /* compress_exposure */ 147 True, /* compress_enterleave */ 148 False, /* visible_interest */ 149 Destroy, /* destroy */ 150 Resize, /* resize */ 151 Redisplay, /* expose */ 152 SetValues, /* set_values */ 153 NULL, /* set_values_hook */ 154 XtInheritSetValuesAlmost, /* set_values_almost */ 155 NULL, /* get_values_hook */ 156 NULL, /* accept_focus */ 157 XtVersion, /* version */ 158 NULL, /* callback_private */ 159 translations, /* default_translations */ 160 QueryGeometry, /* query_geometry */ 161 XtInheritDisplayAccelerator, /* display_accelerator */ 162 NULL, /* extension */ 163 }, 164 /*}}}*/ 165 /*{{{ composite class part*/ 166 { 167 XtInheritGeometryManager, 168 XtInheritChangeManaged, 169 XtInheritInsertChild, 170 XtInheritDeleteChild, 171 NULL 172 }, 173 /*}}}*/ 174 /*{{{ shell class part*/ 175 { 176 NULL 177 }, 178 /*}}}*/ 179 /*{{{ override shell class part*/ 180 { 181 NULL 182 }, 183 /*}}}*/ 184 /*{{{ drag class part*/ 185 { 186 0, /* dummy */ 187 }, 188 /*}}}*/ 189 }; 190 /*}}}*/ 191 WidgetClass dragWidgetClass = (WidgetClass)&dragClassRec; 192 /* actions */ 193 /*{{{ void Drag(widget, event, params, num_params)*/ 194 static VOIDFUNC Drag 195 FUNCARG((widget, event, params, num_params), 196 Widget widget 197 ARGSEP XEvent *event 198 ARGSEP String *params 199 ARGSEP Cardinal *num_params 200 ) 201 /* Follows the widget to the current pointer position 202 * If invoked by non pointer event, nothing happens 203 */ 204 { 205 static Arg args[] = {{XtNx, 0}, {XtNy, 0}}; 206 DragWidget dw; 207 Position x, y; 208 209 dw = (DragWidget)widget; 210 if(GetPointerPosition(event, &x, &y)) 211 { 212 args[0].value = x - dw->drag.offset_x - dw->core.border_width; 213 args[1].value = y - dw->drag.offset_y - dw->core.border_width; 214 XtSetValues((Widget)dw, args, XtNumber(args)); 215 } 216 return; 217 } 218 /*}}}*/ 219 /*{{{ void Notify(widget, event, params, num_params)*/ 220 static VOIDFUNC Notify 221 FUNCARG((widget, event, params, num_params), 222 Widget widget 223 ARGSEP XEvent *event 224 ARGSEP String *params 225 ARGSEP Cardinal *num_params 226 ) 227 /* Ungrabs the pointer and pops down the widget 228 * notifies the call back that we've selected a widget 229 * Normally called on button up 230 * searches the list of selectable widgets for the one underneath 231 * the center of the widget. 232 * if no widget is found then the callback is not called. 233 */ 234 { 235 DragWidget dw; 236 Position x, y; 237 238 XtUngrabPointer(widget, event->xbutton.time); 239 XtPopdown(widget); 240 dw = (DragWidget)widget; 241 if(GetPointerPosition(event, &x, &y)) 242 { 243 int count; 244 Widget *wptr; 245 246 x += dw->core.width / 2 - dw->drag.offset_x; 247 y += dw->core.height / 2 - dw->drag.offset_y; 248 for(wptr = dw->drag.choices, count = dw->drag.num_choices; 249 count--; wptr++) 250 { 251 Position rx, ry; 252 253 XtTranslateCoords(*wptr, 0, 0, &rx, &ry); 254 if(XtIsRealized(*wptr) && rx <= x && ry <= y && 255 rx + (int)((CorePart *)*wptr)->width > x && 256 ry + (int)((CorePart *)*wptr)->height > y) 257 { 258 DragCallback data; 259 260 data.selected = *wptr; 261 data.invoker = dw->drag.widget; 262 data.offset_x = x - rx; 263 data.offset_y = y - ry; 264 XtCallCallbackList((Widget)dw, dw->drag.callbacks, 265 (XtPointer)&data); 266 break; 267 } 268 } 269 } 270 return; 271 } 272 /*}}}*/ 273 /* class methods */ 274 /*{{{ void ClassPartInitialize(widgetclass)*/ 275 static VOIDFUNC ClassPartInitialize 276 FUNCARG((widgetclass), 277 WidgetClass widgetclass 278 ) 279 /* just add the composite class extension 280 */ 281 { 282 DragWidgetClass dwclass; 283 284 dwclass = (DragWidgetClass)widgetclass; 285 compositeExtension.next_extension = 286 dwclass->composite_class.extension; 287 dwclass->composite_class.extension = (XtPointer)&compositeExtension; 288 return; 289 } 290 /*}}}*/ 291 /* methods */ 292 /*{{{ void Destroy(widget)*/ 293 static VOIDFUNC Destroy 294 FUNCARG((widget), 295 Widget widget 296 ) 297 /* free the gc we use for drawing 298 */ 299 { 300 DragWidget dw; 301 302 dw = (DragWidget)widget; 303 XtReleaseGC((Widget)dw, dw->drag.gc); 304 return; 305 } 306 /*}}}*/ 307 /*{{{ void Initialize(treq, tnew, args, num_args)*/ 308 static VOIDFUNC Initialize 309 FUNCARG((treq, tnew, args, num_args), 310 Widget treq 311 ARGSEP Widget tnew 312 ARGSEP ArgList args 313 ARGSEP Cardinal *num_args 314 ) 315 /* initialize widget instance, 316 * allocate gc and set default size 317 */ 318 { 319 DragWidget ndw; 320 321 ndw = (DragWidget)tnew; 322 GetPixmapSize(ndw); 323 if(!ndw->core.width) 324 ndw->core.width = ndw->drag.width ? ndw->drag.width : 16; 325 if(!ndw->core.height) 326 ndw->core.height = ndw->drag.height ? ndw->drag.height : 16; 327 GetGC(ndw); 328 return; 329 } 330 /*}}}*/ 331 /*{{{ XtGeometryResult QueryGeometry(widget, proposed, answer)*/ 332 static XtGeometryResult QueryGeometry 333 FUNCARG((widget, proposed, answer), 334 Widget widget 335 ARGSEP XtWidgetGeometry *proposed 336 ARGSEP XtWidgetGeometry *answer 337 ) 338 /* validate geometry request from parent 339 * try to set to size of pixmap 340 */ 341 { 342 DragWidget dw; 343 344 dw = (DragWidget)widget; 345 answer->request_mode = CWWidth | CWHeight; 346 answer->height = dw->drag.width ? dw->drag.width : 16; 347 answer->height = dw->drag.height ? dw->drag.height : 16; 348 if((proposed->request_mode & (CWWidth | CWHeight)) == (CWWidth | CWHeight) && 349 proposed->width == answer->width && proposed->height == answer->height) 350 return XtGeometryYes; 351 else if(answer->width == dw->core.width && answer->height == dw->core.height) 352 return XtGeometryNo; 353 else 354 return XtGeometryAlmost; 355 } 356 /*}}}*/ 357 /*{{{ void Redisplay(widget, event, region)*/ 358 static VOIDFUNC Redisplay 359 FUNCARG((widget, event, region), 360 Widget widget 361 ARGSEP XEvent *event 362 ARGSEP Region region 363 ) 364 /* repaint the widget on expose 365 */ 366 { 367 DragWidget dw; 368 369 if(!XtIsRealized(widget)) 370 return; 371 dw = (DragWidget)widget; 372 if(dw->drag.pixmap != None) 373 XCopyArea(XtDisplay(dw), dw->drag.pixmap, XtWindow(dw), dw->drag.gc, 0, 0, 374 dw->drag.width, dw->drag.height, dw->drag.x, dw->drag.y); 375 return; 376 } 377 /*}}}*/ 378 /*{{{ void Resize(widget)*/ 379 static VOIDFUNC Resize 380 FUNCARG((widget), 381 Widget widget 382 ) 383 /* when resized, reset the pixmap corner so that its centered 384 */ 385 { 386 DragWidget dw; 387 388 dw = (DragWidget)widget; 389 dw->drag.x = (dw->core.width - dw->drag.width) / 2; 390 dw->drag.y = (dw->core.height - dw->drag.height) / 2; 391 return; 392 } 393 /*}}}*/ 394 /*{{{ Boolean SetValues(cw, rw, nw, args, num_args)*/ 395 static Boolean SetValues 396 FUNCARG((cw, rw, nw, args, num_args), 397 Widget cw 398 ARGSEP Widget rw 399 ARGSEP Widget nw 400 ARGSEP ArgList args 401 ARGSEP Cardinal *num_args 402 ) 403 /* recompute things if the pixmap changes 404 */ 405 { 406 Boolean redraw; 407 DragWidget cdw; 408 DragWidget ndw; 409 410 redraw = False; 411 cdw = (DragWidget)cw; 412 ndw = (DragWidget)nw; 413 if(cdw->drag.pixmap != ndw->drag.pixmap) 414 { 415 redraw = True; 416 GetPixmapSize(ndw); 417 if(ndw->drag.width) 418 { 419 ndw->core.width = ndw->drag.width; 420 ndw->drag.x = 0; 421 } 422 if(ndw->drag.height) 423 { 424 ndw->core.height = ndw->drag.height; 425 ndw->drag.y = 0; 426 } 427 } 428 if(cdw->core.width != ndw->core.width || 429 cdw->core.height != ndw->core.height) 430 redraw = False; 431 return redraw; 432 } 433 /*}}}*/ 434 /* public routines */ 435 /*{{{ void DragPopup(invoker, widget, pixmap, offset_x, offset_y, x, y, time)*/ 436 extern VOIDFUNC DragPopup 437 FUNCARG((invoker, widget, pixmap, offset_x, offset_y, x, y, time), 438 Widget invoker /* invoker widget */ 439 ARGSEP Widget widget /* drag widget */ 440 ARGSEP Pixmap pixmap /* pixmap to display */ 441 ARGSEP Position offset_x /* pointer offset on pixmap */ 442 ARGSEP Position offset_y 443 ARGSEP Position x /* root postion of pointer */ 444 ARGSEP Position y 445 ARGSEP Time time /* time of last event (needed for grab) */ 446 ) 447 /* called to invoke a drag selection 448 * set pixmap and offset values 449 * popup the widget 450 * grab the pointer 451 * If the pointer grab fails, the widget is popped down 452 */ 453 { 454 /*{{{ set_args*/ 455 static Arg set_args[] = 456 { 457 {XtNx}, 458 {XtNy}, 459 {XtNpixmap}, 460 }; 461 /*}}}*/ 462 DragWidget dw; 463 464 dw = (DragWidget)widget; 465 if(!XtIsSubclass((Widget)dw, (WidgetClass)&dragClassRec)) 466 return; 467 dw->drag.offset_x = offset_x; 468 dw->drag.offset_y = offset_y; 469 dw->drag.widget = invoker; 470 set_args[0].value = x - dw->core.border_width - offset_x; 471 set_args[1].value = y - dw->core.border_width - offset_y; 472 set_args[2].value = pixmap; 473 XtSetValues((Widget)dw, set_args, pixmap == None ? 2 : 3); 474 XtPopupSpringLoaded((Widget)dw); 475 if(XtGrabPointer((Widget)dw, False, ButtonMotionMask | ButtonReleaseMask | 476 EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, 477 dw->drag.cursor, time) != GrabSuccess) 478 XtPopdown((Widget)dw); 479 return; 480 } 481 /*}}}*/ 482 /* private routines */ 483 /*{{{ void GetGC(dw)*/ 484 static VOIDFUNC GetGC 485 FUNCARG((widget), 486 DragWidget widget 487 ) 488 /* get a default gc to copy with 489 */ 490 { 491 XGCValues values; 492 493 widget->drag.gc = XtGetGC((Widget)widget, 0, &values); 494 return; 495 } 496 /*}}}*/ 497 /*{{{ void GetPixmapSize(dw)*/ 498 static VOIDFUNC GetPixmapSize 499 FUNCARG((widget), 500 DragWidget widget 501 ) 502 /* get the pixmap size 503 * Note this requires a server query 504 * if the pixmap is None, then set 0 size 505 */ 506 { 507 if(widget->drag.pixmap != None) 508 { 509 Window root; 510 int x, y; 511 unsigned border; 512 unsigned depth; 513 Status status; 514 515 status = XGetGeometry(XtDisplay(widget), widget->drag.pixmap, &root, 516 &x, &y, &widget->drag.width, &widget->drag.height, &border, &depth); 517 } 518 else 519 widget->drag.width = widget->drag.height = 0; 520 return; 521 } 522 /*}}}*/ 523 /*{{{ unsigned GetPointerPosition(dw, event, x, y)*/ 524 static unsigned GetPointerPosition 525 FUNCARG((event, xp, yp), 526 XEvent *event /* the event to query */ 527 ARGSEP Position *xp /* root pointer x return */ 528 ARGSEP Position *yp /* root pointer y return */ 529 ) 530 /* get the pointer position on the root window. 531 * returns 0 if not a pointer event 532 */ 533 { 534 unsigned got; 535 Position x; 536 Position y; 537 538 /*{{{ get position from event*/ 539 switch(event->type) 540 { 541 case ButtonPress: 542 case ButtonRelease: 543 x = event->xbutton.x_root; 544 y = event->xbutton.y_root; 545 got = 1; 546 break; 547 case MotionNotify: 548 x = event->xmotion.x_root; 549 y = event->xmotion.y_root; 550 got = 1; 551 break; 552 case EnterNotify: 553 case LeaveNotify: 554 x = event->xcrossing.x_root; 555 y = event->xcrossing.y_root; 556 got = 1; 557 break; 558 default: 559 x = y = 0; 560 got = 0; 561 } 562 /*}}}*/ 563 if(got) 564 { 565 *xp = x; 566 *yp = y; 567 } 568 return got; 569 } 570 /*}}}*/ 571