1/* $XConsortium: popup.c,v 2.38 94/08/26 18:04:22 swick Exp $ 2 * 3 * 4 * COPYRIGHT 1989 5 * DIGITAL EQUIPMENT CORPORATION 6 * MAYNARD, MASSACHUSETTS 7 * ALL RIGHTS RESERVED. 8 * 9 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND 10 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. 11 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR 12 * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. 13 * 14 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT 15 * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN 16 * ADDITION TO THAT SET FORTH ABOVE. 17 * 18 * Permission to use, copy, modify, and distribute this software and its 19 * documentation for any purpose and without fee is hereby granted, provided 20 * that the above copyright notice appear in all copies and that both that 21 * copyright notice and this permission notice appear in supporting 22 * documentation, and that the name of Digital Equipment Corporation not be 23 * used in advertising or publicity pertaining to distribution of the software 24 * without specific, written prior permission. 25 */ 26/* $XFree86$ */ 27 28/* popup.c -- Handle pop-up widgets. */ 29 30#include "xmh.h" 31#include "actions.h" 32 33#include <X11/Xaw/Cardinals.h> 34 35typedef struct _PopupStatus { 36 Widget popup; /* order of fields same as CommandStatusRec */ 37 struct _LastInput lastInput; 38 char* shell_command; /* NULL, or contains sh -c command */ 39} PopupStatusRec, *PopupStatus; 40 41/* these are just strings which are used more than one place in the code */ 42static String XmhNconfirm = "confirm"; 43static String XmhNdialog = "dialog"; 44static String XmhNerror = "error"; 45static String XmhNnotice = "notice"; 46static String XmhNokay = "okay"; 47static String XmhNprompt = "prompt"; 48static String XmhNvalue = "value"; 49 50/* The popups were originally parented from toplevel and neglected the 51 * transientFor resource. In order not to break existing user resource 52 * settings for the popups, transientFor is set independent of the parent, 53 * which remains the toplevel widget. 54 */ 55 56static void DeterminePopupPosition( 57 Position *x_ptr, 58 Position *y_ptr, 59 Widget *transFor_return) /* return a suitable top level shell */ 60{ 61 if (lastInput.win != (Window) -1) { 62 if (transFor_return) { 63 Widget source; 64 source = XtWindowToWidget(XtDisplay(toplevel), lastInput.win); 65 while (source && !XtIsWMShell(source)) 66 source = XtParent(source); 67 *transFor_return = source; 68 } 69 /* use the site of the last KeyPress or ButtonPress */ 70 *x_ptr = lastInput.x; 71 *y_ptr = lastInput.y; 72 } else { 73 Widget source; 74 int i = 0; 75 Dimension width, height; 76 Arg args[2]; 77 78 /* %%% need to keep track of last screen */ 79 /* guess which screen and use the the center of it */ 80 while (i < numScrns && !scrnList[i]->mapped) 81 i++; 82 source = ((i < numScrns) ? scrnList[i]->parent : toplevel); 83 XtSetArg(args[0], XtNwidth, &width); 84 XtSetArg(args[1], XtNheight, &height); 85 XtGetValues(source, args, TWO); 86 XtTranslateCoords(source, (Position) (width / 2), 87 (Position) (height / 2), x_ptr, y_ptr); 88 if (transFor_return) *transFor_return = source; 89 } 90} 91 92static Boolean PositionThePopup( 93 Widget popup, 94 Position x, 95 Position y) 96{ 97 /* Hack. Fix up the position of the popup. The xmh app defaults file 98 * contains an Xmh*Geometry specification; the effects of that on 99 * popups, and the lack of any user-supplied geometry specification for 100 * popups, are mitigated here, by giving the popup shell a position. 101 * (Xmh*Geometry is needed in case there is no user-supplied default.) 102 * Returns True if an explicit geometry was inferred; false if the 103 * widget was repositioned to (x,y). 104 */ 105 106 Arg args[4]; 107 String top_geom, pop_geom; 108 109 XtSetArg( args[0], XtNgeometry, &top_geom ); 110 XtGetValues( toplevel, args, ONE ); 111 XtSetArg( args[0], XtNgeometry, &pop_geom ); 112 XtGetValues( popup, args, ONE ); 113 114 if (pop_geom == NULL || pop_geom == top_geom) { 115 /* if same db entry, then ... */ 116 XtSetArg( args[0], XtNgeometry, (String) NULL); 117 XtSetArg( args[1], XtNx, x); 118 XtSetArg( args[2], XtNy, y); 119 XtSetArg( args[3], XtNwinGravity, SouthWestGravity); 120 XtSetValues( popup, args, FOUR); 121 return False; 122 } 123 return True; 124} 125 126 127static void CenterPopupPosition( 128 Widget widget, 129 Widget popup, 130 Position px, 131 Position py) 132{ 133 Position x, y; 134 Position nx, ny; 135 Arg args[3]; 136 137 if (widget == NULL) return; 138 XtSetArg(args[0], XtNx, &x); 139 XtSetArg(args[1], XtNy, &y); 140 XtGetValues(popup, args, TWO); 141 if (x == px && y == py) { 142 143 /* Program sets geometry. Correct our earlier calculations. */ 144 145 nx = (GetWidth(widget) - GetWidth(popup)) / 2; 146 ny = (GetHeight(widget) - GetHeight(popup)) / 2; 147 if (nx < 0) nx = 0; 148 if (ny < 0) ny = 0; 149 XtTranslateCoords(widget, nx, ny, &x, &y); 150 XtSetArg(args[0], XtNx, x); 151 XtSetArg(args[1], XtNy, y); 152 XtSetArg(args[2], XtNwinGravity, CenterGravity); 153 XtSetValues(popup, args, THREE); 154 } 155} 156 157 158/* Insure that the popup is wholly showing on the screen. 159 Optionally center the widget horizontally and/or vertically 160 on current position. 161 */ 162 163static void InsureVisibility( 164 Widget popup, 165 Widget popup_child, 166 Position x, /* assert: current position = (x,y) */ 167 Position y, 168 Boolean centerX, 169 Boolean centerY) 170{ 171 Position root_x, root_y; 172 Dimension width, height, border; 173 Arg args[3]; 174 175 176 XtSetArg( args[0], XtNwidth, &width ); 177 XtSetArg( args[1], XtNheight, &height ); 178 XtSetArg( args[2], XtNborderWidth, &border ); 179 XtGetValues( popup, args, THREE ); 180 181 XtTranslateCoords(popup_child, (Position)0, (Position)0, &root_x, &root_y); 182 if (centerX) root_x -= width/2 + border; 183 if (centerY) root_y -= height/2 + border; 184 if (root_x < 0) root_x = 0; 185 if (root_y < 0) root_y = 0; 186 border <<= 1; 187 188 if ((int)(root_x + width + border) > WidthOfScreen(XtScreen(toplevel))) { 189 root_x = WidthOfScreen(XtScreen(toplevel)) - width - border; 190 } 191 if ((int)(root_y + height + border) > HeightOfScreen(XtScreen(toplevel))) { 192 root_y = HeightOfScreen(XtScreen(toplevel)) - height - border; 193 } 194 195 if (root_x != x || root_y != y) { 196 XtSetArg( args[0], XtNx, root_x ); 197 XtSetArg( args[1], XtNy, root_y ); 198 XtSetValues( popup, args, TWO ); 199 } 200} 201 202 203/*ARGSUSED*/ 204void DestroyPopup( 205 Widget widget, /* unused */ 206 XtPointer client_data, 207 XtPointer call_data) /* unused */ 208{ 209 Widget popup = (Widget) client_data; 210 XtPopdown(popup); 211 XtDestroyWidget(popup); 212} 213 214void WMDeletePopup( 215 Widget popup, /* transient shell */ 216 XEvent* event) 217{ 218 String shellName; 219 String buttonName; 220 Widget button; 221 222 shellName = XtName(popup); 223 if (strcmp(shellName, XmhNconfirm) == 0) 224 buttonName = "*no"; 225 else if (strcmp(shellName, XmhNprompt) == 0) 226 buttonName = "*cancel"; 227 else if (strcmp(shellName, XmhNnotice) == 0) 228 buttonName = "*confirm"; 229 else if (strcmp(shellName, XmhNerror) == 0) 230 buttonName = "*OK"; 231 else 232 return; /* WM may kill us */ 233 234 button = XtNameToWidget(popup, buttonName); 235 if (! button) return; 236 XtCallActionProc(button, "set", event, (String*)NULL, ZERO); 237 XtCallActionProc(button, "notify", event, (String*)NULL, ZERO); 238 XtCallActionProc(button, "unset", event, (String*)NULL, ZERO); 239} 240 241static void TheUsual( 242 Widget popup) /* shell */ 243{ 244 XtInstallAllAccelerators(popup, popup); 245 XtAugmentTranslations(popup, app_resources.wm_protocols_translations); 246 XtRealizeWidget(popup); 247 XDefineCursor(XtDisplay(popup), XtWindow(popup), app_resources.cursor); 248 (void) XSetWMProtocols(XtDisplay(popup), XtWindow(popup), 249 protocolList, XtNumber(protocolList)); 250} 251 252 253/*ARGSUSED*/ 254void XmhPromptOkayAction( 255 Widget w, /* the "value" widget in the Dialog box */ 256 XEvent *event, /* unused */ 257 String *params, /* unused */ 258 Cardinal *num_params) /* unused */ 259{ 260 XtCallCallbacks(XtNameToWidget(XtParent(w), XmhNokay), XtNcallback, 261 (XtPointer)XtParent(w)); 262} 263 264 265void PopupPrompt( 266 Widget transientFor, /* required to be a top-level shell */ 267 String question, /* the prompting string */ 268 XtCallbackProc okayCallback) /* CreateFolder() */ 269{ 270 Widget popup; 271 Widget dialog; 272 Widget value; 273 Position x, y; 274 Boolean positioned; 275 Arg args[3]; 276 static XtTranslations PromptTextTranslations = NULL; 277 278 DeterminePopupPosition(&x, &y, (Widget*)NULL); 279 XtSetArg(args[0], XtNallowShellResize, True); 280 XtSetArg(args[1], XtNinput, True); 281 XtSetArg(args[2], XtNtransientFor, transientFor); 282 popup = XtCreatePopupShell(XmhNprompt, transientShellWidgetClass, toplevel, 283 args, THREE); 284 positioned = PositionThePopup(popup, x, y); 285 286 XtSetArg(args[0], XtNlabel, question); 287 XtSetArg(args[1], XtNvalue, ""); 288 dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args, 289 TWO); 290 XtSetArg(args[0], XtNresizable, True); 291 XtSetValues( XtNameToWidget(dialog, "label"), args, ONE); 292 value = XtNameToWidget(dialog, XmhNvalue); 293 XtSetValues( value, args, ONE); 294 if (! PromptTextTranslations) 295 PromptTextTranslations = XtParseTranslationTable 296 ("<Key>Return: XmhPromptOkayAction()\n\ 297 Ctrl<Key>R: no-op(RingBell)\n\ 298 Ctrl<Key>S: no-op(RingBell)\n"); 299 XtOverrideTranslations(value, PromptTextTranslations); 300 301 XawDialogAddButton(dialog, XmhNokay, okayCallback, (XtPointer) dialog); 302 XawDialogAddButton(dialog, "cancel", DestroyPopup, (XtPointer) popup); 303 TheUsual(popup); 304 InsureVisibility(popup, dialog, x, y, !positioned, False); 305 XtPopup(popup, XtGrabNone); 306} 307 308 309/* ARGSUSED */ 310static void FreePopupStatus( 311 Widget w, /* unused */ 312 XtPointer closure, 313 XtPointer call_data) /* unused */ 314{ 315 PopupStatus popup = (PopupStatus)closure; 316 XtPopdown(popup->popup); 317 XtDestroyWidget(popup->popup); 318 if (popup->shell_command) 319 XtFree(popup->shell_command); 320 XtFree((char *) closure); 321} 322 323 324void PopupNotice( 325 String message, 326 XtCallbackProc callback, 327 XtPointer closure) 328{ 329 PopupStatus popup_status = (PopupStatus)closure; 330 Widget transientFor; 331 Widget dialog; 332 Widget value; 333 Position x, y; 334 Arg args[3]; 335 char command[65], label[128]; 336 337 if (popup_status == (PopupStatus)NULL) { 338 popup_status = XtNew(PopupStatusRec); 339 popup_status->lastInput = lastInput; 340 popup_status->shell_command = (char*)NULL; 341 } 342 if (! popup_status->shell_command) { 343 /* MH command */ 344 if (sscanf( message, "%64s", command ) != 1) 345 (void) strcpy( command, "system" ); 346 else { 347 int l = strlen(command); 348 if (l && command[--l] == ':') 349 command[l] = '\0'; 350 } 351 snprintf(label, sizeof(label), "%.64s command returned:", command); 352 } else { 353 /* arbitrary shell command */ 354 int len = strlen(popup_status->shell_command); 355 snprintf(label, sizeof(label), "%.88s %s\nshell command returned:", 356 popup_status->shell_command, 357 ((len > 88) ? "[truncated]" : "")); 358 } 359 360 DeterminePopupPosition(&x, &y, &transientFor); 361 XtSetArg( args[0], XtNallowShellResize, True ); 362 XtSetArg( args[1], XtNinput, True ); 363 XtSetArg( args[2], XtNtransientFor, transientFor); 364 popup_status->popup = XtCreatePopupShell(XmhNnotice, 365 transientShellWidgetClass, toplevel, args, THREE); 366 PositionThePopup(popup_status->popup, x, y); 367 368 XtSetArg( args[0], XtNlabel, label ); 369 XtSetArg( args[1], XtNvalue, message ); 370 dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, 371 popup_status->popup, args, TWO); 372 373 /* The text area of the dialog box will not be editable. */ 374 value = XtNameToWidget(dialog, XmhNvalue); 375 XtSetArg( args[0], XtNeditType, XawtextRead); 376 XtSetArg( args[1], XtNdisplayCaret, False); 377 XtSetValues( value, args, TWO); 378 XtOverrideTranslations(value, NoTextSearchAndReplace); 379 380 XawDialogAddButton( dialog, XmhNconfirm, 381 ((callback != (XtCallbackProc) NULL) 382 ? callback : (XtCallbackProc) FreePopupStatus), 383 (XtPointer) popup_status 384 ); 385 386 TheUsual(popup_status->popup); 387 InsureVisibility(popup_status->popup, dialog, x, y, False, False); 388 XtPopup(popup_status->popup, XtGrabNone); 389} 390 391 392void PopupConfirm( 393 Widget center_widget, /* where to center; may be NULL */ 394 String question, 395 XtCallbackList affirm_callbacks, 396 XtCallbackList negate_callbacks) 397{ 398 Widget popup; 399 Widget dialog; 400 Widget button; 401 Widget transientFor; 402 Position x, y; 403 Arg args[3]; 404 static XtCallbackRec callbacks[] = { 405 {DestroyPopup, (XtPointer) NULL}, 406 {(XtCallbackProc) NULL, (XtPointer) NULL} 407 }; 408 409 DeterminePopupPosition(&x, &y, &transientFor); 410 XtSetArg(args[0], XtNinput, True); 411 XtSetArg(args[1], XtNallowShellResize, True); 412 XtSetArg(args[2], XtNtransientFor, transientFor); 413 popup = XtCreatePopupShell(XmhNconfirm, transientShellWidgetClass, 414 toplevel, args, THREE); 415 PositionThePopup(popup, x, y); 416 417 XtSetArg(args[0], XtNlabel, question); 418 dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args, 419 ONE); 420 421 callbacks[0].closure = (XtPointer) popup; 422 XtSetArg(args[0], XtNcallback, callbacks); 423 button = XtCreateManagedWidget("yes", commandWidgetClass, dialog, 424 args, ONE); 425 if (affirm_callbacks) 426 XtAddCallbacks(button, XtNcallback, affirm_callbacks); 427 428 button = XtCreateManagedWidget("no", commandWidgetClass, dialog, 429 args, ZERO); 430 XtAddCallback(button, XtNcallback, DestroyPopup, (XtPointer) popup); 431 if (negate_callbacks) 432 XtAddCallbacks(button, XtNcallback, negate_callbacks); 433 434 TheUsual(popup); 435 CenterPopupPosition(center_widget ? center_widget : transientFor, 436 popup, x, y); 437 InsureVisibility(popup, dialog, x, y, False, False); 438 XtPopup(popup, XtGrabNone); 439} 440 441 442void PopupError( 443 Widget widget, /* transient for this top-level shell, or NULL */ 444 String message) 445{ 446 Widget transFor, error_popup, dialog; 447 Position x, y; 448 Boolean positioned; 449 Arg args[3]; 450 static XtCallbackRec callbacks[] = { 451 {DestroyPopup, (XtPointer) NULL}, 452 {(XtCallbackProc) NULL, (XtPointer) NULL} 453 }; 454 455 transFor = widget; 456 DeterminePopupPosition(&x, &y, transFor ? (Widget*)NULL : &transFor); 457 458 XtSetArg(args[0], XtNallowShellResize, True); 459 XtSetArg(args[1], XtNinput, True); 460 XtSetArg(args[2], XtNtransientFor, transFor); 461 error_popup = XtCreatePopupShell(XmhNerror, transientShellWidgetClass, 462 toplevel, args, THREE); 463 positioned = PositionThePopup(error_popup, x, y); 464 465 XtSetArg(args[0], XtNlabel, message); 466 dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, error_popup, 467 args, ONE); 468 callbacks[0].closure = (XtPointer) error_popup; 469 XtSetArg(args[0], XtNcallback, callbacks); 470 XawDialogAddButton(dialog, "OK", DestroyPopup, (XtPointer) error_popup); 471 TheUsual(error_popup); 472 InsureVisibility(error_popup, dialog, x, y, !positioned, !positioned); 473 XtPopup(error_popup, XtGrabNone); 474} 475 476/*ARGSUSED*/ 477void PopupWarningHandler( 478 String name, 479 String type, 480 String class, 481 String msg, 482 String *params, 483 Cardinal *num) 484{ 485 char *ptr; 486 int i; 487 String par[10]; 488 char message[500]; 489 char buffer[500]; 490 static Boolean allowPopup = True; /* protect against recursion */ 491 492 XtGetErrorDatabaseText(name, type, class, msg, buffer, 500); 493 494 if (params && num && *num) { 495 i = (*num <= 10) ? *num : 10; 496 memmove( (char*)par, (char*)params, i * sizeof(String)); 497 bzero( &par[i], (10-i) * sizeof(String)); 498 if (*num > 10) 499 par[9] = "(truncated)"; 500 snprintf(message, sizeof(message), buffer, par[0], par[1], par[2], 501 par[3], par[4], par[5], par[6], par[7], par[8], par[9]); 502 ptr = message; 503 } else { 504 ptr = buffer; 505 } 506 if (allowPopup) { 507 allowPopup = False; 508 PopupError((Widget)NULL, ptr); 509 allowPopup = True; 510 } else { 511 fprintf(stderr, ptr); 512 } 513} 514