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