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