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