/* $TOG: ComboBox.c /main/16 1999/07/28 10:59:19 vipin $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ /* * HISTORY */ /* ComboBox.c */ #include #include #include #include /* for bzero et al */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "GeoUtilsI.h" #include "MenuShellI.h" #include "MessagesI.h" #include "RepTypeI.h" #include "TraversalI.h" #include "UniqueEvnI.h" #include "XmI.h" #include "XmStringI.h" #define NOKIDS _XmMMsgComboBox_0000 #define TYPEWARNING _XmMMsgComboBox_0001 #define MISSINGKID _XmMMsgComboBox_0004 #define UNMANAGEDKID _XmMMsgComboBox_0005 #define MBWARNING _XmMMsgComboBox_0006 #define WRONGPARAMS _XmMMsgComboBox_0007 #define WRONGWIDGET _XmMMsgComboBox_0008 #define SELECTBADITEM _XmMMsgComboBox_0009 #define SETBADITEM _XmMMsgComboBox_0010 #define DELETEBADPOS _XmMMsgComboBox_0011 #define NOTACOMBOBOX _XmMMsgComboBox_0012 #define NOSETKIDS _XmMMsgComboBox_0013 #define MODEWARNING _XmMMsgComboBox_0014 #define default_translations _XmComboBox_defaultTranslations #define LIST_CHILD_NAME "List" #define TEXT_CHILD_NAME "Text" #define SHELL_CHILD_NAME "GrabShell" /* From GrabShell.c */ #define Events (EnterWindowMask | LeaveWindowMask | ButtonPressMask | \ ButtonReleaseMask) /* CB_ShellState() values. */ enum { POPPED_DOWN, POPPING_UP, POPPED_UP, POPPING_DOWN }; /******** Static Function Declarations ********/ /** Widget Class Methods **/ static void Initialize (Widget, Widget, ArgList, Cardinal *); static void ClassInitialize (void); static void ClassPartInitialize (WidgetClass wc); static Boolean SetValues (Widget, Widget, Widget, ArgList, Cardinal *); static XtGeometryResult GeometryManager (Widget, XtWidgetGeometry *, XtWidgetGeometry *); static void InsertChild (Widget child); static void Resize (Widget widget); static void Redisplay (Widget widget, XEvent *event, Region region); static XtGeometryResult QueryGeometry (Widget, XtWidgetGeometry *, XtWidgetGeometry *); static void ChangeManaged (Widget widget); static void Destroy (Widget widget); static void ConstraintDestroy (Widget widget); static Boolean ComboBoxParentProcess (Widget wid, XmParentProcessData event); /** Event Handlers **/ static void PopupEH (Widget, XtPointer, XEvent *, Boolean *); static void SBBtnDownEH (Widget, XtPointer, XEvent *, Boolean *); static void SBBtnUpEH (Widget, XtPointer, XEvent *, Boolean *); /** Callbacks **/ static void TextChangedCB (Widget, XtPointer, XtPointer); static void ListSelectionCB (Widget, XtPointer, XtPointer); static void ShellPopupCB (Widget, XtPointer, XtPointer); static void ShellPopdownCB (Widget, XtPointer, XtPointer); static void FocusMovedCB (Widget, XtPointer, XtPointer); /** Action Routines **/ static void CBArmAndDropDownList (Widget, XEvent *, String *, Cardinal *); static void CBDisarm (Widget, XEvent *, String *, Cardinal *); static void CBDropDownList (Widget, XEvent *, String *, Cardinal *); static void CBFocusIn (Widget, XEvent *, String *, Cardinal *); static void CBFocusOut (Widget, XEvent *, String *, Cardinal *); static void CBCancel (Widget, XEvent *, String *, Cardinal *); static void CBActivate (Widget, XEvent *, String *, Cardinal *); static void CBListAction (Widget, XEvent *, String *, Cardinal *); static void CBTextFocusOut (Widget, XEvent *, String *, Cardinal *); /** Default resource value call procs. **/ static void CheckSetRenderTable (Widget, int, XrmValue *); /** Synthetic Resource access methods **/ static XmImportOperator CBSetSelectedItem (Widget, int, XtArgVal *); static XmImportOperator CBSetSelectedPos (Widget, int, XtArgVal *); static void CBGetSelectedItem (Widget, int, XtArgVal *); static void CBGetSelectedPos (Widget, int, XtArgVal *); static void CBGetColumns (Widget, int, XtArgVal *); static void CBGetItems (Widget, int, XtArgVal *); static void CBGetItemCount (Widget, int, XtArgVal *); static void CBGetVisibleItemCount (Widget, int, XtArgVal *); /** Additional Functions **/ static Boolean PopdownList (Widget cb, XEvent *event); static void CreateChildren (Widget, ArgList, Cardinal *); static Widget CreateEditBox (Widget, String, Widget, ArgList, Cardinal *); static Widget CreateScrolledList (Widget, String, Widget, ArgList, Cardinal *); static Widget CreatePulldown (Widget, String, Widget, ArgList, Cardinal *); static void ComputeSize (Widget, Dimension, Dimension, Dimension*, Dimension*); static void DoLayout (Widget); static void GetThickness (Widget, Dimension *, Dimension *); static void GetArrowGC (Widget); static void DrawArrow (Widget, Boolean); static void DrawShadows (Widget); static Boolean Hit (XButtonEvent*, XRectangle); static void HighlightBorder (Widget w); static void UnhighlightBorder (Widget w); static XmComboBoxWidget FindComboBox (Widget w); static void CallSelectionCallbacks (Widget w, XEvent *event); static void SetHitArea (Widget w); static Boolean ReduceResources (Widget widget, Dimension *width, Dimension *height, Mask flags); static int Reduce (Dimension *size, int max_change, int min_size); static void GetIdealTextSize (Widget cb, int *width, int *height); static Dimension GetDefaultArrowSize (Widget cb); static XmString GetEditBoxValue (Widget cb); static void SetEditBoxValue (Widget cb, XmString value); /******** End Static Function Declarations ********/ #define POPUP_EVENT_MASK \ (EnterWindowMask | ButtonPressMask | ButtonReleaseMask) #define MINIMUM_SHADOWTHICKNESS 1 #define MINIMUM_HIGHLIGHTTHICKNESS 2 #define MINIMUM_ARROWSPACE 1 #define MINTXT 1 #define MINLIST 1 #define DEFAULT_MARGINWIDTH 2 #define DEFAULT_MARGINHEIGHT 2 #define HIGHLIGHT_THICKNESS ((Mask) (1<<0)) #define SHADOW_THICKNESS ((Mask) (1<<1)) #define ARROW_SPACING ((Mask) (1<<2)) #define MARGIN_WIDTH ((Mask) (1<<3)) #define MARGIN_HEIGHT ((Mask) (1<<4)) #define ARROW_SIZE ((Mask) (1<<5)) #define DEFAULT_ARROW_SCALING 0.75 #define SQRT3_OVER_2 0.8660254037844 #define XmUNSPECIFIED_COLUMNS ((short) XmUNSPECIFIED_COUNT) #define XmUNSPECIFIED_ITEMS ((XmStringTable) XmUNSPECIFIED) /* * These instance fields have special non-obvious meanings, and are * used for passing synthetic resource values to and from children. * The value of these fields is NOT maintained -- always query the * child for the current value. */ #define CBS_Columns(w) (((XmComboBoxWidget)(w))->combo_box.columns) #define CBS_ItemCount(w) (((XmComboBoxWidget)(w))->combo_box.item_count) #define CBS_Items(w) (((XmComboBoxWidget)(w))->combo_box.items) #define CBS_SelectedItem(w) (((XmComboBoxWidget)(w))->combo_box.selected_item) #define CBS_SelectedPosition(w) \ (((XmComboBoxWidget)(w))->combo_box.selected_position) #define CBS_VisibleItemCount(w) \ (((XmComboBoxWidget)(w))->combo_box.visible_item_count) #define Offset(field) XtOffsetOf(XmComboBoxRec, field) static XtResource resources[] = { { XmNarrowSize, XmCArrowSize, XmRHorizontalDimension, sizeof(Dimension), Offset(combo_box.arrow_size), XmRImmediate, (XtPointer)XmINVALID_DIMENSION }, { XmNarrowSpacing, XmCArrowSpacing, XmRHorizontalDimension, sizeof(Dimension), Offset(combo_box.arrow_spacing), XmRImmediate, (XtPointer)XmINVALID_DIMENSION }, { XmNhighlightThickness, XmCHighlightThickness, XmRHorizontalDimension, sizeof(Dimension), Offset(combo_box.highlight_thickness), XmRCallProc, (XtPointer) _XmSetThickness }, { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof(Dimension), Offset(combo_box.margin_height), XmRImmediate, (XtPointer)DEFAULT_MARGINHEIGHT }, { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof(Dimension), Offset(combo_box.margin_width), XmRImmediate, (XtPointer)DEFAULT_MARGINWIDTH }, { XmNmatchBehavior, XmCMatchBehavior, XmRMatchBehavior, sizeof(unsigned char), Offset(combo_box.match_behavior), XmRImmediate, (XtPointer)XmINVALID_MATCH_BEHAVIOR }, { XmNnavigationType, XmCNavigationType, XmRNavigationType, sizeof(unsigned char), Offset(manager.navigation_type), XmRImmediate, (XtPointer)XmSTICKY_TAB_GROUP }, { XmNselectionCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList), Offset(combo_box.selection_callback), XmRCallback, NULL }, { XmNselectedItem, XmCSelectedItem, XmRXmString, sizeof(XmString), Offset(combo_box.selected_item), XmRImmediate, (XtPointer)NULL }, { XmNselectedPosition, XmCSelectedPosition, XmRInt, sizeof(int), Offset(combo_box.selected_position), XmRImmediate, (XtPointer)XmINVALID_POSITION }, { XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, sizeof(Dimension), Offset(manager.shadow_thickness), XmRCallProc, (XtPointer) _XmSetThickness }, { XmNcomboBoxType, XmCComboBoxType, XmRComboBoxType, sizeof(unsigned char), Offset(combo_box.type), XtRImmediate, (XtPointer)XmCOMBO_BOX }, /* Borrow the "text_changed" instance field .. ! */ { "pri.vate", "Pri.vate", XmRBoolean, sizeof(Boolean), Offset(combo_box.text_changed), XmRImmediate, (XtPointer) False }, { XmNfontList, XmCFontList, XmRFontList, sizeof(XmRenderTable), Offset(combo_box.render_table), XmRCallProc, (XtPointer)CheckSetRenderTable }, { XmNrenderTable, XmCRenderTable, XmRRenderTable, sizeof(XmRenderTable), Offset(combo_box.render_table), XmRCallProc, (XtPointer)CheckSetRenderTable }, { XmNlist, XmCList, XmRWidget, sizeof(Widget), Offset(combo_box.list), XmRImmediate, (XtPointer)NULL }, { XmNtextField, XmCTextField, XmRWidget, sizeof(Widget), Offset(combo_box.edit_box), XmRImmediate, (XtPointer)NULL }, { XmNitems, XmCItems, XmRXmStringTable, sizeof(XmStringTable), Offset(combo_box.items), XmRImmediate, (XtPointer)XmUNSPECIFIED_ITEMS }, { XmNitemCount, XmCItemCount, XmRInt, sizeof(int), Offset(combo_box.item_count), XmRImmediate, (XtPointer)XmUNSPECIFIED_COUNT }, { XmNvisibleItemCount, XmCVisibleItemCount, XmRInt, sizeof(int), Offset(combo_box.visible_item_count), XmRImmediate, (XtPointer)10 }, { XmNcolumns, XmCColumns, XmRShort, sizeof(short), Offset(combo_box.columns), XmRImmediate, (XtPointer)XmUNSPECIFIED_COLUMNS }, { XmNpositionMode, XmCPositionMode, XmRPositionMode, sizeof(XtEnum), Offset(combo_box.position_mode), XmRImmediate, (XtPointer)XmZERO_BASED } }; /* * Resolution independent resources and resources needing special processing. */ static XmSyntheticResource syn_resources[] = { { XmNselectedItem, sizeof(XmString), Offset(combo_box.selected_item), CBGetSelectedItem, CBSetSelectedItem }, { XmNselectedPosition, sizeof(int), Offset(combo_box.selected_position), CBGetSelectedPos, CBSetSelectedPos }, { XmNmarginWidth, sizeof(Dimension), Offset(combo_box.margin_width), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { XmNmarginHeight, sizeof(Dimension), Offset(combo_box.margin_height), XmeFromVerticalPixels, XmeToVerticalPixels }, { XmNhighlightThickness, sizeof(Dimension), Offset(combo_box.highlight_thickness), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { XmNshadowThickness, sizeof(Dimension), Offset(manager.shadow_thickness), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { XmNarrowSize, sizeof(Dimension), Offset(combo_box.arrow_size), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { XmNarrowSpacing, sizeof(Dimension), Offset(combo_box.arrow_spacing), XmeFromHorizontalPixels, XmeToHorizontalPixels }, { XmNcolumns, sizeof(short), Offset(combo_box.columns), CBGetColumns, NULL }, { XmNitems, sizeof(XmStringTable), Offset(combo_box.items), CBGetItems, NULL }, { XmNitemCount, sizeof(int), Offset(combo_box.item_count), CBGetItemCount, NULL }, { XmNvisibleItemCount, sizeof(int), Offset(combo_box.visible_item_count), CBGetVisibleItemCount, NULL } }; #undef Offset static XtAccelerators parsed_accelerators; static XtAccelerators parsed_list_accelerators; static XtTranslations parsed_list_translations; static XtTranslations parsed_text_focus_translations; static XtActionsRec actionsList[] = { { "CBArmAndDropDownList", CBArmAndDropDownList }, { "CBDisarm", CBDisarm }, { "CBDropDownList", CBDropDownList }, { "CBFocusIn", CBFocusIn }, { "CBFocusOut", CBFocusOut }, { "CBCancel", CBCancel }, { "CBActivate", CBActivate }, { "CBListAction", CBListAction }, { "CBTextFocusOut", CBTextFocusOut } }; /* Class Record Initialization */ externaldef(xmcomboboxclassrec) XmComboBoxClassRec xmComboBoxClassRec = { { /* core_class fields */ (WidgetClass) &xmManagerClassRec, /* superclass */ "XmComboBox", /* class_name */ sizeof(XmComboBoxRec), /* widget_size */ ClassInitialize, /* class_initialize */ ClassPartInitialize, /* class_part_initialize */ FALSE, /* class_inited */ Initialize, /* initialize */ NULL, /* initialize_hook */ XtInheritRealize, /* realize */ actionsList, /* actions */ XtNumber(actionsList), /* num_actions */ (XtResourceList)resources, /* resources */ XtNumber(resources), /* num_resources */ NULLQUARK, /* xrm_class */ True, /* compress_motion */ XtExposeCompressMaximal | /* compress_exposure */ XtExposeNoRegion, FALSE, /* compress_enterleave */ FALSE, /* visible_interest */ Destroy, /* destroy */ Resize, /* resize */ Redisplay, /* expose */ SetValues, /* set_values */ NULL, /* set_values_hook */ XtInheritSetValuesAlmost, /* set_values_almost */ NULL, /* get_values_hook */ NULL, /* accept_focus */ XtVersion, /* version */ NULL, /* callback_private */ default_translations, /* tm_table */ QueryGeometry, /* Query Geometry proc */ NULL, /* disp accelerator */ NULL /* extension */ }, { /* composite_class fields */ GeometryManager, /* geometry_manager */ ChangeManaged, /* change_managed */ InsertChild, /* insert_child */ XtInheritDeleteChild, /* delete_child */ NULL /* extension */ }, { /* constraint_class fields */ NULL, /* constraint resource */ 0, /* number of constraints */ sizeof(XmManagerConstraintRec), /* size of constraint */ NULL, /* initialization */ ConstraintDestroy, /* destroy proc */ NULL, /* set_values proc */ NULL /* extension */ }, { /* manager_class fields */ XtInheritTranslations, /* translations */ syn_resources, /* syn_resources */ XtNumber(syn_resources), /* num_syn_resources */ NULL, /* syn_cont_resources */ 0, /* num_syn_cont_resources */ ComboBoxParentProcess, /* parent_process */ NULL /* extension */ }, { /* combo_box class fields */ NULL /* extension */ } }; externaldef(xmcomboboxwidgetclass) WidgetClass xmComboBoxWidgetClass = (WidgetClass) &xmComboBoxClassRec; /* ------------- WIDGET CLASS METHODS ---------- */ /* * Initialize() * Called by the Intrinsics when a new ComboBox is created. */ /*ARGSUSED*/ static void Initialize(Widget request, /* unused */ Widget new_w, ArgList args, Cardinal *num_args) { XmComboBoxWidget newcb = (XmComboBoxWidget)new_w; Widget ancestor; /* Setup internal state. */ CB_ShellState(newcb) = POPPED_DOWN; newcb->combo_box.scrolling = FALSE; newcb->combo_box.vsb = NULL; newcb->combo_box.hsb = NULL; CB_TextChanged(newcb) = FALSE; /* Set defaults */ CB_Highlighted(newcb) = FALSE; CB_ArrowPressed(newcb) = FALSE; CB_ScrolledW(newcb) = CB_ListShell(newcb) = 0; newcb->combo_box.ideal_ebwidth = newcb->combo_box.ideal_ebheight = 0; CB_HitRect(newcb).width = 0; CB_HitRect(newcb).height = 0; CB_HitRect(newcb).x = 0; CB_HitRect(newcb).y = 0; newcb->combo_box.arrow_shadow_width = newcb->manager.shadow_thickness; if (newcb->core.accelerators == NULL) newcb->core.accelerators = parsed_accelerators; /* Validate XmNpositionMode. */ if (!XmRepTypeValidValue(XmRID_POSITION_MODE, CB_PositionMode(newcb), (Widget) newcb)) { CB_PositionMode(newcb) = XmZERO_BASED; } /* The list child cannot be set. */ if (CB_List(newcb)) { CB_List(newcb) = NULL; XmeWarning(new_w, NOSETKIDS); } /* The editbox child cannot be set. */ if (CB_EditBox(newcb)) { CB_EditBox(newcb) = NULL; XmeWarning(new_w, NOSETKIDS); } /* Get arrow GC. */ GetArrowGC((Widget) newcb); if (CB_ArrowSpacing(newcb) == XmINVALID_DIMENSION) { CB_ArrowSpacing(newcb) = CB_MarginWidth(newcb); /* ArrowSize set dynamically in DoLayout() */ } if (CB_MatchBehavior(newcb) == XmINVALID_MATCH_BEHAVIOR) { if (CB_Type(newcb) == XmDROP_DOWN_LIST) CB_MatchBehavior(newcb) = XmQUICK_NAVIGATE; else CB_MatchBehavior(newcb) = XmNONE; } else if ((CB_MatchBehavior(newcb) == XmQUICK_NAVIGATE) && (CB_Type(newcb) != XmDROP_DOWN_LIST)) { CB_MatchBehavior(newcb) = XmNONE; XmeWarning(new_w, MBWARNING); } /* Get a real render table. */ if (CB_RenderTable(newcb) == NULL) CB_RenderTable(newcb) = XmeGetDefaultRenderTable(new_w, XmTEXT_FONTLIST); CB_RenderTable(newcb) = XmFontListCopy(CB_RenderTable(newcb)); /* Create the widgets that make up a ComboBox. */ CreateChildren(new_w, args, num_args); /* Walk up hierarchy to find vendor shell. */ ancestor = XtParent(new_w); while (ancestor && !XmIsVendorShell(ancestor)) ancestor = XtParent(ancestor); /* Setup focus moved callback so ComboBox can draw highlighting rect. */ if (ancestor) XmeAddFocusChangeCallback(ancestor, FocusMovedCB, (XtPointer) new_w); /* Since we cannot add children to a ComboBox, we can consider it as a Primitive, and set its preferred width/height. */ if (XtWidth(new_w) == 0 || XtHeight(new_w) == 0) ComputeSize((Widget) new_w, 0, 0, &(XtWidth(new_w)), &(XtHeight(new_w))); /* Reset synthetic child resources so SetValues can detect all changes. */ CBS_Columns(newcb) = XmUNSPECIFIED_COLUMNS; CBS_ItemCount(newcb) = XmUNSPECIFIED_COUNT; CBS_Items(newcb) = XmUNSPECIFIED_ITEMS; CBS_VisibleItemCount(newcb) = XmUNSPECIFIED_COUNT; /* Reset fields whose value we ignore after creation. */ CBS_SelectedPosition(newcb) = XmINVALID_POSITION; CBS_SelectedItem(newcb) = NULL; } /* * ClassInitialize() * This routine is invoked only once. */ static void ClassInitialize(void) { /* Parse the default translation and accelerator tables */ parsed_accelerators = XtParseAcceleratorTable(_XmComboBox_defaultAccelerators); parsed_list_accelerators = XtParseAcceleratorTable(_XmComboBox_dropDownComboBoxAccelerators); parsed_list_translations = XtParseTranslationTable(_XmComboBox_dropDownListTranslations); parsed_text_focus_translations = XtParseTranslationTable(_XmComboBox_textFocusTranslations); } /* * ClassPartInitialize() * Set up the fast subclassing. Invoked for every (sub)class of * ComboBox. */ static void ClassPartInitialize(WidgetClass wc) { _XmFastSubclassInit (wc, XmCOMBO_BOX_BIT); } /* * InsertChild() * Called by the intrinsics when a new child is added to a ComboBox. * Generate an error message if an application tries to insert widgets * into the ComboBox. */ static void InsertChild(Widget child) { XmComboBoxWidget cb = (XmComboBoxWidget) XtParent(child); XtWidgetProc insert_child; /* * If the ComboBox has already created its children, the application * is trying to add another child, which is an error. */ if (cb->composite.num_children > 2) XmeWarning((Widget)cb, NOKIDS); else { /* Call manager InsertChild to update child info. */ _XmProcessLock(); insert_child = ((XmManagerWidgetClass) xmManagerWidgetClass) ->composite_class.insert_child; _XmProcessUnlock(); (*insert_child)(child); } } /* * SetValues() * Called by Intrinsics when XtSetValues is called on the ComboBox. */ /*ARGSUSED*/ static Boolean SetValues(Widget current, Widget request, /* unused */ Widget new_w, ArgList user_args, /* unused */ Cardinal *num_args) /* unused */ { XmComboBoxWidget curcb = (XmComboBoxWidget)current; XmComboBoxWidget newcb = (XmComboBoxWidget)new_w; Boolean resize = FALSE; Boolean dolayout = FALSE; Boolean redisplay = FALSE; Cardinal nlist = 0; Arg list_args[10]; Cardinal nshell = 0; Arg shell_args[10]; Cardinal neditbox = 0; Arg editbox_args[10]; XmFontList old_render_table = NULL; /* The position_mode cannot be changed after creation. */ if (CB_PositionMode(curcb) != CB_PositionMode(newcb)) { CB_PositionMode(newcb) = CB_PositionMode(curcb); XmeWarning(current, MODEWARNING); } /* The ComboBox type cannot change after creation. */ if (CB_Type(curcb) != CB_Type(newcb)) { CB_Type(newcb) = CB_Type(curcb); XmeWarning(current, TYPEWARNING); } /* The list child cannot be set. */ if (CB_List(curcb) != CB_List(newcb)) { CB_List(newcb) = CB_List(curcb); XmeWarning(current, NOSETKIDS); } /* The editbox child cannot be set. */ if (CB_EditBox(curcb) != CB_EditBox(newcb)) { CB_EditBox(newcb) = CB_EditBox(curcb); XmeWarning(current, NOSETKIDS); } /* Validate XmNmatchBehavior and propagate it to the list. */ if (CB_MatchBehavior(curcb) != CB_MatchBehavior(newcb)) { if (CB_Type(curcb) != XmDROP_DOWN_LIST && CB_MatchBehavior(newcb) == XmQUICK_NAVIGATE) { CB_MatchBehavior(newcb) = CB_MatchBehavior(curcb); XmeWarning(current, MBWARNING); } else if (CB_Type(curcb) == XmDROP_DOWN_LIST) { XtSetArg(list_args[nlist], XmNmatchBehavior, CB_MatchBehavior(newcb)), nlist++; } } /* Propagate XmNcolumns to the edit box. */ if (CBS_Columns(newcb) != XmUNSPECIFIED_COLUMNS) { XtSetArg(editbox_args[neditbox], XmNcolumns, CBS_Columns(newcb)), neditbox++; CBS_Columns(newcb) = XmUNSPECIFIED_COLUMNS; resize = dolayout = redisplay = TRUE; } /* Propagate XmNitems to the list. */ if (CBS_Items(newcb) != XmUNSPECIFIED_ITEMS) { XtSetArg(list_args[nlist], XmNitems, CBS_Items(newcb)), nlist++; CBS_Items(newcb) = XmUNSPECIFIED_ITEMS; } /* Propagate XmNitemCount to the list. */ if (CBS_ItemCount(newcb) != XmUNSPECIFIED_COUNT) { XtSetArg(list_args[nlist], XmNitemCount, CBS_ItemCount(newcb)), nlist++; CBS_ItemCount(newcb) = XmUNSPECIFIED_COUNT; } /* Propagate XmNvisibleItemCount to the list. */ if (CBS_VisibleItemCount(newcb) != XmUNSPECIFIED_COUNT) { XtSetArg(list_args[nlist], XmNvisibleItemCount, CBS_VisibleItemCount(newcb)), nlist++; CBS_VisibleItemCount(newcb) = XmUNSPECIFIED_COUNT; resize = dolayout = redisplay = TRUE; } /* Propagate our XmNborderWidth to the popup list shell. */ if (XtBorderWidth(curcb) != XtBorderWidth(newcb)) { if (CB_Type(curcb) != XmCOMBO_BOX) { XtSetArg(shell_args[nshell], XmNborderWidth, XtBorderWidth(newcb)), nshell++; redisplay = TRUE; } } /* Propagate XmNrenderTable to all children. */ if (CB_RenderTable(curcb) != CB_RenderTable(newcb)) { if (CB_RenderTable(newcb) == NULL) CB_RenderTable(newcb) = XmeGetDefaultRenderTable(new_w, XmTEXT_FONTLIST); CB_RenderTable(newcb) = XmFontListCopy(CB_RenderTable(newcb)); XtSetArg(editbox_args[neditbox], XmNrenderTable, CB_RenderTable(newcb)), neditbox++; XtSetArg(list_args[nlist], XmNrenderTable, CB_RenderTable(newcb)), nlist++; /* Free the old render table after we update the children. */ old_render_table = CB_RenderTable(curcb); } /* Process resources that affect layout. */ if ((CB_HighlightThickness(curcb) != CB_HighlightThickness(newcb)) || (CB_MarginWidth(curcb) != CB_MarginWidth(newcb)) || (CB_MarginHeight(curcb) != CB_MarginHeight(newcb)) || (CB_RenderTable(curcb) != CB_RenderTable(newcb))) { resize = dolayout = redisplay = TRUE; } if (MGR_ShadowThickness(curcb) != MGR_ShadowThickness(newcb)) { resize = dolayout = redisplay = TRUE; if (CB_Type(newcb) != XmDROP_DOWN_LIST) { XtSetArg(editbox_args[neditbox], XmNshadowThickness, MGR_ShadowThickness(newcb)), neditbox++; } if (CB_Type(curcb) != XmCOMBO_BOX) { XtSetArg(shell_args[nshell], XmNshadowThickness, MGR_ShadowThickness(newcb)), nshell++; } } if ((CB_ArrowSpacing(curcb) != CB_ArrowSpacing(newcb)) || (CB_ArrowSize(curcb) != CB_ArrowSize(newcb))) { if (CB_Type(curcb) != XmCOMBO_BOX) { resize = dolayout = redisplay = TRUE; } } /* Propagate shadow GC changes to the popup list shell. */ if ((curcb->manager.top_shadow_color != newcb->manager.top_shadow_color) || (curcb->manager.top_shadow_pixmap != newcb->manager.top_shadow_pixmap) || (curcb->manager.bottom_shadow_color != newcb->manager.bottom_shadow_color) || (curcb->manager.bottom_shadow_pixmap != newcb->manager.bottom_shadow_pixmap)) { if (CB_Type(curcb) != XmCOMBO_BOX) { XtSetArg(shell_args[nshell], XmNtopShadowColor, newcb->manager.top_shadow_color), nshell++; XtSetArg(shell_args[nshell], XmNbottomShadowColor, newcb->manager.bottom_shadow_color), nshell++; XtSetArg(shell_args[nshell], XmNtopShadowPixmap, newcb->manager.top_shadow_pixmap), nshell++; XtSetArg(shell_args[nshell], XmNbottomShadowPixmap, newcb->manager.bottom_shadow_pixmap), nshell++; } } if (XtBackground(curcb) != XtBackground(newcb)) { if (CB_Type(newcb) != XmCOMBO_BOX) { XtReleaseGC((Widget) newcb, newcb->combo_box.arrow_GC); GetArrowGC((Widget) newcb); /* Core won't request redisplay if a background pixmap is used. */ redisplay = TRUE; } } if (XtIsSensitive((Widget)curcb) != XtIsSensitive((Widget)newcb)) { redisplay = TRUE; } /* Push all the accrued updates onto the children. */ assert(nshell <= XtNumber(shell_args)); if (nshell && CB_ListShell(newcb)) XtSetValues(CB_ListShell(newcb), shell_args, nshell); assert(nlist <= XtNumber(list_args)); if (nlist && CB_List(newcb)) XtSetValues(CB_List(newcb), list_args, nlist); assert(neditbox <= XtNumber(editbox_args)); if (neditbox && CB_EditBox(newcb)) XtSetValues(CB_EditBox(newcb), editbox_args, neditbox); if (old_render_table) XmFontListFree(old_render_table); /* Recompute our ideal size. */ if ( resize) { if (XtIsRealized((Widget)newcb)) XtWidth(newcb) = XtHeight(newcb) = 0; ComputeSize((Widget)newcb, 0, 0, &(XtWidth(newcb)), &(XtHeight(newcb))); } /* * Recompute our layout, if realized. * We could check and not resize and add a SetValuesAlmost (like the * one in Label) that would call Resize if the parent denies the request. */ if (XtIsRealized((Widget)new_w) && dolayout) DoLayout(new_w); return redisplay; } /* ReduceResources() * Called from ChangeManaged when some resources need to be * diminished in order to satisfy a resource request. */ /*ARGSUSED*/ static Boolean ReduceResources(Widget widget, Dimension *width, Dimension *height, Mask flags) { XmComboBoxWidget newcb = (XmComboBoxWidget)widget; int delta; /* Space is stolen from resources in this order: * MarginHeight, MarginWidth, ArrowSpacing, * ShadowThickness, HighlightThickness */ /* Reduce width. Try resources in order. */ if (*width) { if (!(flags & MARGIN_WIDTH)) *width -= Reduce(&CB_MarginWidth(newcb), *width, 0); } if (*width) { if (!(flags & ARROW_SPACING)) { assert(CB_Type(newcb) != XmCOMBO_BOX); *width -= Reduce(&CB_ArrowSpacing(newcb), *width, MINIMUM_ARROWSPACE); } } if (*width) { if (!(flags & SHADOW_THICKNESS)) { delta = Reduce(&MGR_ShadowThickness(newcb), *width, MINIMUM_SHADOWTHICKNESS); *width -= delta; /* This affects height too. */ if (*height) Reduce(height, delta, 0); /* Keep the shell's shadowThickness in sync. */ if (CB_Type(newcb) != XmCOMBO_BOX) { Arg args[1]; Cardinal n = 0; XtSetArg(args[n], XmNshadowThickness, MGR_ShadowThickness(newcb)), n++; assert(n <= XtNumber(args)); XtSetValues(CB_ListShell(newcb), args, n); } } } if (*width) { if (!(flags & HIGHLIGHT_THICKNESS)) { delta = Reduce(&CB_HighlightThickness(newcb), *width, MINIMUM_HIGHLIGHTTHICKNESS); *width -= delta; /* This affects height too. */ if (*height) Reduce(height, delta, 0); } } /* Reduce height. Try resouces in order. */ if (*height) { if (!(flags & MARGIN_HEIGHT)) *height -= Reduce(&CB_MarginHeight(newcb), *height, 0); } if (*height) { if (!(flags & SHADOW_THICKNESS)) *height -= Reduce(&MGR_ShadowThickness(newcb), *height, MINIMUM_SHADOWTHICKNESS); } if (*height) { if (!(flags & HIGHLIGHT_THICKNESS)) *height -= Reduce(&CB_HighlightThickness(newcb), *height, MINIMUM_HIGHLIGHTTHICKNESS); } return (!*width && !*height); } /* * Reduce() * A helper for ReduceResources, this routine will decrement a * single resource. */ static int Reduce(Dimension *size, int max_change, int min_size) { int change = 0; if (*size > min_size) { change = MIN(*size - min_size, max_change); *size -= change; } return change; } /* * Resize() * Sizes and places the children of the ComboBox depending on its * type: XmCOMBO_BOX, XmDROP_DOWN_COMBO_BOX, or XmDROP_DOWN_LIST. * The instrinsics call Expose() immediately after calling this method. */ static void Resize(Widget widget) { /* CR 6911: Need to erase old margins, arrow spacing, and arrow areas. */ /* CR 7255: Erase old margins too. */ if (XtIsRealized(widget)) XClearWindow (XtDisplay(widget), XtWindow(widget)); DoLayout(widget); } static void CheckMinimalSize( Widget widget, Dimension * pwidth, Dimension * pheight) { XmComboBoxWidget cb = (XmComboBoxWidget) widget; Dimension min_height, min_width; min_height = min_width = 2 * (MINIMUM_SHADOWTHICKNESS + MINIMUM_HIGHLIGHTTHICKNESS) + MINTXT; if (CB_Type(cb) != XmCOMBO_BOX) { if (CB_ArrowSize(cb) == XmINVALID_DIMENSION) CB_ArrowSize(cb) = GetDefaultArrowSize((Widget)cb); min_width += MINIMUM_ARROWSPACE + (int)CB_ArrowSize(cb); } else min_height += MINLIST; *pwidth = MAX(min_width, *pwidth); *pheight = MAX(min_height, *pheight); } /* * ChangeManaged() * Called whenever there is a change in the managed set. */ static void ChangeManaged(Widget widget) { XmComboBoxWidget cb = (XmComboBoxWidget) widget; XtWidgetGeometry desired; Dimension thickW, thickH, width, height; Dimension widthXcess = 0, heightXcess = 0; desired.request_mode = 0; if (!XtIsRealized((Widget)cb)) { /* Only attempt to change non-specified sizes. */ desired.width = XtWidth(cb); /* might be 0 */ desired.height = XtHeight(cb); /* might be 0 */ } else { desired.width = 0; desired.height = 0; } /* Compute our desired size. * If user has set width or height, use it * else use preferred sized of children */ if (desired.width == 0 || desired.height == 0) ComputeSize((Widget)cb, 0, 0, &desired.width, &desired.height); else CheckMinimalSize((Widget)cb, &desired.width, &desired.height); /* Make request to parent*/ desired.request_mode = (CWWidth | CWHeight); _XmMakeGeometryRequest((Widget) cb, &desired); /* Determine if not enough space due to resources requested */ GetThickness((Widget) cb, &thickW, &thickH); width = 2*thickW + MINTXT; height = 2*thickH + MINTXT; if (CB_Type(cb) != XmCOMBO_BOX) width += CB_ArrowSize(cb) + CB_ArrowSpacing(cb); else height += MINLIST; if (width > XtWidth(cb)) widthXcess = width - XtWidth(cb); if (height > XtHeight(cb)) heightXcess = height - XtHeight(cb); if (widthXcess || heightXcess) { /* take it off the resources * this will reduce each to their minimum value if needed */ if (widthXcess || heightXcess) ReduceResources((Widget)cb, &widthXcess, &heightXcess, 0); } /* Layout the widget */ DoLayout(widget); } /* * GeometryManager() * Handle geometry change requests from our children. */ static XtGeometryResult GeometryManager(Widget mychild, XtWidgetGeometry *request, XtWidgetGeometry *reply) { XmComboBoxWidget cb = (XmComboBoxWidget) XtParent(mychild); XtWidgetGeometry my_request; int width, height ; /* make them int so that we deal with negative */ XtGeometryResult res ; Dimension almost_list_width = 0 ; /* * Reject anything requests a change of position. We don't even * consider "almosting" what goes with it, i.e. a size request. */ if (request->request_mode & (CWX | CWY)) return XtGeometryNo ; my_request.request_mode = 0; width = XtWidth(cb) ; height = XtHeight(cb) ; /* * Try to honor a child's request to change size by passing the * decision to our parent. For now we don't compromise or check * the other child's preferred size, only our own minimal size. */ if (request->request_mode & XtCWQueryOnly) my_request.request_mode |= XtCWQueryOnly; if (request->request_mode & CWWidth) { my_request.request_mode |= CWWidth; width += (int)request->width - (int)XtWidth(mychild); if (mychild == CB_EditBox(cb)) cb->combo_box.ideal_ebwidth = request->width; else { /* * Compromise rather than letting the list shrink smaller * than the Text's preferred width. */ Dimension thickW, thickH; GetThickness((Widget)cb, &thickW, &thickH); if (!cb->combo_box.ideal_ebwidth) GetIdealTextSize((Widget)cb, &cb->combo_box.ideal_ebwidth, NULL); if (width < cb->combo_box.ideal_ebwidth + 2 * (thickW + XtBorderWidth(CB_EditBox(cb)))) { width = cb->combo_box.ideal_ebwidth + 2 * (thickW + XtBorderWidth(CB_EditBox(cb))) ; almost_list_width = cb->combo_box.ideal_ebwidth ; my_request.request_mode |= XtCWQueryOnly; } } } if (request->request_mode & CWHeight) { my_request.request_mode |= CWHeight; height += (int)request->height - (int)XtHeight(mychild); if (mychild == CB_EditBox(cb)) cb->combo_box.ideal_ebheight = request->height; } if (request->request_mode & CWBorderWidth) { my_request.request_mode |= (CWWidth | CWHeight); width += 2*((int)request->border_width - (int)XtBorderWidth(mychild)); height += 2*((int)request->border_width - (int)XtBorderWidth(mychild)); } if (width > 0) my_request.width = (Dimension) width ; if (height > 0) my_request.height = (Dimension) height ; CheckMinimalSize((Widget)cb, &my_request.width, &my_request.height); res = XtMakeGeometryRequest((Widget)cb, &my_request, NULL); if (res == XtGeometryYes) { if (!(my_request.request_mode & XtCWQueryOnly)) { XtWidgetProc resize; if (request->request_mode & CWWidth) XtWidth(mychild) = request->width; if (request->request_mode & CWHeight) XtHeight(mychild) = request->height; if (request->request_mode & CWBorderWidth) XtBorderWidth(mychild) = request->border_width; _XmProcessLock(); resize = XtCoreProc(cb, resize); _XmProcessUnlock(); (*resize)((Widget) cb) ; } else if (almost_list_width) { reply->request_mode = request->request_mode ; reply->width = almost_list_width ; reply->height = request->height ; reply->border_width = request->border_width ; return XtGeometryAlmost ; } return XtGeometryYes; } else return XtGeometryNo; } /* * Redisplay() * General redisplay function called on exposure events. */ /*ARGSUSED*/ static void Redisplay(Widget widget, XEvent *event, /* unused */ Region region) /* unused */ { XmComboBoxWidget cb = (XmComboBoxWidget) widget; if (XtIsRealized(widget)) { if (CB_Type(cb) != XmCOMBO_BOX) DrawArrow(widget, CB_ArrowPressed(cb)); DrawShadows(widget); /* CR 6356: Refresh the highlight border too. */ if (CB_Highlighted(cb)) HighlightBorder(widget); else UnhighlightBorder(widget); } } /* * Destroy() * Called by the Intrinsics when a ComboBox widget is destroyed. */ static void Destroy(Widget widget) { XmComboBoxWidget cb = (XmComboBoxWidget) widget; Widget ancestor; if (CB_Type(widget) != XmCOMBO_BOX) XtRemoveEventHandler(widget, POPUP_EVENT_MASK, FALSE, PopupEH, (XtPointer)widget); /* The remove focus moved callback from the vendor shell. */ ancestor = widget; while (ancestor && !XmIsVendorShell(ancestor)) ancestor = XtParent(ancestor); if (ancestor && !ancestor->core.being_destroyed) XmeRemoveFocusChangeCallback(ancestor, FocusMovedCB, (XtPointer) widget); if (cb->combo_box.arrow_GC) XtReleaseGC(widget, cb->combo_box.arrow_GC); XmFontListFree(CB_RenderTable(cb)); } /* * QueryGeometry() */ static XtGeometryResult QueryGeometry(Widget widget, XtWidgetGeometry *intended, XtWidgetGeometry *desired) { /* Deal with user initial size setting. */ if (!XtIsRealized(widget)) { desired->width = XtWidth(widget); /* might be 0 */ desired->height = XtHeight(widget); /* might be 0 */ } else { /* Always computes natural size afterwards. */ desired->width = 0; desired->height = 0; } ComputeSize(widget, 0, 0, &desired->width, &desired->height); /* This function will set CWidth and CHeight. */ return XmeReplyToQueryGeometry(widget, intended, desired); } /* * ConstraintDestroy() */ static void ConstraintDestroy(Widget child) { XmComboBoxWidget cb; if (!XtIsRectObj (child)) return; cb = (XmComboBoxWidget) XtParent(child); if (child == CB_EditBox(cb)) { CB_EditBox(cb) = NULL; } else if (child == CB_ScrolledW(cb)) { CB_ScrolledW(cb) = NULL; CB_List(cb) = NULL; cb->combo_box.vsb = NULL; cb->combo_box.hsb = NULL; } else if (child == CB_ListShell(cb)) { CB_ListShell(cb) = NULL; CB_ScrolledW(cb) = NULL; CB_List(cb) = NULL; cb->combo_box.vsb = NULL; cb->combo_box.hsb = NULL; } } /* * ComboBoxParentProcess() */ static Boolean ComboBoxParentProcess(Widget wid, XmParentProcessData event) { XmComboBoxWidget cb = (XmComboBoxWidget) wid; XmGrabShellWidget gs = NULL; Boolean cont = TRUE; int count = 0; Cardinal n = 0; Arg args[2]; if ((event->any.process_type == XmINPUT_ACTION) && ((event->input_action.action == XmPARENT_ACTIVATE) || (event->input_action.action == XmPARENT_CANCEL))) { if (CB_Type(cb) != XmCOMBO_BOX) { gs = (XmGrabShellWidget)CB_ListShell(cb); if (gs && (CB_ShellState(cb) == POPPED_UP)) { PopdownList((Widget)cb, event->input_action.event); CBDisarm((Widget)cb, event->input_action.event, NULL, NULL); cont = FALSE; } } if (event->input_action.action == XmPARENT_ACTIVATE) { /* * If the event occurred in the text widget and the popup shell * is up, call the defaultAction callbacks on the List. * We can't call the action routine for List that will do this * because it will pass the Activate back to us and we'll end * up looping. Only call them if there are items in the list - * this is what list does in KbdActivate(). */ /* CR 9484: Always compute pos. */ XmString item = GetEditBoxValue((Widget) cb); int pos = XmListItemPos(CB_List(cb), item); n = 0; XtSetArg(args[n], XmNitemCount, &count), n++; assert(n <= XtNumber(args)); XtGetValues(CB_List(cb), args, n); if (((CB_Type(cb) == XmCOMBO_BOX) || !cont) && count) { XmListCallbackStruct call_data; bzero((char*) &call_data, sizeof(XmListCallbackStruct)); if (pos) { call_data.item = XmStringCopy(item); call_data.item_length = XmStringLength(item); call_data.item_position = pos; call_data.selected_item_count = 1; call_data.selected_item_positions = &pos; call_data.selected_items = &item; } call_data.reason = XmCR_DEFAULT_ACTION; call_data.event = event->input_action.event; XtCallCallbacks(CB_List(cb), XmNdefaultActionCallback, (XtPointer)&call_data); XmStringFree(call_data.item); } XmStringFree(item); /* CR 6552: Fixup the list selection. */ if (pos) XmListSelectPos(CB_List(cb), pos, FALSE); else XmListDeselectAllItems(CB_List(cb)); /* Call the selection callbacks. */ CallSelectionCallbacks((Widget)cb, event->input_action.event); } } if (cont) return _XmParentProcess(XtParent(cb), event); else return True; } /* ------------- CALLBACKS ---------- */ /* * TextChangedCB() * Callback function invoked when the edit box value is changed. */ /*ARGSUSED*/ static void TextChangedCB(Widget widget, /* unused */ XtPointer client_data, XtPointer call_data) /* unused */ { XmComboBoxWidget cb = (XmComboBoxWidget)client_data; /* Remember to generate a selection callback when focus moves. */ CB_TextChanged(cb) = TRUE; } /* * ListSelectionCB() * Callback function called when an item is selected from the list. */ /*ARGSUSED*/ static void ListSelectionCB(Widget widget, /* unused */ XtPointer client_data, XtPointer call_data) { XmComboBoxWidget cb = (XmComboBoxWidget)client_data; XmString item; int top, vis_items; Cardinal n; Arg args[3]; XmListCallbackStruct *cb_data = (XmListCallbackStruct *)call_data; if (!CB_EditBox(cb)) { XmeWarning((Widget)cb, MISSINGKID); return; } /* If the EditBox does not contain the selected text, set it. */ item = GetEditBoxValue((Widget) cb); if (! XmStringCompare(item, cb_data->item)) SetEditBoxValue((Widget) cb, cb_data->item); XmStringFree(item); /* If the selected item is not viewable, scroll the list. */ n = 0; XtSetArg(args[n], XmNtopItemPosition, &top), n++; XtSetArg(args[n], XmNvisibleItemCount, &vis_items), n++; assert(n <= XtNumber(args)); XtGetValues(CB_List(cb), args, n); if ((cb_data->item_position < top) || (cb_data->item_position >= (top + vis_items))) XmListSetBottomItem(CB_List(cb), cb_data->item); CallSelectionCallbacks((Widget)cb, cb_data->event); if (cb_data->event && (cb_data->event->type == ButtonPress || cb_data->event->type == ButtonRelease)) { if (CB_Type(cb) != XmCOMBO_BOX) { PopdownList((Widget)cb, cb_data->event); /* CR 6147: If we drag straight from the arrow to a list */ /* item we won't get a normal disarm callback. */ CBDisarm((Widget)cb, cb_data->event, NULL, NULL); } } } /* * ShellPopupCB() * Called when the grab shell is posted. Fixup focus. */ /*ARGSUSED*/ static void ShellPopupCB(Widget widget, /* unused */ XtPointer client_data, XtPointer call_data) /* unused */ { XmComboBoxWidget cb = (XmComboBoxWidget)client_data; CB_ShellState(cb) = POPPED_UP; (void) XmProcessTraversal(CB_List(cb), XmTRAVERSE_CURRENT); } /* * ShellPopdownCB() * Called when the grab shell is unposted. */ /*ARGSUSED*/ static void ShellPopdownCB(Widget widget, /* unused */ XtPointer client_data, XtPointer call_data) /* unused */ { XmComboBoxWidget cb = (XmComboBoxWidget)client_data; XmDisplay disp = (XmDisplay) XmGetXmDisplay(XtDisplay(cb)); Window old_focus; int old_revert; /* CR 9887: List may not have seen the BtnUp event... */ XtCallActionProc(CB_List(cb), "ListKbdCancel", NULL, NULL, 0); /* Re-enable drag and drop. */ disp->display.userGrabbed = False; CB_ShellState(cb) = POPPED_DOWN; cb->combo_box.scrolling = FALSE; /* In click-to-type mode we can't lose focus, and won't get a */ /* focus-in event when the grabshell pops down. */ XGetInputFocus(XtDisplay(cb), &old_focus, &old_revert); if (old_revert != RevertToParent) { CBFocusOut((Widget)cb, NULL, NULL, NULL); /* CR 7122: Tell the edit box to stop blinking now. */ if (CB_Type(cb) == XmDROP_DOWN_COMBO_BOX) { XEvent focus_event; focus_event.xfocus.type = FocusOut; focus_event.xfocus.send_event = True; XtCallActionProc(CB_EditBox(cb), "focusOut", &focus_event, NULL, 0); } } } /* * FocusMovedCB() * Called from VendorShell every time focus moves. */ /*ARGSUSED*/ static void FocusMovedCB(Widget widget, /* unused */ XtPointer client_data, XtPointer call_data) { XmFocusMovedCallbackStruct *callback = (XmFocusMovedCallbackStruct *) call_data; XmComboBoxWidget cb = (XmComboBoxWidget) client_data; Boolean have_focus, getting_focus; /* Some other focus-moved callback has aborted this shift. */ if (!callback->cont) return; have_focus = CB_Highlighted(cb); getting_focus = (((callback->new_focus == NULL) && (CB_ShellState(cb) != POPPED_DOWN)) || (callback->new_focus == (Widget)cb) || (callback->new_focus == CB_EditBox(cb)) || (callback->new_focus == CB_ScrolledW(cb)) || (callback->new_focus == CB_List(cb)) || ((cb->combo_box.hsb != NULL) && (callback->new_focus == cb->combo_box.hsb)) || ((cb->combo_box.vsb != NULL) && (callback->new_focus == cb->combo_box.vsb))); /* CR 9868: In XmPOINTER mode focus goes to weird places. */ if (!getting_focus && (_XmGetFocusPolicy((Widget) cb) == XmPOINTER)) { Window root, child; int root_x, root_y, win_x, win_y; unsigned int mod_mask; if (CB_ShellState(cb) == POPPED_UP) getting_focus = TRUE; else if ((callback->new_focus == XtParent(cb)) && XQueryPointer(XtDisplay(cb), XtWindow(cb), &root, &child, &root_x, &root_y, &win_x, &win_y, &mod_mask) && (win_x >= 0) && (win_x < XtWidth(cb)) && (win_y >= 0) && (win_y < XtHeight(cb))) getting_focus = TRUE; } if (have_focus && !getting_focus) { CBFocusOut((Widget)cb, callback->event, NULL, NULL); /* Is this necessary? */ if (CB_ShellState(cb) == POPPED_UP) { PopdownList((Widget)cb, callback->event); CBDisarm((Widget)cb, callback->event, NULL, NULL); } } else if (!have_focus && getting_focus) { CBFocusIn((Widget)cb, callback->event, NULL, NULL); } } /* ------------- ACTION ROUTINES ---------- */ /* * CBArmAndDropDownList() * Handle button down action in widget. First determine if hit * is in an arrow, otherwise ignore the event. If it is in an arrow, * then supply visual feedback and perform the appropriate action. */ /*ARGSUSED*/ static void CBArmAndDropDownList(Widget widget, XEvent *event, String *params, /* unused */ Cardinal *num_params) /* unused */ { XmComboBoxWidget cb = FindComboBox(widget); XmGrabShellWidget gs = (XmGrabShellWidget)CB_ListShell(cb); /* Return if this is a replay of the unpost event. */ if (gs && (event->xbutton.time == gs->grab_shell.unpost_time)) return; /* Ignore the event if this is a replay */ if (! _XmIsEventUnique(event)) return; if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } /* CR 9833: Attempt to grab focus. */ XmProcessTraversal((Widget)cb, XmTRAVERSE_CURRENT); if ((CB_Type(cb) != XmCOMBO_BOX) && Hit((XButtonEvent *)event, CB_HitRect(cb))) { CB_ArrowPressed(cb) = TRUE; DrawArrow((Widget)cb, CB_ArrowPressed(cb)); CBDropDownList((Widget)cb, event, NULL, NULL); } } /* * CBDisarm() * Handle button up action and undo CBArmAndDropDownList. */ /*ARGSUSED*/ static void CBDisarm(Widget widget, XEvent *event, /* unused */ String *params, /* unused */ Cardinal *num_params) /* unused */ { XmComboBoxWidget cb = FindComboBox(widget); if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } if (CB_Type(cb) != XmCOMBO_BOX) { if (CB_ArrowPressed(cb)) { CB_ArrowPressed(cb) = FALSE; DrawArrow((Widget)cb, CB_ArrowPressed(cb)); } } } /* * CBDropDownList() * Action to post/unpost the drop down list. */ static void CBDropDownList(Widget widget, XEvent *event, String *params, Cardinal *num_params) { XmComboBoxWidget cb = FindComboBox(widget); if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } if ((CB_Type(cb) != XmCOMBO_BOX)) { XmGrabShellWidget gs = (XmGrabShellWidget)CB_ListShell(cb); if (gs && (CB_ShellState(cb) == POPPED_DOWN)) { XmDisplay disp = (XmDisplay) XmGetDisplay(widget); Arg args[3]; Cardinal n; int tmp; Position root_x, root_y, shell_x, shell_y; Dimension shell_width; XtTranslateCoords((Widget)cb, XtX(cb), XtY(cb), &root_x, &root_y); shell_x = root_x - XtX(cb) + CB_HighlightThickness(cb) - XtBorderWidth(CB_ListShell(cb)); shell_y = root_y + XtHeight(cb) - CB_HighlightThickness(cb) - XtY(cb); /* Try to position the shell on the screen. */ tmp = WidthOfScreen(XtScreen(cb)) - XtWidth(CB_ListShell(cb)); tmp = MIN(tmp, shell_x); shell_x = MAX(0, tmp); tmp = HeightOfScreen(XtScreen(cb)) - XtHeight(CB_ListShell(cb)); tmp = MIN(tmp, shell_y); shell_y = MAX(0, tmp); /* CR 8446: The shell width may have changed unexpectedly. */ shell_width = XtWidth(cb) - 2 * CB_HighlightThickness(cb); n = 0; XtSetArg(args[n], XmNx, shell_x), n++; XtSetArg(args[n], XmNy, shell_y), n++; XtSetArg(args[n], XmNwidth, shell_width), n++; assert(n <= XtNumber(args)); XtSetValues(CB_ListShell(cb), args, n); CB_ShellState(cb) = POPPING_UP; cb->combo_box.scrolling = FALSE; /* Don't let drag and drop confuse things. */ disp->display.userGrabbed = True; /* Record the post time */ gs->grab_shell.post_time = event->xbutton.time; /* Record the event to prevent popdown on replay */ _XmRecordEvent(event); _XmPopupSpringLoaded(CB_ListShell(cb)); } else /* shell is popped up */ { PopdownList((Widget)cb, event); CBDisarm((Widget)cb, event, params, num_params); } } } /* * CBFocusIn() * Action routine to draw focus highlighting. */ /*ARGSUSED*/ static void CBFocusIn(Widget widget, XEvent *event, /* unused */ String *params, /* unused */ Cardinal *num_params) /* unused */ { XmComboBoxWidget cb = FindComboBox(widget); if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } HighlightBorder((Widget)cb); } /* * CBFocusOut() * Action routine to erase focus highlighting. */ /*ARGSUSED*/ static void CBFocusOut(Widget widget, XEvent *event, String *params, /* unused */ Cardinal *num_params) /* unused */ { XmComboBoxWidget cb = FindComboBox(widget); if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } UnhighlightBorder((Widget)cb); if (CB_TextChanged(cb)) CallSelectionCallbacks((Widget)cb, event); } /* * CBTextFocusOut() * Action routine to fake the text field cursor into blinking. */ /*ARGSUSED*/ static void CBTextFocusOut(Widget widget, XEvent *event, String *params, Cardinal *num_params) { XmComboBoxWidget cb = FindComboBox(widget); if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } /* CR 7122: Suppress text focus-out events when the grab shell is */ /* posted, so that the insertion cursor will continue to blink. */ if ((CB_Type(cb) != XmDROP_DOWN_COMBO_BOX) || (CB_ShellState(cb) != POPPED_UP)) { XtCallActionProc(CB_EditBox(cb), "focusOut", event, params, (num_params ? *num_params : 0)); } } /* * CBActivate() * Action routine called when the list is activated. */ static void CBActivate(Widget widget, XEvent *event, String *params, Cardinal *num_params) { XmComboBoxWidget cb = FindComboBox(widget); XmParentInputActionRec p_event; if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } p_event.process_type = XmINPUT_ACTION; p_event.action = XmPARENT_ACTIVATE; p_event.event = event; p_event.params = params; p_event.num_params = num_params; ComboBoxParentProcess((Widget)cb, (XmParentProcessData) &p_event); } /* * CBCancel() * Action invoked from the drop down list. */ static void CBCancel(Widget widget, XEvent *event, String *params, Cardinal *num_params) { XmComboBoxWidget cb = FindComboBox(widget); XmParentInputActionRec p_event; if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } p_event.process_type = XmINPUT_ACTION; p_event.action = XmPARENT_CANCEL; p_event.event = event; p_event.params = params; p_event.num_params = num_params; ComboBoxParentProcess((Widget)cb, (XmParentProcessData) &p_event); } /* * CBListAction() * Generic action to perform operations on the list. */ static void CBListAction(Widget widget, XEvent *event, String *params, Cardinal *num_params) { /* This enum matches the order of the string constants in RepType.c */ enum { UP, DOWN, PREVPAGE, NEXTPAGE, BEGINDATA, ENDDATA }; XmComboBoxWidget cb = FindComboBox(widget); int direction; if (!cb) { XmeWarning((Widget)cb, WRONGWIDGET); return; } if (!num_params || (*num_params != 1) || !params) { XmeWarning((Widget) cb, WRONGPARAMS); return; } if (_XmConvertActionParamToRepTypeId ((Widget) cb, XmRID_COMBO_BOX_LIST_ACTION_ACTION_PARAMS, params[0], False, &direction) == False) { /* Unknown value. A warning should already have been printed. */ return; } switch (direction) { case UP: case DOWN: { int *pos, count, num_items; Cardinal n; Arg args[3]; /* Find the current number of items and selected position. */ n = 0; XtSetArg(args[n], XmNitemCount, &num_items), n++; XtSetArg(args[n], XmNselectedPositions, &pos), n++; XtSetArg(args[n], XmNselectedPositionCount, &count), n++; assert(n <= XtNumber(args)); XtGetValues(CB_List(cb), args, n); if (count) { switch (direction) { case UP: if (*pos >= 1) XmListSelectPos(CB_List(cb), *pos - 1, TRUE); break; case DOWN: if (*pos < num_items) XmListSelectPos(CB_List(cb), *pos + 1, TRUE); else if (*pos == num_items) XmListSelectPos(CB_List(cb), 1, TRUE); break; default: assert(FALSE); } } else if (num_items) { XmListSelectPos(CB_List(cb), 1, TRUE); } } break; case PREVPAGE: if ((CB_Type(cb) == XmCOMBO_BOX) || (CB_ShellState(cb) == POPPED_UP)) XtCallActionProc(CB_List(cb), "ListPrevPage", event, NULL, 0); break; case NEXTPAGE: if ((CB_Type(cb) == XmCOMBO_BOX) || (CB_ShellState(cb) == POPPED_UP)) XtCallActionProc(CB_List(cb), "ListNextPage", event, NULL, 0); break; case BEGINDATA: XtCallActionProc(CB_List(cb), "ListBeginData", event, NULL, 0); break; case ENDDATA: XtCallActionProc(CB_List(cb), "ListEndData", event, NULL, 0); break; default: assert(FALSE); } } /* * PopdownList() * Internal utility to unpost the grabshell. */ static Boolean PopdownList(Widget cb, XEvent *event) { Widget gs = CB_ListShell(cb); /* Popping down while in the process of popping up causes X errors. */ if (gs && XmIsGrabShell(gs) && (CB_ShellState(cb) == POPPED_UP)) { CB_ShellState(cb) = POPPING_DOWN; XtCallActionProc(gs, "GrabShellPopdown", event, NULL, 0); return TRUE; } return FALSE; } /* ------------- EVENT HANDLERS ---------- */ /* * PopupEH() * An XtEventHandler for the popup shell (the drop-down list). */ /*ARGSUSED*/ static void PopupEH(Widget widget, /* unused */ XtPointer client_data, XEvent *event, Boolean *dispatch) { XmComboBoxWidget cb = (XmComboBoxWidget)client_data; switch (event->type) { case ButtonRelease: CBDisarm((Widget)cb, event, NULL, NULL); /* CR 9899: Only discard matched pairs of scrollbar button events. */ /* Should combo_box.scrolling be a counter??? */ if (cb->combo_box.scrolling) *dispatch = cb->combo_box.scrolling = FALSE; break; case ButtonPress: /* Press & release in the scrollbar shouldn't popdown the list. */ if ((cb->combo_box.vsb && XtIsRealized(cb->combo_box.vsb) && (event->xbutton.window == XtWindow(cb->combo_box.vsb))) || (cb->combo_box.hsb && XtIsRealized(cb->combo_box.hsb) && (event->xbutton.window == XtWindow(cb->combo_box.hsb)))) cb->combo_box.scrolling = TRUE; break; case EnterNotify: if (CB_ArrowPressed(cb)) XtCallActionProc(CB_List(cb), "ListBeginSelect", event, NULL, 0); break; default: /* This shouldn't happen. */ break; } } /* * CR 6925: The following two event handlers are used to coordinate * grabs between the scrollbar and the grab shell in dropdown lists. * In this case the existing active grab started by the grab shell * will interfere with the passive grab started by X when the user * presses a button within the scrollbar. * * To deal with the problem, SBBtnDownEH will do an XtGrabPointer * to transfer the grab to the scrollbar and SBBtnUpEH will cause * the grab to return to the grab shell. */ /*ARGSUSED*/ static void SBBtnDownEH(Widget w, XtPointer client_data, XEvent *event, Boolean *cont) /* unused */ { XmGrabShellWidget shell = (XmGrabShellWidget) client_data; XtGrabPointer(w, False, Events | PointerMotionMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, None, shell->grab_shell.cursor, event->xbutton.time); } /*ARGSUSED*/ static void SBBtnUpEH(Widget w, /* unused */ XtPointer client_data, XEvent *event, Boolean *cont) /* unused */ { XmGrabShellWidget shell = (XmGrabShellWidget) client_data; /* Note that this regrab to the grab shell will need to be changed * if the kind of grab that the grabshell imposes changes. */ XtGrabPointer((Widget) shell, shell->grab_shell.owner_events, Events, shell->grab_shell.grab_style, GrabModeAsync, None, shell->grab_shell.cursor, event->xbutton.time); if (shell->grab_shell.grab_style == GrabModeSync) XAllowEvents(XtDisplay(shell), SyncPointer, event->xbutton.time); } /* * FindComboBox() * An internal utility routine to traverse up the widget * hierarchy until a ComboBox is found. */ static XmComboBoxWidget FindComboBox(Widget widget) { Widget cb = widget; while (cb && !XmIsComboBox(cb)) cb = XtParent(cb); return (XmComboBoxWidget) cb; } /* * CallSelectionCallbacks() * Utility routine to invoke the ComboBox selection callback. */ static void CallSelectionCallbacks(Widget widget, XEvent *event) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; XmComboBoxCallbackStruct call_data; XmString item; int pos; /* The user knows about this change. */ CB_TextChanged(cb) = FALSE; item = GetEditBoxValue((Widget) cb); /* Implement bogus zero-based positions for DtComboBox compatibility. */ pos = XmListItemPos(CB_List(cb), item); if ((CB_PositionMode(cb) == XmZERO_BASED) && (pos > 0)) --pos; /* Call callback list */ call_data.item_or_text = item; call_data.item_position = pos; call_data.reason = XmCR_SELECT; call_data.event = event; XtCallCallbackList((Widget)cb, CB_SelectionCB(cb), (XtPointer)&call_data); XmStringFree(item); } /* ------------- DEFAULT RESOURCE VALUE CALLPROCS ---------- */ /* * XmRCallProc routine for checking font before setting it to NULL * if no value is specified for both XmNrenderTable and XmNfontList. * I'm usurping the text_changed field to act as a flag to indicate that * this function has been called twice on same widget -> implying that * resource needs to be set NULL, otherwise leave it alone. */ /*ARGSUSED*/ static void CheckSetRenderTable(Widget wid, int offset, XrmValue *value) { XmComboBoxWidget cb = (XmComboBoxWidget)wid; if (cb->combo_box.text_changed) /* Already been here once, so set resource = NULL */ value->addr = NULL; else { cb->combo_box.text_changed = True; value->addr = (char*) &CB_RenderTable(cb); } } /* ------------- SYNTHETIC RESOURCE ACCESS METHODS ------------- */ /* * CBSetSelectedItem() * A synthetic resource import procedure. This procedure can get * called at create time BEFORE ComboBox's Initialize() has been * called and children have been created. Make sure children have * been created before doing anything. */ /*ARGSUSED*/ static XmImportOperator CBSetSelectedItem(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; XmString new_value = (XmString) *value; int pos = 0; if (!cb->composite.num_children) return XmSYNTHETIC_NONE; pos = XmListItemPos(CB_List(cb), new_value); if (pos > 0) { XmListSelectPos(CB_List(cb), pos, TRUE); } else { XmString item = GetEditBoxValue((Widget) cb); if (! XmStringCompare(item, new_value)) { XmListDeselectAllItems(CB_List(cb)); SetEditBoxValue((Widget) cb, new_value); } XmStringFree(item); } return XmSYNTHETIC_NONE; } /* * CBGetSelectedItem() * A synthetic resource export procedure. */ /*ARGSUSED*/ static void CBGetSelectedItem(Widget widget, int offset, /* unused */ XtArgVal *value) { *value = (XtArgVal) GetEditBoxValue(widget); } /* * CBSetSelectedPos() * A synthetic resource import procedure. */ /*ARGSUSED*/ static XmImportOperator CBSetSelectedPos(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; int *selPosns = NULL, curpos = 0; Cardinal n; Arg args[3]; int new_pos; /* * This procedure can get called at create time BEFORE ComboBox's * Initialize() has been called and children have been created. * Make sure children have been created before doing anything. */ if (!cb->composite.num_children) return XmSYNTHETIC_NONE; /* Get current (1-based) list selectedPos */ n = 0; XtSetArg(args[n], XmNselectedPositions, &selPosns), n++; assert(n <= XtNumber(args)); XtGetValues(CB_List(cb), args, n); if (selPosns) curpos = *selPosns; /* Implement bogus zero-based positions for DtComboBox compatibility. */ new_pos = (int)*value; if (CB_PositionMode(cb) == XmZERO_BASED) new_pos++; if (curpos != new_pos) XmListSelectPos(CB_List(cb), new_pos, TRUE); return XmSYNTHETIC_NONE; } /* * CBGetSelectedPos() * A synthetic resource export procedure. */ /*ARGSUSED*/ static void CBGetSelectedPos(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg args[2]; Cardinal nargs; int *pos, count; int result; /* Using XmListGetSelectedPos would copy the positions array. */ nargs = 0; XtSetArg(args[nargs], XmNselectedPositions, &pos), nargs++; XtSetArg(args[nargs], XmNselectedPositionCount, &count), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_List(cb), args, nargs); /* Implement bogus zero-based positions for DtComboBox compatibility. */ result = (count > 0) ? *pos : 0; if ((CB_PositionMode(cb) == XmZERO_BASED) && (result > 0)) --result; *value = result; } /* * CBGetColumns() * A synthetic resource export procedure. */ /*ARGSUSED*/ static void CBGetColumns(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg args[1]; Cardinal nargs; short columns = 0; /* Fetch the value from the child widget. */ if (CB_EditBox(cb)) { nargs = 0; XtSetArg(args[nargs], XmNcolumns, &columns), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_EditBox(cb), args, nargs); } *value = (XtArgVal)columns; } /* * CBGetItems() * A synthetic resource export procedure. */ /*ARGSUSED*/ static void CBGetItems(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg args[1]; Cardinal nargs; XmStringTable items = NULL; /* Fetch the value from the child widget. */ if (CB_List(cb)) { nargs = 0; XtSetArg(args[nargs], XmNitems, &items), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_List(cb), args, nargs); } *value = (XtArgVal)items; } /* * CBGetItemCount() * A synthetic resource export procedure. */ /*ARGSUSED*/ static void CBGetItemCount(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg args[1]; Cardinal nargs; int count = 0; /* Fetch the value from the child widget. */ if (CB_List(cb)) { nargs = 0; XtSetArg(args[nargs], XmNitemCount, &count), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_List(cb), args, nargs); } *value = (XtArgVal)count; } /* * CBGetVisibleItemCount() * A synthetic resource export procedure. */ /*ARGSUSED*/ static void CBGetVisibleItemCount(Widget widget, int offset, /* unused */ XtArgVal *value) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg args[1]; Cardinal nargs; int viz_count = 0; /* Fetch the value from the child widget. */ if (CB_List(cb)) { nargs = 0; XtSetArg(args[nargs], XmNvisibleItemCount, &viz_count), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_List(cb), args, nargs); } *value = (XtArgVal)viz_count; } /* ------------- ADDITIONAL FUNCTIONS ---------- */ /* * HighlightBorder() */ static void HighlightBorder(Widget w) { XmComboBoxWidget cb = (XmComboBoxWidget)w; CB_Highlighted(cb) = TRUE; if ((XtWidth(cb) == 0) || (XtHeight(cb) == 0) || (CB_HighlightThickness(cb) == 0)) return; XmeDrawHighlight(XtDisplay(cb), XtWindow(cb), cb->manager.highlight_GC, 0, 0, XtWidth(cb), XtHeight(cb), CB_HighlightThickness(cb)); } /* * UnhighlightBorder() */ static void UnhighlightBorder(Widget w) { XmComboBoxWidget cb = (XmComboBoxWidget)w; CB_Highlighted(cb) = FALSE; if ((XtWidth(w) == 0) || (XtHeight(w) == 0) || (CB_HighlightThickness(cb) == 0)) return; XmeDrawHighlight(XtDisplay(cb), XtWindow(cb), cb->manager.background_GC, 0, 0, XtWidth(w), XtHeight(w), CB_HighlightThickness(cb)); } /* * DoLayout() * Computes layout and sizes of children given a size for their * parent. Called from SetValues, Resize and ChangeManaged. */ static void DoLayout(Widget widg) { XmComboBoxWidget cb = (XmComboBoxWidget)widg; Dimension ebW = 0, ebH = 0, thickW = 0, thickH = 0; Dimension listW = 0, listH = 0 ; Dimension diff, htLeft = 0; XtWidgetGeometry geom, replygeom; XtGeometryResult result; /* Make sure all our children are around */ if (!CB_EditBox(cb)) XmeWarning(widg, MISSINGKID); else if (!XtIsManaged(CB_EditBox(cb))) { XmeWarning(widg, UNMANAGEDKID); return; } if (!CB_List(cb)) XmeWarning(widg, MISSINGKID); else if (!XtIsManaged(CB_List(cb))) { XmeWarning(widg, UNMANAGEDKID); return; } GetThickness(widg, &thickW, &thickH); ebW = XtWidth(cb) - 2 * (thickW + XtBorderWidth(CB_EditBox(cb))); if (CB_Type(cb) != XmCOMBO_BOX) { /* These are the drop down types */ Arg args[1]; Cardinal nargs; ebH = XtHeight(cb) - 2 * (thickH + XtBorderWidth(CB_EditBox(cb))); if (CB_ArrowSize(cb) == XmINVALID_DIMENSION) CB_ArrowSize(cb) = GetDefaultArrowSize((Widget)cb); SetHitArea((Widget)cb); ebW -= (CB_ArrowSpacing(cb) + CB_ArrowSize(cb)); /* Realize the shell so these values will be maintained. */ if (!XtIsRealized(CB_ListShell(cb))) XtRealizeWidget(CB_ListShell(cb)); /* CR 7676: These numbers don't make sense, but they work? */ listW = XtWidth(cb) - 2 * CB_HighlightThickness(cb); nargs = 0; XtSetArg(args[nargs], XmNwidth, listW), nargs++; assert(nargs <= XtNumber(args)); XtSetValues(CB_ListShell(cb), args, nargs); } else { Dimension new_width = XtWidth(CB_ScrolledW(cb)); Dimension new_height = XtHeight(CB_ScrolledW(cb)); htLeft = XtHeight(cb) - 2 * (XtBorderWidth(CB_EditBox(cb)) + XtBorderWidth(CB_ScrolledW(cb)) + CB_HighlightThickness(cb) + MGR_ShadowThickness(cb) + CB_MarginHeight(cb)); if (!cb->combo_box.ideal_ebheight) GetIdealTextSize((Widget)cb, NULL, &cb->combo_box.ideal_ebheight); /* If ideal edit box height fits, use it. */ ebH = MIN(htLeft - MINLIST, cb->combo_box.ideal_ebheight); listW = XtWidth(cb) - 2 * (thickW + XtBorderWidth(CB_ScrolledW(cb))); listH = htLeft - ebH; /* Ask ScrolledW if this size is okay */ geom.request_mode = 0; geom.request_mode |= CWWidth; geom.request_mode |= CWHeight; geom.width = listW; geom.height = listH; switch (XtQueryGeometry(CB_ScrolledW(cb), &geom, &replygeom)) { case XtGeometryAlmost: if (replygeom.request_mode & CWHeight) { /* see if EditBox will shrink. */ listH = replygeom.height; diff = abs(geom.height - replygeom.height); geom.request_mode = 0; geom.request_mode |= CWHeight; geom.height = ebH - diff; result = XtQueryGeometry(CB_EditBox(cb), &geom, NULL); if (result == XtGeometryYes) { ebH = geom.height; /* will be resized below */ new_width = listW; new_height = listH; } } break; case XtGeometryYes: case XtGeometryNo: default: /* No compromises. These are the new dimensions. */ new_width = listW; new_height = listH; } XmeConfigureObject(CB_ScrolledW(cb), thickW, (thickH + ebH + 2 * XtBorderWidth(CB_EditBox(cb)) + XtBorderWidth(CB_ScrolledW(cb))), new_width, new_height, XtBorderWidth(CB_ScrolledW(cb))); } { Position new_x, new_y; if ((CB_Type(cb) != XmCOMBO_BOX) && LayoutIsRtoLM(cb)) { new_x = (thickW + CB_ArrowSize(cb) + CB_ArrowSpacing(cb)); new_y = thickH ; } else { new_x = thickW ; new_y = thickH ; } XmeConfigureObject(CB_EditBox(cb), new_x, new_y, ebW, ebH, XtBorderWidth(CB_EditBox(cb))); } } /* * ComputeSize() * Determines the width and height of a ComboBox. */ static void ComputeSize(Widget w, Dimension editW, Dimension editH, Dimension *width, Dimension *height) { Dimension cbWidth, cbHeight; Dimension thickW, thickH; XmComboBoxWidget cb = (XmComboBoxWidget)w; int textWidth, textHeight; /* Size of ComboBox * width = (text width) + [arrow space & width] + (surrounding space) * height = (text height) + (surrounding space) + [list height] */ GetThickness(w, &thickW, &thickH); /* Compute size of textfield */ if (!editW || !editH) { /* Ask for text child's preferred size */ GetIdealTextSize((Widget)cb, &textWidth, &textHeight); } else { textWidth = editW; textHeight = editH; } cb->combo_box.ideal_ebwidth = textWidth; cb->combo_box.ideal_ebheight = textHeight; cbWidth = textWidth + 2 * (thickW + XtBorderWidth(CB_EditBox(cb))); cbHeight = textHeight + 2 * (thickH + XtBorderWidth(CB_EditBox(cb))); /* Adjust dimensions taking optional children into account. */ if (CB_Type(cb) == XmCOMBO_BOX) { /* For a COMBO_BOX add in the height of the list. */ XtWidgetGeometry pref; (void) XtQueryGeometry(CB_ScrolledW(cb), NULL, &pref); cbHeight += (pref.height + 2 * XtBorderWidth(CB_ScrolledW(cb))); } else { /* For other types consider the arrow dimensions. */ if (CB_ArrowSize(cb) == XmINVALID_DIMENSION) CB_ArrowSize(cb) = (int) ((float) textHeight * DEFAULT_ARROW_SCALING); cbWidth += CB_ArrowSize(cb) + CB_ArrowSpacing(cb); if (CB_ArrowSize(cb) > textHeight) cbHeight += (CB_ArrowSize(cb) - textHeight); } /* Preserve existing sizes. */ if (! *width) *width = MAX(cbWidth, 1); if (! *height) *height = MAX(cbHeight, 1); } /* * GetIdealTextSize() */ static void GetIdealTextSize(Widget w, int *width, int *height) { XmComboBoxWidget cb = (XmComboBoxWidget)w; XtWidgetGeometry text_pref, list_pref; /* Ask for text child's preferred size */ (void) XtQueryGeometry(CB_EditBox(cb), NULL, &text_pref); /* Ask for list child's preferred size */ (void) XtQueryGeometry(CB_ScrolledW(cb), NULL, &list_pref); if (width) *width = MAX(text_pref.width, list_pref.width); if (height) *height = text_pref.height; } /* * GetDefaultArrowSize() */ static Dimension GetDefaultArrowSize(Widget w) { XmComboBoxWidget cb = (XmComboBoxWidget)w; if (!cb->combo_box.ideal_ebheight) GetIdealTextSize((Widget)cb, NULL, &cb->combo_box.ideal_ebheight); return((int) ((float) cb->combo_box.ideal_ebheight * DEFAULT_ARROW_SCALING)); } /* * SetHitArea() * Sets hit area for arrow. */ static void SetHitArea(Widget w) { XmComboBoxWidget cb = (XmComboBoxWidget)w; Dimension thickW = 0, thickH = 0, ebH; assert(CB_Type(cb) != XmCOMBO_BOX); GetThickness(w, &thickW, &thickH); ebH = XtHeight(cb) - 2 * (thickH + XtBorderWidth(CB_EditBox(cb))); if (CB_ArrowSize(cb) == XmINVALID_DIMENSION) CB_ArrowSize(cb) = GetDefaultArrowSize((Widget)cb); CB_HitRect(cb).width = CB_ArrowSize(cb); CB_HitRect(cb).height = ebH; if (LayoutIsRtoLM(cb)) { CB_HitRect(cb).x = thickW; CB_HitRect(cb).y = thickH; } else { CB_HitRect(cb).x = XtWidth(cb) - thickW - CB_ArrowSize(cb); CB_HitRect(cb).y = thickH; } } /* * GetThickness() * Gets "thickness" resources, i.e. shadow thickness, marginW, etc. */ static void GetThickness(Widget widg, Dimension *width, Dimension *height) { XmComboBoxWidget cb = (XmComboBoxWidget)widg; if (width) *width = cb->combo_box.margin_width + cb->combo_box.highlight_thickness + cb->manager.shadow_thickness; if (height) *height = cb->combo_box.margin_height + cb->combo_box.highlight_thickness + cb->manager.shadow_thickness; } /* * GetArrowGC() * Create the GC for drawing sensitive arrows. */ static void GetArrowGC(Widget widget) { XmComboBoxWidget cb = (XmComboBoxWidget) widget; XGCValues values; XtGCMask mask; /* Only plain ComboBoxes have arrows. */ if (CB_Type(cb) != XmCOMBO_BOX) { mask = 0; values.foreground = XtBackground(widget), mask |= GCForeground; values.graphics_exposures = False, mask |= GCGraphicsExposures; cb->combo_box.arrow_GC = XtGetGC(widget, mask, &values); } else { cb->combo_box.arrow_GC = NULL; } } /* * DrawArrow() */ static void DrawArrow(Widget widget, Boolean pressed) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; int newbox, size, max_height, excess; int x, y, w, h; assert(CB_Type(cb) != XmCOMBO_BOX); if (CB_ArrowSize(cb) == XmINVALID_DIMENSION) CB_ArrowSize(cb) = GetDefaultArrowSize((Widget) cb); /* CR 6912: Reduce size to fit the actual space available. */ max_height = XtHeight(cb) - 2 * (CB_MarginHeight(cb) + CB_HighlightThickness(cb) + cb->manager.shadow_thickness + XtBorderWidth(CB_EditBox(cb))); if (CB_ArrowSize(cb) > max_height) { excess = CB_ArrowSize(cb) - max_height; size = max_height; } else { excess = 0; size = CB_ArrowSize(cb); } newbox = (int) (size * SQRT3_OVER_2); /* CR 6890: Center the arrow within the box. */ x = CB_HitRect(cb).x + (size - newbox + excess) / 2; y = CB_HitRect(cb).y + (CB_HitRect(cb).height - size - 1) / 2; w = h = newbox; XmeDrawArrow(XtDisplay(widget), XtWindow(widget), (pressed ? cb->manager.bottom_shadow_GC : cb->manager.top_shadow_GC), (pressed ? cb->manager.top_shadow_GC : cb->manager.bottom_shadow_GC), (XtIsSensitive(widget) ? cb->combo_box.arrow_GC : cb->manager.background_GC), x, y, w, h, cb->combo_box.arrow_shadow_width, XmARROW_DOWN); y += newbox; h = size - newbox; XmeDrawShadows(XtDisplay(widget), XtWindow(widget), cb->manager.top_shadow_GC, cb->manager.bottom_shadow_GC, x, y, w, h, cb->combo_box.arrow_shadow_width, XmSHADOW_OUT); } /* * DrawShadows() */ static void DrawShadows(Widget widget) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; int offset = CB_HighlightThickness(cb); XmeDrawShadows(XtDisplay(widget), XtWindow(widget), cb->manager.top_shadow_GC, cb->manager.bottom_shadow_GC, offset, offset, XtWidth(widget) - 2 * offset, XtHeight(widget) - 2 * offset, MGR_ShadowThickness(cb), XmSHADOW_OUT); } /* * CreateChildren() * Called by the ComboBox Widget's Initialize proc. to create * the children that make up a ComboBox. */ static void CreateChildren(Widget widget, ArgList arglist, Cardinal *num_args) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg loc_args[10]; Cardinal n_args; /* * N.B.: Do not change order of creation! The CB_EditBox() macro * and code in CreateScrolledList() both rely on this order. */ /* Create the editbox. */ CreateEditBox(widget, TEXT_CHILD_NAME, widget, arglist, num_args); XtAddCallback(CB_EditBox(cb), XmNvalueChangedCallback, TextChangedCB, (XtPointer) cb); /* Create the popup shell if appropriate. */ if (CB_Type(cb) != XmCOMBO_BOX) { CB_ListShell(cb) = CreatePulldown(widget, SHELL_CHILD_NAME, widget, arglist, num_args); XtAddCallback(CB_ListShell(cb), XmNpopupCallback, ShellPopupCB, (XtPointer) cb); XtAddCallback(CB_ListShell(cb), XmNpopdownCallback, ShellPopdownCB, (XtPointer) cb); /* Add Event Handler for pointer events in the popup */ XtAddEventHandler(CB_ListShell(cb), POPUP_EVENT_MASK, FALSE, PopupEH, (XtPointer)cb); } /* Create the list. */ CB_List(cb) = CreateScrolledList(CB_ListShell(cb) ? CB_ListShell(cb) : widget, LIST_CHILD_NAME, widget, arglist, num_args); XtAddCallback(CB_List(cb), XmNbrowseSelectionCallback, (XtCallbackProc)ListSelectionCB, (XtPointer)cb); /* CR 9868: Direct events properly in pointer focus mode. */ if (_XmGetFocusPolicy((Widget) cb) == XmPOINTER) { XtSetKeyboardFocus((Widget) cb, CB_EditBox(cb)); if (CB_ListShell(cb)) XtSetKeyboardFocus(CB_ListShell(cb), CB_List(cb)); } /* Setup all the accelerators and translations. */ switch (CB_Type(cb)) { case XmCOMBO_BOX: /* The standard ComboBox accelerators will handle this case. */ break; case XmDROP_DOWN_LIST: XtOverrideTranslations(CB_List(cb), parsed_list_translations); break; case XmDROP_DOWN_COMBO_BOX: n_args = 0; XtSetArg(loc_args[n_args], XmNaccelerators, parsed_list_accelerators), n_args++; assert(n_args <= XtNumber(loc_args)); XtSetValues(CB_EditBox(cb), loc_args, n_args); XtInstallAccelerators(CB_List(cb), CB_EditBox(cb)); XtOverrideTranslations(CB_EditBox(cb), parsed_text_focus_translations); break; default: assert(False); } /* Install the standard ComboBox accelerators everywhere. */ XtInstallAccelerators(CB_List(cb), (Widget) cb); XtInstallAccelerators(CB_EditBox(cb), (Widget) cb); /* Add event handlers to the scrollbar in the scrolled list widget */ if (CB_Type(cb) != XmCOMBO_BOX) { Widget sb; sb = cb->combo_box.vsb; if (sb != (Widget) NULL) { XtInsertEventHandler(sb, ButtonPressMask, False, SBBtnDownEH, (XtPointer) CB_ListShell(cb), XtListHead); XtInsertEventHandler(sb, ButtonReleaseMask, False, SBBtnUpEH, (XtPointer) CB_ListShell(cb), XtListHead); } sb = cb->combo_box.hsb; if (sb != (Widget) NULL) { XtInsertEventHandler(sb, ButtonPressMask, False, SBBtnDownEH, (XtPointer) CB_ListShell(cb), XtListHead); XtInsertEventHandler(sb, ButtonReleaseMask, False, SBBtnUpEH, (XtPointer) CB_ListShell(cb), XtListHead); } } } /* * CreateEditBox() * Creates the EditBox child of the ComboBox. */ static Widget CreateEditBox(Widget parent, String name, Widget w, ArgList arglist, Cardinal *num_args) { XmComboBoxWidget cb = (XmComboBoxWidget)w; Widget text_widget; Arg loc_args[15]; Cardinal nloc; ArgList merged_args; char *item = NULL; /* * The combo-box itself was already created with this argument * list, so there is no need to separate "optional" and "required" * resources. Our instance field values will be identical if we * override any duplicates. */ nloc = 0; if (CBS_Columns(cb) != XmUNSPECIFIED_COLUMNS) { XtSetArg(loc_args[nloc], XmNcolumns, CBS_Columns(cb)), nloc++; } if (CBS_SelectedItem(cb)) { item = _XmStringGetTextConcat(CBS_SelectedItem(cb)); if (item) XtSetArg(loc_args[nloc], XmNvalue, item), nloc++; } XtSetArg(loc_args[nloc], XmNrenderTable, CB_RenderTable(cb)), nloc++; XtSetArg(loc_args[nloc], XmNnavigationType, XmNONE), nloc++; XtSetArg(loc_args[nloc], XmNhighlightThickness, 0), nloc++; XtSetArg(loc_args[nloc], XmNborderWidth, 0), nloc++; if (CB_Type(cb) == XmDROP_DOWN_LIST) { XtSetArg(loc_args[nloc], XmNeditable, FALSE), nloc++; XtSetArg(loc_args[nloc], XmNcursorPositionVisible, FALSE), nloc++; XtSetArg(loc_args[nloc], XmNshadowThickness, 0), nloc++; } else { XtSetArg(loc_args[nloc], XmNeditable, TRUE), nloc++; XtSetArg(loc_args[nloc], XmNeditMode, XmSINGLE_LINE_EDIT), nloc++; XtSetArg(loc_args[nloc], XmNcursorPositionVisible, TRUE), nloc++; } assert(nloc <= XtNumber(loc_args)); /* Create the edit box. */ merged_args = XtMergeArgLists(arglist, *num_args, loc_args, nloc); text_widget = XmCreateTextField(parent, name, merged_args, *num_args + nloc); XtFree((char *)merged_args); if (item) XtFree(item); XtManageChild(text_widget); assert(cb->composite.children[0] == text_widget); CB_EditBox(cb) = text_widget; return text_widget; } /* * CreatePulldown() * Creates the pulldown shell for the list if the ComboBox is * a XmDROP_DOWN_COMBO_BOX or a XmDROP_DOWN_LIST. */ /*ARGSUSED*/ static Widget CreatePulldown(Widget parent, String name, Widget w, /* unused */ ArgList arglist, Cardinal *num_args) { Widget shell; Arg args[4]; ArgList merged_args; Cardinal n; n = 0; XtSetArg(args[n], XmNlayoutDirection, LayoutM(parent)), n++; XtSetArg(args[n], XmNownerEvents, True), n++; XtSetArg(args[n], XmNgrabStyle, GrabModeSync), n++; assert(n <= XtNumber(args)); merged_args = XtMergeArgLists(arglist, *num_args, args, n); shell = XtCreatePopupShell(name, xmGrabShellWidgetClass, parent, merged_args, n + *num_args); XtFree((char*)merged_args); return shell; } /* * CreateScrolledList() * Creates the Scrolled List child of the ComboBox. If the * ComboBox is a XmDROP_DOWN_COMBO_BOX or a XmDROP_DOWN_LIST it * creates the list in a popup shell. */ static Widget CreateScrolledList(Widget parent, String name, Widget w, ArgList arglist, Cardinal *num_args) { Cardinal n; Arg loc_args[16]; Widget list; XmComboBoxWidget cb = (XmComboBoxWidget)w; ArgList merged_args; int pos, nitems = 0; Boolean setpos = FALSE; XmString *items; n = 0; if (CBS_Items(cb) != XmUNSPECIFIED_ITEMS) { XtSetArg(loc_args[n], XmNitems, CBS_Items(cb)), n++; } if (CBS_ItemCount(cb) != XmUNSPECIFIED_COUNT) { XtSetArg(loc_args[n], XmNitemCount, CBS_ItemCount(cb)), n++; } if (CBS_VisibleItemCount(cb) != XmUNSPECIFIED_COUNT) { XtSetArg(loc_args[n], XmNvisibleItemCount, CBS_VisibleItemCount(cb)), n++; } if (CBS_SelectedItem(cb)) { XtSetArg(loc_args[n], XmNselectedItems, &CBS_SelectedItem(cb)), n++; XtSetArg(loc_args[n], XmNselectedItemCount, 1), n++; } else { if (CBS_SelectedPosition(cb) == XmINVALID_POSITION) pos = 1; /* Issue 138 */ else if (CB_PositionMode(cb) == XmZERO_BASED) pos = CBS_SelectedPosition(cb) + 1; else pos = CBS_SelectedPosition(cb); XtSetArg(loc_args[n], XmNselectedPositions, &pos), n++; XtSetArg(loc_args[n], XmNselectedPositionCount, 1), n++; setpos = TRUE; } XtSetArg(loc_args[n], XmNrenderTable, CB_RenderTable(cb)), n++; if (CB_Type(cb) == XmCOMBO_BOX) XtSetArg(loc_args[n], XmNtraversalOn, FALSE), n++; XtSetArg(loc_args[n], XmNhighlightThickness, ((CB_Type(cb) == XmDROP_DOWN_LIST) ? 2 : 0)), n++; XtSetArg(loc_args[n], XmNborderWidth, 0), n++; XtSetArg(loc_args[n], XmNnavigationType, XmNONE), n++; XtSetArg(loc_args[n], XmNselectionPolicy, XmBROWSE_SELECT), n++; XtSetArg(loc_args[n], XmNlistSizePolicy, XmVARIABLE), n++; XtSetArg(loc_args[n], XmNspacing, 0), n++; XtSetArg(loc_args[n], XmNvisualPolicy, XmVARIABLE), n++; assert(n <= XtNumber(loc_args)); merged_args = XtMergeArgLists(arglist, *num_args, loc_args, n); list = XmCreateScrolledList(parent, name, merged_args, n + *num_args); XtManageChild(list); XtFree((char *)merged_args); CB_ScrolledW(cb) = XtParent(list); n = 0; XtSetArg(loc_args[n], XmNhorizontalScrollBar, &cb->combo_box.hsb), n++; XtSetArg(loc_args[n], XmNverticalScrollBar, &cb->combo_box.vsb), n++; assert(n <= XtNumber(loc_args)); XtGetValues(CB_ScrolledW(cb), loc_args, n); n = 0; XtSetArg(loc_args[n], XmNshadowThickness, 0), n++; assert(n <= XtNumber(loc_args)); XtSetValues(list, loc_args, n); if (setpos) { n = 0; XtSetArg(loc_args[n], XmNitems, &items), n++; XtSetArg(loc_args[n], XmNitemCount, &nitems), n++; assert(n <= XtNumber(loc_args)); XtGetValues(list, loc_args, n); /* CR 7064: Don't try to set the edit box value if no items exist. */ if (nitems && (nitems >= pos)) { if (pos > 0) pos--; SetEditBoxValue((Widget) cb, items[pos]); } } return list; } /* * Hit() * Decide whether a button event happened within a particular XRectangle. */ static Boolean Hit(XButtonEvent* event, XRectangle r) { if (event == NULL) return False; else return ((r.x <= event->x) && (event->x <= (r.x + r.width)) && (r.y <= event->y) && (event->y <= (r.y + r.height))); } /* * GetEditBoxValue() * Retrieve the XmString value of the edit box. */ static XmString GetEditBoxValue(Widget cb) { Widget edit_box = CB_EditBox(cb); XmAccessTextualTrait textTrait; textTrait = (XmAccessTextualTrait) XmeTraitGet((XtPointer) XtClass(edit_box), XmQTaccessTextual); if (textTrait) return (XmString) textTrait->getValue(edit_box, XmFORMAT_XmSTRING); else return NULL; } /* * SetEditBoxValue() * Set the XmString value of the edit box. */ static void SetEditBoxValue(Widget cb, XmString value) { Widget edit_box = CB_EditBox(cb); XmAccessTextualTrait textTrait; textTrait = (XmAccessTextualTrait) XmeTraitGet((XtPointer) XtClass(edit_box), XmQTaccessTextual); textTrait->setValue(edit_box, value, XmFORMAT_XmSTRING); } /* ------------- CONVENIENCE FUNCTIONS ---------- */ Widget XmCreateComboBox(Widget parent, char *name, ArgList args, Cardinal num_args) { return XtCreateWidget(name, xmComboBoxWidgetClass, parent, args, num_args); } Widget XmCreateDropDownComboBox(Widget parent, char *name, ArgList args, Cardinal num_args) { Arg loc_args[5]; Cardinal n; ArgList merged_args; Widget result; n = 0; XtSetArg(loc_args[n], XmNcomboBoxType, XmDROP_DOWN_COMBO_BOX), n++; assert(n <= XtNumber(loc_args)); merged_args = XtMergeArgLists(args, num_args, loc_args, n); result = XtCreateWidget(name, xmComboBoxWidgetClass, parent, merged_args, n + num_args); XtFree((char *)merged_args); return result; } Widget XmCreateDropDownList(Widget parent, char *name, ArgList args, Cardinal num_args) { Arg loc_args[5]; Cardinal n; ArgList merged_args; Widget result; n = 0; XtSetArg(loc_args[n], XmNcomboBoxType, XmDROP_DOWN_LIST), n++; assert(n <= XtNumber(loc_args)); merged_args = XtMergeArgLists(args, num_args, loc_args, n); result = XtCreateWidget(name, xmComboBoxWidgetClass, parent, merged_args, n + num_args); XtFree((char *)merged_args); return result; } /* * XmComboBoxAddItem * Convenience function added for CDE compatibility. Add an item * to the ComboBox list. */ void XmComboBoxAddItem(Widget widget, XmString item, int pos, Boolean unique) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; _XmWidgetToAppContext(widget); _XmAppLock(app); if (!XmIsComboBox(widget)) { XmeWarning(widget, NOTACOMBOBOX); _XmAppUnlock(app); return; } else if (!CB_List(cb)) { _XmAppUnlock(app); return; } /* If requested, scan for duplicate items. */ if (unique && item && XmListItemExists(CB_List(cb), item)) { _XmAppUnlock(app); return; } /* Add the item to the list and update our selected_position. */ XmListAddItemUnselected(CB_List(cb), item, pos); XmComboBoxUpdate(widget); _XmAppUnlock(app); } /* * XmComboBoxDeletePos * Convenience function added for CDE compatibility. Delete the * list item at the indicated position. */ void XmComboBoxDeletePos(Widget widget, int pos) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; int selpos; int nitems; _XmWidgetToAppContext(widget); _XmAppLock(app); if (!XmIsComboBox(widget)) { XmeWarning(widget, NOTACOMBOBOX); _XmAppUnlock(app); return; } else if (!CB_List(cb)) { _XmAppUnlock(app); return; } /* Validate the position. */ { Arg args[10]; Cardinal nargs; int *positions; int count; nargs = 0; XtSetArg(args[nargs], XmNitemCount, &nitems), nargs++; XtSetArg(args[nargs], XmNselectedPositions, &positions), nargs++; XtSetArg(args[nargs], XmNselectedPositionCount, &count), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_List(widget), args, nargs); /* Ensure that we're using a one-based position. */ selpos = (count > 0) ? *positions : 0; } /* DtComboBoxDeletePos rejects pos == 0, but we allow it. */ if ((pos < 0) || (pos > nitems) || (nitems <= 0)) { XmeWarning(widget, DELETEBADPOS); _XmAppUnlock(app); return; } /* Delete this item. */ XmListDeletePos(CB_List(cb), pos); /* If our selected item just got deleted select seomthing else. */ if ((pos ? pos : nitems) == selpos) { if (nitems > 1) { /* Invoke the callbacks now. */ XmListSelectPos(CB_List(cb), selpos, True); } else { /* The list is now empty. */ CB_TextChanged(cb) = FALSE; XmComboBoxUpdate(widget); CB_TextChanged(cb) = FALSE; } } _XmAppUnlock(app); } /* * XmComboBoxSelectItem * Convenience function added for CDE compatibility. Select the * indicated item. Do not invoke the callbacks or scroll the list. */ void XmComboBoxSelectItem(Widget widget, XmString item) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; int pos; _XmWidgetToAppContext(widget); _XmAppLock(app); if (!XmIsComboBox(widget)) { XmeWarning(widget, NOTACOMBOBOX); _XmAppUnlock(app); return; } else if (!CB_List(cb)) { _XmAppUnlock(app); return; } /* Calculate the position of this item. */ pos = XmListItemPos(CB_List(cb), item); if (pos > 0) { /* Select the indicated item. */ XmListDeselectAllItems(CB_List(cb)); XmListSelectPos(CB_List(cb), pos, FALSE); /* Discard user changes to the edit box and suppress all callbacks. */ CB_TextChanged(cb) = FALSE; XmComboBoxUpdate(widget); CB_TextChanged(cb) = FALSE; } else XmeWarning(widget, SELECTBADITEM); _XmAppUnlock(app); } /* * XmComboBoxSetItem * Convenience function added for CDE compatibility. Select the * indicated item and force it to be visible. Do not invoke the callbacks. */ void XmComboBoxSetItem(Widget widget, XmString item) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; int pos; _XmWidgetToAppContext(widget); _XmAppLock(app); if (!XmIsComboBox(widget)) { XmeWarning(widget, NOTACOMBOBOX); _XmAppUnlock(app); return; } else if (!CB_List(cb)) { _XmAppUnlock(app); return; } /* Calculate the position of this item. */ pos = XmListItemPos(CB_List(cb), item); if (pos > 0) { /* Scroll the item to the top and select it. */ XmListSetPos(CB_List(cb), pos); XmListSelectPos(CB_List(cb), pos, FALSE); /* Discard user changes to the edit box and suppress all callbacks. */ CB_TextChanged(cb) = FALSE; XmComboBoxUpdate(widget); CB_TextChanged(cb) = FALSE; } else XmeWarning(widget, SETBADITEM); _XmAppUnlock(app); } /* * XmComboBoxUpdate() * Resynchronize internal data structures after an application * updates our children directly. */ void XmComboBoxUpdate(Widget widget) { XmComboBoxWidget cb = (XmComboBoxWidget)widget; Arg args[10]; Cardinal nargs; XmString *items; int icount; int *pos; int pcount; _XmWidgetToAppContext(widget); _XmAppLock(app); if (!XmIsComboBox(widget)) { XmeWarning(widget, NOTACOMBOBOX); _XmAppUnlock(app); return; } else if (!CB_List(cb)) { _XmAppUnlock(app); return; } /* CR 8445: If no text entry is in progress echo the new list selection. */ if (!CB_TextChanged(cb)) { /* The selected item and position of the list may have changed. */ nargs = 0; XtSetArg(args[nargs], XmNselectedPositions, &pos), nargs++; XtSetArg(args[nargs], XmNselectedPositionCount, &pcount), nargs++; XtSetArg(args[nargs], XmNitems, &items), nargs++; XtSetArg(args[nargs], XmNitemCount, &icount), nargs++; assert(nargs <= XtNumber(args)); XtGetValues(CB_List(cb), args, nargs); if ((pcount > 0) && (icount > 0)) SetEditBoxValue((Widget) cb, items[pos[0] - 1]); } _XmAppUnlock(app); }