/* $XConsortium: popup.c,v 2.38 94/08/26 18:04:22 swick Exp $ * * * COPYRIGHT 1989 * DIGITAL EQUIPMENT CORPORATION * MAYNARD, MASSACHUSETTS * ALL RIGHTS RESERVED. * * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. * * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN * ADDITION TO THAT SET FORTH ABOVE. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Digital Equipment Corporation not be * used in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. */ /* popup.c -- Handle pop-up widgets. */ #include "xmh.h" #include typedef struct _PopupStatus { Widget popup; /* order of fields same as CommandStatusRec */ struct _LastInput lastInput; char* shell_command; /* NULL, or contains sh -c command */ } PopupStatusRec, *PopupStatus; /* these are just strings which are used more than one place in the code */ static String XmhNconfirm = "confirm"; static String XmhNdialog = "dialog"; static String XmhNerror = "error"; static String XmhNnotice = "notice"; static String XmhNokay = "okay"; static String XmhNprompt = "prompt"; static String XmhNvalue = "value"; /* The popups were originally parented from toplevel and neglected the * transientFor resource. In order not to break existing user resource * settings for the popups, transientFor is set independent of the parent, * which remains the toplevel widget. */ static void DeterminePopupPosition(x_ptr, y_ptr, transFor_return) Position *x_ptr, *y_ptr; Widget *transFor_return; /* return a suitable top level shell */ { if (lastInput.win != -1) { if (transFor_return) { Widget source; source = XtWindowToWidget(XtDisplay(toplevel), lastInput.win); while (source && !XtIsWMShell(source)) source = XtParent(source); *transFor_return = source; } /* use the site of the last KeyPress or ButtonPress */ *x_ptr = lastInput.x; *y_ptr = lastInput.y; } else { Widget source; int i = 0; Dimension width, height; Arg args[2]; /* %%% need to keep track of last screen */ /* guess which screen and use the the center of it */ while (i < numScrns && !scrnList[i]->mapped) i++; source = ((i < numScrns) ? scrnList[i]->parent : toplevel); XtSetArg(args[0], XtNwidth, &width); XtSetArg(args[1], XtNheight, &height); XtGetValues(source, args, TWO); XtTranslateCoords(source, (Position) (width / 2), (Position) (height / 2), x_ptr, y_ptr); if (transFor_return) *transFor_return = source; } } static Boolean PositionThePopup(popup, x, y) Widget popup; Position x, y; { /* Hack. Fix up the position of the popup. The xmh app defaults file * contains an Xmh*Geometry specification; the effects of that on * popups, and the lack of any user-supplied geometry specification for * popups, are mitigated here, by giving the popup shell a position. * (Xmh*Geometry is needed in case there is no user-supplied default.) * Returns True if an explicit geometry was inferred; false if the * widget was repositioned to (x,y). */ Arg args[4]; String top_geom, pop_geom; XtSetArg( args[0], XtNgeometry, &top_geom ); XtGetValues( toplevel, args, ONE ); XtSetArg( args[0], XtNgeometry, &pop_geom ); XtGetValues( popup, args, ONE ); if (pop_geom == NULL || pop_geom == top_geom) { /* if same db entry, then ... */ XtSetArg( args[0], XtNgeometry, (String) NULL); XtSetArg( args[1], XtNx, x); XtSetArg( args[2], XtNy, y); XtSetArg( args[3], XtNwinGravity, SouthWestGravity); XtSetValues( popup, args, FOUR); return False; } return True; } static void CenterPopupPosition(widget, popup, px, py) Widget widget; Widget popup; Position px, py; { Position x, y; Position nx, ny; Arg args[3]; if (widget == NULL) return; XtSetArg(args[0], XtNx, &x); XtSetArg(args[1], XtNy, &y); XtGetValues(popup, args, TWO); if (x == px && y == py) { /* Program sets geometry. Correct our earlier calculations. */ nx = (GetWidth(widget) - GetWidth(popup)) / 2; ny = (GetHeight(widget) - GetHeight(popup)) / 2; if (nx < 0) nx = 0; if (ny < 0) ny = 0; XtTranslateCoords(widget, nx, ny, &x, &y); XtSetArg(args[0], XtNx, x); XtSetArg(args[1], XtNy, y); XtSetArg(args[2], XtNwinGravity, CenterGravity); XtSetValues(popup, args, THREE); } } /* Insure that the popup is wholly showing on the screen. Optionally center the widget horizontally and/or vertically on current position. */ static void InsureVisibility(popup, popup_child, x, y, centerX, centerY) Widget popup, popup_child; Position x, y; /* assert: current position = (x,y) */ Boolean centerX, centerY; { Position root_x, root_y; Dimension width, height, border; Arg args[3]; XtSetArg( args[0], XtNwidth, &width ); XtSetArg( args[1], XtNheight, &height ); XtSetArg( args[2], XtNborderWidth, &border ); XtGetValues( popup, args, THREE ); XtTranslateCoords(popup_child, (Position)0, (Position)0, &root_x, &root_y); if (centerX) root_x -= width/2 + border; if (centerY) root_y -= height/2 + border; if (root_x < 0) root_x = 0; if (root_y < 0) root_y = 0; border <<= 1; if ((int)(root_x + width + border) > WidthOfScreen(XtScreen(toplevel))) { root_x = WidthOfScreen(XtScreen(toplevel)) - width - border; } if ((int)(root_y + height + border) > HeightOfScreen(XtScreen(toplevel))) { root_y = HeightOfScreen(XtScreen(toplevel)) - height - border; } if (root_x != x || root_y != y) { XtSetArg( args[0], XtNx, root_x ); XtSetArg( args[1], XtNy, root_y ); XtSetValues( popup, args, TWO ); } } /*ARGSUSED*/ void DestroyPopup(widget, client_data, call_data) Widget widget; /* unused */ XtPointer client_data; XtPointer call_data; /* unused */ { Widget popup = (Widget) client_data; XtPopdown(popup); XtDestroyWidget(popup); } void WMDeletePopup(popup, event) Widget popup; /* transient shell */ XEvent* event; { String shellName; String buttonName; Widget button; shellName = XtName(popup); if (strcmp(shellName, XmhNconfirm) == 0) buttonName = "*no"; else if (strcmp(shellName, XmhNprompt) == 0) buttonName = "*cancel"; else if (strcmp(shellName, XmhNnotice) == 0) buttonName = "*confirm"; else if (strcmp(shellName, XmhNerror) == 0) buttonName = "*OK"; else return; /* WM may kill us */ button = XtNameToWidget(popup, buttonName); if (! button) return; XtCallActionProc(button, "set", event, (String*)NULL, ZERO); XtCallActionProc(button, "notify", event, (String*)NULL, ZERO); XtCallActionProc(button, "unset", event, (String*)NULL, ZERO); } static void TheUsual(popup) Widget popup; /* shell */ { XtInstallAllAccelerators(popup, popup); XtAugmentTranslations(popup, app_resources.wm_protocols_translations); XtRealizeWidget(popup); XDefineCursor(XtDisplay(popup), XtWindow(popup), app_resources.cursor); (void) XSetWMProtocols(XtDisplay(popup), XtWindow(popup), protocolList, XtNumber(protocolList)); } /*ARGSUSED*/ void XmhPromptOkayAction(w, event, params, num_params) Widget w; /* the "value" widget in the Dialog box */ XEvent *event; /* unused */ String *params; /* unused */ Cardinal *num_params; /* unused */ { XtCallCallbacks(XtNameToWidget(XtParent(w), XmhNokay), XtNcallback, (XtPointer)XtParent(w)); } void PopupPrompt(transientFor, question, okayCallback) Widget transientFor; /* required to be a top-level shell */ String question; /* the prompting string */ XtCallbackProc okayCallback; /* CreateFolder() */ { Widget popup; Widget dialog; Widget value; Position x, y; Boolean positioned; Arg args[3]; static XtTranslations PromptTextTranslations = NULL; DeterminePopupPosition(&x, &y, (Widget*)NULL); XtSetArg(args[0], XtNallowShellResize, True); XtSetArg(args[1], XtNinput, True); XtSetArg(args[2], XtNtransientFor, transientFor); popup = XtCreatePopupShell(XmhNprompt, transientShellWidgetClass, toplevel, args, THREE); positioned = PositionThePopup(popup, x, y); XtSetArg(args[0], XtNlabel, question); XtSetArg(args[1], XtNvalue, ""); dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args, TWO); XtSetArg(args[0], XtNresizable, True); XtSetValues( XtNameToWidget(dialog, "label"), args, ONE); value = XtNameToWidget(dialog, XmhNvalue); XtSetValues( value, args, ONE); if (! PromptTextTranslations) PromptTextTranslations = XtParseTranslationTable ("Return: XmhPromptOkayAction()\n\ CtrlR: no-op(RingBell)\n\ CtrlS: no-op(RingBell)\n"); XtOverrideTranslations(value, PromptTextTranslations); XawDialogAddButton(dialog, XmhNokay, okayCallback, (XtPointer) dialog); XawDialogAddButton(dialog, "cancel", DestroyPopup, (XtPointer) popup); TheUsual(popup); InsureVisibility(popup, dialog, x, y, !positioned, False); XtPopup(popup, XtGrabNone); } /* ARGSUSED */ static void FreePopupStatus( w, closure, call_data ) Widget w; /* unused */ XtPointer closure; XtPointer call_data; /* unused */ { PopupStatus popup = (PopupStatus)closure; XtPopdown(popup->popup); XtDestroyWidget(popup->popup); if (popup->shell_command) XtFree(popup->shell_command); XtFree((char *) closure); } void PopupNotice(message, callback, closure) String message; XtCallbackProc callback; XtPointer closure; { PopupStatus popup_status = (PopupStatus)closure; Widget transientFor; Widget dialog; Widget value; Position x, y; Arg args[3]; char command[65], label[128]; if (popup_status == (PopupStatus)NULL) { popup_status = XtNew(PopupStatusRec); popup_status->lastInput = lastInput; popup_status->shell_command = (char*)NULL; } if (! popup_status->shell_command) { /* MH command */ if (sscanf( message, "%64s", command ) != 1) (void) strcpy( command, "system" ); else { int l = strlen(command); if (l && command[--l] == ':') command[l] = '\0'; } (void) sprintf( label, "%.64s command returned:", command ); } else { /* arbitrary shell command */ int len = strlen(popup_status->shell_command); (void) sprintf(label, "%.88s %s\nshell command returned:", popup_status->shell_command, ((len > 88) ? "[truncated]" : "")); } DeterminePopupPosition(&x, &y, &transientFor); XtSetArg( args[0], XtNallowShellResize, True ); XtSetArg( args[1], XtNinput, True ); XtSetArg( args[2], XtNtransientFor, transientFor); popup_status->popup = XtCreatePopupShell(XmhNnotice, transientShellWidgetClass, toplevel, args, THREE); PositionThePopup(popup_status->popup, x, y); XtSetArg( args[0], XtNlabel, label ); XtSetArg( args[1], XtNvalue, message ); dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup_status->popup, args, TWO); /* The text area of the dialog box will not be editable. */ value = XtNameToWidget(dialog, XmhNvalue); XtSetArg( args[0], XtNeditType, XawtextRead); XtSetArg( args[1], XtNdisplayCaret, False); XtSetValues( value, args, TWO); XtOverrideTranslations(value, NoTextSearchAndReplace); XawDialogAddButton( dialog, XmhNconfirm, ((callback != (XtCallbackProc) NULL) ? callback : (XtCallbackProc) FreePopupStatus), (XtPointer) popup_status ); TheUsual(popup_status->popup); InsureVisibility(popup_status->popup, dialog, x, y, False, False); XtPopup(popup_status->popup, XtGrabNone); } void PopupConfirm(center_widget, question, affirm_callbacks, negate_callbacks) Widget center_widget; /* where to center; may be NULL */ String question; XtCallbackList affirm_callbacks; XtCallbackList negate_callbacks; { Widget popup; Widget dialog; Widget button; Widget transientFor; Position x, y; Arg args[3]; static XtCallbackRec callbacks[] = { {DestroyPopup, (XtPointer) NULL}, {(XtCallbackProc) NULL, (XtPointer) NULL} }; DeterminePopupPosition(&x, &y, &transientFor); XtSetArg(args[0], XtNinput, True); XtSetArg(args[1], XtNallowShellResize, True); XtSetArg(args[2], XtNtransientFor, transientFor); popup = XtCreatePopupShell(XmhNconfirm, transientShellWidgetClass, toplevel, args, THREE); PositionThePopup(popup, x, y); XtSetArg(args[0], XtNlabel, question); dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, popup, args, ONE); callbacks[0].closure = (XtPointer) popup; XtSetArg(args[0], XtNcallback, callbacks); button = XtCreateManagedWidget("yes", commandWidgetClass, dialog, args, ONE); if (affirm_callbacks) XtAddCallbacks(button, XtNcallback, affirm_callbacks); button = XtCreateManagedWidget("no", commandWidgetClass, dialog, args, ZERO); XtAddCallback(button, XtNcallback, DestroyPopup, (XtPointer) popup); if (negate_callbacks) XtAddCallbacks(button, XtNcallback, negate_callbacks); TheUsual(popup); CenterPopupPosition(center_widget ? center_widget : transientFor, popup, x, y); InsureVisibility(popup, dialog, x, y, False, False); XtPopup(popup, XtGrabNone); } void PopupError(widget, message) Widget widget; /* transient for this top-level shell, or NULL */ String message; { Widget transFor, error_popup, dialog; Position x, y; Boolean positioned; Arg args[3]; static XtCallbackRec callbacks[] = { {DestroyPopup, (XtPointer) NULL}, {(XtCallbackProc) NULL, (XtPointer) NULL} }; transFor = widget; DeterminePopupPosition(&x, &y, transFor ? (Widget*)NULL : &transFor); XtSetArg(args[0], XtNallowShellResize, True); XtSetArg(args[1], XtNinput, True); XtSetArg(args[2], XtNtransientFor, transFor); error_popup = XtCreatePopupShell(XmhNerror, transientShellWidgetClass, toplevel, args, THREE); positioned = PositionThePopup(error_popup, x, y); XtSetArg(args[0], XtNlabel, message); dialog = XtCreateManagedWidget(XmhNdialog, dialogWidgetClass, error_popup, args, ONE); callbacks[0].closure = (XtPointer) error_popup; XtSetArg(args[0], XtNcallback, callbacks); XawDialogAddButton(dialog, "OK", DestroyPopup, (XtPointer) error_popup); TheUsual(error_popup); InsureVisibility(error_popup, dialog, x, y, !positioned, !positioned); XtPopup(error_popup, XtGrabNone); } /*ARGSUSED*/ void PopupWarningHandler(name, type, class, msg, params, num) String name; String type; String class; String msg; String *params; Cardinal *num; { char *ptr; int i; String par[10]; char message[500]; char buffer[500]; static Boolean allowPopup = True; /* protect against recursion */ XtGetErrorDatabaseText(name, type, class, msg, buffer, 500); if (params && num && *num) { i = (*num <= 10) ? *num : 10; memmove( (char*)par, (char*)params, i * sizeof(String)); bzero( &par[i], (10-i) * sizeof(String)); if (*num > 10) par[9] = "(truncated)"; (void) sprintf(message, buffer, par[0], par[1], par[2], par[3], par[4], par[5], par[6], par[7], par[8], par[9]); ptr = message; } else { ptr = buffer; } if (allowPopup) { allowPopup = False; PopupError((Widget)NULL, ptr); allowPopup = True; } else { fprintf(stderr, ptr); } }