summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/gui/message-window.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/gui/message-window.c')
-rw-r--r--Build/source/texk/xdvik/gui/message-window.c1207
1 files changed, 1207 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/gui/message-window.c b/Build/source/texk/xdvik/gui/message-window.c
new file mode 100644
index 00000000000..55e148daa1b
--- /dev/null
+++ b/Build/source/texk/xdvik/gui/message-window.c
@@ -0,0 +1,1207 @@
+/*------------------------------------------------------------
+message-window.c: message popups for xdvi.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+------------------------------------------------------------*/
+
+
+
+/*
+
+============================================
+Suggested Policy for using the GUI messages:
+============================================
+
+- Use the statusline for shorter messages, a message window for more
+ important messages or such where you'd like to give further help info
+ (see the `helptext' argument of popup_message()). When in doubt,
+ prefer the statusline (excessive use of popup windows is a nuisance
+ for the user).
+
+- Don't use any of the GUI messages to report internal workings of
+ the program; for important internal information, there should be a
+ debugging setting to print it to stderr. Use the GUI messages
+ only in situations such as the following:
+
+ - to give the user feedback on actions that (s)he initiated
+
+ - to indicate that an internal action causes a delay perceptible
+ by the user (as a rough guide: a delay of more than half a second)
+
+ - to report situations that might require new actions by the user.
+
+*/
+
+#include <ctype.h>
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+#include "string-utils.h"
+
+/* Xaw specific stuff */
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#ifdef MOTIF
+# include <Xm/DialogS.h>
+# include <Xm/MessageB.h>
+# include <Xm/PushB.h>
+# include <Xm/Label.h>
+# include <Xm/Form.h>
+# include <Xm/MenuShell.h>
+# include <Xm/Protocols.h>
+# include <Xm/AtomMgr.h>
+#else
+# include <X11/Xaw/Paned.h>
+# include <X11/Xaw/Box.h>
+# include <X11/Xaw/MenuButton.h>
+# include <X11/Xaw/SimpleMenu.h>
+# include <X11/Xaw/Sme.h>
+# include <X11/Xaw/SmeBSB.h>
+# include <X11/Xaw/AsciiText.h>
+# include <X11/Xaw/Dialog.h>
+#endif
+
+#include "kpathsea/c-vararg.h"
+#include "xdvi.h"
+#include "util.h"
+#include "string-utils.h"
+#include "x_util.h"
+#include "message-window.h"
+
+/* have no more than MAX_POPUPS open simultaneously */
+#define MAX_POPUPS 10
+
+/* offset for cascading popups */
+#define POPUP_OFFSET ((my_popup_num * 20))
+
+#ifdef MOTIF
+/* wrap messages after MSG_WRAP_LEN characters at whitespace */
+#define MSG_WRAP_LEN 60
+#endif
+
+/* array of active popups: */
+static int g_popup_array[MAX_POPUPS];
+
+static Atom WM_DELETE_WINDOW;
+
+/* arrays for saving the widgets of multiple popups;
+ * same index as in g_popup_array:
+ */
+#ifdef MOTIF
+static Widget popup_window[MAX_POPUPS], dialog[MAX_POPUPS];
+#else
+static Widget popup_window[MAX_POPUPS], message_box[MAX_POPUPS],
+ message_text[MAX_POPUPS], message_paned[MAX_POPUPS],
+ message_ok[MAX_POPUPS], message_help[MAX_POPUPS], message_not_ok[MAX_POPUPS];
+#endif
+
+/* map popupMessageT's to strings/motif dialog elements */
+static struct message_map {
+ char *window_title;
+#ifdef MOTIF
+ int motif_msg_type;
+#endif
+} my_msg_map[] = {
+ { "Xdvi Question"
+#ifdef MOTIF
+ , XmDIALOG_QUESTION
+#endif
+ }, /* MSG_QUESTION */
+ { "Xdvi Help"
+#ifdef MOTIF
+ , XmDIALOG_INFORMATION
+#endif
+ }, /* MSG_HELP */
+ { "Xdvi Info"
+#ifdef MOTIF
+ , XmDIALOG_INFORMATION
+#endif
+ }, /* MSG_INFO */
+ { "Xdvi Warning"
+#ifdef MOTIF
+ , XmDIALOG_WARNING
+#endif
+ }, /* MSG_WARN */
+ { "Xdvi Error"
+#ifdef MOTIF
+ , XmDIALOG_ERROR
+#endif
+ }, /* MSG_ERR */
+};
+
+struct ok_or_cancel_cb {
+ message_cbT callback; /* callback function */
+ XtPointer arg; /* arg for callback function */
+};
+
+struct pre_ok_or_cancel_cb {
+ pre_message_cbT callback; /* callback function */
+ XtPointer arg; /* arg for callback function */
+};
+
+static struct ok_or_cancel_cb yes_callbacks[MAX_POPUPS];
+static struct ok_or_cancel_cb no_callbacks[MAX_POPUPS];
+static struct ok_or_cancel_cb cancel_callbacks[MAX_POPUPS];
+static struct pre_ok_or_cancel_cb pre_callbacks[MAX_POPUPS];
+
+
+static void
+popdown_cancel(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ size_t idx;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ ASSERT(*num_params == 1, "Wrong number of parameters in callback");
+ idx = strtoul(*params, (char **)NULL, 10);
+
+ /* First call pre_message_cb with window widget ID
+ * as additional parameter.
+ */
+ if (pre_callbacks[idx].callback != NULL) {
+ pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg);
+ }
+
+ /* Then pop down window and mark its position as free, then
+ * invoke the OK callback. The reason for this is that the callback
+ * may need to wait for open windows.
+ */
+ XtPopdown(popup_window[idx]);
+ XtDestroyWidget(popup_window[idx]);
+ g_popup_array[idx] = 0;
+ XSync(DISP, True);
+
+ /* invoke the callback if present */
+ if (cancel_callbacks[idx].callback != NULL) {
+ cancel_callbacks[idx].callback(cancel_callbacks[idx].arg);
+ }
+}
+
+#ifndef MOTIF
+static void
+xaw_popdown(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ size_t idx;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ ASSERT(*num_params == 1, "Wrong number of parameters in callback");
+ idx = strtoul(*params, (char **)NULL, 10);
+
+ /*
+ NOTE: first pop down window and mark its position as free, then
+ invoke the callback. The reason for this is that the callback
+ may need to wait for open windows.
+ */
+ XtPopdown(popup_window[idx]);
+ XtDestroyWidget(popup_window[idx]);
+ g_popup_array[idx] = 0;
+ XSync(DISP, True);
+}
+
+#endif
+
+static XtActionsRec popdown_actions[] = {
+ {"close-popup-cancel", popdown_cancel },
+#if !MOTIF
+ {"WM_popdown", popdown_cancel },
+ {"close-popup", xaw_popdown },
+#endif
+};
+
+static void
+ok_action(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ int idx = -1;
+
+ UNUSED(call_data);
+
+#if MOTIF
+ UNUSED(client_data);
+ XtVaGetValues(w, XmNuserData, &idx, NULL);
+ ASSERT(idx >= 0, "Couldn't get idx from XmNuserData!");
+#else
+ UNUSED(w);
+ idx = (int)client_data;
+#endif
+
+#if DEBUG
+ fprintf(stderr, "quit_action called for popup %d\n", idx);
+#endif
+ ASSERT(idx >= 0 && idx < MAX_POPUPS, "Invalid widget index in ok_action()");
+
+ /* First call pre_message_cb with window widget ID
+ * as additional parameter.
+ */
+ if (pre_callbacks[idx].callback != NULL) {
+ pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg);
+ }
+
+ /* Then pop down window and mark its position as free, then
+ * invoke the OK callback. The reason for this is that the callback
+ * may need to wait for open windows.
+ */
+ XtPopdown(popup_window[idx]);
+ XtDestroyWidget(popup_window[idx]);
+ g_popup_array[idx] = 0;
+ XSync(DISP, True);
+
+ if (yes_callbacks[idx].callback != NULL) {
+ yes_callbacks[idx].callback(yes_callbacks[idx].arg);
+ }
+}
+
+/*------------------------------------------------------------
+ * help_action
+ *
+ * Arguments:
+ * Widget w, XtPointer call_data
+ * - (ignored)
+ * XtPointer client_data
+ * - the help string
+ *
+ * Returns:
+ * void
+ *
+ * Purpose:
+ * Callback for the `Help' button; opens another window
+ * containing the help text. The new window won't have
+ * another `Help' button.
+ *------------------------------------------------------------*/
+
+static void
+help_action(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ UNUSED(call_data);
+
+ /* open another window with the help text */
+ popup_message(get_matching_parent(w, globals.widgets.top_level, "message_popup", NULL), MSG_HELP, NULL, client_data);
+}
+
+
+/*
+ * Callback for cancel button in choice_dialog.
+*/
+static void
+cancel_action(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ int idx = -1;
+
+ UNUSED(call_data);
+
+#if MOTIF
+ /* for motif, the index is in XmNuserData */
+ UNUSED(client_data);
+ if (strcmp(XtName(w), Xdvi_MESSAGE_SHELL_NAME) == 0) {
+ /* invoked by the WM, get the messagebox child */
+ Widget child;
+ if (get_widget_by_name(&child, w, Xdvi_MESSAGE_DIALOG_NAME, True)) {
+ XtVaGetValues(child, XmNuserData, &idx, NULL);
+ }
+ }
+ else {
+ XtVaGetValues(w, XmNuserData, &idx, NULL);
+ }
+ ASSERT(idx >= 0, "Couldn't get idx from XmNuserData!");
+#else
+ UNUSED(w);
+ idx = (int)client_data;
+#endif
+ ASSERT(idx >= 0 && idx < MAX_POPUPS, "Invalid widget index in cancel_action()");
+
+ /* First call pre_message_cb with window widget ID
+ * as additional parameter.
+ */
+ if (pre_callbacks[idx].callback != NULL) {
+ pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg);
+ }
+
+ /* Then pop down window and mark its position as free, then
+ * invoke the OK callback. The reason for this is that the callback
+ * may need to wait for open windows.
+ */
+ XtPopdown(popup_window[idx]);
+ XtDestroyWidget(popup_window[idx]);
+ g_popup_array[idx] = 0;
+ XSync(DISP, True);
+
+ /* invoke the callback if present */
+ if (cancel_callbacks[idx].callback != NULL) {
+ cancel_callbacks[idx].callback(cancel_callbacks[idx].arg);
+ }
+}
+
+#if MOTIF
+static void
+not_ok_action(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ /* Note: unmanage the parent of the button */
+ int idx = -1;
+
+ UNUSED(client_data);
+
+ UNUSED(call_data);
+ XtVaGetValues(w, XmNuserData, &idx, NULL);
+ ASSERT(idx >= 0, "Couldn't get idx from XmNuserData!");
+ ASSERT(idx >= 0 && idx < MAX_POPUPS, "Invalid widget index in ok_action()");
+
+ /* First call pre_message_cb with window widget ID
+ * as additional parameter.
+ */
+ if (pre_callbacks[idx].callback != NULL) {
+ pre_callbacks[idx].callback(popup_window[idx], pre_callbacks[idx].arg);
+ }
+
+ /* Then pop down window and mark its position as free, then
+ * invoke the OK callback. The reason for this is that the callback
+ * may need to wait for open windows.
+ */
+ XtPopdown(popup_window[idx]);
+ XtDestroyWidget(popup_window[idx]);
+ g_popup_array[idx] = 0;
+ XSync(DISP, True);
+
+ if (no_callbacks[idx].callback != NULL) {
+ no_callbacks[idx].callback(no_callbacks[idx].arg);
+ }
+}
+#endif /* MOTIF */
+
+/*------------------------------------------------------------
+ * create_dialogs
+ *
+ * Arguments:
+ * Widget toplevel - parent for the dialog window
+ *
+ * helptext - if not-NULL, create an additional
+ * `help' button
+ *
+ * cnt - number of current popup dialog
+ *
+ * Returns:
+ * void
+ *
+ * Purpose:
+ * Create message dialog widgets
+ *------------------------------------------------------------*/
+
+static Widget
+create_dialogs(popupMessageSizeHintT size,
+ Widget parent,
+ int cnt,
+ const char *helptext,
+ pre_message_cbT pre_cb, XtPointer arg,
+ const char *yes_button, message_cbT yes_cb, XtPointer yes_arg,
+ const char *no_button, message_cbT no_cb, XtPointer no_arg,
+ const char *cancel_button, message_cbT cancel_cb, XtPointer cancel_arg)
+{
+ Widget new_popup_window;
+ char *translations_str = NULL;
+#ifdef MOTIF
+ Widget new_dialog;
+ UNUSED(size);
+#else
+ char *key_translations_str = NULL;
+ int msg_w = 400, msg_h = 100;
+ Widget new_message_paned, new_message_text, new_message_box, new_message_ok,
+ new_message_help = 0, new_message_not_ok;
+ XtTranslations wm_translations, key_translations;
+#endif
+
+ /* save callbacks to global arrays */
+ pre_callbacks[cnt].callback = pre_cb;
+ pre_callbacks[cnt].arg = arg;
+
+ yes_callbacks[cnt].callback = yes_cb;
+ yes_callbacks[cnt].arg = yes_arg;
+
+ no_callbacks[cnt].callback = no_cb;
+ no_callbacks[cnt].arg = no_arg;
+
+ cancel_callbacks[cnt].callback = cancel_cb;
+ cancel_callbacks[cnt].arg = cancel_arg;
+
+ XtAddActions(popdown_actions, XtNumber(popdown_actions));
+
+#ifndef MOTIF
+ /* get index into WM_popdown arg */
+ translations_str = get_string_va("<Message>WM_PROTOCOLS: WM_popdown(%d)", cnt);
+ wm_translations = XtParseTranslationTable(translations_str);
+ free(translations_str);
+#endif
+
+ if (!XtIsRealized(globals.widgets.top_level)) {
+ /* If toplevel window hasn't been realized yet, create a new toplevel shell
+ (otherwise, setting visual/color map wouldn't work); use same application names
+ so that resource settings will also apply to this window.
+ */
+ new_popup_window = XtVaAppCreateShell("xdvi", "Xdvi",
+ transientShellWidgetClass, DISP,
+ NULL);
+ }
+ else {
+ new_popup_window = XtVaCreatePopupShell(Xdvi_MESSAGE_SHELL_NAME,
+#ifdef MOTIF
+ xmDialogShellWidgetClass, parent,
+ XmNdeleteResponse, XmDO_NOTHING, /* we'll take care of that ourselves */
+#else
+ transientShellWidgetClass, parent,
+ XtNx, 60,
+ XtNy, 80,
+ XtNtranslations, wm_translations,
+ XtNaccelerators, G_accels_cr,
+#endif
+ XtNtransientFor, parent,
+ XtNmappedWhenManaged, False,
+ NULL);
+ }
+
+#ifdef MOTIF
+
+ WM_DELETE_WINDOW = XmInternAtom(XtDisplay(new_popup_window), "WM_DELETE_WINDOW", False);
+ XmAddWMProtocolCallback(new_popup_window, WM_DELETE_WINDOW, cancel_action, NULL);
+
+ /* We also need to override the default ESC binding to use our internal
+ housekeeping functions */
+ translations_str = get_string_va("#override\n<Key>osfCancel:close-popup-cancel(%d)", cnt);
+/* { */
+/* XtTranslations xlats; */
+/* char *translation_str = get_string_va("<Key>osfCancel:close-popup-cancel(%d)", cnt); */
+/* xlats = XtParseTranslationTable(translation_str); */
+/* free(translation_str); */
+/* XtOverrideTranslations(new_dialog, xlats); */
+/* } */
+
+ new_dialog = XtVaCreateWidget(Xdvi_MESSAGE_DIALOG_NAME, xmMessageBoxWidgetClass, new_popup_window,
+ XmNdialogType, XmDIALOG_WARNING, /* default */
+ XmNtraversalOn, True,
+ XmNhighlightOnEnter, True,
+ XmNuserData, cnt,
+ XmNtranslations, XtParseTranslationTable(translations_str),
+ NULL);
+ free(translations_str);
+ XtAddCallback(new_dialog, XmNokCallback, ok_action, NULL);
+
+ if (no_button != NULL) {
+ Arg args[4];
+ Widget b;
+ XmString b_str = XmStringCreateLocalized((char *)no_button);
+ XtSetArg(args[0], XmNlabelString, b_str);
+ b = XmCreatePushButton(new_dialog, "no_button", args, 1);
+ XtAddCallback(b, XmNactivateCallback, not_ok_action, NULL);
+ XtManageChild(b);
+ }
+
+ if (cancel_button != NULL) {
+ XmString cancel_label = XmStringCreateLtoR((char *)cancel_button, G_charset);
+ XtVaSetValues(XmMessageBoxGetChild(new_dialog, XmDIALOG_CANCEL_BUTTON),
+ XmNlabelString, cancel_label, NULL);
+ XmStringFree(cancel_label);
+ XtAddCallback(new_dialog, XmNcancelCallback, cancel_action, NULL);
+ }
+ else {
+ XtUnmanageChild(XmMessageBoxGetChild(new_dialog, XmDIALOG_CANCEL_BUTTON));
+ }
+ XtInstallAllAccelerators(new_dialog,
+ XmMessageBoxGetChild(new_dialog, XmDIALOG_OK_BUTTON));
+
+ if (helptext != NULL) {
+ XtAddCallback(new_dialog, XmNhelpCallback, help_action, (XtPointer)helptext);
+ }
+ else {
+ XtUnmanageChild(XmMessageBoxGetChild(new_dialog, XmDIALOG_HELP_BUTTON));
+ }
+
+ if (yes_button != NULL) { /* change `OK' button label */
+ XmString yes_label;
+ yes_label = XmStringCreateLtoR((char *)yes_button, G_charset);
+ XtVaSetValues(XmMessageBoxGetChild(new_dialog, XmDIALOG_OK_BUTTON),
+ XmNlabelString, yes_label, NULL);
+ XmStringFree(yes_label);
+ }
+
+ /* insert the new widgets into the global arrays */
+ dialog[cnt] = new_dialog;
+
+#else /* MOTIF */
+ switch (size) {
+ case SIZE_SMALL:
+ msg_w = 300;
+ msg_h = 100;
+ break;
+ case SIZE_MEDIUM:
+ msg_w = 430;
+ msg_h = 160;
+ break;
+ case SIZE_LARGE:
+ msg_w = 450;
+ msg_h = 180;
+ break;
+ }
+ WM_DELETE_WINDOW = XInternAtom(XtDisplay(new_popup_window), "WM_DELETE_WINDOW", False);
+
+ new_message_paned = XtVaCreateManagedWidget("message_paned", panedWidgetClass, new_popup_window,
+ XtNaccelerators, G_accels_cr,
+ NULL);
+
+ new_message_text = XtVaCreateManagedWidget("message_text", asciiTextWidgetClass, new_message_paned,
+/* XtNheight, 100, */
+/* XtNwidth, 400, */
+ XtNwidth, msg_w,
+ XtNheight, msg_h,
+ /* wrap horizontally instead of scrolling
+ * TODO: this won't work for the first widget instance?
+ */
+ XtNwrap, XawtextWrapWord,
+ XtNscrollVertical, XAW_SCROLL_ALWAYS,
+ XtNeditType, XawtextRead,
+ XtNinput, True,
+ XtNdisplayCaret, False,
+ XtNleftMargin, 5,
+ XtNaccelerators, G_accels_cr,
+ NULL);
+
+ /* box for the OK/Cancel button */
+ new_message_box = XtVaCreateManagedWidget("message_box", formWidgetClass, new_message_paned,
+ /* resizing by user isn't needed */
+ XtNshowGrip, False,
+ XtNdefaultDistance, 6, /* some padding */
+ /* resizing the window shouldn't influence this box,
+ * but only the text widget
+ */
+ XtNskipAdjust, True,
+ XtNaccelerators, G_accels_cr,
+ NULL);
+
+ new_message_ok = XtVaCreateManagedWidget(yes_button == NULL ? "OK" : yes_button,
+ commandWidgetClass, new_message_box,
+ XtNtop, XtChainTop,
+ XtNbottom, XtChainBottom,
+ XtNleft, XtChainLeft,
+ XtNright, XtChainLeft,
+ XtNaccelerators, G_accels_cr,
+ NULL);
+ /* add quit_action callback for the "OK" button */
+ /* FIXME: how to make accelerators be accepted by new_popup_window as well? */
+ key_translations_str = get_string_va("<Key>q:close-popup-cancel(%d)\n"
+ "<Key>Return:close-popup-cancel(%d)\n"
+ "<Key>Escape:close-popup-cancel(%d)\n",
+ cnt, cnt, cnt);
+ key_translations = XtParseTranslationTable(key_translations_str);
+ free(key_translations_str);
+ XtOverrideTranslations(new_popup_window, key_translations);
+ XtOverrideTranslations(new_message_paned, key_translations);
+ XtOverrideTranslations(new_message_text, key_translations);
+
+ XtInstallAllAccelerators(new_message_box, new_message_ok);
+ XtAddCallback(new_message_ok, XtNcallback, ok_action, (XtPointer)cnt);
+
+ /* we create additional buttons in any case,
+ to make the sizing more consistent */
+ new_message_help = XtVaCreateManagedWidget("Help", commandWidgetClass, new_message_box,
+ XtNtop, XtChainTop,
+ XtNfromHoriz, new_message_ok,
+ XtNbottom, XtChainBottom,
+ XtNleft, XtChainRight,
+ XtNright, XtChainRight,
+ XtNaccelerators, G_accels_cr,
+ NULL);
+ message_help[cnt] = new_message_help;
+
+ /* add cancel button */
+ new_message_not_ok = XtVaCreateManagedWidget(cancel_button == NULL ? "Cancel" : cancel_button,
+ commandWidgetClass, new_message_box,
+ XtNtop, XtChainTop,
+ XtNfromHoriz, new_message_ok,
+ XtNbottom, XtChainBottom,
+ XtNleft, helptext == NULL ? XtChainRight : XtChainLeft,
+ XtNright, helptext == NULL ? XtChainRight : XtChainLeft,
+ XtNaccelerators, G_accels_cr,
+ NULL);
+ message_not_ok[cnt] = new_message_not_ok;
+
+ if (no_button != NULL) {
+ ASSERT(0, "third button not yet implemented in Xaw!!!");
+ }
+
+
+ { /* set all buttons to same size */
+ Dimension w1, w2, w3, max;
+ XtVaGetValues(new_message_ok, XtNwidth, &w1, NULL);
+ XtVaGetValues(new_message_help, XtNwidth, &w2, NULL);
+ XtVaGetValues(new_message_not_ok, XtNwidth, &w3, NULL);
+ max = w1;
+ if (w2 > max)
+ max = w2;
+ if (w3 > max)
+ max = w3;
+ XtVaSetValues(new_message_ok, XtNwidth, max, NULL);
+ XtVaSetValues(new_message_help, XtNwidth, max, NULL);
+ XtVaSetValues(new_message_not_ok, XtNwidth, max, NULL);
+ }
+ /* if helptext argument is not-NULL, add help_action callback,
+ else unmanage help button */
+ if (helptext != NULL) {
+ XtAddCallback(new_message_help, XtNcallback, help_action, (XtPointer)helptext);
+ }
+ else {
+ XtUnmanageChild(new_message_help);
+ }
+
+ if (cancel_button != NULL) {
+ XtAddCallback(new_message_not_ok, XtNcallback, cancel_action, (XtPointer)cnt);
+ }
+ else {
+ XtUnmanageChild(new_message_not_ok);
+ }
+ /* insert the new widgets into the global arrays */
+ message_box[cnt] = new_message_box;
+ message_paned[cnt] = new_message_paned;
+ message_text[cnt] = new_message_text;
+ message_ok[cnt] = new_message_ok;
+
+#endif /* MOTIF */
+ popup_window[cnt] = new_popup_window;
+
+ return new_popup_window;
+}
+
+
+/*
+ * Popup a window with wrapped text in it.
+ * For Motif, the text is explicitly wrapped inside this method.
+ */
+static Widget
+internal_popup_window(Widget parent,
+ popupMessageSizeHintT size,
+ popupMessageT type,
+ int x_coord, int y_coord,
+ const char *helptext,
+ char *msg_buf,
+#ifndef MOTIF
+ const char *xaw_ret_action_str,
+#endif
+ pre_message_cbT pre_cb, XtPointer arg,
+ const char *yes_button, message_cbT yes_cb, XtPointer yes_arg,
+ const char *no_button, message_cbT no_cb, XtPointer no_arg,
+ const char *cancel_button, message_cbT cancel_cb, XtPointer cancel_arg)
+{
+ char win_title[64];
+ int my_popup_num = 0;
+#ifdef MOTIF
+ XmString str;
+#endif
+ Widget ret;
+
+ ASSERT(type < (sizeof my_msg_map / sizeof my_msg_map[0]), "too few elements in my_msg_map");
+ sprintf(win_title, my_msg_map[type].window_title);
+
+#if DEBUG
+ fprintf(stderr, "internal_popup_window called with prompt: \"%s\"\n", msg_buf);
+#endif
+
+ if (globals.widgets.top_level == 0) {
+ /* If toplevel window hasn't been create yet, dump messages to STDERR and return.
+ All text must be passed as arguments to fprintf (NOT puts), since they are
+ supposed to be printf-format strings (i.e. with doubled `%' to escape them)
+ */
+ fprintf(stderr, "\n%s:\n", my_msg_map[type].window_title);
+ fprintf(stderr, msg_buf);
+ fprintf(stderr, "\n");
+ if (helptext) {
+ fprintf(stderr, "---------- helptext ----------\n");
+ fprintf(stderr, helptext);
+ fprintf(stderr, "\n---------- end of helptext ----------\n");
+ }
+ return NULL;
+ }
+ /* search for first free position in g_popup_array */
+ while (my_popup_num < MAX_POPUPS && (g_popup_array[my_popup_num] == 1)) {
+ my_popup_num++;
+ }
+ if (my_popup_num == MAX_POPUPS) {
+ /* already enough popups on screen, just dump it to stderr */
+ fprintf(stderr, "%s:\n", win_title);
+ fprintf(stderr, msg_buf);
+ /* Note: If a mad function continues to open popups, this will
+ * stop after MAX_POPUPS, but open a new window for each
+ * window the user pops down. Maybe we ought to do something
+ * about this.
+ */
+ return NULL;
+ }
+ else {
+ /* mark it as non-free */
+ g_popup_array[my_popup_num] = 1;
+ }
+#if DEBUG
+ fprintf(stderr, "first free position in g_popup_array: %d\n", my_popup_num);
+#endif
+
+ /* just to make sure ... */
+ if (parent == NULL)
+ parent = globals.widgets.top_level;
+
+ /* create a new set of widgets for the additional popup. */
+ ret = create_dialogs(size, parent,
+ my_popup_num,
+ helptext,
+ pre_cb, arg,
+ yes_button, yes_cb, yes_arg,
+ no_button, no_cb, no_arg,
+ cancel_button, cancel_cb, cancel_arg);
+#ifdef MOTIF
+ XtVaSetValues(popup_window[my_popup_num], XmNtitle, win_title, NULL);
+ XtVaSetValues(dialog[my_popup_num], XmNdialogType, my_msg_map[type].motif_msg_type, NULL);
+ { /* wrap message at space before MSG_WRAP_LEN */
+ char *testwrap = msg_buf;
+ int ctr;
+ for (ctr = 0; *testwrap++; ctr++) {
+ if (*testwrap == '\n') {
+ ctr = 0;
+ }
+ else if (ctr > MSG_WRAP_LEN) {
+ size_t before_len = 0, after_len = 0;
+ char *before_ptr, *after_ptr;
+ before_ptr = after_ptr = testwrap;
+ /* try to find shortest sequence before or after point to wrap at;
+ this seems to give the most pleasing results.
+ */
+ while (before_ptr > msg_buf && !isspace((int)*--before_ptr)) {
+ before_len++;
+ }
+ while (*after_ptr != '\0' && !isspace((int)*++after_ptr)) {
+ after_len++;
+ }
+
+ if (before_len < after_len && isspace((int)*before_ptr)) {
+ /* use last in sequence of multiple spaces */
+ while (isspace((int)*++before_ptr)) { ; }
+ /* back up, and wrap */
+ *--before_ptr = '\n';
+ ctr = 0;
+ }
+ else if (isspace((int)*after_ptr)) {
+ /* use last in sequence of multiple spaces */
+ while (isspace((int)*++after_ptr)) { ; }
+ /* back up, and wrap */
+ *--after_ptr = '\n';
+ ctr = 0;
+ }
+ }
+ }
+ }
+ str = XmStringCreateLtoR((char *)msg_buf, G_charset);
+ XtVaSetValues(dialog[my_popup_num],
+ XmNmessageString, str,
+ XmNtraversalOn, True,
+ XmNhighlightOnEnter, True,
+ NULL);
+ XmStringFree(str);
+
+ XtManageChild(dialog[my_popup_num]);
+
+ if (x_coord > 0 && y_coord > 0) {
+ position_window(XtParent(dialog[my_popup_num]), (Position)x_coord, (Position)y_coord);
+ }
+
+ XtPopup(XtParent(dialog[my_popup_num]), XtGrabNone);
+/* XtPopup(XtParent(dialog[my_popup_num]), XtGrabExclusive); */
+
+#else /* MOTIF */
+
+ /* add a binding of xaw_ret_action_str to <Return> to relevant widgets.
+ The callbacks (xaw_ret_action_str) are responsible for parsing the
+ passed arguments (pointers, or empty arguments).
+ */
+ if (xaw_ret_action_str != NULL) {
+ XtTranslations xlats;
+ char *translation_str;
+
+ if (yes_arg != NULL)
+ translation_str = get_string_va("<Key>Return:close-popup(%d)%s(%p)",
+ my_popup_num, xaw_ret_action_str, yes_arg);
+ else
+ translation_str = get_string_va("<Key>Return:close-popup(%d)%s()",
+ my_popup_num, xaw_ret_action_str);
+
+ xlats = XtParseTranslationTable(translation_str);
+ free(translation_str);
+ XtOverrideTranslations(popup_window[my_popup_num], xlats);
+ XtOverrideTranslations(message_paned[my_popup_num], xlats);
+ XtOverrideTranslations(message_text[my_popup_num], xlats);
+ }
+
+ XtVaSetValues(popup_window[my_popup_num], XtNtitle, win_title, NULL);
+ XtVaSetValues(message_text[my_popup_num], XtNstring, msg_buf, NULL);
+ XtRealizeWidget(popup_window[my_popup_num]);
+
+ XSetWMProtocols(XtDisplay(popup_window[my_popup_num]), XtWindow(popup_window[my_popup_num]),
+ &WM_DELETE_WINDOW, 1);
+
+ if (x_coord <= 0 || y_coord <= 0)
+ center_window(popup_window[my_popup_num], parent);
+ else
+ position_window(popup_window[my_popup_num], (Position)x_coord, (Position)y_coord);
+
+ if (my_popup_num > 0) {
+ /* some window managers position new windows exactly above the
+ existing one; to prevent this, move it with some offset
+ from the previous one: */
+ Position x = 0, y = 0;
+ XtVaGetValues(popup_window[my_popup_num-1], XtNx, &x, XtNy, &y, NULL);
+ XtVaSetValues(popup_window[my_popup_num], XtNx, x + POPUP_OFFSET, XtNy, y + POPUP_OFFSET, NULL);
+
+ }
+ XtPopup(popup_window[my_popup_num], XtGrabNone);
+/* XtPopup(XtParent(popup_window[my_popup_num]), XtGrabExclusive); */
+ if (XtIsManaged(message_not_ok[my_popup_num]) && XtIsManaged(message_help[my_popup_num])) {
+ /* center the help button. This is something of a sham, since it won't
+ survive resizing; but in general most users won't resize dialogs ;-) */
+ Position x1, x2, bw;
+ int w, dist;
+
+ XtVaGetValues(message_ok[my_popup_num], XtNx, &x1, XtNwidth, &w, XtNborderWidth, &bw, NULL);
+ XtVaGetValues(message_help[my_popup_num], XtNx, &x2, NULL);
+ /* following formula is measured, not calculated -
+ I have no idea why it's e.g. 2 * w, not 1.5 * w ... */
+ dist = (x2 - x1 - 2 * w) / 2 - 2 * bw;
+ XtVaSetValues(message_not_ok[my_popup_num], XtNhorizDistance, dist, NULL);
+ }
+
+#endif /* MOTIF */
+ return ret;
+}
+
+
+/*------------------------------------------------------------
+ * popup_message
+ *
+ * Arguments:
+ * popupMessageT - info, warning, error etc; see message-window.h for details
+ *
+ * char *helptext
+ * - if not-null, this will add a `Help'
+ * button to the message widget that pops
+ * up another message widget containing
+ * <helptext>.
+ *
+ *
+ * char *msg, ...
+ * - format string followed by a variable
+ * number of arguments to be formatted.
+ *
+ * Returns:
+ * void
+ *
+ * Purpose:
+ * Pop up a message window containing <msg>.
+ * If there are already n popups open, will open
+ * a new one unless n >= MAX_POPUPS.
+ *------------------------------------------------------------*/
+
+Widget
+popup_message(Widget parent, popupMessageT type, const char *helptext, const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ SIZE_SMALL,
+ type,
+ -1, -1, /* just center it */
+ helptext, msg_buf,
+#ifndef MOTIF
+ NULL,
+#endif
+ /* no special callbacks here */
+ NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free(msg_buf);
+ return w;
+}
+
+Widget
+popup_message_sized(Widget parent,
+ popupMessageT type,
+ popupMessageSizeHintT sizehint,
+ const char *helptext,
+ const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ sizehint,
+ type,
+ -1, -1, /* just center it */
+ helptext, msg_buf,
+#ifndef MOTIF
+ NULL,
+#endif
+ /* empty callbacks */
+ NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free(msg_buf);
+ return w;
+}
+
+Widget
+positioned_popup_message(Widget parent,
+ popupMessageT type,
+ int x, int y,
+ const char *helptext, const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ SIZE_SMALL,
+ type,
+ x, y, /* position at these coordinates */
+ helptext, msg_buf,
+#ifndef MOTIF
+ NULL,
+#endif
+ /* empty callbacks */
+ NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL);
+ free(msg_buf);
+ return w;
+}
+
+Widget
+choice_dialog(Widget parent,
+ popupMessageT type,
+ const char *helptext,
+#ifndef MOTIF
+ const char *xaw_ret_action_str,
+#endif
+ pre_message_cbT pre_cb, XtPointer arg,
+ const char *ok_label, message_cbT ok_cb, XtPointer ok_arg,
+ const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg,
+ const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ SIZE_SMALL,
+ type,
+ -1, -1, /* just center it */
+ helptext, msg_buf,
+#ifndef MOTIF
+ xaw_ret_action_str,
+#endif
+ pre_cb, arg,
+ ok_label, ok_cb, ok_arg,
+ NULL, NULL, NULL,
+ cancel_label, cancel_cb, cancel_arg);
+ free(msg_buf);
+ return w;
+}
+
+#if MOTIF
+Widget
+choice3_dialog(Widget parent,
+ popupMessageT type,
+ const char *helptext,
+ pre_message_cbT pre_cb, XtPointer arg,
+ const char *yes_label, message_cbT yes_cb, XtPointer yes_arg,
+ const char *no_label, message_cbT no_cb, XtPointer no_arg,
+ const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg,
+ const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ SIZE_SMALL,
+ type,
+ -1, -1, /* just center it */
+ helptext, msg_buf,
+ pre_cb, arg,
+ yes_label, yes_cb, yes_arg,
+ no_label, no_cb, no_arg,
+ cancel_label, cancel_cb, cancel_arg);
+ free(msg_buf);
+ return w;
+}
+#endif
+
+Widget
+choice_dialog_sized(Widget parent,
+ popupMessageT type,
+ popupMessageSizeHintT sizehint,
+ const char *helptext,
+#ifndef MOTIF
+ const char *xaw_ret_action_str,
+#endif
+ pre_message_cbT pre_cb, XtPointer arg,
+ const char *ok_label, message_cbT ok_cb, XtPointer ok_arg,
+ const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg,
+ const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ sizehint,
+ type,
+ -1, -1, /* just center it */
+ helptext, msg_buf,
+#ifndef MOTIF
+ xaw_ret_action_str,
+#endif
+ pre_cb, arg,
+ ok_label, ok_cb, ok_arg,
+ NULL, NULL, NULL,
+ cancel_label, cancel_cb, cancel_arg);
+ free(msg_buf);
+ return w;
+}
+
+Widget
+positioned_choice_dialog(Widget parent,
+ popupMessageT type,
+ int x_pos, int y_pos,
+ const char *helptext,
+#ifndef MOTIF
+ const char *xaw_ret_action_str,
+#endif
+ pre_message_cbT pre_cb, XtPointer arg,
+ const char *ok_label, message_cbT ok_cb, XtPointer ok_arg,
+ const char *cancel_label, message_cbT cancel_cb, XtPointer cancel_arg,
+ const char *format, ...)
+{
+ char *msg_buf = NULL;
+ Widget w;
+
+ XDVI_GET_STRING_ARGP(msg_buf, format);
+
+ w = internal_popup_window(parent,
+ SIZE_SMALL,
+ type,
+ x_pos, y_pos,
+ helptext, msg_buf,
+#ifndef MOTIF
+ xaw_ret_action_str,
+#endif
+ pre_cb, arg,
+ ok_label, ok_cb, ok_arg,
+ NULL, NULL, NULL,
+ cancel_label, cancel_cb, cancel_arg);
+ free(msg_buf);
+ return w;
+}
+
+void
+warn_overstrike(void)
+{
+ static Boolean warned_overstrike = False;
+
+ if (!warned_overstrike) {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN,
+ /* helptext */
+ "Greyscaling is running in copy mode; this will cause overstrike characters to "
+ "appear incorrectly, and may result in poor display quality. "
+ "Possible fixes are:\n"
+ "- Use the ``-thorough'' command-line option.\n"
+ "- Quit some other color-hungry applications (e.g. Netscape).\n"
+ "- Use the ``-install'' command-line option.\n"
+ "See the section ``GREYSCALING AND COLORMAPS'' in the "
+ "xdvi manual page for more details.",
+ /* text */
+ "Couldn't allocate enough colors - expect low display quality.");
+ warned_overstrike = True;
+ }
+}
+
+Boolean
+is_message_window(Widget w)
+{
+ int i;
+ for (i = 0; i < MAX_POPUPS; i++) {
+ if (w == popup_window[i])
+ return True;
+ }
+ return False;
+}
+
+Boolean
+kill_message_window(Widget w)
+{
+ int i;
+ for (i = 0; i < MAX_POPUPS; i++) {
+ if (g_popup_array[i] != 0 && XtIsRealized(popup_window[i]) && w == popup_window[i]) {
+ g_popup_array[i] = 0;
+ XtPopdown(popup_window[i]);
+ XtDestroyWidget(popup_window[i]);
+ XSync(DISP, True);
+ return True;
+ }
+ }
+ return False;
+}
+
+/*
+ Raise any popups that currently exist; return True iff such popups
+ exist, else False.
+*/
+Boolean
+raise_message_windows(void)
+{
+ int i;
+ Boolean have_popups = False;
+
+ for (i = 0; i < MAX_POPUPS; i++) {
+ if (g_popup_array[i] != 0 && XtIsRealized(popup_window[i])) {
+ XRaiseWindow(DISP, XtWindow(popup_window[i]));
+ have_popups = True;
+ }
+ }
+
+ return have_popups;
+}