1 ///////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "listbox.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
20 #define XtParent XTPARENT
21 #define XtDisplay XTDISPLAY
24 # include "wx/listbox.h"
25 #include "wx/settings.h"
26 #include "wx/dynarray.h"
29 #include "wx/arrstr.h"
32 #pragma message disable nosimpint
36 #pragma message enable nosimpint
38 #include "wx/motif/private.h"
40 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
42 static void wxListBoxCallback(Widget w
,
44 XmListCallbackStruct
* cbs
);
46 // ----------------------------------------------------------------------------
48 // ----------------------------------------------------------------------------
50 // helper class to reduce code duplication
56 wxSizeKeeper( wxWindow
* w
)
59 m_w
->GetSize( &m_x
, &m_y
);
66 m_w
->GetSize( &x
, &y
);
67 if( x
!= m_x
|| y
!= m_y
)
68 m_w
->SetSize( -1, -1, m_x
, m_y
);
72 // ============================================================================
73 // list box control implementation
74 // ============================================================================
77 wxListBox::wxListBox()
82 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
85 int n
, const wxString choices
[],
87 const wxValidator
& validator
,
90 if( !wxControl::CreateControl( parent
, id
, pos
, size
, style
,
95 m_backgroundColour
= * wxWHITE
;
97 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
99 WXFontType fontType
= (WXFontType
)NULL
;
103 fontType
= m_font
.GetFontType(XtDisplay(parentWidget
));
108 XtSetArg( args
[count
], XmNlistSizePolicy
, XmCONSTANT
); ++count
;
109 XtSetArg( args
[count
], XmNselectionPolicy
,
110 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
111 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
116 XtSetArg( args
[count
], (String
)wxFont::GetFontTag(), fontType
);
119 if( m_windowStyle
& wxLB_ALWAYS_SB
)
121 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
126 XmCreateScrolledList(parentWidget
,
127 wxConstCast(name
.c_str(), char), args
, count
);
129 m_mainWidget
= (WXWidget
) listWidget
;
133 XtManageChild (listWidget
);
135 wxSize best
= GetBestSize();
136 if( size
.x
!= -1 ) best
.x
= size
.x
;
137 if( size
.y
!= -1 ) best
.y
= size
.y
;
139 XtAddCallback (listWidget
,
140 XmNbrowseSelectionCallback
,
141 (XtCallbackProc
) wxListBoxCallback
,
143 XtAddCallback (listWidget
,
144 XmNextendedSelectionCallback
,
145 (XtCallbackProc
) wxListBoxCallback
,
147 XtAddCallback (listWidget
,
148 XmNmultipleSelectionCallback
,
149 (XtCallbackProc
) wxListBoxCallback
,
151 XtAddCallback (listWidget
,
152 XmNdefaultActionCallback
,
153 (XtCallbackProc
) wxListBoxCallback
,
156 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
157 pos
.x
, pos
.y
, best
.x
, best
.y
);
159 ChangeBackgroundColour();
164 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
167 const wxArrayString
& choices
,
169 const wxValidator
& validator
,
170 const wxString
& name
)
172 wxCArrayString
chs(choices
);
173 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
174 style
, validator
, name
);
177 wxListBox::~wxListBox()
179 if( HasClientObjectData() )
180 m_clientDataDict
.DestroyData();
183 void wxListBox::SetSelectionPolicy()
185 Widget listBox
= (Widget
)m_mainWidget
;
188 XtSetArg( args
[0], XmNlistSizePolicy
, XmCONSTANT
);
190 XtSetArg( args
[1], XmNselectionPolicy
,
191 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
192 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
195 XtSetValues( listBox
, args
, 2 );
198 void wxListBox::DoSetFirstItem( int N
)
204 XtVaGetValues ((Widget
) m_mainWidget
,
205 XmNvisibleItemCount
, &count
,
206 XmNitemCount
, &length
,
208 if ((N
+ count
) >= length
)
210 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
213 void wxListBox::Delete(int N
)
215 wxSizeKeeper
sk( this );
216 Widget listBox
= (Widget
) m_mainWidget
;
218 bool managed
= XtIsManaged(listBox
);
221 XtUnmanageChild (listBox
);
223 XmListDeletePos (listBox
, N
+ 1);
226 XtManageChild (listBox
);
229 m_clientDataDict
.Delete(N
, HasClientObjectData());
233 int wxListBox::DoAppend(const wxString
& item
)
235 wxSizeKeeper
sk( this );
236 Widget listBox
= (Widget
) m_mainWidget
;
238 bool managed
= XtIsManaged(listBox
);
241 XtUnmanageChild (listBox
);
243 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
244 wxXmString
text( item
);
245 // XmListAddItem(listBox, text, n + 1);
246 XmListAddItemUnselected (listBox
, text(), 0);
248 // It seems that if the list is cleared, we must re-ask for
249 // selection policy!!
250 SetSelectionPolicy();
253 XtManageChild (listBox
);
258 return GetCount() - 1;
261 void wxListBox::DoSetItems(const wxArrayString
& items
, void** clientData
)
263 wxSizeKeeper
sk( this );
264 Widget listBox
= (Widget
) m_mainWidget
;
266 if( HasClientObjectData() )
267 m_clientDataDict
.DestroyData();
269 bool managed
= XtIsManaged(listBox
);
272 XtUnmanageChild (listBox
);
273 XmString
*text
= new XmString
[items
.GetCount()];
275 for (i
= 0; i
< items
.GetCount(); ++i
)
276 text
[i
] = wxStringToXmString (items
[i
]);
279 for (i
= 0; i
< items
.GetCount(); ++i
)
280 m_clientDataDict
.Set(i
, (wxClientData
*)clientData
[i
], FALSE
);
282 XmListAddItems (listBox
, text
, items
.GetCount(), 0);
283 for (i
= 0; i
< items
.GetCount(); i
++)
284 XmStringFree (text
[i
]);
287 // It seems that if the list is cleared, we must re-ask for
288 // selection policy!!
289 SetSelectionPolicy();
292 XtManageChild (listBox
);
296 m_noItems
= items
.GetCount();
299 int wxDoFindStringInList(Widget w
, const wxString
& s
)
302 int *positions
= NULL
;
303 int no_positions
= 0;
304 bool success
= XmListGetMatchPos (w
, str(),
305 &positions
, &no_positions
);
309 int pos
= positions
[0];
311 XtFree ((char *) positions
);
318 int wxListBox::FindString(const wxString
& s
) const
320 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
323 void wxListBox::Clear()
328 wxSizeKeeper
sk( this );
329 Widget listBox
= (Widget
) m_mainWidget
;
331 XmListDeleteAllItems (listBox
);
332 if( HasClientObjectData() )
333 m_clientDataDict
.DestroyData();
340 void wxListBox::SetSelection(int N
, bool select
)
346 if (m_windowStyle
& wxLB_MULTIPLE
)
348 int *selections
= NULL
;
349 int n
= GetSelections (&selections
);
351 // This hack is supposed to work, to make it possible
352 // to select more than one item, but it DOESN'T under Motif 1.1.
354 XtVaSetValues ((Widget
) m_mainWidget
,
355 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
359 for (i
= 0; i
< n
; i
++)
360 XmListSelectPos ((Widget
) m_mainWidget
,
361 selections
[i
] + 1, FALSE
);
363 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
365 XtVaSetValues ((Widget
) m_mainWidget
,
366 XmNselectionPolicy
, XmEXTENDED_SELECT
,
371 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
375 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
377 m_inSetValue
= FALSE
;
380 bool wxListBox::IsSelected(int N
) const
382 // In Motif, no simple way to determine if the item is selected.
383 wxArrayInt theSelections
;
384 int count
= GetSelections (theSelections
);
390 for (j
= 0; j
< count
; j
++)
391 if (theSelections
[j
] == N
)
397 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
399 m_clientDataDict
.Set(n
, clientData
, FALSE
);
402 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
404 return m_clientDataDict
.Get(n
);
407 void *wxListBox::DoGetItemClientData(int N
) const
409 return (void*)m_clientDataDict
.Get(N
);
412 void wxListBox::DoSetItemClientData(int N
, void *Client_data
)
414 m_clientDataDict
.Set(N
, (wxClientData
*)Client_data
, FALSE
);
417 // Return number of selections and an array of selected integers
418 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
422 Widget listBox
= (Widget
) m_mainWidget
;
425 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
430 aSelections
.Alloc(posCnt
);
433 for (i
= 0; i
< posCnt
; i
++)
434 aSelections
.Add(posList
[i
] - 1);
436 XtFree ((char *) posList
);
446 // Get single selection, for single choice list items
447 int wxDoGetSelectionInList(Widget listBox
)
451 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
457 XtFree ((char *) posList
);
464 int wxListBox::GetSelection() const
466 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
469 // Find string for position
470 wxString
wxDoGetStringInList( Widget listBox
, int n
)
474 XtVaGetValues( listBox
,
475 XmNitemCount
, &count
,
478 if( n
< count
&& n
>= 0 )
479 return wxXmStringToString( strlist
[n
] );
481 return wxEmptyString
;
484 wxString
wxListBox::GetString( int n
) const
486 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
489 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
491 wxSizeKeeper
sk( this );
492 Widget listBox
= (Widget
) m_mainWidget
;
494 bool managed
= XtIsManaged(listBox
);
497 XtUnmanageChild(listBox
);
499 XmString
*text
= new XmString
[items
.GetCount()];
501 // Steve Hammes: Motif 1.1 compatibility
502 // #if XmVersion > 1100
503 // Corrected by Sergey Krasnov from Steve Hammes' code
505 for (i
= 0; i
< items
.GetCount(); i
++)
506 text
[i
] = wxStringToXmString(items
[i
]);
507 XmListAddItemsUnselected(listBox
, text
, items
.GetCount(), pos
+1);
509 for (i
= 0; i
< items
.GetCount(); i
++)
511 text
[i
] = wxStringToXmString(items
[i
]);
512 // Another Sergey correction
513 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1);
516 for (i
= 0; i
< items
.GetCount(); i
++)
517 XmStringFree(text
[i
]);
520 // It seems that if the list is cleared, we must re-ask for
521 // selection policy!!
522 SetSelectionPolicy();
525 XtManageChild(listBox
);
529 m_noItems
+= items
.GetCount();
532 void wxListBox::SetString(int N
, const wxString
& s
)
534 wxSizeKeeper
sk( this );
535 Widget listBox
= (Widget
) m_mainWidget
;
537 wxXmString
text( s
);
539 // delete the item and add it again.
540 // FIXME isn't there a way to change it in place?
541 XmListDeletePos (listBox
, N
+1);
542 XmListAddItem (listBox
, text(), N
+1);
547 void wxListBox::Command (wxCommandEvent
& event
)
549 if (event
.GetExtraLong())
550 SetSelection (event
.GetInt());
553 Deselect (event
.GetInt());
556 ProcessCommand (event
);
559 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
560 XmListCallbackStruct
* cbs
)
562 wxListBox
*item
= (wxListBox
*) clientData
;
564 if (item
->InSetValue())
569 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
570 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
572 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
574 int n
= cbs
->item_position
- 1;
575 wxCommandEvent
event (evtType
, item
->GetId());
576 if ( item
->HasClientObjectData() )
577 event
.SetClientObject( item
->GetClientObject(n
) );
578 else if ( item
->HasClientUntypedData() )
579 event
.SetClientData( item
->GetClientData(n
) );
581 event
.SetExtraLong(TRUE
);
582 event
.SetEventObject(item
);
583 event
.SetString( item
->GetString( n
) );
586 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
588 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
595 case XmCR_MULTIPLE_SELECT
:
596 case XmCR_BROWSE_SELECT
:
597 #if wxUSE_CHECKLISTBOX
598 item
->DoToggleItem( n
, x
);
600 case XmCR_DEFAULT_ACTION
:
601 item
->GetEventHandler()->ProcessEvent(event
);
603 case XmCR_EXTENDED_SELECT
:
604 switch (cbs
->selection_type
)
609 item
->DoToggleItem( n
, x
);
610 item
->GetEventHandler()->ProcessEvent(event
);
617 WXWidget
wxListBox::GetTopWidget() const
619 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
622 void wxListBox::ChangeBackgroundColour()
624 wxWindow::ChangeBackgroundColour();
626 Widget parent
= XtParent ((Widget
) m_mainWidget
);
629 XtVaGetValues (parent
,
630 XmNhorizontalScrollBar
, &hsb
,
631 XmNverticalScrollBar
, &vsb
,
634 /* TODO: should scrollbars be affected? Should probably have separate
635 * function to change them (by default, taken from wxSystemSettings)
637 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
638 wxDoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, TRUE
);
639 wxDoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, TRUE
);
642 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
645 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
648 // MBN: why change parent's background? It looks really ugly.
649 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
652 void wxListBox::ChangeForegroundColour()
654 wxWindow::ChangeForegroundColour();
656 Widget parent
= XtParent ((Widget
) m_mainWidget
);
659 XtVaGetValues(parent
,
660 XmNhorizontalScrollBar
, &hsb
,
661 XmNverticalScrollBar
, &vsb
,
664 /* TODO: should scrollbars be affected? Should probably have separate
665 function to change them (by default, taken from wxSystemSettings)
667 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
668 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
669 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
673 int wxListBox::GetCount() const
678 #define LIST_SCROLL_SPACING 6
680 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
683 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
687 XtVaGetValues( listWidget
,
689 XmNlistSpacing
, &spacing
,
690 XmNhighlightThickness
, &highlight
,
691 XmNlistMarginWidth
, &xmargin
,
692 XmNlistMarginHeight
, &ymargin
,
693 XmNshadowThickness
, &shadow
,
696 for( size_t i
= 0; i
< (size_t)max
; ++i
)
698 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
699 width
= wxMax( width
, x
);
702 // use some arbitrary value if there are no strings
707 window
->GetTextExtent( "v", &x
, &y
);
709 // make it a little larger than widest string, plus the scrollbar
710 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
711 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
713 // at least 3 items, at most 10
714 int height
= wxMax( 3, wxMin( 10, max
) ) *
715 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
717 return wxSize( width
, height
);
720 wxSize
wxListBox::DoGetBestSize() const
722 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );