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