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