summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/gui/topic-window.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/gui/topic-window.c')
-rw-r--r--Build/source/texk/xdvik/gui/topic-window.c650
1 files changed, 650 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/gui/topic-window.c b/Build/source/texk/xdvik/gui/topic-window.c
new file mode 100644
index 00000000000..7a87a03b5c8
--- /dev/null
+++ b/Build/source/texk/xdvik/gui/topic-window.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2004 Stefan Ulrich
+ *
+ * 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.
+ *
+ */
+
+/*
+ * `Topics' window framework used by help and preferences windows.
+ * Contains a list of topics on the left-hand side, and corresponding
+ * contents on the right-hand side.
+ */
+
+#include "xdvi-config.h"
+#include "xdvi.h"
+
+#if MOTIF
+# include <Xm/Xm.h>
+# include <Xm/Form.h>
+# include <Xm/DialogS.h>
+# include <Xm/PushB.h>
+# include <Xm/Frame.h>
+# include <Xm/PanedW.h>
+# include <Xm/LabelG.h>
+# include <Xm/Label.h>
+# include <Xm/Protocols.h>
+# include <Xm/List.h>
+# include <Xm/AtomMgr.h>
+#else /* MOTIF */
+# include <X11/Xaw/Paned.h>
+# include <X11/Xaw/Box.h>
+# include <X11/Xaw/Form.h>
+# include <X11/Xaw/Cardinals.h>
+# include <X11/Xaw/Command.h>
+# include <X11/Xaw/List.h>
+#endif /* MOTIF */
+
+#include "version.h"
+#include "topic-window.h"
+#include "string-utils.h"
+#include "util.h"
+#include "x_util.h"
+
+#if MOTIF
+
+/* special formatting for headings in right window, if desired */
+const char *const TAG_NORMAL = "NORMAL";
+const char *const TAG_TOPIC_HEADING = "TOPIC_HEADING";
+const char *const TAG_TOPIC_LABEL = "TOPIC_LABEL";
+
+# define SHELL_WIDGET_CLASS xmDialogShellWidgetClass
+# define PANED_WIDGET_CLASS xmPanedWindowWidgetClass
+# define FORM_WIDGET_CLASS xmFormWidgetClass
+
+# define FORM_ARGS XmNhorizontalSpacing, 10, \
+ NULL
+# define HELP_SHELL_ARGS XmNdeleteResponse, XmDO_NOTHING, \
+ XtNmappedWhenManaged, False, \
+ NULL
+# define HELP_PANED_ARGS XmNsashWidth, 1, \
+ XmNuserData, info, \
+ XmNsashHeight, 1, \
+ NULL
+# define LEFT_FORM_ARGS XmNtopAttachment, XmATTACH_FORM, \
+ XmNleftAttachment, XmATTACH_FORM, \
+ XmNbottomAttachment, XmATTACH_FORM, \
+ XmNtopOffset, 9, \
+ XmNbottomOffset, 10, \
+ NULL
+# define RIGHT_FORM_ARGS XmNtopAttachment, XmATTACH_FORM, \
+ XmNleftAttachment, XmATTACH_WIDGET, \
+ XmNleftWidget, left_form, \
+ XmNrightAttachment, XmATTACH_FORM, \
+ XmNbottomAttachment, XmATTACH_FORM, \
+ XmNbottomOffset, 10, \
+ NULL
+# define ACTION_AREA_ARGS NULL
+
+#else /* MOTIF */
+
+# define SHELL_WIDGET_CLASS transientShellWidgetClass
+# define PANED_WIDGET_CLASS panedWidgetClass
+# define FORM_WIDGET_CLASS formWidgetClass
+
+# define FORM_ARGS XtNdefaultDistance, 14, \
+ NULL
+# define HELP_SHELL_ARGS XtNx, 60, \
+ XtNy, 80, \
+ XtNtransientFor, globals.widgets.top_level, \
+ XtNtranslations, xlats, \
+ XtNtransientFor, parent, \
+ XtNallowShellResize, False, \
+ NULL
+# define HELP_PANED_ARGS NULL
+# define LEFT_FORM_ARGS XtNtop, XtChainTop, \
+ XtNbottom, XtChainBottom, \
+ XtNleft, XtChainLeft, \
+ XtNright, XtChainLeft, \
+ XtNborderWidth, 0, \
+ NULL
+# define RIGHT_FORM_ARGS XtNfromHoriz, left_form, \
+ XtNtop, XtChainTop, \
+ XtNbottom, XtChainBottom, \
+ XtNleft, XtChainLeft, \
+ XtNright, XtChainRight, \
+ XtNborderWidth, 0, \
+ NULL
+# define ACTION_AREA_ARGS XtNdefaultDistance, 6, \
+ XtNshowGrip, False, \
+ XtNskipAdjust, True, \
+ NULL
+#endif /* MOTIF */
+
+
+static void
+ok_cb(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ struct topic_info *info = (struct topic_info *)client_data;
+
+ UNUSED(w);
+ UNUSED(call_data);
+
+ ASSERT(info != NULL, "No info passed to callback!");
+
+ if (info->ok_callback != NULL)
+ info->ok_callback(info);
+ XtPopdown(info->shell);
+}
+
+static void
+cancel_cb(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ struct topic_info *info = (struct topic_info *)client_data;
+
+ UNUSED(w);
+ UNUSED(call_data);
+
+ ASSERT(info != NULL, "No info passed to callback!");
+
+ if (info->cancel_callback != NULL)
+ info->cancel_callback(info);
+ XtPopdown(info->shell);
+}
+
+#if MOTIF
+/*
+ * We are overriding this since otherwise the Text widget would attempt to
+ * first move the invisible cursor instead of the scroll bars.
+ */
+/* FIXME: This gives a warning:
+ Warning: Actions not found: scroll-one-line-down, scroll-one-line-up
+*/
+/* "<Key>osfUp: scroll-one-line-down()\n" */
+/* "<Key>osfDown: scroll-one-line-up()\n" */
+#endif
+
+#if MOTIF
+static XmString
+#else
+static char *
+#endif
+create_label_string(const char *title, const char *subtitle)
+{
+#if MOTIF
+ XmString label;
+ if (subtitle == NULL) { /* simple bold label */
+ label = XmStringCreate((char *)title, (char *)TAG_TOPIC_LABEL);
+ }
+ else { /* two-part title with subheading */
+ XmString s1, s2, s3, s4;
+ s1 = XmStringCreate((char *)title, (char *)TAG_TOPIC_HEADING);
+ s2 = XmStringCreate((char *)" ", (char *)TAG_NORMAL);
+ s3 = XmStringCreate((char *)subtitle, (char *)TAG_NORMAL);
+ s4 = XmStringConcat(s1, s2);
+ label = XmStringConcat(s4, s3);
+ XmStringFree(s1);
+ XmStringFree(s2);
+ XmStringFree(s3);
+ XmStringFree(s4);
+ }
+#else
+ char *label = xstrdup(title);
+ if (subtitle != NULL && strlen(subtitle) > 0) {
+ label = xstrcat(label, " - ");
+ label = xstrcat(label, subtitle);
+ }
+#endif
+ return label;
+}
+
+static Widget
+create_label_widget(Widget parent, const char *name, struct topic_info *info)
+{
+ Widget label;
+
+#if MOTIF
+
+ XmString label_string = create_label_string(info->items[0].topic, info->items[0].title);
+ label = XtVaCreateWidget(name, xmLabelWidgetClass, parent,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNtopOffset, 14,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNlabelString, label_string,
+ XmNalignment, XmALIGNMENT_BEGINNING,
+ NULL);
+ XmStringFree(label_string);
+
+#else /* MOTIF */
+
+ char *label_string = create_label_string(info->items[0].topic, info->items[0].title);
+ label = XtVaCreateWidget(name, labelWidgetClass, parent,
+ XtNlabel, label_string,
+ XtNborderWidth, 0,
+ /* XtNinternalHeight, 2, */
+ XtNjustify, XtJustifyLeft,
+ NULL);
+ free(label_string);
+
+#endif /* MOTIF */
+
+ return label;
+}
+
+void
+select_topic(struct topic_info *info, size_t idx)
+{
+#if MOTIF
+ XmString label;
+#else
+ char *label;
+#endif
+
+ /* change the heading in right window */
+ label = create_label_string(info->items[idx].topic, info->items[idx].title);
+ XtVaSetValues(info->topic_label,
+#if MOTIF
+ XmNlabelString,
+#else
+ XtNlabel,
+#endif
+ label, NULL);
+
+#if MOTIF
+ XmStringFree(label);
+ XmListSelectPos(info->topics_list, idx + 1, False);
+#else
+ free(label);
+ XawListHighlight(info->topics_list, idx);
+#endif
+
+ if (info->curr_selected != 0) {
+ XtUnmanageChild(info->curr_selected);
+ }
+ else {
+ /* Note: doesn't matter if not managed yet */
+/* XtUnmanageChild(info->items[0].widget); */
+ }
+
+ XtManageChild(info->items[idx].widget);
+ info->curr_selected = info->items[idx].widget;
+
+#if MOTIF
+ XmUpdateDisplay(info->shell);
+#endif
+}
+
+static void
+select_topic_cb(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ struct topic_info *info = (struct topic_info *)client_data;
+ size_t idx;
+
+#if MOTIF
+ idx = ((XmListCallbackStruct *)call_data)->item_position - 1;
+#else
+ idx = ((XawListReturnStruct *)call_data)->list_index;
+#endif
+ UNUSED(w);
+
+ /* ASSERT(idx < info->items_size, "list position exceeds items_size!"); */
+
+ select_topic(info, idx);
+}
+
+static Widget
+create_button(Widget parent, const char *name, Boolean show_as_default, Boolean left_position)
+{
+ Widget button;
+#if MOTIF
+ XmString s1 = XmStringCreateLocalized((char *)name);
+ button = XtVaCreateWidget(name, xmPushButtonWidgetClass, parent,
+ XmNlabelString, s1,
+ XmNdefaultButtonShadowThickness, 1,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ XmNmarginWidth, 6,
+ XmNmarginHeight, 4,
+ XmNtopOffset, 10,
+ XmNbottomOffset, 10,
+ NULL);
+ if (left_position)
+ XtVaSetValues(button,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNleftOffset, 10,
+ NULL);
+ else
+ XtVaSetValues(button,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNrightOffset, 10,
+ NULL);
+ if (show_as_default)
+ XtVaSetValues(button, XmNshowAsDefault, True, NULL);
+
+ XtManageChild(button);
+ XmStringFree(s1);
+#else /* MOTIF */
+ UNUSED(show_as_default);
+ button = XtVaCreateManagedWidget(name, commandWidgetClass, parent,
+ XtNtop, XtChainTop,
+ XtNbottom, XtChainBottom,
+ NULL);
+ if (left_position)
+ XtVaSetValues(button,
+ XtNleft, XtChainLeft,
+ XtNright, XtChainLeft,
+ NULL);
+ else
+ XtVaSetValues(button,
+ XtNleft, XtChainRight,
+ XtNright, XtChainRight,
+ NULL);
+#endif /* MOTIF */
+ return button;
+}
+
+static void
+close_topic_window(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ Widget button;
+ void *ptr;
+ struct topic_info *info;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (*num_params < 1) {
+ XDVI_WARNING((stderr, "Wrong argument number (%d) in callback!", *num_params));
+ return;
+ }
+
+ sscanf(*params, "%p", &ptr);
+ info = (struct topic_info *)ptr;
+
+ /* get a callback to close the button, in decreasing order of preference */
+ if (get_widget_by_name(&button, info->shell, "Cancel", False)
+ || get_widget_by_name(&button, info->shell, "Close", False)
+ || get_widget_by_name(&button, info->shell, "OK", False)) {
+ XtCallCallbacks(button,
+#if MOTIF
+ XmNactivateCallback,
+#else
+ XtNcallback,
+#endif
+ info);
+ }
+ else {
+ XDVI_WARNING((stderr, "No button found for widget %p!\n", (void *)info->shell));
+ }
+}
+
+static XtActionsRec popdown_actions[] = {
+#if !MOTIF
+ { "WM_popdown", close_topic_window },
+#endif
+ { "close-topic-window", close_topic_window },
+};
+
+static Widget
+create_list_widget(Widget parent, const char *w_name, struct topic_info *info)
+{
+ Widget list;
+ size_t tnum;
+#if MOTIF
+ XmString *topics = NULL;
+#else
+ static char **topics = NULL;
+#endif
+
+ for (tnum = 0; info->items[tnum].topic != NULL; tnum++) {
+ /* 1 more for terminating NULL needed for Xaw */
+ topics = xrealloc(topics, (tnum + 2) * sizeof *topics);
+#if MOTIF
+ topics[tnum] = XmStringCreateLocalized(info->items[tnum].topic);
+#else
+ topics[tnum] = xstrdup(info->items[tnum].topic);
+#endif
+ }
+
+#ifndef MOTIF
+ /* list needs to be terminated for Xaw */
+ topics[tnum] = NULL;
+#endif
+
+#if MOTIF
+ {
+ size_t i;
+ Arg args[20];
+ int n = 0;
+ XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+ XtSetArg(args[n], XmNlistSpacing, 4); n++;
+ XtSetArg(args[n], XmNlistMarginHeight, 4); n++;
+ XtSetArg(args[n], XmNlistMarginWidth, 4); n++;
+ XtSetArg(args[n], XmNhighlightThickness, 0); n++;
+ XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
+
+ list = XmCreateScrolledList(parent, (char *)w_name, args, n);
+
+#if defined(__GNUC__) && DEVEL_MODE
+#warning TODO: make up/down scroll the list, and PgUp/PgDown scroll help text
+#warning TODO: add wheel mouse bindings
+ /*
+ * also fix bug with arrow keys first moving invisible cursor, then scrollbars
+ */
+#endif
+
+ XmListDeleteAllItems(list);
+ XmListAddItems(list, topics, tnum, 0);
+
+ for (i = 0; i < tnum; ++i) {
+ XmStringFree(topics[i]);
+ }
+ free(topics);
+ }
+ XtAddCallback(list, XmNbrowseSelectionCallback, select_topic_cb, info);
+ XmListSelectPos(list, 1, False); /* default position */
+#else /* MOTIF */
+ list = XtVaCreateWidget(w_name, listWidgetClass, parent,
+ XtNlist, topics,
+ XtNdefaultColumns, 1,
+ XtNforceColumns, True,
+ XtNverticalList, True,
+ XtNrowSpacing, 4,
+ XtNheight, 429,
+ NULL);
+ XtAddCallback(list, XtNcallback, select_topic_cb, info);
+ XawListHighlight(list, 0); /* default position */
+#endif /* MOTIF */
+ XtManageChild(list);
+ return list;
+}
+
+
+Widget
+create_topic_window(Widget parent,
+ const char *window_title,
+ const char *widget_name,
+ struct topic_info *info,
+ void (*init_items_func)(struct topic_info *info),
+ /* OK button/callbacks */
+ const char *ok_label,
+ /* Cancel button/callbacks (can be NULL) */
+ const char *cancel_label)
+{
+ Widget topic_window, topics_list, topic_label;
+ Widget pane, form, left_form, right_container_form, right_form;
+ Widget action_area, ok_button, cancel_button;
+ XtTranslations xlats;
+
+ XtAppContext app = NULL;
+ Atom WM_DELETE_WINDOW;
+
+ char *translation_str;
+ size_t i;
+
+#if !MOTIF
+ translation_str = get_string_va("<Message>WM_PROTOCOLS: WM_popdown(%p)", info);
+ xlats = XtParseTranslationTable(translation_str);
+ free(translation_str);
+#endif
+
+ topic_window = XtVaCreatePopupShell(widget_name, SHELL_WIDGET_CLASS,
+ parent,
+ XtNtitle, window_title,
+ HELP_SHELL_ARGS);
+
+#if MOTIF
+ /* make the window manager destroy action pop down the window */
+ WM_DELETE_WINDOW = XmInternAtom(XtDisplay(topic_window), "WM_DELETE_WINDOW", False);
+ XmAddWMProtocolCallback(topic_window, WM_DELETE_WINDOW, cancel_cb, info);
+#else
+ WM_DELETE_WINDOW = XInternAtom(XtDisplay(topic_window), "WM_DELETE_WINDOW", False);
+#endif
+ app = XtWidgetToApplicationContext(topic_window);
+ XtAppAddActions(app, popdown_actions, XtNumber(popdown_actions));
+
+ pane = XtVaCreateWidget("topic_pane", PANED_WIDGET_CLASS, topic_window, HELP_PANED_ARGS);
+
+ form = XtVaCreateWidget("form", FORM_WIDGET_CLASS, pane, FORM_ARGS);
+
+ /* left pane for topics selection */
+ left_form = XtVaCreateWidget("left_form", FORM_WIDGET_CLASS, form, LEFT_FORM_ARGS);
+
+ /* right form for topics display */
+ right_container_form = XtVaCreateWidget("right_container_form", FORM_WIDGET_CLASS, form, RIGHT_FORM_ARGS);
+
+ /*
+ Initialize the topic label and title string with dummy values - the real values
+ are only known inside init_items_func(). For Xaw, the size of the widget will be
+ adjusted below (for Motif it's always the full width of the right form, which is
+ what we want).
+ */
+ /* FIXME: Motif label is still chopped off if it's longer than right form -
+ work around by putting longest possible lable here ... */
+ /* Also, not all children will be sized correctly, e.g. the colors - work around
+ by specifying a rather large dummy text ... */
+ info->items[0].topic = "text text text";
+ info->items[0].title = "text text text text text text text text text text text text text text text text text";
+ topic_label = create_label_widget(right_container_form, "topic_label", info);
+ info->topic_label = topic_label;
+
+ right_form = XtVaCreateWidget("right_form", FORM_WIDGET_CLASS,
+ right_container_form,
+#if MOTIF
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, topic_label,
+ XmNtopOffset, 10,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+#else
+ XtNborderWidth, 0,
+ XtNfromVert, topic_label,
+ XtNvertDistance, 6,
+#endif /* MOTIF */
+ NULL);
+ info->right_form = right_form;
+
+ /*
+ Call the init_items_func callback. This will populate the info->items list
+ with the appropriate info, and create the forms (info->items[i].widget)
+ which are the children of right_form (which is accessed inside init_items_func()
+ through info->right_form), and all their subchildren.
+
+ It is important that every child form is already managed inside init_items_func
+ so that the total size of the preferences window is set to the maximum size of
+ the children. (The children will be unmanaged again below). All these widgets
+ are never destroyed, and are managed by demand via the list selection callback
+ (select_topic_cb).
+ */
+ init_items_func(info);
+
+ XtManageChild(topic_label);
+
+ topics_list = create_list_widget(left_form, "topics_list", info);
+ info->topics_list = topics_list;
+
+ translation_str = get_string_va("#override \n"
+ "<Key>q:close-topic-window(%p)\n"
+#ifdef MOTIF
+ "<Key>osfCancel:close-topic-window(%p)\n"
+#else
+ "<Key>Escape:close-topic-window(%p)\n"
+#endif
+ "<Key>Return:close-topic-window(%p)",
+ info, info, info);
+ xlats = XtParseTranslationTable(translation_str);
+ free(translation_str);
+ translation_str = NULL;
+
+ XtOverrideTranslations(pane, xlats);
+ XtOverrideTranslations(topics_list, xlats);
+
+ XtManageChild(left_form);
+
+ XtManageChild(right_form);
+ XtManageChild(right_container_form);
+
+ /* action area at bottom */
+ action_area = XtVaCreateWidget("action_area", FORM_WIDGET_CLASS, pane, ACTION_AREA_ARGS);
+
+ ok_button = create_button(action_area, ok_label, True, True);
+ XtOverrideTranslations(ok_button, xlats);
+
+ if (cancel_label != NULL) {
+ cancel_button = create_button(action_area, cancel_label, False, False);
+ adjust_width(ok_button, cancel_button);
+#if MOTIF
+ XtAddCallback(cancel_button, XmNactivateCallback, cancel_cb, info);
+#else
+ XtAddCallback(cancel_button, XtNcallback, cancel_cb, info);
+#endif
+ XtOverrideTranslations(cancel_button, xlats);
+ }
+
+ XtManageChild(action_area);
+
+ XtManageChild(form);
+
+ XtManageChild(pane);
+
+ XtRealizeWidget(topic_window);
+
+ /* Now unmanage all children of right_form, as described above. */
+ for (i = 0; info->items[i].topic != NULL; i++) {
+ XtUnmanageChild(info->items[i].widget);
+ }
+ info->curr_selected = 0;
+
+#if MOTIF
+ /* enable OK button */
+ XmProcessTraversal(ok_button, XmTRAVERSE_CURRENT);
+ XtOverrideTranslations(ok_button,
+ XtParseTranslationTable("<Key>Return:ArmAndActivate()\n"
+ "<Key>q:ArmAndActivate()"));
+ XtAddCallback(ok_button, XmNactivateCallback, ok_cb, info);
+ XmAddWMProtocolCallback(topic_window, WM_DELETE_WINDOW, cancel_cb, info);
+ { /* disable resizing of lower part of pane (and button) */
+ Dimension h;
+ XtVaGetValues(ok_button, XmNheight, &h, NULL);
+ XtVaSetValues(action_area,
+ XmNpaneMaximum, h + 20,
+ XmNpaneMinimum, h + 20,
+ NULL);
+ }
+#else
+ XSetWMProtocols(XtDisplay(topic_window), XtWindow(topic_window), &WM_DELETE_WINDOW, 1);
+ XtAddCallback(ok_button, XtNcallback, ok_cb, info);
+#endif
+ return topic_window;
+}