From 0e3cdce02d796625ee894bfa0ba1bd7b3237c2bc Mon Sep 17 00:00:00 2001 From: "Richard M. Stallman" Date: Tue, 18 Jan 1994 23:47:41 +0000 Subject: Initial revision --- lwlib/Makefile.in | 397 ++++++++++++++ lwlib/dispatch.c | 275 ++++++++++ lwlib/lwlib-Xlw.c | 178 ++++++ lwlib/lwlib-Xlw.h | 29 + lwlib/lwlib-Xm.c | 1495 +++++++++++++++++++++++++++++++++++++++++++++++++++ lwlib/lwlib-Xm.h | 34 ++ lwlib/lwlib-Xol.h | 29 + lwlib/lwlib-int.h | 54 ++ lwlib/lwlib-utils.c | 162 ++++++ lwlib/lwlib-utils.h | 20 + lwlib/lwlib.c | 1111 ++++++++++++++++++++++++++++++++++++++ lwlib/lwlib.h | 103 ++++ lwlib/xlwmenu.c | 1265 +++++++++++++++++++++++++++++++++++++++++++ lwlib/xlwmenu.h | 50 ++ 14 files changed, 5202 insertions(+) create mode 100644 lwlib/Makefile.in create mode 100644 lwlib/dispatch.c create mode 100644 lwlib/lwlib-Xlw.c create mode 100644 lwlib/lwlib-Xlw.h create mode 100644 lwlib/lwlib-Xm.c create mode 100644 lwlib/lwlib-Xm.h create mode 100644 lwlib/lwlib-Xol.h create mode 100644 lwlib/lwlib-int.h create mode 100644 lwlib/lwlib-utils.c create mode 100644 lwlib/lwlib-utils.h create mode 100644 lwlib/lwlib.c create mode 100644 lwlib/lwlib.h create mode 100644 lwlib/xlwmenu.c create mode 100644 lwlib/xlwmenu.h (limited to 'lwlib') diff --git a/lwlib/Makefile.in b/lwlib/Makefile.in new file mode 100644 index 00000000000..5caa9310b99 --- /dev/null +++ b/lwlib/Makefile.in @@ -0,0 +1,397 @@ +# Makefile generated by imake - do not edit! +# $XConsortium: imake.c,v 1.51 89/12/12 12:37:30 jim Exp $ +# +# The cpp used on this machine replaces all newlines and multiple tabs and +# spaces in a macro expansion with a single space. Imake tries to compensate +# for this, but is not always successful. +# + +########################################################################### +# Makefile generated from "Imake.tmpl" and +# $XConsortium: Imake.tmpl,v 1.77 89/12/18 17:01:37 jim Exp $ +# +# Platform-specific parameters may be set in the appropriate .cf +# configuration files. Site-wide parameters may be set in the file +# site.def. Full rebuilds are recommended if any parameters are changed. +# +# If your C preprocessor doesn't define any unique symbols, you'll need +# to set BOOTSTRAPCFLAGS when rebuilding imake (usually when doing +# "make Makefile", "make Makefiles", or "make World"). +# +# If you absolutely can't get imake to work, you'll need to set the +# variables at the top of each Makefile as well as the dependencies at the +# bottom (makedepend will do this automatically). +# + +########################################################################### +# platform-specific configuration parameters - edit sun.cf to change + +# platform: $XConsortium: sun.cf,v 1.38 89/12/23 16:10:10 jim Exp $ +# operating system: SunOS 4.0.3 + +########################################################################### +# site-specific configuration parameters - edit site.def to change + +# site: $XConsortium: site.def,v 1.21 89/12/06 11:46:50 jim Exp $ + + SHELL = /bin/sh + + TOP = . + CURRENT_DIR = . + + AR = ar cq + BOOTSTRAPCFLAGS = + CC = gcc -DNOSTDHDRS -fstrength-reduce -fpcc-struct-return -fwritable-strings -traditional + + COMPRESS = compress + CPP = /lib/cpp $(STD_CPP_DEFINES) + PREPROCESSCMD = gcc -DNOSTDHDRS -fstrength-reduce -fpcc-struct-return -fwritable-strings -traditional -E $(STD_CPP_DEFINES) + INSTALL = install + LD = ld + LINT = lint + LINTLIBFLAG = -C + LINTOPTS = -axz + LN = ln -s + MAKE = make + MV = mv + CP = cp + RANLIB = ranlib + RANLIBINSTFLAGS = + RM = rm -f + STD_INCLUDES = + STD_CPP_DEFINES = + STD_DEFINES = + EXTRA_LOAD_FLAGS = + EXTRA_LIBRARIES = + TAGS = ctags + + SHAREDCODEDEF = -DSHAREDCODE + SHLIBDEF = -DSUNSHLIB + + PROTO_DEFINES = + + INSTPGMFLAGS = + + INSTBINFLAGS = -m 0755 + INSTUIDFLAGS = -m 4755 + INSTLIBFLAGS = -m 0664 + INSTINCFLAGS = -m 0444 + INSTMANFLAGS = -m 0444 + INSTDATFLAGS = -m 0444 + INSTKMEMFLAGS = -m 4755 + + DESTDIR = + + TOP_INCLUDES = -I$(INCROOT) + + CDEBUGFLAGS = -O + CCOPTIONS = + COMPATFLAGS = + + ALLINCLUDES = $(STD_INCLUDES) $(TOP_INCLUDES) $(INCLUDES) $(EXTRA_INCLUDES) + ALLDEFINES = $(ALLINCLUDES) $(STD_DEFINES) $(PROTO_DEFINES) $(DEFINES) $(COMPATFLAGS) + CFLAGS = $(CDEBUGFLAGS) $(CCOPTIONS) $(ALLDEFINES) + LINTFLAGS = $(LINTOPTS) -DLINT $(ALLDEFINES) + LDLIBS = $(SYS_LIBRARIES) $(EXTRA_LIBRARIES) + LDOPTIONS = $(CDEBUGFLAGS) $(CCOPTIONS) + LDCOMBINEFLAGS = -X -r + + MACROFILE = sun.cf + RM_CMD = $(RM) *.CKP *.ln *.BAK *.bak *.o core errs ,* *~ *.a .emacs_* tags TAGS make.log MakeOut + + IMAKE_DEFINES = + + IRULESRC = $(CONFIGDIR) + IMAKE_CMD = $(IMAKE) -DUseInstalled -I$(IRULESRC) $(IMAKE_DEFINES) + + ICONFIGFILES = $(IRULESRC)/Imake.tmpl $(IRULESRC)/Imake.rules \ + $(IRULESRC)/Project.tmpl $(IRULESRC)/site.def \ + $(IRULESRC)/$(MACROFILE) $(EXTRA_ICONFIGFILES) + +########################################################################### +# X Window System Build Parameters +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + +########################################################################### +# X Window System make variables; this need to be coordinated with rules +# $XConsortium: Project.tmpl,v 1.63 89/12/18 16:46:44 jim Exp $ + + PATHSEP = / + USRLIBDIR = $(DESTDIR)/usr/lib + BINDIR = $(DESTDIR)/usr/bin/X11 + INCROOT = $(DESTDIR)/usr/include + BUILDINCROOT = $(TOP) + BUILDINCDIR = $(BUILDINCROOT)/X11 + BUILDINCTOP = .. + INCDIR = $(INCROOT)/X11 + ADMDIR = $(DESTDIR)/usr/adm + LIBDIR = $(USRLIBDIR)/X11 + CONFIGDIR = $(LIBDIR)/config + LINTLIBDIR = $(USRLIBDIR)/lint + + FONTDIR = $(LIBDIR)/fonts + XINITDIR = $(LIBDIR)/xinit + XDMDIR = $(LIBDIR)/xdm + AWMDIR = $(LIBDIR)/awm + TWMDIR = $(LIBDIR)/twm + GWMDIR = $(LIBDIR)/gwm + MANPATH = $(DESTDIR)/usr/man + MANSOURCEPATH = $(MANPATH)/man + MANDIR = $(MANSOURCEPATH)n + LIBMANDIR = $(MANSOURCEPATH)3 + XAPPLOADDIR = $(LIBDIR)/app-defaults + + SOXLIBREV = 4.2 + SOXTREV = 4.0 + SOXAWREV = 4.0 + SOOLDXREV = 4.0 + SOXMUREV = 4.0 + SOXEXTREV = 4.0 + + FONTCFLAGS = -t + + INSTAPPFLAGS = $(INSTDATFLAGS) + + IMAKE = imake + DEPEND = makedepend + RGB = rgb + FONTC = bdftosnf + MKFONTDIR = mkfontdir + MKDIRHIER = /bin/sh $(BINDIR)/mkdirhier + + CONFIGSRC = $(TOP)/config + CLIENTSRC = $(TOP)/clients + DEMOSRC = $(TOP)/demos + LIBSRC = $(TOP)/lib + FONTSRC = $(TOP)/fonts + INCLUDESRC = $(TOP)/X11 + SERVERSRC = $(TOP)/server + UTILSRC = $(TOP)/util + SCRIPTSRC = $(UTILSRC)/scripts + EXAMPLESRC = $(TOP)/examples + CONTRIBSRC = $(TOP)/../contrib + DOCSRC = $(TOP)/doc + RGBSRC = $(TOP)/rgb + DEPENDSRC = $(UTILSRC)/makedepend + IMAKESRC = $(CONFIGSRC) + XAUTHSRC = $(LIBSRC)/Xau + XLIBSRC = $(LIBSRC)/X + XMUSRC = $(LIBSRC)/Xmu + TOOLKITSRC = $(LIBSRC)/Xt + AWIDGETSRC = $(LIBSRC)/Xaw + OLDXLIBSRC = $(LIBSRC)/oldX + XDMCPLIBSRC = $(LIBSRC)/Xdmcp + BDFTOSNFSRC = $(FONTSRC)/bdftosnf + MKFONTDIRSRC = $(FONTSRC)/mkfontdir + EXTENSIONSRC = $(TOP)/extensions + + DEPEXTENSIONLIB = $(USRLIBDIR)/libXext.a + EXTENSIONLIB = -lXext + + DEPXLIB = $(DEPEXTENSIONLIB) + XLIB = $(EXTENSIONLIB) -lX11 + + DEPXAUTHLIB = $(USRLIBDIR)/libXau.a + XAUTHLIB = -lXau + + DEPXMULIB = + XMULIB = -lXmu + + DEPOLDXLIB = + OLDXLIB = -loldX + + DEPXTOOLLIB = + XTOOLLIB = -lXt + + DEPXAWLIB = + XAWLIB = -lXaw + + LINTEXTENSIONLIB = $(USRLIBDIR)/llib-lXext.ln + LINTXLIB = $(USRLIBDIR)/llib-lX11.ln + LINTXMU = $(USRLIBDIR)/llib-lXmu.ln + LINTXTOOL = $(USRLIBDIR)/llib-lXt.ln + LINTXAW = $(USRLIBDIR)/llib-lXaw.ln + + XWLIBSRC = $(CONTRIBSRC)/toolkits/Xw + DEPXWLIB = $(USRLIBDIR)/libXw.a + XWLIB = -lXw + + DEPLIBS = $(DEPXAWLIB) $(DEPXMULIB) $(DEPXTOOLLIB) $(DEPXLIB) + + DEPLIBS1 = $(DEPLIBS) + DEPLIBS2 = $(DEPLIBS) + DEPLIBS3 = $(DEPLIBS) + +########################################################################### +# Imake rules for building libraries, programs, scripts, and data files +# rules: $XConsortium: Imake.rules,v 1.67 89/12/18 17:14:15 jim Exp $ + +########################################################################### +# start of Imakefile + +# Imakefile file for liblw.a, Copyright (c) 1992-1993 Lucid, Inc. + + STD_DEFINES = + CDEBUGFLAGS = -O + EXT_DEFINES = -DTHIS_IS_X11R4 + WHICH_X = x11r4 + + LUCID_SRCS = lwlib-Xlw.c xlwmenu.c + LUCID_OBJS = lwlib-Xlw.o xlwmenu.o + MOTIF_SRCS = lwlib-Xm.c + MOTIF_OBJS = lwlib-Xm.o + OLIT_SRCS = lwlib-Xol.c lwlib-Xol-mb.c + OLIT_OBJS = lwlib-Xol.o lwlib-Xol-mb.o + +TOOLKIT_DEFINES = -DUSE_LUCID + TOOLKIT_SRCS = $(LUCID_SRC) + TOOLKIT_OBJS = $(LUCID_OBJS) + + SRCS = lwlib.c $(TOOLKIT_SRCS) lwlib-utils.c $(EXT_SRCS) + OBJS = lwlib.o $(TOOLKIT_OBJS) lwlib-utils.o $(EXT_OBJS) $(EZ_OBJS) + EXT_FLAGS = -I$(TOOLKITSRC) $(EXT_DEFINES) + LIBNAME = liblw.a + +.c.o: + $(RM) $@ + $(CC) -c $(CFLAGS) $*.c + +all:: liblw.a + +liblw.a: $(OBJS) + $(RM) $@ + $(AR) $@ $(OBJS) + $(RANLIB) $@ + +Makefiles:: + @case '${MFLAGS}' in *[ik]*) set +e;; esac; \ + for i in energize ;\ + do \ + echo "making Makefiles in $(CURRENT_DIR)/$$i..."; \ + case "$$i" in \ + ./?*/?*/?*/?*) newtop=../../../../ sub=subsubsubsub;; \ + ./?*/?*/?*) newtop=../../../ sub=subsubsub;; \ + ./?*/?*) newtop=../../ sub=subsub;; \ + ./?*) newtop=../ sub=sub;; \ + */?*/?*/?*) newtop=../../../../ sub=subsubsubsub;; \ + */?*/?*) newtop=../../../ sub=subsubsub;; \ + */?*) newtop=../../ sub=subsub;; \ + *) newtop=../ sub=sub;; \ + esac; \ + case "$(TOP)" in \ + /?*) newtop= upprefix= ;; \ + *) upprefix=../ ;; \ + esac; \ + $(MAKE) $${sub}dirMakefiles UPPREFIX=$$upprefix NEWTOP=$$newtop \ + MAKEFILE_SUBDIR=$$i NEW_CURRENT_DIR=$(CURRENT_DIR)/$$i;\ + done + +subdirMakefiles: + $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak + -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then \ + echo " $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak"; \ + $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \ + else exit 0; fi + cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \ + $(MAKE) $(MFLAGS) Makefiles + +subsubdirMakefiles: + $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak + -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then \ + echo " $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak"; \ + $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \ + else exit 0; fi + cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \ + $(MAKE) $(MFLAGS) Makefiles + +subsubsubdirMakefiles: + $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak + -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then \ + echo " $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak"; \ + $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \ + else exit 0; fi + cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(UPPREFIX)$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \ + $(MAKE) $(MFLAGS) Makefiles + +subsubsubsubdirMakefiles: + $(RM) $(MAKEFILE_SUBDIR)/Makefile.bak + -@if [ -f $(MAKEFILE_SUBDIR)/Makefile ]; then \ + echo " $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak"; \ + $(MV) $(MAKEFILE_SUBDIR)/Makefile $(MAKEFILE_SUBDIR)/Makefile.bak; \ + else exit 0; fi + cd $(MAKEFILE_SUBDIR); $(IMAKE_CMD) -DTOPDIR=$(UPPREFIX)$(UPPREFIX)$(UPPREFIX)$(UPPREFIX)$(TOP) -DCURDIR=$(NEW_CURRENT_DIR); \ + $(MAKE) $(MFLAGS) Makefiles + +depend:: + $(DEPEND) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(SRCS) + +CPPDEFS=-DCPP_PROGRAM=\"/lib/cpp\" + +lwlib.o: lwlib.c + $(RM) $@ + $(CC) -c $(CFLAGS) $(TOOLKIT_DEFINES) $*.c + +dispatch.o: dispatch.c + $(RM) $@ + $(CC) -c $(CFLAGS) $(EXT_FLAGS) $*.c + +xrdb-cpp.o: xrdb-cpp.c + $(RM) $@ + $(CC) -c $(CFLAGS) $(EXT_FLAGS) "$(CPPDEFS)" $*.c + +xrdb.o: xrdb.c + $(RM) $@ + $(CC) -c $(CFLAGS) $(EXT_FLAGS) $*.c + +lwlib-Xm.o: lwlib-Xm.c + $(RM) $@ + $(CC) -c $(CFLAGS) $(ENERGIZEP) $*.c + +lwlib-utils.o: lwlib-utils.h +lwlib.o: lwlib.h lwlib-internal.h +lwlib-Xlw.o: lwlib.h lwlib-internal.h +lwlib-Xm.o: lwlib.h lwlib-internal.h lwlib-utils.h +lwlib-Xol.o: lwlib.h lwlib-internal.h +lwlib-Xol-mb.o: lwlib-Xol-mb.h lwlib-Xol-mbP.h + +########################################################################### +# common rules for all Makefiles - do not edit + +emptyrule:: + +clean:: + $(RM_CMD) \#* + +Makefile:: + -@if [ -f Makefile ]; then \ + echo " $(RM) Makefile.bak; $(MV) Makefile Makefile.bak"; \ + $(RM) Makefile.bak; $(MV) Makefile Makefile.bak; \ + else exit 0; fi + $(IMAKE_CMD) -DTOPDIR=$(TOP) -DCURDIR=$(CURRENT_DIR) + +tags:: + $(TAGS) -w *.[ch] + $(TAGS) -xw *.[ch] > TAGS + +saber: + #load $(ALLDEFINES) $(SRCS) + +osaber: + #load $(ALLDEFINES) $(OBJS) + +########################################################################### +# empty rules for directories that do not have SUBDIRS - do not edit + +install:: + @echo "install in $(CURRENT_DIR) done" + +install.man:: + @echo "install.man in $(CURRENT_DIR) done" + +Makefiles:: + +includes:: + +########################################################################### +# dependencies generated by makedepend + diff --git a/lwlib/dispatch.c b/lwlib/dispatch.c new file mode 100644 index 00000000000..428512bc3d7 --- /dev/null +++ b/lwlib/dispatch.c @@ -0,0 +1,275 @@ +/* Defines a function to find the Widget that XtDispatchEvent() would use. + Copyright (C) 1992 Lucid, Inc. + +This file is part of the Lucid Widget Library. + +The Lucid Widget Library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +The Lucid Widget Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * The function XtWidgetToDispatchTo(), given an XEvent, returns the + * widget that XtDispatchEvent() would send that event to if called now. + * This file copies much code from the X11r4 Xt source, and is thus a + * portability problem. It also requires data structures defined in + * IntrinsicI.h, which is a non-exported Xt header file, so you can't + * compile this file unless you have the Xt sources online. + */ + +#include /* Don't change this: see comments in Imakefile. */ +#include +#include "dispatch.h" + +#ifdef THIS_IS_X11R4 + +#ifdef THIS_IS_X11R5 +ERROR!! only one of THIS_IS_X11R4 or THIS_IS_X11R5 must be defined. +#endif + +#else /* ! THIS_IS_X11R4 */ + +#ifndef THIS_IS_X11R5 +ERROR!! one of THIS_IS_X11R4 or THIS_IS_X11R5 must be defined. +#endif + +#endif /* ! THIS_IS_X11R4 */ + + +/* ## All of the code on this page was copied from the X11R5 lib/Xt/Event.c, + ## but is compatible with X11R4; the code in Event.c is different, but + ## functionally equivalent for our purposes. + */ + +#if __STDC__ +#define Const const +#else +#define Const /**/ +#endif + +#define NonMaskableMask ((EventMask)0x80000000L) + +#define COMP_EXPOSE (widget->core.widget_class->core_class.compress_exposure) +#define COMP_EXPOSE_TYPE (COMP_EXPOSE & 0x0f) +#define GRAPHICS_EXPOSE ((XtExposeGraphicsExpose & COMP_EXPOSE) || \ + (XtExposeGraphicsExposeMerged & COMP_EXPOSE)) +#define NO_EXPOSE (XtExposeNoExpose & COMP_EXPOSE) + + +/* -- lots of stuff we don't need to copy, omitted -- */ + + +static EventMask Const masks[] = { + 0, /* Error, should never see */ + 0, /* Reply, should never see */ + KeyPressMask, /* KeyPress */ + KeyReleaseMask, /* KeyRelease */ + ButtonPressMask, /* ButtonPress */ + ButtonReleaseMask, /* ButtonRelease */ + PointerMotionMask /* MotionNotify */ + | ButtonMotionMask, + EnterWindowMask, /* EnterNotify */ + LeaveWindowMask, /* LeaveNotify */ + FocusChangeMask, /* FocusIn */ + FocusChangeMask, /* FocusOut */ + KeymapStateMask, /* KeymapNotify */ + ExposureMask, /* Expose */ + NonMaskableMask, /* GraphicsExpose, in GC */ + NonMaskableMask, /* NoExpose, in GC */ + VisibilityChangeMask, /* VisibilityNotify */ + SubstructureNotifyMask, /* CreateNotify */ + StructureNotifyMask /* DestroyNotify */ + | SubstructureNotifyMask, + StructureNotifyMask /* UnmapNotify */ + | SubstructureNotifyMask, + StructureNotifyMask /* MapNotify */ + | SubstructureNotifyMask, + SubstructureRedirectMask, /* MapRequest */ + StructureNotifyMask /* ReparentNotify */ + | SubstructureNotifyMask, + StructureNotifyMask /* ConfigureNotify */ + | SubstructureNotifyMask, + SubstructureRedirectMask, /* ConfigureRequest */ + StructureNotifyMask /* GravityNotify */ + | SubstructureNotifyMask, + ResizeRedirectMask, /* ResizeRequest */ + StructureNotifyMask /* CirculateNotify */ + | SubstructureNotifyMask, + SubstructureRedirectMask, /* CirculateRequest */ + PropertyChangeMask, /* PropertyNotify */ + NonMaskableMask, /* SelectionClear */ + NonMaskableMask, /* SelectionRequest */ + NonMaskableMask, /* SelectionNotify */ + ColormapChangeMask, /* ColormapNotify */ + NonMaskableMask, /* ClientMessage */ + NonMaskableMask /* MappingNotify */ +}; + +#ifdef THIS_IS_X11R4 + +static /* in R5, this is not static, so we don't need to define it at all */ +EventMask _XtConvertTypeToMask (eventType) + int eventType; +{ + eventType &= 0x7f; /* Events sent with XSendEvent have high bit set. */ + if (eventType < XtNumber(masks)) + return masks[eventType]; + else + return 0; +} + +#endif /* R4 */ + +/* -- _XtOnGrabList() omitted -- */ + + +static Widget LookupSpringLoaded(grabList) + XtGrabList grabList; +{ + XtGrabList gl; + + for (gl = grabList; gl != NULL; gl = gl->next) { + if (gl->spring_loaded) + if (XtIsSensitive(gl->widget)) + return gl->widget; + else + return NULL; + if (gl->exclusive) break; + } + return NULL; +} + + + +/* This function is new. */ + +static Boolean WouldDispatchEvent(event, widget, mask, pd) + register XEvent *event; + Widget widget; + EventMask mask; + XtPerDisplay pd; +{ + XtEventRec *p; + Boolean would_dispatched = False; + + if ((mask == ExposureMask) || + ((event->type == NoExpose) && NO_EXPOSE) || + ((event->type == GraphicsExpose) && GRAPHICS_EXPOSE) ) + if (widget->core.widget_class->core_class.expose != NULL ) + return True; + + + if ((mask == VisibilityChangeMask) && + XtClass(widget)->core_class.visible_interest) + return True; + + for (p=widget->core.event_table; p != NULL; p = p->next) + if ((mask & p->mask) != 0 +#ifdef THIS_IS_X11R4 + || (mask == 0 && p->non_filter) +#endif + ) + return True; + + return False; +} + + +/* #### This function is mostly copied from DecideToDispatch(). + */ + +typedef enum _GrabType {pass, ignore, remap} GrabType; + +Widget +XtWidgetToDispatchTo (XEvent* event) +{ + register Widget widget; + EventMask mask; + GrabType grabType; + Widget dspWidget; + Time time = 0; + XtPerDisplay pd; + XtPerDisplayInput pdi; + XtGrabList grabList; + + widget = XtWindowToWidget (event->xany.display, event->xany.window); + pd = _XtGetPerDisplay(event->xany.display); + pdi = _XtGetPerDisplayInput(event->xany.display); + grabList = *_XtGetGrabList(pdi); + + mask = _XtConvertTypeToMask(event->xany.type); + grabType = pass; + switch (event->xany.type & 0x7f) { + case KeyPress: + case KeyRelease: grabType = remap; break; + case ButtonPress: + case ButtonRelease: grabType = remap; break; + case MotionNotify: grabType = ignore; +#define XKnownButtons (Button1MotionMask|Button2MotionMask|Button3MotionMask|\ + Button4MotionMask|Button5MotionMask) + mask |= (event->xmotion.state & XKnownButtons); +#undef XKnownButtons + break; + case EnterNotify: grabType = ignore; break; + } + + if (widget == NULL) { + if (grabType != remap) return False; + /* event occurred in a non-widget window, but we've promised also + to dispatch it to the nearest accessible spring_loaded widget */ + else if ((widget = LookupSpringLoaded(grabList)) != NULL) + return widget; + return False; + } + + switch(grabType) { + case pass: + return widget; + + case ignore: + if ((grabList == NULL || _XtOnGrabList(widget,grabList)) + && XtIsSensitive(widget)) { + return widget; + } + return NULL; + + case remap: + + { + Widget was_dispatched_to= NULL; + extern Widget _XtFindRemapWidget(); + extern void _XtUngrabBadGrabs(); + + dspWidget = _XtFindRemapWidget(event, widget, mask, pdi); + + if ((grabList == NULL || + _XtOnGrabList(dspWidget, grabList)) && + XtIsSensitive(dspWidget)) { + if (WouldDispatchEvent (event, dspWidget, mask, pd)) + was_dispatched_to = dspWidget; + } + + /* Also dispatch to nearest accessible spring_loaded. */ + /* Fetch this afterward to reflect modal list changes */ + grabList = *_XtGetGrabList(pdi); + widget = LookupSpringLoaded(grabList); + if (widget != NULL && widget != dspWidget) { + if (!was_dispatched_to) + was_dispatched_to = widget; + } + + return was_dispatched_to; + } + } + /* should never reach here */ + return NULL; +} diff --git a/lwlib/lwlib-Xlw.c b/lwlib/lwlib-Xlw.c new file mode 100644 index 00000000000..1c56b8eb3a3 --- /dev/null +++ b/lwlib/lwlib-Xlw.c @@ -0,0 +1,178 @@ +/* The lwlib interface to "xlwmenu" menus. + Copyright (C) 1992 Lucid, Inc. + +This file is part of the Lucid Widget Library. + +The Lucid Widget Library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +The Lucid Widget Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "lwlib-Xlw.h" +#include +#include +#include +#include +#include +#include "xlwmenu.h" + + /* Menu callbacks */ +static void +pre_hook (Widget w, XtPointer client_data, XtPointer call_data) +{ + widget_instance* instance = (widget_instance*)client_data; + widget_value* val; + + if (w->core.being_destroyed) + return; + + val = lw_get_widget_value_for_widget (instance, w); + if (instance->info->pre_activate_cb) + instance->info->pre_activate_cb (w, instance->info->id, + val ? val->call_data : NULL); +} + +static void +pick_hook (Widget w, XtPointer client_data, XtPointer call_data) +{ + widget_instance* instance = (widget_instance*)client_data; + widget_value* contents_val = (widget_value*)call_data; + widget_value* widget_val; + XtPointer widget_arg; + + if (w->core.being_destroyed) + return; + + if (instance->info->selection_cb && contents_val && contents_val->enabled + && !contents_val->contents) + instance->info->selection_cb (w, instance->info->id, + contents_val->call_data); + + widget_val = lw_get_widget_value_for_widget (instance, w); + widget_arg = widget_val ? widget_val->call_data : NULL; + if (instance->info->post_activate_cb) + instance->info->post_activate_cb (w, instance->info->id, widget_arg); + +} + + /* creation functions */ +static Widget +xlw_create_menubar (widget_instance* instance) +{ + Widget widget = + XtVaCreateWidget (instance->info->name, xlwMenuWidgetClass, + instance->parent, + XtNmenu, instance->info->val, + 0); + XtAddCallback (widget, XtNopen, pre_hook, (XtPointer)instance); + XtAddCallback (widget, XtNselect, pick_hook, (XtPointer)instance); + return widget; +} + +static Widget +xlw_create_popup_menu (widget_instance* instance) +{ + Widget popup_shell = + XtCreatePopupShell (instance->info->name, overrideShellWidgetClass, + instance->parent, NULL, 0); + + Widget widget = + XtVaCreateManagedWidget ("popup", xlwMenuWidgetClass, + popup_shell, + XtNmenu, instance->info->val, + XtNhorizontal, False, + 0); + + XtAddCallback (widget, XtNselect, pick_hook, (XtPointer)instance); + + return popup_shell; +} + +widget_creation_entry +xlw_creation_table [] = +{ + {"menubar", xlw_create_menubar}, + {"popup", xlw_create_popup_menu}, + {NULL, NULL} +}; + +Boolean +lw_lucid_widget_p (Widget widget) +{ + WidgetClass the_class = XtClass (widget); + if (the_class == xlwMenuWidgetClass) + return True; + if (the_class == overrideShellWidgetClass) + return + XtClass (((CompositeWidget)widget)->composite.children [0]) + == xlwMenuWidgetClass; + return False; +} + +void +xlw_update_one_widget (widget_instance* instance, Widget widget, + widget_value* val, Boolean deep_p) +{ + XlwMenuWidget mw; + + if (XtIsShell (widget)) + mw = (XlwMenuWidget)((CompositeWidget)widget)->composite.children [0]; + else + mw = (XlwMenuWidget)widget; + XtVaSetValues (widget, XtNmenu, val, 0); +} + +void +xlw_update_one_value (widget_instance* instance, Widget widget, + widget_value* val) +{ + return; +} + +void +xlw_pop_instance (widget_instance* instance, Boolean up) +{ +} + +void +xlw_popup_menu (Widget widget) +{ + XButtonPressedEvent dummy; + XlwMenuWidget mw; + + if (!XtIsShell (widget)) + return; + + mw = (XlwMenuWidget)((CompositeWidget)widget)->composite.children [0]; + + dummy.type = ButtonPress; + dummy.serial = 0; + dummy.send_event = 0; + dummy.display = XtDisplay (widget); + dummy.window = XtWindow (XtParent (widget)); + dummy.time = CurrentTime; + dummy.button = 0; + XQueryPointer (dummy.display, dummy.window, &dummy.root, + &dummy.subwindow, &dummy.x_root, &dummy.y_root, + &dummy.x, &dummy.y, &dummy.state); + + pop_up_menu (mw, &dummy); +} + + /* Destruction of instances */ +void +xlw_destroy_instance (widget_instance* instance) +{ + if (instance->widget) + XtDestroyWidget (instance->widget); +} + diff --git a/lwlib/lwlib-Xlw.h b/lwlib/lwlib-Xlw.h new file mode 100644 index 00000000000..33d83e88850 --- /dev/null +++ b/lwlib/lwlib-Xlw.h @@ -0,0 +1,29 @@ +#ifndef LWLIB_XLW_H +#define LWLIB_XLW_H + +#include "lwlib-internal.h" + +extern widget_creation_entry xlw_creation_table []; +extern widget_creation_function xlw_create_dialog; + +Boolean +lw_lucid_widget_p (Widget widget); + +void +xlw_update_one_widget (widget_instance* instance, Widget widget, + widget_value* val, Boolean deep_p); + +void +xlw_update_one_value (widget_instance* instance, Widget widget, + widget_value* val); + +void +xlw_destroy_instance (widget_instance* instance); + +void +xlw_pop_instance (widget_instance* instance, Boolean up); + +void +xlw_popup_menu (Widget widget); + +#endif /* LWLIB_XLW_H */ diff --git a/lwlib/lwlib-Xm.c b/lwlib/lwlib-Xm.c new file mode 100644 index 00000000000..13d28862697 --- /dev/null +++ b/lwlib/lwlib-Xm.c @@ -0,0 +1,1495 @@ +/* The lwlib interface to Motif widgets. + Copyright (C) 1992 Lucid, Inc. + +This file is part of the Lucid Widget Library. + +The Lucid Widget Library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +The Lucid Widget Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "lwlib-Xm.h" +#include "lwlib-utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void xm_pull_down_callback (Widget, XtPointer, XtPointer); +static void xm_internal_update_other_instances (Widget, XtPointer, + XtPointer); +static void xm_generic_callback (Widget, XtPointer, XtPointer); +static void xm_nosel_callback (Widget, XtPointer, XtPointer); +static void xm_pop_down_callback (Widget, XtPointer, XtPointer); + +static void +xm_update_menu (widget_instance* instance, Widget widget, widget_value* val, + Boolean deep_p); + + /* Structures to keep destroyed instances */ +typedef struct _destroyed_instance +{ + char* name; + char* type; + Widget widget; + Widget parent; + Boolean pop_up_p; + struct _destroyed_instance* next; +} destroyed_instance; + +static destroyed_instance* +all_destroyed_instances = NULL; + +static destroyed_instance* +make_destroyed_instance (char* name, char* type, Widget widget, Widget parent, + Boolean pop_up_p) +{ + destroyed_instance* instance = + (destroyed_instance*)malloc (sizeof (destroyed_instance)); + instance->name = strdup (name); + instance->type = strdup (type); + instance->widget = widget; + instance->parent = parent; + instance->pop_up_p = pop_up_p; + instance->next = NULL; + return instance; +} + +static void +free_destroyed_instance (destroyed_instance* instance) +{ + free (instance->name); + free (instance->type); + free (instance); +} + + /* motif utility functions */ +Widget +first_child (Widget widget) +{ + return ((CompositeWidget)widget)->composite.children [0]; +} + +Boolean +lw_motif_widget_p (Widget widget) +{ + return + XtClass (widget) == xmDialogShellWidgetClass + || XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget); +} + +static XmString +resource_motif_string (Widget widget, char* name) +{ + XtResource resource; + XmString result = 0; + + resource.resource_name = name; + resource.resource_class = XmCXmString; + resource.resource_type = XmRXmString; + resource.resource_size = sizeof (XmString); + resource.resource_offset = 0; + resource.default_type = XtRImmediate; + resource.default_addr = 0; + + XtGetSubresources (widget, (XtPointer)&result, "dialogString", + "DialogString", &resource, 1, NULL, 0); + return result; +} + +static void +destroy_all_children (Widget widget) +{ + Widget* children; + unsigned int number; + int i; + + children = XtCompositeChildren (widget, &number); + if (children) + { + /* Unmanage all children and destroy them. They will only be + * really destroyed when we get out of DispatchEvent. */ + for (i = 0; i < number; i++) + { + Widget child = children [i]; + if (!child->core.being_destroyed) + { + XtUnmanageChild (child); + XtDestroyWidget (child); + } + } + XtFree ((char *) children); + } +} + + /* update the label of anything subclass of a label */ +static void +xm_update_label (widget_instance* instance, Widget widget, widget_value* val) +{ + XmString res_string = 0; + XmString built_string = 0; + XmString key_string = 0; + Arg al [256]; + int ac; + + ac = 0; + + if (val->value) + { + res_string = resource_motif_string (widget, val->value); + + if (res_string) + { + XtSetArg (al [ac], XmNlabelString, res_string); ac++; + } + else + { + built_string = + XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET); + XtSetArg (al [ac], XmNlabelString, built_string); ac++; + } + XtSetArg (al [ac], XmNlabelType, XmSTRING); ac++; + } + + if (val->key) + { + key_string = XmStringCreateLtoR (val->key, XmSTRING_DEFAULT_CHARSET); + XtSetArg (al [ac], XmNacceleratorText, key_string); ac++; + } + + if (ac) + XtSetValues (widget, al, ac); + + if (built_string) + XmStringFree (built_string); + + if (key_string) + XmStringFree (key_string); +} + + /* update of list */ +static void +xm_update_list (widget_instance* instance, Widget widget, widget_value* val) +{ + widget_value* cur; + int i; + XtRemoveAllCallbacks (widget, XmNsingleSelectionCallback); + XtAddCallback (widget, XmNsingleSelectionCallback, xm_generic_callback, + instance); + for (cur = val->contents, i = 0; cur; cur = cur->next) + if (cur->value) + { + XmString xmstr = XmStringCreate (cur->value, XmSTRING_DEFAULT_CHARSET); + i += 1; + XmListAddItem (widget, xmstr, 0); + if (cur->selected) + XmListSelectPos (widget, i, False); + XmStringFree (xmstr); + } +} + + /* update of buttons */ +static void +xm_update_pushbutton (widget_instance* instance, Widget widget, + widget_value* val) +{ + XtVaSetValues (widget, XmNalignment, XmALIGNMENT_CENTER, 0); + XtRemoveAllCallbacks (widget, XmNactivateCallback); + XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); +} + +static void +xm_update_cascadebutton (widget_instance* instance, Widget widget, + widget_value* val) +{ + /* Should also rebuild the menu by calling ...update_menu... */ + XtRemoveAllCallbacks (widget, XmNcascadingCallback); + XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback, + instance); +} + + /* update toggle and radiobox */ +static void +xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val) +{ + XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); + XtAddCallback (widget, XmNvalueChangedCallback, + xm_internal_update_other_instances, instance); + XtVaSetValues (widget, XmNset, val->selected, + XmNalignment, XmALIGNMENT_BEGINNING, 0); +} + +static void +xm_update_radiobox (widget_instance* instance, Widget widget, + widget_value* val) +{ + Widget toggle; + widget_value* cur; + + /* update the callback */ + XtRemoveAllCallbacks (widget, XmNentryCallback); + XtAddCallback (widget, XmNentryCallback, xm_generic_callback, instance); + + /* first update all the toggles */ + /* Energize kernel interface is currently bad. It sets the selected widget + with the selected flag but returns it by its name. So we currently + have to support both setting the selection with the selected slot + of val contents and setting it with the "value" slot of val. The latter + has a higher priority. This to be removed when the kernel is fixed. */ + for (cur = val->contents; cur; cur = cur->next) + { + toggle = XtNameToWidget (widget, cur->value); + if (toggle) + { + XtVaSetValues (toggle, XmNsensitive, cur->enabled, 0); + if (!val->value && cur->selected) + XtVaSetValues (toggle, XmNset, cur->selected, 0); + if (val->value && strcmp (val->value, cur->value)) + XtVaSetValues (toggle, XmNset, False, 0); + } + } + + /* The selected was specified by the value slot */ + if (val->value) + { + toggle = XtNameToWidget (widget, val->value); + if (toggle) + XtVaSetValues (toggle, XmNset, True, 0); + } +} + + /* update a popup menu, pulldown menu or a menubar */ +static Boolean +all_dashes_p (char* s) +{ + char* t; + for (t = s; *t; t++) + if (*t != '-') + return False; + return True; +} + +static void +make_menu_in_widget (widget_instance* instance, Widget widget, + widget_value* val) +{ + Widget* children = 0; + int num_children; + int child_index; + widget_value* cur; + Widget button = 0; + Widget menu; + Arg al [256]; + int ac; + Boolean menubar_p; + + /* Allocate the children array */ + for (num_children = 0, cur = val; cur; num_children++, cur = cur->next); + children = (Widget*)XtMalloc (num_children * sizeof (Widget)); + + /* tricky way to know if this RowColumn is a menubar or a pulldown... */ + menubar_p = False; + XtSetArg (al[0], XmNisHomogeneous, &menubar_p); + XtGetValues (widget, al, 1); + + /* add the unmap callback for popups and pulldowns */ + /*** this sounds bogus ***/ + if (!menubar_p) + XtAddCallback (XtParent (widget), XmNpopdownCallback, + xm_pop_down_callback, (XtPointer)instance); + + for (child_index = 0, cur = val; cur; child_index++, cur = cur->next) + { + ac = 0; + XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++; + XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; + XtSetArg (al [ac], XmNuserData, cur->call_data); ac++; + + if (all_dashes_p (cur->name)) + { + button = XmCreateSeparator (widget, cur->name, NULL, 0); + } + else if (!cur->contents) + { + if (menubar_p) + button = XmCreateCascadeButton (widget, cur->name, al, ac); + else if (!cur->call_data) + button = XmCreateLabel (widget, cur->name, al, ac); + else + button = XmCreatePushButtonGadget (widget, cur->name, al, ac); + + xm_update_label (instance, button, cur); + + /* don't add a callback to a simple label */ + if (cur->call_data) + XtAddCallback (button, XmNactivateCallback, xm_generic_callback, + (XtPointer)instance); + } + else + { + menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0); + make_menu_in_widget (instance, menu, cur->contents); + XtSetArg (al [ac], XmNsubMenuId, menu); ac++; + button = XmCreateCascadeButton (widget, cur->name, al, ac); + + xm_update_label (instance, button, cur); + + XtAddCallback (button, XmNcascadingCallback, xm_pull_down_callback, + (XtPointer)instance); + } + + children [child_index] = button; + } + + XtManageChildren (children, num_children); + + /* Last entry is the help button. Has to be done after managing + * the buttons otherwise the menubar is only 4 pixels high... */ + if (button) + { + ac = 0; + XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++; + XtSetValues (widget, al, ac); + } + + XtFree ((char *) children); +} + +static void +update_one_menu_entry (widget_instance* instance, Widget widget, + widget_value* val, Boolean deep_p) +{ + Arg al [256]; + int ac; + Widget menu; + widget_value* contents; + + if (val->change == NO_CHANGE) + return; + + /* update the sensitivity and userdata */ + /* Common to all widget types */ + XtVaSetValues (widget, + XmNsensitive, val->enabled, + XmNuserData, val->call_data, + 0); + + /* update the menu button as a label. */ + if (val->change >= VISIBLE_CHANGE) + xm_update_label (instance, widget, val); + + /* update the pulldown/pullaside as needed */ + ac = 0; + menu = NULL; + XtSetArg (al [ac], XmNsubMenuId, &menu); ac++; + XtGetValues (widget, al, ac); + + contents = val->contents; + + if (!menu) + { + if (contents) + { + menu = XmCreatePulldownMenu (widget, "pulldown", NULL, 0); + make_menu_in_widget (instance, menu, contents); + ac = 0; + XtSetArg (al [ac], XmNsubMenuId, menu); ac++; + XtSetValues (widget, al, ac); + } + } + else if (!contents) + { + ac = 0; + XtSetArg (al [ac], XmNsubMenuId, NULL); ac++; + XtSetValues (widget, al, ac); + XtDestroyWidget (menu); + } + else if (deep_p && contents->change != NO_CHANGE) + xm_update_menu (instance, menu, val, 1); +} + +static void +xm_update_menu (widget_instance* instance, Widget widget, widget_value* val, + Boolean deep_p) +{ + /* Widget is a RowColumn widget whose contents have to be updated + * to reflect the list of items in val->contents */ + if (val->contents->change == STRUCTURAL_CHANGE) + { + destroy_all_children (widget); + make_menu_in_widget (instance, widget, val->contents); + } + else + { + /* Update all the buttons of the RowColumn in order. */ + Widget* children; + unsigned int num_children; + int i; + widget_value* cur; + + children = XtCompositeChildren (widget, &num_children); + if (children) + { + for (i = 0, cur = val->contents; i < num_children; i++) + { + if (!cur) + abort (); + if (children [i]->core.being_destroyed + || strcmp (XtName (children [i]), cur->name)) + continue; + update_one_menu_entry (instance, children [i], cur, deep_p); + cur = cur->next; + } + XtFree ((char *) children); + } + if (cur) + abort (); + } +} + + +/* update text widgets */ + +static void +xm_update_text (widget_instance* instance, Widget widget, widget_value* val) +{ + XmTextSetString (widget, val->value ? val->value : ""); + XtRemoveAllCallbacks (widget, XmNactivateCallback); + XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); + XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); + XtAddCallback (widget, XmNvalueChangedCallback, + xm_internal_update_other_instances, instance); +} + +static void +xm_update_text_field (widget_instance* instance, Widget widget, + widget_value* val) +{ + XmTextFieldSetString (widget, val->value ? val->value : ""); + XtRemoveAllCallbacks (widget, XmNactivateCallback); + XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); + XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); + XtAddCallback (widget, XmNvalueChangedCallback, + xm_internal_update_other_instances, instance); +} + + +/* update a motif widget */ + +void +xm_update_one_widget (widget_instance* instance, Widget widget, + widget_value* val, Boolean deep_p) +{ + WidgetClass class; + + /* Mark as not edited */ + val->edited = False; + + /* Common to all widget types */ + XtVaSetValues (widget, + XmNsensitive, val->enabled, + XmNuserData, val->call_data, + 0); + + /* Common to all label like widgets */ + if (XtIsSubclass (widget, xmLabelWidgetClass)) + xm_update_label (instance, widget, val); + + class = XtClass (widget); + /* Class specific things */ + if (class == xmPushButtonWidgetClass || + class == xmArrowButtonWidgetClass) + { + xm_update_pushbutton (instance, widget, val); + } + else if (class == xmCascadeButtonWidgetClass) + { + xm_update_cascadebutton (instance, widget, val); + } + else if (class == xmToggleButtonWidgetClass + || class == xmToggleButtonGadgetClass) + { + xm_update_toggle (instance, widget, val); + } + else if (class == xmRowColumnWidgetClass) + { + Boolean radiobox = 0; + int ac = 0; + Arg al [1]; + + XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++; + XtGetValues (widget, al, ac); + + if (radiobox) + xm_update_radiobox (instance, widget, val); + else + xm_update_menu (instance, widget, val, deep_p); + } + else if (class == xmTextWidgetClass) + { + xm_update_text (instance, widget, val); + } + else if (class == xmTextFieldWidgetClass) + { + xm_update_text_field (instance, widget, val); + } + else if (class == xmListWidgetClass) + { + xm_update_list (instance, widget, val); + } +} + + /* getting the value back */ +void +xm_update_one_value (widget_instance* instance, Widget widget, + widget_value* val) +{ + WidgetClass class = XtClass (widget); + widget_value *old_wv; + + /* copy the call_data slot into the "return" widget_value */ + for (old_wv = instance->info->val->contents; old_wv; old_wv = old_wv->next) + if (!strcmp (val->name, old_wv->name)) + { + val->call_data = old_wv->call_data; + break; + } + + if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass) + { + XtVaGetValues (widget, XmNset, &val->selected, 0); + val->edited = True; + } + else if (class == xmTextWidgetClass) + { + if (val->value) + free (val->value); + val->value = XmTextGetString (widget); + val->edited = True; + } + else if (class == xmTextFieldWidgetClass) + { + if (val->value) + free (val->value); + val->value = XmTextFieldGetString (widget); + val->edited = True; + } + else if (class == xmRowColumnWidgetClass) + { + Boolean radiobox = 0; + int ac = 0; + Arg al [1]; + + XtSetArg (al [ac], XmNradioBehavior, &radiobox); ac++; + XtGetValues (widget, al, ac); + + if (radiobox) + { + CompositeWidget radio = (CompositeWidget)widget; + int i; + for (i = 0; i < radio->composite.num_children; i++) + { + int set = False; + Widget toggle = radio->composite.children [i]; + + XtVaGetValues (toggle, XmNset, &set, 0); + if (set) + { + if (val->value) + free (val->value); + val->value = strdup (XtName (toggle)); + } + } + val->edited = True; + } + } + else if (class == xmListWidgetClass) + { + int pos_cnt; + int* pos_list; + if (XmListGetSelectedPos (widget, &pos_list, &pos_cnt)) + { + int i; + widget_value* cur; + for (cur = val->contents, i = 0; cur; cur = cur->next) + if (cur->value) + { + int j; + cur->selected = False; + i += 1; + for (j = 0; j < pos_cnt; j++) + if (pos_list [j] == i) + { + cur->selected = True; + val->value = strdup (cur->name); + } + } + val->edited = 1; + XtFree ((char *) pos_list); + } + } +} + + +/* This function is for activating a button from a program. It's wrong because + we pass a NULL argument in the call_data which is not Motif compatible. + This is used from the XmNdefaultAction callback of the List widgets to + have a dble-click put down a dialog box like the button woudl do. + I could not find a way to do that with accelerators. + */ +static void +activate_button (Widget widget, XtPointer closure, XtPointer call_data) +{ + Widget button = (Widget)closure; + XtCallCallbacks (button, XmNactivateCallback, NULL); +} + +/* creation functions */ + +/* dialogs */ +static Widget +make_dialog (char* name, Widget parent, Boolean pop_up_p, + char* shell_title, char* icon_name, Boolean text_input_slot, + Boolean radio_box, Boolean list, + int left_buttons, int right_buttons) +{ + Widget result; + Widget form; + Widget row; + Widget icon; + Widget icon_separator; + Widget message; + Widget value = 0; + Widget separator; + Widget button = 0; + Widget children [16]; /* for the final XtManageChildren */ + int n_children; + Arg al[64]; /* Arg List */ + int ac; /* Arg Count */ + int i; + + if (pop_up_p) + { + ac = 0; + XtSetArg(al[ac], XmNtitle, shell_title); ac++; + XtSetArg(al[ac], XtNallowShellResize, True); ac++; + XtSetArg(al[ac], XmNdeleteResponse, XmUNMAP); ac++; + result = XmCreateDialogShell (parent, "dialog", al, ac); + ac = 0; + XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++; +/* XtSetArg(al[ac], XmNautoUnmanage, TRUE); ac++; */ /* ####is this ok? */ + XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; + form = XmCreateForm (result, shell_title, al, ac); + } + else + { + ac = 0; + XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++; + XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; + form = XmCreateForm (parent, shell_title, al, ac); + result = form; + } + + ac = 0; + XtSetArg(al[ac], XmNpacking, XmPACK_COLUMN); ac++; + XtSetArg(al[ac], XmNorientation, XmVERTICAL); ac++; + XtSetArg(al[ac], XmNnumColumns, left_buttons + right_buttons + 1); ac++; + XtSetArg(al[ac], XmNmarginWidth, 0); ac++; + XtSetArg(al[ac], XmNmarginHeight, 0); ac++; + XtSetArg(al[ac], XmNspacing, 13); ac++; + XtSetArg(al[ac], XmNadjustLast, False); ac++; + XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++; + XtSetArg(al[ac], XmNisAligned, True); ac++; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNbottomOffset, 13); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNleftOffset, 13); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNrightOffset, 13); ac++; + row = XmCreateRowColumn (form, "row", al, ac); + + n_children = 0; + for (i = 0; i < left_buttons; i++) + { + char button_name [16]; + sprintf (button_name, "button%d", i + 1); + ac = 0; + if (i == 0) + { + XtSetArg(al[ac], XmNhighlightThickness, 1); ac++; + XtSetArg(al[ac], XmNshowAsDefault, TRUE); ac++; + } + XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; + children [n_children] = XmCreatePushButton (row, button_name, al, ac); + + if (i == 0) + { + button = children [n_children]; + ac = 0; + XtSetArg(al[ac], XmNdefaultButton, button); ac++; + XtSetValues (row, al, ac); + } + + n_children++; + } + + /* invisible seperator button */ + ac = 0; + XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++; + children [n_children] = XmCreateLabel (row, "separator_button", al, ac); + n_children++; + + for (i = 0; i < right_buttons; i++) + { + char button_name [16]; + sprintf (button_name, "button%d", left_buttons + i + 1); + ac = 0; + XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; + children [n_children] = XmCreatePushButton (row, button_name, al, ac); + if (! button) button = children [n_children]; + n_children++; + } + + XtManageChildren (children, n_children); + + ac = 0; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNbottomOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomWidget, row); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNleftOffset, 0); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNrightOffset, 0); ac++; + separator = XmCreateSeparator (form, "", al, ac); + + ac = 0; + XtSetArg(al[ac], XmNlabelType, XmPIXMAP); ac++; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNtopOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_NONE); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNleftOffset, 13); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++; + icon = XmCreateLabel (form, icon_name, al, ac); + + ac = 0; + XtSetArg(al[ac], XmNmappedWhenManaged, FALSE); ac++; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNtopOffset, 6); ac++; + XtSetArg(al[ac], XmNtopWidget, icon); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNbottomOffset, 6); ac++; + XtSetArg(al[ac], XmNbottomWidget, separator); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_NONE); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_NONE); ac++; + icon_separator = XmCreateLabel (form, "", al, ac); + + if (text_input_slot) + { + ac = 0; + XtSetArg(al[ac], XmNcolumns, 50); ac++; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNbottomOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomWidget, separator); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNleftOffset, 13); ac++; + XtSetArg(al[ac], XmNleftWidget, icon); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNrightOffset, 13); ac++; + value = XmCreateTextField (form, "value", al, ac); + } + else if (radio_box) + { + Widget radio_butt; + ac = 0; + XtSetArg(al[ac], XmNmarginWidth, 0); ac++; + XtSetArg(al[ac], XmNmarginHeight, 0); ac++; + XtSetArg(al[ac], XmNspacing, 13); ac++; + XtSetArg(al[ac], XmNalignment, XmALIGNMENT_CENTER); ac++; + XtSetArg(al[ac], XmNorientation, XmHORIZONTAL); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNbottomOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomWidget, separator); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNleftOffset, 13); ac++; + XtSetArg(al[ac], XmNleftWidget, icon); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNrightOffset, 13); ac++; + value = XmCreateRadioBox (form, "radiobutton1", al, ac); + ac = 0; + i = 0; + radio_butt = XmCreateToggleButtonGadget (value, "radio1", al, ac); + children [i++] = radio_butt; + radio_butt = XmCreateToggleButtonGadget (value, "radio2", al, ac); + children [i++] = radio_butt; + radio_butt = XmCreateToggleButtonGadget (value, "radio3", al, ac); + children [i++] = radio_butt; + XtManageChildren (children, i); + } + else if (list) + { + ac = 0; + XtSetArg(al[ac], XmNvisibleItemCount, 5); ac++; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_NONE); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNbottomOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomWidget, separator); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNleftOffset, 13); ac++; + XtSetArg(al[ac], XmNleftWidget, icon); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNrightOffset, 13); ac++; + value = XmCreateScrolledList (form, "list", al, ac); + + /* this is the easiest way I found to have the dble click in the + list activate the default button */ + XtAddCallback (value, XmNdefaultActionCallback, activate_button, button); + } + + ac = 0; + XtSetArg(al[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; + XtSetArg(al[ac], XmNtopAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNtopOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNbottomOffset, 13); ac++; + XtSetArg(al[ac], XmNbottomWidget, + text_input_slot || radio_box || list ? value : separator); ac++; + XtSetArg(al[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++; + XtSetArg(al[ac], XmNleftOffset, 13); ac++; + XtSetArg(al[ac], XmNleftWidget, icon); ac++; + XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++; + XtSetArg(al[ac], XmNrightOffset, 13); ac++; + message = XmCreateLabel (form, "message", al, ac); + + if (list) + XtManageChild (value); + + i = 0; + children [i] = row; i++; + children [i] = separator; i++; + if (text_input_slot || radio_box) + { + children [i] = value; i++; + } + children [i] = message; i++; + children [i] = icon; i++; + children [i] = icon_separator; i++; + XtManageChildren (children, i); + + if (text_input_slot || list) + { + XtInstallAccelerators (value, button); + XtSetKeyboardFocus (result, value); + } + else + { + XtInstallAccelerators (form, button); + XtSetKeyboardFocus (result, button); + } + + return result; +} + +static destroyed_instance* +find_matching_instance (widget_instance* instance) +{ + destroyed_instance* cur; + destroyed_instance* prev; + char* type = instance->info->type; + char* name = instance->info->name; + + for (prev = NULL, cur = all_destroyed_instances; + cur; + prev = cur, cur = cur->next) + { + if (!strcmp (cur->name, name) + && !strcmp (cur->type, type) + && cur->parent == instance->parent + && cur->pop_up_p == instance->pop_up_p) + { + if (prev) + prev->next = cur->next; + else + all_destroyed_instances = cur->next; + return cur; + } + /* do some cleanup */ + else if (!cur->widget) + { + if (prev) + prev->next = cur->next; + else + all_destroyed_instances = cur->next; + free_destroyed_instance (cur); + cur = prev ? prev : all_destroyed_instances; + } + } + return NULL; +} + +static void +mark_dead_instance_destroyed (Widget widget, XtPointer closure, + XtPointer call_data) +{ + destroyed_instance* instance = (destroyed_instance*)closure; + instance->widget = NULL; +} + +static void +recenter_widget (Widget widget) +{ + Widget parent = XtParent (widget); + Screen* screen = XtScreen (widget); + Dimension screen_width = WidthOfScreen (screen); + Dimension screen_height = HeightOfScreen (screen); + Dimension parent_width = 0; + Dimension parent_height = 0; + Dimension child_width = 0; + Dimension child_height = 0; + Position x; + Position y; + + XtVaGetValues (widget, XtNwidth, &child_width, XtNheight, &child_height, 0); + XtVaGetValues (parent, XtNwidth, &parent_width, XtNheight, &parent_height, + 0); + + x = (((Position)parent_width) - ((Position)child_width)) / 2; + y = (((Position)parent_height) - ((Position)child_height)) / 2; + + XtTranslateCoords (parent, x, y, &x, &y); + + if (x + child_width > screen_width) + x = screen_width - child_width; + if (x < 0) + x = 0; + + if (y + child_height > screen_height) + y = screen_height - child_height; + if (y < 0) + y = 0; + + XtVaSetValues (widget, XtNx, x, XtNy, y, 0); +} + +static Widget +recycle_instance (destroyed_instance* instance) +{ + Widget widget = instance->widget; + + /* widget is NULL if the parent was destroyed. */ + if (widget) + { + Widget focus; + Widget separator; + + /* Remove the destroy callback as the instance is not in the list + anymore */ + XtRemoveCallback (instance->parent, XtNdestroyCallback, + mark_dead_instance_destroyed, + (XtPointer)instance); + + /* Give the focus to the initial item */ + focus = XtNameToWidget (widget, "*value"); + if (!focus) + focus = XtNameToWidget (widget, "*button1"); + if (focus) + XtSetKeyboardFocus (widget, focus); + + /* shrink the separator label back to their original size */ + separator = XtNameToWidget (widget, "*separator_button"); + if (separator) + XtVaSetValues (separator, XtNwidth, 5, XtNheight, 5, 0); + + /* Center the dialog in its parent */ + recenter_widget (widget); + } + free_destroyed_instance (instance); + return widget; +} + +Widget +xm_create_dialog (widget_instance* instance) +{ + char* name = instance->info->type; + Widget parent = instance->parent; + Widget widget; + Boolean pop_up_p = instance->pop_up_p; + char* shell_name = 0; + char* icon_name; + Boolean text_input_slot = False; + Boolean radio_box = False; + Boolean list = False; + int total_buttons; + int left_buttons = 0; + int right_buttons = 1; + destroyed_instance* dead_one; + + /* try to find a widget to recycle */ + dead_one = find_matching_instance (instance); + if (dead_one) + { + Widget recycled_widget = recycle_instance (dead_one); + if (recycled_widget) + return recycled_widget; + } + + switch (name [0]){ + case 'E': case 'e': + icon_name = "dbox-error"; + shell_name = "Error"; + break; + + case 'I': case 'i': + icon_name = "dbox-info"; + shell_name = "Information"; + break; + + case 'L': case 'l': + list = True; + icon_name = "dbox-question"; + shell_name = "Prompt"; + break; + + case 'P': case 'p': + text_input_slot = True; + icon_name = "dbox-question"; + shell_name = "Prompt"; + break; + + case 'Q': case 'q': + icon_name = "dbox-question"; + shell_name = "Question"; + break; + } + + total_buttons = name [1] - '0'; + + if (name [3] == 'T' || name [3] == 't') + { + text_input_slot = False; + radio_box = True; + } + else if (name [3]) + right_buttons = name [4] - '0'; + + left_buttons = total_buttons - right_buttons; + + widget = make_dialog (name, parent, pop_up_p, + shell_name, icon_name, text_input_slot, radio_box, + list, left_buttons, right_buttons); + + XtAddCallback (widget, XmNpopdownCallback, xm_nosel_callback, + (XtPointer) instance); + return widget; +} + +static Widget +make_menubar (widget_instance* instance) +{ + return XmCreateMenuBar (instance->parent, instance->info->name, NULL, 0); +} + +static void +remove_grabs (Widget shell, XtPointer closure, XtPointer call_data) +{ + XmRowColumnWidget menu = (XmRowColumnWidget) closure; + XmRemoveFromPostFromList (menu, XtParent (XtParent ((Widget) menu))); +} + +static Widget +make_popup_menu (widget_instance* instance) +{ + Widget parent = instance->parent; + Window parent_window = parent->core.window; + Widget result; + + /* sets the parent window to 0 to fool Motif into not generating a grab */ + parent->core.window = 0; + result = XmCreatePopupMenu (parent, instance->info->name, NULL, 0); + XtAddCallback (XtParent (result), XmNpopdownCallback, remove_grabs, + (XtPointer)result); + parent->core.window = parent_window; + return result; +} + + /* Table of functions to create widgets */ + +#ifdef ENERGIZE + +/* interface with the XDesigner generated functions */ +typedef Widget (*widget_maker) (Widget); +extern Widget create_project_p_sheet (Widget parent); +extern Widget create_debugger_p_sheet (Widget parent); +extern Widget create_breaklist_p_sheet (Widget parent); +extern Widget create_le_browser_p_sheet (Widget parent); +extern Widget create_class_browser_p_sheet (Widget parent); +extern Widget create_call_browser_p_sheet (Widget parent); +extern Widget create_build_dialog (Widget parent); +extern Widget create_editmode_dialog (Widget parent); +extern Widget create_search_dialog (Widget parent); +extern Widget create_project_display_dialog (Widget parent); + +static Widget +make_one (widget_instance* instance, widget_maker fn) +{ + Widget result; + Arg al [64]; + int ac = 0; + + if (instance->pop_up_p) + { + XtSetArg (al [ac], XmNallowShellResize, TRUE); ac++; + result = XmCreateDialogShell (instance->parent, "dialog", NULL, 0); + XtAddCallback (result, XmNpopdownCallback, &xm_nosel_callback, + (XtPointer) instance); + (*fn) (result); + } + else + { + result = (*fn) (instance->parent); + XtRealizeWidget (result); + } + return result; +} + +static Widget +make_project_p_sheet (widget_instance* instance) +{ + return make_one (instance, create_project_p_sheet); +} + +static Widget +make_debugger_p_sheet (widget_instance* instance) +{ + return make_one (instance, create_debugger_p_sheet); +} + +static Widget +make_breaklist_p_sheet (widget_instance* instance) +{ + return make_one (instance, create_breaklist_p_sheet); +} + +static Widget +make_le_browser_p_sheet (widget_instance* instance) +{ + return make_one (instance, create_le_browser_p_sheet); +} + +static Widget +make_class_browser_p_sheet (widget_instance* instance) +{ + return make_one (instance, create_class_browser_p_sheet); +} + +static Widget +make_call_browser_p_sheet (widget_instance* instance) +{ + return make_one (instance, create_call_browser_p_sheet); +} + +static Widget +make_build_dialog (widget_instance* instance) +{ + return make_one (instance, create_build_dialog); +} + +static Widget +make_editmode_dialog (widget_instance* instance) +{ + return make_one (instance, create_editmode_dialog); +} + +static Widget +make_search_dialog (widget_instance* instance) +{ + return make_one (instance, create_search_dialog); +} + +static Widget +make_project_display_dialog (widget_instance* instance) +{ + return make_one (instance, create_project_display_dialog); +} + +#endif /* ENERGIZE */ + +widget_creation_entry +xm_creation_table [] = +{ + {"menubar", make_menubar}, + {"popup", make_popup_menu}, +#ifdef ENERGIZE + {"project_p_sheet", make_project_p_sheet}, + {"debugger_p_sheet", make_debugger_p_sheet}, + {"breaklist_psheet", make_breaklist_p_sheet}, + {"leb_psheet", make_le_browser_p_sheet}, + {"class_browser_psheet", make_class_browser_p_sheet}, + {"ctree_browser_psheet", make_call_browser_p_sheet}, + {"build", make_build_dialog}, + {"editmode", make_editmode_dialog}, + {"search", make_search_dialog}, + {"project_display", make_project_display_dialog}, +#endif /* ENERGIZE */ + {NULL, NULL} +}; + + /* Destruction of instances */ +void +xm_destroy_instance (widget_instance* instance) +{ + Widget widget = instance->widget; + /* recycle the dialog boxes */ + /* Disable the recycling until we can find a way to have the dialog box + get reasonable layout after we modify its contents. */ + if (0 + && XtClass (widget) == xmDialogShellWidgetClass) + { + destroyed_instance* dead_instance = + make_destroyed_instance (instance->info->name, + instance->info->type, + instance->widget, + instance->parent, + instance->pop_up_p); + dead_instance->next = all_destroyed_instances; + all_destroyed_instances = dead_instance; + XtUnmanageChild (first_child (instance->widget)); + XFlush (XtDisplay (instance->widget)); + XtAddCallback (instance->parent, XtNdestroyCallback, + mark_dead_instance_destroyed, (XtPointer)dead_instance); + } + else + { + /* This might not be necessary now that the nosel is attached to + popdown instead of destroy, but it can't hurt. */ + XtRemoveCallback (instance->widget, XtNdestroyCallback, + xm_nosel_callback, (XtPointer)instance); + XtDestroyWidget (instance->widget); + } +} + + /* popup utility */ +void +xm_popup_menu (Widget widget) +{ + XButtonPressedEvent dummy; + XEvent* event; + + dummy.type = ButtonPress; + dummy.serial = 0; + dummy.send_event = 0; + dummy.display = XtDisplay (widget); + dummy.window = XtWindow (XtParent (widget)); + dummy.time = 0; + dummy.button = 0; + XQueryPointer (dummy.display, dummy.window, &dummy.root, + &dummy.subwindow, &dummy.x_root, &dummy.y_root, + &dummy.x, &dummy.y, &dummy.state); + event = (XEvent *) &dummy; + + if (event->type == ButtonPress || event->type == ButtonRelease) + { + /* This is so totally ridiculous: there's NO WAY to tell Motif + that *any* button can select a menu item. Only one button + can have that honor. + */ + char *trans = 0; + if (event->xbutton.state & Button5Mask) trans = ""; + else if (event->xbutton.state & Button4Mask) trans = ""; + else if (event->xbutton.state & Button3Mask) trans = ""; + else if (event->xbutton.state & Button2Mask) trans = ""; + else if (event->xbutton.state & Button1Mask) trans = ""; + if (trans) XtVaSetValues (widget, XmNmenuPost, trans, 0); + XmMenuPosition (widget, (XButtonPressedEvent *) event); + } + XtManageChild (widget); +} + +static void +set_min_dialog_size (Widget w) +{ + short width; + short height; + XtVaGetValues (w, XmNwidth, &width, XmNheight, &height, 0); + XtVaSetValues (w, XmNminWidth, width, XmNminHeight, height, 0); +} + +void +xm_pop_instance (widget_instance* instance, Boolean up) +{ + Widget widget = instance->widget; + + if (XtClass (widget) == xmDialogShellWidgetClass) + { + Widget widget_to_manage = first_child (widget); + if (up) + { + XtManageChild (widget_to_manage); + set_min_dialog_size (widget); + XtSetKeyboardFocus (instance->parent, widget); + } + else + XtUnmanageChild (widget_to_manage); + } + else + { + if (up) + XtManageChild (widget); + else + XtUnmanageChild (widget); + } +} + + +/* motif callback */ + +enum do_call_type { pre_activate, selection, no_selection, post_activate }; + +static void +do_call (Widget widget, XtPointer closure, enum do_call_type type) +{ + Arg al [256]; + int ac; + XtPointer user_data; + widget_instance* instance = (widget_instance*)closure; + Widget instance_widget; + LWLIB_ID id; + + if (!instance) + return; + if (widget->core.being_destroyed) + return; + + instance_widget = instance->widget; + if (!instance_widget) + return; + + id = instance->info->id; + ac = 0; + user_data = NULL; + XtSetArg (al [ac], XmNuserData, &user_data); ac++; + XtGetValues (widget, al, ac); + switch (type) + { + case pre_activate: + if (instance->info->pre_activate_cb) + instance->info->pre_activate_cb (widget, id, user_data); + break; + case selection: + if (instance->info->selection_cb) + instance->info->selection_cb (widget, id, user_data); + break; + case no_selection: + if (instance->info->selection_cb) + instance->info->selection_cb (widget, id, (XtPointer) -1); + break; + case post_activate: + if (instance->info->post_activate_cb) + instance->info->post_activate_cb (widget, id, user_data); + break; + default: + abort (); + } +} + +/* Like lw_internal_update_other_instances except that it does not do + anything if its shell parent is not managed. This is to protect + lw_internal_update_other_instances to dereference freed memory + if the widget was ``destroyed'' by caching it in the all_destroyed_instances + list */ +static void +xm_internal_update_other_instances (Widget widget, XtPointer closure, + XtPointer call_data) +{ + Widget parent; + for (parent = widget; parent; parent = XtParent (parent)) + if (XtIsShell (parent)) + break; + else if (!XtIsManaged (parent)) + return; + lw_internal_update_other_instances (widget, closure, call_data); +} + +static void +xm_generic_callback (Widget widget, XtPointer closure, XtPointer call_data) +{ + lw_internal_update_other_instances (widget, closure, call_data); + do_call (widget, closure, selection); +} + +static void +xm_nosel_callback (Widget widget, XtPointer closure, XtPointer call_data) +{ + /* This callback is only called when a dialog box is dismissed with the wm's + destroy button (WM_DELETE_WINDOW.) We want the dialog box to be destroyed + in that case, not just unmapped, so that it releases its keyboard grabs. + But there are problems with running our callbacks while the widget is in + the process of being destroyed, so we set XmNdeleteResponse to XmUNMAP + instead of XmDESTROY and then destroy it ourself after having run the + callback. + */ + do_call (widget, closure, no_selection); + XtDestroyWidget (widget); +} + +static void +xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data) +{ + do_call (widget, closure, pre_activate); +} + +static void +xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data) +{ + do_call (widget, closure, post_activate); +} + + +/* set the keyboard focus */ +void +xm_set_keyboard_focus (Widget parent, Widget w) +{ + XmProcessTraversal (w, 0); + XtSetKeyboardFocus (parent, w); +} diff --git a/lwlib/lwlib-Xm.h b/lwlib/lwlib-Xm.h new file mode 100644 index 00000000000..e5568effcf9 --- /dev/null +++ b/lwlib/lwlib-Xm.h @@ -0,0 +1,34 @@ +#ifndef LWLIB_XM_H +#define LWLIB_XM_H + +#include "lwlib-internal.h" + +extern widget_creation_entry xm_creation_table []; + +Widget +xm_create_dialog (widget_instance* instance); + +Boolean +lw_motif_widget_p (Widget widget); + +void +xm_update_one_widget (widget_instance* instance, Widget widget, + widget_value* val, Boolean deep_p); + +void +xm_update_one_value (widget_instance* instance, Widget widget, + widget_value* val); + +void +xm_destroy_instance (widget_instance* instance); + +void +xm_set_keyboard_focus (Widget parent, Widget w); + +void +xm_popup_menu (Widget widget); + +void +xm_pop_instance (widget_instance* instance, Boolean up); + +#endif /* LWLIB_XM_H */ diff --git a/lwlib/lwlib-Xol.h b/lwlib/lwlib-Xol.h new file mode 100644 index 00000000000..ee4d6481cb3 --- /dev/null +++ b/lwlib/lwlib-Xol.h @@ -0,0 +1,29 @@ +#ifndef LWLIB_XOL_H +#define LWLIB_XOL_H + +#include "lwlib-internal.h" + +extern widget_creation_entry xol_creation_table []; +extern Widget xol_create_dialog (widget_instance *); + +Boolean +lw_olit_widget_p (Widget widget); + +void +xol_update_one_widget (widget_instance* instance, Widget widget, + widget_value* val, Boolean deep_p); + +void +xol_update_one_value (widget_instance* instance, Widget widget, + widget_value* val); + +void +xol_destroy_instance (widget_instance* instance); + +void +xol_pop_instance (widget_instance* instance, Boolean up); + +void +xol_popup_menu (Widget widget); + +#endif /* LWLIB_XOL_H */ diff --git a/lwlib/lwlib-int.h b/lwlib/lwlib-int.h new file mode 100644 index 00000000000..0f2c594e7de --- /dev/null +++ b/lwlib/lwlib-int.h @@ -0,0 +1,54 @@ +#ifndef LWLIB_INTERNAL_H +#define LWLIB_INTERNAL_H + +#include "lwlib.h" + +/* +extern char *strdup (const char *); +extern int strcasecmp (const char *, const char *); +*/ + +typedef struct _widget_instance +{ + Widget widget; + Widget parent; + Boolean pop_up_p; + struct _widget_info* info; + struct _widget_instance* next; +} widget_instance; + +typedef struct _widget_info +{ + char* type; + char* name; + LWLIB_ID id; + widget_value* val; + Boolean busy; + lw_callback pre_activate_cb; + lw_callback selection_cb; + lw_callback post_activate_cb; + struct _widget_instance* instances; + struct _widget_info* next; +} widget_info; + +typedef Widget +(*widget_creation_function) (widget_instance* instance); + +typedef struct _widget_creation_entry +{ + char* type; + widget_creation_function function; +} widget_creation_entry; + +/* update all other instances of a widget. Can be used in a callback when + a wiget has been used by the user */ +void +lw_internal_update_other_instances (Widget widget, XtPointer closure, + XtPointer call_data); + +/* get the widget_value for a widget in a given instance */ +widget_value* +lw_get_widget_value_for_widget (widget_instance* instance, Widget w); + +#endif /* LWLIB_INTERNAL_H */ + diff --git a/lwlib/lwlib-utils.c b/lwlib/lwlib-utils.c new file mode 100644 index 00000000000..97952afce55 --- /dev/null +++ b/lwlib/lwlib-utils.c @@ -0,0 +1,162 @@ +/* Defines some widget utility functions. + Copyright (C) 1992 Lucid, Inc. + +This file is part of the Lucid Widget Library. + +The Lucid Widget Library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 1, or (at your option) +any later version. + +The Lucid Widget Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include +#include +#include +#include + +#include +#include +#include +#include "lwlib-utils.h" + +/* Redisplay the contents of the widget, without first clearing it. */ +void +XtNoClearRefreshWidget (Widget widget) +{ + XEvent event; + + event.type = Expose; + event.xexpose.serial = 0; + event.xexpose.send_event = 0; + event.xexpose.display = XtDisplay (widget); + event.xexpose.window = XtWindow (widget); + event.xexpose.x = 0; + event.xexpose.y = 0; + event.xexpose.width = widget->core.width; + event.xexpose.height = widget->core.height; + event.xexpose.count = 0; + + (*widget->core.widget_class->core_class.expose) + (widget, &event, (Region)NULL); +} + + +/* + * Apply a function to all the subwidgets of a given widget recursively. +*/ +void +XtApplyToWidgets (Widget w, XtApplyToWidgetsProc proc, XtPointer arg) +{ + if (XtIsComposite (w)) + { + CompositeWidget cw = (CompositeWidget) w; + /* We have to copy the children list before mapping over it, because + the procedure might add/delete elements, which would lose badly. + */ + int nkids = cw->composite.num_children; + Widget *kids = (Widget *) malloc (sizeof (Widget) * nkids); + int i; + memcpy (kids, cw->composite.children, sizeof (Widget) * nkids); + for (i = 0; i < nkids; i++) +/* This prevent us from using gadgets, why is it here? */ +/* if (XtIsWidget (kids [i])) */ + { + /* do the kiddies first in case we're destroying */ + XtApplyToWidgets (kids [i], proc, arg); + proc (kids [i], arg); + } + free (kids); + } +} + + +/* + * Apply a function to all the subwidgets of a given widget recursively. + * Stop as soon as the function returns non NULL and returns this as a value. + */ +void * +XtApplyUntilToWidgets (Widget w, XtApplyUntilToWidgetsProc proc, XtPointer arg) +{ + void* result; + if (XtIsComposite (w)) + { + CompositeWidget cw = (CompositeWidget)w; + int i; + for (i = 0; i < cw->composite.num_children; i++) + if (XtIsWidget (cw->composite.children [i])){ + result = proc (cw->composite.children [i], arg); + if (result) + return result; + result = XtApplyUntilToWidgets (cw->composite.children [i], proc, + arg); + if (result) + return result; + } + } + return NULL; +} + + +/* + * Returns a copy of the list of all children of a composite widget + */ +Widget * +XtCompositeChildren (Widget widget, unsigned int* number) +{ + CompositeWidget cw = (CompositeWidget)widget; + Widget* result; + int n; + int i; + + if (!XtIsComposite (widget)) + { + *number = 0; + return NULL; + } + n = cw->composite.num_children; + result = (Widget*)XtMalloc (n * sizeof (Widget)); + *number = n; + for (i = 0; i < n; i++) + result [i] = cw->composite.children [i]; + return result; +} + +Boolean +XtWidgetBeingDestroyedP (Widget widget) +{ + return widget->core.being_destroyed; +} + +void +XtSafelyDestroyWidget (Widget widget) +{ +#if 0 + + /* this requires IntrinsicI.h (actually, InitialI.h) */ + + XtAppContext app = XtWidgetToApplicationContext(widget); + + if (app->dispatch_level == 0) + { + app->dispatch_level = 1; + XtDestroyWidget (widget); + /* generates an event so that the event loop will be called */ + XChangeProperty (XtDisplay (widget), XtWindow (widget), + XA_STRING, XA_STRING, 32, PropModeAppend, NULL, 0); + app->dispatch_level = 0; + } + else + XtDestroyWidget (widget); + +#else + abort (); +#endif +} diff --git a/lwlib/lwlib-utils.h b/lwlib/lwlib-utils.h new file mode 100644 index 00000000000..c5991f2d5e4 --- /dev/null +++ b/lwlib/lwlib-utils.h @@ -0,0 +1,20 @@ +#ifndef _LWLIB_UTILS_H_ +#define _LWLIB_UTILS_H_ + +void XtNoClearRefreshWidget (Widget); + +typedef void (*XtApplyToWidgetsProc) (Widget, XtPointer); +typedef void* (*XtApplyUntilToWidgetsProc) (Widget, XtPointer); + +void XtApplyToWidgets (Widget, XtApplyToWidgetsProc, XtPointer); +void *XtApplyUntilToWidgets (Widget, XtApplyUntilToWidgetsProc, XtPointer); + +Widget *XtCompositeChildren (Widget, unsigned int *); + +/* returns True is the widget is being destroyed, False otherwise */ +Boolean +XtWidgetBeingDestroyedP (Widget widget); + +void XtSafelyDestroyWidget (Widget); + +#endif /* _LWLIB_UTILS_H_ */ diff --git a/lwlib/lwlib.c b/lwlib/lwlib.c new file mode 100644 index 00000000000..20f7682cfd4 --- /dev/null +++ b/lwlib/lwlib.c @@ -0,0 +1,1111 @@ +/* A general interface to the widgets of different toolkits. + Copyright (C) 1992, 1993 Lucid, Inc. + +This file is part of the Lucid Widget Library. + +The Lucid Widget Library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The Lucid Widget Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef NeXT +#undef __STRICT_BSD__ /* ick */ +#endif + +#include +#include +#include +#include +#include +#include +#include "lwlib-internal.h" +#include "lwlib-utils.h" + +#if defined(__GNUC__) && !defined(alloca) +#define alloca __builtin_alloca +#endif + +#if ((!__GNUC__) && !defined(__hpux)) && !defined(AIXV3) +#include +#endif + +#if defined(AIXV3) +#pragma alloca +#endif + +#if defined (USE_LUCID) +#include "lwlib-Xlw.h" +#endif +#if defined (USE_MOTIF) +#include "lwlib-Xm.h" +#endif +#if defined (USE_OLIT) +#include "lwlib-Xol.h" +#endif + +#if !defined (USE_LUCID) && !defined (USE_MOTIF) && !defined (USE_OLIT) +ERROR! At least one of USE_LUCID, USE_MOTIF or USE_OLIT must be defined. +#endif + +#if defined (USE_MOTIF) && defined (USE_OLIT) +ERROR! no more than one of USE_MOTIF and USE_OLIT may be defined. +#endif + +/* List of all widgets managed by the library. */ +static widget_info* +all_widget_info = NULL; + + /* Forward declarations */ +static void +instanciate_widget_instance (widget_instance* instance); + + /* utility functions for widget_instance and widget_info */ +static char * +safe_strdup (char* s) +{ + char *result; + if (! s) return 0; + result = (char *) malloc (strlen (s) + 1); + if (! result) + return 0; + strcpy (result, s); + return result; +} + +static void +safe_free_str (char* s) +{ + if (s) free (s); +} + +static widget_value *widget_value_free_list = 0; + +widget_value * +malloc_widget_value () +{ + widget_value *wv; + if (widget_value_free_list) + { + wv = widget_value_free_list; + widget_value_free_list = wv->free_list; + wv->free_list = 0; + } + else + { + wv = (widget_value *) malloc (sizeof (widget_value)); + } + memset (wv, 0, sizeof (widget_value)); + return wv; +} + +/* this is analagous to free(). It frees only what was allocated + by malloc_widget_value(), and no substructures. + */ +void +free_widget_value (wv) + widget_value *wv; +{ + if (wv->free_list) + abort (); + wv->free_list = widget_value_free_list; + widget_value_free_list = wv; +} + +static void +free_widget_value_tree (widget_value* wv) +{ + if (!wv) + return; + + if (wv->name) free (wv->name); + if (wv->value) free (wv->value); + if (wv->key) free (wv->key); + + wv->name = wv->value = wv->key = (char *) 0xDEADBEEF; + + if (wv->toolkit_data && wv->free_toolkit_data) + { + free (wv->toolkit_data); + wv->toolkit_data = (void *) 0xDEADBEEF; + } + + if (wv->contents && (wv->contents != (widget_value*)1)) + { + free_widget_value_tree (wv->contents); + wv->contents = (widget_value *) 0xDEADBEEF; + } + if (wv->next) + { + free_widget_value_tree (wv->next); + wv->next = (widget_value *) 0xDEADBEEF; + } + free_widget_value (wv); +} + +static widget_value * +copy_widget_value_tree (widget_value* val, change_type change) +{ + widget_value* copy; + + if (!val) + return NULL; + if (val == (widget_value *) 1) + return val; + + copy = malloc_widget_value (); + copy->name = safe_strdup (val->name); + copy->value = safe_strdup (val->value); + copy->key = safe_strdup (val->key); + copy->enabled = val->enabled; + copy->selected = val->selected; + copy->edited = False; + copy->change = change; + copy->contents = copy_widget_value_tree (val->contents, change); + copy->call_data = val->call_data; + copy->next = copy_widget_value_tree (val->next, change); + copy->toolkit_data = NULL; + copy->free_toolkit_data = False; + return copy; +} + +static widget_info * +allocate_widget_info (char* type, char* name, LWLIB_ID id, widget_value* val, + lw_callback pre_activate_cb, lw_callback selection_cb, + lw_callback post_activate_cb) +{ + widget_info* info = (widget_info*)malloc (sizeof (widget_info)); + info->type = safe_strdup (type); + info->name = safe_strdup (name); + info->id = id; + info->val = copy_widget_value_tree (val, STRUCTURAL_CHANGE); + info->busy = False; + info->pre_activate_cb = pre_activate_cb; + info->selection_cb = selection_cb; + info->post_activate_cb = post_activate_cb; + info->instances = NULL; + + info->next = all_widget_info; + all_widget_info = info; + + return info; +} + +static void +free_widget_info (widget_info* info) +{ + safe_free_str (info->type); + safe_free_str (info->name); + free_widget_value_tree (info->val); + memset ((void*)info, 0xDEADBEEF, sizeof (widget_info)); + free (info); +} + +static void +mark_widget_destroyed (Widget widget, XtPointer closure, XtPointer call_data) +{ + widget_instance* instance = (widget_instance*)closure; + + /* be very conservative */ + if (instance->widget == widget) + instance->widget = NULL; +} + +static widget_instance * +allocate_widget_instance (widget_info* info, Widget parent, Boolean pop_up_p) +{ + widget_instance* instance = + (widget_instance*)malloc (sizeof (widget_instance)); + instance->parent = parent; + instance->pop_up_p = pop_up_p; + instance->info = info; + instance->next = info->instances; + info->instances = instance; + + instanciate_widget_instance (instance); + + XtAddCallback (instance->widget, XtNdestroyCallback, + mark_widget_destroyed, (XtPointer)instance); + return instance; +} + +static void +free_widget_instance (widget_instance* instance) +{ + memset ((void*)instance, 0xDEADBEEF, sizeof (widget_instance)); + free (instance); +} + +static widget_info * +get_widget_info (LWLIB_ID id, Boolean remove_p) +{ + widget_info* info; + widget_info* prev; + for (prev = NULL, info = all_widget_info; + info; + prev = info, info = info->next) + if (info->id == id) + { + if (remove_p) + { + if (prev) + prev->next = info->next; + else + all_widget_info = info->next; + } + return info; + } + return NULL; +} + +static widget_instance * +get_widget_instance (Widget widget, Boolean remove_p) +{ + widget_info* info; + widget_instance* instance; + widget_instance* prev; + for (info = all_widget_info; info; info = info->next) + for (prev = NULL, instance = info->instances; + instance; + prev = instance, instance = instance->next) + if (instance->widget == widget) + { + if (remove_p) + { + if (prev) + prev->next = instance->next; + else + info->instances = instance->next; + } + return instance; + } + return (widget_instance *) 0; +} + +static widget_instance* +find_instance (LWLIB_ID id, Widget parent, Boolean pop_up_p) +{ + widget_info* info = get_widget_info (id, False); + widget_instance* instance; + + if (info) + for (instance = info->instances; instance; instance = instance->next) + if (instance->parent == parent && instance->pop_up_p == pop_up_p) + return instance; + + return NULL; +} + + +/* utility function for widget_value */ +static Boolean +safe_strcmp (char* s1, char* s2) +{ + if (!!s1 ^ !!s2) return True; + return (s1 && s2) ? strcmp (s1, s2) : s1 ? False : !!s2; +} + +static int +max (int i1, int i2) +{ + return i1 > i2 ? i1 : i2; +} + + +#if 0 +# define EXPLAIN(name, oc, nc, desc, a1, a2) \ + printf ("Change: \"%s\"\tmax(%s=%d,%s=%d)\t%s %d %d\n", \ + name, \ + (oc == NO_CHANGE ? "none" : \ + (oc == INVISIBLE_CHANGE ? "invisible" : \ + (oc == VISIBLE_CHANGE ? "visible" : \ + (oc == STRUCTURAL_CHANGE ? "structural" : "???")))), \ + oc, \ + (nc == NO_CHANGE ? "none" : \ + (nc == INVISIBLE_CHANGE ? "invisible" : \ + (nc == VISIBLE_CHANGE ? "visible" : \ + (nc == STRUCTURAL_CHANGE ? "structural" : "???")))), \ + nc, desc, a1, a2) +#else +# define EXPLAIN(name, oc, nc, desc, a1, a2) +#endif + + +static widget_value * +merge_widget_value (widget_value* val1, widget_value* val2, int level) +{ + change_type change; + widget_value* merged_next; + widget_value* merged_contents; + + if (!val1) + { + if (val2) + return copy_widget_value_tree (val2, STRUCTURAL_CHANGE); + else + return NULL; + } + if (!val2) + { + free_widget_value_tree (val1); + return NULL; + } + + change = NO_CHANGE; + + if (safe_strcmp (val1->name, val2->name)) + { + EXPLAIN (val1->name, change, STRUCTURAL_CHANGE, "name change", + val1->name, val2->name); + change = max (change, STRUCTURAL_CHANGE); + safe_free_str (val1->name); + val1->name = safe_strdup (val2->name); + } + if (safe_strcmp (val1->value, val2->value)) + { + EXPLAIN (val1->name, change, VISIBLE_CHANGE, "value change", + val1->value, val2->value); + change = max (change, VISIBLE_CHANGE); + safe_free_str (val1->value); + val1->value = safe_strdup (val2->value); + } + if (safe_strcmp (val1->key, val2->key)) + { + EXPLAIN (val1->name, change, VISIBLE_CHANGE, "key change", + val1->key, val2->key); + change = max (change, VISIBLE_CHANGE); + safe_free_str (val1->key); + val1->key = safe_strdup (val2->key); + } + if (val1->enabled != val2->enabled) + { + EXPLAIN (val1->name, change, VISIBLE_CHANGE, "enablement change", + val1->enabled, val2->enabled); + change = max (change, VISIBLE_CHANGE); + val1->enabled = val2->enabled; + } + if (val1->selected != val2->selected) + { + EXPLAIN (val1->name, change, VISIBLE_CHANGE, "selection change", + val1->selected, val2->selected); + change = max (change, VISIBLE_CHANGE); + val1->selected = val2->selected; + } + if (val1->call_data != val2->call_data) + { + EXPLAIN (val1->name, change, INVISIBLE_CHANGE, "call-data change", + val1->call_data, val2->call_data); + change = max (change, INVISIBLE_CHANGE); + val1->call_data = val2->call_data; + } + + if (level > 0) + { + merged_contents = + merge_widget_value (val1->contents, val2->contents, level - 1); + + if (val1->contents && !merged_contents) + { + EXPLAIN (val1->name, change, INVISIBLE_CHANGE, "(contents gone)", + 0, 0); + change = max (change, INVISIBLE_CHANGE); + } + else if (merged_contents && merged_contents->change != NO_CHANGE) + { + EXPLAIN (val1->name, change, INVISIBLE_CHANGE, "(contents change)", + 0, 0); + change = max (change, INVISIBLE_CHANGE); + } + + val1->contents = merged_contents; + } + + merged_next = merge_widget_value (val1->next, val2->next, level); + + if (val1->next && !merged_next) + { + EXPLAIN (val1->name, change, STRUCTURAL_CHANGE, "(following gone)", + 0, 0); + change = max (change, STRUCTURAL_CHANGE); + } + else if (merged_next) + { + if (merged_next->change) + EXPLAIN (val1->name, change, merged_next->change, "(following change)", + 0, 0); + change = max (change, merged_next->change); + } + + val1->next = merged_next; + + val1->change = change; + + if (change > NO_CHANGE && val1->toolkit_data) + { + if (val1->free_toolkit_data) + free (val1->toolkit_data); + val1->toolkit_data = NULL; + } + + return val1; +} + + +/* modifying the widgets */ +static Widget +name_to_widget (widget_instance* instance, char* name) +{ + Widget widget = NULL; + + if (!instance->widget) + return NULL; + + if (!strcmp (XtName (instance->widget), name)) + widget = instance->widget; + else + { + int length = strlen (name) + 2; + char* real_name = (char *) alloca (length); + real_name [0] = '*'; + strcpy (real_name + 1, name); + + widget = XtNameToWidget (instance->widget, real_name); + } + return widget; +} + +static void +set_one_value (widget_instance* instance, widget_value* val, Boolean deep_p) +{ + Widget widget = name_to_widget (instance, val->name); + + if (widget) + { +#if defined (USE_LUCID) + if (lw_lucid_widget_p (instance->widget)) + xlw_update_one_widget (instance, widget, val, deep_p); +#endif +#if defined (USE_MOTIF) + if (lw_motif_widget_p (instance->widget)) + xm_update_one_widget (instance, widget, val, deep_p); +#endif +#if defined (USE_OLIT) + if (lw_olit_widget_p (instance->widget)) + xol_update_one_widget (instance, widget, val, deep_p); +#endif + } +} + +static void +update_one_widget_instance (widget_instance* instance, Boolean deep_p) +{ + widget_value *val; + + if (!instance->widget) + /* the widget was destroyed */ + return; + + for (val = instance->info->val; val; val = val->next) + if (val->change != NO_CHANGE) + set_one_value (instance, val, deep_p); +} + +static void +update_all_widget_values (widget_info* info, Boolean deep_p) +{ + widget_instance* instance; + widget_value* val; + + for (instance = info->instances; instance; instance = instance->next) + update_one_widget_instance (instance, deep_p); + + for (val = info->val; val; val = val->next) + val->change = NO_CHANGE; +} + +void +lw_modify_all_widgets (LWLIB_ID id, widget_value* val, Boolean deep_p) +{ + widget_info* info = get_widget_info (id, False); + widget_value* new_val; + widget_value* next_new_val; + widget_value* cur; + widget_value* prev; + widget_value* next; + int found; + + if (!info) + return; + + for (new_val = val; new_val; new_val = new_val->next) + { + next_new_val = new_val->next; + new_val->next = NULL; + found = False; + for (prev = NULL, cur = info->val; cur; prev = cur, cur = cur->next) + if (!strcmp (cur->name, new_val->name)) + { + found = True; + next = cur->next; + cur->next = NULL; + cur = merge_widget_value (cur, new_val, deep_p ? 1000 : 1); + if (prev) + prev->next = cur ? cur : next; + else + info->val = cur ? cur : next; + if (cur) + cur->next = next; + break; + } + if (!found) + { + /* Could not find it, add it */ + if (prev) + prev->next = copy_widget_value_tree (new_val, STRUCTURAL_CHANGE); + else + info->val = copy_widget_value_tree (new_val, STRUCTURAL_CHANGE); + } + new_val->next = next_new_val; + } + + update_all_widget_values (info, deep_p); +} + + +/* creating the widgets */ + +static void +initialize_widget_instance (widget_instance* instance) +{ + widget_value* val; + + for (val = instance->info->val; val; val = val->next) + val->change = STRUCTURAL_CHANGE; + + update_one_widget_instance (instance, True); + + for (val = instance->info->val; val; val = val->next) + val->change = NO_CHANGE; +} + + +static widget_creation_function +find_in_table (char* type, widget_creation_entry* table) +{ + widget_creation_entry* cur; + for (cur = table; cur->type; cur++) + if (!strcasecmp (type, cur->type)) + return cur->function; + return NULL; +} + +static Boolean +dialog_spec_p (char* name) +{ + /* return True if name matches [EILPQeilpq][1-9][Bb] or + [EILPQeilpq][1-9][Bb][Rr][1-9] */ + if (!name) + return False; + + switch (name [0]) + { + case 'E': case 'I': case 'L': case 'P': case 'Q': + case 'e': case 'i': case 'l': case 'p': case 'q': + if (name [1] >= '0' && name [1] <= '9') + { + if (name [2] != 'B' && name [2] != 'b') + return False; + if (!name [3]) + return True; + if ((name [3] == 'T' || name [3] == 't') && !name [4]) + return True; + if ((name [3] == 'R' || name [3] == 'r') + && name [4] >= '0' && name [4] <= '9' && !name [5]) + return True; + return False; + } + else + return False; + + default: + return False; + } +} + +static void +instanciate_widget_instance (widget_instance* instance) +{ + widget_creation_function function = NULL; + +#if defined (USE_LUCID) + if (!function) + function = find_in_table (instance->info->type, xlw_creation_table); +#endif +#if defined(USE_MOTIF) + if (!function) + function = find_in_table (instance->info->type, xm_creation_table); +#endif +#if defined (USE_OLIT) + if (!function) + function = find_in_table (instance->info->type, xol_creation_table); +#endif + + if (!function) + { + if (dialog_spec_p (instance->info->type)) + { +#if defined (USE_LUCID) + /* not yet */ +#endif +#if defined(USE_MOTIF) + if (!function) + function = xm_create_dialog; +#endif +#if defined (USE_OLIT) + /* not yet */ +#endif + } + } + + if (!function) + { + printf ("No creation function for widget type %s\n", + instance->info->type); + abort (); + } + + instance->widget = (*function) (instance); + + if (!instance->widget) + abort (); + + /* XtRealizeWidget (instance->widget);*/ +} + +void +lw_register_widget (char* type, char* name, LWLIB_ID id, widget_value* val, + lw_callback pre_activate_cb, lw_callback selection_cb, + lw_callback post_activate_cb) +{ + if (!get_widget_info (id, False)) + allocate_widget_info (type, name, id, val, pre_activate_cb, selection_cb, + post_activate_cb); +} + +Widget +lw_get_widget (LWLIB_ID id, Widget parent, Boolean pop_up_p) +{ + widget_instance* instance; + + instance = find_instance (id, parent, pop_up_p); + return instance ? instance->widget : NULL; +} + +Widget +lw_make_widget (LWLIB_ID id, Widget parent, Boolean pop_up_p) +{ + widget_instance* instance; + widget_info* info; + + instance = find_instance (id, parent, pop_up_p); + if (!instance) + { + info = get_widget_info (id, False); + if (!info) + return NULL; + instance = allocate_widget_instance (info, parent, pop_up_p); + initialize_widget_instance (instance); + } + if (!instance->widget) + abort (); + return instance->widget; +} + +Widget +lw_create_widget (char* type, char* name, LWLIB_ID id, widget_value* val, + Widget parent, Boolean pop_up_p, lw_callback pre_activate_cb, + lw_callback selection_cb, lw_callback post_activate_cb) +{ + lw_register_widget (type, name, id, val, pre_activate_cb, selection_cb, + post_activate_cb); + return lw_make_widget (id, parent, pop_up_p); +} + + +/* destroying the widgets */ +static void +destroy_one_instance (widget_instance* instance) +{ + /* Remove the destroy callback on the widget; that callback will try to + dereference the instance object (to set its widget slot to 0, since the + widget is dead.) Since the instance is now dead, we don't have to worry + about the fact that its widget is dead too. + + This happens in the Phase2Destroy of the widget, so this callback would + not have been run until arbitrarily long after the instance was freed. + */ + if (instance->widget) + XtRemoveCallback (instance->widget, XtNdestroyCallback, + mark_widget_destroyed, (XtPointer)instance); + + if (instance->widget) + { + /* The else are pretty tricky here, including the empty statement + at the end because it would be very bad to destroy a widget + twice. */ +#if defined (USE_LUCID) + if (lw_lucid_widget_p (instance->widget)) + xlw_destroy_instance (instance); + else +#endif +#if defined (USE_MOTIF) + if (lw_motif_widget_p (instance->widget)) + xm_destroy_instance (instance); + else +#endif +#if defined (USE_OLIT) + if (lw_olit_widget_p (instance->widget)) + xol_destroy_instance (instance); + else +#endif + /* do not remove the empty statement */ + ; + } + + free_widget_instance (instance); +} + +void +lw_destroy_widget (Widget w) +{ + widget_instance* instance = get_widget_instance (w, True); + + if (instance) + { + widget_info *info = instance->info; + /* instance has already been removed from the list; free it */ + destroy_one_instance (instance); + /* if there are no instances left, free the info too */ + if (!info->instances) + lw_destroy_all_widgets (info->id); + } +} + +void +lw_destroy_all_widgets (LWLIB_ID id) +{ + widget_info* info = get_widget_info (id, True); + widget_instance* instance; + widget_instance* next; + + if (info) + { + for (instance = info->instances; instance; ) + { + next = instance->next; + destroy_one_instance (instance); + instance = next; + } + free_widget_info (info); + } +} + +void +lw_destroy_everything () +{ + while (all_widget_info) + lw_destroy_all_widgets (all_widget_info->id); +} + +void +lw_destroy_all_pop_ups () +{ + widget_info* info; + widget_info* next; + widget_instance* instance; + + for (info = all_widget_info; info; info = next) + { + next = info->next; + instance = info->instances; + if (instance && instance->pop_up_p) + lw_destroy_all_widgets (info->id); + } +} + +#ifdef USE_MOTIF +extern Widget first_child (Widget); /* garbage */ +#endif + +Widget +lw_raise_all_pop_up_widgets () +{ + widget_info* info; + widget_instance* instance; + Widget result = NULL; + + for (info = all_widget_info; info; info = info->next) + for (instance = info->instances; instance; instance = instance->next) + if (instance->pop_up_p) + { + Widget widget = instance->widget; + if (widget) + { + if (XtIsManaged (widget) +#ifdef USE_MOTIF + /* What a complete load of crap!!!! + When a dialogShell is on the screen, it is not managed! + */ + || (lw_motif_widget_p (instance->widget) && + XtIsManaged (first_child (widget))) +#endif + ) + { + if (!result) + result = widget; + XMapRaised (XtDisplay (widget), XtWindow (widget)); + } + } + } + return result; +} + +static void +lw_pop_all_widgets (LWLIB_ID id, Boolean up) +{ + widget_info* info = get_widget_info (id, False); + widget_instance* instance; + + if (info) + for (instance = info->instances; instance; instance = instance->next) + if (instance->pop_up_p && instance->widget) + { + if (!XtIsRealized (instance->widget)) + XtRealizeWidget (instance->widget); +#if defined (USE_LUCID) + if (lw_lucid_widget_p (instance->widget)) + xlw_pop_instance (instance, up); +#endif +#if defined (USE_MOTIF) + if (lw_motif_widget_p (instance->widget)) + xm_pop_instance (instance, up); +#endif +#if defined (USE_OLIT) + if (lw_olit_widget_p (instance->widget)) + xol_pop_instance (instance, up); +#endif + } +} + +void +lw_pop_up_all_widgets (LWLIB_ID id) +{ + lw_pop_all_widgets (id, True); +} + +void +lw_pop_down_all_widgets (LWLIB_ID id) +{ + lw_pop_all_widgets (id, False); +} + +void +lw_popup_menu (Widget widget) +{ +#if defined (USE_LUCID) + if (lw_lucid_widget_p (widget)) + xlw_popup_menu (widget); +#endif +#if defined (USE_MOTIF) + if (lw_motif_widget_p (widget)) + xm_popup_menu (widget); +#endif +#if defined (USE_OLIT) + if (lw_olit_widget_p (widget)) + xol_popup_menu (widget); +#endif +} + + /* get the values back */ +static Boolean +get_one_value (widget_instance* instance, widget_value* val) +{ + Widget widget = name_to_widget (instance, val->name); + + if (widget) + { +#if defined (USE_LUCID) + if (lw_lucid_widget_p (instance->widget)) + xlw_update_one_value (instance, widget, val); +#endif +#if defined (USE_MOTIF) + if (lw_motif_widget_p (instance->widget)) + xm_update_one_value (instance, widget, val); +#endif +#if defined (USE_OLIT) + if (lw_olit_widget_p (instance->widget)) + xol_update_one_value (instance, widget, val); +#endif + return True; + } + else + return False; +} + +Boolean +lw_get_some_values (LWLIB_ID id, widget_value* val_out) +{ + widget_info* info = get_widget_info (id, False); + widget_instance* instance; + widget_value* val; + Boolean result = False; + + if (!info) + return False; + + instance = info->instances; + if (!instance) + return False; + + for (val = val_out; val; val = val->next) + if (get_one_value (instance, val)) + result = True; + + return result; +} + +widget_value* +lw_get_all_values (LWLIB_ID id) +{ + widget_info* info = get_widget_info (id, False); + widget_value* val = info->val; + if (lw_get_some_values (id, val)) + return val; + else + return NULL; +} + +/* internal function used by the library dependent implementation to get the + widget_value for a given widget in an instance */ +widget_value* +lw_get_widget_value_for_widget (widget_instance* instance, Widget w) +{ + char* name = XtName (w); + widget_value* cur; + for (cur = instance->info->val; cur; cur = cur->next) + if (!strcmp (cur->name, name)) + return cur; + return NULL; +} + + /* update other instances value when one thing changed */ +/* This function can be used as a an XtCallback for the widgets that get + modified to update other instances of the widgets. Closure should be the + widget_instance. */ +void +lw_internal_update_other_instances (Widget widget, XtPointer closure, + XtPointer call_data) +{ + /* To forbid recursive calls */ + static Boolean updating; + + widget_instance* instance = (widget_instance*)closure; + char* name = XtName (widget); + widget_info* info; + widget_instance* cur; + widget_value* val; + + /* never recurse as this could cause infinite recursions. */ + if (updating) + return; + + /* protect against the widget being destroyed */ + if (XtWidgetBeingDestroyedP (widget)) + return; + + /* Return immediately if there are no other instances */ + info = instance->info; + if (!info->instances->next) + return; + + updating = True; + + for (val = info->val; val && strcmp (val->name, name); val = val->next); + + if (val && get_one_value (instance, val)) + for (cur = info->instances; cur; cur = cur->next) + if (cur != instance) + set_one_value (cur, val, True); + + updating = False; +} + + + /* get the id */ + +LWLIB_ID +lw_get_widget_id (Widget w) +{ + widget_instance* instance = get_widget_instance (w, False); + + return instance ? instance->info->id : 0; +} + + /* set the keyboard focus */ +void +lw_set_keyboard_focus (Widget parent, Widget w) +{ +#if defined (USE_MOTIF) + xm_set_keyboard_focus (parent, w); +#else + XtSetKeyboardFocus (parent, w); +#endif +} + + /* Show busy */ +static void +show_one_widget_busy (Widget w, Boolean flag) +{ + Pixel foreground = 0; + Pixel background = 1; + Widget widget_to_invert = XtNameToWidget (w, "*sheet"); + if (!widget_to_invert) + widget_to_invert = w; + + XtVaGetValues (widget_to_invert, + XtNforeground, &foreground, + XtNbackground, &background, + 0); + XtVaSetValues (widget_to_invert, + XtNforeground, background, + XtNbackground, foreground, + 0); +} + +void +lw_show_busy (Widget w, Boolean busy) +{ + widget_instance* instance = get_widget_instance (w, False); + widget_info* info; + widget_instance* next; + + if (instance) + { + info = instance->info; + if (info->busy != busy) + { + for (next = info->instances; next; next = next->next) + if (next->widget) + show_one_widget_busy (next->widget, busy); + info->busy = busy; + } + } +} diff --git a/lwlib/lwlib.h b/lwlib/lwlib.h new file mode 100644 index 00000000000..195bbb5df8b --- /dev/null +++ b/lwlib/lwlib.h @@ -0,0 +1,103 @@ +#ifndef LWLIB_H +#define LWLIB_H + +#include + +/* +** Widget values depend on the Widget type: +** +** widget: (name value key enabled data contents/selected) +** +** label: ("name" "string" NULL NULL NULL NULL) +** button: ("name" "string" "key" T/F data ) +** button w/menu: +** ("name" "string" "key" T/F data (label|button|button w/menu...)) +** menubar: ("name" NULL NULL T/F data (button w/menu)) +** selectable thing: +** ("name" "string" "key" T/F data T/F) +** checkbox: selectable thing +** radio: ("name" NULL NULL T/F data (selectable thing...)) +** strings: ("name" NULL NULL T/F data (selectable thing...)) +** text: ("name" "string" T/F data) +*/ + +typedef unsigned long LWLIB_ID; + +typedef enum _change_type +{ + NO_CHANGE = 0, + INVISIBLE_CHANGE = 1, + VISIBLE_CHANGE = 2, + STRUCTURAL_CHANGE = 3 +} change_type; + +typedef struct _widget_value +{ + /* name of widget */ + char* name; + /* value (meaning depend on widget type) */ + char* value; + /* keyboard equivalent. no implications for XtTranslations */ + char* key; + /* true if enabled */ + Boolean enabled; + /* true if selected */ + Boolean selected; + /* true if was edited (maintained by get_value) */ + Boolean edited; + /* true if has changed (maintained by lw library) */ + change_type change; + /* Contents of the sub-widgets, also selected slot for checkbox */ + struct _widget_value* contents; + /* data passed to callback */ + XtPointer call_data; + /* next one in the list */ + struct _widget_value* next; + /* slot for the toolkit dependent part. Always initialize to NULL. */ + void* toolkit_data; + /* tell us if we should free the toolkit data slot when freeing the + widget_value itself. */ + Boolean free_toolkit_data; + + /* we resource the widget_value structures; this points to the next + one on the free list if this one has been deallocated. + */ + struct _widget_value *free_list; +} widget_value; + + +typedef void (*lw_callback) (Widget w, LWLIB_ID id, void* data); + +void lw_register_widget (char* type, char* name, LWLIB_ID id, + widget_value* val, lw_callback pre_activate_cb, + lw_callback selection_cb, + lw_callback post_activate_cb); +Widget lw_get_widget (LWLIB_ID id, Widget parent, Boolean pop_up_p); +Widget lw_make_widget (LWLIB_ID id, Widget parent, Boolean pop_up_p); +Widget lw_create_widget (char* type, char* name, LWLIB_ID id, + widget_value* val, Widget parent, Boolean pop_up_p, + lw_callback pre_activate_cb, + lw_callback selection_cb, + lw_callback post_activate_cb); +LWLIB_ID lw_get_widget_id (Widget w); +void lw_modify_all_widgets (LWLIB_ID id, widget_value* val, Boolean deep_p); +void lw_destroy_widget (Widget w); +void lw_destroy_all_widgets (LWLIB_ID id); +void lw_destroy_everything (void); +void lw_destroy_all_pop_ups (void); +Widget lw_raise_all_pop_up_widgets (void); +widget_value* lw_get_all_values (LWLIB_ID id); +Boolean lw_get_some_values (LWLIB_ID id, widget_value* val); +void lw_pop_up_all_widgets (LWLIB_ID id); +void lw_pop_down_all_widgets (LWLIB_ID id); +widget_value *malloc_widget_value (); +void free_widget_value (widget_value *); +void lw_popup_menu (Widget); + +/* Toolkit independent way of focusing on a Widget at the Xt level. */ +void lw_set_keyboard_focus (Widget parent, Widget w); + +/* Silly Energize hack to invert the "sheet" button */ +void lw_show_busy (Widget w, Boolean busy); + +#endif /* LWLIB_H */ diff --git a/lwlib/xlwmenu.c b/lwlib/xlwmenu.c new file mode 100644 index 00000000000..4d4efd4a01c --- /dev/null +++ b/lwlib/xlwmenu.c @@ -0,0 +1,1265 @@ +/* Implements a lightweight menubar widget. + Copyright (C) 1992 Lucid, Inc. + +This file is part of the Lucid Widget Library. + +The Lucid Widget Library is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +The Lucid Widget Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Created by devin@lucid.com */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "xlwmenuP.h" +#include + +static char +xlwMenuTranslations [] = +": start()\n\ +: drag()\n\ +: select()\n\ +"; + +#define offset(field) XtOffset(XlwMenuWidget, field) +static XtResource +xlwMenuResources[] = +{ + {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), + offset(menu.font),XtRString, "XtDefaultFont"}, + {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + offset(menu.foreground), XtRString, "XtDefaultForeground"}, + {XtNbuttonForeground, XtCButtonForeground, XtRPixel, sizeof(Pixel), + offset(menu.button_foreground), XtRString, "XtDefaultForeground"}, + {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension), + offset(menu.margin), XtRImmediate, (XtPointer)0}, + {XtNhorizontalSpacing, XtCMargin, XtRDimension, sizeof(Dimension), + offset(menu.horizontal_spacing), XtRImmediate, (XtPointer)3}, + {XtNverticalSpacing, XtCMargin, XtRDimension, sizeof(Dimension), + offset(menu.vertical_spacing), XtRImmediate, (XtPointer)1}, + {XtNarrowSpacing, XtCMargin, XtRDimension, sizeof(Dimension), + offset(menu.arrow_spacing), XtRImmediate, (XtPointer)10}, + + {XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, + sizeof (Dimension), offset (menu.shadow_thickness), + XtRImmediate, (XtPointer) 2}, + {XmNtopShadowColor, XmCTopShadowColor, XtRPixel, sizeof (Pixel), + offset (menu.top_shadow_color), XtRImmediate, (XtPointer)-1}, + {XmNbottomShadowColor, XmCBottomShadowColor, XtRPixel, sizeof (Pixel), + offset (menu.bottom_shadow_color), XtRImmediate, (XtPointer)-1}, + {XmNtopShadowPixmap, XmCTopShadowPixmap, XtRPixmap, sizeof (Pixmap), + offset (menu.top_shadow_pixmap), XtRImmediate, (XtPointer)None}, + {XmNbottomShadowPixmap, XmCBottomShadowPixmap, XtRPixmap, sizeof (Pixmap), + offset (menu.bottom_shadow_pixmap), XtRImmediate, (XtPointer)None}, + + {XtNopen, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(menu.open), XtRCallback, (XtPointer)NULL}, + {XtNselect, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(menu.select), XtRCallback, (XtPointer)NULL}, + {XtNmenu, XtCMenu, XtRPointer, sizeof(XtPointer), + offset(menu.contents), XtRImmediate, (XtPointer)NULL}, + {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor), + offset(menu.cursor_shape), XtRString, (XtPointer)"right_ptr"}, + {XtNhorizontal, XtCHorizontal, XtRInt, sizeof(int), + offset(menu.horizontal), XtRImmediate, (XtPointer)True}, +}; +#undef offset + +static Boolean XlwMenuSetValues(); +static void XlwMenuRealize(); +static void XlwMenuRedisplay(); +static void XlwMenuResize(); +static void XlwMenuInitialize(); +static void XlwMenuRedisplay(); +static void XlwMenuDestroy(); +static void XlwMenuClassInitialize(); +static void Start(); +static void Drag(); +static void Select(); + +static XtActionsRec +xlwMenuActionsList [] = +{ + {"start", Start}, + {"drag", Drag}, + {"select", Select}, +}; + +#define SuperClass ((CoreWidgetClass)&coreClassRec) + +XlwMenuClassRec xlwMenuClassRec = +{ + { /* CoreClass fields initialization */ + (WidgetClass) SuperClass, /* superclass */ + "XlwMenu", /* class_name */ + sizeof(XlwMenuRec), /* size */ + XlwMenuClassInitialize, /* class_initialize */ + NULL, /* class_part_initialize */ + FALSE, /* class_inited */ + XlwMenuInitialize, /* initialize */ + NULL, /* initialize_hook */ + XlwMenuRealize, /* realize */ + xlwMenuActionsList, /* actions */ + XtNumber(xlwMenuActionsList), /* num_actions */ + xlwMenuResources, /* resources */ + XtNumber(xlwMenuResources), /* resource_count */ + NULLQUARK, /* xrm_class */ + TRUE, /* compress_motion */ + TRUE, /* compress_exposure */ + TRUE, /* compress_enterleave */ + FALSE, /* visible_interest */ + XlwMenuDestroy, /* destroy */ + XlwMenuResize, /* resize */ + XlwMenuRedisplay, /* expose */ + XlwMenuSetValues, /* set_values */ + NULL, /* set_values_hook */ + XtInheritSetValuesAlmost, /* set_values_almost */ + NULL, /* get_values_hook */ + NULL, /* accept_focus */ + XtVersion, /* version */ + NULL, /* callback_private */ + xlwMenuTranslations, /* tm_table */ + XtInheritQueryGeometry, /* query_geometry */ + XtInheritDisplayAccelerator, /* display_accelerator */ + NULL /* extension */ + }, /* XlwMenuClass fields initialization */ + { + 0 /* dummy */ + }, +}; + +WidgetClass xlwMenuWidgetClass = (WidgetClass) &xlwMenuClassRec; + + /* Utilities */ +static void +push_new_stack (XlwMenuWidget mw, widget_value* val) +{ + if (!mw->menu.new_stack) + { + mw->menu.new_stack_length = 10; + mw->menu.new_stack = + (widget_value**)XtCalloc (mw->menu.new_stack_length, + sizeof (widget_value*)); + } + else if (mw->menu.new_depth == mw->menu.new_stack_length) + { + mw->menu.new_stack_length *= 2; + mw->menu.new_stack = + (widget_value**)XtRealloc ((char*)mw->menu.new_stack, + mw->menu.new_stack_length * sizeof (widget_value*)); + } + mw->menu.new_stack [mw->menu.new_depth++] = val; +} + +static void +pop_new_stack_if_no_contents (XlwMenuWidget mw) +{ + if (mw->menu.new_depth) + { + if (!mw->menu.new_stack [mw->menu.new_depth - 1]->contents) + mw->menu.new_depth -= 1; + } +} + +static void +make_old_stack_space (XlwMenuWidget mw, int n) +{ + if (!mw->menu.old_stack) + { + mw->menu.old_stack_length = 10; + mw->menu.old_stack = + (widget_value**)XtCalloc (mw->menu.old_stack_length, + sizeof (widget_value*)); + } + else if (mw->menu.old_stack_length < n) + { + mw->menu.old_stack_length *= 2; + mw->menu.old_stack = + (widget_value**)XtRealloc ((char*)mw->menu.old_stack, + mw->menu.old_stack_length * sizeof (widget_value*)); + } +} + + /* Size code */ +static Boolean +all_dashes_p (char* s) +{ + char* p; + for (p = s; *p == '-'; p++); + return !*p; +} + +static int +string_width (XlwMenuWidget mw, char* s) +{ + XCharStruct xcs; + int drop; + + XTextExtents (mw->menu.font, s, strlen (s), &drop, &drop, &drop, &xcs); + return xcs.width; +} + +static int +arrow_width (XlwMenuWidget mw) +{ + return mw->menu.font->ascent / 2 | 1; +} + +static XtResource +nameResource[] = +{ + {"labelString", "LabelString", XtRString, sizeof(String), + 0, XtRImmediate, 0}, +}; + +static char* +resource_widget_value (XlwMenuWidget mw, widget_value* val) +{ + if (!val->toolkit_data) + { + char* resourced_name = NULL; + char* complete_name; + XtGetSubresources ((Widget) mw, + (XtPointer) &resourced_name, + val->name, val->name, + nameResource, 1, NULL, 0); + if (!resourced_name) + resourced_name = val->name; + if (!val->value) + complete_name = (char *) strdup (resourced_name); + else + { + int complete_length = + strlen (resourced_name) + strlen (val->value) + 2; + complete_name = XtMalloc (complete_length); + *complete_name = 0; + strcat (complete_name, resourced_name); + strcat (complete_name, " "); + strcat (complete_name, val->value); + } + + val->toolkit_data = complete_name; + val->free_toolkit_data = True; + } + return (char*)val->toolkit_data; +} + +/* Returns the sizes of an item */ +static void +size_menu_item (XlwMenuWidget mw, widget_value* val, int horizontal_p, + int* label_width, int* rest_width, int* height) +{ + if (all_dashes_p (val->name)) + { + *height = 2; + *label_width = 1; + *rest_width = 0; + } + else + { + *height = + mw->menu.font->ascent + mw->menu.font->descent + + 2 * mw->menu.vertical_spacing + 2 * mw->menu.shadow_thickness; + + *label_width = + string_width (mw, resource_widget_value (mw, val)) + + mw->menu.horizontal_spacing + mw->menu.shadow_thickness; + + *rest_width = mw->menu.horizontal_spacing + mw->menu.shadow_thickness; + if (!horizontal_p) + { + if (val->contents) + *rest_width += arrow_width (mw) + mw->menu.arrow_spacing; + else if (val->key) + *rest_width += + string_width (mw, val->key) + mw->menu.arrow_spacing; + } + } +} + +static void +size_menu (XlwMenuWidget mw, int level) +{ + int label_width = 0; + int rest_width = 0; + int max_rest_width = 0; + int height = 0; + int horizontal_p = mw->menu.horizontal && (level == 0); + widget_value* val; + window_state* ws; + + if (level >= mw->menu.old_depth) + abort (); + + ws = &mw->menu.windows [level]; + ws->width = 0; + ws->height = 0; + ws->label_width = 0; + + for (val = mw->menu.old_stack [level]->contents; val; val = val->next) + { + size_menu_item (mw, val, horizontal_p, &label_width, &rest_width, + &height); + if (horizontal_p) + { + ws->width += label_width + rest_width; + if (height > ws->height) + ws->height = height; + } + else + { + if (label_width > ws->label_width) + ws->label_width = label_width; + if (rest_width > max_rest_width) + max_rest_width = rest_width; + ws->height += height; + } + } + + if (horizontal_p) + ws->label_width = 0; + else + ws->width = ws->label_width + max_rest_width; + + ws->width += 2 * mw->menu.shadow_thickness; + ws->height += 2 * mw->menu.shadow_thickness; +} + + + /* Display code */ +static void +draw_arrow (XlwMenuWidget mw, Window window, GC gc, int x, int y, int width) +{ + XPoint points [3]; + points [0].x = x; + points [0].y = y + mw->menu.font->ascent; + points [1].x = x; + points [1].y = y; + points [2].x = x + width; + points [2].y = y + mw->menu.font->ascent / 2; + + XFillPolygon (XtDisplay (mw), window, gc, points, 3, Convex, + CoordModeOrigin); +} + +static void +draw_shadow_rectangle (XlwMenuWidget mw, Window window, + int x, int y, int width, int height, int erase_p) +{ + Display *dpy = XtDisplay (mw); + GC top_gc = !erase_p ? mw->menu.shadow_top_gc : mw->menu.background_gc; + GC bottom_gc = !erase_p ? mw->menu.shadow_bottom_gc : mw->menu.background_gc; + int thickness = mw->menu.shadow_thickness; + XPoint points [4]; + points [0].x = x; + points [0].y = y; + points [1].x = x + width; + points [1].y = y; + points [2].x = x + width - thickness; + points [2].y = y + thickness; + points [3].x = x; + points [3].y = y + thickness; + XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin); + points [0].x = x; + points [0].y = y + thickness; + points [1].x = x; + points [1].y = y + height; + points [2].x = x + thickness; + points [2].y = y + height - thickness; + points [3].x = x + thickness; + points [3].y = y + thickness; + XFillPolygon (dpy, window, top_gc, points, 4, Convex, CoordModeOrigin); + points [0].x = x + width; + points [0].y = y; + points [1].x = x + width - thickness; + points [1].y = y + thickness; + points [2].x = x + width - thickness; + points [2].y = y + height - thickness; + points [3].x = x + width; + points [3].y = y + height - thickness; + XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin); + points [0].x = x; + points [0].y = y + height; + points [1].x = x + width; + points [1].y = y + height; + points [2].x = x + width; + points [2].y = y + height - thickness; + points [3].x = x + thickness; + points [3].y = y + height - thickness; + XFillPolygon (dpy, window, bottom_gc, points, 4, Convex, CoordModeOrigin); +} + + +/* Display the menu item and increment where.x and where.y to show how large +** the menu item was. +*/ +static void +display_menu_item (XlwMenuWidget mw, widget_value* val, window_state* ws, + XPoint* where, Boolean highlighted_p, Boolean horizontal_p, + Boolean just_compute_p) +{ + GC deco_gc; + GC text_gc; + int font_ascent = mw->menu.font->ascent; + int font_descent = mw->menu.font->descent; + int shadow = mw->menu.shadow_thickness; + int separator_p = all_dashes_p (val->name); + int h_spacing = mw->menu.horizontal_spacing; + int v_spacing = mw->menu.vertical_spacing; + int label_width; + int rest_width; + int height; + int width; + int button_p; + + /* compute the sizes of the item */ + size_menu_item (mw, val, horizontal_p, &label_width, &rest_width, &height); + + if (horizontal_p) + width = label_width + rest_width; + else + { + label_width = ws->label_width; + width = ws->width - 2 * shadow; + } + + /* see if it should be a button in the menubar */ + button_p = horizontal_p && val->call_data; + + /* Only highlight an enabled item that has a callback. */ + if (highlighted_p) + if (!val->enabled || !(val->call_data || val->contents)) + highlighted_p = 0; + + /* do the drawing. */ + if (!just_compute_p) + { + /* Add the shadow border of the containing menu */ + int x = where->x + shadow; + int y = where->y + shadow; + + /* pick the foreground and background GC. */ + if (val->enabled) + text_gc = button_p ? mw->menu.button_gc : mw->menu.foreground_gc; + else + text_gc = + button_p ? mw->menu.inactive_button_gc : mw->menu.inactive_gc; + deco_gc = mw->menu.foreground_gc; + + if (separator_p) + { + XDrawLine (XtDisplay (mw), ws->window, mw->menu.shadow_bottom_gc, + x, y, x + width, y); + XDrawLine (XtDisplay (mw), ws->window, mw->menu.shadow_top_gc, + x, y + 1, x + width, y + 1); + } + else + { + char* display_string = resource_widget_value (mw, val); + draw_shadow_rectangle (mw, ws->window, x, y, width, height, True); + XDrawString (XtDisplay (mw), ws->window, text_gc, + x + h_spacing + shadow, + y + v_spacing + shadow + font_ascent, + display_string, strlen (display_string)); + + if (!horizontal_p) + { + if (val->contents) + { + int a_w = arrow_width (mw); + draw_arrow (mw, ws->window, deco_gc, + x + label_width + mw->menu.arrow_spacing, + y + v_spacing + shadow, a_w); + } + else if (val->key) + { + XDrawString (XtDisplay (mw), ws->window, text_gc, + x + label_width + mw->menu.arrow_spacing, + y + v_spacing + shadow + font_ascent, + val->key, strlen (val->key)); + } + } + + else if (button_p) + { +#if 1 + XDrawRectangle (XtDisplay (mw), ws->window, deco_gc, + x + shadow, y + shadow, + label_width + h_spacing - 1, + font_ascent + font_descent + 2 * v_spacing - 1); +#else + highlighted_p = True; +#endif + } + + if (highlighted_p) + draw_shadow_rectangle (mw, ws->window, x, y, width, height, False); + } + } + + where->x += width; + where->y += height; +} + +static void +display_menu (XlwMenuWidget mw, int level, Boolean just_compute_p, + XPoint* highlighted_pos, XPoint* hit, widget_value** hit_return, + widget_value* this, widget_value* that) +{ + widget_value* val; + widget_value* following_item; + window_state* ws; + XPoint where; + int horizontal_p = mw->menu.horizontal && (level == 0); + int highlighted_p; + int just_compute_this_one_p; + + if (level >= mw->menu.old_depth) + abort (); + + if (level < mw->menu.old_depth - 1) + following_item = mw->menu.old_stack [level + 1]; + else + following_item = NULL; + + if (hit) + *hit_return = NULL; + + where.x = 0; + where.y = 0; + + ws = &mw->menu.windows [level]; + for (val = mw->menu.old_stack [level]->contents; val; val = val->next) + { + highlighted_p = val == following_item; + if (highlighted_p && highlighted_pos) + { + if (horizontal_p) + highlighted_pos->x = where.x; + else + highlighted_pos->y = where.y; + } + + just_compute_this_one_p = + just_compute_p || ((this || that) && val != this && val != that); + + display_menu_item (mw, val, ws, &where, highlighted_p, horizontal_p, + just_compute_this_one_p); + + if (highlighted_p && highlighted_pos) + { + if (horizontal_p) + highlighted_pos->y = where.y; + else + highlighted_pos->x = where.x; + } + + if (hit + && !*hit_return + && (horizontal_p ? hit->x < where.x : hit->y < where.y) + && !all_dashes_p (val->name)) + *hit_return = val; + + if (horizontal_p) + where.y = 0; + else + where.x = 0; + } + + if (!just_compute_p) + draw_shadow_rectangle (mw, ws->window, 0, 0, ws->width, ws->height, False); +} + + /* Motion code */ +static void +set_new_state (XlwMenuWidget mw, widget_value* val, int level) +{ + int i; + + mw->menu.new_depth = 0; + for (i = 0; i < level; i++) + push_new_stack (mw, mw->menu.old_stack [i]); + push_new_stack (mw, val); +} + +static void +make_windows_if_needed (XlwMenuWidget mw, int n) +{ + int i; + int start_at; + XSetWindowAttributes xswa; + int mask; + Window root = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw))); + window_state* windows; + + if (mw->menu.windows_length >= n) + return; + + xswa.save_under = True; + xswa.override_redirect = True; + xswa.background_pixel = mw->core.background_pixel; + xswa.border_pixel = mw->core.border_pixel; + xswa.event_mask = + ExposureMask | ButtonMotionMask | PointerMotionHintMask + | ButtonReleaseMask | ButtonPressMask; + xswa.cursor = mw->menu.cursor_shape; + mask = CWSaveUnder | CWOverrideRedirect | CWBackPixel | CWBorderPixel + | CWEventMask | CWCursor; + + if (!mw->menu.windows) + { + mw->menu.windows = + (window_state*)XtMalloc (n * sizeof (window_state)); + start_at = 0; + } + else + { + mw->menu.windows = + (window_state*)XtRealloc ((char*)mw->menu.windows, + n * sizeof (window_state)); + start_at = mw->menu.windows_length; + } + mw->menu.windows_length = n; + + windows = mw->menu.windows; + + for (i = start_at; i < n; i++) + { + windows [i].x = 0; + windows [i].y = 0; + windows [i].width = 1; + windows [i].height = 1; + windows [i].window = + XCreateWindow (XtDisplay (mw), root, 0, 0, 1, 1, + 0, 0, CopyFromParent, CopyFromParent, mask, &xswa); + } +} + +/* Make the window fit in the screen */ +static void +fit_to_screen (XlwMenuWidget mw, window_state* ws, window_state* previous_ws, + Boolean horizontal_p) +{ + int screen_width = WidthOfScreen (XtScreen (mw)); + int screen_height = HeightOfScreen (XtScreen (mw)); + + if (ws->x < 0) + ws->x = 0; + else if (ws->x + ws->width > screen_width) + { + if (!horizontal_p) + ws->x = previous_ws->x - ws->width; + else + ws->x = screen_width - ws->width; + } + if (ws->y < 0) + ws->y = 0; + else if (ws->y + ws->height > screen_height) + { + if (horizontal_p) + ws->y = previous_ws->y - ws->height; + else + ws->y = screen_height - ws->height; + } +} + +/* Updates old_stack from new_stack and redisplays. */ +static void +remap_menubar (XlwMenuWidget mw) +{ + int i; + int last_same; + XPoint selection_position; + int old_depth = mw->menu.old_depth; + int new_depth = mw->menu.new_depth; + widget_value** old_stack; + widget_value** new_stack; + window_state* windows; + widget_value* old_selection; + widget_value* new_selection; + + /* Check that enough windows and old_stack are ready. */ + make_windows_if_needed (mw, new_depth); + make_old_stack_space (mw, new_depth); + windows = mw->menu.windows; + old_stack = mw->menu.old_stack; + new_stack = mw->menu.new_stack; + + /* compute the last identical different entry */ + for (i = 1; i < old_depth && i < new_depth; i++) + if (old_stack [i] != new_stack [i]) + break; + last_same = i - 1; + + /* Memorize the previously selected item to be able to refresh it */ + old_selection = last_same + 1 < old_depth ? old_stack [last_same + 1] : NULL; + if (old_selection && !old_selection->enabled) + old_selection = NULL; + new_selection = last_same + 1 < new_depth ? new_stack [last_same + 1] : NULL; + if (new_selection && !new_selection->enabled) + new_selection = NULL; + + /* updates old_state from new_state. It has to be done now because + display_menu (called below) uses the old_stack to know what to display. */ + for (i = last_same + 1; i < new_depth; i++) + old_stack [i] = new_stack [i]; + mw->menu.old_depth = new_depth; + + /* refresh the last seletion */ + selection_position.x = 0; + selection_position.y = 0; + display_menu (mw, last_same, new_selection == old_selection, + &selection_position, NULL, NULL, old_selection, new_selection); + + /* Now popup the new menus */ + for (i = last_same + 1; i < new_depth && new_stack [i]->contents; i++) + { + window_state* previous_ws = &windows [i - 1]; + window_state* ws = &windows [i]; + + ws->x = + previous_ws->x + selection_position.x + mw->menu.shadow_thickness; + if (!mw->menu.horizontal || i > 1) + ws->x += mw->menu.shadow_thickness; + ws->y = + previous_ws->y + selection_position.y + mw->menu.shadow_thickness; + + size_menu (mw, i); + + fit_to_screen (mw, ws, previous_ws, mw->menu.horizontal && i == 1); + + XClearWindow (XtDisplay (mw), ws->window); + XMoveResizeWindow (XtDisplay (mw), ws->window, ws->x, ws->y, + ws->width, ws->height); + XMapRaised (XtDisplay (mw), ws->window); + display_menu (mw, i, False, &selection_position, NULL, NULL, NULL, NULL); + } + + /* unmap the menus that popped down */ + for (i = new_depth - 1; i < old_depth; i++) + if (i >= new_depth || !new_stack [i]->contents) + XUnmapWindow (XtDisplay (mw), windows [i].window); +} + +static Boolean +motion_event_is_in_menu (XlwMenuWidget mw, XMotionEvent* ev, int level, + XPoint* relative_pos) +{ + window_state* ws = &mw->menu.windows [level]; + int x = level == 0 ? ws->x : ws->x + mw->menu.shadow_thickness; + int y = level == 0 ? ws->y : ws->y + mw->menu.shadow_thickness; + relative_pos->x = ev->x_root - x; + relative_pos->y = ev->y_root - y; + return (x < ev->x_root && ev->x_root < x + ws->width + && y < ev->y_root && ev->y_root < y + ws->height); +} + +static Boolean +map_event_to_widget_value (XlwMenuWidget mw, XMotionEvent* ev, + widget_value** val, int* level) +{ + int i; + XPoint relative_pos; + window_state* ws; + + *val = NULL; + + /* Find the window */ + for (i = mw->menu.old_depth - 1; i >= 0; i--) + { + ws = &mw->menu.windows [i]; + if (ws && motion_event_is_in_menu (mw, ev, i, &relative_pos)) + { + display_menu (mw, i, True, NULL, &relative_pos, val, NULL, NULL); + + if (*val) + { + *level = i + 1; + return True; + } + } + } + return False; +} + + /* Procedures */ +static void +make_drawing_gcs (XlwMenuWidget mw) +{ + XGCValues xgcv; + + xgcv.font = mw->menu.font->fid; + xgcv.foreground = mw->menu.foreground; + xgcv.background = mw->core.background_pixel; + mw->menu.foreground_gc = XtGetGC ((Widget)mw, + GCFont | GCForeground | GCBackground, + &xgcv); + + xgcv.font = mw->menu.font->fid; + xgcv.foreground = mw->menu.button_foreground; + xgcv.background = mw->core.background_pixel; + mw->menu.button_gc = XtGetGC ((Widget)mw, + GCFont | GCForeground | GCBackground, + &xgcv); + + xgcv.font = mw->menu.font->fid; + xgcv.foreground = mw->menu.foreground; + xgcv.background = mw->core.background_pixel; + xgcv.fill_style = FillStippled; + xgcv.stipple = mw->menu.gray_pixmap; + mw->menu.inactive_gc = XtGetGC ((Widget)mw, + (GCFont | GCForeground | GCBackground + | GCFillStyle | GCStipple), &xgcv); + + xgcv.font = mw->menu.font->fid; + xgcv.foreground = mw->menu.button_foreground; + xgcv.background = mw->core.background_pixel; + xgcv.fill_style = FillStippled; + xgcv.stipple = mw->menu.gray_pixmap; + mw->menu.inactive_button_gc = XtGetGC ((Widget)mw, + (GCFont | GCForeground | GCBackground + | GCFillStyle | GCStipple), &xgcv); + + xgcv.font = mw->menu.font->fid; + xgcv.foreground = mw->core.background_pixel; + xgcv.background = mw->menu.foreground; + mw->menu.background_gc = XtGetGC ((Widget)mw, + GCFont | GCForeground | GCBackground, + &xgcv); +} + +static void +release_drawing_gcs (XlwMenuWidget mw) +{ + XtReleaseGC ((Widget) mw, mw->menu.foreground_gc); + XtReleaseGC ((Widget) mw, mw->menu.button_gc); + XtReleaseGC ((Widget) mw, mw->menu.inactive_gc); + XtReleaseGC ((Widget) mw, mw->menu.inactive_button_gc); + XtReleaseGC ((Widget) mw, mw->menu.background_gc); + /* let's get some segvs if we try to use these... */ + mw->menu.foreground_gc = (GC) -1; + mw->menu.button_gc = (GC) -1; + mw->menu.inactive_gc = (GC) -1; + mw->menu.inactive_button_gc = (GC) -1; + mw->menu.background_gc = (GC) -1; +} + +#define MINL(x,y) ((((unsigned long) (x)) < ((unsigned long) (y))) \ + ? ((unsigned long) (x)) : ((unsigned long) (y))) + +static void +make_shadow_gcs (XlwMenuWidget mw) +{ + XGCValues xgcv; + unsigned long pm = 0; + Display *dpy = XtDisplay ((Widget) mw); + Colormap cmap = DefaultColormapOfScreen (XtScreen ((Widget) mw)); + XColor topc, botc; + int top_frobbed = 0, bottom_frobbed = 0; + + if (mw->menu.top_shadow_color == -1) + mw->menu.top_shadow_color = mw->core.background_pixel; + if (mw->menu.bottom_shadow_color == -1) + mw->menu.bottom_shadow_color = mw->menu.foreground; + + if (mw->menu.top_shadow_color == mw->core.background_pixel || + mw->menu.top_shadow_color == mw->menu.foreground) + { + topc.pixel = mw->core.background_pixel; + XQueryColor (dpy, cmap, &topc); + /* don't overflow/wrap! */ + topc.red = MINL (65535, topc.red * 1.2); + topc.green = MINL (65535, topc.green * 1.2); + topc.blue = MINL (65535, topc.blue * 1.2); + if (XAllocColor (dpy, cmap, &topc)) + { + mw->menu.top_shadow_color = topc.pixel; + top_frobbed = 1; + } + } + if (mw->menu.bottom_shadow_color == mw->menu.foreground || + mw->menu.bottom_shadow_color == mw->core.background_pixel) + { + botc.pixel = mw->core.background_pixel; + XQueryColor (dpy, cmap, &botc); + botc.red *= 0.6; + botc.green *= 0.6; + botc.blue *= 0.6; + if (XAllocColor (dpy, cmap, &botc)) + { + mw->menu.bottom_shadow_color = botc.pixel; + bottom_frobbed = 1; + } + } + + if (top_frobbed && bottom_frobbed) + { + int top_avg = ((topc.red / 3) + (topc.green / 3) + (topc.blue / 3)); + int bot_avg = ((botc.red / 3) + (botc.green / 3) + (botc.blue / 3)); + if (bot_avg > top_avg) + { + Pixel tmp = mw->menu.top_shadow_color; + mw->menu.top_shadow_color = mw->menu.bottom_shadow_color; + mw->menu.bottom_shadow_color = tmp; + } + else if (topc.pixel == botc.pixel) + { + if (botc.pixel == mw->menu.foreground) + mw->menu.top_shadow_color = mw->core.background_pixel; + else + mw->menu.bottom_shadow_color = mw->menu.foreground; + } + } + + if (!mw->menu.top_shadow_pixmap && + mw->menu.top_shadow_color == mw->core.background_pixel) + { + mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap; + mw->menu.top_shadow_color = mw->menu.foreground; + } + if (!mw->menu.bottom_shadow_pixmap && + mw->menu.bottom_shadow_color == mw->core.background_pixel) + { + mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap; + mw->menu.bottom_shadow_color = mw->menu.foreground; + } + + xgcv.fill_style = FillStippled; + xgcv.foreground = mw->menu.top_shadow_color; + xgcv.stipple = mw->menu.top_shadow_pixmap; + pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0); + mw->menu.shadow_top_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv); + + xgcv.foreground = mw->menu.bottom_shadow_color; + xgcv.stipple = mw->menu.bottom_shadow_pixmap; + pm = (xgcv.stipple ? GCStipple|GCFillStyle : 0); + mw->menu.shadow_bottom_gc = XtGetGC ((Widget)mw, GCForeground | pm, &xgcv); +} + + +static void +release_shadow_gcs (XlwMenuWidget mw) +{ + XtReleaseGC ((Widget) mw, mw->menu.shadow_top_gc); + XtReleaseGC ((Widget) mw, mw->menu.shadow_bottom_gc); +} + +static void +XlwMenuInitialize (Widget request, Widget new, ArgList args, + Cardinal *num_args) +{ + /* Get the GCs and the widget size */ + XlwMenuWidget mw = (XlwMenuWidget)new; + + XSetWindowAttributes xswa; + int mask; + + Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw))); + Display* display = XtDisplay (mw); + +/* mw->menu.cursor = XCreateFontCursor (display, mw->menu.cursor_shape); */ + mw->menu.cursor = mw->menu.cursor_shape; + + mw->menu.gray_pixmap = XCreatePixmapFromBitmapData (display, window, + gray_bits, gray_width, + gray_height, 1, 0, 1); + + make_drawing_gcs (mw); + make_shadow_gcs (mw); + + xswa.background_pixel = mw->core.background_pixel; + xswa.border_pixel = mw->core.border_pixel; + mask = CWBackPixel | CWBorderPixel; + + mw->menu.popped_up = False; + + mw->menu.old_depth = 1; + mw->menu.old_stack = (widget_value**)XtMalloc (sizeof (widget_value*)); + mw->menu.old_stack_length = 1; + mw->menu.old_stack [0] = mw->menu.contents; + + mw->menu.new_depth = 0; + mw->menu.new_stack = 0; + mw->menu.new_stack_length = 0; + push_new_stack (mw, mw->menu.contents); + + mw->menu.windows = (window_state*)XtMalloc (sizeof (window_state)); + mw->menu.windows_length = 1; + mw->menu.windows [0].x = 0; + mw->menu.windows [0].y = 0; + mw->menu.windows [0].width = 0; + mw->menu.windows [0].height = 0; + size_menu (mw, 0); + + mw->core.width = mw->menu.windows [0].width; + mw->core.height = mw->menu.windows [0].height; +} + +static void +XlwMenuClassInitialize () +{ +} + +static void +XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes) +{ + XlwMenuWidget mw = (XlwMenuWidget)w; + XSetWindowAttributes xswa; + int mask; + + (*xlwMenuWidgetClass->core_class.superclass->core_class.realize) + (w, valueMask, attributes); + + xswa.save_under = True; + xswa.cursor = mw->menu.cursor_shape; + mask = CWSaveUnder | CWCursor; + XChangeWindowAttributes (XtDisplay (w), XtWindow (w), mask, &xswa); + + mw->menu.windows [0].window = XtWindow (w); + mw->menu.windows [0].x = w->core.x; + mw->menu.windows [0].y = w->core.y; + mw->menu.windows [0].width = w->core.width; + mw->menu.windows [0].height = w->core.height; +} + +/* Only the toplevel menubar/popup is a widget so it's the only one that + receives expose events through Xt. So we repaint all the other panes + when receiving an Expose event. */ +static void +XlwMenuRedisplay (Widget w, XEvent* ev, Region region) +{ + XlwMenuWidget mw = (XlwMenuWidget)w; + int i; + + for (i = 0; i < mw->menu.old_depth; i++) + display_menu (mw, i, False, NULL, NULL, NULL, NULL, NULL); +} + +static void +XlwMenuDestroy (Widget w) +{ + int i; + XlwMenuWidget mw = (XlwMenuWidget) w; + + release_drawing_gcs (mw); + release_shadow_gcs (mw); + + /* this doesn't come from the resource db but is created explicitly + so we must free it ourselves. */ + XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap); + mw->menu.gray_pixmap = (Pixmap) -1; + + /* Don't free mw->menu.contents because that comes from our creator. + The `*_stack' elements are just pointers into `contents' so leave + that alone too. But free the stacks themselves. */ + if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack); + if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack); + + /* Remember, you can't free anything that came from the resource + database. This includes: + mw->menu.cursor + mw->menu.top_shadow_pixmap + mw->menu.bottom_shadow_pixmap + mw->menu.font + Also the color cells of top_shadow_color, bottom_shadow_color, + foreground, and button_foreground will never be freed until this + client exits. Nice, eh? + */ + + /* start from 1 because the one in slot 0 is w->core.window */ + for (i = 1; i < mw->menu.windows_length; i++) + XDestroyWindow (XtDisplay (mw), mw->menu.windows [i].window); + if (mw->menu.windows) + XtFree ((char *) mw->menu.windows); +} + +static Boolean +XlwMenuSetValues (Widget current, Widget request, Widget new) +{ + XlwMenuWidget oldmw = (XlwMenuWidget)current; + XlwMenuWidget newmw = (XlwMenuWidget)new; + Boolean redisplay = False; + int i; + + if (newmw->menu.contents + && newmw->menu.contents->contents + && newmw->menu.contents->contents->change >= VISIBLE_CHANGE) + redisplay = True; + + if (newmw->core.background_pixel != oldmw->core.background_pixel + || newmw->menu.foreground != oldmw->menu.foreground) + { + release_drawing_gcs (newmw); + make_drawing_gcs (newmw); + redisplay = True; + + for (i = 0; i < oldmw->menu.windows_length; i++) + { + XSetWindowBackground (XtDisplay (oldmw), + oldmw->menu.windows [i].window, + newmw->core.background_pixel); + /* clear windows and generate expose events */ + XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window, + 0, 0, 0, 0, True); + } + } + + return redisplay; +} + +static void +XlwMenuResize (Widget w) +{ + XlwMenuWidget mw = (XlwMenuWidget)w; + + mw->menu.windows [0].width = mw->core.width; + mw->menu.windows [0].height = mw->core.height; +} + + /* Action procedures */ +static void +handle_single_motion_event (XlwMenuWidget mw, XMotionEvent* ev) +{ + widget_value* val; + int level; + + if (!map_event_to_widget_value (mw, ev, &val, &level)) + pop_new_stack_if_no_contents (mw); + else + set_new_state (mw, val, level); + remap_menubar (mw); + + /* Sync with the display. Makes it feel better on X terms. */ + XSync (XtDisplay (mw), False); +} + +static void +handle_motion_event (XlwMenuWidget mw, XMotionEvent* ev) +{ + int x = ev->x_root; + int y = ev->y_root; + int state = ev->state; + + handle_single_motion_event (mw, ev); + + /* allow motion events to be generated again */ + if (ev->is_hint + && XQueryPointer (XtDisplay (mw), ev->window, + &ev->root, &ev->subwindow, + &ev->x_root, &ev->y_root, + &ev->x, &ev->y, + &ev->state) + && ev->state == state + && (ev->x_root != x || ev->y_root != y)) + handle_single_motion_event (mw, ev); +} + +static void +Start (Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + XlwMenuWidget mw = (XlwMenuWidget)w; + + XtCallCallbackList ((Widget)mw, mw->menu.open, NULL); + + /* notes the absolute position of the menubar window */ + mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x; + mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y; + + /* handles the down like a move, slots are compatible */ + handle_motion_event (mw, &ev->xmotion); +} + +static void +Drag (Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + XlwMenuWidget mw = (XlwMenuWidget)w; + handle_motion_event (mw, &ev->xmotion); +} + +static void +Select (Widget w, XEvent *ev, String *params, Cardinal *num_params) +{ + XlwMenuWidget mw = (XlwMenuWidget)w; + widget_value* selected_item = mw->menu.old_stack [mw->menu.old_depth - 1]; + + /* pop down everything */ + mw->menu.new_depth = 1; + remap_menubar (mw); + + if (mw->menu.popped_up) + { + mw->menu.popped_up = False; + XtUngrabPointer ((Widget)mw, ev->xmotion.time); + XtPopdown (XtParent (mw)); + } + + /* callback */ + XtCallCallbackList ((Widget)mw, mw->menu.select, (XtPointer)selected_item); + +} + + + /* Special code to pop-up a menu */ +void +pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent* event) +{ + int x = event->x_root; + int y = event->y_root; + int w; + int h; + int borderwidth = mw->menu.shadow_thickness; + Screen* screen = XtScreen (mw); + + XtCallCallbackList ((Widget)mw, mw->menu.open, NULL); + + size_menu (mw, 0); + + w = mw->menu.windows [0].width; + h = mw->menu.windows [0].height; + + x -= borderwidth; + y -= borderwidth; + if (x < borderwidth) + x = borderwidth; + if (x + w + 2 * borderwidth > WidthOfScreen (screen)) + x = WidthOfScreen (screen) - w - 2 * borderwidth; + if (y < borderwidth) + y = borderwidth; + if (y + h + 2 * borderwidth> HeightOfScreen (screen)) + y = HeightOfScreen (screen) - h - 2 * borderwidth; + + mw->menu.popped_up = True; + XtConfigureWidget (XtParent (mw), x, y, w, h, + XtParent (mw)->core.border_width); + XtPopup (XtParent (mw), XtGrabExclusive); + display_menu (mw, 0, False, NULL, NULL, NULL, NULL, NULL); + XtGrabPointer ((Widget)mw, False, + (ButtonMotionMask | PointerMotionHintMask | ButtonReleaseMask + | ButtonPressMask), + GrabModeAsync, GrabModeAsync, None, mw->menu.cursor_shape, + event->time); + + mw->menu.windows [0].x = x + borderwidth; + mw->menu.windows [0].y = y + borderwidth; + + handle_motion_event (mw, (XMotionEvent*)event); +} diff --git a/lwlib/xlwmenu.h b/lwlib/xlwmenu.h new file mode 100644 index 00000000000..e8d3c93778f --- /dev/null +++ b/lwlib/xlwmenu.h @@ -0,0 +1,50 @@ +#ifndef _XlwMenu_h +#define _XlwMenu_h + +/*********************************************************************** + * + * XlwMenu Widget + * + ***********************************************************************/ + +#include "lwlib.h" + +/* Resource names used by the XlwMenu widget */ +#define XtNbuttonForeground "buttonForeground" +#define XtCButtonForeground "ButtonForeground" +#define XtNmargin "margin" +#define XtNhorizontalSpacing "horizontalSpacing" +#define XtNverticalSpacing "verticalSpacing" +#define XtNarrowSpacing "arrowSpacing" +#define XtNmenu "menu" +#define XtCMenu "Menu" +#define XtNopen "open" +#define XtNselect "select" +#define XtNmenuBorderWidth "menuBorderWidth" +#define XtNhorizontal "horizontal" +#define XtCHorizontal "Horizontal" +#define XtNcursor "cursor" +#define XtNCursor "Cursor" + +/* Motif-compatible resource names */ +#define XmNshadowThickness "shadowThickness" +#define XmCShadowThickness "ShadowThickness" +#define XmNtopShadowColor "topShadowColor" +#define XmCTopShadowColor "TopShadowColor" +#define XmNbottomShadowColor "bottomShadowColor" +#define XmCBottomShadowColor "BottomShadowColor" +#define XmNtopShadowPixmap "topShadowPixmap" +#define XmCTopShadowPixmap "TopShadowPixmap" +#define XmNbottomShadowPixmap "bottomShadowPixmap" +#define XmCBottomShadowPixmap "BottomShadowPixmap" +#define XmRHorizontalDimension "HorizontalDimension" + +typedef struct _XlwMenuRec *XlwMenuWidget; +typedef struct _XlwMenuClassRec *XlwMenuWidgetClass; + +extern WidgetClass xlwMenuWidgetClass; + +void +pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent* event); + +#endif /* _XlwMenu_h */ -- cgit v1.2.1