Mercurial > hg > xemacs-beta
view lwlib/lwlib-Xm.c @ 814:a634e3b7acc8
[xemacs-hg @ 2002-04-14 12:41:59 by ben]
latest changes
TODO.ben-mule-21-5: Update.
make-docfile.c: Add basic support for handling ISO 2022 doc strings -- we parse
the basic charset designation sequences so we know whether we're
in ASCII and have to pay attention to end quotes and such.
Reformat code according to coding standards.
abbrev.el: Add `global-abbrev-mode', which turns on or off abbrev-mode in all
buffers. Added `defining-abbrev-turns-on-abbrev-mode' -- if
non-nil, defining an abbrev through an interactive function will
automatically turn on abbrev-mode, either globally or locally
depending on the command. This is the "what you'd expect"
behavior.
indent.el: general function for indenting a balanced expression in a
mode-correct way. Works similar to indent-region in that a mode
can specify a specific command to do the whole operation; if not,
figure out the region using forward-sexp and indent each line
using indent-according-to-mode.
keydefs.el: Removed.
Modify M-C-backslash to do indent-region-or-balanced-expression.
Make S-Tab just insert a TAB char, like it's meant to do.
make-docfile.el: Now that we're using the call-process-in-lisp, we need to load
an extra file win32-native.el because we're running a bare temacs.
menubar-items.el: Totally redo the Cmds menu so that most used commands appear
directly on the menu and less used commands appear in submenus.
The old way may have been very pretty, but rather impractical.
process.el: Under Windows, don't ever use old-call-process-internal, even
in batch mode. We can do processes in batch mode.
subr.el: Someone recoded truncate-string-to-width, saying "the FSF version
is too complicated and does lots of hard-to-understand stuff" but
the resulting recoded version was *totally* wrong! it
misunderstood the basic point of this function, which is work in
*columns* not chars. i dumped ours and copied the version from
FSF 21.1. Also added truncate-string-with-continuation-dots,
since this idiom is used often.
config.inc.samp, xemacs.mak: Separate out debug and optimize flags.
Remove all vestiges of USE_MINIMAL_TAGBITS,
USE_INDEXED_LRECORD_IMPLEMENTATION, and GUNG_HO, since those
ifdefs have long been removed.
Make error-checking support actually work.
Some rearrangement of config.inc.samp to make it more logical.
Remove callproc.c and ntproc.c from xemacs.mak, no longer used.
Make pdump the default.
lisp.h: Add support for strong type-checking of Bytecount, Bytebpos,
Charcount, Charbpos, and others, by making them classes,
overloading the operators to provide integer-like operation and
carefully controlling what operations are allowed. Not currently
enabled in C++ builds because there are still a number of compile
errors, and it won't really work till we merge in my "8-bit-Mule"
workspace, in which I make use of the new types Charxpos,
Bytexpos, Memxpos, representing a "position" either in a buffer or
a string. (This is especially important in the extent code.)
abbrev.c, alloc.c, eval.c, buffer.c, buffer.h, editfns.c, fns.c, text.h: Warning fixes, some of them related to new C++ strict type
checking of Bytecount, Charbpos, etc.
dired.c: Caught an actual error due to strong type checking -- char len
being passed when should be byte len.
alloc.c, backtrace.h, bytecode.c, bytecode.h, eval.c, sysdep.c: Further optimize Ffuncall:
-- process arg list at compiled-function creation time, converting
into an array for extra-quick access at funcall time.
-- rewrite funcall_compiled_function to use it, and inline this
function.
-- change the order of check for magic stuff in
SPECBIND_FAST_UNSAFE to be faster.
-- move the check for need to garbage collect into the allocation
code, so only a single flag needs to be checked in funcall.
buffer.c, symbols.c: add debug funs to check on mule optimization info in buffers and
strings.
eval.c, emacs.c, text.c, regex.c, scrollbar-msw.c, search.c: Fix evil crashes due to eistrings not properly reinitialized under
pdump. Redo a bit some of the init routines; convert some
complex_vars_of() into simple vars_of(), because they didn't need
complex processing.
callproc.c, emacs.c, event-stream.c, nt.c, process.c, process.h, sysdep.c, sysdep.h, syssignal.h, syswindows.h, ntproc.c: Delete. Hallelujah, praise the Lord, there is no god
but Allah!!!
fix so that processes can be invoked in bare temacs -- thereby
eliminating any need for callproc.c. (currently only eliminated
under NT.) remove all crufty and unnecessary old process code in
ntproc.c and elsewhere. move non-callproc-specific stuff (mostly
environment) into process.c, so callproc.c can be left out under
NT.
console-tty.c, doc.c, file-coding.c, file-coding.h, lstream.c, lstream.h: fix doc string handling so it works with Japanese, etc docs.
change handling of "character mode" so callers don't have to
manually set it (quite error-prone).
event-msw.c: spacing fixes.
lread.c: eliminate unused crufty vintage-19 "FSF defun hack" code.
lrecord.h: improve pdump description docs.
buffer.c, ntheap.c, unexnt.c, win32.c, emacs.c: Mule-ize some unexec and startup code. It was pseudo-Mule-ized
before by simply always calling the ...A versions of functions,
but that won't cut it -- eventually we want to be able to run
properly even if XEmacs has been installed in a Japanese
directory. (The current problem is the timing of the loading of
the Unicode tables; this will eventually be fixed.) Go through and
fix various other places where the code was not Mule-clean.
Provide a function mswindows_get_module_file_name() to get our own
name without resort to PATH_MAX and such. Add a big comment in
main() about the problem with Unicode table load timing that I
just alluded to.
emacs.c: When error-checking is enabled (interpreted as "user is developing
XEmacs"), don't ask user to "pause to read messages" when a fatal
error has occurred, because it will wedge if we are in an inner
modal loop (typically when a menu is popped up) and make us unable
to get a useful stack trace in the debugger.
text.c: Correct update_entirely_ascii_p_flag to actually work.
lisp.h, symsinit.h: declarations for above changes.
author | ben |
---|---|
date | Sun, 14 Apr 2002 12:43:31 +0000 |
parents | e5083672c894 |
children | 4a27df428c73 |
line wrap: on
line source
/* The lwlib interface to Motif widgets. Copyright (C) 1992, 1993, 1994 Lucid, Inc. Copyright (C) 1995 Tinker Systems and INS Engineering Corp. 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 XEmacs; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <config.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <limits.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include <X11/StringDefs.h> #include <X11/IntrinsicP.h> #include <X11/ObjectP.h> #include <X11/CoreP.h> #include <X11/CompositeP.h> #include "lwlib-Xm.h" #include "lwlib-utils.h" #include <Xm/Xm.h> #include <Xm/BulletinB.h> #include <Xm/CascadeB.h> #include <Xm/DrawingA.h> #include <Xm/FileSB.h> #include <Xm/Label.h> #include <Xm/List.h> #include <Xm/MenuShell.h> #include <Xm/MessageB.h> #include <Xm/PushB.h> #include <Xm/PushBG.h> #include <Xm/ArrowB.h> #include <Xm/ScrollBar.h> #include <Xm/SelectioB.h> #include <Xm/Text.h> #include <Xm/TextF.h> #include <Xm/ToggleB.h> #include <Xm/ToggleBG.h> #include <Xm/RowColumn.h> #include <Xm/ScrolledW.h> #include <Xm/Separator.h> #include <Xm/DialogS.h> #include <Xm/Form.h> #ifdef LWLIB_WIDGETS_MOTIF #include <Xm/Scale.h> #if XmVERSION > 1 #include <Xm/ComboBoxP.h> #endif #endif #ifdef LWLIB_MENUBARS_MOTIF static void xm_pull_down_callback (Widget, XtPointer, XtPointer); #endif static void xm_internal_update_other_instances (Widget, XtPointer, XtPointer); static void xm_pop_down_callback (Widget, XtPointer, XtPointer); static void xm_generic_callback (Widget, XtPointer, XtPointer); static void mark_dead_instance_destroyed (Widget widget, XtPointer closure, XtPointer call_data); #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) static void xm_nosel_callback (Widget, XtPointer, XtPointer); #endif #ifdef LWLIB_SCROLLBARS_MOTIF static void xm_scrollbar_callback (Widget, XtPointer, XtPointer); #endif #ifdef LWLIB_MENUBARS_MOTIF static void xm_update_menu (widget_instance* instance, Widget widget, widget_value* val, Boolean deep_p); #endif /* 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; /* Utility function. */ 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 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 = safe_strdup (name); instance->type = safe_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 #ifdef LWLIB_DIALOGS_MOTIF XtClass (widget) == xmDialogShellWidgetClass || #endif XmIsPrimitive (widget) || XmIsManager (widget) || XmIsGadget (widget); } static char * resource_string (Widget widget, char *name) { XtResource resource; char *result = NULL; resource.resource_name = "labelString"; resource.resource_class = "LabelString"; /* #### should be Xmsomething... */ resource.resource_type = XtRString; resource.resource_size = sizeof (String); resource.resource_offset = 0; resource.default_type = XtRImmediate; resource.default_addr = 0; XtGetSubresources (widget, (XtPointer)&result, name, name, &resource, 1, NULL, 0); return result; } #ifdef LWLIB_DIALOGS_MOTIF static Boolean is_in_dialog_box (Widget w) { Widget wmshell; wmshell = XtParent (w); while (wmshell && (XtClass (wmshell) != xmDialogShellWidgetClass)) wmshell = XtParent (wmshell); if (wmshell && XtClass (wmshell) == xmDialogShellWidgetClass) return True; else return False; } #endif /* LWLIB_DIALOGS_MOTIF */ #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) /* update the label of anything subclass of a label */ static void xm_update_label (widget_instance* instance, Widget widget, widget_value* val) { XmString built_string = NULL; XmString key_string = NULL; XmString val_string = NULL; XmString name_string = NULL; Arg al [20]; int ac = 0; int type; /* Don't clobber pixmap types. */ XtSetArg (al [0], XmNlabelType, &type); XtGetValues (widget, al, 1); if (type == XmPIXMAP) return; if (val->value) { /* #### Temporary fix. I though Motif was supposed to grok %_ type things. */ lw_remove_accelerator_spec (val->value); #ifdef LWLIB_DIALOGS_MOTIF /* * Sigh. The main text of a label is the name field for menubar * entries. The value field is a possible additional field to be * concatenated on to the name field. HOWEVER, with dialog boxes * the value field is the complete text which is supposed to be * displayed as the label. Yuck. */ if (is_in_dialog_box (widget)) { char *value_name = NULL; value_name = resource_string (widget, val->value); if (!value_name) value_name = val->value; built_string = XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET); } else #endif /* LWLIB_DIALOGS_MOTIF */ { char *value_name = NULL; char *res_name = NULL; res_name = resource_string (widget, val->name); /* Concatenating the value with itself seems just plain daft. */ if (!res_name) { built_string = XmStringCreateLtoR (val->value, XmSTRING_DEFAULT_CHARSET); } else { name_string = XmStringCreateLtoR (res_name, XmSTRING_DEFAULT_CHARSET); value_name = XtMalloc (strlen (val->value) + 2); *value_name = 0; strcat (value_name, " "); strcat (value_name, val->value); val_string = XmStringCreateLtoR (value_name, XmSTRING_DEFAULT_CHARSET); built_string = XmStringConcat (name_string, val_string); XtFree (value_name); } } 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); if (name_string) XmStringFree (name_string); if (val_string) XmStringFree (val_string); } #endif /* defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) */ /* 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) { Arg al [1]; XtSetArg (al [0], XmNalignment, XmALIGNMENT_CENTER); XtSetValues (widget, al, 1); XtRemoveAllCallbacks (widget, XmNactivateCallback); XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); } static void xm_update_progress (widget_instance* instance, Widget scale, widget_value* val) { Arg al[20]; int ac = 0; Dimension height = 0; Dimension width = 0; if (!val->call_data) { XtSetArg (al [ac], XmNeditable, False); ac++; } else { XtSetArg (al [ac], XmNeditable, val->enabled); ac++; } height = (Dimension)lw_get_value_arg (val, XtNheight); width = (Dimension)lw_get_value_arg (val, XtNwidth); if (height > 0) { XtSetArg (al [ac], XmNscaleHeight, height); ac++; } if (width > 0) { XtSetArg (al [ac], XmNscaleWidth, width); ac++; } XtSetValues (scale, al, 1); } #ifdef LWLIB_MENUBARS_MOTIF static void xm_update_cascadebutton (widget_instance* instance, Widget widget, widget_value* val) { /* Should also rebuild the menu by calling ...update_menu... */ if (val && val->type == CASCADE_TYPE && val->contents && val->contents->type == INCREMENTAL_TYPE) { /* okay, we're now doing a lisp callback to incrementally generate more of the menu. */ XtRemoveAllCallbacks (widget, XmNcascadingCallback); XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback, instance); XtCallCallbacks ((Widget)widget, XmNcascadingCallback, (XtPointer)val->contents); } else { XtRemoveAllCallbacks (widget, XmNcascadingCallback); XtAddCallback (widget, XmNcascadingCallback, xm_pull_down_callback, instance); } } #endif /* LWLIB_MENUBARS_MOTIF */ /* update toggle and radiobox */ static void xm_update_toggle (widget_instance* instance, Widget widget, widget_value* val) { Arg al [2]; XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); XtAddCallback (widget, XmNvalueChangedCallback, xm_generic_callback, instance); XtSetArg (al [0], XmNset, val->selected); XtSetArg (al [1], XmNalignment, XmALIGNMENT_BEGINNING); XtSetValues (widget, al, 1); } 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) { Arg al [2]; XtSetArg (al [0], XmNsensitive, cur->enabled); XtSetArg (al [1], XmNset, (!val->value && cur->selected ? cur->selected : False)); XtSetValues (toggle, al, 2); } } /* The selected was specified by the value slot */ if (val->value) { toggle = XtNameToWidget (widget, val->value); if (toggle) { Arg al [1]; XtSetArg (al [0], XmNset, True); XtSetValues (toggle, al, 1); } } } #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 /* update of combo box */ static void xm_update_combo_box (widget_instance* instance, Widget widget, widget_value* val) { widget_value* cur; int i; XtRemoveAllCallbacks (widget, XmNselectionCallback); XtAddCallback (widget, XmNselectionCallback, 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 (CB_List (widget), xmstr, 0); if (cur->selected) XmListSelectPos (CB_List (widget), i, False); XmStringFree (xmstr); } } #endif #ifdef LWLIB_MENUBARS_MOTIF /* update a popup menu, pulldown menu or a menubar */ 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 = False; /* 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... */ XtSetArg (al [0], XmNisHomogeneous, &menubar_p); XtGetValues (widget, al, 1); /* add the unmap callback for popups and pulldowns */ /*** this sounds bogus ***/ /* probably because it is -- cet */ /* if (!menubar_p) XtAddCallback (XtParent (widget), XmNpopdownCallback, xm_pop_down_callback, (XtPointer)instance); */ num_children = 0; for (child_index = 0, cur = val; cur; child_index++, cur = cur->next) { ac = 0; button = 0; XtSetArg (al [ac], XmNsensitive, cur->enabled); ac++; XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; XtSetArg (al [ac], XmNuserData, cur->call_data); ac++; switch (cur->type) { case PUSHRIGHT_TYPE: /* A pushright marker which is not needed for the real Motif menubar. */ break; case SEPARATOR_TYPE: ac = 0; if (cur->value) { /* #### - xlwmenu.h supports several types that motif does not. Also, motif supports pixmaps w/ type NO_LINE and lwlib provides no way to access that functionality. --Stig */ XtSetArg (al [ac], XmNseparatorType, cur->value), ac++; } button = XmCreateSeparator (widget, "separator", al, ac); break; case CASCADE_TYPE: 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); break; default: if (menubar_p) button = XmCreateCascadeButton (widget, cur->name, al, ac); else if (!cur->call_data) button = XmCreateLabel (widget, cur->name, al, ac); else if (cur->type == TOGGLE_TYPE || cur->type == RADIO_TYPE) { XtSetArg (al [ac], XmNindicatorType, (cur->type == TOGGLE_TYPE ? XmN_OF_MANY : XmONE_OF_MANY)); ac++; XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++; button = XmCreateToggleButtonGadget (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->type == TOGGLE_TYPE || cur->type == RADIO_TYPE) xm_update_toggle (instance, button, cur); else if (cur->call_data) XtAddCallback (button, XmNactivateCallback, xm_generic_callback, (XtPointer)instance); } /* switch (cur->type) */ if (button) children [num_children++] = button; } /* Last entry is the help button. This used be done after managing the buttons. The comment claimed that it had to be done this way otherwise the menubar ended up only 4 pixels high. That must have been in the Old World. In the New World it stays the proper height if you don't manage them until after you set this and as a bonus the Help menu ends up where it is supposed to. */ if (button) { ac = 0; XtSetArg (al [ac], XmNmenuHelpWidget, button); ac++; XtSetValues (widget, al, ac); } if (num_children) XtManageChildren (children, num_children); XtFree ((char *) children); } static void update_one_menu_entry (widget_instance* instance, Widget widget, widget_value* val, Boolean deep_p) { Arg al [2]; int ac; Widget menu; widget_value* contents; if (val->change == NO_CHANGE) return; /* update the sensitivity and userdata */ /* Common to all widget types */ XtSetArg (al [0], XmNsensitive, val->enabled); XtSetArg (al [1], XmNuserData, val->call_data); XtSetValues (widget, al, 2); /* update the menu button as a label. */ if (val->change >= VISIBLE_CHANGE) { xm_update_label (instance, widget, val); if (XtClass (widget) == xmToggleButtonWidgetClass || XtClass (widget) == xmToggleButtonGadgetClass) { xm_update_toggle (instance, widget, val); } } /* update the pulldown/pullaside as needed */ menu = NULL; XtSetArg (al [0], XmNsubMenuId, &menu); XtGetValues (widget, al, 1); 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 = 0; children = XtCompositeChildren (widget, &num_children); if (children) { for (i = 0, cur = val->contents; i < num_children; i++) { if (!cur) abort (); /* skip if this is a pushright marker or a separator */ if (cur->type == PUSHRIGHT_TYPE || cur->type == SEPARATOR_TYPE) { cur = cur->next; #if 0 /* #### - this could puke if you have a separator as the last item on a pullright menu. */ if (!cur) abort (); #else if (!cur) continue; #endif } 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 (); } } #endif /* LWLIB_MENUBARS_MOTIF */ /* update text widgets */ static void xm_update_text (widget_instance* instance, Widget widget, widget_value* val) { XmTextSetString (widget, val->value ? val->value : (char *) ""); 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 : (char *) ""); XtRemoveAllCallbacks (widget, XmNactivateCallback); XtAddCallback (widget, XmNactivateCallback, xm_generic_callback, instance); XtRemoveAllCallbacks (widget, XmNvalueChangedCallback); XtAddCallback (widget, XmNvalueChangedCallback, xm_internal_update_other_instances, instance); } #ifdef LWLIB_SCROLLBARS_MOTIF /* * If this function looks like it does a lot more work than it needs to, * you're right. Blame the Motif scrollbar for not being smart about * updating its appearance. */ static void xm_update_scrollbar (widget_instance *instance, Widget widget, widget_value *val) { if (val->scrollbar_data) { scrollbar_values *data = val->scrollbar_data; int widget_sliderSize, widget_val; int new_sliderSize, new_value; double percent; double h_water, l_water; Arg al [4]; /* First size and position the scrollbar widget. */ XtSetArg (al [0], XtNx, data->scrollbar_x); XtSetArg (al [1], XtNy, data->scrollbar_y); XtSetArg (al [2], XtNwidth, data->scrollbar_width); XtSetArg (al [3], XtNheight, data->scrollbar_height); XtSetValues (widget, al, 4); /* Now size the scrollbar's slider. */ XtSetArg (al [0], XmNsliderSize, &widget_sliderSize); XtSetArg (al [1], XmNvalue, &widget_val); XtGetValues (widget, al, 2); percent = (double) data->slider_size / (double) (data->maximum - data->minimum); new_sliderSize = (int) ((double) (INT_MAX - 1) * percent); percent = (double) (data->slider_position - data->minimum) / (double) (data->maximum - data->minimum); new_value = (int) ((double) (INT_MAX - 1) * percent); if (new_sliderSize > (INT_MAX - 1)) new_sliderSize = INT_MAX - 1; else if (new_sliderSize < 1) new_sliderSize = 1; if (new_value > (INT_MAX - new_sliderSize)) new_value = INT_MAX - new_sliderSize; else if (new_value < 1) new_value = 1; h_water = 1.05; l_water = 0.95; if (new_sliderSize != widget_sliderSize || new_value != widget_val) { int force = ((INT_MAX - widget_sliderSize - widget_val) ? 0 : (INT_MAX - new_sliderSize - new_value)); if (force || (double)new_sliderSize < (l_water * (double)widget_sliderSize) || (double)new_sliderSize > (h_water * (double)widget_sliderSize) || (double)new_value < (l_water * (double)widget_val) || (double)new_value > (h_water * (double)widget_val)) { XmScrollBarSetValues (widget, new_value, new_sliderSize, 1, 1, False); } } } } #endif /* LWLIB_SCROLLBARS_MOTIF */ /* update a motif widget */ void xm_update_one_widget (widget_instance* instance, Widget widget, widget_value* val, Boolean deep_p) { WidgetClass class; Arg al [20]; int ac = 0; /* Mark as not edited */ val->edited = False; /* Common to all widget types */ XtSetArg (al [ac], XmNsensitive, val->enabled); ac++; XtSetArg (al [ac], XmNuserData, val->call_data); ac++; XtSetValues (widget, al, ac); #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) /* Common to all label like widgets */ if (XtIsSubclass (widget, xmLabelWidgetClass)) xm_update_label (instance, widget, val); #endif class = XtClass (widget); /* Class specific things */ if (class == xmPushButtonWidgetClass || class == xmArrowButtonWidgetClass) { xm_update_pushbutton (instance, widget, val); } #ifdef LWLIB_MENUBARS_MOTIF else if (class == xmCascadeButtonWidgetClass) { xm_update_cascadebutton (instance, widget, val); } #endif else if (class == xmToggleButtonWidgetClass || class == xmToggleButtonGadgetClass) { xm_update_toggle (instance, widget, val); } else if (class == xmRowColumnWidgetClass) { Boolean radiobox = 0; XtSetArg (al [0], XmNradioBehavior, &radiobox); XtGetValues (widget, al, 1); if (radiobox) xm_update_radiobox (instance, widget, val); #ifdef LWLIB_MENUBARS_MOTIF else xm_update_menu (instance, widget, val, deep_p); #endif } 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); } #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 else if (class == xmComboBoxWidgetClass) { xm_update_combo_box (instance, widget, val); } #endif #ifdef LWLIB_SCROLLBARS_MOTIF else if (class == xmScrollBarWidgetClass) { xm_update_scrollbar (instance, widget, val); } #endif else if (class == xmScaleWidgetClass) { xm_update_progress (instance, widget, val); } /* Lastly update our global arg values. */ if (val->args && val->args->nargs) XtSetValues (widget, val->args->args, val->args->nargs); } /* 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) { Arg al [1]; XtSetArg (al [0], XmNset, &val->selected); XtGetValues (widget, al, 1); val->edited = True; } else if (class == xmTextWidgetClass) { if (val->value) XtFree (val->value); val->value = XmTextGetString (widget); val->edited = True; } else if (class == xmTextFieldWidgetClass) { if (val->value) XtFree (val->value); val->value = XmTextFieldGetString (widget); val->edited = True; } else if (class == xmRowColumnWidgetClass) { Boolean radiobox = 0; { Arg al [1]; XtSetArg (al [0], XmNradioBehavior, &radiobox); XtGetValues (widget, al, 1); } if (radiobox) { CompositeWidget radio = (CompositeWidget)widget; unsigned int i; for (i = 0; i < radio->composite.num_children; i++) { int set = False; Widget toggle = radio->composite.children [i]; Arg al [1]; XtSetArg (al [0], XmNset, &set); XtGetValues (toggle, al, 1); if (set) { if (val->value) free (val->value); val->value = safe_strdup (XtName (toggle)); } } val->edited = True; } } else if (class == xmListWidgetClass #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 || class == xmComboBoxWidgetClass #endif ) { int pos_cnt; int* pos_list; Widget list = widget; #if defined (LWLIB_WIDGETS_MOTIF) && XmVERSION > 1 if (class == xmComboBoxWidgetClass) list = CB_List (widget); #endif if (XmListGetSelectedPos (list, &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 = safe_strdup (cur->name); } } val->edited = 1; XtFree ((char *) pos_list); } } #ifdef LWLIB_SCROLLBARS_MOTIF else if (class == xmScrollBarWidgetClass) { /* This function is not used by the scrollbar. */ return; } #endif } /* 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 double-click put down a dialog box like the button would 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 */ #ifdef LWLIB_DIALOGS_MOTIF /* dialogs */ #if (XmVersion >= 1002) # define ARMANDACTIVATE_KLUDGE # define DND_KLUDGE #endif #ifdef ARMANDACTIVATE_KLUDGE /* We want typing Return at a dialog box to select the default button; but we're satisfied with having it select the leftmost button instead. In Motif 1.1.5 we could do this by putting this resource in the app-defaults file: *dialog*button1.accelerators:#override\ <KeyPress>Return: ArmAndActivate()\n\ <KeyPress>KP_Enter: ArmAndActivate()\n\ Ctrl<KeyPress>m: ArmAndActivate()\n but that doesn't work with 1.2.1 and I don't understand why. However, doing the equivalent C code does work, with the notable disadvantage that the user can't override it. So that's what we do until we figure out something better.... */ static char button_trans[] = "\ <KeyPress>Return: ArmAndActivate()\n\ <KeyPress>KP_Enter: ArmAndActivate()\n\ Ctrl<KeyPress>m: ArmAndActivate()\n"; #endif /* ARMANDACTIVATE_KLUDGE */ #ifdef DND_KLUDGE /* This is a kludge to disable drag-and-drop in dialog boxes. The symptom was a segv down in libXm somewhere if you used the middle button on a dialog box to begin a drag; when you released the button to make a drop things would lose if you were not over the button where you started the drag (canceling the operation). This was probably due to the fact that the dialog boxes were not set up to handle a drag but were trying to do so anyway for some reason. So we disable drag-and-drop in dialog boxes by turning off the binding for Btn2Down which, by default, initiates a drag. Clearly this is a shitty solution as it only works in default configurations, but... */ static char disable_dnd_trans[] = "<Btn2Down>: "; #endif /* DND_KLUDGE */ static Widget make_dialog (char* name, Widget parent, Boolean pop_up_p, const char* shell_title, const 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; #ifdef DND_KLUDGE XtTranslations dnd_override = XtParseTranslationTable (disable_dnd_trans); # define DO_DND_KLUDGE(widget) XtOverrideTranslations ((widget), dnd_override) #else /* ! DND_KLUDGE */ # define DO_DND_KLUDGE(widget) #endif /* ! DND_KLUDGE */ 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); 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, (char *) shell_title, al, ac); } else { ac = 0; XtSetArg(al[ac], XmNautoUnmanage, FALSE); ac++; XtSetArg(al[ac], XmNnavigationType, XmTAB_GROUP); ac++; form = XmCreateForm (parent, (char *) 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); DO_DND_KLUDGE (children [n_children]); if (i == 0) { button = children [n_children]; ac = 0; XtSetArg(al[ac], XmNdefaultButton, button); ac++; XtSetValues (row, al, ac); #ifdef ARMANDACTIVATE_KLUDGE /* See comment above */ { XtTranslations losers = XtParseTranslationTable (button_trans); XtOverrideTranslations (button, losers); XtFree ((char *) losers); } #endif /* ARMANDACTIVATE_KLUDGE */ } n_children++; } /* invisible separator button */ ac = 0; XtSetArg (al[ac], XmNmappedWhenManaged, FALSE); ac++; children [n_children] = XmCreateLabel (row, "separator_button", al, ac); DO_DND_KLUDGE (children [n_children]); 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); DO_DND_KLUDGE (children [n_children]); 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, (char *) icon_name, al, ac); DO_DND_KLUDGE (icon); 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); DO_DND_KLUDGE (icon_separator); 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); DO_DND_KLUDGE (value); } 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 double click in the list activate the default button */ XtAddCallback (value, XmNdefaultActionCallback, activate_button, button); } /* else add nothing; it's a separator */ 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); DO_DND_KLUDGE (message); 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); XmProcessTraversal(value, XmTRAVERSE_CURRENT); } else if (radio_box) { XtInstallAccelerators (form, button); XmProcessTraversal(value, XmTRAVERSE_CURRENT); } /* else we don' need no STEENKIN' assellerators. */ #ifdef DND_KLUDGE XtFree ((char *) dnd_override); #endif #undef DO_DND_KLUDGE 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 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; Arg al [2]; XtSetArg (al [0], XtNwidth, &child_width); XtSetArg (al [1], XtNheight, &child_height); XtGetValues (widget, al, 2); XtSetArg (al [0], XtNwidth, &parent_width); XtSetArg (al [1], XtNheight, &parent_height); XtGetValues (parent, al, 2); x = (Position) ((parent_width - child_width) / 2); y = (Position) ((parent_height - child_height) / 2); XtTranslateCoords (parent, x, y, &x, &y); if ((Dimension) (x + child_width) > screen_width) x = screen_width - child_width; if (x < 0) x = 0; if ((Dimension) (y + child_height) > screen_height) y = screen_height - child_height; if (y < 0) y = 0; XtSetArg (al [0], XtNx, x); XtSetArg (al [1], XtNy, y); XtSetValues (widget, al, 2); } 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) XmProcessTraversal(focus, XmTRAVERSE_CURRENT); /* shrink the separator label back to their original size */ separator = XtNameToWidget (widget, "*separator_button"); if (separator) { Arg al [2]; XtSetArg (al [0], XtNwidth, 5); XtSetArg (al [1], XtNheight, 5); XtSetValues (separator, al, 2); } /* 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; const char* shell_name = 0; const char* icon_name = 0; 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; } #endif /* LWLIB_DIALOGS_MOTIF */ #ifdef LWLIB_MENUBARS_MOTIF static Widget make_menubar (widget_instance* instance) { Arg al[10]; int ac = 0; XtSetArg(al[ac], XmNmarginHeight, 0); ac++; XtSetArg(al[ac], XmNshadowThickness, 3); ac++; return XmCreateMenuBar (instance->parent, instance->info->name, al, ac); } static void remove_grabs (Widget shell, XtPointer closure, XtPointer call_data) { Widget menu = (Widget) 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; } #endif /* LWLIB_MENUBARS_MOTIF */ #ifdef LWLIB_SCROLLBARS_MOTIF static Widget make_scrollbar (widget_instance *instance, int vertical) { Arg al[20]; int ac = 0; static XtCallbackRec callbacks[2] = { {xm_scrollbar_callback, NULL}, {NULL, NULL} }; callbacks[0].closure = (XtPointer) instance; XtSetArg (al[ac], XmNminimum, 1); ac++; XtSetArg (al[ac], XmNmaximum, INT_MAX); ac++; XtSetArg (al[ac], XmNincrement, 1); ac++; XtSetArg (al[ac], XmNpageIncrement, 1); ac++; XtSetArg (al[ac], XmNborderWidth, 0); ac++; XtSetArg (al[ac], XmNorientation, vertical ? XmVERTICAL : XmHORIZONTAL); ac++; XtSetArg (al[ac], XmNdecrementCallback, callbacks); ac++; XtSetArg (al[ac], XmNdragCallback, callbacks); ac++; XtSetArg (al[ac], XmNincrementCallback, callbacks); ac++; XtSetArg (al[ac], XmNpageDecrementCallback, callbacks); ac++; XtSetArg (al[ac], XmNpageIncrementCallback, callbacks); ac++; XtSetArg (al[ac], XmNtoBottomCallback, callbacks); ac++; XtSetArg (al[ac], XmNtoTopCallback, callbacks); ac++; XtSetArg (al[ac], XmNvalueChangedCallback, callbacks); ac++; return XmCreateScrollBar (instance->parent, instance->info->name, al, ac); } static Widget make_vertical_scrollbar (widget_instance *instance) { return make_scrollbar (instance, 1); } static Widget make_horizontal_scrollbar (widget_instance *instance) { return make_scrollbar (instance, 0); } #endif /* LWLIB_SCROLLBARS_MOTIF */ #ifdef LWLIB_WIDGETS_MOTIF /* glyph widgets */ static Widget xm_create_button (widget_instance *instance) { Arg al[20]; int ac = 0; Widget button = 0; widget_value* val = instance->info->val; XtSetArg (al [ac], XmNsensitive, val->enabled); ac++; XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; XtSetArg (al [ac], XmNuserData, val->call_data); ac++; XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); if (!val->call_data) button = XmCreateLabel (instance->parent, val->name, al, ac); else if (val->type == TOGGLE_TYPE || val->type == RADIO_TYPE) { XtSetArg (al [ac], XmNset, val->selected); ac++; XtSetArg (al [ac], XmNindicatorType, (val->type == TOGGLE_TYPE ? XmN_OF_MANY : XmONE_OF_MANY)); ac++; XtSetArg (al [ac], XmNvisibleWhenOff, True); ac++; button = XmCreateToggleButton (instance->parent, val->name, al, ac); XtRemoveAllCallbacks (button, XmNvalueChangedCallback); XtAddCallback (button, XmNvalueChangedCallback, xm_generic_callback, (XtPointer)instance); } else { button = XmCreatePushButton (instance->parent, val->name, al, ac); XtAddCallback (button, XmNactivateCallback, xm_generic_callback, (XtPointer)instance); } XtManageChild (button); return button; } static Widget xm_create_progress (widget_instance *instance) { Arg al[20]; int ac = 0; Dimension height = 0; Dimension width = 0; Widget scale = 0; widget_value* val = instance->info->val; if (!val->call_data) { XtSetArg (al [ac], XmNeditable, False); ac++; } else { XtSetArg (al [ac], XmNeditable, val->enabled); ac++; } XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; XtSetArg (al [ac], XmNuserData, val->call_data); ac++; XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++; XtSetArg (al [ac], XmNorientation, XmHORIZONTAL); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++; height = (Dimension)lw_get_value_arg (val, XtNheight); width = (Dimension)lw_get_value_arg (val, XtNwidth); if (height > 0) { XtSetArg (al [ac], XmNscaleHeight, height); ac++; } if (width > 0) { XtSetArg (al [ac], XmNscaleWidth, width); ac++; } /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); scale = XmCreateScale (instance->parent, val->name, al, ac); if (val->call_data) XtAddCallback (scale, XmNvalueChangedCallback, xm_generic_callback, (XtPointer)instance); XtManageChild (scale); return scale; } static Widget xm_create_text_field (widget_instance *instance) { Arg al[20]; int ac = 0; Widget text = 0; widget_value* val = instance->info->val; XtSetArg (al [ac], XmNsensitive, val->enabled); ac++; XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; XtSetArg (al [ac], XmNuserData, val->call_data); ac++; XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); text = XmCreateTextField (instance->parent, val->name, al, ac); if (val->call_data) XtAddCallback (text, XmNvalueChangedCallback, xm_generic_callback, (XtPointer)instance); XtManageChild (text); return text; } static Widget xm_create_label_field (widget_instance *instance) { return xm_create_label (instance->parent, instance->info->val); } Widget xm_create_label (Widget parent, widget_value* val) { Arg al[20]; int ac = 0; Widget label = 0; XtSetArg (al [ac], XmNsensitive, val->enabled); ac++; XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); label = XmCreateLabel (parent, val->name, al, ac); XtManageChild (label); /* Do it again for arguments that have no effect until the widget is realized. */ ac = 0; lw_add_value_args_to_args (val, al, &ac); XtSetValues (label, al, ac); return label; } #if XmVERSION > 1 static Widget xm_create_combo_box (widget_instance *instance) { Arg al[20]; int ac = 0; Widget combo = 0; widget_value* val = instance->info->val; XtSetArg (al [ac], XmNsensitive, val->enabled); ac++; XtSetArg (al [ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++; XtSetArg (al [ac], XmNuserData, val->call_data); ac++; XtSetArg (al [ac], XmNmappedWhenManaged, FALSE); ac++; /* The highlight doesn't appear to be dynamically set which makes it look ugly. I think this may be a LessTif bug but for now we just get rid of it. */ XtSetArg (al [ac], XmNhighlightThickness, (Dimension)0);ac++; /* add any args the user supplied for creation time */ lw_add_value_args_to_args (val, al, &ac); combo = XmCreateDropDownComboBox (instance->parent, val->name, al, ac); if (val->call_data) XtAddCallback (combo, XmNselectionCallback, xm_generic_callback, (XtPointer)instance); XtManageChild (combo); return combo; } #endif #endif /* LWLIB_WIDGETS_MOTIF */ /* Table of functions to create widgets */ const widget_creation_entry xm_creation_table [] = { #ifdef LWLIB_MENUBARS_MOTIF {"menubar", make_menubar}, {"popup", make_popup_menu}, #endif #ifdef LWLIB_SCROLLBARS_MOTIF {"vertical-scrollbar", make_vertical_scrollbar}, {"horizontal-scrollbar", make_horizontal_scrollbar}, #endif #ifdef LWLIB_WIDGETS_MOTIF {"button", xm_create_button}, {"progress", xm_create_progress}, {"text-field", xm_create_text_field}, {"label", xm_create_label_field}, #if XmVERSION > 1 {"combo-box", xm_create_combo_box}, #endif #endif {NULL, NULL} }; /* Destruction of instances */ void xm_destroy_instance (widget_instance* instance) { #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) /* It appears that this is used only for dialog boxes. */ 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); } #endif /* LWLIB_DIALOGS_MOTIF || LWLIB_WIDGETS_MOTIF */ } /* popup utility */ #ifdef LWLIB_MENUBARS_MOTIF void xm_popup_menu (Widget widget, XEvent *event) { 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 = "<Btn5Down>"; else if (event->xbutton.state & Button4Mask) trans = "<Btn4Down>"; else if (event->xbutton.state & Button3Mask) trans = "<Btn3Down>"; else if (event->xbutton.state & Button2Mask) trans = "<Btn2Down>"; else if (event->xbutton.state & Button1Mask) trans = "<Btn1Down>"; if (trans) { Arg al [1]; XtSetArg (al [0], XmNmenuPost, trans); XtSetValues (widget, al, 1); } XmMenuPosition (widget, (XButtonPressedEvent *) event); } XtManageChild (widget); } #endif #ifdef LWLIB_DIALOGS_MOTIF static void set_min_dialog_size (Widget w) { short width; short height; Arg al [2]; XtSetArg (al [0], XmNwidth, &width); XtSetArg (al [1], XmNheight, &height); XtGetValues (w, al, 2); XtSetArg (al [0], XmNminWidth, width); XtSetArg (al [1], XmNminHeight, height); XtSetValues (w, al, 2); } #endif void xm_pop_instance (widget_instance* instance, Boolean up) { Widget widget = instance->widget; #ifdef LWLIB_DIALOGS_MOTIF if (XtClass (widget) == xmDialogShellWidgetClass) { Widget widget_to_manage = first_child (widget); if (up) { XtManageChild (widget_to_manage); set_min_dialog_size (widget); XmProcessTraversal(widget, XmTRAVERSE_CURRENT); } else XtUnmanageChild (widget_to_manage); } else #endif { 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) { XtPointer user_data; widget_instance* instance = (widget_instance*)closure; Widget instance_widget; LWLIB_ID id; Arg al [1]; if (!instance) return; if (widget->core.being_destroyed) return; instance_widget = instance->widget; if (!instance_widget) return; id = instance->info->id; user_data = NULL; XtSetArg(al [0], XmNuserData, &user_data); XtGetValues (widget, al, 1); 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) { #if (defined (LWLIB_MENUBARS_MOTIF) || defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF)) /* We want the selected status to change only when we decide it should change. Yuck but correct. */ if (XtClass (widget) == xmToggleButtonWidgetClass || XtClass (widget) == xmToggleButtonGadgetClass) { Boolean check; Arg al [1]; XtSetArg (al [0], XmNset, &check); XtGetValues (widget, al, 1); XtSetArg (al [0], XmNset, !check); XtSetValues (widget, al, 1); } #endif lw_internal_update_other_instances (widget, closure, call_data); do_call (widget, closure, selection); } static void xm_pop_down_callback (Widget widget, XtPointer closure, XtPointer call_data) { do_call (widget, closure, post_activate); } #ifdef LWLIB_MENUBARS_MOTIF static void xm_pull_down_callback (Widget widget, XtPointer closure, XtPointer call_data) { #if 0 if (call_data) { /* new behavior for incremental menu construction */ } else #endif do_call (widget, closure, pre_activate); } #endif /* LWLIB_MENUBARS_MOTIF */ #ifdef LWLIB_SCROLLBARS_MOTIF static void xm_scrollbar_callback (Widget widget, XtPointer closure, XtPointer call_data) { widget_instance *instance = (widget_instance *) closure; LWLIB_ID id; XmScrollBarCallbackStruct *data = (XmScrollBarCallbackStruct *) call_data; scroll_event event_data; scrollbar_values *val = (scrollbar_values *) instance->info->val->scrollbar_data; double percent; if (!instance || widget->core.being_destroyed) return; id = instance->info->id; percent = (double) (data->value - 1) / (double) (INT_MAX - 1); event_data.slider_value = (int) (percent * (double) (val->maximum - val->minimum)) + val->minimum; if (event_data.slider_value > (val->maximum - val->slider_size)) event_data.slider_value = val->maximum - val->slider_size; else if (event_data.slider_value < 1) event_data.slider_value = 1; if (data->event) { switch (data->event->xany.type) { case KeyPress: case KeyRelease: event_data.time = data->event->xkey.time; break; case ButtonPress: case ButtonRelease: event_data.time = data->event->xbutton.time; break; case MotionNotify: event_data.time = data->event->xmotion.time; break; case EnterNotify: case LeaveNotify: event_data.time = data->event->xcrossing.time; break; default: event_data.time = 0; break; } } else event_data.time = 0; switch (data->reason) { case XmCR_DECREMENT: event_data.action = SCROLLBAR_LINE_UP; break; case XmCR_INCREMENT: event_data.action = SCROLLBAR_LINE_DOWN; break; case XmCR_PAGE_DECREMENT: event_data.action = SCROLLBAR_PAGE_UP; break; case XmCR_PAGE_INCREMENT: event_data.action = SCROLLBAR_PAGE_DOWN; break; case XmCR_TO_TOP: event_data.action = SCROLLBAR_TOP; break; case XmCR_TO_BOTTOM: event_data.action = SCROLLBAR_BOTTOM; break; case XmCR_DRAG: event_data.action = SCROLLBAR_DRAG; break; case XmCR_VALUE_CHANGED: event_data.action = SCROLLBAR_CHANGE; break; default: event_data.action = SCROLLBAR_CHANGE; break; } if (instance->info->pre_activate_cb) instance->info->pre_activate_cb (widget, id, (XtPointer) &event_data); } #endif /* LWLIB_SCROLLBARS_MOTIF */ #if defined (LWLIB_DIALOGS_MOTIF) || defined (LWLIB_WIDGETS_MOTIF) static void mark_dead_instance_destroyed (Widget widget, XtPointer closure, XtPointer call_data) { destroyed_instance* instance = (destroyed_instance*)closure; instance->widget = NULL; } 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); } #endif /* set the keyboard focus */ void xm_set_keyboard_focus (Widget parent, Widget w) { XmProcessTraversal (w, XmTRAVERSE_CURRENT); /* At some point we believed that it was necessary to use XtSetKeyboardFocus instead of XmProcessTraversal when using Motif >= 1.2.1, but that's bogus. Presumably the problem was elsewhere, and is now gone... */ }