]> git.saurik.com Git - wxWidgets.git/blob - src/motif/xmcombo/xmcombo.c
Space now move active cell right if grid is not editable
[wxWidgets.git] / src / motif / xmcombo / xmcombo.c
1 /*
2 * ComboBox.c - Das schon lange schmerzlich vermisste Combo-Box-
3 * Widget -- nun endlich auf fuer Motif!
4 *
5 * Version 1.32a -- 04.10.95
6 *
7 * Letzte Modifikation:
8 * 04.10.1995 Layoutfehler behoben, der bei angezeigter horizontaler Liste
9 * dazu fuehrt, dass das Listenfeld schrumpft. Daneben wird
10 * jetzt auch der Fall beruecksichtigt, dass das Listenfeld am
11 * unteren Bildschirmrand abgeschnitten wuerde. In diesem Fall
12 * erscheint das Listenfeld oberhalb des Eingabefeldes.
13 * 20.03.1995 XmNscrollbarDisplayPolicy,... koennen nun immer vom Pro-
14 * grammierer gesetzt werden, statische Liste hin und her.
15 * 21.10.1994 Fehler in SetValues behoben, der auftritt, wenn man versucht,
16 * XmNitems und XmNitemCount zu setzen.
17 * 01.10.1994 Externe Sortierung wird nun unterstuetzt sowie seitenweises
18 * Rollen in der Liste mittels PgUp und PgDn.
19 * 25.09.1994 Unterstuetzung fuer XmNautomaticSelection implementiert.
20 * Damit wird die Sache noch ein bischen runder in der Bedienung.
21 * Des weiteren sind etliche Callbacks neu hinzugekommen.
22 * 04.09.1994 Erweiterungen fuer XmSINGLE_SELECT eingebaut. Ausserdem
23 * kann die Liste jetzt auch statisch unterhalb des Eingabe-
24 * felds erscheinen. Damit sind wir nun noch kompatibler ge-
25 * worden -- fragt sich nur, zu was?!
26 * 29.08.1994 Alle Mirror-Ressourcen tauchen nun auch in der Ressourcen-
27 * liste der ComboBox-Klasse auf. Allerdings stehen keine
28 * sinnvollen Werte fuer die Initialisierung 'drin. Weiterhin
29 * den GeometryManager so veraendert, dass ab sofort das
30 * Label in der Breite wachsen oder schrumpfen darf.
31 * 07.06.1994 XmNmnemonic und XmNmnemonicCharSet implementiert.
32 * 29.05.1994 XmNsensitive angepasst. XmNcursorPositionVisible ist nun
33 * False, falls die ComboBox nicht editierbar ist.
34 * 07.05.1994 Drag'n'Drop funktioniert endlich!!! Zudem Anpassung an
35 * den fvwm ausgefuehrt ('st vom Focus-Verhalten ja ein halber
36 * twm). Hach', so'ne Linux-Box mit Motif 1.2 macht doch
37 * einfach Spass... vor allem geht hier so richtig die Post ab.
38 * Das kann man ja von M$ Windoze (Windoze for Mondays) nicht
39 * behaupten!
40 * 14.04.1994 Ein paar Speicherlecks korrigiert.
41 * 21.02.1994 Die Resourcen XmNitems und XmNitemCount lassen sich nun
42 * auch von einer Resourcendatei aus initialisieren. ACHTUNG:
43 * diese beiden Resourcen mussen immer beide beim Aufruf von
44 * XtSetValues zugleich angegeben werden, ansonsten werden
45 * diese Angaben ignoriert.
46 * 03.02.1994 Convenience-Funktionen auf Vordermann gebracht und noch
47 * einen Callback eingebaut, der immer dann aufgerufen wird,
48 * wenn die List angezeigt oder wieder versteckt wird.
49 * 01.02.1994 Motif 1.2-fest!!! Das wird aber heute abend gefeiert!!
50 * Endlich EIN Alptraum weniger! Naja, Drag'n'Drop bleibt
51 * noch zu loesen. Spaeter...
52 * 31.01.1994 VAX-fest (mit Hilfe von Vincenct Li)
53 * owlm sollte man abschaffen! Aber es scheint so, als ob
54 * ich jetzt doch noch das FocusOut-Problem geknackt habe.
55 * Ebenso die OSF...mit viel Arbeit habe ich nun auch noch
56 * eine anstaendige Initialisierung der Fontliste des Label-
57 * Kinds erreicht.
58 * 12.01.1994 Revisionsstand: 1.10a
59 * nun wirklich voll ANSI-faehiger C-Code
60 * Pixmaps werden ggf. aufgeraeumt; Druckrichtung
61 * wird vom Vater erfragt und an das Label weiter-
62 * gegeben.
63 * ESC-Behandlung implementiert.
64 * Spiegel-Ressourcen-Initialisierung aus Ressourcen-Daten-
65 * bank implementiert.
66 * Weitergabe von neu gesetzten Farben an die Kinder
67 * implementiert.
68 * Combo-Box kann jetzt wahlweise auch links neben dem
69 * Eingabefeld ein Label anzeigen.
70 * 09.12.1993 Revisionsstand: 1.00
71 * erste oeffentlich zugaengliche Version der Combo-Box
72 *
73 * (c) 1993, 1994, 1995 Harald Albrecht
74 * Institut fuer Geometrie und Praktische Mathematik
75 * RWTH Aachen, Germany
76 * albrecht@igpm.rwth-aachen.de
77 *
78 * This program is free software; you can redistribute it and/or modify
79 * it under the terms of the GNU General Public License as published by
80 * the Free Software Foundation; either version 2 of the License, or
81 * (at your option) any later version.
82 *
83 * This program is distributed in the hope that it will be useful,
84 * but WITHOUT ANY WARRANTY; without even the implied warranty of
85 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
86 * GNU General Public License for more details.
87 *
88 * You should have received a copy of the GNU General Public License
89 * along with this program (see the file COPYING for more details);
90 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave,
91 * Cambridge, MA 02139, USA.
92 *
93 */
94
95 #include <X11/IntrinsicP.h>
96 #include <X11/StringDefs.h>
97 #include <X11/cursorfont.h>
98 #include <X11/Shell.h>
99 #ifdef VMS /* Huch, wo gibt's denn noch sowas ... ?! */
100 /* Bitte keine Mail bzgl. dieser Bemerkung schicken...
101 * Ich weiss, das ist ein Vorurteil...aber es gibt
102 * ja auch wahre Vorurteile....
103 */
104 #include <Xmu/Converters.h>
105 #else
106 #include <X11/Xmu/Converters.h>
107 #endif
108 #include <Xm/ArrowB.h>
109 #include <Xm/TextF.h>
110 #include <Xm/List.h>
111 #include <Xm/LabelP.h>
112 #include <string.h>
113 #include <ctype.h> /* define toupper */
114 #include "combop.h"
115
116 #include <stdio.h>
117
118 /* --- Systemspezifische Definitionen */
119 #ifdef VMS
120 #define strcasecmp(s1, s2) strcmp(s1, s2)
121 #endif
122
123 /* --- sonstiger Quark */
124 /* #ifdef DEBUG */
125 #if 0
126 #define LOG(p1) fprintf(stderr, p1);
127 #define LOG2(p1, p2) fprintf(stderr, p1, p2);
128 #define LOG3(p1, p2, p3) fprintf(stderr, p1, p2, p3);
129 #else
130 #define LOG(p1)
131 #define LOG2(p1, p2)
132 #define LOG3(p1, p2, p3)
133 #endif
134
135 /* ---------------------------------------------------------------------------
136 * Resourcen-Liste...
137 * Hier werden diejenigen Resourcen definiert, die von "aussen" - also fuer
138 * den Programmierer oder Anwender - benutzbar und veraenderbar sind.
139 *
140 * Der Aufbau der einzelnen Eintraege ist immer wieder gleich:
141 * Resourcen-Name XmN... oder XtN
142 * Resourcen-Klasse XmC... oder XtC
143 * Resourcen-Type XmR... oder XtR (Datentyp der Variable in der
144 * struct der jeweiligen Widgetinstanz)
145 * Resourcen-Groesse aktuelle Groesse dieses Datentyps
146 * Resourcen-Offset Lage der Variable innerhalb der struct der
147 * Widgetinstanz
148 * Defaultwert-Type Typ des Defaultwertes
149 * Defaultwert (normalerweise) Zeiger auf den Defaultwert
150 */
151 #define offset(field) XtOffsetOf(XmComboBoxRec, field)
152 static XtResource resources[] = {
153 { /* Eingabefeld kann veraendert werden, oder aber es sind nur
154 * die Vorgaben aus der Liste auswaehlbar.
155 */
156 XmNeditable, XmCEditable, XmRBoolean, sizeof(Boolean),
157 offset(combobox.Editable), XmRString, "False"
158 },
159 { /* Liste wird automatisch sortiert -- wie konnten die bei
160 * der OSF denn SOETWAS nur vergessen ??
161 */
162 XmNsorted, XmCSorted, XmRBoolean, sizeof(Boolean),
163 offset(combobox.Sorted), XmRString, "False"
164 },
165 { /* externe Sortierreihenfolge */
166 XmNsortingCallback, XmCSortingCallback, XmRCallback,
167 sizeof(XtCallbackList),
168 offset(combobox.SortingCBL), XmRCallback, NULL
169 },
170 { /* Anzahl auf einmal sichtbarer Eintraege in der Liste (ent-
171 * spricht damit der Listenhoehe.
172 */
173 XmNvisibleItemCount, XmCVisibleItemCount, XmRInt, sizeof(int),
174 offset(combobox.VisibleItemCount), XmRImmediate, (caddr_t) 8
175 },
176 { /* Fuer das Eingabefeld sowie die Liste verwandte Fonts */
177 XmNfontList, XmCFontList, XmRFontList, sizeof(XmFontList),
178 offset(combobox.Font), XmRImmediate, NULL
179 },
180 { /* Rueckruf bei Anwahl */
181 XmNselectionCallback, XmCSelectionCallback, XmRCallback,
182 sizeof(XtCallbackList),
183 offset(combobox.SelectionCBL), XmRCallback, NULL
184 },
185 { /* Gegenteil zum vorherigen Callback! */
186 XmNunselectionCallback, XmCUnselectionCallback, XmRCallback,
187 sizeof(XtCallbackList),
188 offset(combobox.UnselectionCBL), XmRCallback, NULL
189 },
190 { /* Doppelklick in der Liste */
191 XmNdefaultActionCallback, XmCCallback, XmRCallback,
192 sizeof(XtCallbackList),
193 offset(combobox.DefaultActionCBL), XmRCallback, NULL
194 },
195 { /* Rueckruf bei Liste ausklappen/verstecken */
196 XmNdropDownCallback, XmCDropDownCallback, XmRCallback,
197 sizeof(XtCallbackList),
198 offset(combobox.DropDownCBL), XmRCallback, NULL
199 },
200 { /* Eingabe abchecken... */
201 XmNmodifyVerifyCallback, XmCCallback, XmRCallback,
202 sizeof(XtCallbackList),
203 offset(combobox.ModifyVerifyCBL), XmRCallback, NULL
204 },
205 {
206 XmNvalueChangedCallback, XmCCallback, XmRCallback,
207 sizeof(XtCallbackList),
208 offset(combobox.ValueChangedCBL), XmRCallback, NULL
209 },
210 {
211 XmNactivateCallback, XmCCallback, XmRCallback,
212 sizeof(XtCallbackList),
213 offset(combobox.ActivateCBL), XmRCallback, NULL
214 },
215 {
216 XmNmotionVerifyCallback, XmCCallback, XmRCallback,
217 sizeof(XtCallbackList),
218 offset(combobox.MotionVerifyCBL), XmRCallback, NULL
219 },
220 { /* Verhalten der ausgeklappten Liste bei Focus-Out */
221 XmNpersistentDropDown, XmCPersistentDropDown, XmRBoolean,
222 sizeof(Boolean),
223 offset(combobox.Persistent), XmRString, "False"
224 },
225 { /* Wie verhaelt sich der Window-Manager? */
226 XmNtwmHandlingOn, XmCTwmHandlingOn, XmRBoolean, sizeof(Boolean),
227 offset(combobox.TwmHandlingOn), XmRString, "False"
228 },
229 { /* Label anzeigen oder nicht? */
230 XmNshowLabel, XmCShowLabel, XmRBoolean, sizeof(Boolean),
231 offset(combobox.ShowLabel), XmRString, "False"
232 },
233 { /* Abstand zw. linkem Rand Eingabefeld und linkem Rand Liste */
234 XmNdropDownOffset, XmCDropDownOffset, XmRPosition,
235 sizeof(Position), offset(combobox.DropDownOffset),
236 XmRImmediate, (caddr_t) -1
237 },
238 { /* Neue Voreinstellung bzgl. des Randes */
239 XmNborderWidth, XmCBorderWidth, XmRDimension, sizeof(Dimension),
240 offset(core.border_width), XmRImmediate, (caddr_t) 0
241 },
242 { /* welcher Cursor soll in der Dropdown-Liste benutzt werden? */
243 XmNdropDownCursor, XmCDropDownCursor, XmRCursor, sizeof(Cursor),
244 offset(combobox.ArrowCursor), XmRString, "center_ptr"
245 },
246 { /* wie lassen sich Eintraege auswaehlen? */
247 XmNselectionPolicy, XmCSelectionPolicy, XmRSelectionPolicy, sizeof(unsigned char),
248 offset(combobox.SelectionPolicy), XmRImmediate, (caddr_t) XmBROWSE_SELECT
249 },
250 { /* Wann werden die Callbacks aufgerufen? */
251 XmNautomaticSelection, XmCAutomaticSelection, XmRBoolean, sizeof(Boolean),
252 offset(combobox.AutomaticSelection), XmRString, "False"
253 },
254 { /* erscheint die Liste staendig? */
255 XmNstaticList, XmCStaticList, XmRBoolean, sizeof(Boolean),
256 offset(combobox.StaticList), XmRString, "False"
257 },
258 {
259 XmNscrollBarDisplayPolicy, XmCScrollBarDisplayPolicy, XmRScrollBarDisplayPolicy, sizeof(unsigned char),
260 offset(combobox.ScrollBarDisplayPolicy), XmRImmediate, (caddr_t) XmAS_NEEDED
261 },
262 {
263 XmNlistSizePolicy, XmCListSizePolicy, XmRListSizePolicy, sizeof(unsigned char),
264 offset(combobox.ListSizePolicy), XmRImmediate, (caddr_t) XmVARIABLE
265 },
266 {
267 XmNsquareArrow, XmCSquareArrow, XmRBoolean, sizeof(Boolean),
268 offset(combobox.SquareArrow), XmRString, "False"
269 },
270 {
271 XmNarrowSpacingOn, XmCArrowSpacingOn, XmRBoolean, sizeof(Boolean),
272 offset(combobox.ArrowSpacingOn), XmRString, "True"
273 },
274 #ifndef DONT_LOOK_IN_THE_MIRROR
275 /* Mirror-Ressourcen, Adressen sind ungueltig!!!! */
276 {
277 XmNalignment, XmCAlignment, XmRAlignment, sizeof(unsigned char),
278 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
279 },
280 {
281 XmNblinkRate, XmCBlinkRate, XmRInt, sizeof(int),
282 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
283 },
284 {
285 XmNcolumns, XmCColumns, XmRShort, sizeof(short),
286 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
287 },
288 {
289 XmNcursorPosition, XmCCursorPosition, XmRTextPosition, sizeof(XmTextPosition),
290 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
291 },
292 {
293 XmNitemCount, XmCItemCount, XmRInt, sizeof(int),
294 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
295 },
296 {
297 XmNitems, XmCItems, XmRXmStringTable, sizeof(XmStringTable),
298 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
299 },
300 {
301 XmNlabelFontList, XmCLabelFontList, XmRFontList, sizeof(XmFontList),
302 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
303 },
304 {
305 XmNlabelInsensitivePixmap, XmCLabelInsensitivePixmap, XmRPixmap, sizeof(Pixmap),
306 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
307 },
308 {
309 XmNlabelMarginBottom, XmCLabelMarginBottom, XmRDimension, sizeof(Dimension),
310 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
311 },
312 {
313 XmNlabelMarginHeight, XmCLabelMarginHeight, XmRDimension, sizeof(Dimension),
314 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
315 },
316 {
317 XmNlabelMarginLeft, XmCLabelMarginLeft, XmRDimension, sizeof(Dimension),
318 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
319 },
320 {
321 XmNlabelMarginRight, XmCLabelMarginRight, XmRDimension, sizeof(Dimension),
322 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
323 },
324 {
325 XmNlabelMarginTop, XmCLabelMarginTop, XmRDimension, sizeof(Dimension),
326 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
327 },
328 {
329 XmNlabelMarginWidth, XmCLabelMarginWidth, XmRDimension, sizeof(Dimension),
330 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
331 },
332 {
333 XmNlabelPixmap, XmCLabelPixmap, XmRPixmap, sizeof(Pixmap),
334 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
335 },
336 {
337 XmNlabelString, XmCLabelString, XmRString, sizeof(XmString),
338 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
339 },
340 {
341 XmNlabelType, XmCLabelType, XmRLabelType, sizeof(unsigned char),
342 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
343 },
344 {
345 XmNlistMarginHeight, XmCListMarginHeight, XmRDimension, sizeof(Dimension),
346 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
347 },
348 {
349 XmNlistMarginWidth, XmCListMarginWidth, XmRDimension, sizeof(Dimension),
350 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
351 },
352 {
353 XmNlistSpacing, XmCListSpacing, XmRDimension, sizeof(Dimension),
354 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
355 },
356 {
357 XmNmarginHeight, XmCMarginHeight, XmRDimension, sizeof(Dimension),
358 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
359 },
360 {
361 XmNmarginWidth, XmCMarginWidth, XmRDimension, sizeof(Dimension),
362 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
363 },
364 {
365 XmNmaxLength, XmCMaxLength, XmRInt, sizeof(int),
366 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
367 },
368 {
369 XmNselectThreshold, XmCSelectThreshold, XmRInt, sizeof(int),
370 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
371 },
372 {
373 XmNstringDirection, XmCStringDirection, XmRStringDirection, sizeof(XmStringDirection),
374 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
375 },
376 {
377 XmNtopItemPosition, XmCTopItemPosition, XmRInt, sizeof(int),
378 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
379 },
380 {
381 XmNvalue, XmCValue, XmRString, sizeof(String),
382 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
383 },
384 {
385 XmNvalue, XmCValue, XmRInt, sizeof(int),
386 offset(combobox.Dummy), XmRImmediate, (caddr_t) 0
387 },
388 #endif
389 }; /* resources[] */
390
391 /* ---------------------------------------------------------------------------
392 * Funktions-Prototypen fuer die 'Methoden' des ComboBox-Widgets. Diese
393 * 'Methoden' werden vom Xt-Toolkit aufgerufen und sorgen dafuer, dass eine
394 * ComboBox sich wie ein anstaendiges Widget verhaelt.
395 */
396 static void Initialize (Widget, XmComboBoxWidget, ArgList,
397 Cardinal *);
398 static void Destroy (XmComboBoxWidget);
399 static void Resize (XmComboBoxWidget);
400 static Boolean SetValues (XmComboBoxWidget, XmComboBoxWidget,
401 XmComboBoxWidget, ArgList, Cardinal *);
402 static void GetValuesAlmost(XmComboBoxWidget, ArgList, Cardinal *);
403 static XtGeometryResult QueryGeometry (XmComboBoxWidget, XtWidgetGeometry *,
404 XtWidgetGeometry *);
405 static XtGeometryResult GeometryManager(Widget, XtWidgetGeometry *,
406 XtWidgetGeometry *);
407 static void ClassInit ();
408 static void Realize (XmComboBoxWidget, Mask *,
409 XSetWindowAttributes *);
410 /* ---------------------------------------------------------------------------
411 * diverse restliche Prototypen... naja, hier halt etwas mager! Hierbei
412 */
413 static void ShowHideDropDownList (XmComboBoxWidget w, XEvent *event,
414 Boolean Show);
415 static void ShellCallback (Widget w, XtPointer cbw,
416 XEvent *event, Boolean *ContDispatch);
417 static void DoLayout (XmComboBoxWidget w);
418 /* --------------------------------------------------------------------
419 * Klassen-Definition
420 */
421 XmComboBoxClassRec xmComboBoxClassRec = {
422 { /*** core-Klasse ***/
423 /* superclass */ (WidgetClass) &xmManagerClassRec,
424 /* class_name */ "XmComboBox",
425 /* widget_size */ sizeof(XmComboBoxRec),
426 /* class_initialize */ (XtProc) ClassInit,
427 /* class_part_initialize */ NULL,
428 /* class_inited */ False, /* IMMER mit FALSE initialisieren !! */
429 /* initialize */ (XtInitProc) Initialize,
430 /* initialize_hook */ NULL,
431 /* realize */ (XtRealizeProc) Realize,
432 /* actions */ NULL,
433 /* num_actions */ 0,
434 /* resources */ resources,
435 /* num_resources */ XtNumber(resources),
436 /* xrm_class */ NULLQUARK,
437 /* compress_motion */ True,
438 /* compress_exposure */ XtExposeCompressMultiple,
439 /* compress_enterleave */ True,
440 /* visible_interest */ False,
441 /* destroy */ (XtWidgetProc) Destroy,
442 /* resize */ (XtWidgetProc) Resize,
443 /* expose */ NULL,
444 /* set_values */ (XtSetValuesFunc) SetValues,
445 /* set_values_hook */ NULL,
446 /* set_values_almost */ XtInheritSetValuesAlmost,
447 /* get_values_hook */ (XtArgsProc) GetValuesAlmost,
448 /* accept_focus */ NULL,
449 /* version */ XtVersion,
450 /* callback_private */ NULL,
451 /* tm_table */ NULL,
452 /* query_geometry */ (XtGeometryHandler) QueryGeometry,
453 /* display_accelerator */ XtInheritDisplayAccelerator,
454 /* extension */ NULL
455 },
456 { /*** composite-Klasse ***/
457 /* geometry_manager */ (XtGeometryHandler) GeometryManager,
458 /* change_managed */ XtInheritChangeManaged,
459 /* insert_child */ XtInheritInsertChild,
460 /* delete_child */ XtInheritDeleteChild,
461 /* extension */ NULL
462 },
463 { /*** constraint-Klasse ***/
464 /* resources */ NULL,
465 /* num_resources */ 0,
466 /* constraint_size */ sizeof(XmManagerConstraintPart),
467 /* initialize */ NULL,
468 /* destroy */ NULL,
469 /* set_values */ NULL,
470 /* extension */ NULL
471 },
472 { /*** xmManager-Klasse ***/
473 /* translations */ XtInheritTranslations,
474 /* syn_resources */ NULL,
475 /* num_syn_resources */ 0,
476 /* syn_constraint_resources */ NULL,
477 /* num_syn_constraint_resources */ 0,
478 /* parent_process */ XmInheritParentProcess,
479 /* extension */ NULL
480 },
481 { /*** combobox-Klasse ***/
482 /* */ 0
483 }
484 }; /* xmComboBoxClassRec */
485 WidgetClass xmComboBoxWidgetClass = (WidgetClass) &xmComboBoxClassRec;
486
487 /* --------------------------------------------------------------------
488 * --------------------------------------------------------------------
489 * Translation-Tabelle (hier allerdings fuer das Eingabefeld!)
490 * Tjaja....mit der Reihenfolge von Translations ist das schon so eine
491 * ziemlich boese Sache!
492 */
493 static char newEditTranslations[] =
494 "Alt<Key>osfDown: ComboBox-Manager(show-hide-list) \n\
495 Meta<Key>osfDown: ComboBox-Manager(show-hide-list) \n\
496 Alt<Key>osfUp: ComboBox-Manager(hide-list) \n\
497 Meta<Key>osfUp: ComboBox-Manager(hide-list) \n\
498 <Key>osfUp: ComboBox-Manager(up) \n\
499 <Key>osfDown: ComboBox-Manager(down) \n\
500 <Key>osfPageUp: ComboBox-Manager(page-up) \n\
501 <Key>osfPageDown: ComboBox-Manager(page-down) \n\
502 <Key>osfCancel: ComboBox-Manager(cancel) \n\
503 <Key>Return: ComboBox-Manager(activate) activate()"
504 ;
505 /* speziell bei der nicht editierbaren Combo-Box sind noch einige
506 * andere Tasten belegt, die sonst dem Eingabefeld alleine gehoeren.
507 * Die dazugehoerigen neuen Translations befinden sich in dieser
508 * zusaetzlichen Tabelle, das Anhaengsel ...NE ist dabei die Ab-
509 * kuerzung fuer "non editable".
510 */
511 static char newEditTranslationsNE[] =
512 "<Key>osfDelete: ComboBox-Manager(wipe-out) \n\
513 <Key>osfBeginLine: ComboBox-Manager(top) \n\
514 <Key>osfEndLine: ComboBox-Manager(bottom) "
515 ;
516 /* Momentan gibt es noch Aerger mit dem Drag'n'Drop-Mechanismus
517 * von Motif 1.2. Legen wir ihn deshalb erst einmal still, solange
518 * bis ich weiss, warum, und eine Loesung parat habe. NEU: Nur wenn
519 * Sie mit einer libXm geschlagen sind, die partout nicht funktionieren
520 * will, muessen Sie Drag'n'Drop stillegen, ansonsten klappts doch!
521 */
522 #ifdef NODRAGNDROP
523 static char newListTranslations[] =
524 "<Btn2Down>: ComboBox-Manager(no-operation) ";
525 #endif
526 static char newListTranslationsE[] =
527 "<Key>osfPageUp: ComboBox-Manager(page-up) \n\
528 <Key>osfPageDown: ComboBox-Manager(page-down) ";
529
530 /* ---------------------------------------------------------------------------
531 * ---------------------------------------------------------------------------
532 * Aktionen-Tabelle: Hierdurch werden den einzelnen Translations die dazuge-
533 * hoerigen C-Routinen zugeordnet. Da wir hier ein anstaendiges ANSI-C be-
534 * nutzen, werden hier zuerst einmal die Prototypen faellig... Ach ja, noch
535 * ein Hinweis in eigener Sache... der ComboBox-Manager muss applikationsweit
536 * registriert werden, da er auch von Translationen in den Kindern der Combo-
537 * Box aktiviert wird. Bei diesem Namen der 'Aktion' steht aber nicht zu be-
538 * fuerchten, dass er anderweitig bereits in Anwendung ist.
539 */
540 static void CBoxManager(Widget w, XEvent *event, String *params,
541 Cardinal *num_params);
542
543 static XtActionsRec actions[] = {
544 { "ComboBox-Manager", CBoxManager },
545 { NULL, NULL }
546 }; /* actions */
547
548
549 /* --------------------------------------------------------------------
550 * Eine Instanz dieser Widget-Klasse wird erstmalig in Betrieb ge-
551 * nommen, daher sind noch Vorbereitungen notwendig, die nun hier
552 * durchgefuehrt werden.
553 */
554 static XtTranslations NewEditTranslations, NewEditTranslationsNE,
555 NewListTranslations, NewListTranslationsE;
556
557 static XtConvertArgRec ConverterScreenConvertArg[] = {
558 { XtBaseOffset, (XtPointer) XtOffset(Widget, core.screen),
559 sizeof(Screen *) }
560 };
561
562 static void ClassInit()
563 {
564 NewEditTranslations =
565 XtParseTranslationTable(newEditTranslations);
566 NewEditTranslationsNE =
567 XtParseTranslationTable(newEditTranslationsNE);
568 #ifdef NODRAGNDROP
569 NewListTranslations =
570 XtParseTranslationTable(newListTranslations);
571 #endif
572 NewListTranslationsE =
573 XtParseTranslationTable(newListTranslationsE);
574 XtAddConverter(XtRString, XtRBitmap,
575 XmuCvtStringToBitmap,
576 ConverterScreenConvertArg,
577 XtNumber(ConverterScreenConvertArg));
578 } /* ClassInit */
579
580 /* ---------------------------------------------------------------------------
581 * Weil es sich bei diesem Widget um ein etwas komplizierteres zusammengesetz-
582 * tes Widget handelt, muessen wir hier - wo eigentlich nur das die Combobox
583 * bildende Fenster auf dem X-Server erzeugt wird - noch einmal das Layout
584 * auf Vordermann bringen. Den Aerger loest dabei das Listenfeld der OSF aus,
585 * das einfach keine Geometrie-Nachfragen verschickt, solange es nicht
586 * 'realized' ist!!! Nicht, dass ich mich ueber so einen Sauhaufen aufregen
587 * wuerde...ich doch nicht! ABER MACHT IHR DENN NUR SO'N MIST...? WARUM KOENNT
588 * IHR DENN NICHT EINMAL DIESES ****(BIEP)**** MOTIF TOOLKIT ANSTAENDIG
589 * DOKUMENTIEREN! Ich glaub', ich kann mich nach dem Chaos eigentlich nur noch
590 * hemmungslos besaufen... Die Suche nach der Ursache (bzw. Urheber = OSF) hat
591 * mich doch einige Tage gekostet (jaja...die Mannstunden!).
592 */
593 static void Realize(XmComboBoxWidget w, Mask *ValueMask,
594 XSetWindowAttributes *Attributes)
595 {
596 /*
597 * Also: wenn die Liste staendig sichtbar ist, dann zuerst noch einmal
598 * das Layout berechnen. Sonst wird vorne und hinten 'was abgeschnitten.
599 */
600 if ( w->combobox.StaticList )
601 DoLayout(w);
602 (*w->core.widget_class->core_class.superclass->core_class.realize)
603 ((Widget) w, ValueMask, Attributes);
604 } /* Realize */
605
606 /* ---------------------------------------------------------------------------
607 * Suche dasjenige Fenster, in dem unsere Shell liegt, in der wiederum die
608 * Combo-Box steckt. Diese Information wird benoetigt, um die Drop-Down-Liste
609 * innerhalb des Fensterstacks immer direkt oberhalb der Shell mit der Combo-
610 * Box zu halten. Jajaja -- ich muss halt davon ausgehen, dass der Fenster-
611 * manager ein sog. "reparenting wm" ist; also die Dekorationen in einem
612 * Fenster dargestellt werden und unsere Shell in dieses Fenster hineingepackt
613 * ist. Die Dekoration ist damit ein Kind des 'root window' - wie die Shell,
614 * in der die Drop-Down-Liste steckt. Und da nur Geschwisterfenster (sibling
615 * windows) im gleichen Stapel stecken, reicht das Shellfenster nicht aus.
616 * Alle gaengigen Fenstermanager sind solche "reparenting wm's", so dass ich
617 * hier zu diesem Trick greifen kann, um die Drop-Down-Liste immer ueber der
618 * ComboBox zu halten.
619 *
620 * Parameter:
621 * w Diejenige Combo-Box, fuer die wir dasjenige
622 * Fenster des Window-Managers ermitteln sollen,
623 * dass direkt unterhalb des Root-Fensters liegt.
624 * Ergebnis:
625 * besagtes zu suchendes Fenster, dass die Dekoration enthaelt (hoffentlich
626 * nur echte Bruesseler Spitze!)
627 */
628 static Window GetDecorationWindow(XmComboBoxWidget w)
629 {
630 Window Root, Parent, AWindow;
631 Window *Children;
632 unsigned int NumChildren;
633
634 Parent = XtWindow((Widget) w);
635 /* Suche nach dem Dekorationsfenster des Window-Managers */
636 do {
637 AWindow = Parent;
638 XQueryTree(XtDisplay((Widget) w), AWindow,
639 &Root, &Parent, &Children, &NumChildren);
640 XFree((char *) Children);
641 } while ( Parent != Root );
642 return AWindow;
643 } /* GetDecorationWindow */
644
645 /* --------------------------------------------------------------------
646 * Eine Combo-Box aus dem Wege raeumen...
647 * Momentan muessen wir hier nur den Cursor wieder los werden sowie
648 * eventuell reservierte Pixmaps.
649 * Ups -- natuerlich muss auch wieder der Callback entfernt werden,
650 * der noch an der Shell haengt.
651 */
652 static void Destroy(XmComboBoxWidget w)
653 {
654 /* fprintf(stderr, "Destroy: %08X\n", w->core.window);*/
655 if ( w->combobox.ConvertBitmapToPixmap )
656 XFreePixmap(XtDisplay((Widget) w),
657 w->combobox.LabelPixmap);
658 if ( w->combobox.ConvertBitmapToPixmapInsensitive )
659 XFreePixmap(XtDisplay((Widget) w),
660 w->combobox.LabelInsensitivePixmap);
661 if ( w->combobox.PendingFocusOut )
662 XtRemoveWorkProc(w->combobox.WorkProcID);
663 XtRemoveEventHandler(w->combobox.MyNextShell,
664 StructureNotifyMask | FocusChangeMask,
665 True, (XtEventHandler) ShellCallback,
666 (XtPointer) w);
667 } /* Destroy */
668
669 /* ---------------------------------------------------------------------------
670 * Ueberpruefe, ob fuer die Ressource "DropDownOffset" ein gueltiger Wert vom
671 * Benutzer angegeben wurde. Diese Ressource gibt an, wie weit die Drop-Down-
672 * Liste nach rechts gegenueber dem Eingabefeld eingerueckt sein soll. Wenn
673 * hierfuer ein negativer Wert angegeben ist, so berechne statt dessen einen
674 * Standardwert: dieser entspricht der Breite der Pfeilschaltflaeche, was
675 * optisch ganz gut wirkt (jedenfall nach meinem Dafuerhalten).
676 */
677 static void CheckDropDownOffset(XmComboBoxWidget w)
678 {
679 if ( w->combobox.DropDownOffset < 0 ) {
680 XtWidgetGeometry ArrowGeom;
681
682 XtQueryGeometry(w->combobox.ArrowCtrl, NULL, &ArrowGeom);
683 w->combobox.DropDownOffset = ArrowGeom.width;
684 }
685 } /* CheckDropDownOffset */
686
687 /* --------------------------------------------------------------------
688 * Berechne die voreinzustellende Groesse, die diese Combo-Box be-
689 * sitzen muss, um ausreichenden Raum fuer das Eingabefeld und den
690 * Pfeil rechts daneben zur Verfuegung zu stellen. Bei einer
691 * editierbaren Combo-Box ist zwischen dem Eingabefeld und dem Pfeil
692 * noch ein Angst-Rasen von der halben Breite eines Pfeiles vorhanden.
693 * Wird das Listenfeld staendig dargestellt, so entfallen sowohl Pfeil
694 * als auch der Angstrasen, dafuer muss aber die Hoehe des Listenfelds
695 * beruecksichtigt werden.
696 */
697 static void DefaultGeometry(XmComboBoxWidget w,
698 Dimension *TotalWidth,
699 Dimension *TotalHeight,
700 Dimension *EditCtrlWidth,
701 Dimension *LabelCtrlWidth)
702 {
703 XtWidgetGeometry EditGeom, ArrowGeom, LabelGeom, ListGeom;
704
705 XtQueryGeometry(w->combobox.EditCtrl, NULL, &EditGeom);
706 XtQueryGeometry(w->combobox.ArrowCtrl, NULL, &ArrowGeom);
707 XtQueryGeometry(w->combobox.LabelCtrl, NULL, &LabelGeom);
708
709 /*
710 * Soll die Pfeilschaltflaeche quadratisch, praktisch, gut sein?
711 */
712 if ( w->combobox.SquareArrow )
713 ArrowGeom.width = ArrowGeom.height;
714 else
715 ArrowGeom.width = (ArrowGeom.height * 4) / 5;
716
717 /*
718 * Zuerst einmal ein paar einfache Werte ermitteln und zurueckgeben...
719 */
720 *TotalHeight = EditGeom.height;
721 *EditCtrlWidth = EditGeom.width;
722 *LabelCtrlWidth = LabelGeom.width;
723
724 /*
725 * Ermittele nun die Breite, welche die Combobox benoetigt. Je nach-
726 * dem, ob das Eingabefeld oder die Liste breiter sind, wird der
727 * entsprechende Wert genommen. Diese Auswahl zwischen der Breite von
728 * Eingabefeld und Liste findet aber nur statt, wenn die Liste auch
729 * wirklich staendig sichtbar ist. Waehrend der Initialisierung hat
730 * allerdings XmNcolumns, so dass in diesem Moment die List nicht
731 * mehr die Breite kontrollieren kann!
732 */
733 if ( w->combobox.StaticList ) {
734 /*
735 * Beachte: Frage nicht die Listbox, sondern das ScrolledWindow,
736 * in welchem die Liste eingebettet ist.
737 */
738 CheckDropDownOffset(w);
739 XtQueryGeometry(XtParent(w->combobox.ListCtrl), NULL, &ListGeom);
740 if ( w->combobox.InInit ) {
741 *TotalWidth = EditGeom.width;
742 } else {
743 if ( EditGeom.width < (Dimension)
744 (ListGeom.width + w->combobox.DropDownOffset) )
745 *TotalWidth = ListGeom.width + w->combobox.DropDownOffset;
746 else
747 *TotalWidth = EditGeom.width;
748 }
749 *TotalHeight += ListGeom.height;
750 } else {
751 /*
752 * Das Listenfeld interessiert uns hier nicht. Degegen sollte noch
753 * die Breite fuer den Pfeil und ein evtl. Angstrasen beachtet
754 * werden.
755 */
756 *TotalWidth = EditGeom.width + ArrowGeom.width;
757 if ( w->combobox.Editable && w->combobox.ArrowSpacingOn )
758 *TotalWidth += ArrowGeom.width/2;
759 }
760
761 /*
762 * Vergiss nicht, auch noch ein evtl. sichtbares Schriftfeld zu berueck-
763 * sichtigen!
764 */
765 if ( w->combobox.ShowLabel )
766 *TotalWidth += LabelGeom.width;
767
768 } /* DefaultGeometry */
769
770 /* --------------------------------------------------------------------
771 * Anhand eines Widgets ermittele darueber die Screennummer desjenigen
772 * Screens, auf dem das Widget erscheint.
773 * Parameter:
774 * w betroffenes Widget.
775 * Ergebnis:
776 * Nummer desjenigen Screens, auf dem das Widget angezeigt wird.
777 */
778 static int WidgetToScreen(Widget w)
779 {
780 Screen *screen;
781 Display *display;
782 int NumScreens, i;
783
784 screen = XtScreen(w); NumScreens = ScreenCount(XtDisplay(w));
785 display = DisplayOfScreen(screen);
786 for ( i = 0; i < NumScreens; ++i )
787 if ( ScreenOfDisplay(display, i) == screen )
788 return i;
789 XtError("WidgetToScreen: data structures are destroyed.");
790 } /* WidgetToScreen */
791
792 /* --------------------------------------------------------------------
793 * Positioniere die DropDown-Liste (soweit sie natuerlich auch momentan
794 * sichtbar ist) so auf dem Bildschirm, dass sie sich unterhalb des
795 * Eingabefeldes anschliesst.
796 */
797 static void DoDropDownLayout(XmComboBoxWidget w)
798 {
799 Position abs_x, abs_y;
800 Dimension ArrowWidth, ListWidth, ListHeight;
801 Dimension ScreenHeight, LabelWidth;
802 Window Decoration;
803 XWindowChanges WindowChanges;
804
805 /*
806 * etwa nicht sichtbar ?!! Oder etwa immer sichtbar ?!!
807 * Dann sind wir jetzt sofort fertig.
808 */
809 if ( !w->combobox.ListVisible || w->combobox.StaticList ) return;
810 /*
811 * Finde zuerst einmal heraus, wo wir uns denn auf dem Bildschirm be-
812 * finden sollen... Beachte dabei auch, dass eventuell die Liste zu schmal
813 * werden koennte und gib' ihr dann ggf. eine Mindestbreite, damit es
814 * keinen core-Dump gibt.
815 */
816 XtVaGetValues(w->combobox.ArrowCtrl, XmNwidth, &ArrowWidth, NULL);
817 XtTranslateCoords((Widget) w, 0, w->core.height, &abs_x, &abs_y);
818 CheckDropDownOffset(w);
819 ListWidth = w->core.width - w->combobox.DropDownOffset - 2;
820 abs_x += w->combobox.DropDownOffset;
821 if ( w->combobox.ShowLabel ) {
822 XtVaGetValues(w->combobox.LabelCtrl, XmNwidth, &LabelWidth, NULL);
823 ListWidth -= LabelWidth;
824 abs_x += LabelWidth;
825 }
826 if ( ListWidth < 20 ) ListWidth = 20;
827 XtVaGetValues(XtParent(w->combobox.ListCtrl), XmNheight, &ListHeight, NULL);
828 /*
829 * Hier ueberpruefen wir noch, ob die Liste unten aus dem Bildschirm
830 * herausfallen wuerde. In dem Fall klappen wir die Liste oberhalb des
831 * Eingabefeldes auf.
832 */
833 ScreenHeight = DisplayHeight(XtDisplay((Widget) w),
834 WidgetToScreen((Widget) w));
835 if ( abs_y + ListHeight + 2 > ScreenHeight ) {
836 int y;
837
838 y = ((int) abs_y) - ListHeight - w->core.height - 1;
839 if ( y < 0 ) y = 0;
840 abs_y = (Position) y;
841 }
842 XtConfigureWidget(w->combobox.PopupShell,
843 abs_x, abs_y, ListWidth, ListHeight, 1);
844 /*
845 * So...das hier dient der Kosmetik: hier sorgen wir dafuer, dass die
846 * Liste auch wirklich immer direkt ueber der ComboBox innerhalb des
847 * Fensterstapels schwebt. Siehe dazu auch die Erlaeuterungen und An-
848 * merkungen in GetDecorationWindow().
849 */
850 if ( XtIsRealized((Widget) w) ) {
851 WindowChanges.sibling = GetDecorationWindow(w);
852 WindowChanges.stack_mode = Above;
853 XReconfigureWMWindow(XtDisplay((Widget) w),
854 XtWindow(w->combobox.PopupShell),
855 WidgetToScreen(w->combobox.PopupShell),
856 CWSibling | CWStackMode, &WindowChanges);
857 }
858 } /* DoDropDownLayout */
859
860 /* --------------------------------------------------------------------
861 * Naja... diese Routine scheint ja bereits zu einer Institution beim
862 * Schreiben von Composite-Widgets geworden zu sein.
863 *
864 * Hier beim ComboBox-Widget ist die Aufgabe ziemlich einfach: es
865 * genuegt, die Eingabezeile und den Pfeil-Button entsprechend inner-
866 * halb des ComboBox-Widgets zu plazieren. Seit allerdings noch das
867 * Textlabel hinzukommt, wird's langsam aufwendiger. Nun ja - da sich
868 * das Listenfeld wahlweise auch statisch einblenden laesst, ist nun
869 * noch mehr zu beruecksichtigen, wenn die Kinder-Widgets an ihre
870 * Plaetze geschoben werden.
871 */
872 static void DoLayout(XmComboBoxWidget w)
873 {
874 Dimension EditCtrlWidth, ArrowCtrlWidth, LabelCtrlWidth;
875 Dimension ComboBoxHeight;
876 Dimension BorderWidth;
877 Dimension HighlightThickness;
878 Position EditX;
879
880 XtVaGetValues(w->combobox.ArrowCtrl,
881 XmNheight, &ArrowCtrlWidth, NULL);
882 if ( !w->combobox.SquareArrow )
883 ArrowCtrlWidth = (ArrowCtrlWidth * 4) / 5;
884 XtVaGetValues(w->combobox.LabelCtrl,
885 XmNwidth, &LabelCtrlWidth, NULL);
886
887 /*
888 * In Abhaengigkeit davon, ob die ComboBox editierbar ist und ob das
889 * Listenfeld staendig sichtbar sein soll, hier die Breite einzelner
890 * Widgets bestimmen.
891 */
892 if ( w->combobox.StaticList ) {
893 ComboBoxHeight = w->combobox.EditCtrl->core.height;
894 EditCtrlWidth = w->core.width;
895 } else {
896 ComboBoxHeight = w->core.height;
897 EditCtrlWidth = w->core.width - ArrowCtrlWidth;
898 if ( w->combobox.Editable && w->combobox.ArrowSpacingOn )
899 EditCtrlWidth -= ArrowCtrlWidth/2;
900 }
901 /* Beruecksichtige noch ein evtl. ebenfalls anzuzeigendes Schriftfeld
902 * neben dem Eingabefeld.
903 */
904 if ( w->combobox.ShowLabel ) {
905 EditX = LabelCtrlWidth;
906 EditCtrlWidth -= LabelCtrlWidth;
907 } else
908 EditX = 0;
909 if ( EditCtrlWidth < 20 ) EditCtrlWidth = 20;
910 /* Plaziere nun das Eingabefeld... */
911 XtVaGetValues(w->combobox.EditCtrl,
912 XmNborderWidth, &BorderWidth,
913 XmNhighlightThickness, &HighlightThickness,
914 NULL);
915 XtConfigureWidget(w->combobox.EditCtrl,
916 EditX, 0,
917 EditCtrlWidth, ComboBoxHeight, BorderWidth);
918 /* ...und nun den Pfeil... */
919 XtVaGetValues(w->combobox.ArrowCtrl,
920 XtNborderWidth, &BorderWidth, NULL);
921 XtConfigureWidget(w->combobox.ArrowCtrl,
922 w->core.width-ArrowCtrlWidth, HighlightThickness,
923 ArrowCtrlWidth,
924 ComboBoxHeight - 2 * HighlightThickness,
925 BorderWidth);
926 /* ...und ggf. das Textlabel. */
927 if ( w->combobox.ShowLabel ) {
928 XtVaGetValues(w->combobox.LabelCtrl,
929 XmNborderWidth, &BorderWidth,
930 NULL);
931 XtConfigureWidget(w->combobox.LabelCtrl,
932 0, 0,
933 LabelCtrlWidth, ComboBoxHeight,
934 BorderWidth);
935 }
936 /* Falls da noch die Liste herumgurkt... */
937 if ( w->combobox.StaticList ) {
938 Dimension Width, Height;
939
940 if ( w->core.height > ComboBoxHeight )
941 Height = w->core.height - ComboBoxHeight;
942 else
943 Height = 10;
944
945 if ( w->core.width > (Dimension)(ArrowCtrlWidth + EditX) )
946 Width = w->core.width - ArrowCtrlWidth - EditX;
947 else
948 Width = 10;
949
950 XtConfigureWidget(XtParent(w->combobox.ListCtrl),
951 EditX + ArrowCtrlWidth, ComboBoxHeight, Width, Height, 0);
952 } else if ( w->combobox.ListVisible )
953 DoDropDownLayout(w);
954 } /* DoLayout */
955
956 /* --------------------------------------------------------------------
957 * Pappi fragt nach, wie gross wir denn sein wollen.
958 * Die hier benutzte Vorgehensweise zur Ermittlung der Groesse:
959 * Sobald der Vater uns eine Breite (oder aber Hoehe) vorschlaegt,
960 * die fuer uns eigentlich zu klein ist, meckern wir und schlagen
961 * die von uns benoetigte Breite (Hoehe) vor.
962 * Soweit also zur Theorie... leider sieht es beispielsweise das
963 * Motif Form-Widget ueberhaupt nicht ein, uns auch nur ein einziges
964 * Mal nach unseren Wuenschen zu fragen! Damit es bei derart unum-
965 * gaenglichen Widgets dann doch noch geht, muss ChangedManaged die
966 * Kohlen wieder aus dem Feuer holen mit einer Sondertour.
967 * Parameter:
968 * *Request Vom Vater vorgeschlagene Geometrie
969 * Ergebnis:
970 * *Reply Unsere Antwort auf die vorgeschlagene Geometrie
971 * sowie XtGeometryYes oder XtGeometryAlmost, je nachdem, wie gut
972 * uns Pappis Vorschlag in den Kram passt.
973 */
974 static XtGeometryResult QueryGeometry(XmComboBoxWidget w,
975 XtWidgetGeometry *Request,
976 XtWidgetGeometry *Reply)
977 {
978 XtGeometryResult result = XtGeometryYes;
979 Dimension minW, minH, editW, labelW;
980
981 /* Elternteil will nichts weiter aendern, also ist uns das
982 * recht so.
983 */
984 Request->request_mode &= CWWidth | CWHeight;
985 if ( Request->request_mode == 0 ) return result;
986
987 DefaultGeometry(w, &minW, &minH, &editW, &labelW);
988
989 /* Ueberpruefe, ob uns das in der Breite passt, was Pappi moechte... */
990 if ( Request->request_mode & CWWidth ) {
991 if ( Request->width < minW ) {
992 /* Wenn Pappi uns etwas vorschlaegt, was im wahrsten Sinne des Wortes
993 * vorn und hinten nicht reicht, dann versuchen wir ihn entsprechend
994 * zu korrigieren. ("Versuchen" deshalb, weil er diesen Vorschlag auch
995 * voellig ignorieren kann.)
996 */
997 result = XtGeometryAlmost;
998 Reply->width = minW;
999 Reply->request_mode |= CWWidth;
1000 }
1001 }
1002 /* Die ganze Chose nun noch vertikal */
1003 if ( Request->request_mode & CWHeight ) {
1004 if ( Request->height < minH ) {
1005 result = XtGeometryAlmost;
1006 Reply->height = minH;
1007 Reply->request_mode |= CWHeight;
1008 }
1009 }
1010 return result;
1011 } /* QueryGeometry */
1012
1013 /* --------------------------------------------------------------------
1014 * Die Groesse des ComboBox-Widgets hat sich veraendert und deshalb
1015 * mussen alle Kinder neu positioniert werden.
1016 * Letzten Endes laeuft hier alles auf ein ordinaeres DoLayout()
1017 * hinaus, um die Kinder umher zu schieben.
1018 * Parameter:
1019 * w Die bereits hinlaenglich bekannte Instanz dieses
1020 * Widgets
1021 */
1022 static void Resize(XmComboBoxWidget w)
1023 {
1024 DoLayout(w);
1025 } /* Resize */
1026
1027 /* --------------------------------------------------------------------
1028 * Dieses Widget hat sich in irgendeiner Form bewegt (und das nicht
1029 * nur relativ zum Vater, sondern moeglicherweise auch der Vater
1030 * selbst!) bzw. die Shell, in der sich irgendwo unsere Combo-Box
1031 * befindet, hat soeben den Fokus verschusselt und kann ihn nicht
1032 * mehr wiederfinden. Daneben kann es auch sein, dass die Shell
1033 * ikonisiert wurde. (Welch' Vielfalt! Dieses ist hier halt eine
1034 * multifunktionale Routine.)
1035 *
1036 * Parameter:
1037 * w Die naechste Shell in Reichweite ueber unserer
1038 * Combo-Box.
1039 * cbw Diese Combo-Box.
1040 * event ^ auf den Event, enthaelt genauerere Informationen
1041 * (naja... sieht so aus, als ob Motif hier auch
1042 * schon 'mal Schrott 'reinpackt...)
1043 * ContDispatch Auf True setzen, damit dieser Event noch weiter-
1044 * gereicht wird an all' die anderen, die auch noch
1045 * mithoeren.
1046 */
1047 static void ShellCallback(Widget w, XtPointer pClientData,
1048 XEvent *event, Boolean *ContDispatch)
1049 {
1050 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
1051
1052 switch ( event->type ) {
1053 case ConfigureNotify:
1054 case CirculateNotify:
1055 DoDropDownLayout((XmComboBoxWidget) cbw);
1056 break;
1057 case FocusOut:
1058 LOG3("ShellCallback: FocusOut, mode: %i, detail: %i\n",
1059 (int)event->xfocus.mode, (int)event->xfocus.detail);
1060 if ( cbw->combobox.Persistent )
1061 cbw->combobox.IgnoreFocusOut = True;
1062 else if ( (event->xfocus.mode == NotifyGrab) &&
1063 cbw->combobox.ListVisible )
1064 cbw->combobox.IgnoreFocusOut = True;
1065 break;
1066 case UnmapNotify:
1067 ShowHideDropDownList((XmComboBoxWidget) cbw,
1068 event, False);
1069 break;
1070 }
1071 *ContDispatch = True;
1072 } /* ShellCallback */
1073
1074 /* --------------------------------------------------------------------
1075 * Diese Routine sorgt dafuer, dass die Liste nicht irrtuemlich bei
1076 * manchen Window Managern vom Bildschirm genommen wird, bloss weil
1077 * diese der OverrideShell den Tastaturfocus schenken bzw. diesen
1078 * dem Combo-Box-Widget wegnehmen, sobald der Mauszeiger in die Liste
1079 * bewegt wird.
1080 */
1081 static void OverrideShellCallback(Widget w, XtPointer pClientData,
1082 XEvent *event, Boolean *ContDispatch)
1083
1084 {
1085 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
1086 switch ( event->type ) {
1087 case EnterNotify:
1088 LOG2("OverrideShellCallback: EnterNotify, PendingFO: %s\n",
1089 cbw->combobox.PendingFocusOut ? "True" : "False");
1090 if ( cbw->combobox.PendingFocusOut )
1091 cbw->combobox.IgnoreFocusOut = True;
1092 if ( cbw->combobox.TwmHandlingOn )
1093 cbw->combobox.PendingOverrideInOut = True;
1094 break;
1095 case LeaveNotify:
1096 LOG("OverrideShellCallback: LeaveNotify\n");
1097 if ( cbw->combobox.TwmHandlingOn )
1098 cbw->combobox.PendingOverrideInOut = True;
1099 break;
1100 }
1101 } /* OverrideShellCallback */
1102
1103 /* --------------------------------------------------------------------
1104 * Ha! Anscheinend kann man das Problem mit der einklappenden Liste,
1105 * sobald man den Arrow-Button anklickt, doch loesen! Allerdings geht
1106 * das auch nur von hinten durch die Brust in's Auge. Hier war die
1107 * Reihenfolge der Events bislang das Problem: Klickt man den Arrow-
1108 * Button an, so verliert das Eingabefeld den Focus, dann wird leider
1109 * schon die WorkProc aktiviert und laesst die Liste verschwinden.
1110 * Danach erst kommt der Arrow-Button-Callback an die Reihe. Um dieses
1111 * Dilemma doch noch zu loesen, wird hier darauf gelauert, wann und
1112 * welcher LeaveNotify kommt. Klickt der Benutzer den Pfeil an, so
1113 * kommt hier noch rechtzeitig ein LeaveNotify vorbei, der aber durch
1114 * einen Grab ausgeloest wurde. Und das ist eben nur beim Anklicken
1115 * der Fall. Damit wissen wir, das der FocusOut getrost ignoriert
1116 * werden darf.
1117 * Puhhh -- ist das ein kompliziertes Chaos.
1118 * Uebrigends...auch wenn manche Befehle zuerst ueberfluessig er-
1119 * scheinen...sie sind erforderlich, damit die ComboBox auch mit unter-
1120 * schiedlichen Window Managern zurechtkommt!
1121 */
1122 static void ArrowCrossingCallback(Widget w, XtPointer pClientData,
1123 XEvent *event, Boolean *ContDispatch)
1124
1125 {
1126 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
1127 switch ( event->type ) {
1128 case LeaveNotify:
1129 LOG2("ArrowCrossingCallback: LeaveNotify, mode: %i\n",
1130 event->xcrossing.mode);
1131 if ( event->xcrossing.mode == NotifyGrab )
1132 cbw->combobox.IgnoreFocusOut = True;
1133 else
1134 cbw->combobox.IgnoreFocusOut = False;
1135 break;
1136 }
1137 } /* ArrowCrossingCallback */
1138
1139 /* --------------------------------------------------------------------
1140 * Alle Hilfeaufrufe innerhalb der Kinder gehen an das eigentliche
1141 * Combo-Box-Widget weiter, so dass auch hier nach aussen hin die
1142 * Kinder-Widgets nicht in Erscheinung treten.
1143 */
1144 static void HelpCallback(Widget w, XtPointer cbw, XtPointer CallData)
1145 {
1146 XtCallCallbacks((Widget) cbw, XmNhelpCallback, CallData);
1147 } /* HelpCallback */
1148
1149 /* --------------------------------------------------------------------
1150 * Wenn der Benutzer im Eingabefeld osfActivate drueckt, dann dieses
1151 * Ereignis offiziell bekanntgeben.
1152 */
1153 static void ActivateCallback(Widget w, XtPointer cbw, XtPointer CallData)
1154 {
1155 XtCallCallbacks((Widget) cbw, XmNactivateCallback, CallData);
1156 } /* ActivateCallback */
1157
1158 /* --------------------------------------------------------------------
1159 * Ein Kind moechte sein Groesse veraendern und fragt deshalb hier bei
1160 * uns an.
1161 * Parameter:
1162 * w Naja...
1163 * *Request Vorschlag des Kindes
1164 * Ergebnis:
1165 * *Reply Unsere Antwort darauf
1166 * XtGeometryNo, da es uns bislang grundsaetzlich nie passt, es sei
1167 * denn, es ist das Label... Naja, jetzt darf auch schon einmal das
1168 * Listenfeld quengeln (aber nur, wenn es staendig sichtbar ist,
1169 * ansonsten wird es nicht beruecksichtigt!).
1170 */
1171 static XtGeometryResult GeometryManager(Widget w,
1172 XtWidgetGeometry *Request,
1173 XtWidgetGeometry *Reply)
1174 {
1175 XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
1176 XtGeometryResult Result = XtGeometryNo;
1177
1178 /*
1179 * Falls das Listenfeld statisch dargestellt wird, muessen wir seine
1180 * Wuensche doch beruecksichtigen. Was fuer ein Aufwand...
1181 */
1182 if ( (w == XtParent(cbw->combobox.ListCtrl)) && cbw->combobox.StaticList ) {
1183 Dimension TotalWidth, TotalHeight, EditWidth, LabelWidth;
1184 XtWidgetGeometry MyRequest, YourReply, EditGeom;
1185
1186 XtQueryGeometry(cbw->combobox.EditCtrl, NULL, &EditGeom);
1187 DefaultGeometry(cbw, &TotalWidth, &TotalHeight,
1188 &EditWidth, &LabelWidth);
1189 CheckDropDownOffset(cbw);
1190
1191 if ( Request->request_mode && CWWidth )
1192 if ( (Dimension)(LabelWidth + cbw->combobox.DropDownOffset +
1193 Request->width) > TotalWidth )
1194 TotalWidth = LabelWidth + cbw->combobox.DropDownOffset +
1195 Request->width;
1196
1197 if ( Request->request_mode && CWHeight )
1198 TotalHeight = EditGeom.height + Request->height;
1199 /*
1200 * Bastele nun eine Anfrage an Pappi zusammen und geh' ihm damit auf den
1201 * Keks. Wenn er zustimmt, ist sofort alles gut, wir muessen dann nur
1202 * noch das Layout aufpolieren, damit das Listenfeld die neue Groesse
1203 * bekommt. Wenn Pappi nur halb zustimmt, akzeptieren wir das und fragen
1204 * ihn damit noch einmal....
1205 */
1206 MyRequest.request_mode = CWWidth | CWHeight;
1207 MyRequest.width = TotalWidth;
1208 MyRequest.height = TotalHeight;
1209 Result = XtMakeGeometryRequest((Widget) cbw, &MyRequest, &YourReply);
1210 if ( Result == XtGeometryAlmost ) {
1211 MyRequest.width = YourReply.width;
1212 MyRequest.height = YourReply.height;
1213 Result = XtMakeGeometryRequest((Widget) cbw, &MyRequest, &YourReply);
1214 }
1215 if ( Result == XtGeometryYes )
1216 DoLayout(cbw);
1217 } else
1218 /*
1219 * Ansonsten darf nur noch das Schriftfeld Ansprueche anmelden.
1220 */
1221 if ( w != cbw->combobox.LabelCtrl )
1222 return XtGeometryNo; /* Was ICH hier vorgegeben habe, gilt! */
1223 else if ( cbw->combobox.ShowLabel ) { /* Naja, 'mal schauen! */
1224 Dimension TotalWidth, TotalHeight, EditWidth, LabelWidth;
1225 XtWidgetGeometry MyRequest;
1226
1227 if ( Request->request_mode & CWWidth ) {
1228 DefaultGeometry(cbw, &TotalWidth, &TotalHeight,
1229 &EditWidth, &LabelWidth);
1230 TotalWidth = TotalWidth - LabelWidth +
1231 Request->width;
1232
1233 MyRequest.request_mode = CWWidth;
1234 MyRequest.width = TotalWidth;
1235 Result = XtMakeGeometryRequest((Widget) cbw, &MyRequest, NULL);
1236
1237 if ( Result == XtGeometryYes )
1238 DoLayout(cbw);
1239 }
1240 }
1241 return Result;
1242 } /* GeometryManager */
1243
1244 /* --------------------------------------------------------------------
1245 * Hier werden auf Wunsch diejenigen Farben, die bei der Combo-Box neu
1246 * gesetzt wurden, an alle Kinder weitergegeben.
1247 */
1248 #define BOTTOMSHADOWCOLOR 0x0001
1249 #define TOPSHADOWCOLOR 0x0002
1250 #define FOREGROUND 0x0004
1251 #define BACKGROUND 0x0008
1252
1253 static struct { String Resource; int Flag; }
1254 ColorResources[] = {
1255 { XmNbottomShadowColor, BOTTOMSHADOWCOLOR },
1256 { XmNtopShadowColor, TOPSHADOWCOLOR },
1257 { XmNforeground, FOREGROUND },
1258 { XmNbackground, BACKGROUND }
1259 };
1260
1261 static UpdateColors(XmComboBoxWidget w, int flags)
1262 {
1263 Pixel Color;
1264 int i, size = XtNumber(ColorResources);
1265 Widget ScrolledWin, ScrollBar;
1266
1267 ScrolledWin = XtParent(w->combobox.ListCtrl);
1268 XtVaGetValues(ScrolledWin, XmNverticalScrollBar, &ScrollBar, NULL);
1269 for ( i=0; i<size; i++ )
1270 if ( flags & ColorResources[i].Flag ) {
1271 XtVaGetValues((Widget) w, ColorResources[i].Resource, &Color,
1272 NULL);
1273 XtVaSetValues(w->combobox.ListCtrl,
1274 ColorResources[i].Resource, Color, NULL);
1275 XtVaSetValues(ScrolledWin,
1276 ColorResources[i].Resource, Color, NULL);
1277 XtVaSetValues(ScrollBar,
1278 ColorResources[i].Resource, Color, NULL);
1279 XtVaSetValues(w->combobox.EditCtrl,
1280 ColorResources[i].Resource, Color, NULL);
1281 XtVaSetValues(w->combobox.LabelCtrl,
1282 ColorResources[i].Resource, Color, NULL);
1283 XtVaSetValues(w->combobox.ArrowCtrl,
1284 ColorResources[i].Resource, Color, NULL);
1285 if ( ColorResources[i].Flag & BACKGROUND )
1286 XtVaSetValues(ScrollBar, XmNtroughColor, Color, NULL);
1287 }
1288
1289 return 1;
1290 } /* UpdateColors */
1291
1292 /* --------------------------------------------------------------------
1293 * Liste aller vorgespiegelten Resourcen, die automatisch verarbeitet
1294 * werden koennen, ohne weiter darueber nachdenken zu muessen...
1295 */
1296 typedef enum { EDITCTRL, LISTCTRL, LABELCTRL } CHILDCTRL;
1297 typedef enum { RO, RW, RWS, RWL, RWI, RWIGNORE } aUniqueName;
1298 typedef struct {
1299 String rsc;
1300 CHILDCTRL ctrl;
1301 /* enum { RO, RW, RWS, RWL, RWI, RWIGNORE } dir; */
1302 aUniqueName dir;
1303 /* nur lesen, lesen&schreiben, lesen&schreiben spezial,
1304 lesen&schreiben label, lesen&schreiben items */
1305 } MIRROR;
1306
1307 /* Alle mit !!! gekennzeichneten Eintraege werden auf die richtigen
1308 * Namen des entsprechenden Widgets umgesetzt.
1309 */
1310 static MIRROR MirroredResources[] = {
1311 { XmNitems, LISTCTRL, RWI }, /* Urgs! */
1312 { XmNitemCount, LISTCTRL, RWIGNORE }, /* dto. */
1313 { XmNlistMarginHeight, LISTCTRL, RW },
1314 { XmNlistMarginWidth, LISTCTRL, RW },
1315 { XmNlistSpacing, LISTCTRL, RW },
1316 { XmNstringDirection, LISTCTRL, RO }, /* Naja? */
1317 { XmNtopItemPosition, LISTCTRL, RO },
1318
1319 { XmNblinkRate, EDITCTRL, RW },
1320 { XmNcolumns, EDITCTRL, RW },
1321 { XmNcursorPosition, EDITCTRL, RW },
1322 { XmNcursorPositionVisible, EDITCTRL, RW },
1323 { XmNmarginHeight, EDITCTRL, RW },
1324 { XmNmarginWidth, EDITCTRL, RW },
1325 { XmNmaxLength, EDITCTRL, RW },
1326 { XmNselectThreshold, EDITCTRL, RW },
1327 { XmNvalue, EDITCTRL, RWS },
1328
1329 { XmNalignment, LABELCTRL, RW },
1330 { XmNmnemonic, LABELCTRL, RW },
1331 { XmNmnemonicCharSet, LABELCTRL, RW },
1332 { XmNlabelPixmap, LABELCTRL, RW },
1333 { XmNlabelInsensitivePixmap, LABELCTRL, RW },
1334 { XmNlabelString, LABELCTRL, RW },
1335 { XmNlabelType, LABELCTRL, RW },
1336 { XmNlabelMarginBottom, LABELCTRL, RWL }, /* !!! */
1337 { XmNlabelMarginHeight, LABELCTRL, RWL }, /* !!! */
1338 { XmNlabelMarginLeft, LABELCTRL, RWL }, /* !!! */
1339 { XmNlabelMarginRight, LABELCTRL, RWL }, /* !!! */
1340 { XmNlabelMarginTop, LABELCTRL, RWL }, /* !!! */
1341 { XmNlabelMarginWidth, LABELCTRL, RWL }, /* !!! */
1342 { XmNlabelFontList, LABELCTRL, RWL }, /* !!! */
1343 };
1344
1345 typedef struct {
1346 char *from, *to;
1347 } TRANSFORMATION;
1348 static TRANSFORMATION Transformations[] = {
1349 { XmNlabelMarginBottom, XmNmarginBottom },
1350 { XmNlabelMarginHeight, XmNmarginHeight },
1351 { XmNlabelMarginLeft, XmNmarginLeft },
1352 { XmNlabelMarginRight, XmNmarginRight },
1353 { XmNlabelMarginTop, XmNmarginTop },
1354 { XmNlabelMarginWidth, XmNmarginWidth },
1355 { XmNlabelFontList, XmNfontList },
1356 };
1357
1358 /* --------------------------------------------------------------------
1359 * Sobald irgendeine Resource veraendert wird, erfolgt der Aufruf
1360 * hierin als Benachrichtigung, einmal nach dem rechten zu sehen.
1361 * Parameter:
1362 * current Kopie der Widget-Instanz, bevor irgendwelche
1363 * Resourcen veraendert oder set_values()-Methoden
1364 * aufgerufen wurden.
1365 * req Kopie der Widget-Instanz, aber bereits mit den
1366 * durch XtSetValues veraenderten Werten
1367 * new aktuellster Zustand der Widget-Instanz mit
1368 * veraenderten Werten (entweder durch XtSetValues
1369 * oder set_values()-Methoden der Superklasse)
1370 * args Argumentenliste beim Aufruf von XtSetValues()
1371 * NumArgs Anzahl der Argumente in der Liste
1372 * Ergebnis:
1373 * True, falls Widget neu gezeichnet werden soll.
1374 */
1375 static Boolean SetValues(XmComboBoxWidget current, XmComboBoxWidget req,
1376 XmComboBoxWidget newW,
1377 ArgList args, Cardinal *NumArgs)
1378 {
1379 Boolean Update = False;
1380 int i, j, MirrorSize = XtNumber(MirroredResources);
1381 int k, TransformationSize = XtNumber(Transformations);
1382 Arg arg;
1383 int Flags;
1384
1385 /*
1386 * Alle Resourcen, die nicht mehr nach dem Erstellen der Widget-Instanz
1387 * veraendert werden koennen.
1388 */
1389 newW->combobox.Editable = current->combobox.Editable;
1390 newW->combobox.ListCtrl = current->combobox.ListCtrl;
1391 newW->combobox.EditCtrl = current->combobox.EditCtrl;
1392 newW->combobox.LabelCtrl = current->combobox.LabelCtrl;
1393 newW->combobox.SelectionPolicy = current->combobox.SelectionPolicy;
1394 newW->combobox.ListSizePolicy = current->combobox.ListSizePolicy;
1395 newW->combobox.StaticList = current->combobox.StaticList;
1396
1397 /*
1398 * Kontrolliere nun alle Resourcen, die sich veraendert haben koennten
1399 * und gebe die neuen Einstellungen entsprechend weiter...
1400 *
1401 * Hat sich der Sensitive-Zustand veraendert? Dann muessen wir hier dafuer
1402 * sorgen, dass alle Kinder ebenfalls den neuen Zustand annehmen.
1403 */
1404 if ( current->core.sensitive != newW->core.sensitive ) {
1405 XtSetSensitive(newW->combobox.ListCtrl, newW->core.sensitive);
1406 XtSetSensitive(newW->combobox.EditCtrl, newW->core.sensitive);
1407 XtSetSensitive(newW->combobox.ArrowCtrl, newW->core.sensitive);
1408 XtSetSensitive(newW->combobox.ListCtrl, newW->core.sensitive);
1409 if ( !newW->core.sensitive )
1410 ShowHideDropDownList(newW, NULL, False);
1411 }
1412 /*
1413 * Die ScrollBarPolicy kann nur dann geaendert werden, wenn das Listenfeld
1414 * dauerhaft dargestellt wird.
1415 */
1416 if ( newW->combobox.ScrollBarDisplayPolicy !=
1417 current->combobox.ScrollBarDisplayPolicy )
1418 if ( newW->combobox.StaticList )
1419 XtVaSetValues(newW->combobox.ListCtrl,
1420 XmNscrollBarDisplayPolicy, newW->combobox.ScrollBarDisplayPolicy,
1421 NULL);
1422 else
1423 XtWarning(
1424 "XmComboBox: ScrollBarDisplayPolicy can not be changed when StaticList == False."
1425 );
1426 /* Anzahl der in der Liste gleichzeitig darstellbaren Eintraege */
1427 if ( current->combobox.VisibleItemCount !=
1428 newW->combobox.VisibleItemCount ) {
1429 XtVaSetValues(newW->combobox.ListCtrl,
1430 XmNvisibleItemCount, newW->combobox.VisibleItemCount,
1431 NULL);
1432 Update = True;
1433 }
1434 if ( current->combobox.AutomaticSelection !=
1435 newW->combobox.AutomaticSelection )
1436 XtVaSetValues(newW->combobox.ListCtrl,
1437 XmNautomaticSelection, newW->combobox.AutomaticSelection,
1438 NULL);
1439
1440 /*
1441 * benutzter Font: hier erhalten Liste und Eingabefeld jeweils die
1442 * gleiche Fontliste, wohingegen das Label getrennt behandelt wird.
1443 * Das macht auch Sinn, denn Liste und Eingabefeld beinhalten gleich-
1444 * artigen Text, so dass hier auch tunlichst der gleiche Font zu
1445 * benutzen ist.
1446 */
1447 if ( current->combobox.Font != newW->combobox.Font ) {
1448 XtVaSetValues(newW->combobox.ListCtrl,
1449 XmNfontList, newW->combobox.Font, NULL);
1450 XtVaSetValues(newW->combobox.EditCtrl,
1451 XmNfontList, newW->combobox.Font, NULL);
1452 Update = True;
1453 }
1454
1455 Flags = 0;
1456 if ( newW->manager.top_shadow_color !=
1457 current->manager.top_shadow_color ) Flags |= TOPSHADOWCOLOR;
1458 if ( newW->manager.bottom_shadow_color !=
1459 current->manager.bottom_shadow_color ) Flags |= BOTTOMSHADOWCOLOR;
1460 if ( newW->manager.foreground !=
1461 current->manager.foreground ) Flags |= FOREGROUND;
1462 if ( newW->core.background_pixel !=
1463 current->core.background_pixel ) Flags |= BACKGROUND;
1464 if ( Flags ) { UpdateColors(newW, Flags); Update = True; }
1465
1466
1467 if ( newW->combobox.ArrowCursor != current->combobox.ArrowCursor ) {
1468 if ( newW->combobox.ListVisible )
1469 XDefineCursor(XtDisplay(newW->combobox.PopupShell),
1470 XtWindow(newW->combobox.PopupShell),
1471 newW->combobox.ArrowCursor);
1472 }
1473 /* Hier werden die vorgespiegelten Resourcen verwaltet, die in
1474 * Wirklichkeit zu einem unserer Kinder gehoeren.
1475 */
1476 for ( i = 0; i < *NumArgs; i++ ) {
1477 /* Ist es eine vorgespiegelte Resource ? Wenn ja, dann leite die
1478 * Anfrage an das entsprechende Kind-Widget weiter.
1479 */
1480 for ( j = 0; j < MirrorSize; j++ ) {
1481 if ( (strcmp(args[i].name, MirroredResources[j].rsc) == 0) ) {
1482 switch ( MirroredResources[j].dir ) {
1483 case RW: /* schreibender Zugriff erlaubt */
1484 XtSetValues(MirroredResources[j].ctrl == LISTCTRL ?
1485 newW->combobox.ListCtrl :
1486 (MirroredResources[j].ctrl == EDITCTRL ?
1487 newW->combobox.EditCtrl :
1488 newW->combobox.LabelCtrl),
1489 &(args[i]), 1);
1490 break;
1491 case RWS: /* schreibender Zugriff unter Kontrolle */
1492 if ( strcmp(args[i].name, XmNvalue) == 0 ) {
1493 if ( newW->combobox.Editable )
1494 XtSetValues(newW->combobox.EditCtrl,
1495 &(args[i]), 1);
1496 }
1497 break;
1498 case RWL: /* Transformation in andere Resource beim
1499 Label-Widget */
1500 for ( k = 0; k < TransformationSize; k++ )
1501 if ( strcmp(args[i].name, Transformations[k].from) == 0 ) {
1502 arg.value = args[i].value;
1503 arg.name = Transformations[k].to;
1504 XtSetValues(newW->combobox.LabelCtrl,
1505 &arg, 1);
1506 break;
1507 }
1508 break;
1509 case RWIGNORE: /* Zugriff auf XmNitemCount */
1510 /* Wird von XmNitems erledigt! */
1511 break;
1512 case RWI: /* Zugriff auf XmNitems */
1513 for ( k = 0; k < *NumArgs; k++ )
1514 if ( strcmp(args[k].name, XmNitemCount) == 0 ) {
1515 Arg MyArgs[2];
1516
1517 MyArgs[0].name = XmNitems;
1518 MyArgs[0].value = args[i].value;
1519 MyArgs[1].name = XmNitemCount;
1520 MyArgs[1].value = args[k].value;
1521 XtSetValues(newW->combobox.ListCtrl,
1522 args, 2);
1523 /*XtVaSetValues(newW->combobox.ListCtrl,
1524 XmNitems, args[i].value,
1525 XmNitemCount, args[k].value,
1526 NULL);*/
1527 break;
1528 }
1529 break;
1530 case RO:
1531 break;
1532 } /* case write mode */
1533 goto ScanForNextResource;
1534 } /* if entry found */
1535 } /* for every mirrored entry */
1536 ScanForNextResource: ;
1537 } /* for every Arg */
1538
1539 if ( (newW->combobox.SquareArrow != current->combobox.SquareArrow) ||
1540 (newW->combobox.ArrowSpacingOn != current->combobox.ArrowSpacingOn) ) {
1541 Update = False;
1542 DoLayout(newW);
1543 }
1544
1545 return Update;
1546 } /* SetValues */
1547
1548 /* --------------------------------------------------------------------
1549 * Werden irgendwelche Resourcen abgefragt, so muessen wir hier erst
1550 * noch vor der Rueckkehr zum Frager klaeren, ob davon eine Resource
1551 * betroffen ist, die nur vorgespiegelt ist, da sie eigentlich einem
1552 * der Widgets gehoert, die von uns hier verwaltet werden, um daraus
1553 * eine ordentliche Combo-Box zu machen.
1554 * Parameter:
1555 * w Widget-Instanz
1556 * args Abgefragte Resourcen
1557 * NumArgs Anzahl der abgefragten Resourcen
1558 */
1559 static void GetValuesAlmost(XmComboBoxWidget w, ArgList args,
1560 Cardinal *NumArgs)
1561 {
1562 int i, j, MirrorSize = XtNumber(MirroredResources);
1563 int k, TransformationSize = XtNumber(Transformations);
1564 Arg arg;
1565
1566 for ( i = 0; i < *NumArgs; i++ ) {
1567 /* Ist es eine vorgespiegelte Resource ? Wenn ja, dann leite die
1568 * Anfrage an das entsprechende Kind-Widget weiter.
1569 */
1570 for ( j = 0; j < MirrorSize; j++ ) {
1571 if ( strcmp(args[i].name, MirroredResources[j].rsc) == 0 ) {
1572 switch ( MirroredResources[j].dir ) {
1573 case RO:
1574 case RW:
1575 case RWS:
1576 case RWI:
1577 XtGetValues(MirroredResources[j].ctrl == LISTCTRL ?
1578 w->combobox.ListCtrl :
1579 MirroredResources[j].ctrl == EDITCTRL ?
1580 w->combobox.EditCtrl :
1581 w->combobox.LabelCtrl,
1582 &(args[i]), 1);
1583 break;
1584 case RWL: /* Umzuleitende Resource bei Label-Widget */
1585 for ( k = 0; k < TransformationSize; k++ )
1586 if ( strcmp(args[i].name, Transformations[k].from) == 0 ) {
1587 arg.value = args[i].value;
1588 arg.name = Transformations[k].to;
1589 XtGetValues(w->combobox.LabelCtrl,
1590 (ArgList) &arg, 1);
1591 break;
1592 }
1593 break;
1594 } /* case read mode */
1595 } /* if entry found */
1596 } /* for every mirrored entry */
1597 } /* for every Arg */
1598 } /* GetValuesAlmost */
1599
1600 /* --------------------------------------------------------------------
1601 * Zeige beziehungsweise verstecke die Drop-Down-Liste der Combo-Box.
1602 * Falls die Liste bereits den entsprechenden Zustand hat, geht's
1603 * sofort zum Aufrufer zurueck.
1604 * Parameter:
1605 * w Her Royal Majesty ComboBox
1606 * Show True, falls anzuzeigen, andernfalls False
1607 */
1608 static void ShowHideDropDownList(XmComboBoxWidget w, XEvent *event,
1609 Boolean Show)
1610 {
1611 XmComboBoxDropDownCallbackStruct info;
1612
1613 if ( w->combobox.StaticList ||
1614 (Show == w->combobox.ListVisible) ) return;
1615 w->combobox.ListVisible = Show;
1616 if ( Show ) { /* Klapp' die Liste aus! */
1617 DoDropDownLayout(w);
1618 info.reason = XmCR_SHOW_LIST;
1619 info.event = event;
1620 XtCallCallbacks((Widget) w, XmNdropDownCallback,
1621 (XtPointer) &info);
1622 XDefineCursor(XtDisplay(w->combobox.PopupShell),
1623 XtWindow(w->combobox.PopupShell),
1624 w->combobox.ArrowCursor);
1625 XtPopup(w->combobox.PopupShell, XtGrabNone);
1626 XtVaSetValues(w->combobox.ArrowCtrl,
1627 XmNarrowDirection, XmARROW_UP, NULL);
1628 } else { /* Klapp' die Liste wieder ein... */
1629 XtPopdown(w->combobox.PopupShell);
1630 XtVaSetValues(w->combobox.ArrowCtrl,
1631 XmNarrowDirection, XmARROW_DOWN, NULL);
1632 info.reason = XmCR_HIDE_LIST;
1633 info.event = event;
1634 XtCallCallbacks((Widget) w, XmNdropDownCallback,
1635 (XtPointer) &info);
1636 }
1637 } /* ShowHideDropDownList */
1638
1639 /* --------------------------------------------------------------------
1640 * Hier laeuft die Nachricht auf, dass der Pfeil ausgeloest wurde...
1641 * (Daraufhin sollte die Liste aus- oder eingeklappt werden)
1642 * ...oder dass der Benutzer da draussen auf der anderen Seite der
1643 * Mattscheibe den Pfeil bereits anklickte ohne aber bereits losge-
1644 * gelassen zu haben. Bereits hier bekommt das Eingabefeld den Fokus
1645 * vor den Latz geknallt, denn sonst kann es passieren, dass zwar die
1646 * Liste ausgeklappt ist, aber das Eingabefeld noch keinen Tastatur-
1647 * fokus erhalten hat. Das sollte aber nicht so sein, denn es ist dann
1648 * keine konsequente Tastaturbedienung.
1649 */
1650 static void ArrowCallback(Widget w, XtPointer pClientData,
1651 XmAnyCallbackStruct *info)
1652 {
1653 XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
1654
1655 switch ( info->reason ) {
1656 case XmCR_ARM:
1657 LOG("ArrowCallback: XmCR_ARM\n");
1658 XmProcessTraversal(cbw->combobox.EditCtrl, XmTRAVERSE_CURRENT);
1659 if ( cbw->combobox.TwmHandlingOn && cbw->combobox.ListVisible )
1660 cbw->combobox.IgnoreFocusOut = True;
1661 break;
1662 case XmCR_ACTIVATE:
1663 XmProcessTraversal(cbw->combobox.EditCtrl, XmTRAVERSE_CURRENT);
1664 ShowHideDropDownList(cbw, info->event,
1665 (Boolean)(!cbw->combobox.ListVisible));
1666 break;
1667 }
1668 } /* ArrowCallback */
1669
1670 /* --------------------------------------------------------------------
1671 * Diese Benachrichtigung moechte uns nur mitteilen, dass wir soeben
1672 * den Fokus verloren haben (Ohhhh!) Sollte allerdings der Fokus nur
1673 * aus dem Grunde perdue sein, dass der Anwender den Mauszeiger ausser-
1674 * halb des Applikationsfensters plaziert hat, so koennen wir diese
1675 * Nachricht uebergehen. Erst wenn der Fokus an ein anderes Widget in
1676 * unserer Applikation verlorenging, muessen wir auf diese Information
1677 * reagieren.
1678 * Und jetzt zu noch einem total beknackten Problem - alles nur wegen
1679 * Motif und den diversen Window-Managern (bspw. olwm)... Leider kommt
1680 * beim FocusOut kein richtiger Hinweis auf den tatsaechlichen Event,
1681 * der dieses Callback ausloeste -- warum liefert denn dann Motif ueber-
1682 * haupt noch den Event???? Und ueberhauupt, die Geschichte mit dem
1683 * Fokus ist schon der reinste Horror. Aktueller Ausweg: wenn wir die
1684 * Benachrichtigung ueber den Focusabgang bekommen, registrieren wir
1685 * eine Work-Prozedur, die, sobald der Rechner wieder Luft hat, auf-
1686 * gerufen wird. Sie kann dann nachschauen, ob nicht inzwischen die
1687 * OverrideShell den Focus bekahm. Wenn ja, koennen wir den FocusOut
1688 * uebergehen, ansonsten muessen wir ihn beruecksichtigen.
1689 * -- Ist das eine ^@#$^*(#$^&! (Meine gute Erziehung hindert mich
1690 * daran, diesen Begriff hier zu nennen.)
1691 */
1692 static Boolean DelayedFocusOutWorkProc(XtPointer pClientData)
1693 {
1694 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
1695 LOG2("DelayedFocusOutWorkProc: IgnoreFocusOut: %s\n",
1696 cbw->combobox.IgnoreFocusOut ? "True" : "False");
1697 if ( !cbw->combobox.IgnoreFocusOut )
1698 ShowHideDropDownList(cbw, &(cbw->combobox.xevent), False);
1699 cbw->combobox.IgnoreFocusOut = False;
1700 cbw->combobox.PendingFocusOut = False;
1701 return True; /* diese Routine wird nicht mehr benoetigt. */
1702 } /* DelayedFocusOutWorkProc */
1703
1704 static void EditFocusCallback(Widget w, XtPointer pClientData,
1705 XmAnyCallbackStruct *info)
1706 {
1707 XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
1708
1709 if ( cbw->combobox.StaticList ) return;
1710
1711 if ( info->reason == XmCR_LOSING_FOCUS ) {
1712 LOG2("EditFocusCallback: PendingFocusOut: %s, ",
1713 cbw->combobox.PendingFocusOut ? "True" : "False");
1714 LOG3("mode: %i, detail: %i, ", (int)info->event->xcrossing.mode,
1715 (int)info->event->xcrossing.detail);
1716 LOG2("PendingOverrideInOut: %s\n",
1717 cbw->combobox.PendingOverrideInOut ? "True" : "False");
1718 if ( !cbw->combobox.PendingFocusOut &&
1719 !cbw->combobox.PendingOverrideInOut ) {
1720 /* Normalerweise duerfen aber keine NULL-Events hier
1721 * vorbeikommen...aber man weiss ja nie so genau und
1722 * sicher ist sicher. Defensiv programmieren!
1723 */
1724 if ( info->event )
1725 cbw->combobox.xevent = *info->event;
1726 cbw->combobox.WorkProcID = XtAppAddWorkProc(
1727 XtWidgetToApplicationContext((Widget) cbw),
1728 (XtWorkProc) DelayedFocusOutWorkProc,
1729 (XtPointer) cbw);
1730 cbw->combobox.PendingFocusOut = True;
1731 }
1732 cbw->combobox.PendingOverrideInOut = False;
1733 }
1734 } /* EditFocusCallback */
1735
1736 /* --------------------------------------------------------------------
1737 * Hier wird der angegebene Eintrag in der Listbox der Combo-Box
1738 * markiert und zudem in den sichtbaren Bereich gerollt, sollte er
1739 * sich ausserhalb des dargestellten Bereichs der Liste befinden.
1740 * Parameter:
1741 * w Die Combo-Box (ueblicher Parameter)
1742 * Index Index des neu zu markierenden Eintrages.
1743 * Notify Schickt Mitteilung via Callback
1744 * Ergebnis:
1745 * Index des markierten Eintrages oder 0, falls die Listbox leer
1746 * war und deshalb auch kein Eintrag markiert werden konnte.
1747 */
1748 static int SetSelectionPos(XmComboBoxWidget w, int Index, Boolean Notify)
1749 {
1750 Widget ListBox = w->combobox.ListCtrl;
1751 int ItemCount; /* Anzahl Eintraege in Listbox */
1752 int TopItem, VisibleItems;
1753
1754 XtVaGetValues(ListBox, XmNitemCount, &ItemCount,
1755 XmNtopItemPosition, &TopItem,
1756 XmNvisibleItemCount, &VisibleItems,
1757 NULL);
1758 if ( Index < 1 ) Index = 1;
1759 if ( Index > ItemCount ) Index = ItemCount;
1760 if ( Index != 0 && ItemCount != 0 ) {
1761 if ( Index < TopItem )
1762 XmListSetPos(ListBox, Index);
1763 if ( Index >= TopItem + VisibleItems )
1764 XmListSetBottomPos(ListBox, Index);
1765 XmListSelectPos(ListBox, Index, Notify);
1766 return Index;
1767 } else
1768 return 0;
1769 } /* SetSelectionPos */
1770
1771 /* --------------------------------------------------------------------
1772 * Diese Routine kuemmert sich darum, denjenigen Eintrag aus der List-
1773 * box mit der angegebenen Nummer herauszufischen und an die Eingabe-
1774 * zeile zu uebergeben. Dabei wird der Index auf den Eintrag auto-
1775 * matisch auf den zulaessigen Bereich begrenzt. Zugleich wird auch
1776 * noch der angegebene Eintrag in der Listbox markiert.
1777 */
1778 static void TransferToEditCtrl(XmComboBoxWidget w, int SelectionIndex,
1779 Boolean MayWipeOut)
1780 {
1781 Widget ListBox = w->combobox.ListCtrl;
1782 XmStringTable Items;
1783 char *pItemText;
1784
1785 XtVaGetValues(ListBox, XmNitems, &Items, NULL);
1786
1787 if ( MayWipeOut &&
1788 (SelectionIndex == w->combobox.LastSelection) &&
1789 (w->combobox.SelectionPolicy == XmSINGLE_SELECT) ) {
1790 SelectionIndex = 0;
1791 }
1792
1793 if ( (SelectionIndex == 0) &&
1794 (w->combobox.SelectionPolicy == XmSINGLE_SELECT) ) {
1795 XmListDeselectAllItems(w->combobox.ListCtrl);
1796 w->combobox.PassVerification = True;
1797 XmTextFieldSetString(w->combobox.EditCtrl, "");
1798 } else {
1799 SelectionIndex = SetSelectionPos(w, SelectionIndex, False);
1800 if ( SelectionIndex > 0 ) {
1801 XmStringGetLtoR(Items[SelectionIndex-1],
1802 XmSTRING_DEFAULT_CHARSET, &pItemText);
1803 w->combobox.PassVerification = True;
1804 XmTextFieldSetString(w->combobox.EditCtrl, pItemText);
1805 XtFree(pItemText);
1806 }
1807 }
1808 w->combobox.LastSelection = SelectionIndex;
1809 } /* TransferToEditCtrl */
1810
1811 /* --------------------------------------------------------------------
1812 * Alle registrierten Callbacks bei Anwahl eines neuen Eintrages in
1813 * der Listbox aktivieren.
1814 */
1815 static void CallSelectionCBL(XmComboBoxWidget w, XEvent *Event)
1816 {
1817 int index;
1818
1819 index = XmComboBoxGetSelectedPos((Widget) w);
1820 /*
1821 * Wenn momentan KEIN Eintrag selektiert ist, dann rufe den neuen
1822 * XmNunselectionCallback auf!
1823 */
1824 if ( index == 0 ) {
1825 XmComboBoxUnselectionCallbackStruct info;
1826
1827 info.reason = XmCR_UNSELECT;
1828 info.event = Event;
1829 XtCallCallbacks((Widget) w, XmNunselectionCallback, (XtPointer) &info);
1830 } else {
1831 /*
1832 * Ansonsten den ueblichen SelectionCallback!
1833 */
1834 XmComboBoxSelectionCallbackStruct info;
1835 XmStringTable Items;
1836
1837 info.reason = w->combobox.SelectionPolicy == XmSINGLE_SELECT ?
1838 XmCR_SINGLE_SELECT : XmCR_BROWSE_SELECT;
1839 info.event = Event;
1840 info.index = index;
1841 XtVaGetValues(w->combobox.ListCtrl, XmNitems, &Items, NULL);
1842 info.value = Items[info.index-1];
1843 XtCallCallbacks((Widget) w, XmNselectionCallback, (XtPointer) &info);
1844 }
1845 } /* CallSelectionCBL */
1846
1847 /* --------------------------------------------------------------------
1848 * Hier laeuft das Tastatur-Management fuer die ComboBox zusammen.
1849 * ACHTUNG: Der 'w'-Parameter wird nur benoetigt, um das eigentliche
1850 * ComboBox-Widget zu ermitteln. Er muss daher die ID eines direkten
1851 * Kinds der ComboBox enthalten!
1852 */
1853 static void CBoxManager(Widget w, XEvent *Event, String *params,
1854 Cardinal *num_params)
1855 {
1856 XmComboBoxWidget cbw;
1857 Widget ListBox;
1858 int *SelectionList;
1859 int SelectionCount;
1860 int SelectionIndex; /* Wer denn nun markiert wird... */
1861 int ItemCount; /* Anzahl Eintraege in Listbox */
1862 int VisibleItems; /* Hoehe der Liste in Eintraegen */
1863 char opt;
1864
1865 /*
1866 * Nur wenn eine der Translationen page-up und page-down direkt im
1867 * Listenfeld ausgeloest wurden, wird auch als "w" die Liste ueber-
1868 * geben. Bei allen anderen Faellen ist dieses zumeist das TextField.
1869 */
1870 if ( XtClass(w) == xmListWidgetClass )
1871 cbw = (XmComboBoxWidget) XtParent(XtParent(w));
1872 else
1873 cbw = (XmComboBoxWidget) XtParent(w);
1874 ListBox = cbw->combobox.ListCtrl;
1875
1876 switch ( *(params[0]) ) {
1877 /* --------------------------------------------------------------------
1878 * Klappe die Liste auf Wunsch des Benutzers aus oder wieder ein.
1879 */
1880 case 's': /* show-hide-list */
1881 ShowHideDropDownList(cbw, Event,
1882 (Boolean)(!cbw->combobox.ListVisible));
1883 break;
1884 case 'h': /* hide-list */
1885 ShowHideDropDownList(cbw, Event, False);
1886 break;
1887 /* --------------------------------------------------------------------
1888 * Hier werden die Bewegungen in der Listbox behandelt.
1889 */
1890 case 'u': /* up */
1891 case 'd': /* down */
1892 case 't': /* top */
1893 case 'b': /* bottom */
1894 case 'p': /* page-up/page-down */
1895 opt = *(params[0]);
1896 XtVaGetValues(ListBox, XmNitemCount, &ItemCount,
1897 XmNvisibleItemCount, &VisibleItems, NULL);
1898 if ( XmListGetSelectedPos(ListBox,
1899 &SelectionList, &SelectionCount) ) {
1900 SelectionIndex = *SelectionList;
1901 XtFree((char *)SelectionList);
1902 switch ( opt ) {
1903 case 'u': SelectionIndex--; break;
1904 case 'd': SelectionIndex++; break;
1905 case 't': SelectionIndex = 1; break;
1906 case 'b': SelectionIndex = ItemCount; break;
1907 case 'p': if ( *(params[0]+5) == 'u' )
1908 SelectionIndex -= VisibleItems;
1909 else
1910 SelectionIndex += VisibleItems;
1911 break;
1912 }
1913 } else { /* momentan noch kein Eintrag in der Liste ausgewaehlt */
1914 if ( opt == 'b' ) SelectionIndex = ItemCount;
1915 else SelectionIndex = 1; /* nun ersten Eintrag nehmen */
1916 }
1917 TransferToEditCtrl(cbw, SelectionIndex, False);
1918 CallSelectionCBL(cbw, Event);
1919 break;
1920 /* --------------------------------------------------------------------
1921 * Der Benutzer hat die Eingabetaste gedrueckt oder einen Eintrag in
1922 * der Listbox angeklickt.
1923 */
1924 case 'a': /* Return = activate */
1925 case 'S': /* Selection */
1926 if ( !cbw->combobox.StaticList && !cbw->combobox.ListVisible ) break;
1927 XtVaGetValues(ListBox, XmNitemCount, &ItemCount, NULL);
1928 if ( ItemCount == 0 ) break;
1929 if ( XmListGetSelectedPos(ListBox,
1930 &SelectionList, &SelectionCount) ) {
1931 SelectionIndex = *SelectionList;
1932 XtFree((char *)SelectionList);
1933 } else {
1934 if ( cbw->combobox.SelectionPolicy != XmSINGLE_SELECT )
1935 SelectionIndex = 1;
1936 else
1937 SelectionIndex = 0;
1938 }
1939
1940 TransferToEditCtrl(cbw, SelectionIndex,
1941 *(params[0]) == 'S');
1942 CallSelectionCBL(cbw, Event);
1943 ShowHideDropDownList(cbw, Event, (Boolean)
1944 (*(params[0]) == 'S' ? True : False));
1945 break;
1946 /* --------------------------------------------------------------------
1947 * Der Benutzer hat die ESC-Taste gedrueckt. Ist die Liste zu diesem
1948 * Zeitpunkt noch ausgeklappt, so wird sie einfach nur eingeklappt und
1949 * weiter passiert nichts. Ist die Liste jedoch eingeklappt, so wird
1950 * das ESC an die normale Action-Routine des Eingabefeldes weiter-
1951 * gegeben, damit damit bspw. Dialog u.a. abgebrochen werden koennen.
1952 */
1953 case 'c': /* Cancel */
1954 if ( cbw->combobox.ListVisible )
1955 ShowHideDropDownList(cbw, Event, False);
1956 else
1957 XtCallActionProc(cbw->combobox.EditCtrl,
1958 "process-cancel", Event, NULL, 0);
1959 break;
1960 /* --------------------------------------------------------------------
1961 * Wenn es erlaubt ist, dass auch einmal kein Eintrag in einer ComboBox
1962 * mit nicht editierbarem Eingabefeld ausgewaehlt ist, dann darf der
1963 * Anwender mittels osfDelete den aktuellen Eintrag deselektieren.
1964 */
1965 case 'w': /* wipe */
1966 if ( cbw->combobox.SelectionPolicy == XmSINGLE_SELECT ) {
1967 TransferToEditCtrl(cbw, 0, True);
1968 CallSelectionCBL(cbw, Event);
1969 }
1970 break;
1971 /* --------------------------------------------------------------------
1972 * Dummy-Operation
1973 */
1974 case 'n': /* no-operation */
1975 break;
1976 }
1977 } /* CBoxManager */
1978
1979 /* --------------------------------------------------------------------
1980 * Der Benutzer hat einen Eintrag in der Listbox angeklickt. Der Ein-
1981 * fachkeit halber wird einfach nur ein Druecken der Eingabetaste
1982 * simuliert.
1983 */
1984 static void ListSelectionCallback(Widget w, XtPointer pClientData,
1985 XmAnyCallbackStruct *info)
1986 {
1987 String paramsMouse[1] = { "a" }, paramsKeyboard[1] = { "S" };
1988 Cardinal NumParams = 1;
1989 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
1990 /*
1991 * Wurde der Event durch die Tastatur oder einen Mausklick
1992 * ausgeloest? Wenn es ein Mausklick auf das Listenfeld war und es
1993 * sich um ein staendig angezeigtes Listenfeld einer nicht editierbaren
1994 * ComboBox handelt, dann gib' dem Eingabefeld den Tastaturfokus.
1995 */
1996 if ( info->event == NULL )
1997 CBoxManager(cbw->combobox.EditCtrl, info->event,
1998 paramsKeyboard, &NumParams);
1999 else {
2000 CBoxManager(cbw->combobox.EditCtrl, info->event,
2001 paramsMouse, &NumParams);
2002 if ( !cbw->combobox.StaticList ||
2003 (cbw->combobox.StaticList && !cbw->combobox.Editable) )
2004 XmProcessTraversal(cbw->combobox.EditCtrl,
2005 XmTRAVERSE_CURRENT);
2006 }
2007 } /* ListSelectionCallback */
2008
2009 /* --------------------------------------------------------------------
2010 * Nach einem Doppelklick innerhalb des Listenfelds wird diese Routine
2011 * aufgerufen. Zunaechst einmal wird ganz normal wie bei einem ein-
2012 * fachen Anklicken vorgegangen, danach aber noch der ein spezieller
2013 * Callback aufgerufen.
2014 */
2015 static void ListDefaultActionCallback(Widget w, XtPointer pClientData,
2016 XmAnyCallbackStruct *OldInfo)
2017 {
2018 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
2019 XmComboBoxDefaultActionCallbackStruct info;
2020 XmStringTable Items;
2021
2022 ListSelectionCallback(w, pClientData, OldInfo);
2023 info.reason = XmCR_DEFAULT_ACTION;
2024 info.event = OldInfo->event;
2025 info.index = XmComboBoxGetSelectedPos((Widget) cbw);
2026 XtVaGetValues(cbw->combobox.ListCtrl, XmNitems, &Items, NULL);
2027 info.value = Items[info.index-1];
2028 XtCallCallbacks((Widget) cbw, XmNdefaultActionCallback, (XtPointer) &info);
2029 } /* ListDefaultActionCallback */
2030
2031
2032 /* --------------------------------------------------------------------
2033 * Ohweh!! Diese Routine wurde erforderlich, um XmNautomaticSelection
2034 * zu unterstuetzen. Denn wenn der Benutzer in der Liste herumsucht und
2035 * automaticSelection 'True' ist, kommt kein Callback-Aufruf mehr, wenn
2036 * die Maustaste losgelassen wird. Und damit wuessten wir sonst nicht,
2037 * wann die Liste einzuklappen ist! Irgendwie wird das alles mit der
2038 * Zeit immer konfuser und aufwendiger. Wenn das Chaos gequantelt
2039 * sein sollte, dann muss das Chaos-Quant (sog. 'Chaotonen') aber jede
2040 * Menge Chaos transportieren!!!
2041 */
2042 static void Button1UpInList(Widget w, XtPointer pClientData,
2043 XEvent *Event, Boolean *ContDispatch)
2044 {
2045 XmComboBoxWidget cbw = (XmComboBoxWidget) pClientData;
2046
2047 if ( Event->xbutton.button == Button1 ) {
2048 if ( cbw->combobox.AutomaticSelection )
2049 ShowHideDropDownList(cbw, Event, False);
2050 }
2051 } /* Button1UpInList */
2052
2053 /* --------------------------------------------------------------------
2054 * Sobald sich irgendetwas im Eingabefeld veraenderte, kommt das
2055 * TextField-Widget zuerst zu uns gelaufen, um sich unser Okay zu
2056 * holen. Bei einer nicht editierbaren Combo-Box wird hierueber die
2057 * Schnellsuche realisiert.
2058 */
2059 static void EditVerifyCallback(Widget w, XtPointer pClientData,
2060 XmTextVerifyCallbackStruct *info)
2061 {
2062 XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
2063
2064 /*
2065 * Sollte gerade dem Eingabefeld Text aus der Listbox einverleibt
2066 * werden, so duerfen wir hier darueber natuerlich nicht meckern,
2067 * sondern unser <<ok>> dazu geben. (D.h. in diesem Fall haben wir
2068 * kein Recht, zu intervenieren.)
2069 */
2070 if ( cbw->combobox.PassVerification ) {
2071 cbw->combobox.PassVerification = False;
2072 info->doit = True;
2073 return;
2074 }
2075 /*
2076 * Ist es eine Combo-Box, in die kein Text vom Benutzer eingegeben
2077 * werden kann, so wird bei der Eingabe von Zeichen die Schnellsuche
2078 * ausgeloest.
2079 */
2080 if ( !cbw->combobox.Editable ) {
2081 Widget ListBox = cbw->combobox.ListCtrl;
2082 char WarpCharLow, WarpCharHigh;
2083 XmString Item;
2084 XmStringTable Items;
2085 int *SelectionList;
2086 int SelectionCount;
2087 int i, ItemCount, Start, End;
2088 char *pItem;
2089 Boolean Ignore;
2090
2091 info->doit = False;
2092 if ( (info->text == NULL ) ||
2093 (info->text->length == 0 ) ) return; /* Hoppala! */
2094 /*
2095 * Nun aus dem Zeichen einen String (Motif-like) basteln und
2096 * in der Listbox danach auf die Suche gehen.
2097 */
2098 if ( info->text->length > 1 ) {
2099 /* Das ist nun endweder ein normaler Paste, oder aber
2100 * das Ergebnis einer Drag'n'Drop-Operation.
2101 */
2102 Item = XmStringCreateSimple(info->text->ptr);
2103 XmComboBoxSelectItem((Widget) cbw, Item, True);
2104 XmStringFree(Item);
2105 } else {
2106 /* Ansonsten soll nur eine Schnellsuche ausgefuehrt
2107 * werden, der entsprechende Buchstabe ist das einzige
2108 * Zeichen im dem Callback uebergebenen Text.
2109 */
2110 WarpCharLow = tolower(*(info->text->ptr));
2111 WarpCharHigh = toupper(WarpCharLow);
2112
2113 XtVaGetValues(ListBox, XmNitemCount, &ItemCount,
2114 XmNitems, &Items,
2115 NULL);
2116 if ( ItemCount < 1 ) return;
2117 /* Ermittele, wo's los geht mit der Suche... */
2118 if ( XmListGetSelectedPos(ListBox,
2119 &SelectionList, &SelectionCount) ) {
2120 Start = *SelectionList; i = Start + 1;
2121 XtFree((char *)SelectionList);
2122 } else i = Start = 1;
2123
2124 if ( i > ItemCount ) i = 1;
2125 Ignore = True;
2126 while ( i != Start || Ignore ) {
2127 Ignore = False;
2128 XmStringGetLtoR(Items[i-1], XmSTRING_DEFAULT_CHARSET,
2129 &pItem);
2130 if ( (strchr(pItem, WarpCharLow ) == pItem) ||
2131 (strchr(pItem, WarpCharHigh) == pItem) ) {
2132 XtFree(pItem);
2133 TransferToEditCtrl(cbw, i, False);
2134 CallSelectionCBL(cbw, info->event);
2135 break;
2136 }
2137 XtFree(pItem);
2138 if ( ++i > ItemCount ) i = 1;
2139 }
2140 }
2141 } else {
2142 /*
2143 * Wenn das Eingabefeld editierbar ist, dann fragen wir ueber die Callbacks
2144 * nach, ob es genehm ist, den neuen Text einzufuegen.
2145 */
2146 XtCallCallbacks((Widget) cbw, XmNmodifyVerifyCallback,
2147 (XtPointer) info);
2148 }
2149 } /* EditVerifyCallback */
2150
2151 /* --------------------------------------------------------------------
2152 * Dieser Callback wird immer dann aufgerufen, wenn in einer ComboBox
2153 * mit einem veraenderlichem Eingabefeld der Eingabetext veraendert
2154 * wurde. In diesem Fall suchen wir hier nach einem passenden gleich-
2155 * lautenden Eintrag. Wenn wir einen finden, heben wir ihn in der Liste
2156 * sogleich hervor, ansonsten ist kein Eintrag hervorgehoben.
2157 */
2158 static void EditChangedCallback(Widget w, XtPointer pClientDate,
2159 XmAnyCallbackStruct *info)
2160 {
2161 XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
2162 XmStringTable Items;
2163 int ItemCount, i;
2164 XmString EditStr;
2165 String EditLine;
2166
2167 /*
2168 * Zuerst nach einem passenden Eintrag zum Eingabefeld in der Liste
2169 * suchen...
2170 */
2171 XtVaGetValues(cbw->combobox.EditCtrl, XmNvalue, &EditLine, NULL);
2172 XtVaGetValues(cbw->combobox.ListCtrl,
2173 XmNitemCount, &ItemCount,
2174 XmNitems, &Items,
2175 NULL);
2176 EditStr = XmStringCreateSimple(EditLine);
2177 XtVaSetValues(cbw->combobox.ListCtrl, XmNselectedItemCount, 0, NULL);
2178 if ( ItemCount < 1 ) return;
2179 for ( i = 0; i < ItemCount; i++ )
2180 if ( XmStringCompare(Items[i], EditStr) ) {
2181 SetSelectionPos(cbw, i+1, False);
2182 break;
2183 }
2184 XmStringFree(EditStr);
2185 /*
2186 * Zum Abschluss noch den Callback aufrufen...
2187 */
2188 XtCallCallbacks((Widget) cbw, XmNvalueChangedCallback, (XtPointer) info);
2189 } /* EditChangedCallback */
2190
2191 /* --------------------------------------------------------------------
2192 * Dieser Callback wird immer dann aufgerufen, wenn in einer ComboBox
2193 * mit einem veraenderlichem Eingabefeld der Cursor bewegt wurde.
2194 * Dieser Callback ist nur fuer echte Fans von Callbacks da...
2195 */
2196 static void MotionVerifyCallback(Widget w, XtPointer pClientDate,
2197 XmTextVerifyCallbackStruct *info)
2198 {
2199 XmComboBoxWidget cbw = (XmComboBoxWidget) XtParent(w);
2200
2201 XtCallCallbacks((Widget) cbw, XmNmotionVerifyCallback, (XtPointer) info);
2202 } /* MotionVerifyCallback */
2203
2204 /* --------------------------------------------------------------------
2205 * Bastele einen vollstaendigen Namens- und Klassenbezeichner anhand
2206 * des angegebenen Widgets zusammen.
2207 */
2208 static void MakeNameAndClass(Widget w, char *NameBuff, char *ClassBuff)
2209 {
2210 Widget Parent = XtParent(w);
2211
2212 if ( Parent ) MakeNameAndClass(Parent, NameBuff, ClassBuff);
2213 if ( XtIsSubclass(w, applicationShellWidgetClass) ) {
2214 /* Wenn wir ganz oben angekommen sind, holen wir uns den
2215 * Namen und die Klasse der Applikation selbst und nicht die
2216 * des Widgets.
2217 */
2218 String AppName, AppClass;
2219 XtGetApplicationNameAndClass(
2220 XtDisplayOfObject(w), &AppName, &AppClass);
2221 strcpy(NameBuff, AppName);
2222 strcpy(ClassBuff, AppClass);
2223 } else {
2224 /* Ansonsten sind wir noch mitten irgendwo in der Hierarchie
2225 * und besorgen uns den Namen und die Klasse dieses Widgets
2226 */
2227 strcat(NameBuff, ".");
2228 strcat(NameBuff, XtName(w));
2229 strcat(ClassBuff, ".");
2230 strcat(ClassBuff, ((CoreClassRec *) XtClass(w))->core_class.class_name);
2231 }
2232 } /* MakeNameAndClass */
2233
2234 /* --------------------------------------------------------------------
2235 * Eine einzelne Resource aus der Datenbank herausholen. Diese Resource
2236 * kommt im Allgemeinen immer als String zurueck und muss daher erst
2237 * noch in das gewuenschte Zielformat konvertiert werden.
2238 */
2239 static Boolean FetchResource(Widget w,
2240 char *FullName, char *FullClass,
2241 char *RscName, char *RscClass,
2242 XrmValue *RscValue,
2243 String *RepresentationType)
2244 {
2245 Boolean ok;
2246 char *EndOfName = FullName + strlen(FullName);
2247 char *EndOfClass = FullClass + strlen(FullClass);
2248
2249 strcat(FullName, "."); strcat(FullName, RscName);
2250 strcat(FullClass, "."); strcat(FullClass, RscClass);
2251 ok = XrmGetResource(
2252 XtDatabase(XtDisplayOfObject(w)),
2253 FullName, FullClass, RepresentationType, RscValue);
2254 /* Wieder den alten Namens- und Klassenrumpf herstellen */
2255 *EndOfName = 0; *EndOfClass = 0;
2256 return ok;
2257 } /* FetchResource */
2258
2259 /* --------------------------------------------------------------------
2260 * Nun folgen diejenigen Routinen, mit denen die Konvertierung in das
2261 * gewuenschte Zielformat einer Resource moeglich ist.
2262 * Verfuegbar:
2263 * String --> Int
2264 * String --> Short
2265 * String XmPIXMAP / XmSTRING --> unsigned char
2266 * String --> Dimension
2267 * String --> XmString
2268 * String --> XmStringTable
2269 * String --> XmFontList
2270 * String --> Pixmap (genauer: Bitmap)
2271 * String --> String
2272 * String --> KeySym
2273 */
2274 static Boolean FetchIntResource(Widget w,
2275 char *FullName, char *FullClass,
2276 char *RscName, char *RscClass,
2277 int *pInt)
2278 {
2279 XrmValue RscValue, RscDest;
2280 String RepresentationType;
2281
2282 if ( FetchResource(w, FullName, FullClass,
2283 RscName, RscClass,
2284 &RscValue, &RepresentationType) ) {
2285 RscDest.size = sizeof(int);
2286 RscDest.addr = (caddr_t) pInt;
2287 if ( XtConvertAndStore(w, RepresentationType, &RscValue,
2288 XtRInt, &RscDest) )
2289 return True;
2290 }
2291 return False;
2292 } /* FetchIntResource */
2293
2294 static Boolean FetchShortResource(Widget w,
2295 char *FullName, char *FullClass,
2296 char *RscName, char *RscClass,
2297 short *pShort)
2298 {
2299 XrmValue RscValue, RscDest;
2300 String RepresentationType;
2301
2302 if ( FetchResource(w, FullName, FullClass,
2303 RscName, RscClass,
2304 &RscValue, &RepresentationType) ) {
2305 RscDest.size = sizeof(short);
2306 RscDest.addr = (caddr_t) pShort;
2307 if ( XtConvertAndStore(w, RepresentationType, &RscValue,
2308 XtRShort, &RscDest) )
2309 return True;
2310 }
2311 return False;
2312 } /* FetchShortResource */
2313
2314 static Boolean FetchLabelTypeResource(Widget w,
2315 char *FullName, char *FullClass,
2316 char *RscName, char *RscClass,
2317 unsigned char *pUChar)
2318 {
2319 XrmValue RscValue, RscDest;
2320 String RepresentationType;
2321 int AInt;
2322
2323 if ( FetchResource(w, FullName, FullClass,
2324 RscName, RscClass,
2325 &RscValue, &RepresentationType) ) {
2326 if ( strcasecmp((char *) RscValue.addr, "XmPIXMAP") == 0 )
2327 *pUChar = XmPIXMAP;
2328 else
2329 *pUChar = XmSTRING;
2330 return True;
2331 }
2332 return False;
2333 } /* FetchLabelTypeResource */
2334
2335 static Boolean FetchDimensionResource(Widget w,
2336 char *FullName, char *FullClass,
2337 char *RscName, char *RscClass,
2338 Dimension *pDimension)
2339 {
2340 XrmValue RscValue, RscDest;
2341 String RepresentationType;
2342
2343 if ( FetchResource(w, FullName, FullClass,
2344 RscName, RscClass,
2345 &RscValue, &RepresentationType) ) {
2346 RscDest.size = sizeof(Dimension);
2347 RscDest.addr = (caddr_t) pDimension;
2348 if ( XtConvertAndStore(w, RepresentationType, &RscValue,
2349 XtRDimension, &RscDest) )
2350 return True;
2351 }
2352 return False;
2353 } /* FetchDimensionResource */
2354
2355 static Boolean FetchStringResource(Widget w,
2356 char *FullName, char *FullClass,
2357 char *RscName, char *RscClass,
2358 String *pString)
2359 {
2360 XrmValue RscValue;
2361 String RepresentationType;
2362
2363 if ( FetchResource(w, FullName, FullClass,
2364 RscName, RscClass,
2365 &RscValue, &RepresentationType) ) {
2366 *pString = (char *) RscValue.addr;
2367 return True;
2368 }
2369 return False;
2370 } /* FetchStringResource */
2371
2372 static Boolean FetchKeySymResource(Widget w,
2373 char *FullName, char *FullClass,
2374 char *RscName, char *RscClass,
2375 KeySym *pKeySym)
2376 {
2377 XrmValue RscValue, RscDest;
2378 String RepresentationType;
2379
2380 if ( FetchResource(w, FullName, FullClass,
2381 RscName, RscClass,
2382 &RscValue, &RepresentationType) ) {
2383 RscDest.size = sizeof(KeySym);
2384 RscDest.addr = (caddr_t) pKeySym;
2385 if ( XtConvertAndStore(w, RepresentationType, &RscValue,
2386 XmRKeySym, &RscDest) )
2387 return True;
2388 }
2389 return False;
2390 } /* FetchKeySymResource */
2391
2392 static Boolean FetchXmStringResource(Widget w,
2393 char *FullName, char *FullClass,
2394 char *RscName, char *RscClass,
2395 XmString *pString)
2396 {
2397 XrmValue RscValue;
2398 String RepresentationType;
2399
2400 if ( FetchResource(w, FullName, FullClass,
2401 RscName, RscClass,
2402 &RscValue, &RepresentationType) ) {
2403 *pString = XmCvtCTToXmString((char *) RscValue.addr);
2404 return True;
2405 }
2406 return False;
2407 } /* FetchXmStringResource */
2408
2409 static Boolean FetchXmStringTableResource(Widget w,
2410 char *FullName, char *FullClass,
2411 char *RscName, char *RscClass,
2412 XmStringTable *pStringTable,
2413 int *pTableSize)
2414 {
2415 XrmValue RscValue;
2416 String RepresentationType;
2417 String TmpList, p, pStart;
2418 int Entries, Entry;
2419
2420 if ( FetchResource(w, FullName, FullClass,
2421 RscName, RscClass,
2422 &RscValue, &RepresentationType) ) {
2423 /*
2424 * Zuerst eine Kopie erzeugen und dann daraus die Liste
2425 * zusammenbasteln.
2426 */
2427 TmpList = XtNewString((String)RscValue.addr);
2428 if ( TmpList == NULL ) return False;
2429 if ( *TmpList == 0 ) { XtFree(TmpList); return False; }
2430 /* Ermittele, wieviele Eintrage in der Liste sind und
2431 * erstelle dann daraus die Liste.
2432 */
2433 Entries = 1; p = TmpList;
2434 while ( *p )
2435 if ( *p++ == ',' ) ++Entries;
2436 *pStringTable = (XmStringTable)
2437 XtMalloc(Entries * sizeof(XmString));
2438
2439 p = TmpList;
2440 for ( Entry = 0; Entry < Entries; ++Entry ) {
2441 pStart = p;
2442 while ( (*p != 0) && (*p != ',') ) ++p;
2443 *p++ = 0;
2444 (*pStringTable)[Entry] = (XmString)
2445 XmStringCreateSimple(pStart);
2446 }
2447 /* Hier geht ausnahmsweise einmal Rueckgabe vor
2448 * Entschaedigung... hey, das war doch nur ein
2449 * (wenn auch ziemlich miserabler) Scherz
2450 */
2451 XtFree(TmpList);
2452 *pTableSize = Entries;
2453 return True;
2454 }
2455 return False;
2456 } /* FetchXmStringTableResource */
2457
2458 static Boolean FetchXmFontListResource(Widget w,
2459 char *FullName, char *FullClass,
2460 char *RscName, char *RscClass,
2461 XmFontList *pFontList)
2462 {
2463 XrmValue RscValue, RscDest;
2464 String RepresentationType;
2465
2466 if ( FetchResource(w, FullName, FullClass,
2467 RscName, RscClass,
2468 &RscValue, &RepresentationType) ) {
2469 RscDest.size = sizeof(XmFontList);
2470 RscDest.addr = (caddr_t) pFontList;
2471 if ( XtConvertAndStore(w, RepresentationType, &RscValue,
2472 XmRFontList, &RscDest) )
2473 return True;
2474 }
2475 return False;
2476 } /* FetchXmFontListResource */
2477
2478 static Boolean FetchPixmapResource(Widget w,
2479 char *FullName, char *FullClass,
2480 char *RscName, char *RscClass,
2481 Pixmap *pPixmap)
2482 {
2483 XrmValue RscValue, RscDest;
2484 String RepresentationType;
2485
2486 if ( FetchResource(w, FullName, FullClass,
2487 RscName, RscClass,
2488 &RscValue, &RepresentationType) ) {
2489 RscDest.size = sizeof(Pixmap);
2490 RscDest.addr = (caddr_t) pPixmap;
2491 if ( XtConvertAndStore(w, RepresentationType, &RscValue,
2492 XtRBitmap, &RscDest) )
2493 return True;
2494 }
2495 return False;
2496 } /* FetchPixmapResource */
2497
2498 /* --------------------------------------------------------------------
2499 * Waehrend der Initialisierung alle gespiegelten Resourcen, fuer die
2500 * Eintraege in der Resourcen-Datenbank existieren an die passenden
2501 * Kinder-Widgets weiterleiten. Der Trick an der Sache: wir setzen
2502 * die betroffenen Resourcen vie XtSetValues mit uns selbst als Ziel.
2503 * Dadurch bekommt SetValues die Arbeit aufgehalst, die Resourcen den
2504 * richtigen Kindern zuzuordnen...
2505 */
2506
2507 #define RInt 0
2508 #define RShort 1
2509 #define RLType 2
2510 #define RDimension 3
2511 #define RXmString 4
2512 #define RPixmap 5
2513 #define RXmFontList 6
2514 #define RKeySym 7
2515 #define RString 8
2516 #define RXmStringTable 9
2517 #define RXmItemCount 10
2518
2519
2520 typedef struct
2521 {
2522 String Name, Class;
2523
2524 int Converter;
2525
2526 } RESOURCEMIRROR;
2527
2528 static RESOURCEMIRROR ResourceMirror[] = {
2529 { XmNblinkRate, XmCBlinkRate, RInt, },
2530 { XmNcolumns, XmCColumns, RShort, },
2531 { XmNmaxLength, XmCMaxLength, RInt, },
2532 { XmNmarginHeight, XmCMarginHeight, RDimension },
2533 { XmNmarginWidth, XmCMarginWidth, RDimension },
2534 { XmNselectThreshold, XmCSelectThreshold, RInt },
2535
2536 { XmNlistMarginHeight, XmCListMarginHeight, RDimension },
2537 { XmNlistMarginWidth, XmCListMarginWidth, RDimension },
2538 { XmNlistSpacing, XmCListSpacing, RDimension },
2539 { XmNitems, XmCItems, RXmStringTable },
2540 { XmNitemCount, XmCItemCount, RXmItemCount },
2541
2542 { XmNmnemonic, XmCMnemonic, RKeySym },
2543 { XmNmnemonicCharSet, XmCMnemonicCharSet, RString },
2544 { XmNlabelString, XmCLabelString, RXmString },
2545 { XmNlabelMarginBottom, XmCLabelMarginBottom, RDimension },
2546 { XmNlabelMarginHeight, XmCLabelMarginHeight, RDimension },
2547 { XmNlabelMarginLeft, XmCLabelMarginLeft, RDimension },
2548 { XmNlabelMarginRight, XmCLabelMarginRight, RDimension },
2549 { XmNlabelMarginTop, XmCLabelMarginTop, RDimension },
2550 { XmNlabelMarginWidth, XmCLabelMarginWidth, RDimension },
2551 { XmNlabelPixmap, XmCLabelPixmap, RPixmap },
2552 { XmNlabelInsensitivePixmap, XmCLabelInsensitivePixmap, RPixmap },
2553 { XmNlabelType, XmCLabelType, RLType },
2554 { XmNlabelFontList, XmCLabelFontList, RXmFontList },
2555 };
2556
2557 static void InitMirrorResources(XmComboBoxWidget w)
2558 {
2559 char FullName[1024], FullClass[1024];
2560 int AInt, TableSize;
2561 short AShort;
2562 unsigned char AUChar;
2563 Dimension ADimension;
2564 XmString AXmString;
2565 XmStringTable AStringTable;
2566 Pixmap APixmap;
2567 XmFontList AFontList;
2568 XrmValue RscValue;
2569 String AString;
2570 KeySym AKeySym;
2571 int i, size = XtNumber(ResourceMirror);
2572
2573 FullName[0] = 0; FullClass[0] = 0;
2574 MakeNameAndClass((Widget) w, FullName, FullClass);
2575
2576 for ( i=0; i < size; i++ ) {
2577 switch ( ResourceMirror[i].Converter ) {
2578 case RInt:
2579 if ( FetchIntResource((Widget) w,
2580 FullName, FullClass,
2581 ResourceMirror[i].Name, ResourceMirror[i].Class,
2582 &AInt) )
2583 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2584 AInt, NULL);
2585 break;
2586 case RXmItemCount:
2587 if ( FetchIntResource((Widget) w,
2588 FullName, FullClass,
2589 ResourceMirror[i].Name, ResourceMirror[i].Class,
2590 &AInt) && ( AInt != 0) )
2591 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2592 AInt, NULL);
2593 break;
2594 case RShort:
2595 if ( FetchShortResource((Widget) w,
2596 FullName, FullClass,
2597 ResourceMirror[i].Name, ResourceMirror[i].Class,
2598 &AShort) )
2599 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2600 AShort, NULL);
2601 break;
2602 case RLType:
2603 if ( FetchLabelTypeResource((Widget) w,
2604 FullName, FullClass,
2605 ResourceMirror[i].Name, ResourceMirror[i].Class,
2606 &AUChar) )
2607 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2608 AUChar, NULL);
2609 break;
2610 case RDimension:
2611 if ( FetchDimensionResource((Widget) w,
2612 FullName, FullClass,
2613 ResourceMirror[i].Name, ResourceMirror[i].Class,
2614 &ADimension) )
2615 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2616 ADimension, NULL);
2617 break;
2618 case RXmString:
2619 if ( FetchXmStringResource((Widget) w,
2620 FullName, FullClass,
2621 ResourceMirror[i].Name, ResourceMirror[i].Class,
2622 &AXmString) )
2623 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2624 AXmString, NULL);
2625 break;
2626 case RXmStringTable:
2627 if ( FetchXmStringTableResource((Widget) w,
2628 FullName, FullClass,
2629 ResourceMirror[i].Name, ResourceMirror[i].Class,
2630 &AStringTable, &TableSize) ) {
2631 XtVaSetValues((Widget) w,
2632 XmNitems, (XtPointer) AStringTable,
2633 XmNitemCount, TableSize, NULL);
2634 }
2635 break;
2636 case RKeySym:
2637 if ( FetchKeySymResource((Widget) w,
2638 FullName, FullClass,
2639 ResourceMirror[i].Name, ResourceMirror[i].Class,
2640 &AKeySym) )
2641 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2642 AKeySym, NULL);
2643 break;
2644 case RString:
2645 if ( FetchStringResource((Widget) w,
2646 FullName, FullClass,
2647 ResourceMirror[i].Name, ResourceMirror[i].Class,
2648 &AString) )
2649 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2650 AString, NULL);
2651 break;
2652 case RPixmap:
2653 if ( FetchPixmapResource((Widget) w,
2654 FullName, FullClass,
2655 ResourceMirror[i].Name, ResourceMirror[i].Class,
2656 &APixmap) ) {
2657 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2658 APixmap, NULL);
2659 if ( strcmp(ResourceMirror[i].Name, XmNlabelPixmap) == 0 )
2660 w->combobox.ConvertBitmapToPixmap = True;
2661 else
2662 w->combobox.ConvertBitmapToPixmapInsensitive = True;
2663 }
2664 break;
2665 case RXmFontList:
2666 if ( FetchXmFontListResource((Widget) w,
2667 FullName, FullClass,
2668 ResourceMirror[i].Name, ResourceMirror[i].Class,
2669 &AFontList) )
2670 XtVaSetValues((Widget) w, ResourceMirror[i].Name,
2671 AFontList, NULL);
2672 break;
2673 }
2674 }
2675 } /* InitMirrorResources */
2676
2677 /* --------------------------------------------------------------------
2678 * Wandelt ein 1-Bit tiefes Bitmap in ein n-Bit tiefes Pixmap um, dass
2679 * die gleiche Tiefe besitzt, wie der Bildschirm, auf dem das Pixmap
2680 * spaeter erscheinen soll.
2681 */
2682 static Pixmap BitmapToPixmap(XmComboBoxWidget w,
2683 String Resource, GC ColorGC)
2684 {
2685 Pixmap LabelPixmap, LabelBitmap;
2686 Display *display = XtDisplay(w);
2687 Window root;
2688 int PixX, PixY;
2689 unsigned int PixW, PixH, PixBW, PixDepth;
2690
2691 XtVaGetValues(w->combobox.LabelCtrl, Resource, &LabelBitmap, NULL);
2692 XGetGeometry(display, LabelBitmap, &root,
2693 &PixX, &PixY, &PixW, &PixH, &PixBW, &PixDepth);
2694 LabelPixmap = XCreatePixmap(
2695 display, RootWindowOfScreen(XtScreen(w)),
2696 PixW, PixH,
2697 (w->combobox.LabelCtrl)->core.depth);
2698 XCopyPlane(display, LabelBitmap, LabelPixmap,
2699 ColorGC, 0, 0, PixW, PixH, 0, 0, 1);
2700 XtVaSetValues(w->combobox.LabelCtrl, Resource, LabelPixmap, NULL);
2701 XFreePixmap(display, LabelBitmap);
2702 return LabelPixmap;
2703 } /* BitmapToPixmap */
2704
2705 /* --------------------------------------------------------------------
2706 * Alles initialisieren, sobald das Widget eingerichtet wird. Das sagt
2707 * sich hier so einfach, ist es aber *definitiv* nicht!!!!
2708 */
2709 static void Initialize(Widget request, XmComboBoxWidget newW,
2710 ArgList wargs, Cardinal *ArgCount)
2711 {
2712 Dimension width, height, dummy;
2713 Widget w;
2714 Arg args[10];
2715 int n = 0;
2716 XmString xmstr;
2717 Pixel BackgroundColor;
2718
2719 /*
2720 * Da zu allem Ueberfluss die einzelnen Instanzen einer XmComboBox
2721 * auf verschiedenen Displays auftauchen koennen, wird hier:
2722 * 1. pro Widget ein eigener Cursor erzeugt (benoetigt fuer die Liste)
2723 * 2. pro Widget (hier = pro Applikation) die benoetigte Action-Routine
2724 * registiert. Doppelte Registrierung macht dem Toolkit nichts aus, da es
2725 * dann eine evtl. aeltere Definition loescht.
2726 */
2727 XtAppAddActions(XtWidgetToApplicationContext((Widget) newW),
2728 actions, XtNumber(actions));
2729
2730 /* Allgemeine Initialisierungen... */
2731 newW->combobox.ConvertBitmapToPixmap = False;
2732 newW->combobox.ConvertBitmapToPixmapInsensitive = False;
2733
2734 newW->combobox.LastSelection = 0;
2735
2736 newW->combobox.InInit = True;
2737
2738 /*
2739 * Das folgende Problem mit der Kontrolle, ob sich das Widget absolut auf
2740 * dem Bildschirm verschoben hat, trifft uns nur, wenn die Liste nicht
2741 * dauernd auf dem Bildschirm erscheint:
2742 * Lass' dich benachrichtigen, sobald dieses Widget in irgendeiner
2743 * Form bewegt wird -- und sei es nur, dass das gesamte Applikations-
2744 * fenster umhergeschoben wurde. Um die Benachrichtigung ueberhaupt
2745 * zu erreichen, ist es erforderlich, sich benachrichtigen zu lassen,
2746 * sobald die naechste Shell (oder ein Nachkomme) im Widget-Instanzen-
2747 * Baum verschoben wurde.
2748 */
2749 if ( !newW->combobox.StaticList ) {
2750 w = (Widget) newW;
2751 while ( !XtIsSubclass(w, shellWidgetClass) )
2752 w = XtParent(w);
2753 newW->combobox.MyNextShell = w;
2754 XtAddEventHandler(w,
2755 StructureNotifyMask | FocusChangeMask,
2756 False, (XtEventHandler) ShellCallback,
2757 (XtPointer) newW);
2758 }
2759
2760 /* Richte nun alle zu diesem Widget gehoerenden Kinder ein, als da
2761 * waeren:
2762 * 1 x editierbares Eingabefeld
2763 * 1 x ein Pfeil nach unten
2764 * 1 x ein Schriftfeld
2765 */
2766 newW->combobox.EditCtrl = XtVaCreateManagedWidget(
2767 "edit", xmTextFieldWidgetClass, (Widget) newW,
2768 XmNverifyBell, False,
2769 NULL);
2770 XtAddCallback(newW->combobox.EditCtrl,
2771 XmNlosingFocusCallback,
2772 (XtCallbackProc) EditFocusCallback, NULL);
2773 XtAddCallback(newW->combobox.EditCtrl,
2774 XmNmodifyVerifyCallback,
2775 (XtCallbackProc) EditVerifyCallback, NULL);
2776 XtAddCallback(newW->combobox.EditCtrl,
2777 XmNvalueChangedCallback,
2778 (XtCallbackProc) EditChangedCallback, NULL);
2779 XtAddCallback(newW->combobox.EditCtrl,
2780 XmNhelpCallback,
2781 (XtCallbackProc) HelpCallback,
2782 (XtPointer) newW);
2783 XtAddCallback(newW->combobox.EditCtrl,
2784 XmNactivateCallback,
2785 (XtCallbackProc) ActivateCallback,
2786 (XtPointer) newW);
2787 if ( newW->combobox.Editable )
2788 XtAddCallback(newW->combobox.EditCtrl,
2789 XmNmotionVerifyCallback,
2790 (XtCallbackProc) MotionVerifyCallback,
2791 (XtPointer) newW);
2792 /* Neue Translations fuer das Eingabefeld aufnehmen */
2793 XtOverrideTranslations(newW->combobox.EditCtrl,
2794 NewEditTranslations);
2795 if ( !newW->combobox.Editable ) {
2796 XtOverrideTranslations(newW->combobox.EditCtrl,
2797 NewEditTranslationsNE);
2798 XtVaSetValues(newW->combobox.EditCtrl,
2799 XmNcursorPositionVisible, False, NULL);
2800 }
2801 #ifdef NODRAGNDROP
2802 XtOverrideTranslations(newW->combobox.EditCtrl,
2803 NewListTranslations); /* Btn2Dwn aus! */
2804 #endif
2805
2806 /* --- */
2807 newW->combobox.ArrowCtrl = XtVaCreateManagedWidget(
2808 "arrow", xmArrowButtonWidgetClass, (Widget) newW,
2809 XmNarrowDirection, XmARROW_DOWN,
2810 XmNtraversalOn, False,
2811 XmNnavigationType, XmNONE,
2812 XmNborderWidth, 0,
2813 XmNhighlightThickness, 0,
2814 NULL);
2815 XmRemoveTabGroup(newW->combobox.ArrowCtrl);
2816 if ( newW->combobox.StaticList ) {
2817 XtVaSetValues(newW->combobox.ArrowCtrl,
2818 XmNmappedWhenManaged, False, NULL);
2819 } else {
2820 XtAddEventHandler(newW->combobox.ArrowCtrl,
2821 EnterWindowMask | LeaveWindowMask,
2822 False, (XtEventHandler) ArrowCrossingCallback,
2823 (XtPointer) newW);
2824 XtAddCallback(newW->combobox.ArrowCtrl,
2825 XmNactivateCallback,
2826 (XtCallbackProc) ArrowCallback, NULL);
2827 XtAddCallback(newW->combobox.ArrowCtrl,
2828 XmNarmCallback,
2829 (XtCallbackProc) ArrowCallback, NULL);
2830 XtAddCallback(newW->combobox.ArrowCtrl,
2831 XmNhelpCallback,
2832 (XtCallbackProc) HelpCallback,
2833 (XtPointer) newW);
2834 }
2835
2836 /* --- */
2837 newW->combobox.LabelCtrl = XtVaCreateWidget(
2838 "label", xmLabelWidgetClass, (Widget) newW,
2839 XmNstringDirection, newW->manager.string_direction,
2840 NULL);
2841 if ( newW->combobox.ShowLabel ) {
2842 XtManageChild((Widget) newW->combobox.LabelCtrl);
2843 XtAddCallback(newW->combobox.LabelCtrl,
2844 XmNhelpCallback,
2845 (XtCallbackProc) HelpCallback,
2846 (XtPointer) newW);
2847 }
2848
2849 /*
2850 * Zuerst noch die Shell erzeugen, die so einfach mir nichts dir nichts
2851 * frei auf dem Bildschirm herumschweben kann und damit das Ausklappen
2852 * der Liste erst ermoeglicht -- und uns allerhand Scherereien bereitet!
2853 * War das ein bloeder Fehler in Motif 1.2! Diese Version vertraegt ab-
2854 * solut keine ShellWidgetClass noch overrideShellWidgetClass!!!! Naja,
2855 * mit einer vendorShellWidgetClass laesst sich aber exakt der gleiche
2856 * Effekt erreichen. NEU: vor allem funktioniert dann endlich auch
2857 * Drag'n'Drop!!!
2858 * Noch neuer: Wenn die Liste dauerhaft angezeigt werden muss, entfaellt
2859 * diese Shell zwangslaeufig. Dann ist das Listenfeld ein direktes Kind
2860 * der ComboBox.
2861 */
2862 if ( !newW->combobox.StaticList ) {
2863 newW->combobox.PopupShell = XtVaCreateWidget(
2864 "combobox_shell", vendorShellWidgetClass, (Widget) newW,
2865 XmNoverrideRedirect, True,
2866 XmNsaveUnder, False,
2867 XmNallowShellResize, True,
2868 NULL);
2869 XtAddEventHandler(newW->combobox.PopupShell,
2870 EnterWindowMask | LeaveWindowMask,
2871 False, (XtEventHandler) OverrideShellCallback,
2872 (XtPointer) newW);
2873 } else {
2874 /*
2875 * Sieht ja pervers nach einer Rekursion aus...daher: OBACHT!
2876 */
2877 newW->combobox.PopupShell = (Widget) newW;
2878 }
2879
2880 /*
2881 * Nun kommt die Drop-Down-Liste an die Reihe. Die Liste muss dabei
2882 * mit einer Convenience-Funktion erstellt werden, damit ein Rollbalken
2883 * 'dran ist und das Ganze wird dann in eine Override-Shell gepackt.
2884 * Nicht zu vergessen ist der XtManageChild-Aufruf, damit die Liste
2885 * sofort nach dem Aufklappen der Shell sichtbar wird.
2886 */
2887 XtSetArg(args[n], XmNselectionPolicy, newW->combobox.SelectionPolicy); n++;
2888
2889 if ( !newW->combobox.StaticList ) {
2890 /*
2891 * Es gibt halt so eine ganze Reihe von Einstellungen, die koennen nicht
2892 * veraendert werden, wenn das Listenfeld nur bei Bedarf ausgeklappt wird.
2893 */
2894 XtSetArg(args[n], XmNhighlightThickness, 0); n++;
2895 }
2896 XtSetArg(args[n], XmNlistSizePolicy,
2897 newW->combobox.ListSizePolicy); n++;
2898 XtSetArg(args[n], XmNscrollBarDisplayPolicy,
2899 newW->combobox.ScrollBarDisplayPolicy); n++;
2900
2901 XtSetArg(args[n], XmNautomaticSelection,
2902 newW->combobox.AutomaticSelection); n++;
2903 XtSetArg(args[n], XmNvisibleItemCount,
2904 newW->combobox.VisibleItemCount); n++;
2905 newW->combobox.ListCtrl = XmCreateScrolledList(
2906 newW->combobox.PopupShell, "list",
2907 args, n);
2908
2909 /*
2910 * Fuer den Fall, dass die Liste in einer eigenen Shell steckt und daher frei
2911 * auf dem Bildschirm herumschweben kann, sollten wir sicherheitshalber die
2912 * Tastaturbedienung (Fokus) abschalten, um Probleme zu vermeiden (jedenfalls
2913 * hoffentlich...!)
2914 */
2915 if ( !newW->combobox.StaticList ) {
2916 XtVaSetValues(newW->combobox.ListCtrl,
2917 XmNtraversalOn, False, NULL);
2918 XtVaSetValues(XtParent(newW->combobox.ListCtrl),
2919 XmNtraversalOn, False, NULL);
2920 } else {
2921 if ( !newW->combobox.Editable ) {
2922 XtVaSetValues(XtParent(newW->combobox.ListCtrl),
2923 XmNtraversalOn, False, NULL);
2924 XmRemoveTabGroup(newW->combobox.ListCtrl);
2925 }
2926 }
2927
2928 XtManageChild(newW->combobox.ListCtrl);
2929 XtAddCallback(newW->combobox.ListCtrl,
2930 XmNsingleSelectionCallback,
2931 (XtCallbackProc) ListSelectionCallback,
2932 (XtPointer) newW);
2933 XtAddCallback(newW->combobox.ListCtrl,
2934 XmNbrowseSelectionCallback,
2935 (XtCallbackProc) ListSelectionCallback,
2936 (XtPointer) newW);
2937 XtAddCallback(newW->combobox.ListCtrl,
2938 XmNdefaultActionCallback,
2939 (XtCallbackProc) ListDefaultActionCallback,
2940 (XtPointer) newW);
2941 XtAddCallback(newW->combobox.ListCtrl,
2942 XmNhelpCallback,
2943 (XtCallbackProc) HelpCallback,
2944 (XtPointer) newW);
2945
2946 XtAddEventHandler(newW->combobox.ListCtrl,
2947 ButtonReleaseMask,
2948 False, (XtEventHandler) Button1UpInList,
2949 (XtPointer) newW);
2950
2951 #ifdef NODRAGNDROP
2952 XtOverrideTranslations(newW->combobox.ListCtrl,
2953 NewListTranslations);
2954 #endif
2955 if ( newW->combobox.StaticList && newW->combobox.Editable )
2956 XtOverrideTranslations(newW->combobox.ListCtrl,
2957 NewListTranslationsE);
2958
2959 /* Jetzt wird es dann erst richtig spannend... Zuerst alle evtl.
2960 * in der Resource-Datenbank abgelegten Resourcen an die Kinder
2961 * weitergeben. Danach die uebergebenen Parameter ebenfalls an
2962 * die Kinder weiterreichen und schliesslich das Layout ermitteln.
2963 */
2964 InitMirrorResources(newW);
2965 UpdateColors(newW, -1);
2966 SetValues(newW, newW, newW, wargs, ArgCount);
2967
2968 if ( newW->combobox.ConvertBitmapToPixmap )
2969 newW->combobox.LabelPixmap =
2970 BitmapToPixmap(newW, XmNlabelPixmap,
2971 ((XmLabelRec *) newW->combobox.LabelCtrl)->
2972 label.normal_GC);
2973 if ( newW->combobox.ConvertBitmapToPixmapInsensitive )
2974 newW->combobox.LabelInsensitivePixmap =
2975 BitmapToPixmap(newW, XmNlabelInsensitivePixmap,
2976 ((XmLabelRec *) newW->combobox.LabelCtrl)->
2977 label.insensitive_GC);
2978
2979 DefaultGeometry(newW, &width, &height, &dummy, &dummy);
2980 if ( newW->core.width == 0 )
2981 newW->core.width = width;
2982 if ( newW->core.height == 0 )
2983 newW->core.height = height;
2984
2985 /*
2986 * Falls wir keine Fontliste besitzen, dann nehmen wir die von
2987 * dem Eingabefeld...
2988 */
2989 if ( newW->combobox.Font == NULL ) {
2990 XtVaGetValues(newW->combobox.EditCtrl,
2991 XmNfontList, &newW->combobox.Font, NULL);
2992 XtVaSetValues(newW->combobox.ListCtrl,
2993 XmNfontList, newW->combobox.Font, NULL);
2994 } else {
2995 XtVaSetValues(newW->combobox.ListCtrl,
2996 XmNfontList, newW->combobox.Font, NULL);
2997 XtVaSetValues(newW->combobox.EditCtrl,
2998 XmNfontList, newW->combobox.Font, NULL);
2999 }
3000
3001 /*
3002 * Initialisiere alle Statusflaggen, die mit diesem unseligen Focus-
3003 * problem zu tun haben...
3004 */
3005 newW->combobox.ListVisible = False;
3006 newW->combobox.IgnoreFocusOut = False;
3007 newW->combobox.PendingFocusOut = False;
3008 newW->combobox.PendingOverrideInOut = False;
3009
3010 newW->combobox.PassVerification = False;
3011
3012 /*
3013 * Jooa... bei der OSF pennen die wohl komplett?! Zusammen mit Form-
3014 * Widgets gibt das wohl immer Aerger...daher hier ein DoLayout()
3015 * aufrufen, damit Eingabefeld und Pfeil sowie das Listenfeld an der
3016 * richtigen Stelle sitzen!
3017 */
3018 DoLayout(newW);
3019 /*
3020 * Endlich fertig mit der Initialisierung. Das hier ist aber auch
3021 * wirklich viel Arbeit fuer so ein Widget!
3022 */
3023 newW->combobox.InInit = False;
3024 } /* Initialize */
3025
3026 /* --------------------------------------------------------------------
3027 * Diese Funktionen bitte nur im aeussersten Notfall benutzen, da sie
3028 * die Abstraktion dieser neuen Klasse umgehen und Informationen ueber
3029 * den internen Aufbau voraussetzen.
3030 */
3031 Widget XmComboBoxGetEditWidget(Widget w)
3032 {
3033 return ((XmComboBoxWidget) w)->combobox.EditCtrl;
3034 } /* XmComboBoxGetEditWidget */
3035
3036 Widget XmComboBoxGetListWidget(Widget w)
3037 {
3038 return ((XmComboBoxWidget) w)->combobox.ListCtrl;
3039 } /* XmComboBoxGetListWidget */
3040
3041 Widget XmComboBoxGetLabelWidget(Widget w)
3042 {
3043 return ((XmComboBoxWidget) w)->combobox.LabelCtrl;
3044 } /* XmComboBoxGetLabelWidget */
3045
3046
3047 /* --------------------------------------------------------------------
3048 * Sobald sich im Listenfeld Eintraege veraenderten, sei es, dass sie
3049 * geloescht wurden, sei es, dass sie veraendert wurden, so muss hier
3050 * gegebenenfalls auch der Text im Eingabefeld angepasst werden.
3051 * Letzteres betrifft aber nur Combo-Boxen mit nicht editierbarem
3052 * Eingabefeld. In jedem Fall wird aber bei jeder Combo-Box-Type in
3053 * dem Fall, dass ein Eintrag geloescht wird, der darauffolgende
3054 * Eintrag markiert. Eigentlich ist dieses nur eine nette Geste
3055 * gegenueber dem Benutzer...
3056 *
3057 * Parameter:
3058 * w Combo-Box-Widget
3059 * Index Index auf denjenigen Eintrag der sich geaendert
3060 * hat, oder der geloescht wurde.
3061 * Deleted Zeigt an, ob der Eintrag geloescht wurde (True)
3062 * oder sich nur veraenderte (False)
3063 */
3064 static UpdateComboBox(XmComboBoxWidget w, int Index, Boolean Deleted)
3065 {
3066 int OldIndex, ItemCount;
3067
3068 OldIndex = XmComboBoxGetSelectedPos((Widget) w);
3069 if ( OldIndex == Index ) {
3070 /* Es betrifft den Eintrag, der auch momentan ausgewaehlt ist.
3071 * Sollte er geloescht werden, so nimm' (soweit vorhanden) den
3072 * naechsten Eintrag, wurde er ausgetauscht, so lass ihn ausge-
3073 * waehlt.
3074 */
3075 if ( Deleted ) {
3076 XtVaGetValues(w->combobox.ListCtrl,
3077 XmNitemCount, &ItemCount, NULL);
3078 if ( ItemCount != 0 ) {
3079 if ( Index >= ItemCount ) Index = ItemCount;
3080 /* Markieren des Eintrags, ohne jedoch jetzt schon
3081 * den Eintrag in die Eingabezeile zu kopieren.
3082 */
3083 SetSelectionPos(w, Index, False);
3084 }
3085 }
3086 }
3087 /* Das Problem betrifft uns nur bei nicht editierbaren Combo-Boxen
3088 * im vollen Umfang. Denn dann muss auch der Text im Eingabefeld
3089 * veraendert werden.
3090 */
3091 if ( !w->combobox.Editable ) {
3092 TransferToEditCtrl(w, Index, False);
3093 }
3094
3095 return 1;
3096 } /* UpdateComboBox */
3097
3098
3099 /* --------------------------------------------------------------------
3100 * Die Eintragsposition finden, an der der Eintrag sortiert stehen
3101 * muesste. Naja, es wurde ja 'mal langsam Zeit, diese Routine etwas
3102 * aufzupolieren, damit sie schneller wird.
3103 */
3104 static int FindSortedItemPos(XmComboBoxWidget w, XmString item)
3105 {
3106 Widget ListBox = w->combobox.ListCtrl;
3107 XmStringTable Items;
3108 int ItemCount, index, Left, Right, Result;
3109 char *pItemText, *pCompareText;
3110 Boolean ExternSort;
3111 XmComboBoxSortingCallbackStruct data;
3112
3113 XtVaGetValues(ListBox, XmNitems, &Items,
3114 XmNitemCount, &ItemCount, NULL);
3115 if ( ItemCount == 0 ) return 1;
3116
3117 /*
3118 * Moechte das Programm die Kontrolle ueber den Sortiervorgang
3119 * uebernehmen? Dann bereite alles vor...
3120 */
3121 ExternSort = XtHasCallbacks((Widget) w, XmNsortingCallback) ==
3122 XtCallbackHasSome;
3123 if ( ExternSort ) {
3124 data.reason = XmCR_SORTING;
3125 data.event = NULL;
3126 data.operation = XmOP_INIT;
3127 data.item = item;
3128 XtCallCallbacks((Widget) w, XmNsortingCallback, (XtPointer) &data);
3129 } else
3130 XmStringGetLtoR(item, XmSTRING_DEFAULT_CHARSET, &pCompareText);
3131
3132 Left = 0; Right = ItemCount - 1;
3133 do {
3134 index = (Left + Right) / 2;
3135 if ( ExternSort ) {
3136 data.operation = XmOP_COMPARE;
3137 data.item = Items[index];
3138 data.result = 1;
3139 XtCallCallbacks((Widget) w, XmNsortingCallback, (XtPointer) &data);
3140 Result = data.result;
3141 } else {
3142 XmStringGetLtoR(Items[index], XmSTRING_DEFAULT_CHARSET, &pItemText);
3143 Result = strcmp(pCompareText, pItemText);
3144 XtFree(pItemText);
3145 }
3146 if ( Result < 0 ) Right = index - 1;
3147 else if ( Result > 0 ) Left = index + 1;
3148 } while ( (Result != 0) && (Left <= Right) );
3149
3150 /*
3151 * Nach Gebrauch wieder alles aufraeumen (bei einer externen Sortierung
3152 * muss das das Programm uebernehmen!)
3153 */
3154 if ( ExternSort ) {
3155 data.operation = XmOP_DONE;
3156 XtCallCallbacks((Widget) w, XmNsortingCallback, (XtPointer) &data);
3157 } else
3158 XtFree(pCompareText);
3159
3160 if ( Result < 0 )
3161 return index + 1; /* Beachte, dass Indizes mit 1 beginnen! */
3162 else
3163 return index + 2; /* Beachte, dass Indizes mit 1 beginnen! */
3164 } /* FindSortedItemPos */
3165
3166 /* --------------------------------------------------------------------
3167 * Kontrolliere, ob es sich ueberhaupt um eine Combo-Box (bzw. einen
3168 * hypothetischen Nachkommen) handelt -- ansonsten mecker kraeftig
3169 * herum!
3170 * Ergebnis:
3171 * True, falls wir hier ein falsches Widget untergejubelt bekommen!
3172 */
3173 static Boolean CheckComboBox(Widget w, char *pFuncName)
3174 {
3175 char buff[256];
3176 char *pWName;
3177
3178 #if (XmVersion >= 2000)
3179 return False; /* temporary workaround */
3180 #else
3181 if ( XmIsComboBox(w) ) return False;
3182 pWName = XrmQuarkToString(w->core.xrm_name);
3183 sprintf(buff,
3184 "Warning: %s called on widget named %s beeing \
3185 not a descendant of class XmComboBox!",
3186 pFuncName, pWName);
3187 XtWarning(buff);
3188 return True;
3189 #endif
3190 } /* CheckComboBox */
3191
3192 /* --------------------------------------------------------------------
3193 * Saemtliche Interface-Routinen zur Combo-Box
3194 */
3195 /* Zunaechst alles fuer die Listbox */
3196 #define ListBox (((XmComboBoxWidget) w)->combobox.ListCtrl)
3197 #define EditBox (((XmComboBoxWidget) w)->combobox.EditCtrl)
3198 #define ComboBox ((XmComboBoxWidget) w)
3199
3200 /* !!!
3201 * So angepasst, dass bei doppelt auftretenden Eintraegen, der
3202 * alte Eintrag weiterhin markiert bleibt. Diese Massnahme soll
3203 * eigentlich nur verhindern, dass zufaellig zwei Eintraege
3204 * markiert sind, falls nach der Anwahl eines Eintrages ein zweiter
3205 * gleichlautender Eintrag hinzugefuegt wurde.
3206 * Was hier die reine Lehre (oder war das die Leere?) anbetrifft:
3207 * in einer Combo-Box sollten sich sowieso nie gleichlautende
3208 * Eintraege befinden, da sie dort unsinnig sind und den Benutzer
3209 * nur verwirren...
3210 */
3211 void XmComboBoxAddItem(Widget w, XmString item, int pos)
3212 {
3213 int OldIndex = XmComboBoxGetSelectedPos(w);
3214
3215 if ( CheckComboBox(w, "XmComboBoxAddItem") ) return;
3216 if ( ComboBox->combobox.Sorted )
3217 pos = FindSortedItemPos(ComboBox, item);
3218 XmListAddItem(ListBox, item, pos);
3219 if ( OldIndex != XmComboBoxGetSelectedPos(w) )
3220 /* Hier SetSelectionPos() statt XmComboBoxSelectPos(),
3221 * da der Text nicht in das Eingabefeld uebertragen werden
3222 * soll!
3223 */
3224 SetSelectionPos(ComboBox, OldIndex, False);
3225 } /* XmComboBoxAddItem */
3226 /* !!!
3227 * Hier gilt das bereits oben gesagte (siehe XmComboBoxAddItem).
3228 * Bei sortierten Listboxen wird die Sortierung beim Gebrauch dieser
3229 * Funktion zerstoert!
3230 */
3231 void XmComboBoxAddItems(Widget w, XmString *items, int item_count, int pos)
3232 {
3233 int OldIndex = XmComboBoxGetSelectedPos(w);
3234
3235 if ( CheckComboBox(w, "XmComboBoxAddItems") ) return;
3236 XmListAddItems(ListBox, items, item_count, pos);
3237 if ( OldIndex != XmComboBoxGetSelectedPos(w) )
3238 /* Siehe Anmerkung in XmComboBoxAddItem */
3239 SetSelectionPos(ComboBox, OldIndex, False);
3240 } /* XmComboBoxAddItems */
3241
3242 void XmComboBoxAddItemUnselected(Widget w, XmString item, int pos)
3243 { XmListAddItemUnselected(ListBox, item, pos); }
3244
3245 /* !!!
3246 * Da bei den folgenden Routinen jeweils ein oder mehrere Eintraege
3247 * geloescht oder veraendert werden, muss gegebenefalls das Eingabe-
3248 * feld bei nicht editierbaren Combo-Boxen auf Vordermann gebracht
3249 * werden.
3250 */
3251 void XmComboBoxDeleteItem(Widget w, XmString item)
3252 {
3253 int Index = XmListItemPos(ListBox, item);
3254
3255 if ( CheckComboBox(w, "XmComboBoxDeleteItem") ) return;
3256 if ( Index ) XmComboBoxDeletePos(w, Index);
3257 } /* XmComboBoxDeleteItem */
3258
3259 void XmComboBoxDeleteItems(Widget w, XmString *items, int item_count)
3260 {
3261 int i;
3262
3263 if ( CheckComboBox(w, "XmComboBoxDeleteItems") ) return;
3264 for ( i = 0; i < item_count; i++ )
3265 XmListDeleteItem(w, items[i]);
3266 } /* XmComboBoxDeleteItems */
3267
3268 void XmComboBoxDeletePos(Widget w, int pos)
3269 {
3270 int OldIndex = XmComboBoxGetSelectedPos(w);
3271
3272 if ( CheckComboBox(w, "XmComboBoxDeletePos") ) return;
3273 XmListDeletePos(ListBox, pos);
3274 if ( pos == OldIndex ) UpdateComboBox(ComboBox, pos, True);
3275 } /* XmComboBoxDeletePos */
3276
3277 void XmComboBoxDeleteItemsPos(Widget w, int item_count, int pos)
3278 {
3279 int i;
3280
3281 if ( CheckComboBox(w, "XmComboBoxDeleteItemsPos") ) return;
3282 for ( i = 0; i < item_count; i++ )
3283 XmComboBoxDeletePos(w, pos++);
3284 } /* XmComboBoxDeleteItemsPos */
3285
3286 void XmComboBoxDeleteAllItems(Widget w)
3287 {
3288 if ( CheckComboBox(w, "XmComboBoxAllDeleteItems") ) return;
3289 XmListDeleteAllItems(ListBox);
3290 UpdateComboBox(ComboBox, 0, True);
3291 } /* XmComboBoxDeleteAllItems */
3292
3293 /* !!!
3294 * Werden Eintraege ausgetauscht, so heisst es fuer uns, auch hierbei
3295 * auf der Hut zu sein.
3296 */
3297 void XmComboBoxReplaceItems(Widget w, XmString *old_items, int item_count, XmString *new_items)
3298 {
3299 if ( CheckComboBox(w, "XmComboBoxReplaceItems") ) return;
3300 XmListReplaceItems(ListBox, old_items, item_count, new_items);
3301 UpdateComboBox(ComboBox, XmComboBoxGetSelectedPos(w), False);
3302 } /* XmComboBoxReplaceItems */
3303
3304 void XmComboBoxReplaceItemsPos(Widget w, XmString *new_items, int item_count, int position)
3305 {
3306 int OldIndex = XmComboBoxGetSelectedPos(w);
3307
3308 if ( CheckComboBox(w, "XmComboBoxReplaceItemsPos") ) return;
3309 XmListReplaceItemsPos(ListBox, new_items, item_count, position);
3310 if ( (OldIndex >= position) && (OldIndex < position + item_count) )
3311 UpdateComboBox(ComboBox, OldIndex, False);
3312 } /* XmComboBoxReplaceItemsPos */
3313
3314 Boolean XmComboBoxItemExists(Widget w, XmString item)
3315 {
3316 if ( CheckComboBox(w, "XmComboBoxItemExists") ) return False;
3317 return XmListItemExists(ListBox, item);
3318 } /* XmComboBoxItemExists */
3319
3320 int XmComboBoxItemPos(Widget w, XmString item)
3321 {
3322 if ( CheckComboBox(w, "XmComboBoxItemPos") ) return 0;
3323 return XmListItemPos(ListBox, item);
3324 } /* XmComboBoxItemPos */
3325
3326 Boolean XmComboBoxGetMatchPos(Widget w, XmString item, int **pos_list, int *pos_count)
3327 {
3328 if ( CheckComboBox(w, "XmComboBoxGetMatchPos") ) return False;
3329 return XmListGetMatchPos(ListBox, item, pos_list, pos_count);
3330 } /* XmComboBoxGetMatchPos */
3331
3332 /* !!!
3333 * Sobald ein anderer Eintrag in der Listbox ausgewaehlt werden soll,
3334 * muessen wir hier helfend eingreifen.
3335 */
3336 void XmComboBoxSelectPos(Widget w, int pos, Boolean notify)
3337 {
3338 int index;
3339
3340 if ( CheckComboBox(w, "XmComboBoxSelectPos") ) return;
3341 index = SetSelectionPos(ComboBox, pos, notify);
3342 if ( index ) TransferToEditCtrl(ComboBox, index, False);
3343 } /* XmComboBoxSelectPos */
3344
3345 /* !!!
3346 * dto. analog zu XmComboBoxSelectPos, nur statt des Index wird der
3347 * Eintragstext angegeben, um einen Eintrag in der Listbox zu
3348 * markieren.
3349 */
3350 void XmComboBoxSelectItem(Widget w, XmString item, Boolean notify)
3351 {
3352 int index;
3353
3354 if ( CheckComboBox(w, "XmComboBoxSelectItem") ) return;
3355 XmListSelectItem(ListBox, item, notify);
3356 index = SetSelectionPos(ComboBox, XmComboBoxGetSelectedPos(w), False);
3357 if ( index ) TransferToEditCtrl(ComboBox, index, False);
3358 } /* XmComboBoxSelectItem */
3359
3360 /* !!!
3361 * Geaendert gegenueber dem ListBox-Pendant! Da in einer Combo-Box die
3362 * Liste nur maximal einen ausgewaehlten Eintrag besitzt, macht die
3363 * 'alte' Funktionalitaet von XmListGetSelectedPos ziemlich wenig Sinn.
3364 * Die neue Routine liefert statt dessen direkt den Index des aus-
3365 * gewaehlten Eintrages oder 0 zurueck.
3366 */
3367 int XmComboBoxGetSelectedPos(Widget w)
3368 {
3369 int *SelectionList, SelectionCount, SelectionIndex;
3370
3371 if ( CheckComboBox(w, "XmComboBoxGetSelectedPos") ) return 0;
3372 if ( XmListGetSelectedPos(ListBox,
3373 &SelectionList, &SelectionCount) ) {
3374 SelectionIndex = *SelectionList;
3375 XtFree((char *)SelectionList);
3376 } else SelectionIndex = 0;
3377 return SelectionIndex;
3378 } /* XmComboBoxGetSelectedPos */
3379
3380
3381
3382 void XmComboBoxClearSelection(Widget w, Time time)
3383 {
3384 XmTextFieldClearSelection(EditBox, time);
3385 } /* XmComboBoxClearSelection */
3386
3387 Boolean XmComboBoxCopy(Widget w, Time time)
3388 {
3389 return XmTextFieldCopy(EditBox, time);
3390 } /* XmComboBoxCopy */
3391
3392 Boolean XmComboBoxCut(Widget w, Time time)
3393 {
3394 return XmTextFieldCut(EditBox, time);
3395 } /* XmComboBoxCut */
3396
3397 XmTextPosition XmComboBoxGetInsertionPosition(Widget w)
3398 {
3399 return XmTextFieldGetInsertionPosition(EditBox);
3400 } /* XmComboBoxGetInsertionPosition */
3401
3402 XmTextPosition XmComboBoxGetLastPosition(Widget w)
3403 {
3404 return XmTextFieldGetLastPosition(EditBox);
3405 } /* XmComboBoxGetLastPosition */
3406
3407 int XmComboBoxGetMaxLength(Widget w)
3408 {
3409 return XmTextFieldGetMaxLength(EditBox);
3410 } /* XmComboBoxGetMaxLength */
3411
3412 char * XmComboBoxGetSelection(Widget w)
3413 {
3414 return XmTextFieldGetSelection(EditBox);
3415 } /* XmComboBoxGetSelection */
3416
3417 Boolean XmComboBoxGetSelectionPosition(Widget w, XmTextPosition *left,
3418 XmTextPosition *right)
3419 {
3420 return XmTextFieldGetSelectionPosition(EditBox, left, right);
3421 } /* XmComboBoxGetSelectionPosition */
3422
3423 char * XmComboBoxGetString(Widget w)
3424 {
3425 return XmTextFieldGetString(EditBox);
3426 } /* XmComboBoxGetString */
3427
3428 void XmComboBoxInsert(Widget w, XmTextPosition position, char *value)
3429 {
3430 XmTextFieldInsert(EditBox, position, value);
3431 } /* XmComboBoxInsert */
3432
3433 Boolean XmComboBoxPaste(Widget w)
3434 {
3435 return XmTextFieldPaste(EditBox);
3436 } /* XmComboBoxPaste */
3437
3438 Boolean XmComboBoxRemove(Widget w)
3439 {
3440 return XmTextFieldRemove(EditBox);
3441 } /* XmComboBoxRemove */
3442
3443 void XmComboBoxReplace(Widget w, XmTextPosition from_pos,
3444 XmTextPosition to_pos, char *value)
3445 {
3446 XmTextFieldReplace(EditBox, from_pos, to_pos, value);
3447 } /* XmComboBoxReplace */
3448
3449 void XmComboBoxSetAddMode(Widget w, Boolean state)
3450 {
3451 XmTextFieldSetAddMode(EditBox, state);
3452 } /* XmComboBoxSetAddMode */
3453
3454 void XmComboBoxSetHighlight(Widget w, XmTextPosition left,
3455 XmTextPosition right, XmHighlightMode mode)
3456 {
3457 XmTextFieldSetHighlight(EditBox, left, right, mode);
3458 } /* XmComboBoxSetHighlight */
3459
3460 void XmComboBoxSetInsertionPosition(Widget w, XmTextPosition position)
3461 {
3462 XmTextFieldSetInsertionPosition(EditBox, position);
3463 } /* XmComboBoxSetInsertionPosition */
3464
3465 void XmComboBoxSetMaxLength(Widget w, int max_length)
3466 {
3467 XmTextFieldSetMaxLength(EditBox, max_length);
3468 } /* XmComboBoxSetMaxLength */
3469
3470 void XmComboBoxSetSelection(Widget w, XmTextPosition first,
3471 XmTextPosition last, Time time)
3472 {
3473 XmTextFieldSetSelection(EditBox, first, last, time);
3474 } /* XmComboBoxSetSelection */
3475
3476 void XmComboBoxSetString(Widget w, char *value)
3477 {
3478 /* Liebe OSF...ihr ^&*#%$*&)*(@$(*^(*&%# habt doch einen ziemlich gemeinen
3479 * Fehler in XmTextFieldSetString() drin... wenn man einen leeren String
3480 * (also "") angiebt, gibt's nur noch Aerger, wenn man spaeter wieder an
3481 * den Inhalt des Eingabefeldes heranwill.
3482 */
3483 if ( (value == NULL) || (*value == 0) )
3484 XtVaSetValues(w, XmNvalue, "", NULL);
3485 else
3486 XmTextFieldSetString(EditBox, value);
3487 } /* XmComboBoxSetString */
3488
3489 void XmComboBoxShowPosition(Widget w, XmTextPosition position)
3490 {
3491 XmTextFieldShowPosition(EditBox, position);
3492 } /* XmComboBoxShowPosition */
3493
3494 /*
3495 * Loescht einen evtl. noch ausgewaehlten Eintrag in einer Combo Box,
3496 * wenn diese eine SelectionPolicy von XmSINGLE_SELECT hat.
3497 */
3498 void XmComboBoxClearItemSelection(Widget w)
3499 {
3500 int index;
3501
3502 if ( CheckComboBox(w, "XmComboBoxClearItemSelection") ) return;
3503
3504 /*
3505 * Wenn bereits kein Eintrag markiert ist, dann loeschen wir nur noch
3506 * eben das Eingabefeld.
3507 */
3508 index = XmComboBoxGetSelectedPos(w);
3509 if ( index == 0 ) {
3510 XmComboBoxSetString(w, "");
3511 } else {
3512 /*
3513 * Ansonsten aktuellen Eintrag entsorgen (wie bei der Methode wipe-out)
3514 */
3515 TransferToEditCtrl(ComboBox, 0, True);
3516 CallSelectionCBL(ComboBox, NULL);
3517 }
3518 } /* XmComboBoxClearItemSelection */
3519
3520 /* Die Drop-Down-Liste ein oder ausklappen */
3521 void XmComboBoxShowList(Widget w)
3522 {
3523 if ( CheckComboBox(w, "XmComboBoxShowList") ) return;
3524 ShowHideDropDownList((XmComboBoxWidget) w, NULL, False);
3525 } /* XmComboBoxShowList */
3526
3527 void XmComboBoxHideList(Widget w)
3528 {
3529 if ( CheckComboBox(w, "XmComboBoxHideList") ) return;
3530 ShowHideDropDownList((XmComboBoxWidget) w, NULL, True);
3531 } /* XmComboBoxShowList */
3532
3533 /*
3534 * Naja, ich komm' ja doch nicht um diese olle Funktion herum...
3535 */
3536 Widget XmCreateComboBox(Widget parent, String name, ArgList arglist,
3537 Cardinal argcount)
3538 {
3539 return XtCreateWidget(name, xmComboBoxWidgetClass, parent,
3540 arglist, argcount);
3541 } /* XmCreateComboBox */
3542
3543 /* Ende von ComboBox.c */