+/*
+ * ComboBox.c - Das schon lange schmerzlich vermisste Combo-Box-
+ * Widget -- nun endlich auf fuer Motif!
+ *
+ * Version 1.32a -- 04.10.95
+ *
+ * Letzte Modifikation:
+ * 04.10.1995 Layoutfehler behoben, der bei angezeigter horizontaler Liste
+ * dazu fuehrt, dass das Listenfeld schrumpft. Daneben wird
+ * jetzt auch der Fall beruecksichtigt, dass das Listenfeld am
+ * unteren Bildschirmrand abgeschnitten wuerde. In diesem Fall
+ * erscheint das Listenfeld oberhalb des Eingabefeldes.
+ * 20.03.1995 XmNscrollbarDisplayPolicy,... koennen nun immer vom Pro-
+ * grammierer gesetzt werden, statische Liste hin und her.
+ * 21.10.1994 Fehler in SetValues behoben, der auftritt, wenn man versucht,
+ * XmNitems und XmNitemCount zu setzen.
+ * 01.10.1994 Externe Sortierung wird nun unterstuetzt sowie seitenweises
+ * Rollen in der Liste mittels PgUp und PgDn.
+ * 25.09.1994 Unterstuetzung fuer XmNautomaticSelection implementiert.
+ * Damit wird die Sache noch ein bischen runder in der Bedienung.
+ * Des weiteren sind etliche Callbacks neu hinzugekommen.
+ * 04.09.1994 Erweiterungen fuer XmSINGLE_SELECT eingebaut. Ausserdem
+ * kann die Liste jetzt auch statisch unterhalb des Eingabe-
+ * felds erscheinen. Damit sind wir nun noch kompatibler ge-
+ * worden -- fragt sich nur, zu was?!
+ * 29.08.1994 Alle Mirror-Ressourcen tauchen nun auch in der Ressourcen-
+ * liste der ComboBox-Klasse auf. Allerdings stehen keine
+ * sinnvollen Werte fuer die Initialisierung 'drin. Weiterhin
+ * den GeometryManager so veraendert, dass ab sofort das
+ * Label in der Breite wachsen oder schrumpfen darf.
+ * 07.06.1994 XmNmnemonic und XmNmnemonicCharSet implementiert.
+ * 29.05.1994 XmNsensitive angepasst. XmNcursorPositionVisible ist nun
+ * False, falls die ComboBox nicht editierbar ist.
+ * 07.05.1994 Drag'n'Drop funktioniert endlich!!! Zudem Anpassung an
+ * den fvwm ausgefuehrt ('st vom Focus-Verhalten ja ein halber
+ * twm). Hach', so'ne Linux-Box mit Motif 1.2 macht doch
+ * einfach Spass... vor allem geht hier so richtig die Post ab.
+ * Das kann man ja von M$ Windoze (Windoze for Mondays) nicht
+ * behaupten!
+ * 14.04.1994 Ein paar Speicherlecks korrigiert.
+ * 21.02.1994 Die Resourcen XmNitems und XmNitemCount lassen sich nun
+ * auch von einer Resourcendatei aus initialisieren. ACHTUNG:
+ * diese beiden Resourcen mussen immer beide beim Aufruf von
+ * XtSetValues zugleich angegeben werden, ansonsten werden
+ * diese Angaben ignoriert.
+ * 03.02.1994 Convenience-Funktionen auf Vordermann gebracht und noch
+ * einen Callback eingebaut, der immer dann aufgerufen wird,
+ * wenn die List angezeigt oder wieder versteckt wird.
+ * 01.02.1994 Motif 1.2-fest!!! Das wird aber heute abend gefeiert!!
+ * Endlich EIN Alptraum weniger! Naja, Drag'n'Drop bleibt
+ * noch zu loesen. Spaeter...
+ * 31.01.1994 VAX-fest (mit Hilfe von Vincenct Li)
+ * owlm sollte man abschaffen! Aber es scheint so, als ob
+ * ich jetzt doch noch das FocusOut-Problem geknackt habe.
+ * Ebenso die OSF...mit viel Arbeit habe ich nun auch noch
+ * eine anstaendige Initialisierung der Fontliste des Label-
+ * Kinds erreicht.
+ * 12.01.1994 Revisionsstand: 1.10a
+ * nun wirklich voll ANSI-faehiger C-Code
+ * Pixmaps werden ggf. aufgeraeumt; Druckrichtung
+ * wird vom Vater erfragt und an das Label weiter-
+ * gegeben.
+ * ESC-Behandlung implementiert.
+ * Spiegel-Ressourcen-Initialisierung aus Ressourcen-Daten-
+ * bank implementiert.
+ * Weitergabe von neu gesetzten Farben an die Kinder
+ * implementiert.
+ * Combo-Box kann jetzt wahlweise auch links neben dem
+ * Eingabefeld ein Label anzeigen.
+ * 09.12.1993 Revisionsstand: 1.00
+ * erste oeffentlich zugaengliche Version der Combo-Box
+ *
+ * (c) 1993, 1994, 1995 Harald Albrecht
+ * Institut fuer Geometrie und Praktische Mathematik
+ * RWTH Aachen, Germany
+ * albrecht@igpm.rwth-aachen.de
+ *
+ * This program 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 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program (see the file COPYING for more details);
+ * if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+ * Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <X11/IntrinsicP.h>
+#include <X11/StringDefs.h>
+#include <X11/cursorfont.h>
+#include <X11/Shell.h>
+#ifdef VMS /* Huch, wo gibt's denn noch sowas ... ?! */
+ /* Bitte keine Mail bzgl. dieser Bemerkung schicken...
+ * Ich weiss, das ist ein Vorurteil...aber es gibt
+ * ja auch wahre Vorurteile....
+ */
+#include <Xmu/Converters.h>
+#else
+#include <X11/Xmu/Converters.h>
+#endif
+#include <Xm/ArrowB.h>
+#include <Xm/TextF.h>
+#include <Xm/List.h>
+#include <Xm/LabelP.h>
+#include <string.h>
+#include <ctype.h> /* define toupper */
+#include "combop.h"
+
+#include <stdio.h>
+
+/* --- Systemspezifische Definitionen */
+#ifdef VMS
+#define strcasecmp(s1, s2) strcmp(s1, s2)
+#endif
+
+/* --- sonstiger Quark */
+/* #ifdef DEBUG */
+#if 0
+#define LOG(p1) fprintf(stderr, p1);
+#define LOG2(p1, p2) fprintf(stderr, p1, p2);
+#define LOG3(p1, p2, p3) fprintf(stderr, p1, p2, p3);
+#else
+#define LOG(p1)
+#define LOG2(p1, p2)
+#define LOG3(p1, p2, p3)
+#endif
+
+/* ---------------------------------------------------------------------------
+ * Resourcen-Liste...
+ * Hier werden diejenigen Resourcen definiert, die von "aussen" - also fuer
+ * den Programmierer oder Anwender - benutzbar und veraenderbar sind.
+ *
+ * Der Aufbau der einzelnen Eintraege ist immer wieder gleich:
+ * Resourcen-Name XmN... oder XtN
+ * Resourcen-Klasse XmC... oder XtC
+ * Resourcen-Type XmR... oder XtR (Datentyp der Variable in der
+ * struct der jeweiligen Widgetinstanz)
+ * Resourcen-Groesse aktuelle Groesse dieses Datentyps
+ * Resourcen-Offset Lage der Variable innerhalb der struct der
+ * Widgetinstanz
+ * Defaultwert-Type Typ des Defaultwertes
+ * Defaultwert (normalerweise) Zeiger auf den Defaultwert
+ */
+#define offset(field) XtOffsetOf(XmComboBoxRec, field)
+static XtResource resources[] = {
+ { /* Eingabefeld kann veraendert werden, oder aber es sind nur
+ * die Vorgaben aus der Liste auswaehlbar.
+ */
+ XmNeditable, XmCEditable, XmRBoolean, sizeof(Boolean),
+ offset(combobox.Editable), XmRString, "False"
+ },
+ { /* Liste wird automatisch sortiert -- wie konnten die bei
+ * der OSF denn SOETWAS nur vergessen ??
+ */
+ XmNsorted, XmCSorted, XmRBoolean, sizeof(Boolean),
+ offset(combobox.Sorted), XmRString, "False"
+ },
+ { /* externe Sortierreihenfolge */
+ XmNsortingCallback, XmCSortingCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.SortingCBL), XmRCallback, NULL
+ },
+ { /* Anzahl auf einmal sichtbarer Eintraege in der Liste (ent-
+ * spricht damit der Listenhoehe.
+ */
+ XmNvisibleItemCount, XmCVisibleItemCount, XmRInt, sizeof(int),
+ offset(combobox.VisibleItemCount), XmRImmediate, (caddr_t) 8
+ },
+ { /* Fuer das Eingabefeld sowie die Liste verwandte Fonts */
+ XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
+ offset(combobox.Font), XmRImmediate, NULL
+ },
+ { /* Rueckruf bei Anwahl */
+ XmNselectionCallback, XmCSelectionCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.SelectionCBL), XmRCallback, NULL
+ },
+ { /* Gegenteil zum vorherigen Callback! */
+ XmNunselectionCallback, XmCUnselectionCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.UnselectionCBL), XmRCallback, NULL
+ },
+ { /* Doppelklick in der Liste */
+ XmNdefaultActionCallback, XmCCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.DefaultActionCBL), XmRCallback, NULL
+ },
+ { /* Rueckruf bei Liste ausklappen/verstecken */
+ XmNdropDownCallback, XmCDropDownCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.DropDownCBL), XmRCallback, NULL
+ },
+ { /* Eingabe abchecken... */
+ XmNmodifyVerifyCallback, XmCCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.ModifyVerifyCBL), XmRCallback, NULL
+ },
+ {
+ XmNvalueChangedCallback, XmCCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.ValueChangedCBL), XmRCallback, NULL
+ },
+ {
+ XmNactivateCallback, XmCCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.ActivateCBL), XmRCallback, NULL
+ },
+ {
+ XmNmotionVerifyCallback, XmCCallback, XmRCallback,
+ sizeof(XtCallbackList),
+ offset(combobox.MotionVerifyCBL), XmRCallback, NULL
+ },
+ { /* Verhalten der ausgeklappten Liste bei Focus-Out */
+ XmNpersistentDropDown, XmCPersistentDropDown, XmRBoolean,
+ sizeof(Boolean),
+ offset(combobox.Persistent), XmRString, "False"
+ },
+ { /* Wie verhaelt sich der Window-Manager? */
+ XmNtwmHandlingOn, XmCTwmHandlingOn, XmRBoolean, sizeof(Boolean),
+ offset(combobox.TwmHandlingOn), XmRString, "False"
+ },
+ { /* Label anzeigen oder nicht? */
+ XmNshowLabel, XmCShowLabel, XmRBoolean, sizeof(Boolean),
+ offset(combobox.ShowLabel), XmRString, "False"
+ },
+ { /* Abstand zw. linkem Rand Eingabefeld und linkem Rand Liste */
+ XmNdropDownOffset, XmCDropDownOffset, XmRPosition,
+ sizeof(Position), offset(combobox.DropDownOffset),
+ XmRImmediate, (caddr_t) -1
+ },
+ { /* Neue Voreinstellung bzgl. des Randes */
+ XmNborderWidth, XmCBorderWidth, XmRDimension, sizeof(Dimension),
+ offset(core.border_width), XmRImmediate, (caddr_t) 0
+ },
+ { /* welcher Cursor soll in der Dropdown-Liste benutzt werden? */
+ XmNdropDownCursor, XmCDropDownCursor, XmRCursor, sizeof(Cursor),
+ offset(combobox.ArrowCursor), XmRString, "center_ptr"
+ },
+ { /* wie lassen sich Eintraege auswaehlen? */
+ XmNselectionPolicy, XmCSelectionPolicy, XmRSelectionPolicy, sizeof(unsigned char),
+ offset(combobox.SelectionPolicy), XmRImmediate, (caddr_t) XmBROWSE_SELECT
+ },
+ { /* Wann werden die Callbacks aufgerufen? */
+ XmNautomaticSelection, XmCAutomaticSelection, XmRBoolean, sizeof(Boolean),
+ offset(combobox.AutomaticSelection), XmRString, "False"
+ },
+ { /* erscheint die Liste staendig? */
+ XmNstaticList, XmCStaticList, XmRBoolean, sizeof(Boolean),
+ offset(combobox.StaticList), XmRString, "False"
+ },
+ {
+ XmNscrollBarDisplayPolicy, XmCScrollBarDisplayPolicy, XmRScrollBarDisplayPolicy, sizeof(unsigned char),
+ offset(combobox.ScrollBarDisplayPolicy), XmRImmediate, (caddr_t) XmAS_NEEDED
+ },
+ {
+ XmNlistSizePolicy, XmCListSizePolicy, XmRListSizePolicy, sizeof(unsigned char),
+ offset(combobox.ListSizePolicy), XmRImmediate, (caddr_t) XmVARIABLE
+ },
+ {
+ XmNsquareArrow, XmCSquareArrow, XmRBoolean, sizeof(Boolean),
+ offset(combobox.SquareArrow), XmRString, "False"
+ },
+ {
+ XmNarrowSpacingOn, XmCArrowSpacingOn, XmRBoolean, sizeof(Boolean),
+ offset(combobox.ArrowSpacingOn), XmRString, "True"
+ },
+#ifndef DONT_LOOK_IN_THE_MIRROR
+ /* Mirror-Ressourcen, Adressen sind ungueltig!!!! */
+ {
+ XmNalignment, XmCAlignment, XmRAlignment, sizeof(unsigned char),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNblinkRate, XmCBlinkRate, XmRInt, sizeof(int),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNcolumns, XmCColumns, XmRShort, sizeof(short),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNcursorPosition, XmCCursorPosition, XmRTextPosition, sizeof(XmTextPosition),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNitemCount, XmCItemCount, XmRInt, sizeof(int),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNitems, XmCItems, XmRXmStringTable, sizeof(XmStringTable),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelFontList, XmCLabelFontList, XmRFontList, sizeof(XmFontList),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelInsensitivePixmap, XmCLabelInsensitivePixmap, XmRPixmap, sizeof(Pixmap),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelMarginBottom, XmCLabelMarginBottom, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelMarginHeight, XmCLabelMarginHeight, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelMarginLeft, XmCLabelMarginLeft, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelMarginRight, XmCLabelMarginRight, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelMarginTop, XmCLabelMarginTop, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelMarginWidth, XmCLabelMarginWidth, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelPixmap, XmCLabelPixmap, XmRPixmap, sizeof(Pixmap),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelString, XmCLabelString, XmRString, sizeof(XmString),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlabelType, XmCLabelType, XmRLabelType, sizeof(unsigned char),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlistMarginHeight, XmCListMarginHeight, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlistMarginWidth, XmCListMarginWidth, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNlistSpacing, XmCListSpacing, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNmarginHeight, XmCMarginHeight, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNmarginWidth, XmCMarginWidth, XmRDimension, sizeof(Dimension),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNmaxLength, XmCMaxLength, XmRInt, sizeof(int),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNselectThreshold, XmCSelectThreshold, XmRInt, sizeof(int),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNstringDirection, XmCStringDirection, XmRStringDirection, sizeof(XmStringDirection),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNtopItemPosition, XmCTopItemPosition, XmRInt, sizeof(int),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNvalue, XmCValue, XmRString, sizeof(String),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+ {
+ XmNvalue, XmCValue, XmRInt, sizeof(int),
+ offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
+ },
+#endif
+}; /* resources[] */
+
+/* ---------------------------------------------------------------------------
+ * Funktions-Prototypen fuer die 'Methoden' des ComboBox-Widgets. Diese
+ * 'Methoden' werden vom Xt-Toolkit aufgerufen und sorgen dafuer, dass eine
+ * ComboBox sich wie ein anstaendiges Widget verhaelt.
+ */
+static void Initialize (Widget, XmComboBoxWidget, ArgList,
+ Cardinal *);
+static void Destroy (XmComboBoxWidget);
+static void Resize (XmComboBoxWidget);
+static Boolean SetValues (XmComboBoxWidget, XmComboBoxWidget,
+ XmComboBoxWidget, ArgList, Cardinal *);
+static void GetValuesAlmost(XmComboBoxWidget, ArgList, Cardinal *);
+static XtGeometryResult QueryGeometry (XmComboBoxWidget, XtWidgetGeometry *,
+ XtWidgetGeometry *);
+static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry *,
+ XtWidgetGeometry *);
+static void ClassInit ();
+static void Realize (XmComboBoxWidget, Mask *,
+ XSetWindowAttributes *);
+/* ---------------------------------------------------------------------------
+ * diverse restliche Prototypen... naja, hier halt etwas mager! Hierbei
+ */
+static void ShowHideDropDownList (XmComboBoxWidget w, XEvent *event,
+ Boolean Show);
+static void ShellCallback (Widget w, XtPointer cbw,
+ XEvent *event, Boolean *ContDispatch);
+static void DoLayout (XmComboBoxWidget w);
+/* --------------------------------------------------------------------
+ * Klassen-Definition
+ */
+XmComboBoxClassRec xmComboBoxClassRec = {
+ { /*** core-Klasse ***/
+ /* superclass */ (WidgetClass) &xmManagerClassRec,
+ /* class_name */ "XmComboBox",
+ /* widget_size */ sizeof(XmComboBoxRec),
+ /* class_initialize */ (XtProc) ClassInit,
+ /* class_part_initialize */ NULL,
+ /* class_inited */ False, /* IMMER mit FALSE initialisieren !! */
+ /* initialize */ (XtInitProc) Initialize,
+ /* initialize_hook */ NULL,
+ /* realize */ (XtRealizeProc) Realize,
+ /* actions */ NULL,
+ /* num_actions */ 0,
+ /* resources */ resources,
+ /* num_resources */ XtNumber(resources),
+ /* xrm_class */ NULLQUARK,
+ /* compress_motion */ True,
+ /* compress_exposure */ XtExposeCompressMultiple,
+ /* compress_enterleave */ True,
+ /* visible_interest */ False,
+ /* destroy */ (XtWidgetProc) Destroy,
+ /* resize */ (XtWidgetProc) Resize,
+ /* expose */ NULL,
+ /* set_values */ (XtSetValuesFunc) SetValues,
+ /* set_values_hook */ NULL,
+ /* set_values_almost */ XtInheritSetValuesAlmost,
+ /* get_values_hook */ (XtArgsProc) GetValuesAlmost,
+ /* accept_focus */ NULL,
+ /* version */ XtVersion,
+ /* callback_private */ NULL,
+ /* tm_table */ NULL,
+ /* query_geometry */ (XtGeometryHandler) QueryGeometry,
+ /* display_accelerator */ XtInheritDisplayAccelerator,
+ /* extension */ NULL
+ },
+ { /*** composite-Klasse ***/
+ /* geometry_manager */ (XtGeometryHandler) GeometryManager,
+ /* change_managed */ XtInheritChangeManaged,
+ /* insert_child */ XtInheritInsertChild,
+ /* delete_child */ XtInheritDeleteChild,
+ /* extension */ NULL
+ },
+ { /*** constraint-Klasse ***/
+ /* resources */ NULL,
+ /* num_resources */ 0,
+ /* constraint_size */ sizeof(XmManagerConstraintPart),
+ /* initialize */ NULL,
+ /* destroy */ NULL,
+ /* set_values */ NULL,
+ /* extension */ NULL
+ },
+ { /*** xmManager-Klasse ***/
+ /* translations */ XtInheritTranslations,
+ /* syn_resources */ NULL,
+ /* num_syn_resources */ 0,
+ /* syn_constraint_resources */ NULL,
+ /* num_syn_constraint_resources */ 0,
+ /* parent_process */ XmInheritParentProcess,
+ /* extension */ NULL
+ },
+ { /*** combobox-Klasse ***/
+ /* */ 0
+ }
+}; /* xmComboBoxClassRec */
+WidgetClass xmComboBoxWidgetClass = (WidgetClass) &xmComboBoxClassRec;
+
+/* --------------------------------------------------------------------
+ * --------------------------------------------------------------------
+ * Translation-Tabelle (hier allerdings fuer das Eingabefeld!)
+ * Tjaja....mit der Reihenfolge von Translations ist das schon so eine
+ * ziemlich boese Sache!
+ */
+static char newEditTranslations[] =
+ "Alt<Key>osfDown: ComboBox-Manager(show-hide-list) \n\
+ Meta<Key>osfDown: ComboBox-Manager(show-hide-list) \n\
+ Alt<Key>osfUp: ComboBox-Manager(hide-list) \n\
+ Meta<Key>osfUp: ComboBox-Manager(hide-list) \n\
+ <Key>osfUp: ComboBox-Manager(up) \n\
+ <Key>osfDown: ComboBox-Manager(down) \n\
+ <Key>osfPageUp: ComboBox-Manager(page-up) \n\
+ <Key>osfPageDown: ComboBox-Manager(page-down) \n\
+ <Key>osfCancel: ComboBox-Manager(cancel) \n\
+ <Key>Return: ComboBox-Manager(activate) activate()"
+ ;
+/* speziell bei der nicht editierbaren Combo-Box sind noch einige
+ * andere Tasten belegt, die sonst dem Eingabefeld alleine gehoeren.
+ * Die dazugehoerigen neuen Translations befinden sich in dieser
+ * zusaetzlichen Tabelle, das Anhaengsel ...NE ist dabei die Ab-
+ * kuerzung fuer "non editable".
+ */
+static char newEditTranslationsNE[] =
+ "<Key>osfDelete: ComboBox-Manager(wipe-out) \n\
+ <Key>osfBeginLine: ComboBox-Manager(top) \n\
+ <Key>osfEndLine: ComboBox-Manager(bottom) "
+ ;
+/* Momentan gibt es noch Aerger mit dem Drag'n'Drop-Mechanismus
+ * von Motif 1.2. Legen wir ihn deshalb erst einmal still, solange
+ * bis ich weiss, warum, und eine Loesung parat habe. NEU: Nur wenn
+ * Sie mit einer libXm geschlagen sind, die partout nicht funktionieren
+ * will, muessen Sie Drag'n'Drop stillegen, ansonsten klappts doch!
+ */
+#ifdef NODRAGNDROP
+static char newListTranslations[] =
+ "<Btn2Down>: ComboBox-Manager(no-operation) ";
+#endif
+static char newListTranslationsE[] =
+ "<Key>osfPageUp: ComboBox-Manager(page-up) \n\
+ <Key>osfPageDown: ComboBox-Manager(page-down) ";
+
+/* ---------------------------------------------------------------------------
+ * ---------------------------------------------------------------------------
+ * Aktionen-Tabelle: Hierdurch werden den einzelnen Translations die dazuge-
+ * hoerigen C-Routinen zugeordnet. Da wir hier ein anstaendiges ANSI-C be-
+ * nutzen, werden hier zuerst einmal die Prototypen faellig... Ach ja, noch
+ * ein Hinweis in eigener Sache... der ComboBox-Manager muss applikationsweit
+ * registriert werden, da er auch von Translationen in den Kindern der Combo-
+ * Box aktiviert wird. Bei diesem Namen der 'Aktion' steht aber nicht zu be-
+ * fuerchten, dass er anderweitig bereits in Anwendung ist.
+ */
+static void CBoxManager(Widget w, XEvent *event, String *params,
+ Cardinal *num_params);
+
+static XtActionsRec actions[] = {
+ { "ComboBox-Manager", CBoxManager },
+ { NULL, NULL }
+}; /* actions */
+
+
+/* --------------------------------------------------------------------
+ * Eine Instanz dieser Widget-Klasse wird erstmalig in Betrieb ge-
+ * nommen, daher sind noch Vorbereitungen notwendig, die nun hier
+ * durchgefuehrt werden.
+ */
+static XtTranslations NewEditTranslations, NewEditTranslationsNE,
+ NewListTranslations, NewListTranslationsE;
+
+static XtConvertArgRec ConverterScreenConvertArg[] = {
+ { XtBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
+ sizeof(Screen *) }
+};
+
+static void ClassInit()
+{
+ NewEditTranslations =
+ XtParseTranslationTable(newEditTranslations);
+ NewEditTranslationsNE =
+ XtParseTranslationTable(newEditTranslationsNE);
+#ifdef NODRAGNDROP
+ NewListTranslations =
+ XtParseTranslationTable(newListTranslations);
+#endif
+ NewListTranslationsE =
+ XtParseTranslationTable(newListTranslationsE);
+ XtAddConverter(XtRString, XtRBitmap,
+ XmuCvtStringToBitmap,
+ ConverterScreenConvertArg,
+ XtNumber(ConverterScreenConvertArg));
+} /* ClassInit */
+
+/* ---------------------------------------------------------------------------
+ * Weil es sich bei diesem Widget um ein etwas komplizierteres zusammengesetz-
+ * tes Widget handelt, muessen wir hier - wo eigentlich nur das die Combobox
+ * bildende Fenster auf dem X-Server erzeugt wird - noch einmal das Layout
+ * auf Vordermann bringen. Den Aerger loest dabei das Listenfeld der OSF aus,
+ * das einfach keine Geometrie-Nachfragen verschickt, solange es nicht
+ * 'realized' ist!!! Nicht, dass ich mich ueber so einen Sauhaufen aufregen
+ * wuerde...ich doch nicht! ABER MACHT IHR DENN NUR SO'N MIST...? WARUM KOENNT
+ * IHR DENN NICHT EINMAL DIESES ****(BIEP)**** MOTIF TOOLKIT ANSTAENDIG
+ * DOKUMENTIEREN! Ich glaub', ich kann mich nach dem Chaos eigentlich nur noch
+ * hemmungslos besaufen... Die Suche nach der Ursache (bzw. Urheber = OSF) hat
+ * mich doch einige Tage gekostet (jaja...die Mannstunden!).
+ */
+static void Realize(XmComboBoxWidget w, Mask *ValueMask,
+ XSetWindowAttributes *Attributes)
+{
+ /*
+ * Also: wenn die Liste staendig sichtbar ist, dann zuerst noch einmal
+ * das Layout berechnen. Sonst wird vorne und hinten 'was abgeschnitten.
+ */
+ if ( w->combobox.StaticList )
+ DoLayout(w);
+ (*w->core.widget_class->core_class.superclass->core_class.realize)
+ ((Widget) w, ValueMask, Attributes);
+} /* Realize */
+
+/* ---------------------------------------------------------------------------
+ * Suche dasjenige Fenster, in dem unsere Shell liegt, in der wiederum die
+ * Combo-Box steckt. Diese Information wird benoetigt, um die Drop-Down-Liste
+ * innerhalb des Fensterstacks immer direkt oberhalb der Shell mit der Combo-
+ * Box zu halten. Jajaja -- ich muss halt davon ausgehen, dass der Fenster-
+ * manager ein sog. "reparenting wm" ist; also die Dekorationen in einem
+ * Fenster dargestellt werden und unsere Shell in dieses Fenster hineingepackt
+ * ist. Die Dekoration ist damit ein Kind des 'root window' - wie die Shell,
+ * in der die Drop-Down-Liste steckt. Und da nur Geschwisterfenster (sibling
+ * windows) im gleichen Stapel stecken, reicht das Shellfenster nicht aus.
+ * Alle gaengigen Fenstermanager sind solche "reparenting wm's", so dass ich
+ * hier zu diesem Trick greifen kann, um die Drop-Down-Liste immer ueber der
+ * ComboBox zu halten.
+ *
+ * Parameter:
+ * w Diejenige Combo-Box, fuer die wir dasjenige
+ * Fenster des Window-Managers ermitteln sollen,
+ * dass direkt unterhalb des Root-Fensters liegt.
+ * Ergebnis:
+ * besagtes zu suchendes Fenster, dass die Dekoration enthaelt (hoffentlich
+ * nur echte Bruesseler Spitze!)
+ */
+static Window GetDecorationWindow(XmComboBoxWidget w)
+{
+ Window Root, Parent, AWindow;
+ Window *Children;
+ unsigned int NumChildren;
+
+ Parent = XtWindow((Widget) w);
+ /* Suche nach dem Dekorationsfenster des Window-Managers */
+ do {
+ AWindow = Parent;
+ XQueryTree(XtDisplay((Widget) w), AWindow,
+ &Root, &Parent, &Children, &NumChildren);
+ XFree((char *) Children);
+ } while ( Parent != Root );
+ return AWindow;
+} /* GetDecorationWindow */
+
+/* --------------------------------------------------------------------
+ * Eine Combo-Box aus dem Wege raeumen...
+ * Momentan muessen wir hier nur den Cursor wieder los werden sowie
+ * eventuell reservierte Pixmaps.
+ * Ups -- natuerlich muss auch wieder der Callback entfernt werden,
+ * der noch an der Shell haengt.
+ */
+static void Destroy(XmComboBoxWidget w)
+{
+/* fprintf(stderr, "Destroy: %08X\n", w->core.window);*/
+ if ( w->combobox.ConvertBitmapToPixmap )
+ XFreePixmap(XtDisplay((Widget) w),
+ w->combobox.LabelPixmap);
+ if ( w->combobox.ConvertBitmapToPixmapInsensitive )
+ XFreePixmap(XtDisplay((Widget) w),
+ w->combobox.LabelInsensitivePixmap);
+ if ( w->combobox.PendingFocusOut )
+ XtRemoveWorkProc(w->combobox.WorkProcID);
+ XtRemoveEventHandler(w->combobox.MyNextShell,
+ StructureNotifyMask | FocusChangeMask,
+ True, (XtEventHandler) ShellCallback,
+ (XtPointer) w);
+} /* Destroy */
+
+/* ---------------------------------------------------------------------------
+ * Ueberpruefe, ob fuer die Ressource "DropDownOffset" ein gueltiger Wert vom
+ * Benutzer angegeben wurde. Diese Ressource gibt an, wie weit die Drop-Down-
+ * Liste nach rechts gegenueber dem Eingabefeld eingerueckt sein soll. Wenn
+ * hierfuer ein negativer Wert angegeben ist, so berechne statt dessen einen
+ * Standardwert: dieser entspricht der Breite der Pfeilschaltflaeche, was
+ * optisch ganz gut wirkt (jedenfall nach meinem Dafuerhalten).
+ */
+static void CheckDropDownOffset(XmComboBoxWidget w)
+{
+ if ( w->combobox.DropDownOffset < 0 ) {
+ XtWidgetGeometry ArrowGeom;
+
+ XtQueryGeometry(w->combobox.ArrowCtrl, NULL, &ArrowGeom);
+ w->combobox.DropDownOffset = ArrowGeom.width;
+ }
+} /* CheckDropDownOffset */
+
+/* --------------------------------------------------------------------
+ * Berechne die voreinzustellende Groesse, die diese Combo-Box be-
+ * sitzen muss, um ausreichenden Raum fuer das Eingabefeld und den
+ * Pfeil rechts daneben zur Verfuegung zu stellen. Bei einer
+ * editierbaren Combo-Box ist zwischen dem Eingabefeld und dem Pfeil
+ * noch ein Angst-Rasen von der halben Breite eines Pfeiles vorhanden.
+ * Wird das Listenfeld staendig dargestellt, so entfallen sowohl Pfeil
+ * als auch der Angstrasen, dafuer muss aber die Hoehe des Listenfelds
+ * beruecksichtigt werden.
+ */
+static void DefaultGeometry(XmComboBoxWidget w,
+ Dimension *TotalWidth,
+ Dimension *TotalHeight,
+ Dimension *EditCtrlWidth,
+ Dimension *LabelCtrlWidth)
+{
+ XtWidgetGeometry EditGeom, ArrowGeom, LabelGeom, ListGeom;
+
+ XtQueryGeometry(w->combobox.EditCtrl, NULL, &EditGeom);
+ XtQueryGeometry(w->combobox.ArrowCtrl, NULL, &ArrowGeom);
+ XtQueryGeometry(w->combobox.LabelCtrl, NULL, &LabelGeom);
+
+ /*
+ * Soll die Pfeilschaltflaeche quadratisch, praktisch, gut sein?
+ */
+ if ( w->combobox.SquareArrow )
+ ArrowGeom.width = ArrowGeom.height;
+ else
+ ArrowGeom.width = (ArrowGeom.height * 4) / 5;
+
+ /*
+ * Zuerst einmal ein paar einfache Werte ermitteln und zurueckgeben...
+ */
+ *TotalHeight = EditGeom.height;
+ *EditCtrlWidth = EditGeom.width;
+ *LabelCtrlWidth = LabelGeom.width;
+
+ /*
+ * Ermittele nun die Breite, welche die Combobox benoetigt. Je nach-
+ * dem, ob das Eingabefeld oder die Liste breiter sind, wird der
+ * entsprechende Wert genommen. Diese Auswahl zwischen der Breite von
+ * Eingabefeld und Liste findet aber nur statt, wenn die Liste auch
+ * wirklich staendig sichtbar ist. Waehrend der Initialisierung hat
+ * allerdings XmNcolumns, so dass in diesem Moment die List nicht
+ * mehr die Breite kontrollieren kann!
+ */
+ if ( w->combobox.StaticList ) {
+ /*
+ * Beachte: Frage nicht die Listbox, sondern das ScrolledWindow,
+ * in welchem die Liste eingebettet ist.
+ */
+ CheckDropDownOffset(w);
+ XtQueryGeometry(XtParent(w->combobox.ListCtrl), NULL, &ListGeom);
+ if ( w->combobox.InInit ) {
+ *TotalWidth = EditGeom.width;
+ } else {
+ if ( EditGeom.width < (Dimension)
+ (ListGeom.width + w->combobox.DropDownOffset) )
+ *TotalWidth = ListGeom.width + w->combobox.DropDownOffset;
+ else
+ *TotalWidth = EditGeom.width;
+ }
+ *TotalHeight += ListGeom.height;
+ } else {
+ /*
+ * Das Listenfeld interessiert uns hier nicht. Degegen sollte noch
+ * die Breite fuer den Pfeil und ein evtl. Angstrasen beachtet
+ * werden.
+ */
+ *TotalWidth = EditGeom.width + ArrowGeom.width;
+ if ( w->combobox.Editable && w->combobox.ArrowSpacingOn )
+ *TotalWidth += ArrowGeom.width/2;
+ }
+
+ /*
+ * Vergiss nicht, auch noch ein evtl. sichtbares Schriftfeld zu berueck-
+ * sichtigen!
+ */
+ if ( w->combobox.ShowLabel )
+ *TotalWidth += LabelGeom.width;
+
+} /* DefaultGeometry */
+
+/* --------------------------------------------------------------------
+ * Anhand eines Widgets ermittele darueber die Screennummer desjenigen
+ * Screens, auf dem das Widget erscheint.
+ * Parameter:
+ * w betroffenes Widget.
+ * Ergebnis:
+ * Nummer desjenigen Screens, auf dem das Widget angezeigt wird.
+ */
+static int WidgetToScreen(Widget w)
+{
+ Screen *screen;
+ Display *display;
+ int NumScreens, i;
+
+ screen = XtScreen(w); NumScreens = ScreenCount(XtDisplay(w));
+ display = DisplayOfScreen(screen);
+ for ( i = 0; i < NumScreens; ++i )
+ if ( ScreenOfDisplay(display, i) == screen )
+ return i;
+ XtError("WidgetToScreen: data structures are destroyed.");
+} /* WidgetToScreen */
+
+/* --------------------------------------------------------------------
+ * Positioniere die DropDown-Liste (soweit sie natuerlich auch momentan
+ * sichtbar ist) so auf dem Bildschirm, dass sie sich unterhalb des
+ * Eingabefeldes anschliesst.
+ */
+static void DoDropDownLayout(XmComboBoxWidget w)
+{
+ Position abs_x, abs_y;
+ Dimension ArrowWidth, ListWidth, ListHeight;
+ Dimension ScreenHeight, LabelWidth;
+ Window Decoration;
+ XWindowChanges WindowChanges;
+
+ /*
+ * etwa nicht sichtbar ?!! Oder etwa immer sichtbar ?!!
+ * Dann sind wir jetzt sofort fertig.
+ */
+ if ( !w->combobox.ListVisible || w->combobox.StaticList ) return;
+ /*
+ * Finde zuerst einmal heraus, wo wir uns denn auf dem Bildschirm be-
+ * finden sollen... Beachte dabei auch, dass eventuell die Liste zu schmal
+ * werden koennte und gib' ihr dann ggf. eine Mindestbreite, damit es
+ * keinen core-Dump gibt.
+ */
+ XtVaGetValues(w->combobox.ArrowCtrl, XmNwidth, &ArrowWidth, NULL);
+ XtTranslateCoords((Widget) w, 0, w->core.height, &abs_x, &abs_y);
+ CheckDropDownOffset(w);
+ ListWidth = w->core.width - w->combobox.DropDownOffset - 2;
+ abs_x += w->combobox.DropDownOffset;
+ if ( w->combobox.ShowLabel ) {
+ XtVaGetValues(w->combobox.LabelCtrl, XmNwidth, &LabelWidth, NULL);
+ ListWidth -= LabelWidth;
+ abs_x += LabelWidth;
+ }
+ if ( ListWidth < 20 ) ListWidth = 20;
+ XtVaGetValues(XtParent(w->combobox.ListCtrl), XmNheight, &ListHeight, NULL);
+ /*
+ * Hier ueberpruefen wir noch, ob die Liste unten aus dem Bildschirm
+ * herausfallen wuerde. In dem Fall klappen wir die Liste oberhalb des
+ * Eingabefeldes auf.
+ */
+ ScreenHeight = DisplayHeight(XtDisplay((Widget) w),
+ WidgetToScreen((Widget) w));
+ if ( abs_y + ListHeight + 2 > ScreenHeight ) {
+ int y;
+
+ y = ((int) abs_y) - ListHeight - w->core.height - 1;
+ if ( y < 0 ) y = 0;
+ abs_y = (Position) y;
+ }
+ XtConfigureWidget(w->combobox.PopupShell,
+ abs_x, abs_y, ListWidth, ListHeight, 1);
+ /*
+ * So...das hier dient der Kosmetik: hier sorgen wir dafuer, dass die
+ * Liste auch wirklich immer direkt ueber der ComboBox innerhalb des
+ * Fensterstapels schwebt. Siehe dazu auch die Erlaeuterungen und An-
+ * merkungen in GetDecorationWindow().
+ */
+ if ( XtIsRealized((Widget) w) ) {
+ WindowChanges.sibling = GetDecorationWindow(w);
+ WindowChanges.stack_mode = Above;
+ XReconfigureWMWindow(XtDisplay((Widget) w),
+ XtWindow(w->combobox.PopupShell),
+ WidgetToScreen(w->combobox.PopupShell),
+ CWSibling | CWStackMode, &WindowChanges);
+ }
+} /* DoDropDownLayout */
+
+/* --------------------------------------------------------------------
+ * Naja... diese Routine scheint ja bereits zu einer Institution beim
+ * Schreiben von Composite-Widgets geworden zu sein.
+ *
+ * Hier beim ComboBox-Widget ist die Aufgabe ziemlich einfach: es
+ * genuegt, die Eingabezeile und den Pfeil-Button entsprechend inner-
+ * halb des ComboBox-Widgets zu plazieren. Seit allerdings noch das
+ * Textlabel hinzukommt, wird's langsam aufwendiger. Nun ja - da sich
+ * das Listenfeld wahlweise auch statisch einblenden laesst, ist nun
+ * noch mehr zu beruecksichtigen, wenn die Kinder-Widgets an ihre
+ * Plaetze geschoben werden.
+ */
+static void DoLayout(XmComboBoxWidget w)
+{
+ Dimension EditCtrlWidth, ArrowCtrlWidth, LabelCtrlWidth;
+ Dimension ComboBoxHeight;
+ Dimension BorderWidth;
+ Dimension HighlightThickness;
+ Position EditX;
+
+ XtVaGetValues(w->combobox.ArrowCtrl,
+ XmNheight, &ArrowCtrlWidth, NULL);
+ if ( !w->combobox.SquareArrow )
+ ArrowCtrlWidth = (ArrowCtrlWidth * 4) / 5;
+ XtVaGetValues(w->combobox.LabelCtrl,
+ XmNwidth, &LabelCtrlWidth, NULL);
+
+ /*
+ * In Abhaengigkeit davon, ob die ComboBox editierbar ist und ob das
+ * Listenfeld staendig sichtbar sein soll, hier die Breite einzelner
+ * Widgets bestimmen.
+ */
+ if ( w->combobox.StaticList ) {
+ ComboBoxHeight = w->combobox.EditCtrl->core.height;
+ EditCtrlWidth = w->core.width;
+ } else {
+ ComboBoxHeight = w->core.height;
+ EditCtrlWidth = w->core.width - ArrowCtrlWidth;
+ if ( w->combobox.Editable && w->combobox.ArrowSpacingOn )
+ EditCtrlWidth -= ArrowCtrlWidth/2;
+ }
+ /* Beruecksichtige noch ein evtl. ebenfalls anzuzeigendes Schriftfeld
+ * neben dem Eingabefeld.
+ */
+ if ( w->combobox.ShowLabel ) {
+ EditX = LabelCtrlWidth;
+ EditCtrlWidth -= LabelCtrlWidth;
+ } else
+ EditX = 0;
+ if ( EditCtrlWidth < 20 ) EditCtrlWidth = 20;
+/* Plaziere nun das Eingabefeld... */
+ XtVaGetValues(w->combobox.EditCtrl,
+ XmNborderWidth, &BorderWidth,
+ XmNhighlightThickness, &HighlightThickness,
+ NULL);
+ XtConfigureWidget(w->combobox.EditCtrl,
+ EditX, 0,
+ EditCtrlWidth, ComboBoxHeight, BorderWidth);
+/* ...und nun den Pfeil... */
+ XtVaGetValues(w->combobox.ArrowCtrl,
+ XtNborderWidth, &BorderWidth, NULL);
+ XtConfigureWidget(w->combobox.ArrowCtrl,
+ w->core.width-ArrowCtrlWidth, HighlightThickness,
+ ArrowCtrlWidth,
+ ComboBoxHeight - 2 * HighlightThickness,
+ BorderWidth);
+/* ...und ggf. das Textlabel. */
+ if ( w->combobox.ShowLabel ) {
+ XtVaGetValues(w->combobox.LabelCtrl,
+ XmNborderWidth, &BorderWidth,
+ NULL);
+ XtConfigureWidget(w->combobox.LabelCtrl,
+ 0, 0,
+ LabelCtrlWidth, ComboBoxHeight,
+ BorderWidth);
+ }
+/* Falls da noch die Liste herumgurkt... */
+ if ( w->combobox.StaticList ) {
+ Dimension Width, Height;
+
+ if ( w->core.height > ComboBoxHeight )
+ Height = w->core.height - ComboBoxHeight;
+ else
+ Height = 10;
+
+ if ( w->core.width > (Dimension)(ArrowCtrlWidth + EditX) )
+ Width = w->core.width - ArrowCtrlWidth - EditX;
+ else
+ Width = 10;
+
+ XtConfigureWidget(XtParent(w->combobox.ListCtrl),
+ EditX + ArrowCtrlWidth, ComboBoxHeight, Width, Height, 0);
+ } else if ( w->combobox.ListVisible )
+ DoDropDownLayout(w);
+} /* DoLayout */
+
+/* --------------------------------------------------------------------
+ * Pappi fragt nach, wie gross wir denn sein wollen.
+ * Die hier benutzte Vorgehensweise zur Ermittlung der Groesse:
+ * Sobald der Vater uns eine Breite (oder aber Hoehe) vorschlaegt,
+ * die fuer uns eigentlich zu klein ist, meckern wir und schlagen
+ * die von uns benoetigte Breite (Hoehe) vor.
+ * Soweit also zur Theorie... leider sieht es beispielsweise das
+ * Motif Form-Widget ueberhaupt nicht ein, uns auch nur ein einziges
+ * Mal nach unseren Wuenschen zu fragen! Damit es bei derart unum-
+ * gaenglichen Widgets dann doch noch geht, muss ChangedManaged die
+ * Kohlen wieder aus dem Feuer holen mit einer Sondertour.
+ * Parameter:
+ * *Request Vom Vater vorgeschlagene Geometrie
+ * Ergebnis:
+ * *Reply Unsere Antwort auf die vorgeschlagene Geometrie
+ * sowie XtGeometryYes oder XtGeometryAlmost, je nachdem, wie gut
+ * uns Pappis Vorschlag in den Kram passt.
+ */
+static XtGeometryResult QueryGeometry(XmComboBoxWidget w,
+ XtWidgetGeometry *Request,
+ XtWidgetGeometry *Reply)
+{
+ XtGeometryResult result = XtGeometryYes;
+ Dimension minW, minH, editW, labelW;
+
+/* Elternteil will nichts weiter aendern, also ist uns das
+ * recht so.
+ */
+ Request->request_mode &= CWWidth | CWHeight;
+ if ( Request->request_mode == 0 ) return result;
+
+ DefaultGeometry(w, &minW, &minH, &editW, &labelW);
+
+/* Ueberpruefe, ob uns das in der Breite passt, was Pappi moechte... */
+ if ( Request->request_mode & CWWidth ) {
+ if ( Request->width < minW ) {
+/* Wenn Pappi uns etwas vorschlaegt, was im wahrsten Sinne des Wortes
+ * vorn und hinten nicht reicht, dann versuchen wir ihn entsprechend
+ * zu korrigieren. ("Versuchen" deshalb, weil er diesen Vorschlag auch
+ * voellig ignorieren kann.)
+ */
+ result = XtGeometryAlmost;
+ Reply->width = minW;
+ Reply->request_mode |= CWWidth;
+ }
+ }
+/* Die ganze Chose nun noch vertikal */
+ if ( Request->request_mode & CWHeight ) {
+ if ( Request->height < minH ) {
+ result = XtGeometryAlmost;
+ Reply->height = minH;
+ Reply->request_mode |= CWHeight;
+ }
+ }
+ return result;
+} /* QueryGeometry */
+
+/* --------------------------------------------------------------------
+ * Die Groesse des ComboBox-Widgets hat sich veraendert und deshalb
+ * mussen alle Kinder neu positioniert werden.
+ * Letzten Endes laeuft hier alles auf ein ordinaeres DoLayout()
+ * hinaus, um die Kinder umher zu schieben.
+ * Parameter:
+ * w Die bereits hinlaenglich bekannte Instanz dieses
+ * Widgets
+ */
+static void Resize(XmComboBoxWidget w)
+{
+ DoLayout(w);
+} /* Resize */
+
+/* --------------------------------------------------------------------
+ * Dieses Widget hat sich in irgendeiner Form bewegt (und das nicht
+ * nur relativ zum Vater, sondern moeglicherweise auch der Vater
+ * selbst!) bzw. die Shell, in der sich irgendwo unsere Combo-Box
+ * befindet, hat soeben den Fokus verschusselt und kann ihn nicht
+ * mehr wiederfinden. Daneben kann es auch sein, dass die Shell
+ * ikonisiert wurde. (Welch' Vielfalt! Dieses ist hier halt eine
+ * multifunktionale Routine.)
+ *
+ * Parameter:
+ * w Die naechste Shell in Reichweite ueber unserer
+ * Combo-Box.
+ * cbw Diese Combo-Box.
+ * event ^ auf den Event, enthaelt genauerere Informationen
+ * (naja... sieht so aus, als ob Motif hier auch
+ * schon 'mal Schrott 'reinpackt...)
+ * ContDispatch Auf True setzen, damit dieser Event noch weiter-
+ * gereicht wird an all' die anderen, die auch noch
+ * mithoeren.
+ */
+static void ShellCallback(Widget w, XtPointer pClientData,
+ XEvent *event, Boolean *ContDispatch)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+
+ switch ( event->type ) {
+ case ConfigureNotify:
+ case CirculateNotify:
+ DoDropDownLayout((XmComboBoxWidget) cbw);
+ break;
+ case FocusOut:
+ LOG3("ShellCallback: FocusOut, mode: %i, detail: %i\n",
+ (int)event->xfocus.mode, (int)event->xfocus.detail);
+ if ( cbw->combobox.Persistent )
+ cbw->combobox.IgnoreFocusOut = True;
+ else if ( (event->xfocus.mode == NotifyGrab) &&
+ cbw->combobox.ListVisible )
+ cbw->combobox.IgnoreFocusOut = True;
+ break;
+ case UnmapNotify:
+ ShowHideDropDownList((XmComboBoxWidget) cbw,
+ event, False);
+ break;
+ }
+ *ContDispatch = True;
+} /* ShellCallback */
+
+/* --------------------------------------------------------------------
+ * Diese Routine sorgt dafuer, dass die Liste nicht irrtuemlich bei
+ * manchen Window Managern vom Bildschirm genommen wird, bloss weil
+ * diese der OverrideShell den Tastaturfocus schenken bzw. diesen
+ * dem Combo-Box-Widget wegnehmen, sobald der Mauszeiger in die Liste
+ * bewegt wird.
+ */
+static void OverrideShellCallback(Widget w, XtPointer pClientData,
+ XEvent *event, Boolean *ContDispatch)
+
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+ switch ( event->type ) {
+ case EnterNotify:
+ LOG2("OverrideShellCallback: EnterNotify, PendingFO: %s\n",
+ cbw->combobox.PendingFocusOut ? "True" : "False");
+ if ( cbw->combobox.PendingFocusOut )
+ cbw->combobox.IgnoreFocusOut = True;
+ if ( cbw->combobox.TwmHandlingOn )
+ cbw->combobox.PendingOverrideInOut = True;
+ break;
+ case LeaveNotify:
+ LOG("OverrideShellCallback: LeaveNotify\n");
+ if ( cbw->combobox.TwmHandlingOn )
+ cbw->combobox.PendingOverrideInOut = True;
+ break;
+ }
+} /* OverrideShellCallback */
+
+/* --------------------------------------------------------------------
+ * Ha! Anscheinend kann man das Problem mit der einklappenden Liste,
+ * sobald man den Arrow-Button anklickt, doch loesen! Allerdings geht
+ * das auch nur von hinten durch die Brust in's Auge. Hier war die
+ * Reihenfolge der Events bislang das Problem: Klickt man den Arrow-
+ * Button an, so verliert das Eingabefeld den Focus, dann wird leider
+ * schon die WorkProc aktiviert und laesst die Liste verschwinden.
+ * Danach erst kommt der Arrow-Button-Callback an die Reihe. Um dieses
+ * Dilemma doch noch zu loesen, wird hier darauf gelauert, wann und
+ * welcher LeaveNotify kommt. Klickt der Benutzer den Pfeil an, so
+ * kommt hier noch rechtzeitig ein LeaveNotify vorbei, der aber durch
+ * einen Grab ausgeloest wurde. Und das ist eben nur beim Anklicken
+ * der Fall. Damit wissen wir, das der FocusOut getrost ignoriert
+ * werden darf.
+ * Puhhh -- ist das ein kompliziertes Chaos.
+ * Uebrigends...auch wenn manche Befehle zuerst ueberfluessig er-
+ * scheinen...sie sind erforderlich, damit die ComboBox auch mit unter-
+ * schiedlichen Window Managern zurechtkommt!
+ */
+static void ArrowCrossingCallback(Widget w, XtPointer pClientData,
+ XEvent *event, Boolean *ContDispatch)
+
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+ switch ( event->type ) {
+ case LeaveNotify:
+ LOG2("ArrowCrossingCallback: LeaveNotify, mode: %i\n",
+ event->xcrossing.mode);
+ if ( event->xcrossing.mode == NotifyGrab )
+ cbw->combobox.IgnoreFocusOut = True;
+ else
+ cbw->combobox.IgnoreFocusOut = False;
+ break;
+ }
+} /* ArrowCrossingCallback */
+
+/* --------------------------------------------------------------------
+ * Alle Hilfeaufrufe innerhalb der Kinder gehen an das eigentliche
+ * Combo-Box-Widget weiter, so dass auch hier nach aussen hin die
+ * Kinder-Widgets nicht in Erscheinung treten.
+ */
+static void HelpCallback(Widget w, XtPointer cbw, XtPointer CallData)
+{
+ XtCallCallbacks((Widget) cbw, XmNhelpCallback, CallData);
+} /* HelpCallback */
+
+/* --------------------------------------------------------------------
+ * Wenn der Benutzer im Eingabefeld osfActivate drueckt, dann dieses
+ * Ereignis offiziell bekanntgeben.
+ */
+static void ActivateCallback(Widget w, XtPointer cbw, XtPointer CallData)
+{
+ XtCallCallbacks((Widget) cbw, XmNactivateCallback, CallData);
+} /* ActivateCallback */
+
+/* --------------------------------------------------------------------
+ * Ein Kind moechte sein Groesse veraendern und fragt deshalb hier bei
+ * uns an.
+ * Parameter:
+ * w Naja...
+ * *Request Vorschlag des Kindes
+ * Ergebnis:
+ * *Reply Unsere Antwort darauf
+ * XtGeometryNo, da es uns bislang grundsaetzlich nie passt, es sei
+ * denn, es ist das Label... Naja, jetzt darf auch schon einmal das
+ * Listenfeld quengeln (aber nur, wenn es staendig sichtbar ist,
+ * ansonsten wird es nicht beruecksichtigt!).
+ */
+static XtGeometryResult GeometryManager(Widget w,
+ XtWidgetGeometry *Request,
+ XtWidgetGeometry *Reply)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
+ XtGeometryResult Result = XtGeometryNo;
+
+ /*
+ * Falls das Listenfeld statisch dargestellt wird, muessen wir seine
+ * Wuensche doch beruecksichtigen. Was fuer ein Aufwand...
+ */
+ if ( (w == XtParent(cbw->combobox.ListCtrl)) && cbw->combobox.StaticList ) {
+ Dimension TotalWidth, TotalHeight, EditWidth, LabelWidth;
+ XtWidgetGeometry MyRequest, YourReply, EditGeom;
+
+ XtQueryGeometry(cbw->combobox.EditCtrl, NULL, &EditGeom);
+ DefaultGeometry(cbw, &TotalWidth, &TotalHeight,
+ &EditWidth, &LabelWidth);
+ CheckDropDownOffset(cbw);
+
+ if ( Request->request_mode && CWWidth )
+ if ( (Dimension)(LabelWidth + cbw->combobox.DropDownOffset +
+ Request->width) > TotalWidth )
+ TotalWidth = LabelWidth + cbw->combobox.DropDownOffset +
+ Request->width;
+
+ if ( Request->request_mode && CWHeight )
+ TotalHeight = EditGeom.height + Request->height;
+ /*
+ * Bastele nun eine Anfrage an Pappi zusammen und geh' ihm damit auf den
+ * Keks. Wenn er zustimmt, ist sofort alles gut, wir muessen dann nur
+ * noch das Layout aufpolieren, damit das Listenfeld die neue Groesse
+ * bekommt. Wenn Pappi nur halb zustimmt, akzeptieren wir das und fragen
+ * ihn damit noch einmal....
+ */
+ MyRequest.request_mode = CWWidth | CWHeight;
+ MyRequest.width = TotalWidth;
+ MyRequest.height = TotalHeight;
+ Result = XtMakeGeometryRequest((Widget) cbw, &MyRequest, &YourReply);
+ if ( Result == XtGeometryAlmost ) {
+ MyRequest.width = YourReply.width;
+ MyRequest.height = YourReply.height;
+ Result = XtMakeGeometryRequest((Widget) cbw, &MyRequest, &YourReply);
+ }
+ if ( Result == XtGeometryYes )
+ DoLayout(cbw);
+ } else
+ /*
+ * Ansonsten darf nur noch das Schriftfeld Ansprueche anmelden.
+ */
+ if ( w != cbw->combobox.LabelCtrl )
+ return XtGeometryNo; /* Was ICH hier vorgegeben habe, gilt! */
+ else if ( cbw->combobox.ShowLabel ) { /* Naja, 'mal schauen! */
+ Dimension TotalWidth, TotalHeight, EditWidth, LabelWidth;
+ XtWidgetGeometry MyRequest;
+
+ if ( Request->request_mode & CWWidth ) {
+ DefaultGeometry(cbw, &TotalWidth, &TotalHeight,
+ &EditWidth, &LabelWidth);
+ TotalWidth = TotalWidth - LabelWidth +
+ Request->width;
+
+ MyRequest.request_mode = CWWidth;
+ MyRequest.width = TotalWidth;
+ Result = XtMakeGeometryRequest((Widget) cbw, &MyRequest, NULL);
+
+ if ( Result == XtGeometryYes )
+ DoLayout(cbw);
+ }
+ }
+ return Result;
+} /* GeometryManager */
+
+/* --------------------------------------------------------------------
+ * Hier werden auf Wunsch diejenigen Farben, die bei der Combo-Box neu
+ * gesetzt wurden, an alle Kinder weitergegeben.
+ */
+#define BOTTOMSHADOWCOLOR 0x0001
+#define TOPSHADOWCOLOR 0x0002
+#define FOREGROUND 0x0004
+#define BACKGROUND 0x0008
+
+static struct { String Resource; int Flag; }
+ ColorResources[] = {
+ { XmNbottomShadowColor, BOTTOMSHADOWCOLOR },
+ { XmNtopShadowColor, TOPSHADOWCOLOR },
+ { XmNforeground, FOREGROUND },
+ { XmNbackground, BACKGROUND }
+ };
+
+static UpdateColors(XmComboBoxWidget w, int flags)
+{
+ Pixel Color;
+ int i, size = XtNumber(ColorResources);
+ Widget ScrolledWin, ScrollBar;
+
+ ScrolledWin = XtParent(w->combobox.ListCtrl);
+ XtVaGetValues(ScrolledWin, XmNverticalScrollBar, &ScrollBar, NULL);
+ for ( i=0; i<size; i++ )
+ if ( flags & ColorResources[i].Flag ) {
+ XtVaGetValues((Widget) w, ColorResources[i].Resource, &Color,
+ NULL);
+ XtVaSetValues(w->combobox.ListCtrl,
+ ColorResources[i].Resource, Color, NULL);
+ XtVaSetValues(ScrolledWin,
+ ColorResources[i].Resource, Color, NULL);
+ XtVaSetValues(ScrollBar,
+ ColorResources[i].Resource, Color, NULL);
+ XtVaSetValues(w->combobox.EditCtrl,
+ ColorResources[i].Resource, Color, NULL);
+ XtVaSetValues(w->combobox.LabelCtrl,
+ ColorResources[i].Resource, Color, NULL);
+ XtVaSetValues(w->combobox.ArrowCtrl,
+ ColorResources[i].Resource, Color, NULL);
+ if ( ColorResources[i].Flag & BACKGROUND )
+ XtVaSetValues(ScrollBar, XmNtroughColor, Color, NULL);
+ }
+
+ return 1;
+} /* UpdateColors */
+
+/* --------------------------------------------------------------------
+ * Liste aller vorgespiegelten Resourcen, die automatisch verarbeitet
+ * werden koennen, ohne weiter darueber nachdenken zu muessen...
+ */
+typedef enum { EDITCTRL, LISTCTRL, LABELCTRL } CHILDCTRL;
+typedef enum { RO, RW, RWS, RWL, RWI, RWIGNORE } aUniqueName;
+typedef struct {
+ String rsc;
+ CHILDCTRL ctrl;
+/* enum { RO, RW, RWS, RWL, RWI, RWIGNORE } dir; */
+ aUniqueName dir;
+ /* nur lesen, lesen&schreiben, lesen&schreiben spezial,
+ lesen&schreiben label, lesen&schreiben items */
+} MIRROR;
+
+/* Alle mit !!! gekennzeichneten Eintraege werden auf die richtigen
+ * Namen des entsprechenden Widgets umgesetzt.
+ */
+static MIRROR MirroredResources[] = {
+ { XmNitems, LISTCTRL, RWI }, /* Urgs! */
+ { XmNitemCount, LISTCTRL, RWIGNORE }, /* dto. */
+ { XmNlistMarginHeight, LISTCTRL, RW },
+ { XmNlistMarginWidth, LISTCTRL, RW },
+ { XmNlistSpacing, LISTCTRL, RW },
+ { XmNstringDirection, LISTCTRL, RO }, /* Naja? */
+ { XmNtopItemPosition, LISTCTRL, RO },
+
+ { XmNblinkRate, EDITCTRL, RW },
+ { XmNcolumns, EDITCTRL, RW },
+ { XmNcursorPosition, EDITCTRL, RW },
+ { XmNcursorPositionVisible, EDITCTRL, RW },
+ { XmNmarginHeight, EDITCTRL, RW },
+ { XmNmarginWidth, EDITCTRL, RW },
+ { XmNmaxLength, EDITCTRL, RW },
+ { XmNselectThreshold, EDITCTRL, RW },
+ { XmNvalue, EDITCTRL, RWS },
+
+ { XmNalignment, LABELCTRL, RW },
+ { XmNmnemonic, LABELCTRL, RW },
+ { XmNmnemonicCharSet, LABELCTRL, RW },
+ { XmNlabelPixmap, LABELCTRL, RW },
+ { XmNlabelInsensitivePixmap, LABELCTRL, RW },
+ { XmNlabelString, LABELCTRL, RW },
+ { XmNlabelType, LABELCTRL, RW },
+ { XmNlabelMarginBottom, LABELCTRL, RWL }, /* !!! */
+ { XmNlabelMarginHeight, LABELCTRL, RWL }, /* !!! */
+ { XmNlabelMarginLeft, LABELCTRL, RWL }, /* !!! */
+ { XmNlabelMarginRight, LABELCTRL, RWL }, /* !!! */
+ { XmNlabelMarginTop, LABELCTRL, RWL }, /* !!! */
+ { XmNlabelMarginWidth, LABELCTRL, RWL }, /* !!! */
+ { XmNlabelFontList, LABELCTRL, RWL }, /* !!! */
+};
+
+typedef struct {
+ char *from, *to;
+} TRANSFORMATION;
+static TRANSFORMATION Transformations[] = {
+ { XmNlabelMarginBottom, XmNmarginBottom },
+ { XmNlabelMarginHeight, XmNmarginHeight },
+ { XmNlabelMarginLeft, XmNmarginLeft },
+ { XmNlabelMarginRight, XmNmarginRight },
+ { XmNlabelMarginTop, XmNmarginTop },
+ { XmNlabelMarginWidth, XmNmarginWidth },
+ { XmNlabelFontList, XmNfontList },
+};
+
+/* --------------------------------------------------------------------
+ * Sobald irgendeine Resource veraendert wird, erfolgt der Aufruf
+ * hierin als Benachrichtigung, einmal nach dem rechten zu sehen.
+ * Parameter:
+ * current Kopie der Widget-Instanz, bevor irgendwelche
+ * Resourcen veraendert oder set_values()-Methoden
+ * aufgerufen wurden.
+ * req Kopie der Widget-Instanz, aber bereits mit den
+ * durch XtSetValues veraenderten Werten
+ * new aktuellster Zustand der Widget-Instanz mit
+ * veraenderten Werten (entweder durch XtSetValues
+ * oder set_values()-Methoden der Superklasse)
+ * args Argumentenliste beim Aufruf von XtSetValues()
+ * NumArgs Anzahl der Argumente in der Liste
+ * Ergebnis:
+ * True, falls Widget neu gezeichnet werden soll.
+ */
+static Boolean SetValues(XmComboBoxWidget current, XmComboBoxWidget req,
+ XmComboBoxWidget newW,
+ ArgList args, Cardinal *NumArgs)
+{
+ Boolean Update = False;
+ int i, j, MirrorSize = XtNumber(MirroredResources);
+ int k, TransformationSize = XtNumber(Transformations);
+ Arg arg;
+ int Flags;
+
+/*
+ * Alle Resourcen, die nicht mehr nach dem Erstellen der Widget-Instanz
+ * veraendert werden koennen.
+ */
+ newW->combobox.Editable = current->combobox.Editable;
+ newW->combobox.ListCtrl = current->combobox.ListCtrl;
+ newW->combobox.EditCtrl = current->combobox.EditCtrl;
+ newW->combobox.LabelCtrl = current->combobox.LabelCtrl;
+ newW->combobox.SelectionPolicy = current->combobox.SelectionPolicy;
+ newW->combobox.ListSizePolicy = current->combobox.ListSizePolicy;
+ newW->combobox.StaticList = current->combobox.StaticList;
+
+/*
+ * Kontrolliere nun alle Resourcen, die sich veraendert haben koennten
+ * und gebe die neuen Einstellungen entsprechend weiter...
+ *
+ * Hat sich der Sensitive-Zustand veraendert? Dann muessen wir hier dafuer
+ * sorgen, dass alle Kinder ebenfalls den neuen Zustand annehmen.
+ */
+ if ( current->core.sensitive != newW->core.sensitive ) {
+ XtSetSensitive(newW->combobox.ListCtrl, newW->core.sensitive);
+ XtSetSensitive(newW->combobox.EditCtrl, newW->core.sensitive);
+ XtSetSensitive(newW->combobox.ArrowCtrl, newW->core.sensitive);
+ XtSetSensitive(newW->combobox.ListCtrl, newW->core.sensitive);
+ if ( !newW->core.sensitive )
+ ShowHideDropDownList(newW, NULL, False);
+ }
+/*
+ * Die ScrollBarPolicy kann nur dann geaendert werden, wenn das Listenfeld
+ * dauerhaft dargestellt wird.
+ */
+ if ( newW->combobox.ScrollBarDisplayPolicy !=
+ current->combobox.ScrollBarDisplayPolicy )
+ if ( newW->combobox.StaticList )
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNscrollBarDisplayPolicy, newW->combobox.ScrollBarDisplayPolicy,
+ NULL);
+ else
+ XtWarning(
+"XmComboBox: ScrollBarDisplayPolicy can not be changed when StaticList == False."
+ );
+/* Anzahl der in der Liste gleichzeitig darstellbaren Eintraege */
+ if ( current->combobox.VisibleItemCount !=
+ newW->combobox.VisibleItemCount ) {
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNvisibleItemCount, newW->combobox.VisibleItemCount,
+ NULL);
+ Update = True;
+ }
+ if ( current->combobox.AutomaticSelection !=
+ newW->combobox.AutomaticSelection )
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNautomaticSelection, newW->combobox.AutomaticSelection,
+ NULL);
+
+/*
+ * benutzter Font: hier erhalten Liste und Eingabefeld jeweils die
+ * gleiche Fontliste, wohingegen das Label getrennt behandelt wird.
+ * Das macht auch Sinn, denn Liste und Eingabefeld beinhalten gleich-
+ * artigen Text, so dass hier auch tunlichst der gleiche Font zu
+ * benutzen ist.
+ */
+ if ( current->combobox.Font != newW->combobox.Font ) {
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNfontList, newW->combobox.Font, NULL);
+ XtVaSetValues(newW->combobox.EditCtrl,
+ XmNfontList, newW->combobox.Font, NULL);
+ Update = True;
+ }
+
+ Flags = 0;
+ if ( newW->manager.top_shadow_color !=
+ current->manager.top_shadow_color ) Flags |= TOPSHADOWCOLOR;
+ if ( newW->manager.bottom_shadow_color !=
+ current->manager.bottom_shadow_color ) Flags |= BOTTOMSHADOWCOLOR;
+ if ( newW->manager.foreground !=
+ current->manager.foreground ) Flags |= FOREGROUND;
+ if ( newW->core.background_pixel !=
+ current->core.background_pixel ) Flags |= BACKGROUND;
+ if ( Flags ) { UpdateColors(newW, Flags); Update = True; }
+
+
+ if ( newW->combobox.ArrowCursor != current->combobox.ArrowCursor ) {
+ if ( newW->combobox.ListVisible )
+ XDefineCursor(XtDisplay(newW->combobox.PopupShell),
+ XtWindow(newW->combobox.PopupShell),
+ newW->combobox.ArrowCursor);
+ }
+/* Hier werden die vorgespiegelten Resourcen verwaltet, die in
+ * Wirklichkeit zu einem unserer Kinder gehoeren.
+ */
+ for ( i = 0; i < *NumArgs; i++ ) {
+/* Ist es eine vorgespiegelte Resource ? Wenn ja, dann leite die
+ * Anfrage an das entsprechende Kind-Widget weiter.
+ */
+ for ( j = 0; j < MirrorSize; j++ ) {
+ if ( (strcmp(args[i].name, MirroredResources[j].rsc) == 0) ) {
+ switch ( MirroredResources[j].dir ) {
+ case RW: /* schreibender Zugriff erlaubt */
+ XtSetValues(MirroredResources[j].ctrl == LISTCTRL ?
+ newW->combobox.ListCtrl :
+ (MirroredResources[j].ctrl == EDITCTRL ?
+ newW->combobox.EditCtrl :
+ newW->combobox.LabelCtrl),
+ &(args[i]), 1);
+ break;
+ case RWS: /* schreibender Zugriff unter Kontrolle */
+ if ( strcmp(args[i].name, XmNvalue) == 0 ) {
+ if ( newW->combobox.Editable )
+ XtSetValues(newW->combobox.EditCtrl,
+ &(args[i]), 1);
+ }
+ break;
+ case RWL: /* Transformation in andere Resource beim
+ Label-Widget */
+ for ( k = 0; k < TransformationSize; k++ )
+ if ( strcmp(args[i].name, Transformations[k].from) == 0 ) {
+ arg.value = args[i].value;
+ arg.name = Transformations[k].to;
+ XtSetValues(newW->combobox.LabelCtrl,
+ &arg, 1);
+ break;
+ }
+ break;
+ case RWIGNORE: /* Zugriff auf XmNitemCount */
+ /* Wird von XmNitems erledigt! */
+ break;
+ case RWI: /* Zugriff auf XmNitems */
+ for ( k = 0; k < *NumArgs; k++ )
+ if ( strcmp(args[k].name, XmNitemCount) == 0 ) {
+ Arg MyArgs[2];
+
+ MyArgs[0].name = XmNitems;
+ MyArgs[0].value = args[i].value;
+ MyArgs[1].name = XmNitemCount;
+ MyArgs[1].value = args[k].value;
+ XtSetValues(newW->combobox.ListCtrl,
+ args, 2);
+ /*XtVaSetValues(newW->combobox.ListCtrl,
+ XmNitems, args[i].value,
+ XmNitemCount, args[k].value,
+ NULL);*/
+ break;
+ }
+ break;
+ case RO:
+ break;
+ } /* case write mode */
+ goto ScanForNextResource;
+ } /* if entry found */
+ } /* for every mirrored entry */
+ ScanForNextResource: ;
+ } /* for every Arg */
+
+ if ( (newW->combobox.SquareArrow != current->combobox.SquareArrow) ||
+ (newW->combobox.ArrowSpacingOn != current->combobox.ArrowSpacingOn) ) {
+ Update = False;
+ DoLayout(newW);
+ }
+
+ return Update;
+} /* SetValues */
+
+/* --------------------------------------------------------------------
+ * Werden irgendwelche Resourcen abgefragt, so muessen wir hier erst
+ * noch vor der Rueckkehr zum Frager klaeren, ob davon eine Resource
+ * betroffen ist, die nur vorgespiegelt ist, da sie eigentlich einem
+ * der Widgets gehoert, die von uns hier verwaltet werden, um daraus
+ * eine ordentliche Combo-Box zu machen.
+ * Parameter:
+ * w Widget-Instanz
+ * args Abgefragte Resourcen
+ * NumArgs Anzahl der abgefragten Resourcen
+ */
+static void GetValuesAlmost(XmComboBoxWidget w, ArgList args,
+ Cardinal *NumArgs)
+{
+ int i, j, MirrorSize = XtNumber(MirroredResources);
+ int k, TransformationSize = XtNumber(Transformations);
+ Arg arg;
+
+ for ( i = 0; i < *NumArgs; i++ ) {
+/* Ist es eine vorgespiegelte Resource ? Wenn ja, dann leite die
+ * Anfrage an das entsprechende Kind-Widget weiter.
+ */
+ for ( j = 0; j < MirrorSize; j++ ) {
+ if ( strcmp(args[i].name, MirroredResources[j].rsc) == 0 ) {
+ switch ( MirroredResources[j].dir ) {
+ case RO:
+ case RW:
+ case RWS:
+ case RWI:
+ XtGetValues(MirroredResources[j].ctrl == LISTCTRL ?
+ w->combobox.ListCtrl :
+ MirroredResources[j].ctrl == EDITCTRL ?
+ w->combobox.EditCtrl :
+ w->combobox.LabelCtrl,
+ &(args[i]), 1);
+ break;
+ case RWL: /* Umzuleitende Resource bei Label-Widget */
+ for ( k = 0; k < TransformationSize; k++ )
+ if ( strcmp(args[i].name, Transformations[k].from) == 0 ) {
+ arg.value = args[i].value;
+ arg.name = Transformations[k].to;
+ XtGetValues(w->combobox.LabelCtrl,
+ (ArgList) &arg, 1);
+ break;
+ }
+ break;
+ } /* case read mode */
+ } /* if entry found */
+ } /* for every mirrored entry */
+ } /* for every Arg */
+} /* GetValuesAlmost */
+
+/* --------------------------------------------------------------------
+ * Zeige beziehungsweise verstecke die Drop-Down-Liste der Combo-Box.
+ * Falls die Liste bereits den entsprechenden Zustand hat, geht's
+ * sofort zum Aufrufer zurueck.
+ * Parameter:
+ * w Her Royal Majesty ComboBox
+ * Show True, falls anzuzeigen, andernfalls False
+ */
+static void ShowHideDropDownList(XmComboBoxWidget w, XEvent *event,
+ Boolean Show)
+{
+ XmComboBoxDropDownCallbackStruct info;
+
+ if ( w->combobox.StaticList ||
+ (Show == w->combobox.ListVisible) ) return;
+ w->combobox.ListVisible = Show;
+ if ( Show ) { /* Klapp' die Liste aus! */
+ DoDropDownLayout(w);
+ info.reason = XmCR_SHOW_LIST;
+ info.event = event;
+ XtCallCallbacks((Widget) w, XmNdropDownCallback,
+ (XtPointer) &info);
+ XDefineCursor(XtDisplay(w->combobox.PopupShell),
+ XtWindow(w->combobox.PopupShell),
+ w->combobox.ArrowCursor);
+ XtPopup(w->combobox.PopupShell, XtGrabNone);
+ XtVaSetValues(w->combobox.ArrowCtrl,
+ XmNarrowDirection, XmARROW_UP, NULL);
+ } else { /* Klapp' die Liste wieder ein... */
+ XtPopdown(w->combobox.PopupShell);
+ XtVaSetValues(w->combobox.ArrowCtrl,
+ XmNarrowDirection, XmARROW_DOWN, NULL);
+ info.reason = XmCR_HIDE_LIST;
+ info.event = event;
+ XtCallCallbacks((Widget) w, XmNdropDownCallback,
+ (XtPointer) &info);
+ }
+} /* ShowHideDropDownList */
+
+/* --------------------------------------------------------------------
+ * Hier laeuft die Nachricht auf, dass der Pfeil ausgeloest wurde...
+ * (Daraufhin sollte die Liste aus- oder eingeklappt werden)
+ * ...oder dass der Benutzer da draussen auf der anderen Seite der
+ * Mattscheibe den Pfeil bereits anklickte ohne aber bereits losge-
+ * gelassen zu haben. Bereits hier bekommt das Eingabefeld den Fokus
+ * vor den Latz geknallt, denn sonst kann es passieren, dass zwar die
+ * Liste ausgeklappt ist, aber das Eingabefeld noch keinen Tastatur-
+ * fokus erhalten hat. Das sollte aber nicht so sein, denn es ist dann
+ * keine konsequente Tastaturbedienung.
+ */
+static void ArrowCallback(Widget w, XtPointer pClientData,
+ XmAnyCallbackStruct *info)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
+
+ switch ( info->reason ) {
+ case XmCR_ARM:
+ LOG("ArrowCallback: XmCR_ARM\n");
+ XmProcessTraversal(cbw->combobox.EditCtrl, XmTRAVERSE_CURRENT);
+ if ( cbw->combobox.TwmHandlingOn && cbw->combobox.ListVisible )
+ cbw->combobox.IgnoreFocusOut = True;
+ break;
+ case XmCR_ACTIVATE:
+ XmProcessTraversal(cbw->combobox.EditCtrl, XmTRAVERSE_CURRENT);
+ ShowHideDropDownList(cbw, info->event,
+ (Boolean)(!cbw->combobox.ListVisible));
+ break;
+ }
+} /* ArrowCallback */
+
+/* --------------------------------------------------------------------
+ * Diese Benachrichtigung moechte uns nur mitteilen, dass wir soeben
+ * den Fokus verloren haben (Ohhhh!) Sollte allerdings der Fokus nur
+ * aus dem Grunde perdue sein, dass der Anwender den Mauszeiger ausser-
+ * halb des Applikationsfensters plaziert hat, so koennen wir diese
+ * Nachricht uebergehen. Erst wenn der Fokus an ein anderes Widget in
+ * unserer Applikation verlorenging, muessen wir auf diese Information
+ * reagieren.
+ * Und jetzt zu noch einem total beknackten Problem - alles nur wegen
+ * Motif und den diversen Window-Managern (bspw. olwm)... Leider kommt
+ * beim FocusOut kein richtiger Hinweis auf den tatsaechlichen Event,
+ * der dieses Callback ausloeste -- warum liefert denn dann Motif ueber-
+ * haupt noch den Event???? Und ueberhauupt, die Geschichte mit dem
+ * Fokus ist schon der reinste Horror. Aktueller Ausweg: wenn wir die
+ * Benachrichtigung ueber den Focusabgang bekommen, registrieren wir
+ * eine Work-Prozedur, die, sobald der Rechner wieder Luft hat, auf-
+ * gerufen wird. Sie kann dann nachschauen, ob nicht inzwischen die
+ * OverrideShell den Focus bekahm. Wenn ja, koennen wir den FocusOut
+ * uebergehen, ansonsten muessen wir ihn beruecksichtigen.
+ * -- Ist das eine ^@#$^*(#$^&! (Meine gute Erziehung hindert mich
+ * daran, diesen Begriff hier zu nennen.)
+ */
+static Boolean DelayedFocusOutWorkProc(XtPointer pClientData)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+ LOG2("DelayedFocusOutWorkProc: IgnoreFocusOut: %s\n",
+ cbw->combobox.IgnoreFocusOut ? "True" : "False");
+ if ( !cbw->combobox.IgnoreFocusOut )
+ ShowHideDropDownList(cbw, &(cbw->combobox.xevent), False);
+ cbw->combobox.IgnoreFocusOut = False;
+ cbw->combobox.PendingFocusOut = False;
+ return True; /* diese Routine wird nicht mehr benoetigt. */
+} /* DelayedFocusOutWorkProc */
+
+static void EditFocusCallback(Widget w, XtPointer pClientData,
+ XmAnyCallbackStruct *info)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
+
+ if ( cbw->combobox.StaticList ) return;
+
+ if ( info->reason == XmCR_LOSING_FOCUS ) {
+ LOG2("EditFocusCallback: PendingFocusOut: %s, ",
+ cbw->combobox.PendingFocusOut ? "True" : "False");
+ LOG3("mode: %i, detail: %i, ", (int)info->event->xcrossing.mode,
+ (int)info->event->xcrossing.detail);
+ LOG2("PendingOverrideInOut: %s\n",
+ cbw->combobox.PendingOverrideInOut ? "True" : "False");
+ if ( !cbw->combobox.PendingFocusOut &&
+ !cbw->combobox.PendingOverrideInOut ) {
+ /* Normalerweise duerfen aber keine NULL-Events hier
+ * vorbeikommen...aber man weiss ja nie so genau und
+ * sicher ist sicher. Defensiv programmieren!
+ */
+ if ( info->event )
+ cbw->combobox.xevent = *info->event;
+ cbw->combobox.WorkProcID = XtAppAddWorkProc(
+ XtWidgetToApplicationContext((Widget) cbw),
+ (XtWorkProc) DelayedFocusOutWorkProc,
+ (XtPointer) cbw);
+ cbw->combobox.PendingFocusOut = True;
+ }
+ cbw->combobox.PendingOverrideInOut = False;
+ }
+} /* EditFocusCallback */
+
+/* --------------------------------------------------------------------
+ * Hier wird der angegebene Eintrag in der Listbox der Combo-Box
+ * markiert und zudem in den sichtbaren Bereich gerollt, sollte er
+ * sich ausserhalb des dargestellten Bereichs der Liste befinden.
+ * Parameter:
+ * w Die Combo-Box (ueblicher Parameter)
+ * Index Index des neu zu markierenden Eintrages.
+ * Notify Schickt Mitteilung via Callback
+ * Ergebnis:
+ * Index des markierten Eintrages oder 0, falls die Listbox leer
+ * war und deshalb auch kein Eintrag markiert werden konnte.
+ */
+static int SetSelectionPos(XmComboBoxWidget w, int Index, Boolean Notify)
+{
+ Widget ListBox = w->combobox.ListCtrl;
+ int ItemCount; /* Anzahl Eintraege in Listbox */
+ int TopItem, VisibleItems;
+
+ XtVaGetValues(ListBox, XmNitemCount, &ItemCount,
+ XmNtopItemPosition, &TopItem,
+ XmNvisibleItemCount, &VisibleItems,
+ NULL);
+ if ( Index < 1 ) Index = 1;
+ if ( Index > ItemCount ) Index = ItemCount;
+ if ( Index != 0 && ItemCount != 0 ) {
+ if ( Index < TopItem )
+ XmListSetPos(ListBox, Index);
+ if ( Index >= TopItem + VisibleItems )
+ XmListSetBottomPos(ListBox, Index);
+ XmListSelectPos(ListBox, Index, Notify);
+ return Index;
+ } else
+ return 0;
+} /* SetSelectionPos */
+
+/* --------------------------------------------------------------------
+ * Diese Routine kuemmert sich darum, denjenigen Eintrag aus der List-
+ * box mit der angegebenen Nummer herauszufischen und an die Eingabe-
+ * zeile zu uebergeben. Dabei wird der Index auf den Eintrag auto-
+ * matisch auf den zulaessigen Bereich begrenzt. Zugleich wird auch
+ * noch der angegebene Eintrag in der Listbox markiert.
+ */
+static void TransferToEditCtrl(XmComboBoxWidget w, int SelectionIndex,
+ Boolean MayWipeOut)
+{
+ Widget ListBox = w->combobox.ListCtrl;
+ XmStringTable Items;
+ char *pItemText;
+
+ XtVaGetValues(ListBox, XmNitems, &Items, NULL);
+
+ if ( MayWipeOut &&
+ (SelectionIndex == w->combobox.LastSelection) &&
+ (w->combobox.SelectionPolicy == XmSINGLE_SELECT) ) {
+ SelectionIndex = 0;
+ }
+
+ if ( (SelectionIndex == 0) &&
+ (w->combobox.SelectionPolicy == XmSINGLE_SELECT) ) {
+ XmListDeselectAllItems(w->combobox.ListCtrl);
+ w->combobox.PassVerification = True;
+ XmTextFieldSetString(w->combobox.EditCtrl, "");
+ } else {
+ SelectionIndex = SetSelectionPos(w, SelectionIndex, False);
+ if ( SelectionIndex > 0 ) {
+ XmStringGetLtoR(Items[SelectionIndex-1],
+ XmSTRING_DEFAULT_CHARSET, &pItemText);
+ w->combobox.PassVerification = True;
+ XmTextFieldSetString(w->combobox.EditCtrl, pItemText);
+ XtFree(pItemText);
+ }
+ }
+ w->combobox.LastSelection = SelectionIndex;
+} /* TransferToEditCtrl */
+
+/* --------------------------------------------------------------------
+ * Alle registrierten Callbacks bei Anwahl eines neuen Eintrages in
+ * der Listbox aktivieren.
+ */
+static void CallSelectionCBL(XmComboBoxWidget w, XEvent *Event)
+{
+ int index;
+
+ index = XmComboBoxGetSelectedPos((Widget) w);
+ /*
+ * Wenn momentan KEIN Eintrag selektiert ist, dann rufe den neuen
+ * XmNunselectionCallback auf!
+ */
+ if ( index == 0 ) {
+ XmComboBoxUnselectionCallbackStruct info;
+
+ info.reason = XmCR_UNSELECT;
+ info.event = Event;
+ XtCallCallbacks((Widget) w, XmNunselectionCallback, (XtPointer) &info);
+ } else {
+ /*
+ * Ansonsten den ueblichen SelectionCallback!
+ */
+ XmComboBoxSelectionCallbackStruct info;
+ XmStringTable Items;
+
+ info.reason = w->combobox.SelectionPolicy == XmSINGLE_SELECT ?
+ XmCR_SINGLE_SELECT : XmCR_BROWSE_SELECT;
+ info.event = Event;
+ info.index = index;
+ XtVaGetValues(w->combobox.ListCtrl, XmNitems, &Items, NULL);
+ info.value = Items[info.index-1];
+ XtCallCallbacks((Widget) w, XmNselectionCallback, (XtPointer) &info);
+ }
+} /* CallSelectionCBL */
+
+/* --------------------------------------------------------------------
+ * Hier laeuft das Tastatur-Management fuer die ComboBox zusammen.
+ * ACHTUNG: Der 'w'-Parameter wird nur benoetigt, um das eigentliche
+ * ComboBox-Widget zu ermitteln. Er muss daher die ID eines direkten
+ * Kinds der ComboBox enthalten!
+ */
+static void CBoxManager(Widget w, XEvent *Event, String *params,
+ Cardinal *num_params)
+{
+ XmComboBoxWidget cbw;
+ Widget ListBox;
+ int *SelectionList;
+ int SelectionCount;
+ int SelectionIndex; /* Wer denn nun markiert wird... */
+ int ItemCount; /* Anzahl Eintraege in Listbox */
+ int VisibleItems; /* Hoehe der Liste in Eintraegen */
+ char opt;
+
+ /*
+ * Nur wenn eine der Translationen page-up und page-down direkt im
+ * Listenfeld ausgeloest wurden, wird auch als "w" die Liste ueber-
+ * geben. Bei allen anderen Faellen ist dieses zumeist das TextField.
+ */
+ if ( XtClass(w) == xmListWidgetClass )
+ cbw = (XmComboBoxWidget) XtParent(XtParent(w));
+ else
+ cbw = (XmComboBoxWidget) XtParent(w);
+ ListBox = cbw->combobox.ListCtrl;
+
+ switch ( *(params[0]) ) {
+/* --------------------------------------------------------------------
+ * Klappe die Liste auf Wunsch des Benutzers aus oder wieder ein.
+ */
+ case 's': /* show-hide-list */
+ ShowHideDropDownList(cbw, Event,
+ (Boolean)(!cbw->combobox.ListVisible));
+ break;
+ case 'h': /* hide-list */
+ ShowHideDropDownList(cbw, Event, False);
+ break;
+/* --------------------------------------------------------------------
+ * Hier werden die Bewegungen in der Listbox behandelt.
+ */
+ case 'u': /* up */
+ case 'd': /* down */
+ case 't': /* top */
+ case 'b': /* bottom */
+ case 'p': /* page-up/page-down */
+ opt = *(params[0]);
+ XtVaGetValues(ListBox, XmNitemCount, &ItemCount,
+ XmNvisibleItemCount, &VisibleItems, NULL);
+ if ( XmListGetSelectedPos(ListBox,
+ &SelectionList, &SelectionCount) ) {
+ SelectionIndex = *SelectionList;
+ XtFree((char *)SelectionList);
+ switch ( opt ) {
+ case 'u': SelectionIndex--; break;
+ case 'd': SelectionIndex++; break;
+ case 't': SelectionIndex = 1; break;
+ case 'b': SelectionIndex = ItemCount; break;
+ case 'p': if ( *(params[0]+5) == 'u' )
+ SelectionIndex -= VisibleItems;
+ else
+ SelectionIndex += VisibleItems;
+ break;
+ }
+ } else { /* momentan noch kein Eintrag in der Liste ausgewaehlt */
+ if ( opt == 'b' ) SelectionIndex = ItemCount;
+ else SelectionIndex = 1; /* nun ersten Eintrag nehmen */
+ }
+ TransferToEditCtrl(cbw, SelectionIndex, False);
+ CallSelectionCBL(cbw, Event);
+ break;
+/* --------------------------------------------------------------------
+ * Der Benutzer hat die Eingabetaste gedrueckt oder einen Eintrag in
+ * der Listbox angeklickt.
+ */
+ case 'a': /* Return = activate */
+ case 'S': /* Selection */
+ if ( !cbw->combobox.StaticList && !cbw->combobox.ListVisible ) break;
+ XtVaGetValues(ListBox, XmNitemCount, &ItemCount, NULL);
+ if ( ItemCount == 0 ) break;
+ if ( XmListGetSelectedPos(ListBox,
+ &SelectionList, &SelectionCount) ) {
+ SelectionIndex = *SelectionList;
+ XtFree((char *)SelectionList);
+ } else {
+ if ( cbw->combobox.SelectionPolicy != XmSINGLE_SELECT )
+ SelectionIndex = 1;
+ else
+ SelectionIndex = 0;
+ }
+
+ TransferToEditCtrl(cbw, SelectionIndex,
+ *(params[0]) == 'S');
+ CallSelectionCBL(cbw, Event);
+ ShowHideDropDownList(cbw, Event, (Boolean)
+ (*(params[0]) == 'S' ? True : False));
+ break;
+/* --------------------------------------------------------------------
+ * Der Benutzer hat die ESC-Taste gedrueckt. Ist die Liste zu diesem
+ * Zeitpunkt noch ausgeklappt, so wird sie einfach nur eingeklappt und
+ * weiter passiert nichts. Ist die Liste jedoch eingeklappt, so wird
+ * das ESC an die normale Action-Routine des Eingabefeldes weiter-
+ * gegeben, damit damit bspw. Dialog u.a. abgebrochen werden koennen.
+ */
+ case 'c': /* Cancel */
+ if ( cbw->combobox.ListVisible )
+ ShowHideDropDownList(cbw, Event, False);
+ else
+ XtCallActionProc(cbw->combobox.EditCtrl,
+ "process-cancel", Event, NULL, 0);
+ break;
+/* --------------------------------------------------------------------
+ * Wenn es erlaubt ist, dass auch einmal kein Eintrag in einer ComboBox
+ * mit nicht editierbarem Eingabefeld ausgewaehlt ist, dann darf der
+ * Anwender mittels osfDelete den aktuellen Eintrag deselektieren.
+ */
+ case 'w': /* wipe */
+ if ( cbw->combobox.SelectionPolicy == XmSINGLE_SELECT ) {
+ TransferToEditCtrl(cbw, 0, True);
+ CallSelectionCBL(cbw, Event);
+ }
+ break;
+/* --------------------------------------------------------------------
+ * Dummy-Operation
+ */
+ case 'n': /* no-operation */
+ break;
+ }
+} /* CBoxManager */
+
+/* --------------------------------------------------------------------
+ * Der Benutzer hat einen Eintrag in der Listbox angeklickt. Der Ein-
+ * fachkeit halber wird einfach nur ein Druecken der Eingabetaste
+ * simuliert.
+ */
+static void ListSelectionCallback(Widget w, XtPointer pClientData,
+ XmAnyCallbackStruct *info)
+{
+ String paramsMouse[1] = { "a" }, paramsKeyboard[1] = { "S" };
+ Cardinal NumParams = 1;
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+/*
+ * Wurde der Event durch die Tastatur oder einen Mausklick
+ * ausgeloest? Wenn es ein Mausklick auf das Listenfeld war und es
+ * sich um ein staendig angezeigtes Listenfeld einer nicht editierbaren
+ * ComboBox handelt, dann gib' dem Eingabefeld den Tastaturfokus.
+ */
+ if ( info->event == NULL )
+ CBoxManager(cbw->combobox.EditCtrl, info->event,
+ paramsKeyboard, &NumParams);
+ else {
+ CBoxManager(cbw->combobox.EditCtrl, info->event,
+ paramsMouse, &NumParams);
+ if ( !cbw->combobox.StaticList ||
+ (cbw->combobox.StaticList && !cbw->combobox.Editable) )
+ XmProcessTraversal(cbw->combobox.EditCtrl,
+ XmTRAVERSE_CURRENT);
+ }
+} /* ListSelectionCallback */
+
+/* --------------------------------------------------------------------
+ * Nach einem Doppelklick innerhalb des Listenfelds wird diese Routine
+ * aufgerufen. Zunaechst einmal wird ganz normal wie bei einem ein-
+ * fachen Anklicken vorgegangen, danach aber noch der ein spezieller
+ * Callback aufgerufen.
+ */
+static void ListDefaultActionCallback(Widget w, XtPointer pClientData,
+ XmAnyCallbackStruct *OldInfo)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+ XmComboBoxDefaultActionCallbackStruct info;
+ XmStringTable Items;
+
+ ListSelectionCallback(w, pClientData, OldInfo);
+ info.reason = XmCR_DEFAULT_ACTION;
+ info.event = OldInfo->event;
+ info.index = XmComboBoxGetSelectedPos((Widget) cbw);
+ XtVaGetValues(cbw->combobox.ListCtrl, XmNitems, &Items, NULL);
+ info.value = Items[info.index-1];
+ XtCallCallbacks((Widget) cbw, XmNdefaultActionCallback, (XtPointer) &info);
+} /* ListDefaultActionCallback */
+
+
+/* --------------------------------------------------------------------
+ * Ohweh!! Diese Routine wurde erforderlich, um XmNautomaticSelection
+ * zu unterstuetzen. Denn wenn der Benutzer in der Liste herumsucht und
+ * automaticSelection 'True' ist, kommt kein Callback-Aufruf mehr, wenn
+ * die Maustaste losgelassen wird. Und damit wuessten wir sonst nicht,
+ * wann die Liste einzuklappen ist! Irgendwie wird das alles mit der
+ * Zeit immer konfuser und aufwendiger. Wenn das Chaos gequantelt
+ * sein sollte, dann muss das Chaos-Quant (sog. 'Chaotonen') aber jede
+ * Menge Chaos transportieren!!!
+ */
+static void Button1UpInList(Widget w, XtPointer pClientData,
+ XEvent *Event, Boolean *ContDispatch)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
+
+ if ( Event->xbutton.button == Button1 ) {
+ if ( cbw->combobox.AutomaticSelection )
+ ShowHideDropDownList(cbw, Event, False);
+ }
+} /* Button1UpInList */
+
+/* --------------------------------------------------------------------
+ * Sobald sich irgendetwas im Eingabefeld veraenderte, kommt das
+ * TextField-Widget zuerst zu uns gelaufen, um sich unser Okay zu
+ * holen. Bei einer nicht editierbaren Combo-Box wird hierueber die
+ * Schnellsuche realisiert.
+ */
+static void EditVerifyCallback(Widget w, XtPointer pClientData,
+ XmTextVerifyCallbackStruct *info)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
+
+/*
+ * Sollte gerade dem Eingabefeld Text aus der Listbox einverleibt
+ * werden, so duerfen wir hier darueber natuerlich nicht meckern,
+ * sondern unser <<ok>> dazu geben. (D.h. in diesem Fall haben wir
+ * kein Recht, zu intervenieren.)
+ */
+ if ( cbw->combobox.PassVerification ) {
+ cbw->combobox.PassVerification = False;
+ info->doit = True;
+ return;
+ }
+/*
+ * Ist es eine Combo-Box, in die kein Text vom Benutzer eingegeben
+ * werden kann, so wird bei der Eingabe von Zeichen die Schnellsuche
+ * ausgeloest.
+ */
+ if ( !cbw->combobox.Editable ) {
+ Widget ListBox = cbw->combobox.ListCtrl;
+ char WarpCharLow, WarpCharHigh;
+ XmString Item;
+ XmStringTable Items;
+ int *SelectionList;
+ int SelectionCount;
+ int i, ItemCount, Start, End;
+ char *pItem;
+ Boolean Ignore;
+
+ info->doit = False;
+ if ( (info->text == NULL ) ||
+ (info->text->length == 0 ) ) return; /* Hoppala! */
+/*
+ * Nun aus dem Zeichen einen String (Motif-like) basteln und
+ * in der Listbox danach auf die Suche gehen.
+ */
+ if ( info->text->length > 1 ) {
+ /* Das ist nun endweder ein normaler Paste, oder aber
+ * das Ergebnis einer Drag'n'Drop-Operation.
+ */
+ Item = XmStringCreateSimple(info->text->ptr);
+ XmComboBoxSelectItem((Widget) cbw, Item, True);
+ XmStringFree(Item);
+ } else {
+ /* Ansonsten soll nur eine Schnellsuche ausgefuehrt
+ * werden, der entsprechende Buchstabe ist das einzige
+ * Zeichen im dem Callback uebergebenen Text.
+ */
+ WarpCharLow = tolower(*(info->text->ptr));
+ WarpCharHigh = toupper(WarpCharLow);
+
+ XtVaGetValues(ListBox, XmNitemCount, &ItemCount,
+ XmNitems, &Items,
+ NULL);
+ if ( ItemCount < 1 ) return;
+ /* Ermittele, wo's los geht mit der Suche... */
+ if ( XmListGetSelectedPos(ListBox,
+ &SelectionList, &SelectionCount) ) {
+ Start = *SelectionList; i = Start + 1;
+ XtFree((char *)SelectionList);
+ } else i = Start = 1;
+
+ if ( i > ItemCount ) i = 1;
+ Ignore = True;
+ while ( i != Start || Ignore ) {
+ Ignore = False;
+ XmStringGetLtoR(Items[i-1], XmSTRING_DEFAULT_CHARSET,
+ &pItem);
+ if ( (strchr(pItem, WarpCharLow ) == pItem) ||
+ (strchr(pItem, WarpCharHigh) == pItem) ) {
+ XtFree(pItem);
+ TransferToEditCtrl(cbw, i, False);
+ CallSelectionCBL(cbw, info->event);
+ break;
+ }
+ XtFree(pItem);
+ if ( ++i > ItemCount ) i = 1;
+ }
+ }
+ } else {
+/*
+ * Wenn das Eingabefeld editierbar ist, dann fragen wir ueber die Callbacks
+ * nach, ob es genehm ist, den neuen Text einzufuegen.
+ */
+ XtCallCallbacks((Widget) cbw, XmNmodifyVerifyCallback,
+ (XtPointer) info);
+ }
+} /* EditVerifyCallback */
+
+/* --------------------------------------------------------------------
+ * Dieser Callback wird immer dann aufgerufen, wenn in einer ComboBox
+ * mit einem veraenderlichem Eingabefeld der Eingabetext veraendert
+ * wurde. In diesem Fall suchen wir hier nach einem passenden gleich-
+ * lautenden Eintrag. Wenn wir einen finden, heben wir ihn in der Liste
+ * sogleich hervor, ansonsten ist kein Eintrag hervorgehoben.
+ */
+static void EditChangedCallback(Widget w, XtPointer pClientDate,
+ XmAnyCallbackStruct *info)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
+ XmStringTable Items;
+ int ItemCount, i;
+ XmString EditStr;
+ String EditLine;
+
+ /*
+ * Zuerst nach einem passenden Eintrag zum Eingabefeld in der Liste
+ * suchen...
+ */
+ XtVaGetValues(cbw->combobox.EditCtrl, XmNvalue, &EditLine, NULL);
+ XtVaGetValues(cbw->combobox.ListCtrl,
+ XmNitemCount, &ItemCount,
+ XmNitems, &Items,
+ NULL);
+ EditStr = XmStringCreateSimple(EditLine);
+ XtVaSetValues(cbw->combobox.ListCtrl, XmNselectedItemCount, 0, NULL);
+ if ( ItemCount < 1 ) return;
+ for ( i = 0; i < ItemCount; i++ )
+ if ( XmStringCompare(Items[i], EditStr) ) {
+ SetSelectionPos(cbw, i+1, False);
+ break;
+ }
+ XmStringFree(EditStr);
+ /*
+ * Zum Abschluss noch den Callback aufrufen...
+ */
+ XtCallCallbacks((Widget) cbw, XmNvalueChangedCallback, (XtPointer) info);
+} /* EditChangedCallback */
+
+/* --------------------------------------------------------------------
+ * Dieser Callback wird immer dann aufgerufen, wenn in einer ComboBox
+ * mit einem veraenderlichem Eingabefeld der Cursor bewegt wurde.
+ * Dieser Callback ist nur fuer echte Fans von Callbacks da...
+ */
+static void MotionVerifyCallback(Widget w, XtPointer pClientDate,
+ XmTextVerifyCallbackStruct *info)
+{
+ XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
+
+ XtCallCallbacks((Widget) cbw, XmNmotionVerifyCallback, (XtPointer) info);
+} /* MotionVerifyCallback */
+
+/* --------------------------------------------------------------------
+ * Bastele einen vollstaendigen Namens- und Klassenbezeichner anhand
+ * des angegebenen Widgets zusammen.
+ */
+static void MakeNameAndClass(Widget w, char *NameBuff, char *ClassBuff)
+{
+ Widget Parent = XtParent(w);
+
+ if ( Parent ) MakeNameAndClass(Parent, NameBuff, ClassBuff);
+ if ( XtIsSubclass(w, applicationShellWidgetClass) ) {
+ /* Wenn wir ganz oben angekommen sind, holen wir uns den
+ * Namen und die Klasse der Applikation selbst und nicht die
+ * des Widgets.
+ */
+ String AppName, AppClass;
+ XtGetApplicationNameAndClass(
+ XtDisplayOfObject(w), &AppName, &AppClass);
+ strcpy(NameBuff, AppName);
+ strcpy(ClassBuff, AppClass);
+ } else {
+ /* Ansonsten sind wir noch mitten irgendwo in der Hierarchie
+ * und besorgen uns den Namen und die Klasse dieses Widgets
+ */
+ strcat(NameBuff, ".");
+ strcat(NameBuff, XtName(w));
+ strcat(ClassBuff, ".");
+ strcat(ClassBuff, ((CoreClassRec *) XtClass(w))->core_class.class_name);
+ }
+} /* MakeNameAndClass */
+
+/* --------------------------------------------------------------------
+ * Eine einzelne Resource aus der Datenbank herausholen. Diese Resource
+ * kommt im Allgemeinen immer als String zurueck und muss daher erst
+ * noch in das gewuenschte Zielformat konvertiert werden.
+ */
+static Boolean FetchResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ XrmValue *RscValue,
+ String *RepresentationType)
+{
+ Boolean ok;
+ char *EndOfName = FullName + strlen(FullName);
+ char *EndOfClass = FullClass + strlen(FullClass);
+
+ strcat(FullName, "."); strcat(FullName, RscName);
+ strcat(FullClass, "."); strcat(FullClass, RscClass);
+ ok = XrmGetResource(
+ XtDatabase(XtDisplayOfObject(w)),
+ FullName, FullClass, RepresentationType, RscValue);
+ /* Wieder den alten Namens- und Klassenrumpf herstellen */
+ *EndOfName = 0; *EndOfClass = 0;
+ return ok;
+} /* FetchResource */
+
+/* --------------------------------------------------------------------
+ * Nun folgen diejenigen Routinen, mit denen die Konvertierung in das
+ * gewuenschte Zielformat einer Resource moeglich ist.
+ * Verfuegbar:
+ * String --> Int
+ * String --> Short
+ * String XmPIXMAP / XmSTRING --> unsigned char
+ * String --> Dimension
+ * String --> XmString
+ * String --> XmStringTable
+ * String --> XmFontList
+ * String --> Pixmap (genauer: Bitmap)
+ * String --> String
+ * String --> KeySym
+ */
+static Boolean FetchIntResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ int *pInt)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ RscDest.size = sizeof(int);
+ RscDest.addr = (caddr_t) pInt;
+ if ( XtConvertAndStore(w, RepresentationType, &RscValue,
+ XtRInt, &RscDest) )
+ return True;
+ }
+ return False;
+} /* FetchIntResource */
+
+static Boolean FetchShortResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ short *pShort)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ RscDest.size = sizeof(short);
+ RscDest.addr = (caddr_t) pShort;
+ if ( XtConvertAndStore(w, RepresentationType, &RscValue,
+ XtRShort, &RscDest) )
+ return True;
+ }
+ return False;
+} /* FetchShortResource */
+
+static Boolean FetchLabelTypeResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ unsigned char *pUChar)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+ int AInt;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ if ( strcasecmp((char *) RscValue.addr, "XmPIXMAP") == 0 )
+ *pUChar = XmPIXMAP;
+ else
+ *pUChar = XmSTRING;
+ return True;
+ }
+ return False;
+} /* FetchLabelTypeResource */
+
+static Boolean FetchDimensionResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ Dimension *pDimension)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ RscDest.size = sizeof(Dimension);
+ RscDest.addr = (caddr_t) pDimension;
+ if ( XtConvertAndStore(w, RepresentationType, &RscValue,
+ XtRDimension, &RscDest) )
+ return True;
+ }
+ return False;
+} /* FetchDimensionResource */
+
+static Boolean FetchStringResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ String *pString)
+{
+ XrmValue RscValue;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ *pString = (char *) RscValue.addr;
+ return True;
+ }
+ return False;
+} /* FetchStringResource */
+
+static Boolean FetchKeySymResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ KeySym *pKeySym)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ RscDest.size = sizeof(KeySym);
+ RscDest.addr = (caddr_t) pKeySym;
+ if ( XtConvertAndStore(w, RepresentationType, &RscValue,
+ XmRKeySym, &RscDest) )
+ return True;
+ }
+ return False;
+} /* FetchKeySymResource */
+
+static Boolean FetchXmStringResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ XmString *pString)
+{
+ XrmValue RscValue;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ *pString = XmCvtCTToXmString((char *) RscValue.addr);
+ return True;
+ }
+ return False;
+} /* FetchXmStringResource */
+
+static Boolean FetchXmStringTableResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ XmStringTable *pStringTable,
+ int *pTableSize)
+{
+ XrmValue RscValue;
+ String RepresentationType;
+ String TmpList, p, pStart;
+ int Entries, Entry;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ /*
+ * Zuerst eine Kopie erzeugen und dann daraus die Liste
+ * zusammenbasteln.
+ */
+ TmpList = XtNewString((String)RscValue.addr);
+ if ( TmpList == NULL ) return False;
+ if ( *TmpList == 0 ) { XtFree(TmpList); return False; }
+ /* Ermittele, wieviele Eintrage in der Liste sind und
+ * erstelle dann daraus die Liste.
+ */
+ Entries = 1; p = TmpList;
+ while ( *p )
+ if ( *p++ == ',' ) ++Entries;
+ *pStringTable = (XmStringTable)
+ XtMalloc(Entries * sizeof(XmString));
+
+ p = TmpList;
+ for ( Entry = 0; Entry < Entries; ++Entry ) {
+ pStart = p;
+ while ( (*p != 0) && (*p != ',') ) ++p;
+ *p++ = 0;
+ (*pStringTable)[Entry] = (XmString)
+ XmStringCreateSimple(pStart);
+ }
+ /* Hier geht ausnahmsweise einmal Rueckgabe vor
+ * Entschaedigung... hey, das war doch nur ein
+ * (wenn auch ziemlich miserabler) Scherz
+ */
+ XtFree(TmpList);
+ *pTableSize = Entries;
+ return True;
+ }
+ return False;
+} /* FetchXmStringTableResource */
+
+static Boolean FetchXmFontListResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ XmFontList *pFontList)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ RscDest.size = sizeof(XmFontList);
+ RscDest.addr = (caddr_t) pFontList;
+ if ( XtConvertAndStore(w, RepresentationType, &RscValue,
+ XmRFontList, &RscDest) )
+ return True;
+ }
+ return False;
+} /* FetchXmFontListResource */
+
+static Boolean FetchPixmapResource(Widget w,
+ char *FullName, char *FullClass,
+ char *RscName, char *RscClass,
+ Pixmap *pPixmap)
+{
+ XrmValue RscValue, RscDest;
+ String RepresentationType;
+
+ if ( FetchResource(w, FullName, FullClass,
+ RscName, RscClass,
+ &RscValue, &RepresentationType) ) {
+ RscDest.size = sizeof(Pixmap);
+ RscDest.addr = (caddr_t) pPixmap;
+ if ( XtConvertAndStore(w, RepresentationType, &RscValue,
+ XtRBitmap, &RscDest) )
+ return True;
+ }
+ return False;
+} /* FetchPixmapResource */
+
+/* --------------------------------------------------------------------
+ * Waehrend der Initialisierung alle gespiegelten Resourcen, fuer die
+ * Eintraege in der Resourcen-Datenbank existieren an die passenden
+ * Kinder-Widgets weiterleiten. Der Trick an der Sache: wir setzen
+ * die betroffenen Resourcen vie XtSetValues mit uns selbst als Ziel.
+ * Dadurch bekommt SetValues die Arbeit aufgehalst, die Resourcen den
+ * richtigen Kindern zuzuordnen...
+ */
+
+#define RInt 0
+#define RShort 1
+#define RLType 2
+#define RDimension 3
+#define RXmString 4
+#define RPixmap 5
+#define RXmFontList 6
+#define RKeySym 7
+#define RString 8
+#define RXmStringTable 9
+#define RXmItemCount 10
+
+
+typedef struct
+{
+ String Name, Class;
+
+ int Converter;
+
+} RESOURCEMIRROR;
+
+static RESOURCEMIRROR ResourceMirror[] = {
+ { XmNblinkRate, XmCBlinkRate, RInt, },
+ { XmNcolumns, XmCColumns, RShort, },
+ { XmNmaxLength, XmCMaxLength, RInt, },
+ { XmNmarginHeight, XmCMarginHeight, RDimension },
+ { XmNmarginWidth, XmCMarginWidth, RDimension },
+ { XmNselectThreshold, XmCSelectThreshold, RInt },
+
+ { XmNlistMarginHeight, XmCListMarginHeight, RDimension },
+ { XmNlistMarginWidth, XmCListMarginWidth, RDimension },
+ { XmNlistSpacing, XmCListSpacing, RDimension },
+ { XmNitems, XmCItems, RXmStringTable },
+ { XmNitemCount, XmCItemCount, RXmItemCount },
+
+ { XmNmnemonic, XmCMnemonic, RKeySym },
+ { XmNmnemonicCharSet, XmCMnemonicCharSet, RString },
+ { XmNlabelString, XmCLabelString, RXmString },
+ { XmNlabelMarginBottom, XmCLabelMarginBottom, RDimension },
+ { XmNlabelMarginHeight, XmCLabelMarginHeight, RDimension },
+ { XmNlabelMarginLeft, XmCLabelMarginLeft, RDimension },
+ { XmNlabelMarginRight, XmCLabelMarginRight, RDimension },
+ { XmNlabelMarginTop, XmCLabelMarginTop, RDimension },
+ { XmNlabelMarginWidth, XmCLabelMarginWidth, RDimension },
+ { XmNlabelPixmap, XmCLabelPixmap, RPixmap },
+ { XmNlabelInsensitivePixmap, XmCLabelInsensitivePixmap, RPixmap },
+ { XmNlabelType, XmCLabelType, RLType },
+ { XmNlabelFontList, XmCLabelFontList, RXmFontList },
+};
+
+static void InitMirrorResources(XmComboBoxWidget w)
+{
+ char FullName[1024], FullClass[1024];
+ int AInt, TableSize;
+ short AShort;
+ unsigned char AUChar;
+ Dimension ADimension;
+ XmString AXmString;
+ XmStringTable AStringTable;
+ Pixmap APixmap;
+ XmFontList AFontList;
+ XrmValue RscValue;
+ String AString;
+ KeySym AKeySym;
+ int i, size = XtNumber(ResourceMirror);
+
+ FullName[0] = 0; FullClass[0] = 0;
+ MakeNameAndClass((Widget) w, FullName, FullClass);
+
+ for ( i=0; i < size; i++ ) {
+ switch ( ResourceMirror[i].Converter ) {
+ case RInt:
+ if ( FetchIntResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AInt) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AInt, NULL);
+ break;
+ case RXmItemCount:
+ if ( FetchIntResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AInt) && ( AInt != 0) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AInt, NULL);
+ break;
+ case RShort:
+ if ( FetchShortResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AShort) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AShort, NULL);
+ break;
+ case RLType:
+ if ( FetchLabelTypeResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AUChar) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AUChar, NULL);
+ break;
+ case RDimension:
+ if ( FetchDimensionResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &ADimension) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ ADimension, NULL);
+ break;
+ case RXmString:
+ if ( FetchXmStringResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AXmString) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AXmString, NULL);
+ break;
+ case RXmStringTable:
+ if ( FetchXmStringTableResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AStringTable, &TableSize) ) {
+ XtVaSetValues((Widget) w,
+ XmNitems, (XtPointer) AStringTable,
+ XmNitemCount, TableSize, NULL);
+ }
+ break;
+ case RKeySym:
+ if ( FetchKeySymResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AKeySym) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AKeySym, NULL);
+ break;
+ case RString:
+ if ( FetchStringResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AString) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AString, NULL);
+ break;
+ case RPixmap:
+ if ( FetchPixmapResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &APixmap) ) {
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ APixmap, NULL);
+ if ( strcmp(ResourceMirror[i].Name, XmNlabelPixmap) == 0 )
+ w->combobox.ConvertBitmapToPixmap = True;
+ else
+ w->combobox.ConvertBitmapToPixmapInsensitive = True;
+ }
+ break;
+ case RXmFontList:
+ if ( FetchXmFontListResource((Widget) w,
+ FullName, FullClass,
+ ResourceMirror[i].Name, ResourceMirror[i].Class,
+ &AFontList) )
+ XtVaSetValues((Widget) w, ResourceMirror[i].Name,
+ AFontList, NULL);
+ break;
+ }
+ }
+} /* InitMirrorResources */
+
+/* --------------------------------------------------------------------
+ * Wandelt ein 1-Bit tiefes Bitmap in ein n-Bit tiefes Pixmap um, dass
+ * die gleiche Tiefe besitzt, wie der Bildschirm, auf dem das Pixmap
+ * spaeter erscheinen soll.
+ */
+static Pixmap BitmapToPixmap(XmComboBoxWidget w,
+ String Resource, GC ColorGC)
+{
+ Pixmap LabelPixmap, LabelBitmap;
+ Display *display = XtDisplay(w);
+ Window root;
+ int PixX, PixY;
+ unsigned int PixW, PixH, PixBW, PixDepth;
+
+ XtVaGetValues(w->combobox.LabelCtrl, Resource, &LabelBitmap, NULL);
+ XGetGeometry(display, LabelBitmap, &root,
+ &PixX, &PixY, &PixW, &PixH, &PixBW, &PixDepth);
+ LabelPixmap = XCreatePixmap(
+ display, RootWindowOfScreen(XtScreen(w)),
+ PixW, PixH,
+ (w->combobox.LabelCtrl)->core.depth);
+ XCopyPlane(display, LabelBitmap, LabelPixmap,
+ ColorGC, 0, 0, PixW, PixH, 0, 0, 1);
+ XtVaSetValues(w->combobox.LabelCtrl, Resource, LabelPixmap, NULL);
+ XFreePixmap(display, LabelBitmap);
+ return LabelPixmap;
+} /* BitmapToPixmap */
+
+/* --------------------------------------------------------------------
+ * Alles initialisieren, sobald das Widget eingerichtet wird. Das sagt
+ * sich hier so einfach, ist es aber *definitiv* nicht!!!!
+ */
+static void Initialize(Widget request, XmComboBoxWidget newW,
+ ArgList wargs, Cardinal *ArgCount)
+{
+ Dimension width, height, dummy;
+ Widget w;
+ Arg args[10];
+ int n = 0;
+ XmString xmstr;
+ Pixel BackgroundColor;
+
+/*
+ * Da zu allem Ueberfluss die einzelnen Instanzen einer XmComboBox
+ * auf verschiedenen Displays auftauchen koennen, wird hier:
+ * 1. pro Widget ein eigener Cursor erzeugt (benoetigt fuer die Liste)
+ * 2. pro Widget (hier = pro Applikation) die benoetigte Action-Routine
+ * registiert. Doppelte Registrierung macht dem Toolkit nichts aus, da es
+ * dann eine evtl. aeltere Definition loescht.
+ */
+ XtAppAddActions(XtWidgetToApplicationContext((Widget) newW),
+ actions, XtNumber(actions));
+
+/* Allgemeine Initialisierungen... */
+ newW->combobox.ConvertBitmapToPixmap = False;
+ newW->combobox.ConvertBitmapToPixmapInsensitive = False;
+
+ newW->combobox.LastSelection = 0;
+
+ newW->combobox.InInit = True;
+
+/*
+ * Das folgende Problem mit der Kontrolle, ob sich das Widget absolut auf
+ * dem Bildschirm verschoben hat, trifft uns nur, wenn die Liste nicht
+ * dauernd auf dem Bildschirm erscheint:
+ * Lass' dich benachrichtigen, sobald dieses Widget in irgendeiner
+ * Form bewegt wird -- und sei es nur, dass das gesamte Applikations-
+ * fenster umhergeschoben wurde. Um die Benachrichtigung ueberhaupt
+ * zu erreichen, ist es erforderlich, sich benachrichtigen zu lassen,
+ * sobald die naechste Shell (oder ein Nachkomme) im Widget-Instanzen-
+ * Baum verschoben wurde.
+ */
+ if ( !newW->combobox.StaticList ) {
+ w = (Widget) newW;
+ while ( !XtIsSubclass(w, shellWidgetClass) )
+ w = XtParent(w);
+ newW->combobox.MyNextShell = w;
+ XtAddEventHandler(w,
+ StructureNotifyMask | FocusChangeMask,
+ False, (XtEventHandler) ShellCallback,
+ (XtPointer) newW);
+ }
+
+/* Richte nun alle zu diesem Widget gehoerenden Kinder ein, als da
+ * waeren:
+ * 1 x editierbares Eingabefeld
+ * 1 x ein Pfeil nach unten
+ * 1 x ein Schriftfeld
+ */
+ newW->combobox.EditCtrl = XtVaCreateManagedWidget(
+ "edit", xmTextFieldWidgetClass, (Widget) newW,
+ XmNverifyBell, False,
+ NULL);
+ XtAddCallback(newW->combobox.EditCtrl,
+ XmNlosingFocusCallback,
+ (XtCallbackProc) EditFocusCallback, NULL);
+ XtAddCallback(newW->combobox.EditCtrl,
+ XmNmodifyVerifyCallback,
+ (XtCallbackProc) EditVerifyCallback, NULL);
+ XtAddCallback(newW->combobox.EditCtrl,
+ XmNvalueChangedCallback,
+ (XtCallbackProc) EditChangedCallback, NULL);
+ XtAddCallback(newW->combobox.EditCtrl,
+ XmNhelpCallback,
+ (XtCallbackProc) HelpCallback,
+ (XtPointer) newW);
+ XtAddCallback(newW->combobox.EditCtrl,
+ XmNactivateCallback,
+ (XtCallbackProc) ActivateCallback,
+ (XtPointer) newW);
+ if ( newW->combobox.Editable )
+ XtAddCallback(newW->combobox.EditCtrl,
+ XmNmotionVerifyCallback,
+ (XtCallbackProc) MotionVerifyCallback,
+ (XtPointer) newW);
+/* Neue Translations fuer das Eingabefeld aufnehmen */
+ XtOverrideTranslations(newW->combobox.EditCtrl,
+ NewEditTranslations);
+ if ( !newW->combobox.Editable ) {
+ XtOverrideTranslations(newW->combobox.EditCtrl,
+ NewEditTranslationsNE);
+ XtVaSetValues(newW->combobox.EditCtrl,
+ XmNcursorPositionVisible, False, NULL);
+ }
+#ifdef NODRAGNDROP
+ XtOverrideTranslations(newW->combobox.EditCtrl,
+ NewListTranslations); /* Btn2Dwn aus! */
+#endif
+
+/* --- */
+ newW->combobox.ArrowCtrl = XtVaCreateManagedWidget(
+ "arrow", xmArrowButtonWidgetClass, (Widget) newW,
+ XmNarrowDirection, XmARROW_DOWN,
+ XmNtraversalOn, False,
+ XmNnavigationType, XmNONE,
+ XmNborderWidth, 0,
+ XmNhighlightThickness, 0,
+ NULL);
+ XmRemoveTabGroup(newW->combobox.ArrowCtrl);
+ if ( newW->combobox.StaticList ) {
+ XtVaSetValues(newW->combobox.ArrowCtrl,
+ XmNmappedWhenManaged, False, NULL);
+ } else {
+ XtAddEventHandler(newW->combobox.ArrowCtrl,
+ EnterWindowMask | LeaveWindowMask,
+ False, (XtEventHandler) ArrowCrossingCallback,
+ (XtPointer) newW);
+ XtAddCallback(newW->combobox.ArrowCtrl,
+ XmNactivateCallback,
+ (XtCallbackProc) ArrowCallback, NULL);
+ XtAddCallback(newW->combobox.ArrowCtrl,
+ XmNarmCallback,
+ (XtCallbackProc) ArrowCallback, NULL);
+ XtAddCallback(newW->combobox.ArrowCtrl,
+ XmNhelpCallback,
+ (XtCallbackProc) HelpCallback,
+ (XtPointer) newW);
+ }
+
+/* --- */
+ newW->combobox.LabelCtrl = XtVaCreateWidget(
+ "label", xmLabelWidgetClass, (Widget) newW,
+ XmNstringDirection, newW->manager.string_direction,
+ NULL);
+ if ( newW->combobox.ShowLabel ) {
+ XtManageChild((Widget) newW->combobox.LabelCtrl);
+ XtAddCallback(newW->combobox.LabelCtrl,
+ XmNhelpCallback,
+ (XtCallbackProc) HelpCallback,
+ (XtPointer) newW);
+ }
+
+/*
+ * Zuerst noch die Shell erzeugen, die so einfach mir nichts dir nichts
+ * frei auf dem Bildschirm herumschweben kann und damit das Ausklappen
+ * der Liste erst ermoeglicht -- und uns allerhand Scherereien bereitet!
+ * War das ein bloeder Fehler in Motif 1.2! Diese Version vertraegt ab-
+ * solut keine ShellWidgetClass noch overrideShellWidgetClass!!!! Naja,
+ * mit einer vendorShellWidgetClass laesst sich aber exakt der gleiche
+ * Effekt erreichen. NEU: vor allem funktioniert dann endlich auch
+ * Drag'n'Drop!!!
+ * Noch neuer: Wenn die Liste dauerhaft angezeigt werden muss, entfaellt
+ * diese Shell zwangslaeufig. Dann ist das Listenfeld ein direktes Kind
+ * der ComboBox.
+ */
+ if ( !newW->combobox.StaticList ) {
+ newW->combobox.PopupShell = XtVaCreateWidget(
+ "combobox_shell", vendorShellWidgetClass, (Widget) newW,
+ XmNoverrideRedirect, True,
+ XmNsaveUnder, False,
+ XmNallowShellResize, True,
+ NULL);
+ XtAddEventHandler(newW->combobox.PopupShell,
+ EnterWindowMask | LeaveWindowMask,
+ False, (XtEventHandler) OverrideShellCallback,
+ (XtPointer) newW);
+ } else {
+ /*
+ * Sieht ja pervers nach einer Rekursion aus...daher: OBACHT!
+ */
+ newW->combobox.PopupShell = (Widget) newW;
+ }
+
+/*
+ * Nun kommt die Drop-Down-Liste an die Reihe. Die Liste muss dabei
+ * mit einer Convenience-Funktion erstellt werden, damit ein Rollbalken
+ * 'dran ist und das Ganze wird dann in eine Override-Shell gepackt.
+ * Nicht zu vergessen ist der XtManageChild-Aufruf, damit die Liste
+ * sofort nach dem Aufklappen der Shell sichtbar wird.
+ */
+ XtSetArg(args[n], XmNselectionPolicy, newW->combobox.SelectionPolicy); n++;
+
+ if ( !newW->combobox.StaticList ) {
+ /*
+ * Es gibt halt so eine ganze Reihe von Einstellungen, die koennen nicht
+ * veraendert werden, wenn das Listenfeld nur bei Bedarf ausgeklappt wird.
+ */
+ XtSetArg(args[n], XmNhighlightThickness, 0); n++;
+ }
+ XtSetArg(args[n], XmNlistSizePolicy,
+ newW->combobox.ListSizePolicy); n++;
+ XtSetArg(args[n], XmNscrollBarDisplayPolicy,
+ newW->combobox.ScrollBarDisplayPolicy); n++;
+
+ XtSetArg(args[n], XmNautomaticSelection,
+ newW->combobox.AutomaticSelection); n++;
+ XtSetArg(args[n], XmNvisibleItemCount,
+ newW->combobox.VisibleItemCount); n++;
+ newW->combobox.ListCtrl = XmCreateScrolledList(
+ newW->combobox.PopupShell, "list",
+ args, n);
+
+/*
+ * Fuer den Fall, dass die Liste in einer eigenen Shell steckt und daher frei
+ * auf dem Bildschirm herumschweben kann, sollten wir sicherheitshalber die
+ * Tastaturbedienung (Fokus) abschalten, um Probleme zu vermeiden (jedenfalls
+ * hoffentlich...!)
+ */
+ if ( !newW->combobox.StaticList ) {
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNtraversalOn, False, NULL);
+ XtVaSetValues(XtParent(newW->combobox.ListCtrl),
+ XmNtraversalOn, False, NULL);
+ } else {
+ if ( !newW->combobox.Editable ) {
+ XtVaSetValues(XtParent(newW->combobox.ListCtrl),
+ XmNtraversalOn, False, NULL);
+ XmRemoveTabGroup(newW->combobox.ListCtrl);
+ }
+ }
+
+ XtManageChild(newW->combobox.ListCtrl);
+ XtAddCallback(newW->combobox.ListCtrl,
+ XmNsingleSelectionCallback,
+ (XtCallbackProc) ListSelectionCallback,
+ (XtPointer) newW);
+ XtAddCallback(newW->combobox.ListCtrl,
+ XmNbrowseSelectionCallback,
+ (XtCallbackProc) ListSelectionCallback,
+ (XtPointer) newW);
+ XtAddCallback(newW->combobox.ListCtrl,
+ XmNdefaultActionCallback,
+ (XtCallbackProc) ListDefaultActionCallback,
+ (XtPointer) newW);
+ XtAddCallback(newW->combobox.ListCtrl,
+ XmNhelpCallback,
+ (XtCallbackProc) HelpCallback,
+ (XtPointer) newW);
+
+ XtAddEventHandler(newW->combobox.ListCtrl,
+ ButtonReleaseMask,
+ False, (XtEventHandler) Button1UpInList,
+ (XtPointer) newW);
+
+#ifdef NODRAGNDROP
+ XtOverrideTranslations(newW->combobox.ListCtrl,
+ NewListTranslations);
+#endif
+ if ( newW->combobox.StaticList && newW->combobox.Editable )
+ XtOverrideTranslations(newW->combobox.ListCtrl,
+ NewListTranslationsE);
+
+/* Jetzt wird es dann erst richtig spannend... Zuerst alle evtl.
+ * in der Resource-Datenbank abgelegten Resourcen an die Kinder
+ * weitergeben. Danach die uebergebenen Parameter ebenfalls an
+ * die Kinder weiterreichen und schliesslich das Layout ermitteln.
+ */
+ InitMirrorResources(newW);
+ UpdateColors(newW, -1);
+ SetValues(newW, newW, newW, wargs, ArgCount);
+
+ if ( newW->combobox.ConvertBitmapToPixmap )
+ newW->combobox.LabelPixmap =
+ BitmapToPixmap(newW, XmNlabelPixmap,
+ ((XmLabelRec *) newW->combobox.LabelCtrl)->
+ label.normal_GC);
+ if ( newW->combobox.ConvertBitmapToPixmapInsensitive )
+ newW->combobox.LabelInsensitivePixmap =
+ BitmapToPixmap(newW, XmNlabelInsensitivePixmap,
+ ((XmLabelRec *) newW->combobox.LabelCtrl)->
+ label.insensitive_GC);
+
+ DefaultGeometry(newW, &width, &height, &dummy, &dummy);
+ if ( newW->core.width == 0 )
+ newW->core.width = width;
+ if ( newW->core.height == 0 )
+ newW->core.height = height;
+
+/*
+ * Falls wir keine Fontliste besitzen, dann nehmen wir die von
+ * dem Eingabefeld...
+ */
+ if ( newW->combobox.Font == NULL ) {
+ XtVaGetValues(newW->combobox.EditCtrl,
+ XmNfontList, &newW->combobox.Font, NULL);
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNfontList, newW->combobox.Font, NULL);
+ } else {
+ XtVaSetValues(newW->combobox.ListCtrl,
+ XmNfontList, newW->combobox.Font, NULL);
+ XtVaSetValues(newW->combobox.EditCtrl,
+ XmNfontList, newW->combobox.Font, NULL);
+ }
+
+/*
+ * Initialisiere alle Statusflaggen, die mit diesem unseligen Focus-
+ * problem zu tun haben...
+ */
+ newW->combobox.ListVisible = False;
+ newW->combobox.IgnoreFocusOut = False;
+ newW->combobox.PendingFocusOut = False;
+ newW->combobox.PendingOverrideInOut = False;
+
+ newW->combobox.PassVerification = False;
+
+/*
+ * Jooa... bei der OSF pennen die wohl komplett?! Zusammen mit Form-
+ * Widgets gibt das wohl immer Aerger...daher hier ein DoLayout()
+ * aufrufen, damit Eingabefeld und Pfeil sowie das Listenfeld an der
+ * richtigen Stelle sitzen!
+ */
+ DoLayout(newW);
+/*
+ * Endlich fertig mit der Initialisierung. Das hier ist aber auch
+ * wirklich viel Arbeit fuer so ein Widget!
+ */
+ newW->combobox.InInit = False;
+} /* Initialize */
+
+/* --------------------------------------------------------------------
+ * Diese Funktionen bitte nur im aeussersten Notfall benutzen, da sie
+ * die Abstraktion dieser neuen Klasse umgehen und Informationen ueber
+ * den internen Aufbau voraussetzen.
+ */
+Widget XmComboBoxGetEditWidget(Widget w)
+{
+ return ((XmComboBoxWidget) w)->combobox.EditCtrl;
+} /* XmComboBoxGetEditWidget */
+
+Widget XmComboBoxGetListWidget(Widget w)
+{
+ return ((XmComboBoxWidget) w)->combobox.ListCtrl;
+} /* XmComboBoxGetListWidget */
+
+Widget XmComboBoxGetLabelWidget(Widget w)
+{
+ return ((XmComboBoxWidget) w)->combobox.LabelCtrl;
+} /* XmComboBoxGetLabelWidget */
+
+
+/* --------------------------------------------------------------------
+ * Sobald sich im Listenfeld Eintraege veraenderten, sei es, dass sie
+ * geloescht wurden, sei es, dass sie veraendert wurden, so muss hier
+ * gegebenenfalls auch der Text im Eingabefeld angepasst werden.
+ * Letzteres betrifft aber nur Combo-Boxen mit nicht editierbarem
+ * Eingabefeld. In jedem Fall wird aber bei jeder Combo-Box-Type in
+ * dem Fall, dass ein Eintrag geloescht wird, der darauffolgende
+ * Eintrag markiert. Eigentlich ist dieses nur eine nette Geste
+ * gegenueber dem Benutzer...
+ *
+ * Parameter:
+ * w Combo-Box-Widget
+ * Index Index auf denjenigen Eintrag der sich geaendert
+ * hat, oder der geloescht wurde.
+ * Deleted Zeigt an, ob der Eintrag geloescht wurde (True)
+ * oder sich nur veraenderte (False)
+ */
+static UpdateComboBox(XmComboBoxWidget w, int Index, Boolean Deleted)
+{
+ int OldIndex, ItemCount;
+
+ OldIndex = XmComboBoxGetSelectedPos((Widget) w);
+ if ( OldIndex == Index ) {
+/* Es betrifft den Eintrag, der auch momentan ausgewaehlt ist.
+ * Sollte er geloescht werden, so nimm' (soweit vorhanden) den
+ * naechsten Eintrag, wurde er ausgetauscht, so lass ihn ausge-
+ * waehlt.
+ */
+ if ( Deleted ) {
+ XtVaGetValues(w->combobox.ListCtrl,
+ XmNitemCount, &ItemCount, NULL);
+ if ( ItemCount != 0 ) {
+ if ( Index >= ItemCount ) Index = ItemCount;
+ /* Markieren des Eintrags, ohne jedoch jetzt schon
+ * den Eintrag in die Eingabezeile zu kopieren.
+ */
+ SetSelectionPos(w, Index, False);
+ }
+ }
+ }
+/* Das Problem betrifft uns nur bei nicht editierbaren Combo-Boxen
+ * im vollen Umfang. Denn dann muss auch der Text im Eingabefeld
+ * veraendert werden.
+ */
+ if ( !w->combobox.Editable ) {
+ TransferToEditCtrl(w, Index, False);
+ }
+
+ return 1;
+} /* UpdateComboBox */
+
+
+/* --------------------------------------------------------------------
+ * Die Eintragsposition finden, an der der Eintrag sortiert stehen
+ * muesste. Naja, es wurde ja 'mal langsam Zeit, diese Routine etwas
+ * aufzupolieren, damit sie schneller wird.
+ */
+static int FindSortedItemPos(XmComboBoxWidget w, XmString item)
+{
+ Widget ListBox = w->combobox.ListCtrl;
+ XmStringTable Items;
+ int ItemCount, index, Left, Right, Result;
+ char *pItemText, *pCompareText;
+ Boolean ExternSort;
+ XmComboBoxSortingCallbackStruct data;
+
+ XtVaGetValues(ListBox, XmNitems, &Items,
+ XmNitemCount, &ItemCount, NULL);
+ if ( ItemCount == 0 ) return 1;
+
+ /*
+ * Moechte das Programm die Kontrolle ueber den Sortiervorgang
+ * uebernehmen? Dann bereite alles vor...
+ */
+ ExternSort = XtHasCallbacks((Widget) w, XmNsortingCallback) ==
+ XtCallbackHasSome;
+ if ( ExternSort ) {
+ data.reason = XmCR_SORTING;
+ data.event = NULL;
+ data.operation = XmOP_INIT;
+ data.item = item;
+ XtCallCallbacks((Widget) w, XmNsortingCallback, (XtPointer) &data);
+ } else
+ XmStringGetLtoR(item, XmSTRING_DEFAULT_CHARSET, &pCompareText);
+
+ Left = 0; Right = ItemCount - 1;
+ do {
+ index = (Left + Right) / 2;
+ if ( ExternSort ) {
+ data.operation = XmOP_COMPARE;
+ data.item = Items[index];
+ data.result = 1;
+ XtCallCallbacks((Widget) w, XmNsortingCallback, (XtPointer) &data);
+ Result = data.result;
+ } else {
+ XmStringGetLtoR(Items[index], XmSTRING_DEFAULT_CHARSET, &pItemText);
+ Result = strcmp(pCompareText, pItemText);
+ XtFree(pItemText);
+ }
+ if ( Result < 0 ) Right = index - 1;
+ else if ( Result > 0 ) Left = index + 1;
+ } while ( (Result != 0) && (Left <= Right) );
+
+ /*
+ * Nach Gebrauch wieder alles aufraeumen (bei einer externen Sortierung
+ * muss das das Programm uebernehmen!)
+ */
+ if ( ExternSort ) {
+ data.operation = XmOP_DONE;
+ XtCallCallbacks((Widget) w, XmNsortingCallback, (XtPointer) &data);
+ } else
+ XtFree(pCompareText);
+
+ if ( Result < 0 )
+ return index + 1; /* Beachte, dass Indizes mit 1 beginnen! */
+ else
+ return index + 2; /* Beachte, dass Indizes mit 1 beginnen! */
+} /* FindSortedItemPos */
+
+/* --------------------------------------------------------------------
+ * Kontrolliere, ob es sich ueberhaupt um eine Combo-Box (bzw. einen
+ * hypothetischen Nachkommen) handelt -- ansonsten mecker kraeftig
+ * herum!
+ * Ergebnis:
+ * True, falls wir hier ein falsches Widget untergejubelt bekommen!
+ */
+static Boolean CheckComboBox(Widget w, char *pFuncName)
+{
+ char buff[256];
+ char *pWName;
+
+#if (XmVersion >= 2000)
+ return False; /* temporary workaround */
+#else
+ if ( XmIsComboBox(w) ) return False;
+ pWName = XrmQuarkToString(w->core.xrm_name);
+ sprintf(buff,
+"Warning: %s called on widget named %s beeing \
+not a descendant of class XmComboBox!",
+ pFuncName, pWName);
+ XtWarning(buff);
+ return True;
+#endif
+} /* CheckComboBox */
+
+/* --------------------------------------------------------------------
+ * Saemtliche Interface-Routinen zur Combo-Box
+ */
+/* Zunaechst alles fuer die Listbox */
+#define ListBox (((XmComboBoxWidget) w)->combobox.ListCtrl)
+#define EditBox (((XmComboBoxWidget) w)->combobox.EditCtrl)
+#define ComboBox ((XmComboBoxWidget) w)
+
+/* !!!
+ * So angepasst, dass bei doppelt auftretenden Eintraegen, der
+ * alte Eintrag weiterhin markiert bleibt. Diese Massnahme soll
+ * eigentlich nur verhindern, dass zufaellig zwei Eintraege
+ * markiert sind, falls nach der Anwahl eines Eintrages ein zweiter
+ * gleichlautender Eintrag hinzugefuegt wurde.
+ * Was hier die reine Lehre (oder war das die Leere?) anbetrifft:
+ * in einer Combo-Box sollten sich sowieso nie gleichlautende
+ * Eintraege befinden, da sie dort unsinnig sind und den Benutzer
+ * nur verwirren...
+ */
+void XmComboBoxAddItem(Widget w, XmString item, int pos)
+{
+ int OldIndex = XmComboBoxGetSelectedPos(w);
+
+ if ( CheckComboBox(w, "XmComboBoxAddItem") ) return;
+ if ( ComboBox->combobox.Sorted )
+ pos = FindSortedItemPos(ComboBox, item);
+ XmListAddItem(ListBox, item, pos);
+ if ( OldIndex != XmComboBoxGetSelectedPos(w) )
+ /* Hier SetSelectionPos() statt XmComboBoxSelectPos(),
+ * da der Text nicht in das Eingabefeld uebertragen werden
+ * soll!
+ */
+ SetSelectionPos(ComboBox, OldIndex, False);
+} /* XmComboBoxAddItem */
+/* !!!
+ * Hier gilt das bereits oben gesagte (siehe XmComboBoxAddItem).
+ * Bei sortierten Listboxen wird die Sortierung beim Gebrauch dieser
+ * Funktion zerstoert!
+ */
+void XmComboBoxAddItems(Widget w, XmString *items, int item_count, int pos)
+{
+ int OldIndex = XmComboBoxGetSelectedPos(w);
+
+ if ( CheckComboBox(w, "XmComboBoxAddItems") ) return;
+ XmListAddItems(ListBox, items, item_count, pos);
+ if ( OldIndex != XmComboBoxGetSelectedPos(w) )
+ /* Siehe Anmerkung in XmComboBoxAddItem */
+ SetSelectionPos(ComboBox, OldIndex, False);
+} /* XmComboBoxAddItems */
+
+void XmComboBoxAddItemUnselected(Widget w, XmString item, int pos)
+{ XmListAddItemUnselected(ListBox, item, pos); }
+
+/* !!!
+ * Da bei den folgenden Routinen jeweils ein oder mehrere Eintraege
+ * geloescht oder veraendert werden, muss gegebenefalls das Eingabe-
+ * feld bei nicht editierbaren Combo-Boxen auf Vordermann gebracht
+ * werden.
+ */
+void XmComboBoxDeleteItem(Widget w, XmString item)
+{
+ int Index = XmListItemPos(ListBox, item);
+
+ if ( CheckComboBox(w, "XmComboBoxDeleteItem") ) return;
+ if ( Index ) XmComboBoxDeletePos(w, Index);
+} /* XmComboBoxDeleteItem */
+
+void XmComboBoxDeleteItems(Widget w, XmString *items, int item_count)
+{
+ int i;
+
+ if ( CheckComboBox(w, "XmComboBoxDeleteItems") ) return;
+ for ( i = 0; i < item_count; i++ )
+ XmListDeleteItem(w, items[i]);
+} /* XmComboBoxDeleteItems */
+
+void XmComboBoxDeletePos(Widget w, int pos)
+{
+ int OldIndex = XmComboBoxGetSelectedPos(w);
+
+ if ( CheckComboBox(w, "XmComboBoxDeletePos") ) return;
+ XmListDeletePos(ListBox, pos);
+ if ( pos == OldIndex ) UpdateComboBox(ComboBox, pos, True);
+} /* XmComboBoxDeletePos */
+
+void XmComboBoxDeleteItemsPos(Widget w, int item_count, int pos)
+{
+ int i;
+
+ if ( CheckComboBox(w, "XmComboBoxDeleteItemsPos") ) return;
+ for ( i = 0; i < item_count; i++ )
+ XmComboBoxDeletePos(w, pos++);
+} /* XmComboBoxDeleteItemsPos */
+
+void XmComboBoxDeleteAllItems(Widget w)
+{
+ if ( CheckComboBox(w, "XmComboBoxAllDeleteItems") ) return;
+ XmListDeleteAllItems(ListBox);
+ UpdateComboBox(ComboBox, 0, True);
+} /* XmComboBoxDeleteAllItems */
+
+/* !!!
+ * Werden Eintraege ausgetauscht, so heisst es fuer uns, auch hierbei
+ * auf der Hut zu sein.
+ */
+void XmComboBoxReplaceItems(Widget w, XmString *old_items, int item_count, XmString *new_items)
+{
+ if ( CheckComboBox(w, "XmComboBoxReplaceItems") ) return;
+ XmListReplaceItems(ListBox, old_items, item_count, new_items);
+ UpdateComboBox(ComboBox, XmComboBoxGetSelectedPos(w), False);
+} /* XmComboBoxReplaceItems */
+
+void XmComboBoxReplaceItemsPos(Widget w, XmString *new_items, int item_count, int position)
+{
+ int OldIndex = XmComboBoxGetSelectedPos(w);
+
+ if ( CheckComboBox(w, "XmComboBoxReplaceItemsPos") ) return;
+ XmListReplaceItemsPos(ListBox, new_items, item_count, position);
+ if ( (OldIndex >= position) && (OldIndex < position + item_count) )
+ UpdateComboBox(ComboBox, OldIndex, False);
+} /* XmComboBoxReplaceItemsPos */
+
+Boolean XmComboBoxItemExists(Widget w, XmString item)
+{
+ if ( CheckComboBox(w, "XmComboBoxItemExists") ) return False;
+ return XmListItemExists(ListBox, item);
+} /* XmComboBoxItemExists */
+
+int XmComboBoxItemPos(Widget w, XmString item)
+{
+ if ( CheckComboBox(w, "XmComboBoxItemPos") ) return 0;
+ return XmListItemPos(ListBox, item);
+} /* XmComboBoxItemPos */
+
+Boolean XmComboBoxGetMatchPos(Widget w, XmString item, int **pos_list, int *pos_count)
+{
+ if ( CheckComboBox(w, "XmComboBoxGetMatchPos") ) return False;
+ return XmListGetMatchPos(ListBox, item, pos_list, pos_count);
+} /* XmComboBoxGetMatchPos */
+
+/* !!!
+ * Sobald ein anderer Eintrag in der Listbox ausgewaehlt werden soll,
+ * muessen wir hier helfend eingreifen.
+ */
+void XmComboBoxSelectPos(Widget w, int pos, Boolean notify)
+{
+ int index;
+
+ if ( CheckComboBox(w, "XmComboBoxSelectPos") ) return;
+ index = SetSelectionPos(ComboBox, pos, notify);
+ if ( index ) TransferToEditCtrl(ComboBox, index, False);
+} /* XmComboBoxSelectPos */
+
+/* !!!
+ * dto. analog zu XmComboBoxSelectPos, nur statt des Index wird der
+ * Eintragstext angegeben, um einen Eintrag in der Listbox zu
+ * markieren.
+ */
+void XmComboBoxSelectItem(Widget w, XmString item, Boolean notify)
+{
+ int index;
+
+ if ( CheckComboBox(w, "XmComboBoxSelectItem") ) return;
+ XmListSelectItem(ListBox, item, notify);
+ index = SetSelectionPos(ComboBox, XmComboBoxGetSelectedPos(w), False);
+ if ( index ) TransferToEditCtrl(ComboBox, index, False);
+} /* XmComboBoxSelectItem */
+
+/* !!!
+ * Geaendert gegenueber dem ListBox-Pendant! Da in einer Combo-Box die
+ * Liste nur maximal einen ausgewaehlten Eintrag besitzt, macht die
+ * 'alte' Funktionalitaet von XmListGetSelectedPos ziemlich wenig Sinn.
+ * Die neue Routine liefert statt dessen direkt den Index des aus-
+ * gewaehlten Eintrages oder 0 zurueck.
+ */
+int XmComboBoxGetSelectedPos(Widget w)
+{
+ int *SelectionList, SelectionCount, SelectionIndex;
+
+ if ( CheckComboBox(w, "XmComboBoxGetSelectedPos") ) return 0;
+ if ( XmListGetSelectedPos(ListBox,
+ &SelectionList, &SelectionCount) ) {
+ SelectionIndex = *SelectionList;
+ XtFree((char *)SelectionList);
+ } else SelectionIndex = 0;
+ return SelectionIndex;
+} /* XmComboBoxGetSelectedPos */
+
+
+
+void XmComboBoxClearSelection(Widget w, Time time)
+{
+ XmTextFieldClearSelection(EditBox, time);
+} /* XmComboBoxClearSelection */
+
+Boolean XmComboBoxCopy(Widget w, Time time)
+{
+ return XmTextFieldCopy(EditBox, time);
+} /* XmComboBoxCopy */
+
+Boolean XmComboBoxCut(Widget w, Time time)
+{
+ return XmTextFieldCut(EditBox, time);
+} /* XmComboBoxCut */
+
+XmTextPosition XmComboBoxGetInsertionPosition(Widget w)
+{
+ return XmTextFieldGetInsertionPosition(EditBox);
+} /* XmComboBoxGetInsertionPosition */
+
+XmTextPosition XmComboBoxGetLastPosition(Widget w)
+{
+ return XmTextFieldGetLastPosition(EditBox);
+} /* XmComboBoxGetLastPosition */
+
+int XmComboBoxGetMaxLength(Widget w)
+{
+ return XmTextFieldGetMaxLength(EditBox);
+} /* XmComboBoxGetMaxLength */
+
+char * XmComboBoxGetSelection(Widget w)
+{
+ return XmTextFieldGetSelection(EditBox);
+} /* XmComboBoxGetSelection */
+
+Boolean XmComboBoxGetSelectionPosition(Widget w, XmTextPosition *left,
+ XmTextPosition *right)
+{
+ return XmTextFieldGetSelectionPosition(EditBox, left, right);
+} /* XmComboBoxGetSelectionPosition */
+
+char * XmComboBoxGetString(Widget w)
+{
+ return XmTextFieldGetString(EditBox);
+} /* XmComboBoxGetString */
+
+void XmComboBoxInsert(Widget w, XmTextPosition position, char *value)
+{
+ XmTextFieldInsert(EditBox, position, value);
+} /* XmComboBoxInsert */
+
+Boolean XmComboBoxPaste(Widget w)
+{
+ return XmTextFieldPaste(EditBox);
+} /* XmComboBoxPaste */
+
+Boolean XmComboBoxRemove(Widget w)
+{
+ return XmTextFieldRemove(EditBox);
+} /* XmComboBoxRemove */
+
+void XmComboBoxReplace(Widget w, XmTextPosition from_pos,
+ XmTextPosition to_pos, char *value)
+{
+ XmTextFieldReplace(EditBox, from_pos, to_pos, value);
+} /* XmComboBoxReplace */
+
+void XmComboBoxSetAddMode(Widget w, Boolean state)
+{
+ XmTextFieldSetAddMode(EditBox, state);
+} /* XmComboBoxSetAddMode */
+
+void XmComboBoxSetHighlight(Widget w, XmTextPosition left,
+ XmTextPosition right, XmHighlightMode mode)
+{
+ XmTextFieldSetHighlight(EditBox, left, right, mode);
+} /* XmComboBoxSetHighlight */
+
+void XmComboBoxSetInsertionPosition(Widget w, XmTextPosition position)
+{
+ XmTextFieldSetInsertionPosition(EditBox, position);
+} /* XmComboBoxSetInsertionPosition */
+
+void XmComboBoxSetMaxLength(Widget w, int max_length)
+{
+ XmTextFieldSetMaxLength(EditBox, max_length);
+} /* XmComboBoxSetMaxLength */
+
+void XmComboBoxSetSelection(Widget w, XmTextPosition first,
+ XmTextPosition last, Time time)
+{
+ XmTextFieldSetSelection(EditBox, first, last, time);
+} /* XmComboBoxSetSelection */
+
+void XmComboBoxSetString(Widget w, char *value)
+{
+/* Liebe OSF...ihr ^&*#%$*&)*(@$(*^(*&%# habt doch einen ziemlich gemeinen
+ * Fehler in XmTextFieldSetString() drin... wenn man einen leeren String
+ * (also "") angiebt, gibt's nur noch Aerger, wenn man spaeter wieder an
+ * den Inhalt des Eingabefeldes heranwill.
+ */
+ if ( (value == NULL) || (*value == 0) )
+ XtVaSetValues(w, XmNvalue, "", NULL);
+ else
+ XmTextFieldSetString(EditBox, value);
+} /* XmComboBoxSetString */
+
+void XmComboBoxShowPosition(Widget w, XmTextPosition position)
+{
+ XmTextFieldShowPosition(EditBox, position);
+} /* XmComboBoxShowPosition */
+
+/*
+ * Loescht einen evtl. noch ausgewaehlten Eintrag in einer Combo Box,
+ * wenn diese eine SelectionPolicy von XmSINGLE_SELECT hat.
+ */
+void XmComboBoxClearItemSelection(Widget w)
+{
+ int index;
+
+ if ( CheckComboBox(w, "XmComboBoxClearItemSelection") ) return;
+
+ /*
+ * Wenn bereits kein Eintrag markiert ist, dann loeschen wir nur noch
+ * eben das Eingabefeld.
+ */
+ index = XmComboBoxGetSelectedPos(w);
+ if ( index == 0 ) {
+ XmComboBoxSetString(w, "");
+ } else {
+ /*
+ * Ansonsten aktuellen Eintrag entsorgen (wie bei der Methode wipe-out)
+ */
+ TransferToEditCtrl(ComboBox, 0, True);
+ CallSelectionCBL(ComboBox, NULL);
+ }
+} /* XmComboBoxClearItemSelection */
+
+/* Die Drop-Down-Liste ein oder ausklappen */
+void XmComboBoxShowList(Widget w)
+{
+ if ( CheckComboBox(w, "XmComboBoxShowList") ) return;
+ ShowHideDropDownList((XmComboBoxWidget) w, NULL, False);
+} /* XmComboBoxShowList */
+
+void XmComboBoxHideList(Widget w)
+{
+ if ( CheckComboBox(w, "XmComboBoxHideList") ) return;
+ ShowHideDropDownList((XmComboBoxWidget) w, NULL, True);
+} /* XmComboBoxShowList */
+
+/*
+ * Naja, ich komm' ja doch nicht um diese olle Funktion herum...
+ */
+Widget XmCreateComboBox(Widget parent, String name, ArgList arglist,
+ Cardinal argcount)
+{
+ return XtCreateWidget(name, xmComboBoxWidgetClass, parent,
+ arglist, argcount);
+} /* XmCreateComboBox */
+
+/* Ende von ComboBox.c */