aboutsummaryrefslogtreecommitdiff
path: root/vendor/x11iraf/obm/server.c
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/x11iraf/obm/server.c')
-rw-r--r--vendor/x11iraf/obm/server.c3175
1 files changed, 3175 insertions, 0 deletions
diff --git a/vendor/x11iraf/obm/server.c b/vendor/x11iraf/obm/server.c
new file mode 100644
index 00000000..fa46d2fd
--- /dev/null
+++ b/vendor/x11iraf/obm/server.c
@@ -0,0 +1,3175 @@
+/* Copyright(c) 1993 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <ObmP.h>
+
+/* The following internal files are needed for some widget level SmeBSB code
+ * included in this file.
+ */
+#include <X11/IntrinsicP.h>
+#include <X11/Xaw/SmeBSBP.h>
+
+/*
+ * SERVER class.
+ * --------------------------
+ * The server, or object manager, is the control center of the user interface.
+ * The server object provides a Tcl interpreter calling custom object manager
+ * commands. These are used to define and initialize the user interface, and
+ * execute UI action procedures at runtime.
+ *
+ * reset-server
+ * appInitialize appname, appclass, resources
+ * appExtend new-resources [overwrite]
+ * createObjects [resource-name]
+ * destroyObject object
+ * queryObject object [class [subclass]]
+ * activate
+ * deactivate [unmap]
+ * synchronize
+ * flush
+ *
+ * value = getResource resource-name [default-value [class]]
+ * getResources resource-list
+ *
+ * createMenu menu-name parent item-list
+ * editMenu menu-name parent item-list
+ * destroyMenu menu-name
+ *
+ * createBitmap name width height data
+ * createCursor name source mask fg_color bg_color x_hot y_hot
+ * createPixmap name width height depth fg_color bg_color data
+ * createXPixmap name widget description
+ *
+ * print arg [arg ...] # debug messages
+ * send object message
+ *
+ * postActivateCallback procedure
+ * postDeactivateCallback procedure
+ *
+ * id = postTimedCallback procedure msec [client-data]
+ * deleteTimedCallback id
+ * id = postWorkProc procedure [client-data]
+ * deleteWorkProc id
+ */
+
+#define CB_WORKPROC 1
+#define CB_TIMER 2
+
+/* Callback structure for timer, workproc callbacks. */
+struct _serverCallback {
+ XtPointer obj;
+ int callback_type;
+ char *userproc;
+ char *client_data;
+ union {
+ XtIntervalId intervalId;
+ XtWorkProcId workProcId;
+ } id;
+ struct _serverCallback *next;
+};
+typedef struct _serverCallback serverCallback;
+typedef struct _serverCallback *ServerCallback;
+
+struct serverPrivate {
+ ObmContext obm;
+ ServerCallback cb_head;
+ ServerCallback cb_tail;
+};
+
+typedef struct serverPrivate *ServerPrivate;
+
+struct serverObject {
+ struct obmObjectCore core;
+ struct serverPrivate server;
+};
+
+typedef struct serverObject *ServerObject;
+
+static ObmObject ServerCreate();
+static void ServerDestroy();
+static int ServerEvaluate(), serverQueryObject();
+static int serverCreateMenu(), serverDestroyMenu();
+static int serverAppInitialize(), serverAppExtend(), serverCreateObjects();
+static int serverSend(), serverPrint(), serverDestroyObject();
+static int serverReset(), serverActivate(), serverDeactivate();
+static int serverCreateBitmap(), serverCreatePixmap(), serverCreateCursor();
+static int serverPostActivateCallback(), serverPostDeactivateCallback();
+static int serverPostTimedCallback(), serverPostWorkProc();
+static int serverDeleteTimedCallback(), serverDeleteWorkProc();
+static int serverCreateXPixmap(), serverSynchronize(), serverFlush();
+static int serverGetResource(), serverGetResources();
+static void link_callback(), unlink_callback();
+static void serverTimedProc();
+static Boolean serverWorkProc();
+
+static int editMenu();
+static void menu_popup(), menu_popdown(), menu_popdown_msgHandler();
+static void createMenu(), menuSelect(), build_colorlist();
+static void menu_classInit(), menu_addEntry(), menu_delEntry();
+static void menu_highlight(), menu_unhighlight();
+static Pixmap menu_pullrightBitmap();
+static MenuPtr findMenu();
+extern long strtol();
+
+/* The pull-right bitmap for menus. */
+#define MB_WIDTH 16
+#define MB_HEIGHT 16
+#define MB1_PIXELS \
+ "0x00, 0x00, 0x30, 0x00, 0xf0, 0x00, 0xf0, 0x03, 0xf0, 0x0f, 0xf0, 0x0f,\
+ 0xf0, 0x03, 0xf0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00"
+#define MB2_PIXELS \
+ "0x00, 0x00, 0x30, 0x00, 0xd0, 0x00, 0x10, 0x03, 0x10, 0x0c, 0x10, 0x0c,\
+ 0x10, 0x03, 0xd0, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00"
+
+
+/* ServerClassInit -- Initialize the class record for the server class.
+ */
+void
+ServerClassInit (obm, classrec)
+ObmContext obm;
+register ObjClassRec classrec;
+{
+ classrec->ClassDestroy = obmGenericClassDestroy;
+ classrec->Create = (ObmFunc) ServerCreate;
+ classrec->Destroy = ServerDestroy;
+ classrec->Evaluate = ServerEvaluate;
+}
+
+
+/* ServerCreate -- Create an instance of a server object.
+ */
+static ObmObject
+ServerCreate (obm, name, classrec, parent, args, nargs)
+ObmContext obm;
+char *name;
+ObjClassRec classrec;
+char *parent;
+ArgList args;
+int nargs;
+{
+ register ServerObject obj;
+ register Tcl_Interp *tcl;
+
+ obj = (ServerObject) XtCalloc (1, sizeof (struct serverObject));
+ obm->tcl = tcl = Tcl_CreateInterp();
+ obj->server.obm = obm;
+
+ /* Register server actions. */
+ Tcl_CreateCommand (tcl,
+ "reset-server", serverReset, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "activate", serverActivate, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "deactivate", serverDeactivate, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "synchronize", serverSynchronize, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "flush", serverFlush, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "postActivateCallback", serverPostActivateCallback,
+ (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "postDeactivateCallback", serverPostDeactivateCallback,
+ (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "appInitialize", serverAppInitialize, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "appExtend", serverAppExtend, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "createObjects", serverCreateObjects, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "destroyObject", serverDestroyObject, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "queryObject", serverQueryObject, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "send", serverSend, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "print", serverPrint, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "getResource", serverGetResource, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "getResources", serverGetResources, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "postTimedCallback", serverPostTimedCallback,
+ (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "deleteTimedCallback", serverDeleteTimedCallback,
+ (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "postWorkProc", serverPostWorkProc, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "deleteWorkProc", serverDeleteWorkProc, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "createBitmap", serverCreateBitmap, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "createPixmap", serverCreatePixmap, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "createXPixmap", serverCreateXPixmap, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "createCursor", serverCreateCursor, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "createMenu", serverCreateMenu, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "editMenu", serverCreateMenu, (ClientData)obj, NULL);
+ Tcl_CreateCommand (tcl,
+ "destroyMenu", serverDestroyMenu, (ClientData)obj, NULL);
+
+ return ((ObmObject) obj);
+}
+
+
+/* ServerDestroy -- Destroy an instance of a server object.
+ */
+static void
+ServerDestroy (object)
+ObmObject object;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ register ServerCallback cb, next;
+
+ /* Destroy the object in the second final call to Destroy. */
+ if (!obj->core.being_destroyed++)
+ return;
+
+ /* Delete any pending timers or work procs. */
+ for (cb = obj->server.cb_head; cb; cb = next) {
+ next = cb->next;
+ switch (cb->callback_type) {
+ case CB_TIMER:
+ XtRemoveTimeOut (cb->id.intervalId);
+ break;
+ case CB_WORKPROC:
+ XtRemoveWorkProc (cb->id.workProcId);
+ break;
+ }
+ XtFree ((char *)cb);
+ }
+
+ obj->server.cb_head = NULL;
+ obj->server.cb_tail = NULL;
+
+ /* Destroy the server interpreter. */
+ if (obm->tcl) {
+ Tcl_DeleteInterp (obm->tcl);
+ obm->tcl = NULL;
+ }
+}
+
+
+/* ServerEvaluate -- Evaluate a server command or message.
+ */
+static int
+ServerEvaluate (object, command)
+ObmObject object;
+char *command;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ static char reset[] = "reset-server";
+ register char *ip;
+
+ /* The command "reset-server" is a special case. This destroys the
+ * current user interface including all objects and widgets. One
+ * of the objects destroyed is the server object including the tcl
+ * interpreter. We can't use a normal Tcl command to implement this
+ * as the server and Tcl data structures will be freed and the rest
+ * of the input command would be lost. Instead, we check for the
+ * reset-server command, which must be the first command in the input
+ * command string, and manually reset things then call Tcl to process
+ * the remainder of the input command. The reset-server command should
+ * be the first command in the server config file used to define the
+ * user interface for a new application. Comments and blank lines
+ * at the head of the file are ignored.
+ */
+ for (ip=command; *ip; ) {
+ while (isspace (*ip))
+ ip++;
+ if (*ip == '#')
+ while (*ip && *ip != '\n')
+ ip++;
+ if (isspace (*ip))
+ ip++;
+ else
+ break;
+ }
+ if (strncmp (ip, reset, strlen(reset)) == 0) {
+ ObmInitialize (obm);
+ obj = (ServerObject) obmFindObject (obm, "Server");
+ }
+
+ /* Now interpret the full message using Tcl. This re-executes the
+ * reset-server command, which will be ignored since this is a no-op
+ * when it occurs within a script. We want to leave the command in
+ * the script as otherwise the line numbers won't be correct.
+ */
+ return (Tcl_Eval (obm->tcl, command));
+}
+
+
+/* serverAppInitialize -- TCL command to initialize the server for a new
+ * application, setting the application name and loading the application
+ * resources.
+ *
+ * Usage: appInitialize appname, appclass, resources
+ */
+static int
+serverAppInitialize (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ register ObmCallback cb;
+
+ char *resource_buf, *resource_list[MAX_RESOURCES];
+ char *appname, *appclass, *resources;
+ int sv_argc, nresources;
+ char **sv_argv;
+ char *ip, *op;
+
+ /* Get arguments. */
+ if (argc >= 2) {
+ strcpy (obm->appname, appname = argv[1]);
+ strcpy (obm->appclass, appclass = argv[2]);
+ } else {
+ appname = "gterm-iraf";
+ appclass = "Xgterm";
+ }
+
+ if (argc >= 3)
+ resources = argv[3];
+ else
+ resources = "";
+
+ /* Get fallback resources. */
+ resource_buf = op = XtMalloc (strlen(resources) + MAX_RESOURCES);
+ resource_list[0] = op;
+ nresources = 0;
+
+ for (ip=resources; *ip; ip++) {
+ while (*ip && (*ip == ' ' || *ip == '\t'))
+ ip++;
+ if (*ip == '\n') {
+ ;
+ } else if (*ip == '!') {
+ while (*ip && *ip != '\n')
+ ip++;
+ } else {
+ while (*ip && *ip != '\n')
+ *op++ = *ip++;
+ *op++ = '\0';
+ nresources++;
+ resource_list[nresources] = op;
+ }
+ }
+ *op++ = '\0';
+ resource_list[nresources] = NULL;
+
+ /* Set fallback resources. */
+ XtAppSetFallbackResources (obm->app_context, resource_list);
+
+ /* Get local copy of argc and argv. */
+ if ((sv_argc = obm->argc) > 0) {
+ sv_argv = (char **) XtMalloc (obm->argc * sizeof(char *));
+ memmove (sv_argv, obm->argv, obm->argc * sizeof(char *));
+ } else
+ sv_argv = obm->argv;
+
+ /* Open the display (initializes the resource database). A separate
+ * display descriptor is used so that we can specify the application
+ * name, class, and resources independently from those of the
+ * application using the object manager.
+ */
+ obm->display = XtOpenDisplay (obm->app_context, (String)NULL,
+ appname, appclass, NULL, 0, &sv_argc, sv_argv);
+ if (obm->display == (Display *)NULL)
+ XtAppError (obm->app_context, "appInitialize: Can't open display.");
+
+ if (obm->debug > 1)
+ XSynchronize (obm->display, True);
+
+ /* Create the top level shell. */
+ obm->toplevel = XtAppCreateShell (appname, appclass,
+ applicationShellWidgetClass, obm->display, (ArgList)NULL, 0);
+ obm->screen = XtScreen (obm->toplevel);
+
+ /* Call the client's display connection callback if any. */
+ for (cb = obm->callback_list; cb; cb = cb->next)
+ if ((cb->callback_type & OBMCB_connect) && cb->u.fcn)
+ (*cb->u.fcn) (cb->client_data, obm->display, obm->toplevel, 1);
+
+ /* Add the toplevel shell to the application's object list. */
+ obmNewObject (obm, "toplevel", "TopLevelShell", NULL, NULL, 0);
+
+ if (obm->argc > 0)
+ XtFree ((char *)sv_argv);
+ XtFree ((char *)resource_buf);
+ XtAppSetFallbackResources (obm->app_context, NULL);
+
+ obm->specified++;
+ return (TCL_OK);
+}
+
+
+/* serverAppExtend -- TCL command to extend the application resource database
+ * to allow for the creation of new widgets loaded since the application was
+ * first started. The 'overwrite' option, if present, says to allow the new
+ * resource strings to overwrite the existing resources, otherwise the older
+ * ones will not be changed.
+ *
+ * Usage: appExtend new-resources [overwrite]
+ */
+static int
+serverAppExtend (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ XrmDatabase old_db, extended_db;
+ Boolean overwrite = False;
+ char *resources;
+
+
+ if (!obm->specified || !obm->display || argc < 2)
+ return (TCL_ERROR);
+
+ /* Get arguments. */
+ resources = argv[1];
+ overwrite = (argc > 2) ? (strcmp (argv[2], "overwrite") == 0) : False;
+
+ /* Get the current fallback resource database. */
+ old_db = XrmGetDatabase (obm->display);
+ if (old_db == (XrmDatabase) NULL)
+ return (TCL_ERROR);
+
+ /* Create a database structure from the resource string. */
+ extended_db = XrmGetStringDatabase (resources);
+ if (extended_db == (XrmDatabase) NULL)
+ return (TCL_ERROR);
+
+ /* Combine the old an new databases. */
+ XrmCombineDatabase (extended_db, &old_db, overwrite);
+
+ /* Update the application resource database. */
+ XrmSetDatabase (obm->display, old_db);
+
+ return (TCL_OK);
+}
+
+
+/* serverCreateObjects -- TCL command to create the tree of UI objects
+ * comprising the user interface. The object tree is defined by a string
+ * valued resource. If no resource is named the default "objects" resource
+ * will be used.
+ *
+ * Usage: createObjects [resource-name]
+ */
+static int
+serverCreateObjects (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register char *ip, *op;
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ char name[SZ_NAME], class[SZ_NAME], parent[SZ_NAME];
+ char *objects = NULL;
+ XtResource r;
+
+ r.resource_name = (argc >= 2) ? argv[1] : "objects";
+ r.resource_class = "Objects";
+ r.resource_type = XtRString;
+ r.resource_size = sizeof (char *);
+ r.resource_offset = 0;
+ r.default_type = XtRString;
+ r.default_addr = (caddr_t) NULL;
+
+ /* Get the UI object list. */
+ XtGetApplicationResources (obm->toplevel, &objects, &r, 1, NULL, 0);
+ /* XrmPutFileDatabase (obm->display->db, "zz.list"); */
+
+ /* Parse the objects list and create the objects. Each entry has
+ * the form "parent object-class object-name" with a newline (or
+ * other whitespace) terminating each entry.
+ */
+ for (ip = objects; ip && *ip; ) {
+ /* Get name of parent object. */
+ while (isspace (*ip))
+ ip++;
+ for (op=parent; *ip && !isspace(*ip); )
+ *op++ = *ip++;
+ *op = '\0';
+
+ /* Get object class. */
+ while (isspace (*ip))
+ ip++;
+ for (op=class; *ip && !isspace(*ip); )
+ *op++ = *ip++;
+ *op = '\0';
+
+ /* Get object name. */
+ while (isspace (*ip))
+ ip++;
+ for (op=name; *ip && !isspace(*ip); )
+ *op++ = *ip++;
+ *op = '\0';
+
+ /* Create the new object. */
+ if (*name && *class && *parent)
+ obmNewObject (obm, name, class, parent, NULL, 0);
+
+ while (isspace (*ip))
+ ip++;
+ }
+
+ return (TCL_OK);
+}
+
+
+/* serverDestroyObject -- Destroy an object and all of its children.
+ *
+ * Usage: destroyObject object-name
+ */
+static int
+serverDestroyObject (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ char *object_name;
+ ObmObject killobj;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+
+ object_name = argv[1];
+ if ((killobj = obmFindObject (obm, object_name)) == NULL)
+ return (TCL_ERROR);
+ obmDestroyObject (obm, killobj);
+
+ return (TCL_OK);
+}
+
+
+/* serverQueryObject -- Test if the named object exists.
+ *
+ * Usage: queryObject object-name [class [subclass]]
+ *
+ * A nonzero function value is returned if the named object exists. The
+ * class and subclass of the object are optionally returned in the output
+ * variables given on the command line.
+ */
+static int
+serverQueryObject (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ char *object_name, *s_class, *s_subclass;
+ ObjClassRec classrec;
+ ObmObject o;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+
+ object_name = argv[1];
+ s_class = (argc > 2) ? argv[2] : NULL;
+ s_subclass = (argc > 3) ? argv[3] : NULL;
+
+ if (o = obmFindObject (obm, object_name)) {
+ classrec = o->core.classrec;
+ if (s_class) {
+ BaseClassRec bp;
+ int i;
+
+ for (i=0; i < OtNClasses; i++) {
+ bp = &baseClasses[i];
+ if (bp->class == classrec->object_type) {
+ Tcl_SetVar (obm->tcl, s_class, bp->name, 0);
+ break;
+ }
+ }
+ }
+ if (s_subclass)
+ Tcl_SetVar (obm->tcl, s_subclass,
+ (classrec->object_type == OtShell) ?
+ "Shell" : classrec->name, 0);
+
+ Tcl_SetResult (obm->tcl, TRUESTR, TCL_STATIC);
+ } else
+ Tcl_SetResult (obm->tcl, FALSESTR, TCL_STATIC);
+
+ return (TCL_OK);
+}
+
+
+/* serverActivate -- Activate the user interface. When called the first
+ * time the user interface is created and activated, thereafter the UI is
+ * merely reactivated (e.g. mapped if unmapped).
+ *
+ * Usage: activate
+ */
+static int
+serverActivate (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ XWMHints hints;
+ register int i;
+ ObmObject child;
+ Widget w;
+
+
+ /* Activate the interface. */
+ ObmActivate (obm);
+
+
+ /* Now set the WM hints for the toplevel shell and any subwindows
+ * of the UI. Certain ICCCM-compliant window managers make assumptions
+ * about how the client windows will handle input focus if it's not
+ * set explicitly, and often the UI is not given the focus.
+ */
+ hints.flags = InputHint | StateHint;
+ hints.input = True;
+ hints.initial_state = NormalState;
+ hints.icon_pixmap = None;
+ hints.icon_window = None;
+ hints.icon_x = hints.icon_y = 0;
+ hints.icon_mask = None;
+ hints.window_group = None;
+
+ XSetWMHints(obm->display, XtWindow(obm->toplevel), &hints);
+
+ obj = (ServerObject) obmFindObject (obm, "toplevel");
+ for (i=0; i < obj->core.nchildren; i++) {
+ child = obj->core.children[i];
+ if (child->core.classrec->object_type == OtShell) {
+ w = widgetGetPointer (child);
+ XSetWMHints(obm->display, XtWindow(w), &hints);
+ }
+ }
+
+ return (TCL_OK);
+}
+
+
+/* serverDeactivate -- Deactivate the user interface. Optionally unmaps the
+ * UI and calls the Obm client back to let it know that the UI has been
+ * deactivated.
+ *
+ * Usage: deactivate [unmap]
+ */
+static int
+serverDeactivate (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+
+ ObmDeactivate (obm, argc >=2 && strcmp(argv[1],"unmap") == 0);
+ return (TCL_OK);
+}
+
+
+/* serverSynchronize -- Synchronize the user interface.
+ *
+ * Usage: synchronize
+ *
+ * Any buffered output to the display is flushed and execution pauses until
+ * the display has caught up. It is rarely necessary to sychronize the
+ * display with the client and this defeats the purpose of command buffering,
+ * hence should be done only when necessary. Try "flush" below first.
+ */
+static int
+serverSynchronize (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+
+ XSync (obm->display, False);
+ while (XtAppPending (obm->app_context))
+ XtAppProcessEvent (obm->app_context, XtIMAll);
+
+ return (TCL_OK);
+}
+
+
+/* serverFlush -- Flush output to the user interface.
+ *
+ * Usage: flush
+ *
+ * Any buffered output to the display is flushed to the display and
+ * execution continues.
+ */
+static int
+serverFlush (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+
+ XFlush (obm->display);
+
+ return (TCL_OK);
+}
+
+
+/* serverPostActivateCallback -- Post a callback procedure to be called
+ * when the UI is activated. The UI is activated when it is first
+ * downloaded to server, but it may also be activated (reactivated) after
+ * the application has exited and is later restarted, or when the UI
+ * is deactivated and reactivated. Note that the UI state vis-a-vis the
+ * external world (client application) may no longer be accurate after
+ * it has been idle for a time and then reactivated.
+ *
+ * Usage: postActivateCallback <procedure>
+ */
+static int
+serverPostActivateCallback (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ register ObmCallback cb;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+ if (!(cb = obmAddCallback (&obm->callback_list)))
+ return (TCL_ERROR);
+
+ cb->callback_type = OBMUI_activate;
+ strncpy (cb->name, argv[1], SZ_NAME);
+
+ return (TCL_OK);
+}
+
+
+/* serverPostDeactivateCallback -- Post a callback procedure to be called
+ * when the UI is deactivated.
+ *
+ * Usage: postDeactivateCallback <procedure>
+ */
+static int
+serverPostDeactivateCallback (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ register ObmCallback cb;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+ if (!(cb = obmAddCallback (&obm->callback_list)))
+ return (TCL_ERROR);
+
+ cb->callback_type = OBMUI_deactivate;
+ strncpy (cb->name, argv[1], SZ_NAME);
+
+ return (TCL_OK);
+}
+
+
+/* serverSend -- Send a message to an object. The object interprets the
+ * message and returns a function value as the string result of the TCL
+ * command.
+ *
+ * Usage: send <object> <message>
+ */
+static int
+serverSend (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ int status;
+
+ /* The object which interprets the message leaves the string result,
+ * if any, directly in the server tcl result string.
+ */
+ if (argc == 3)
+ status = ObmDeliverMsg (obm, argv[1], argv[2]);
+ else {
+ char *message = Tcl_Merge (argc-2, &argv[2]);
+ status = ObmDeliverMsg (obm, argv[1], message);
+ free ((char *)message);
+ }
+
+ return (status);
+}
+
+
+/* serverPrint -- Print a string on the standard output. This is used mainly
+ * for debugging user interfaces.
+ *
+ * Usage: print arg [arg ...]
+ */
+static int
+serverPrint (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+
+ if (argc >= 2) {
+ char *message = Tcl_Concat (argc-1, &argv[1]);
+ printf ("%s\n", message);
+ fflush (stdout);
+ free ((char *)message);
+ }
+
+ return (TCL_OK);
+}
+
+
+/* serverReset -- The "reset-server" command is implemented as a special
+ * case in ServerEvaluate. After doing a true reset ServerEvaluate calls
+ * Tcl_Eval to evaluate the full message which still contains the reset-server
+ * command. We want to ignore this the second time, so we treat the command
+ * here as a no-op.
+ *
+ * Usage: reset-server
+ *
+ * Note: for reset-server to be recognized by ServerEvaluate and really reset
+ * things, it must be the first command in a message to the server.
+ */
+static int
+serverReset (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ return (TCL_OK);
+}
+
+
+/* serverGetResource -- Get the string value of the specified application
+ * resource (window system parameter). This allows use of the resource
+ * mechanism to supply default values for GUI parameters.
+ *
+ * Usage: value = getResource resource-name [class [default-value]]
+ *
+ * In the simplest case one merely requests a resource by name and the
+ * string value is returned as the function value. If the resource has
+ * an entry in the fallback resources for the application (appInitialize
+ * resource list) then a value is guaranteed to be returned.
+ *
+ * If the Class name for the resource is given then a class default value
+ * will be returned if no entry is found for the name resource instance.
+ * This is useful when there are a number of resources of the same type
+ * (same class). If most or all resources in the same class have the same
+ * default value one need only make one entry for the Class in the application
+ * defaults resource list. It is up to the application developer to define
+ * the class name of a resource - the class name can be any string. Examples
+ * are "Font", "Cursor", etc. By convention the first character of a class
+ * name is capitalized, while instance names begin with a lower case letter.
+ *
+ * If there is an entry for the named resource in the resource list passed
+ * to appInitialize then a value string is guaranteed to be returned. This
+ * will be either the appInitialize default, or a value specified by the
+ * system or the user in an X resources file. If one is not certain a
+ * default value is defined somewhere, a default value should be specified
+ * in the getResource call as shown above.
+ *
+ * See also getResources, used to get multiple resources in one call.
+ */
+static int
+serverGetResource (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ char *resource_name, *class_name, *default_value;
+ char *value = NULL;
+ XtResource r;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+
+ resource_name = argv[1];
+ class_name = (argc > 2) ? argv[2] : XtCString;
+ default_value = (argc > 3) ? argv[3] : "";
+
+ r.resource_name = resource_name;
+ r.resource_class = class_name;
+ r.resource_type = XtRString;
+ r.resource_size = sizeof (char *);
+ r.resource_offset = 0;
+ r.default_type = XtRString;
+ r.default_addr = (caddr_t) default_value;
+
+ XtGetApplicationResources (obm->toplevel, &value, &r, 1, NULL, 0);
+ Tcl_SetResult (tcl, value, TCL_VOLATILE);
+
+ return (TCL_OK);
+}
+
+
+/* serverGetResources -- Get the string values of a list of resources.
+ *
+ * Usage: getResources resource-list
+ * e.g.
+ * getResources {
+ * { resource [variable class [default-value]]] }
+ * { resource [variable class [default-value]]] }
+ * (etc.)
+ * }
+ *
+ * The resource list is a list of resource descriptions. Each resource
+ * entry must give at least the resource name. If no Tcl variable is named
+ * the resource name will be used and this variable will be set to the
+ * resource value. The class name and default value fields are optional.
+ */
+static int
+serverGetResources (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ register XtResource *r;
+ XtResource resources[MAX_RESOURCES];
+ char *resource_name, *class_name, *default_value;
+ char *resource_list, *variable;
+ char **items, **fields;
+ int nitems, nfields;
+ char buf[SZ_NUMBER];
+ int item, i;
+
+ typedef struct {
+ char *variable;
+ char *value;
+ char *item_list;
+ } Value;
+ Value values[MAX_RESOURCES];
+
+ if (argc < 2) {
+ tcl->result = "missing resource-list argument";
+ return (TCL_ERROR);
+ } else
+ resource_list = argv[1];
+
+ if (Tcl_SplitList (tcl, resource_list, &nitems, &items) != TCL_OK) {
+ tcl->result = "could not parse resource list";
+ return (TCL_ERROR);
+ } else if (nitems > MAX_MENUITEMS)
+ nitems = MAX_MENUITEMS;
+
+ for (item=0; item < nitems; item++) {
+ if (Tcl_SplitList (tcl, items[item], &nfields, &fields) != TCL_OK) {
+err: sprintf (buf, "bad item '%d' in resource list", item + 1);
+ Tcl_AppendResult (tcl, buf, NULL);
+ for (i=0; i < item; i++)
+ free (values[item].item_list);
+ return (TCL_ERROR);
+ }
+
+ if (nfields < 1)
+ goto err;
+
+ resource_name = fields[0];
+ variable = (nfields > 1) ? fields[1] : fields[0];
+ class_name = (nfields > 2) ? fields[2] : XtCString;
+ default_value = (nfields > 3) ? fields[3] : "";
+
+ r = &resources[item];
+ r->resource_name = resource_name;
+ r->resource_class = class_name;
+ r->resource_type = XtRString;
+ r->resource_size = sizeof (char *);
+ r->resource_offset = (unsigned int) &(((Value *)NULL)[item].value);
+ r->default_type = XtRString;
+ r->default_addr = (caddr_t) default_value;
+
+ values[item].variable = variable;
+ values[item].item_list = (char *) fields;
+ }
+
+ XtGetApplicationResources (obm->toplevel,
+ (XtPointer) values, resources, nitems, NULL, 0);
+
+ for (item=0; item < nitems; item++) {
+ if (Tcl_SetVar (tcl,
+ values[item].variable, values[item].value, 0) == NULL) {
+ fprintf (stderr,
+ "Warning (getResources): cannot set value of %s\n",
+ values[item].variable);
+ }
+ free (values[item].item_list);
+ }
+ free ((char *) items);
+
+ return (TCL_OK);
+}
+
+
+/* serverPostTimedCallback -- Post a callback to call the named procedure
+ * back after a specified delay in milliseconds.
+ *
+ * Usage: id = postTimedCallback procedure msec [client-data]
+ *
+ * After the specified delay the user callback procedure will be called
+ * with client_data (if given) as the single argument. Only one call will
+ * be made; the client must repost the callback in each call if the procedure
+ * is to be repeatedly executed.
+ *
+ * An ID value is returned which may be passed to deleteTimedCallback to
+ * delete the timer. If a zero or negative time interval is requested no
+ * timer will be set and zero will be returned as the timer ID.
+ *
+ */
+static int
+serverPostTimedCallback (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ char *userproc, *client_data;
+ unsigned long interval;
+ char buf[SZ_NUMBER];
+ ServerCallback cb;
+ int nchars;
+
+ if (argc < 3)
+ return (TCL_ERROR);
+
+ /* Get arguments. */
+ userproc = argv[1];
+ interval = atoi (argv[2]);
+ client_data = (argc > 3) ? argv[3] : NULL;
+
+ if (interval > 0) {
+ /* Allocate and initialize the callback structure. */
+ nchars = sizeof(serverCallback) + strlen(userproc)+1 +
+ (client_data ? strlen(client_data)+1 : 0);
+ if (!(cb = (ServerCallback) XtCalloc (nchars,1)))
+ return (TCL_ERROR);
+
+ cb->obj = (XtPointer) obj;
+ cb->userproc = (char *)cb + sizeof(serverCallback);
+ cb->client_data = client_data ?
+ cb->userproc+strlen(userproc)+1 : NULL;
+ cb->callback_type = CB_TIMER;
+ cb->next = NULL;
+
+ strcpy (cb->userproc, userproc);
+ if (client_data)
+ strcpy (cb->client_data, client_data);
+
+ cb->id.intervalId = XtAppAddTimeOut (obm->app_context,
+ interval, serverTimedProc, (XtPointer)cb);
+ link_callback (&obj->server, cb);
+ } else
+ cb = NULL;
+
+ sprintf (buf, "0x%lx", cb);
+ Tcl_SetResult (tcl, buf, TCL_VOLATILE);
+
+ return (TCL_OK);
+}
+
+
+/* serverTimedProc -- Xt callback procedure for interval timers.
+ */
+static void
+serverTimedProc (cb_ptr, id)
+XtPointer cb_ptr;
+XtIntervalId *id;
+{
+ register ServerCallback cb = (ServerCallback) cb_ptr;
+ register ServerObject obj = (ServerObject) cb->obj;
+ ObmContext obm = obj->server.obm;
+ int status;
+
+ status = Tcl_VarEval (obm->tcl,
+ cb->userproc, " ",
+ cb->client_data ? cb->client_data : " ",
+ NULL);
+ if (status != TCL_OK) {
+ char *errstr = Tcl_GetVar (obm->tcl, "errorInfo", 0);
+ fprintf (stderr, "Error on line %d in %s: %s\n",
+ obm->tcl->errorLine, cb->userproc,
+ errstr ? errstr : obm->tcl->result);
+ }
+
+ unlink_callback (&obj->server, cb);
+/* XtFree ((char *)cb);*/
+}
+
+
+/* serverDeleteTimedCallback -- Delete a timer callback procedure. This
+ * procedure is typically used to break a timer loop, where the timer
+ * procedure repeatedly reposts itself at the end of each interval.
+ *
+ * Usage: deleteTimedCallback id
+ *
+ * The ID string is returned by postTimedCallback when a timer is posted.
+ */
+static int
+serverDeleteTimedCallback (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ ServerCallback cb;
+ XtIntervalId id;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+
+ if (cb = (ServerCallback) strtol (argv[1], (char **)NULL, 16)) {
+ XtRemoveTimeOut (cb->id.intervalId);
+ unlink_callback (&obj->server, cb);
+ XtFree ((char *)cb);
+ }
+ return (TCL_OK);
+}
+
+
+/* serverPostWorkProc -- Post a callback for a procedure to be called when
+ * the server is idle. Work procedures are used to perform computations in
+ * the background while the user interface remains active and able to respond
+ * to input events. This works only if the user work procedure does its job
+ * in small increments, doing only a small amount of processing in each call.
+ * The work procedure will be called repeatedly until it returns a status
+ * indicating that it has finished its task.
+ *
+ * Usage: id = postWorkProc procedure [client-data]
+ *
+ * When the server has nothing else to do the user work procedure will be
+ * called with client_data (if given) as the single argument. The work
+ * procedure should return the string "done" when all processing is finished,
+ * or any other string if the procedure is to be called again.
+ *
+ * An ID value is returned which may be passed to deleteWorkProc to
+ * delete the work procedure.
+ */
+static int
+serverPostWorkProc (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ register ObmContext obm = obj->server.obm;
+ char *userproc, *client_data;
+ char buf[SZ_NUMBER];
+ ServerCallback cb;
+ int nchars;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+
+ /* Get arguments. */
+ userproc = argv[1];
+ client_data = (argc > 2) ? argv[2] : NULL;
+
+ /* Allocate and initialize the callback structure. */
+ nchars = sizeof(serverCallback) + strlen(userproc)+1 +
+ (client_data ? strlen(client_data)+1 : 0);
+ if (!(cb = (ServerCallback) XtMalloc (nchars)))
+ return (TCL_ERROR);
+
+ cb->obj = (XtPointer) obj;
+ cb->userproc = (char *)cb + sizeof(serverCallback);
+ cb->client_data = client_data ? cb->userproc+strlen(userproc)+1 : NULL;
+ cb->callback_type = CB_WORKPROC;
+ cb->next = NULL;
+
+ strcpy (cb->userproc, userproc);
+ if (client_data)
+ strcpy (cb->client_data, client_data);
+
+ cb->id.workProcId = XtAppAddWorkProc (obm->app_context,
+ serverWorkProc, (XtPointer)cb);
+ link_callback (&obj->server, cb);
+
+ sprintf (buf, "0x%lx", cb);
+ Tcl_SetResult (tcl, buf, TCL_VOLATILE);
+
+ return (TCL_OK);
+}
+
+
+/* serverWorkProc -- Xt callback procedure for work procedures.
+ */
+static Boolean
+serverWorkProc (cb_ptr)
+XtPointer cb_ptr;
+{
+ register ServerCallback cb = (ServerCallback) cb_ptr;
+ register ServerObject obj = (ServerObject) cb->obj;
+ register ObmContext obm = obj->server.obm;
+ Boolean done;
+ int status;
+
+ status = Tcl_VarEval (obm->tcl,
+ cb->userproc, " ",
+ cb->client_data ? cb->client_data : " ",
+ NULL);
+
+ if (status != TCL_OK) {
+ char *errstr = Tcl_GetVar (obm->tcl, "errorInfo", 0);
+ fprintf (stderr, "Error on line %d in %s: %s\n",
+ obm->tcl->errorLine, cb->userproc,
+ errstr ? errstr : obm->tcl->result);
+ done = True;
+ } else
+ done = (strcmp (obm->tcl->result, "done") == 0) ? True : False;
+
+ if (done) {
+ unlink_callback (&obj->server, cb);
+ XtFree ((char *)cb);
+ }
+
+ return (done);
+}
+
+
+/* serverDeleteWorkProc -- Delete a work callback procedure.
+ *
+ * Usage: deleteWorkProc id
+ *
+ * The ID string is returned by postWorkProc when a work procedure is
+ * posted.
+ */
+static int
+serverDeleteWorkProc (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ register ServerObject obj = (ServerObject) object;
+ ServerCallback cb;
+ XtIntervalId id;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+
+ cb = (ServerCallback) strtol (argv[1], (char **)NULL, 16);
+ XtRemoveWorkProc (cb->id.workProcId);
+ unlink_callback (&obj->server, cb);
+ XtFree ((char *)cb);
+ return (TCL_OK);
+}
+
+
+/* link_callback -- Link a callback descriptor into the global server
+ * callback list.
+ */
+static void
+link_callback (server, cb)
+register ServerPrivate server;
+register ServerCallback cb;
+{
+ if (!server->cb_head) {
+ server->cb_head = cb;
+ server->cb_tail = cb;
+ } else {
+ server->cb_tail->next = cb;
+ server->cb_tail = cb;
+ }
+}
+
+
+/* unlink_callback -- Unlink a callback descriptor from the global server
+ * callback list.
+ */
+static void
+unlink_callback (server, cb)
+register ServerPrivate server;
+register ServerCallback cb;
+{
+ register ServerCallback cp;
+
+ if (cb == server->cb_head) {
+ if (!(server->cb_head = cb->next))
+ server->cb_tail = NULL;
+ } else {
+ for (cp = server->cb_head; cp && cp->next != cb; cp = cp->next)
+ ;
+ if (cp) {
+ cp->next = cb->next;
+ if (cb == server->cb_tail)
+ server->cb_tail = cp;
+ }
+ }
+}
+
+
+/* serverCreateBitmap -- Create a named bitmap. This replaces any old bitmap
+ * of the same name. The new bitmap is cached in server memory; when a widget
+ * bitmap resource is set, the bitmap cache will be searched for the named
+ * bitmap before asking Xlib to find the bitmap.
+ *
+ * Usage: createBitmap name width height data
+ *
+ * e.g.,
+ * createBitmap foo 16 16 {
+ * 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x01,
+ * 0x60,0x03,0x20,0x02,0x60,0x03,0xc0,0x01,0x00,0x00,0x00,0x00,
+ * 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
+ */
+static int
+serverCreateBitmap (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ ServerObject obj = (ServerObject) object;
+ ObmContext obm = (ObmContext) obj->server.obm;
+ char *name, *pixels;
+ int width, height;
+ int status;
+
+ if (argc < 5)
+ return (TCL_ERROR);
+
+ name = argv[1];
+ width = atoi (argv[2]);
+ height = atoi (argv[3]);
+ pixels = argv[4];
+
+ status = createBitmap (obm, name, width, height, pixels);
+ return (status == OK ? TCL_OK : TCL_ERROR);
+}
+
+
+/* createBitmap -- Create a bitmap of the indicated size and add it to the
+ * pixmap cache.
+ */
+createBitmap (obm, name, width, height, pixels)
+ObmContext obm;
+char *name;
+int width, height;
+char *pixels;
+{
+ register char *ip, *op;
+ register ObjList lp, last_lp;
+ char numbuf[32], *data, *dp;
+ Icon *icon;
+ int nchars;
+
+ if (!obm->specified || !obm->display)
+ return (TCL_ERROR);
+
+ /* Check if bitmap is already in cache. */
+ for (last_lp = lp = obm->pixmap_cache; lp; lp = lp->next) {
+ if (strcmp (name, lp->name) == 0)
+ break;
+ last_lp = lp;
+ }
+
+ /* Get an empty bitmap descriptor. */
+ if (lp) {
+ if (lp->ptr)
+ freeIcon (obm, (Icon *) lp->ptr);
+ } else {
+ lp = (ObjList) XtMalloc (sizeof (struct objList));
+ if (last_lp)
+ last_lp->next = lp;
+ else
+ obm->pixmap_cache = lp;
+ strcpy (lp->name, name);
+ lp->next = NULL;
+ }
+
+ /* Get bitmap data. */
+ data = (char *) XtCalloc (nchars = (width * height), 1);
+ for (dp=data, ip=pixels; *ip; ) {
+ while (isspace(*ip) || *ip == ',')
+ ip++;
+ for (op=numbuf; *ip && !(isspace(*ip) || *ip == ','); )
+ *op++ = *ip++;
+ *op++ = '\0';
+ if (--nchars >= 0)
+ *dp++ = strtol (numbuf, NULL, 0);
+ }
+
+ /* Create the bitmap. */
+ if (!(icon = (Icon *) XtCalloc (1, sizeof (*icon))))
+ return (TCL_ERROR);
+ icon->pixmap = XCreateBitmapFromData (obm->display,
+ RootWindowOfScreen (obm->screen), data, width, height);
+ lp->ptr = (caddr_t) icon;
+
+ XtFree ((char *)data);
+ return (OK);
+}
+
+
+/* findBitmap -- Search the bitmap cache for the named bitmap. Note that
+ * a bitmap is a pixmap of depth one, hence bitmaps are stored in the pixmap
+ * cache.
+ */
+Pixmap
+findBitmap (obm, name)
+ObmContext obm;
+char *name;
+{
+ return (findPixmap (obm, name));
+}
+
+
+/* serverCreatePixmap -- Create a named pixmap. This replaces any old pixmap
+ * of the same name. The new pixmap is cached in server memory; when a widget
+ * pixmap resource is set, the pixmap cache will be searched for the named
+ * pixmap before asking Xlib to find the pixmap.
+ *
+ * Usage: createPixmap name width height depth fg_color bg_color data
+ *
+ * e.g.,
+ * createPixmap foo 16 16 8 black white {
+ * 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x01,
+ * 0x60,0x03,0x20,0x02,0x60,0x03,0xc0,0x01,0x00,0x00,0x00,0x00,
+ * 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }
+ */
+static int
+serverCreatePixmap (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ ServerObject obj = (ServerObject) object;
+ ObmContext obm = (ObmContext) obj->server.obm;
+ int width, height, depth;
+ char *name, *pixels;
+ unsigned long fg, bg;
+ int status;
+
+ if (argc < 8)
+ return (TCL_ERROR);
+
+ name = argv[1];
+ width = atoi (argv[2]);
+ height = atoi (argv[3]);
+ depth = atoi (argv[4]);
+ pixels = argv[7];
+
+ /* Get fg_color pixel value. */
+ if (isdigit (*argv[5]))
+ fg = strtol (argv[5], NULL, 0);
+ else {
+ XrmValue from, to;
+ from.size = strlen (argv[5]) + 1;
+ from.addr = argv[5];
+ to.addr = (caddr_t) &fg;
+ to.size = sizeof(fg);
+
+ if (!XtConvertAndStore (obm->toplevel,
+ XtRString, &from, XtRPixel, &to))
+ fg = BlackPixelOfScreen (obm->screen);
+ }
+
+ /* Get bg_color pixel value. */
+ if (isdigit (*argv[6]))
+ bg = strtol (argv[6], NULL, 0);
+ else {
+ XrmValue from, to;
+ from.size = strlen (argv[6]) + 1;
+ from.addr = argv[6];
+ to.addr = (caddr_t) &bg;
+ to.size = sizeof(bg);
+
+ if (!XtConvertAndStore (obm->toplevel,
+ XtRString, &from, XtRPixel, &to))
+ bg = WhitePixelOfScreen (obm->screen);
+ }
+
+ status = createPixmap (obm, name, width,height,8, NULL, pixels, fg,bg);
+ return (status == OK ? TCL_OK : TCL_ERROR);
+}
+
+
+/* createPixmap -- Create a pixmap of the indicated size and add it to the
+ * pixmap cache. If PIXMAP is non-null the existing pixmap is merely entered
+ * into the pixmap cache. Otherwise, if PIXELS is NULL an empty pixmap is
+ * created, otherwise PIXELS points to a character string containing the
+ * pixmap data in bitmap format, and BG and FG give the background and
+ * foreground colors.
+ */
+createPixmap (obm, name, width, height, depth, pixmap, pixels, bg, fg)
+ObmContext obm;
+char *name;
+int width, height, depth;
+Pixmap pixmap;
+char *pixels;
+unsigned long fg, bg;
+{
+ register char *ip, *op;
+ register ObjList lp, last_lp;
+ char numbuf[32], *data, *dp;
+ Icon *icon;
+ int nchars;
+
+ if (!obm->specified || !obm->display)
+ return (TCL_ERROR);
+
+ /* Check if pixmap is already in cache. */
+ for (last_lp = lp = obm->pixmap_cache; lp; lp = lp->next) {
+ if (strcmp (name, lp->name) == 0)
+ break;
+ last_lp = lp;
+ }
+
+ /* Get an empty pixmap descriptor. */
+ if (lp) {
+ if (lp->ptr)
+ freeIcon (obm, (Icon *) lp->ptr);
+ } else {
+ lp = (ObjList) XtMalloc (sizeof (struct objList));
+ if (last_lp)
+ last_lp->next = lp;
+ else
+ obm->pixmap_cache = lp;
+ strcpy (lp->name, name);
+ lp->next = NULL;
+ }
+
+ if (!(icon = (Icon *) XtCalloc (1, sizeof (*icon))))
+ return (TCL_ERROR);
+
+ /* Get pixmap data. */
+ if (pixmap) {
+ icon->pixmap = pixmap;
+ } else {
+ if (pixels) {
+ data = (char *) XtCalloc (nchars = (width * height), 1);
+ for (dp=data, ip=pixels; *ip; ) {
+ while (isspace(*ip) || *ip == ',')
+ ip++;
+ for (op=numbuf; *ip && !(isspace(*ip) || *ip == ','); )
+ *op++ = *ip++;
+ *op++ = '\0';
+ if (--nchars >= 0)
+ *dp++ = strtol (numbuf, NULL, 0);
+ }
+
+ /* Create the pixmap. */
+ icon->pixmap = XCreatePixmapFromBitmapData (obm->display,
+ RootWindowOfScreen(obm->screen), data, width,height, fg,bg,
+ depth);
+
+ } else {
+ /* Create the pixmap. */
+ icon->pixmap = XCreatePixmap (obm->display,
+ RootWindowOfScreen(obm->screen), width, height, depth);
+ }
+ }
+
+ lp->ptr = (caddr_t) icon;
+ XtFree ((char *)data);
+
+ return (OK);
+}
+
+
+/* serverCreateXPixmap -- Create a pixmap of the given name. The pixmap is
+ * specified in XPM format which provides much better support for color than
+ * the simpler format used by createPixmap.
+ *
+ * The new pixmap replaces any old pixmap of the same name. The new pixmap
+ * is cached in server memory; when a widget pixmap resource is set, the
+ * pixmap cache will be searched for the named pixmap before asking Xlib to
+ * find the pixmap.
+ *
+ * Usage: createXPixmap name widget description
+ *
+ * where "name" is the name of the pixmap to be created, "widget" is the
+ * name of a widget object to be used to search for pixel resources (to color
+ * the pixmap), and "description" is the XPM format description of the pixmap.
+ *
+ * For example:
+ *
+ * createXPixmap empty_diamond font1 {
+ * [* XPM *]
+ * static char * diamond0c [] = {
+ * [* width height ncolors cpp [x_hot y_hot] *]
+ * "17 17 3 1 0 0",
+ * [* colors *]
+ * " s none m none c none",
+ * ". s topShadowColor m white c #c8c8c8c8c8c8",
+ * "X s bottomShadowColor m black c #646464646464",
+ * [* pixels *]
+ * " ",
+ * " ",
+ * " ... ",
+ * " .. .. ",
+ * " .. .. ",
+ * " .. .. ",
+ * " .. .. ",
+ * " .. .. ",
+ * " . . ",
+ * " XX XX ",
+ * " XX XX ",
+ * " XX XX ",
+ * " XX XX ",
+ * " XX XX ",
+ * " XXX ",
+ * " ",
+ * " "};
+ * }
+ *
+ * In the above the C style comments have been replaced by [* ... *] to avoid
+ * prematurely terminating the C comment you are reading. The actual text
+ * input to createXPixmap should use C style comments, exactly as in the XPM
+ * file.
+ *
+ * The pixmap is specified in XPM format as an array of strings. C style
+ * coments, commas, and whitespace are ignored (but are permitted, to allow
+ * XPM files to be directly included). The XPM format is fully defined in
+ * the XPM documentation, but there is not really that much to it, so we
+ * summarize it here. The advantage of the XPM format is that it uses a
+ * visual ascii representation of the pixmap, and it provides good support
+ * for colored pixmaps, using characters to indicate the color. The fields
+ * of the color description are as follows:
+ *
+ * character character used in "pixels" to signify color (arbitrary)
+ * s resource name used in application to override color
+ * m default color on monochrome screens
+ * g4 for 4-level grayscale screens
+ * g for grayscale with more than 4 levels
+ * c for color screens
+ *
+ * An important feature of createXPixmap is the reference widget. When the
+ * pixmap is created the resource list of the named reference widget will be
+ * searched for any resources that specify colors. If the resource name for
+ * a color resource matches the "s" name given for a color in the XPM pixmap
+ * description, then the widget-specific color will be used. This allows
+ * resources to be used to specify the colors for a pixmap on a per-widget
+ * basis. If a dummy widget-object name is given (e.g. "none") or no matching
+ * resources are found, the default colors will be used. Any widget object
+ * that has color resources may be used for the reference widget (it doesn't
+ * have to be the widget which will later use the pixmap).
+ *
+ * A pixmap created with createXPixmap may be used with the widget-class "set"
+ * command to set the value of any Pixmap or Icon class widget resource.
+ */
+static int
+serverCreateXPixmap (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ ServerObject obj = (ServerObject) object;
+ ObmContext obm = (ObmContext) obj->server.obm;
+ char *name, *widget, *description;
+ int status;
+
+ if (argc < 4)
+ return (TCL_ERROR);
+
+ name = argv[1];
+ widget = argv[2];
+ description = argv[3];
+ while (isspace (*description))
+ description++;
+
+ status = createXPixmap (obm, name, widget, description);
+ return (status == OK ? TCL_OK : TCL_ERROR);
+}
+
+
+/* createXPixmap -- Create a pixmap defined in an XPM format description and
+ * add it to the pixmap cache. DESCRIPTION points to a character string
+ * containing the pixmap description in XPM format. A reference widget object
+ * may be given to associate color resources with the pixmap.
+ */
+createXPixmap (obm, name, widget, description)
+ObmContext obm;
+char *name;
+char *widget;
+char *description;
+{
+ register char *ip, *op;
+ register ObjList lp, last_lp;
+ XpmImage image;
+ ObmObject obj;
+ String *data;
+ Icon *icon;
+ int status;
+ Widget w;
+
+ if (!obm->specified || !obm->display)
+ return (TCL_ERROR);
+
+ /* Get reference widget if any. */
+ w = NULL;
+ if (obj = obmFindObject (obm, widget, obm->toplevel))
+ w = widgetGetPointer (obj);
+
+ /* Create the pixmap (actually icon).
+ */
+ status = XpmCreateXpmImageFromBuffer (description, &image, NULL, NULL);
+ if (status != XpmSuccess)
+ return (TCL_ERROR);
+ XpmCreateDataFromXpmImage (&data, &image, NULL);
+
+ if (data) {
+ static XpmColorSymbol table[MAXCOLORSYM];
+ Cardinal n;
+
+ if (!(icon = (Icon *) XtCalloc (1, sizeof(*icon))))
+ return (TCL_ERROR);
+
+ build_colorlist (w, table, XtNumber(table), &n);
+ icon->attributes.colorsymbols = table;
+ icon->attributes.numsymbols = n;
+ icon->attributes.valuemask = XpmColorSymbols;
+
+ XpmCreatePixmapFromData (obm->display,
+ RootWindowOfScreen(obm->screen), data,
+ &icon->pixmap, &icon->mask, &icon->attributes);
+
+ XtFree ((String) data);
+/* XtFree ((String) table);*/
+ XpmFreeXpmImage (&image);
+
+ } else {
+ XpmFreeXpmImage (&image);
+ return (TCL_ERROR);
+ }
+
+ /* Check if pixmap is already in cache. */
+ for (last_lp = lp = obm->pixmap_cache; lp; lp = lp->next) {
+ if (strcmp (name, lp->name) == 0)
+ break;
+ last_lp = lp;
+ }
+
+ /* Get an empty pixmap descriptor. */
+ if (lp) {
+ if (lp->ptr)
+ freeIcon (obm, (Icon *) lp->ptr);
+ } else {
+ lp = (ObjList) XtMalloc (sizeof (struct objList));
+ if (last_lp)
+ last_lp->next = lp;
+ else
+ obm->pixmap_cache = lp;
+ strcpy (lp->name, name);
+ lp->next = NULL;
+ }
+
+ lp->ptr = (caddr_t) icon;
+ return (OK);
+}
+
+
+/* build_colorlist -- Get a list of all the color resources defined by a
+ * widget. This looks through all the resources for resources that specify
+ * a color (Pixel). All such resources and their values are entered in the
+ * output table.
+ *
+ * To get at the resource value, the resource_offset (an unsigned int) must be
+ * added to the base address of the widget. The widget pointer is first
+ * converted to an unsigned long, tehn the offset is added to it and the result
+ * is converted back to a pointer, in this case a pointer to a Pixel.
+ *
+ * This code is based on build_colortable from icon.c in the FWF sources.
+ */
+static void
+build_colorlist (w, table, size, n)
+Widget w;
+register XpmColorSymbol *table;
+Cardinal size;
+Cardinal *n;
+{
+ Cardinal nres, i;
+ XtResourceList res;
+
+ *n = 0;
+ XtGetResourceList (XtClass(w), &res, &nres);
+ for (i=0; i < nres; i++)
+ if (strcmp(res[i].resource_type, XtRPixel) == 0 && *n < size) {
+ table[*n].name = res[i].resource_name;
+ table[*n].value = NULL;
+ table[*n].pixel =
+ * (Pixel*) ((unsigned long) w + res[i].resource_offset);
+ (*n)++;
+ }
+ if (res)
+ XtFree ((char *)res); /* MF037 */
+}
+
+
+/* findPixmap -- Search the pixmap cache for the named pixmap.
+ */
+Pixmap
+findPixmap (obm, name)
+ObmContext obm;
+char *name;
+{
+ register ObjList lp;
+
+ for (lp = obm->pixmap_cache; lp; lp = lp->next)
+ if (lp->ptr && strcmp (name, lp->name) == 0)
+ return (((Icon *)lp->ptr)->pixmap);
+
+ return ((Pixmap) NULL);
+}
+
+
+/* findIcon -- Search the pixmap cache for the named icon.
+ */
+Icon *
+findIcon (obm, name)
+ObmContext obm;
+char *name;
+{
+ register ObjList lp;
+
+ for (lp = obm->pixmap_cache; lp; lp = lp->next)
+ if (lp->ptr && strcmp (name, lp->name) == 0)
+ return ((Icon *) lp->ptr);
+
+ return ((Icon *) NULL);
+}
+
+
+/* freeIcon -- Free an icon descriptor (pixmap list).
+ */
+void
+freeIcon (obm, icon)
+register ObmContext obm;
+register Icon *icon;
+{
+ if (icon->pixmap)
+ XFreePixmap (obm->display, icon->pixmap);
+ if (icon->mask)
+ XFreePixmap (obm->display, icon->mask);
+ XtFree ((char *) icon);
+}
+
+
+/* serverCreateCursor -- Create a cursor from bitmap data. The cursor is
+ * entered into the server's cursor cache and will override any existing
+ * entry of the same name.
+ *
+ * Usage: createCursor name source mask fg_color bg_color x_hot y_hot
+ * e.g.,
+ * createCursor foo bitmap1 bitmap2 black white 8 8
+ *
+ * The named bitmaps must be created first with createBitmap.
+ */
+static int
+serverCreateCursor (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ ServerObject obj = (ServerObject) object;
+ ObmContext obm = (ObmContext) obj->server.obm;
+ register ObjList lp, last_lp;
+ XColor fg_color, bg_color;
+ unsigned long fg, bg;
+ Pixmap source, mask;
+ Colormap colormap;
+ int x_hot, y_hot;
+ char *name;
+
+ if (!obm->specified || !obm->display)
+ return (TCL_ERROR);
+
+ if (argc < 8)
+ return (TCL_ERROR);
+
+ name = argv[1];
+ source = findPixmap (obm, argv[2]);
+ mask = findPixmap (obm, argv[3]);
+ x_hot = atoi (argv[6]);
+ y_hot = atoi (argv[7]);
+
+ if (!source)
+ return (TCL_ERROR);
+
+ colormap = XDefaultColormapOfScreen (obm->screen);
+
+ /* Get fg_color pixel value. */
+ if (isdigit (*argv[4]))
+ fg = strtol (argv[4], NULL, 0);
+ else {
+ XrmValue from, to;
+ from.size = strlen (argv[4]) + 1;
+ from.addr = argv[4];
+ to.addr = (caddr_t) &fg;
+ to.size = sizeof(fg);
+
+ if (!XtConvertAndStore (obm->toplevel,
+ XtRString, &from, XtRPixel, &to))
+ fg = BlackPixelOfScreen (obm->screen);
+
+ fg_color.pixel = fg;
+ XQueryColor (obm->display, colormap, &fg_color);
+ }
+
+ /* Get bg_color pixel value. */
+ if (isdigit (*argv[5]))
+ bg = strtol (argv[5], NULL, 0);
+ else {
+ XrmValue from, to;
+ from.size = strlen (argv[5]) + 1;
+ from.addr = argv[5];
+ to.addr = (caddr_t) &bg;
+ to.size = sizeof(bg);
+
+ if (!XtConvertAndStore (obm->toplevel,
+ XtRString, &from, XtRPixel, &to))
+ bg = WhitePixelOfScreen (obm->screen);
+
+ bg_color.pixel = bg;
+ XQueryColor (obm->display, colormap, &bg_color);
+ }
+
+ /* Check if cursor is already in cache. */
+ for (last_lp = lp = obm->cursor_cache; lp; lp = lp->next) {
+ if (strcmp (name, lp->name) == 0)
+ break;
+ last_lp = lp;
+ }
+
+ /* Get an empty cursor descriptor. */
+ if (lp) {
+ if (lp->ptr)
+ XFreeCursor (obm->display, (Cursor)lp->ptr);
+ } else {
+ lp = (ObjList) XtMalloc (sizeof (struct objList));
+ if (last_lp)
+ last_lp->next = lp;
+ else
+ obm->cursor_cache = lp;
+ strcpy (lp->name, name);
+ lp->next = NULL;
+ }
+
+ /* Create the cursor. */
+ lp->ptr = (caddr_t) XCreatePixmapCursor (obm->display,
+ source, mask, &fg_color, &bg_color, x_hot, y_hot);
+
+ return (TCL_OK);
+}
+
+
+/* findCursor -- Search the cursor cache for the named cursor.
+ */
+Cursor
+findCursor (obm, name)
+ObmContext obm;
+char *name;
+{
+ register ObjList lp;
+
+ for (lp = obm->cursor_cache; lp; lp = lp->next)
+ if (lp->ptr && strcmp (name, lp->name) == 0)
+ return ((Cursor) lp->ptr);
+
+ return ((Cursor) NULL);
+}
+
+
+/* serverCreateMenu, serverEditMenu -- Create or modify a menu.
+ * The editMenu function is an alias for createMenu.
+ *
+ * Usage: createMenu menu-name parent item-list
+ *
+ * e.g., createMenu menu-name parent {
+ * { label function data [options...] }
+ * { label function data [options...] }
+ * (etc.)
+ * }
+ * where
+ *
+ * menu-name is the object name for the menu popup shell
+ * parent is the parent widget of the menu shell
+ *
+ * label is a menu item label
+ *
+ * function is the function to be performed when the menu
+ * item is selected, e.g., f.exec, f.data, f.space,
+ * or f.line.
+ *
+ * data is function dependent data
+ *
+ * options are option-name option-value pairs, as specified
+ * below.
+ *
+ * In the item list the fields label and option-value may be any Tcl
+ * expression. Expressions are evaluated in the server context. The data
+ * field is a Tcl script to be executed when the menu item is selected.
+ *
+ * Options are specified as "option option-value". The menu item options
+ * are as follows.
+ *
+ * foreground Foreground color.
+ *
+ * background Background color.
+ *
+ * bitmap A bitmap to be displayed left justified in the
+ * label field (e.g. to indicate a parameter setting).
+ *
+ * justify Type of text alignment: left, center, right.
+ *
+ * sensitive Specifies whether the menu item is active (sensitive=
+ * true) or inactive (sensitive=false, item grayed out).
+ *
+ * accelerator Specifies an input translation (accelerator, e.g.,
+ * keyboard event) which can be used to execute the
+ * menu item.
+ *
+ * The option-value field may be any Tcl expression.
+ *
+ * Example: createMenu fileMenu toplevel {
+ * { "File Menu" f.title }
+ * { Open f.exec openFile }
+ * { Save f.exec saveFile }
+ * { Load f.menu loadMenu }
+ * { no-label f.line }
+ * { Quit f.exec "send client Quit" }
+ * }
+ *
+ * The first createMenu is called for a given menu the menu is created,
+ * added to the menu list, and all window system widgets are created for
+ * the menu. Subsequent calls will result in only the changed parts of the
+ * menu being altered provided the changes are not great. Hence this routine
+ * can be called to efficiently modify a menu when minor runtime changes
+ * occur, e.g., an item label or action changes, the item value changes state,
+ * and so on, without need for the GUI code to know how to make the necessary
+ * detailed changes to the widgets used to implement the menu.
+ */
+static int
+serverCreateMenu (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ ServerObject obj = (ServerObject) object;
+ ObmContext obm = (ObmContext) obj->server.obm;
+ register MenuPtr mp, o_mp;
+ register MenuItem ip;
+ register ObjList lp, newobj;
+ char *menu_name, *menu_label;
+ char *parent, *item_list;
+ char **items, **fields;
+ int nitems, nfields;
+ int field, item;
+ ObmObject pobj;
+ Widget w, pw;
+
+ if (obm->being_destroyed)
+ return (TCL_OK);
+ if (!obm->specified || !obm->display)
+ return (TCL_ERROR);
+ if (argc < 4)
+ return (TCL_ERROR);
+
+ menu_name = argv[1];
+ parent = argv[2];
+ item_list = argv[3];
+
+ /* Locate the parent widget. */
+ if ((pobj = (ObmObject) obmFindObject (obm, parent)) == NULL) {
+ fprintf (stderr, "obm: cannot find parent widget %s for menu %s\n",
+ parent, menu_name);
+ return (TCL_ERROR);
+ } else
+ pw = widgetGetPointer (pobj);
+
+ /* Ignore request if parent is being destroyed. */
+ if (pobj->core.being_destroyed)
+ return (TCL_OK);
+
+ /* Get the list of menu item specifier strings. */
+ if (Tcl_SplitList (tcl, item_list, &nitems, &items) != TCL_OK) {
+ fprintf (stderr, "obm: error parsing menu for %s\n", menu_name);
+ return (TCL_ERROR);
+ } else if (nitems > MAX_MENUITEMS)
+ nitems = MAX_MENUITEMS;
+
+ /* Allocate a new, empty menu descriptor. */
+ mp = (MenuPtr) XtCalloc (1, sizeof (Menu));
+
+ /* Process each item and add it to the menu descriptor. */
+ for (item=0; item < nitems; item++) {
+ if (Tcl_SplitList (tcl, items[item], &nfields, &fields) != TCL_OK) {
+ fprintf (stderr, "obm: error parsing menu item %d of %s\n",
+ item + 1, menu_name);
+ continue;
+ }
+
+ ip = &mp->items[mp->nitems++];
+
+ /* The first three fields label,type,data have a fairly strict
+ * syntax and must be in order. Try to interpret the label field
+ * as a string expression; if this fails, assume it is a literal
+ * label string.
+ */
+ field = 0;
+ if (strncmp (fields[field], "f.", 2) == 0)
+ ip->label = NULL;
+ else {
+ char *cp = fields[field++];
+
+ if (Tcl_ExprString (tcl, cp) != TCL_OK)
+ ip->label = cp;
+ else {
+ ip->label = XtMalloc (strlen(tcl->result) + 1);
+ strcpy (ip->label, tcl->result);
+ ip->flags |= M_FreeLabel;
+ }
+ }
+
+ /* Determine menu item type. */
+ if (strcmp (fields[field], "f.exec") == 0) {
+ ip->type = MI_EXEC;
+ ip->data = fields[++field];
+ } else if (strcmp (fields[field], "f.line") == 0) {
+ ip->type = MI_LINE;
+ ip->data = NULL;
+ } else if (strcmp (fields[field], "f.dblline") == 0) {
+ ip->type = MI_DBLLINE;
+ ip->data = NULL;
+ } else if (strcmp (fields[field], "f.menu") == 0) {
+ ip->type = MI_MENU;
+ ip->data = fields[++field];
+ } else if (strcmp (fields[field], "f.space") == 0) {
+ ip->type = MI_SPACE;
+ ip->data = fields[++field];
+ } else if (strcmp (fields[field], "f.title") == 0) {
+ ip->type = MI_TITLE;
+ ip->data = NULL;
+ } else {
+ fprintf (stderr, "obm: bad menu item type `%s'\n",
+ fields[field]);
+ ip->type = MI_IGNORE;
+ ip->data = NULL;
+ }
+ field++;
+
+ /* Process any optional menu item attributes. */
+ for ( ; field < nfields; field++) {
+ if (strcmp (fields[field], "background") == 0) {
+ ip->background = fields[++field];
+
+ } else if (strcmp (fields[field], "foreground") == 0) {
+ ip->foreground = fields[++field];
+
+ } else if (strcmp (fields[field], "bitmap") == 0) {
+ char *cp = fields[++field];
+
+ if (Tcl_ExprString (tcl, cp) != TCL_OK)
+ ip->pixmap = findBitmap (obm, cp);
+ else
+ ip->pixmap = findBitmap (obm, tcl->result);
+
+ } else if (strcmp (fields[field], "justify") == 0) {
+ char *justify = fields[++field];
+ if (strcmp (justify, "left") == 0)
+ ip->justify = XtJustifyLeft;
+ else if (strcmp (justify, "center") == 0)
+ ip->justify = XtJustifyCenter;
+ else if (strcmp (justify, "right") == 0)
+ ip->justify = XtJustifyRight;
+
+ } else if (strcmp (fields[field], "sensitive") == 0) {
+ int ch = fields[++field][0];
+ int bval;
+
+ if (ch == 'f' || ch == 'F')
+ ip->flags |= M_Insensitive;
+ else if (ch == 't' || ch == 'T')
+ ip->flags &= ~M_Insensitive;
+ else {
+ if (Tcl_ExprBoolean (tcl,
+ fields[field], &bval) != TCL_OK) {
+ fprintf (stderr,
+ "menu %s.%d sensitive option: %s\n",
+ menu_name, item, tcl->result);
+ } else if (!bval)
+ ip->flags |= M_Insensitive;
+ }
+
+ } else if (strncmp (fields[field], "accelerator", 5) == 0) {
+ ip->accelerator = fields[++field];
+
+ } else {
+ fprintf (stderr, "obm: bad menu item parameter `%s'\n",
+ fields[field]);
+ }
+ }
+
+ /* Save the string buffer pointer to be freed by menuFree. */
+ ip->sbuf = (char *) fields;
+ }
+
+ /* Free list of menu item specification strings. */
+ free ((char *) items);
+
+ /* Search the menu list and see if there is already a menu with
+ * the given menu name.
+ */
+ for (lp = obm->menu_list, o_mp=NULL; lp; lp = lp->next)
+ if (strcmp (lp->name, menu_name) == 0) {
+ o_mp = (MenuPtr) lp->ptr;
+ break;
+ }
+
+ /* If the menu already exists try to edit it, otherwise delete any
+ * existing menu and create a new one from scratch.
+ */
+ if (o_mp && editMenu (o_mp, mp) == 0) {
+ /* The edit succeeded. Discard the request descriptor. */
+ freeMenu (mp);
+
+ } else if (o_mp) {
+ /* Replace an existing menu. */
+ obmDestroyObject (obm, o_mp->obj);
+ freeMenu (o_mp);
+ createMenu (obm, mp, menu_name, parent, pw);
+ lp->ptr = (caddr_t) mp;
+
+ } else {
+ /* Create a new menu. */
+ createMenu (obm, mp, menu_name, parent, pw);
+
+ newobj = (ObjList) XtMalloc (sizeof (struct objList));
+ strcpy (newobj->name, menu_name);
+ newobj->ptr = (caddr_t) mp;
+ newobj->next = NULL;
+
+ if (obm->menu_list == NULL)
+ obm->menu_list = newobj;
+ else {
+ for (lp = obm->menu_list; lp->next; lp = lp->next)
+ ;
+ lp->next = newobj;
+ }
+ }
+
+ return (TCL_OK);
+}
+
+
+/* serverDestroyMenu -- Destroy a menu. This can be used to free up the
+ * resources used by a menu, e.g., if the menu is not expected to be needed
+ * again for a while.
+ *
+ * Usage: destroyMenu menu-name
+ */
+static int
+serverDestroyMenu (object, tcl, argc, argv)
+ObmObject object;
+Tcl_Interp *tcl;
+int argc;
+char **argv;
+{
+ ServerObject obj = (ServerObject) object;
+ ObmContext obm = (ObmContext) obj->server.obm;
+ register ObjList lp, lpp;
+ register MenuPtr mp;
+ char *menu_name;
+
+ if (argc < 2)
+ return (TCL_ERROR);
+ else
+ menu_name = argv[1];
+
+ /* Locate the menu descriptor. */
+ for (lp = obm->menu_list, lpp=NULL, mp=NULL; lp; lpp=lp, lp=lp->next)
+ if (strcmp (lp->name, menu_name) == 0) {
+ mp = (MenuPtr) lp->ptr;
+ break;
+ }
+ if (mp == NULL)
+ return (TCL_OK);
+
+ /* Unlink the menu from the menu list. */
+ if (lpp == NULL)
+ obm->menu_list = lp->next;
+ else
+ lpp->next = lp->next;
+
+ /* Destroy the menu object and any descendents. */
+ obmDestroyObject (obm, mp->obj);
+ freeMenu (mp);
+
+ return (TCL_OK);
+}
+
+
+/* createMenu -- Create a new menu from a menu descriptor.
+ */
+static void
+createMenu (obm, mp, menu_name, parent, pw)
+ObmContext obm;
+register MenuPtr mp;
+char *menu_name;
+char *parent;
+Widget pw;
+{
+ register MenuItem ip;
+ int itemno, menuno, lineno, spaceno;
+ Widget menu, entry;
+ char name[SZ_NAME];
+ XrmValue from, to[2];
+ Pixel value[2];
+ Arg args[10];
+ int nargs, i;
+
+ menu_classInit();
+
+ /* The following resources are statically defined for all menus. */
+ nargs = 0;
+ if (mp->items[0].type == MI_TITLE) {
+ XtSetArg (args[nargs], XtNlabel, mp->items[0].label);
+ nargs++;
+ }
+
+ /* Create the menu shell. */
+ obmNewObject (obm, menu_name, "SimpleMenu", parent, args, nargs);
+ mp->obj = obmFindObject (obm, menu_name);
+ mp->menuShell = menu = widgetGetPointer (mp->obj);
+ mp->obm = (XtPointer) obm;
+
+ XtAddCallback (menu, XtNpopupCallback, menu_popup, (XtPointer)mp);
+ XtAddCallback (menu, XtNpopdownCallback, menu_popdown, (XtPointer)mp);
+
+ ip = &mp->items[0];
+ itemno = menuno = lineno = spaceno = 1;
+
+ /* Create each menu item. */
+ for (i=0; i < mp->nitems; i++) {
+ ip->menu = (XtPointer)mp;
+
+ /* Create the menu item widget. */
+ switch (ip->type) {
+ case MI_EXEC:
+ sprintf (name, "item%d", itemno++);
+ obmNewObject (obm, name, "SmeBSB", menu_name, NULL, 0);
+ entry = XtNameToWidget (menu, name);
+ XtAddCallback (entry, XtNcallback, menuSelect, (XtPointer)mp);
+ break;
+
+ case MI_LINE:
+ sprintf (name, "line%d", lineno++);
+ obmNewObject (obm, name, "SmeLine", menu_name, NULL, 0);
+ entry = XtNameToWidget (menu, name);
+ break;
+
+ case MI_DBLLINE:
+ nargs = 0;
+ XtSetArg (args[nargs], XtNheight, 2);
+ nargs++;
+ sprintf (name, "line%d", lineno++);
+ obmNewObject (obm, name, "SmeLine", menu_name, args, nargs);
+ sprintf (name, "line%d", lineno++);
+ obmNewObject (obm, name, "SmeLine", menu_name, args, nargs);
+ entry = XtNameToWidget (menu, name);
+ break;
+
+ case MI_MENU:
+ sprintf (name, "menu%d", menuno++);
+ obmNewObject (obm, name, "SmeBSB", menu_name, NULL, 0);
+ entry = XtNameToWidget (menu, name);
+ XtAddCallback (entry, XtNcallback, menuSelect, (XtPointer)mp);
+
+ menu_addEntry (entry, menu_name, ip->data, obm);
+ XtAddCallback (entry, XtNdestroyCallback, menu_delEntry,
+ (XtPointer)NULL);
+ break;
+
+ case MI_SPACE:
+ nargs = 0;
+ XtSetArg (args[nargs], XtNheight, atoi(ip->data));
+ nargs++;
+ sprintf (name, "line%d", lineno++);
+ obmNewObject (obm, name, "Sme", menu_name, args, nargs);
+ entry = XtNameToWidget (menu, name);
+ break;
+
+ case MI_TITLE:
+ if (i > 0)
+ fprintf (stderr,
+ "obm: menu title must be first item in menu\n");
+ ip++;
+ continue;
+
+ default:
+ /* ignore */
+ fprintf (stderr, "obm: unknown menu item type %s[%d]\n",
+ menu_name, i + 1);
+ ip++;
+ continue;
+ }
+
+ /* Set the item specific resources. */
+ nargs = 0;
+ if (ip->label) {
+ XtSetArg (args[nargs], XtNlabel, ip->label);
+ nargs++;
+ }
+ if (ip->background || ip->foreground) {
+ char *s[3];
+ int i=0;
+
+ if (ip->background)
+ s[i++] = ip->background;
+ if (ip->foreground)
+ s[i++] = ip->foreground;
+ s[i++] = NULL;
+
+ for (i=0; s[i]; i++) {
+ from.size = strlen(s[i]) + 1;
+ from.addr = s[i];
+ to[i].addr = (caddr_t) &value[i];
+ to[i].size = sizeof(value[i]);
+
+ if (XtConvertAndStore (entry,
+ XtRString, &from, XtRPixel, &to[i])) {
+ XtSetArg (args[nargs], s[i] == ip->background ?
+ XtNbackground : XtNforeground, value[i]);
+ nargs++;
+ }
+ }
+ }
+ if (ip->justify) {
+ XtSetArg (args[nargs], XtNjustify, ip->justify);
+ nargs++;
+ }
+ if (ip->pixmap) {
+ XtSetArg (args[nargs], XtNleftBitmap, ip->pixmap);
+ nargs++;
+ }
+ if (ip->type == MI_MENU) {
+ XtSetArg (args[nargs], XtNrightBitmap,
+ menu_pullrightBitmap (obm, 0));
+ nargs++;
+ XtSetArg (args[nargs], XtNrightMargin, MB_WIDTH);
+ nargs++;
+ }
+ if (ip->flags & M_Insensitive) {
+ XtSetArg (args[nargs], XtNsensitive, False);
+ nargs++;
+ }
+ if (ip->accelerator) {
+ char buf[SZ_MESSAGE];
+ sprintf (buf, "%s: notify()", ip->accelerator);
+ XtSetArg (args[nargs], XtNaccelerators, buf);
+ nargs++;
+ }
+
+ if (nargs)
+ XtSetValues (entry, args, nargs);
+ ip->entry = entry;
+ ip++;
+ }
+}
+
+
+/* editMenu -- Edit a menu given descriptors for the current menu and the
+ * new version. Zero is returned if the edit succeeds. If the menus are
+ * too different editMenu will play it safe and return nonzero, and the
+ * caller should delete the old one and create a new menu from scratch.
+ */
+static int
+editMenu (mp, request)
+register MenuPtr mp; /* existing menu */
+MenuPtr request; /* requested values */
+{
+ register MenuItem old, new;
+ register int i;
+ int ncolors=0, nargs=0;
+ XrmValue from, to[2];
+ Pixel value[2];
+ Arg args[10];
+
+ /* Make a quick comparision of the old and new menu descriptors to
+ * see if they are similar enough for the edit to make sense.
+ */
+ if (mp->nitems != request->nitems)
+ return (-1);
+ for (i=0; i < mp->nitems; i++) {
+ if (mp->items[i].type != request->items[i].type)
+ return (-1);
+ }
+
+ /* Edit each menu item. */
+ for (i=0; i < mp->nitems; i++) {
+ old = &mp->items[i];
+ new = &request->items[i];
+
+ nargs = 0;
+ if (new->label &&
+ (!old->label || strcmp (old->label, new->label))) {
+ if (old->flags & M_FreeLabel)
+ old->label = XtRealloc (old->label, strlen(new->label)+1);
+ else {
+ old->label = XtMalloc (strlen(new->label) + 1);
+ old->flags |= M_FreeLabel;
+ }
+ strcpy (old->label, new->label);
+ XtSetArg (args[nargs], XtNlabel, old->label);
+ nargs++;
+ }
+
+ if (new->data && (!old->data || strcmp (old->data, new->data))) {
+ if (old->flags & M_FreeData)
+ old->data = XtRealloc (old->data, strlen(new->data)+1);
+ else {
+ old->data = XtMalloc (strlen(new->data) + 1);
+ old->flags |= M_FreeData;
+ }
+ strcpy (old->data, new->data);
+ }
+
+ if (new->background && (!old->background ||
+ strcmp (old->background, new->background))) {
+
+ int nchars = strlen (new->background) + 1;
+ char *s;
+
+ if (old->flags & M_FreeBackground)
+ old->background = XtRealloc (old->background, nchars);
+ else {
+ old->background = XtMalloc (nchars);
+ old->flags |= M_FreeBackground;
+ }
+ strcpy (s = old->background, new->background);
+
+ from.size = strlen(s) + 1;
+ from.addr = s;
+ to[ncolors].addr = (caddr_t) &value[ncolors];
+ to[ncolors].size = sizeof(value[ncolors]);
+
+ if (XtConvertAndStore (old->entry,
+ XtRString, &from, XtRPixel, &to[ncolors])) {
+ XtSetArg (args[nargs], XtNbackground, value[ncolors]);
+ nargs++;
+ ncolors++;
+ }
+ }
+
+ if (new->foreground && (!old->foreground ||
+ strcmp (old->foreground, new->foreground))) {
+
+ int nchars = strlen (new->foreground) + 1;
+ char *s;
+
+ if (old->flags & M_FreeForeground)
+ old->foreground = XtRealloc (old->foreground, nchars);
+ else {
+ old->foreground = XtMalloc (nchars);
+ old->flags |= M_FreeForeground;
+ }
+ strcpy (s = old->foreground, new->foreground);
+
+ from.size = strlen(s) + 1;
+ from.addr = s;
+ to[ncolors].addr = (caddr_t) &value[ncolors];
+ to[ncolors].size = sizeof(value[ncolors]);
+
+ if (XtConvertAndStore (old->entry,
+ XtRString, &from, XtRPixel, &to[ncolors])) {
+ XtSetArg (args[nargs], XtNforeground, value[ncolors]);
+ nargs++;
+ ncolors++;
+ }
+ }
+
+ if (old->justify != new->justify) {
+ old->justify = new->justify;
+ XtSetArg (args[nargs], XtNjustify, old->justify);
+ nargs++;
+ }
+
+ if (new->accelerator && (!old->accelerator ||
+ strcmp (old->accelerator, new->accelerator))) {
+ char buf[SZ_MESSAGE];
+ int nchars = strlen (new->accelerator) + 1;
+ sprintf (buf, "%s: notify()", new->accelerator);
+
+ if (old->flags & M_FreeAccel)
+ old->accelerator = XtRealloc (old->accelerator, nchars);
+ else {
+ old->accelerator = XtMalloc (nchars);
+ old->flags |= M_FreeAccel;
+ }
+ strcpy (old->accelerator, new->accelerator);
+ XtSetArg (args[nargs], XtNaccelerators, buf);
+ nargs++;
+ }
+
+ if (old->pixmap != new->pixmap) {
+ old->pixmap = new->pixmap;
+ XtSetArg (args[nargs], XtNleftBitmap, new->pixmap);
+ nargs++;
+ }
+
+ if ((old->flags & M_Insensitive) != (new->flags & M_Insensitive)) {
+ if (new->flags & M_Insensitive) {
+ old->flags |= M_Insensitive;
+ XtSetArg (args[nargs], XtNsensitive, False);
+ } else {
+ old->flags &= ~M_Insensitive;
+ XtSetArg (args[nargs], XtNsensitive, True);
+ }
+ nargs++;
+ }
+
+ if (old->entry && nargs > 0)
+ XtSetValues (old->entry, args, nargs);
+ }
+
+ return (0);
+}
+
+
+/* freeMenu -- Free a menu descriptor.
+ */
+void
+freeMenu (mp)
+register MenuPtr mp;
+{
+ register MenuItem ip;
+ register int i;
+
+ for (i=0; i < mp->nitems; i++) {
+ ip = &mp->items[i];
+ if (ip->type == MI_MENU && ip->entry)
+ menu_delEntry (ip->entry);
+
+ if ((ip->flags & M_FreeBackground) && ip->background)
+ XtFree (ip->background);
+ if ((ip->flags & M_FreeForeground) && ip->foreground)
+ XtFree (ip->foreground);
+ if ((ip->flags & M_FreeAccel) && ip->accelerator)
+ XtFree (ip->accelerator);
+ if ((ip->flags & M_FreeLabel) && ip->label)
+ XtFree (ip->label);
+ if ((ip->flags & M_FreeData) && ip->data)
+ XtFree (ip->data);
+
+ XtFree (ip->sbuf);
+ }
+
+ XtFree ((char *)mp);
+}
+
+
+/* menuSelect -- Callback routine, called when a menu item is selected.
+ */
+static void
+menuSelect (w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data;
+{
+ register MenuPtr mp = (MenuPtr) client_data;
+ register MenuItem ip;
+ register int i;
+ ObmContext obm = (ObmContext) mp->obm;
+
+ /* Locate the menu item to which the callback refers. */
+ for (i=0, ip=NULL; i < mp->nitems; i++) {
+ ip = &mp->items[i];
+ if (ip->entry == w)
+ break;
+ }
+
+ /* Ignore callbacks other than for command type menu entries (MI_EXEC).
+ * In the case of a command entry the data field of the menu item
+ * descriptor contains the server command to be executed.
+ */
+
+ if (ip && ip->type == MI_EXEC)
+ if (Tcl_Eval (obm->tcl, ip->data) != TCL_OK) {
+ char *errstr = Tcl_GetVar (obm->tcl, "errorInfo", 0);
+ fprintf (stderr, "Error %s.%s line %d: %s\n",
+ mp->obj->core.name, XtName(ip->entry), obm->tcl->errorLine,
+ errstr ? errstr : obm->tcl->result);
+ }
+}
+
+
+/*
+ * The following code is used to interpose special highlight() and
+ * unhighlight() SmeBSB class procedures, to allow menu entries which point to
+ * submenus to automatically popup and popdown the submenu when the menu entry
+ * which points to it is highlighted or unhighlighted. It is a bit of a trick
+ * to replace the standard class procedure as we do here, but this is a lot
+ * simpler than subclassing the entire SmeBSB widget, and works for both the
+ * standard Athena and Xaw3D versions of the widget. Tying the submenu
+ * popup/popdown to the simpleMenu highlight/unhighlight actions allows us
+ * to let the simpleMenu widget track the pointer and determine when the
+ * submenu should be displayed.
+ *
+ * [Note added later]. Having now implemented this technique for cascaded
+ * menus I am not sure it was the best way to do this - it might have been
+ * better to just use motion events. There were a number of subtle problems
+ * to be solved to get this to work. It is true though, that most of these
+ * don't have much to do with the interpose-highlight technique, so maybe
+ * I would have run into the problems anyway. The problems were things like
+ * state changes when the pointer crosses from a menu pane into a submenu,
+ * the need to explicitly popdown submenus when the main window is popped
+ * down or when a nonmenu item in the main menu is entered, getting the
+ * grabs right, an annoying warning message about trying to remove a grab
+ * which no longer existed, and so on. Getting the pull-right bitmap to
+ * look right took a lot of fiddling and it appeared that there might be a
+ * positioning bug in the Xaw3d code related to this. The one problem that
+ * was clearly specific to the highlight/unhighlight technique was that
+ * SmeBSB uses a toggle type function for highlight/unhighlight. This
+ * assumes that the toggle is always in a known state. This works for a
+ * single menu, but it appears that there is a bug in the SmeBSB code when
+ * a pull-right menu obscures the parent menu, causing the toggle to get
+ * out of phase when moving into the submenu band back out (out of phase
+ * means that when one calls the unhighlight class procedure, the toggle
+ * actually highlights, and vice versa). I had to disable highlighting of
+ * submenu items in a menu to avoid this problem.
+ */
+
+/* The following describes a menu entry widget which calls a submenu. */
+struct _menuEntry {
+ Widget w; /* this widget */
+ char name[SZ_NAME]; /* name of menu containing widget */
+ char child[SZ_NAME]; /* submenu name */
+ Widget menu; /* shell widget of submenu */
+ ObmContext obm; /* obm context */
+ struct _menuEntry *next; /* next menuEntry on list */
+};
+typedef struct _menuEntry menuEntry, *MenuEntry;
+
+MenuEntry menuWidgetList;
+static char menu_bitmap1[] = "BSB_pullright1";
+static char menu_bitmap2[] = "BSB_pullright2";
+static void (*BSB_highlight)();
+static void (*BSB_unhighlight)();
+
+
+/* menu_classInit -- Edit the SME class record to interpose our custom
+ * highlight/unhighlight class procedures.
+ */
+static void
+menu_classInit()
+{
+ register SmeClassPart *sme = &smeBSBClassRec.sme_class;
+
+ if (sme->highlight != menu_highlight) {
+ BSB_highlight = sme->highlight;
+ sme->highlight = menu_highlight;
+ }
+ if (sme->unhighlight != menu_unhighlight) {
+ BSB_unhighlight = sme->unhighlight;
+ sme->unhighlight = menu_unhighlight;
+ }
+}
+
+
+/* menu_pullrightBitmap -- Return the bitmap id of the pullright bitmap
+ * displayed on the right side of a menu entry that brings up a submenu.
+ */
+static Pixmap
+menu_pullrightBitmap (obm, state)
+ObmContext obm;
+int state;
+{
+ Pixmap bitmap;
+
+ if (bitmap = findBitmap (obm, state ? menu_bitmap2 : menu_bitmap1))
+ return (bitmap);
+
+ createBitmap (obm, menu_bitmap1, MB_WIDTH, MB_HEIGHT, MB1_PIXELS);
+ createBitmap (obm, menu_bitmap2, MB_WIDTH, MB_HEIGHT, MB2_PIXELS);
+
+ if (bitmap = findBitmap (obm, state ? menu_bitmap2 : menu_bitmap1))
+ return (bitmap);
+ else
+ return ((Pixmap)NULL);
+}
+
+
+/* menu_addEntry -- Add a widget to the menuWidgetList list.
+ */
+static void
+menu_addEntry (w, name, child, obm)
+Widget w; /* menu entry which calls submenu */
+char *name; /* name of menu containing this widget */
+char *child; /* name of submenu shell widget */
+ObmContext obm;
+{
+ register MenuEntry mw, new;
+
+ for (mw=menuWidgetList; mw && mw->next; mw = mw->next)
+ if (mw->w == w)
+ return;
+
+ if ((new = (MenuEntry) XtCalloc (1, sizeof (menuEntry))) == NULL)
+ return;
+
+ new->w = w;
+ strcpy (new->name, name);
+ strcpy (new->child, child);
+ new->obm = obm;
+
+ if (mw)
+ mw->next = new;
+ else
+ menuWidgetList = new;
+}
+
+
+/* menu_delEntry -- Delete a widget from the menuWidgetList list.
+ */
+static void
+menu_delEntry (w, client_data, call_data)
+register Widget w;
+XtPointer client_data; /* not used */
+XtPointer call_data; /* not used */
+{
+ register MenuEntry mw, prev_mw;
+
+ for (mw=menuWidgetList, prev_mw=NULL; mw; prev_mw=mw, mw=mw->next)
+ if (mw->w == w)
+ break;
+
+ if (mw) {
+ if (prev_mw)
+ prev_mw->next = mw->next;
+ else
+ menuWidgetList = NULL;
+ XtFree ((char *)mw);
+ }
+}
+
+
+/* menu_popup -- Called when a menu is popped up.
+ */
+static void
+menu_popup (w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data; /* not used */
+{
+ register MenuPtr mp = (MenuPtr) client_data;
+ mp->popped_up = True;
+}
+
+
+/* menu_popdown -- Called when a menu is popped down. Make sure any
+ * child menus are popped down before popping down the parent.
+ */
+static void
+menu_popdown (w, client_data, call_data)
+Widget w;
+XtPointer client_data;
+XtPointer call_data; /* not used */
+{
+ register MenuPtr mp = (MenuPtr) client_data;
+ ObmContext obm = (ObmContext) mp->obm;
+ register MenuEntry mw;
+ register MenuPtr mm;
+ MenuItem mi;
+ int i;
+
+ /* This routine is called with w=NULL to popdown any child menus.
+ * If w is not NULL that means we are being called as a
+ * popdownCallback for the menu shell.
+ */
+ if (w)
+ mp->popped_up = False;
+
+ for (i=0; i < mp->nitems; i++) {
+ mi = &mp->items[i];
+ if (mi->type == MI_MENU) {
+ /* Locate menu descriptor for named menu. */
+ mm = findMenu (obm, mi->data);
+
+ /* Popdown the child menu (if necessary). The temporary dummy
+ * warning message handler appears to be the only way to avoid
+ * a warning messages about an attempt to remove a grab for a
+ * widget on the grab list. What happens is that when
+ * XtPopdown is called on the main menu this calls
+ * XtRemoveGrab on the window, which removes any grabs for the
+ * main window _and any later grabs_, i.e., for the submenus.
+ * XtPopdown then calls the menu_popdown popdown callback,
+ * which is necessary to popdown any child menus as XtPopdown
+ * does not do this. The XtPopdown called in this routine
+ * tries to remove a grab which has already been removed,
+ * causing the warning message. The situation itself appears
+ * to be harmless, so the best thing to do is just disable the
+ * warning message.
+ */
+ if (mm && mm->menuShell) {
+ XtErrorMsgHandler old_handler;
+
+ old_handler = XtAppSetWarningMsgHandler (obm->app_context,
+ (XtErrorMsgHandler) menu_popdown_msgHandler);
+ XtPopdown (mm->menuShell);
+ XtAppSetWarningMsgHandler (obm->app_context, old_handler);
+ }
+ }
+ }
+}
+
+
+/* menu_popdown_msgHandler -- Dummy warning message handler used in menu
+ * popdown above.
+ */
+static void
+menu_popdown_msgHandler (name,type,class,defaultp,params,num_params)
+String name,type,class,defaultp;
+String* params;
+Cardinal* num_params;
+{
+}
+
+
+/* menu_highlight -- Custom version of the simpleMenu class action highlight
+ * procedure, called when a menu entry is highlighted. This is identical to
+ * the standard class procedure (in fact we call the standard class highlight
+ * procedure) except that we check to see if the widget being highlighted
+ * is a submenu, and if so, popup the submenu.
+ */
+static void
+menu_highlight (w)
+register Widget w;
+{
+ register MenuEntry mw, sm;
+ ObmContext obm = global_obm_handle;
+ MenuPtr mp;
+
+ /* If we are highlighting an entry in a menu then any pull-right
+ * submenus which are still up should not be, so get rid of them.
+ */
+ if (mp = findMenu (obm, XtName(w->core.parent)))
+ menu_popdown ((Widget)NULL, (XtPointer)mp, (XtPointer)NULL);
+
+ /* Is the menu entry being highlighted on our list of call-submenu
+ * widgets?
+ */
+ for (mw=menuWidgetList; mw; mw = mw->next)
+ if (mw->w == w)
+ break;
+
+ /* If the menu item is for a submenu, popup the submenu. */
+ if (mw) {
+ Position x, y;
+ Position menu_x, menu_y;
+ Dimension parent_width;
+ Dimension menu_width, menu_height;
+ char target[SZ_NAME];
+ ObmContext obm = mw->obm;
+ Widget menu;
+
+ /* If the parent menu is not popped up, do not pop up the
+ * child. This doesn't sound likely but it can happen when
+ * menu_highlight is called after a button-up event which
+ * pops down the main menu.
+ */
+ if (mp && mp->popped_up == False)
+ return;
+
+ /* Get shell widget of submenu. */
+ if (!(mp && (mw->menu = XtNameToWidget (mp->menuShell, target)))) {
+ sprintf (target, "*%s", mw->child);
+ if (!(mw->menu = XtNameToWidget (obm->toplevel, target)))
+ return;
+ }
+
+ menu = mw->menu;
+ XtTranslateCoords (w, 0, 0, &x, &y);
+ XtVaGetValues (w, XtNwidth, &parent_width, NULL);
+
+ menu_width = menu->core.width + 2 * menu->core.border_width;
+ menu_height = menu->core.height + 2 * menu->core.border_width;
+ menu_x = x + parent_width - 5;
+ menu_y = y - 5;
+
+ if (menu_x >= 0) {
+ int scr_width = WidthOfScreen(XtScreen(menu));
+ if ((int)(menu_x + menu_width) > (int)scr_width)
+ menu_x = scr_width - menu_width;
+ }
+ if (menu_x < 0)
+ menu_x = 0;
+
+ if (menu_y >= 0) {
+ int scr_height = HeightOfScreen(XtScreen(menu));
+ if ((int)(menu_y + menu_height) > (int)scr_height)
+ menu_y = scr_height - menu_height;
+ }
+ if (menu_y < 0)
+ menu_y = 0;
+
+ XtVaSetValues (menu,
+ XtNx, menu_x,
+ XtNy, menu_y,
+ NULL);
+ /*
+ * This appears to bring out a bug in the Xaw|Xaw3d SmeBSB.
+ *
+ XtVaSetValues (w,
+ XtNrightBitmap, menu_pullrightBitmap (obm, 1),
+ NULL);
+ */
+
+ /* Popup the pull-right menu. */
+ XtPopup (menu, XtGrabNonexclusive);
+
+ } else {
+ /* Call the standard simplemenu highlight method. */
+ BSB_highlight (w);
+ }
+}
+
+
+/* menu_unhighlight -- Custom unhighlight class procedure, interposed in
+ * front of the standard class procedure.
+ */
+static void
+menu_unhighlight (w)
+register Widget w;
+{
+ register MenuEntry mw;
+
+ /* Is the menu entry being unhighlighted on our list of call-submenu
+ * widgets?
+ */
+ for (mw=menuWidgetList; mw; mw = mw->next)
+ if (mw->w == w)
+ break;
+
+ if (mw == NULL) {
+ /* Now call the standard class unhighlight procedure. */
+ BSB_unhighlight (w);
+
+ } else if (mw->menu) {
+ /* Popdown the submenu.
+ */
+ ObmContext obm = mw->obm;
+ Dimension width, height;
+ int in_window, i;
+ XMotionEvent *ev;
+ XEvent event;
+ Position x, y;
+ Widget wl[2];
+
+ /* Get the next window event. All we are looking for here is
+ * the pointer coordinates.
+ */
+ ev = (XMotionEvent *) &event;
+ while (!XtAppPeekEvent (obm->app_context, &event))
+ ;
+
+ wl[0] = w;
+ wl[1] = mw->menu;
+ in_window = 0;
+
+ /* Check if the pointer is in either the pull-right pane of
+ * the parent menu, or the pull-right menu itself.
+ */
+ for (i=0; i < 2; i++) {
+ XtTranslateCoords (wl[i], 0, 0, &x, &y);
+ XtVaGetValues (wl[i],
+ XtNwidth, &width,
+ XtNheight, &height,
+ NULL);
+
+ if (ev->x_root >= x && ev->x_root < x + (Position)width &&
+ ev->y_root >= y && ev->y_root < y + (Position)height) {
+ in_window++;
+ break;
+ }
+ }
+
+ /* If it is not in either window then go ahead and popdown the
+ * child menu, otherwise ignore the request. Erroneous requests
+ * can occur when the pointer is in crossing from one window
+ * to the other.
+ */
+ if (!in_window) {
+ /*
+ * This appears to bring out a bug in the Xaw3d SmeBSB.
+ XtVaSetValues (w,
+ XtNrightBitmap, menu_pullrightBitmap (obm, 0),
+ NULL);
+ */
+
+ XtPopdown (mw->menu);
+ }
+ }
+}
+
+
+/* findMenu -- Return the menu descriptor of a menu given its name.
+ */
+static MenuPtr
+findMenu (obm, name)
+register ObmContext obm;
+char *name;
+{
+ register ObjList lp;
+
+ for (lp = obm->menu_list; lp; lp = lp->next)
+ if (strcmp (lp->name, name) == 0)
+ return ((MenuPtr) lp->ptr);
+
+ return (NULL);
+}