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