1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/motif/listbox.cpp
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
17 #include "wx/listbox.h"
20 #include "wx/dynarray.h"
23 #include "wx/settings.h"
24 #include "wx/arrstr.h"
28 #define XtParent XTPARENT
29 #define XtDisplay XTDISPLAY
33 #pragma message disable nosimpint
37 #pragma message enable nosimpint
39 #include "wx/motif/private.h"
41 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControlWithItems
)
43 static void wxListBoxCallback(Widget w
,
45 XmListCallbackStruct
* cbs
);
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
51 // helper class to reduce code duplication
58 wxSizeKeeper( wxWindow
* w
)
61 m_wnd
->GetSize( &m_w
, &m_h
);
62 m_wnd
->GetPosition( &m_x
, &m_y
);
69 m_wnd
->GetSize( &x
, &y
);
70 if( x
!= m_x
|| y
!= m_y
)
71 m_wnd
->SetSize( m_x
, m_y
, m_w
, m_h
);
75 // ============================================================================
76 // list box control implementation
77 // ============================================================================
80 wxListBox::wxListBox()
85 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
88 int n
, const wxString choices
[],
90 const wxValidator
& validator
,
93 if( !wxControl::CreateControl( parent
, id
, pos
, size
, style
,
98 m_noItems
= (unsigned int)n
;
100 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
101 Display
* dpy
= XtDisplay(parentWidget
);
105 XtSetArg( args
[count
], XmNlistSizePolicy
, XmCONSTANT
); ++count
;
106 XtSetArg( args
[count
], XmNselectionPolicy
,
107 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
108 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
113 XtSetArg( args
[count
],
114 (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
) );
117 if( m_windowStyle
& wxLB_ALWAYS_SB
)
119 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
124 XmCreateScrolledList(parentWidget
,
125 name
.char_str(), args
, count
);
127 m_mainWidget
= (WXWidget
) listWidget
;
131 XtManageChild (listWidget
);
133 wxSize best
= GetBestSize();
134 if( size
.x
!= -1 ) best
.x
= size
.x
;
135 if( size
.y
!= -1 ) best
.y
= size
.y
;
137 XtAddCallback (listWidget
,
138 XmNbrowseSelectionCallback
,
139 (XtCallbackProc
) wxListBoxCallback
,
141 XtAddCallback (listWidget
,
142 XmNextendedSelectionCallback
,
143 (XtCallbackProc
) wxListBoxCallback
,
145 XtAddCallback (listWidget
,
146 XmNmultipleSelectionCallback
,
147 (XtCallbackProc
) wxListBoxCallback
,
149 XtAddCallback (listWidget
,
150 XmNdefaultActionCallback
,
151 (XtCallbackProc
) wxListBoxCallback
,
155 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
156 pos
.x
, pos
.y
, best
.x
, best
.y
);
161 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
164 const wxArrayString
& choices
,
166 const wxValidator
& validator
,
167 const wxString
& name
)
169 wxCArrayString
chs(choices
);
170 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
171 style
, validator
, name
);
174 void wxListBox::SetSelectionPolicy()
176 Widget listBox
= (Widget
)m_mainWidget
;
179 XtSetArg( args
[0], XmNlistSizePolicy
, XmCONSTANT
);
181 XtSetArg( args
[1], XmNselectionPolicy
,
182 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
183 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
186 XtSetValues( listBox
, args
, 2 );
189 void wxListBox::DoSetFirstItem( int N
)
196 XtVaGetValues ((Widget
) m_mainWidget
,
197 XmNvisibleItemCount
, &count
,
198 XmNitemCount
, &length
,
200 if ((N
+ count
) >= length
)
202 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
205 void wxListBox::DoDeleteOneItem(unsigned int n
)
207 Widget listBox
= (Widget
) m_mainWidget
;
209 XmListDeletePos (listBox
, n
+ 1);
211 wxListBoxBase::DoDeleteOneItem(n
);
215 int wxDoFindStringInList(Widget w
, const wxString
& s
)
218 int *positions
= NULL
;
219 int no_positions
= 0;
220 bool success
= XmListGetMatchPos (w
, str(),
221 &positions
, &no_positions
);
223 if (success
&& positions
)
225 int pos
= positions
[0];
226 XtFree ((char *) positions
);
233 int wxListBox::FindString(const wxString
& s
, bool WXUNUSED(bCase
)) const
235 // FIXME: back to base class for not supported value of bCase
237 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
240 void wxListBox::DoClear()
245 wxSizeKeeper
sk( this );
246 Widget listBox
= (Widget
) m_mainWidget
;
248 XmListDeleteAllItems (listBox
);
252 wxListBoxBase::DoClear();
256 void wxListBox::DoSetSelection(int N
, bool select
)
262 if (m_windowStyle
& wxLB_MULTIPLE
)
264 int *selections
= NULL
;
265 int n
= GetSelections (&selections
);
267 // This hack is supposed to work, to make it possible
268 // to select more than one item, but it DOESN'T under Motif 1.1.
270 XtVaSetValues ((Widget
) m_mainWidget
,
271 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
275 for (i
= 0; i
< n
; i
++)
276 XmListSelectPos ((Widget
) m_mainWidget
,
277 selections
[i
] + 1, False
);
279 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
281 XtVaSetValues ((Widget
) m_mainWidget
,
282 XmNselectionPolicy
, XmEXTENDED_SELECT
,
287 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
291 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
293 m_inSetValue
= false;
296 bool wxListBox::IsSelected(int N
) const
298 // In Motif, no simple way to determine if the item is selected.
299 wxArrayInt theSelections
;
300 int count
= GetSelections (theSelections
);
306 for (j
= 0; j
< count
; j
++)
307 if (theSelections
[j
] == N
)
313 // Return number of selections and an array of selected integers
314 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
318 Widget listBox
= (Widget
) m_mainWidget
;
321 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
326 aSelections
.Alloc(posCnt
);
329 for (i
= 0; i
< posCnt
; i
++)
330 aSelections
.Add(posList
[i
] - 1);
332 XtFree ((char *) posList
);
342 // Get single selection, for single choice list items
343 int wxDoGetSelectionInList(Widget listBox
)
347 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
353 XtFree ((char *) posList
);
360 int wxListBox::GetSelection() const
362 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
365 // Find string for position
366 wxString
wxDoGetStringInList( Widget listBox
, int n
)
370 XtVaGetValues( listBox
,
371 XmNitemCount
, &count
,
374 if( n
< count
&& n
>= 0 )
375 return wxXmStringToString( strlist
[n
] );
377 return wxEmptyString
;
380 wxString
wxListBox::GetString(unsigned int n
) const
382 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
385 int wxListBox::DoInsertItems(const wxArrayStringsAdapter
& items
,
387 void **clientData
, wxClientDataType type
)
389 Widget listBox
= (Widget
) m_mainWidget
;
391 const unsigned int numItems
= items
.GetCount();
393 XmString
*text
= new XmString
[numItems
];
396 for (i
= 0; i
< numItems
; i
++)
398 text
[i
] = wxStringToXmString(items
[i
]);
400 XmListAddItemsUnselected(listBox
, text
, numItems
, GetMotifPosition(pos
));
401 InsertNewItemsClientData(pos
, numItems
, clientData
, type
);
403 AllocClientData(numItems
);
405 unsigned int idx
= pos
;
406 for ( i
= 0; i
< numItems
; i
++, idx
++ )
408 text
[i
] = wxStringToXmString(items
[i
]);
409 XmListAddItemUnselected(listBox
, text
[i
], GetMotifPosition(idx
));
410 InsertNewItemClientData(idx
, clientData
, i
, type
);
413 for (i
= 0; i
< numItems
; i
++)
414 XmStringFree(text
[i
]);
417 m_noItems
+= numItems
;
419 SetSelectionPolicy();
421 return pos
+ numItems
- 1;
424 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
426 wxSizeKeeper
sk( this );
427 Widget listBox
= (Widget
) m_mainWidget
;
429 wxXmString
text( s
);
431 // delete the item and add it again.
432 // FIXME isn't there a way to change it in place?
433 XmListDeletePos (listBox
, n
+1);
434 XmListAddItem (listBox
, text(), n
+1);
439 void wxListBox::Command (wxCommandEvent
& event
)
441 if (event
.GetExtraLong())
442 SetSelection (event
.GetInt());
445 Deselect (event
.GetInt());
448 ProcessCommand (event
);
451 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
452 XmListCallbackStruct
* cbs
)
454 wxListBox
*item
= (wxListBox
*) clientData
;
456 if (item
->InSetValue())
461 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
462 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
464 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
466 int n
= cbs
->item_position
- 1;
467 wxCommandEvent
event (evtType
, item
->GetId());
468 if ( item
->HasClientObjectData() )
469 event
.SetClientObject( item
->GetClientObject(n
) );
470 else if ( item
->HasClientUntypedData() )
471 event
.SetClientData( item
->GetClientData(n
) );
473 event
.SetExtraLong(true);
474 event
.SetEventObject(item
);
475 event
.SetString( item
->GetString( n
) );
478 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
480 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
487 case XmCR_MULTIPLE_SELECT
:
488 case XmCR_BROWSE_SELECT
:
489 #if wxUSE_CHECKLISTBOX
490 item
->DoToggleItem( n
, x
);
492 case XmCR_DEFAULT_ACTION
:
493 item
->GetEventHandler()->ProcessEvent(event
);
495 case XmCR_EXTENDED_SELECT
:
496 switch (cbs
->selection_type
)
501 item
->DoToggleItem( n
, x
);
502 item
->GetEventHandler()->ProcessEvent(event
);
509 WXWidget
wxListBox::GetTopWidget() const
511 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
514 void wxListBox::ChangeBackgroundColour()
516 wxWindow::ChangeBackgroundColour();
518 Widget parent
= XtParent ((Widget
) m_mainWidget
);
521 XtVaGetValues (parent
,
522 XmNhorizontalScrollBar
, &hsb
,
523 XmNverticalScrollBar
, &vsb
,
526 /* TODO: should scrollbars be affected? Should probably have separate
527 * function to change them (by default, taken from wxSystemSettings)
529 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
530 wxDoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, true);
531 wxDoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, true);
534 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
537 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
540 // MBN: why change parent's background? It looks really ugly.
541 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
544 void wxListBox::ChangeForegroundColour()
546 wxWindow::ChangeForegroundColour();
548 Widget parent
= XtParent ((Widget
) m_mainWidget
);
551 XtVaGetValues(parent
,
552 XmNhorizontalScrollBar
, &hsb
,
553 XmNverticalScrollBar
, &vsb
,
556 /* TODO: should scrollbars be affected? Should probably have separate
557 function to change them (by default, taken from wxSystemSettings)
559 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
560 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
561 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
565 unsigned int wxListBox::GetCount() const
570 #define LIST_SCROLL_SPACING 6
572 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
575 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
579 XtVaGetValues( listWidget
,
581 XmNlistSpacing
, &spacing
,
582 XmNhighlightThickness
, &highlight
,
583 XmNlistMarginWidth
, &xmargin
,
584 XmNlistMarginHeight
, &ymargin
,
585 XmNshadowThickness
, &shadow
,
588 for( size_t i
= 0; i
< (size_t)max
; ++i
)
590 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
591 width
= wxMax( width
, x
);
594 // use some arbitrary value if there are no strings
599 window
->GetTextExtent( "v", &x
, &y
);
601 // make it a little larger than widest string, plus the scrollbar
602 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
603 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
605 // at least 3 items, at most 10
606 int height
= wxMax( 3, wxMin( 10, max
) ) *
607 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
609 return wxSize( width
, height
);
612 wxSize
wxListBox::DoGetBestSize() const
614 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );
617 #endif // wxUSE_LISTBOX