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"
17 #define XtParent XTPARENT
18 #define XtDisplay XTDISPLAY
21 # include "wx/listbox.h"
22 #include "wx/settings.h"
23 #include "wx/dynarray.h"
26 #include "wx/arrstr.h"
29 #pragma message disable nosimpint
33 #pragma message enable nosimpint
35 #include "wx/motif/private.h"
37 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
39 static void wxListBoxCallback(Widget w
,
41 XmListCallbackStruct
* cbs
);
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
47 // helper class to reduce code duplication
53 wxSizeKeeper( wxWindow
* w
)
56 m_w
->GetSize( &m_x
, &m_y
);
63 m_w
->GetSize( &x
, &y
);
64 if( x
!= m_x
|| y
!= m_y
)
65 m_w
->SetSize( -1, -1, m_x
, m_y
);
69 // ============================================================================
70 // list box control implementation
71 // ============================================================================
74 wxListBox::wxListBox()
79 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
82 int n
, const wxString choices
[],
84 const wxValidator
& validator
,
87 if( !wxControl::CreateControl( parent
, id
, pos
, size
, style
,
92 m_backgroundColour
= * wxWHITE
;
94 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
96 WXFontType fontType
= (WXFontType
)NULL
;
100 fontType
= m_font
.GetFontType(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
], (String
)wxFont::GetFontTag(), fontType
);
116 if( m_windowStyle
& wxLB_ALWAYS_SB
)
118 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
123 XmCreateScrolledList(parentWidget
,
124 wxConstCast(name
.c_str(), char), args
, count
);
126 m_mainWidget
= (WXWidget
) listWidget
;
130 XtManageChild (listWidget
);
132 wxSize best
= GetBestSize();
133 if( size
.x
!= -1 ) best
.x
= size
.x
;
134 if( size
.y
!= -1 ) best
.y
= size
.y
;
136 XtAddCallback (listWidget
,
137 XmNbrowseSelectionCallback
,
138 (XtCallbackProc
) wxListBoxCallback
,
140 XtAddCallback (listWidget
,
141 XmNextendedSelectionCallback
,
142 (XtCallbackProc
) wxListBoxCallback
,
144 XtAddCallback (listWidget
,
145 XmNmultipleSelectionCallback
,
146 (XtCallbackProc
) wxListBoxCallback
,
148 XtAddCallback (listWidget
,
149 XmNdefaultActionCallback
,
150 (XtCallbackProc
) wxListBoxCallback
,
153 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
154 pos
.x
, pos
.y
, best
.x
, best
.y
);
156 ChangeBackgroundColour();
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 wxListBox::~wxListBox()
176 if( HasClientObjectData() )
177 m_clientDataDict
.DestroyData();
180 void wxListBox::SetSelectionPolicy()
182 Widget listBox
= (Widget
)m_mainWidget
;
185 XtSetArg( args
[0], XmNlistSizePolicy
, XmCONSTANT
);
187 XtSetArg( args
[1], XmNselectionPolicy
,
188 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
189 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
192 XtSetValues( listBox
, args
, 2 );
195 void wxListBox::DoSetFirstItem( int N
)
201 XtVaGetValues ((Widget
) m_mainWidget
,
202 XmNvisibleItemCount
, &count
,
203 XmNitemCount
, &length
,
205 if ((N
+ count
) >= length
)
207 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
210 void wxListBox::Delete(int N
)
212 wxSizeKeeper
sk( this );
213 Widget listBox
= (Widget
) m_mainWidget
;
215 bool managed
= XtIsManaged(listBox
);
218 XtUnmanageChild (listBox
);
220 XmListDeletePos (listBox
, N
+ 1);
223 XtManageChild (listBox
);
226 m_clientDataDict
.Delete(N
, HasClientObjectData());
230 int wxListBox::DoAppend(const wxString
& item
)
232 wxSizeKeeper
sk( this );
233 Widget listBox
= (Widget
) m_mainWidget
;
235 bool managed
= XtIsManaged(listBox
);
238 XtUnmanageChild (listBox
);
240 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
241 wxXmString
text( item
);
242 // XmListAddItem(listBox, text, n + 1);
243 XmListAddItemUnselected (listBox
, text(), 0);
245 // It seems that if the list is cleared, we must re-ask for
246 // selection policy!!
247 SetSelectionPolicy();
250 XtManageChild (listBox
);
255 return GetCount() - 1;
258 void wxListBox::DoSetItems(const wxArrayString
& items
, void** clientData
)
260 wxSizeKeeper
sk( this );
261 Widget listBox
= (Widget
) m_mainWidget
;
263 if( HasClientObjectData() )
264 m_clientDataDict
.DestroyData();
266 bool managed
= XtIsManaged(listBox
);
269 XtUnmanageChild (listBox
);
270 XmString
*text
= new XmString
[items
.GetCount()];
272 for (i
= 0; i
< items
.GetCount(); ++i
)
273 text
[i
] = wxStringToXmString (items
[i
]);
276 for (i
= 0; i
< items
.GetCount(); ++i
)
277 m_clientDataDict
.Set(i
, (wxClientData
*)clientData
[i
], FALSE
);
279 XmListAddItems (listBox
, text
, items
.GetCount(), 0);
280 for (i
= 0; i
< items
.GetCount(); i
++)
281 XmStringFree (text
[i
]);
284 // It seems that if the list is cleared, we must re-ask for
285 // selection policy!!
286 SetSelectionPolicy();
289 XtManageChild (listBox
);
293 m_noItems
= items
.GetCount();
296 int wxDoFindStringInList(Widget w
, const wxString
& s
)
299 int *positions
= NULL
;
300 int no_positions
= 0;
301 bool success
= XmListGetMatchPos (w
, str(),
302 &positions
, &no_positions
);
306 int pos
= positions
[0];
308 XtFree ((char *) positions
);
315 int wxListBox::FindString(const wxString
& s
) const
317 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
320 void wxListBox::Clear()
325 wxSizeKeeper
sk( this );
326 Widget listBox
= (Widget
) m_mainWidget
;
328 XmListDeleteAllItems (listBox
);
329 if( HasClientObjectData() )
330 m_clientDataDict
.DestroyData();
337 void wxListBox::SetSelection(int N
, bool select
)
343 if (m_windowStyle
& wxLB_MULTIPLE
)
345 int *selections
= NULL
;
346 int n
= GetSelections (&selections
);
348 // This hack is supposed to work, to make it possible
349 // to select more than one item, but it DOESN'T under Motif 1.1.
351 XtVaSetValues ((Widget
) m_mainWidget
,
352 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
356 for (i
= 0; i
< n
; i
++)
357 XmListSelectPos ((Widget
) m_mainWidget
,
358 selections
[i
] + 1, FALSE
);
360 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
362 XtVaSetValues ((Widget
) m_mainWidget
,
363 XmNselectionPolicy
, XmEXTENDED_SELECT
,
368 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
372 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
374 m_inSetValue
= FALSE
;
377 bool wxListBox::IsSelected(int N
) const
379 // In Motif, no simple way to determine if the item is selected.
380 wxArrayInt theSelections
;
381 int count
= GetSelections (theSelections
);
387 for (j
= 0; j
< count
; j
++)
388 if (theSelections
[j
] == N
)
394 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
396 m_clientDataDict
.Set(n
, clientData
, FALSE
);
399 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
401 return m_clientDataDict
.Get(n
);
404 void *wxListBox::DoGetItemClientData(int N
) const
406 return (void*)m_clientDataDict
.Get(N
);
409 void wxListBox::DoSetItemClientData(int N
, void *Client_data
)
411 m_clientDataDict
.Set(N
, (wxClientData
*)Client_data
, FALSE
);
414 // Return number of selections and an array of selected integers
415 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
419 Widget listBox
= (Widget
) m_mainWidget
;
422 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
427 aSelections
.Alloc(posCnt
);
430 for (i
= 0; i
< posCnt
; i
++)
431 aSelections
.Add(posList
[i
] - 1);
433 XtFree ((char *) posList
);
443 // Get single selection, for single choice list items
444 int wxDoGetSelectionInList(Widget listBox
)
448 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
454 XtFree ((char *) posList
);
461 int wxListBox::GetSelection() const
463 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
466 // Find string for position
467 wxString
wxDoGetStringInList( Widget listBox
, int n
)
471 XtVaGetValues( listBox
,
472 XmNitemCount
, &count
,
475 if( n
< count
&& n
>= 0 )
476 return wxXmStringToString( strlist
[n
] );
478 return wxEmptyString
;
481 wxString
wxListBox::GetString( int n
) const
483 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
486 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
488 wxSizeKeeper
sk( this );
489 Widget listBox
= (Widget
) m_mainWidget
;
491 bool managed
= XtIsManaged(listBox
);
494 XtUnmanageChild(listBox
);
496 XmString
*text
= new XmString
[items
.GetCount()];
498 // Steve Hammes: Motif 1.1 compatibility
499 // #if XmVersion > 1100
500 // Corrected by Sergey Krasnov from Steve Hammes' code
502 for (i
= 0; i
< items
.GetCount(); i
++)
503 text
[i
] = wxStringToXmString(items
[i
]);
504 XmListAddItemsUnselected(listBox
, text
, items
.GetCount(), pos
+1);
506 for (i
= 0; i
< items
.GetCount(); i
++)
508 text
[i
] = wxStringToXmString(items
[i
]);
509 // Another Sergey correction
510 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1);
513 for (i
= 0; i
< items
.GetCount(); i
++)
514 XmStringFree(text
[i
]);
517 // It seems that if the list is cleared, we must re-ask for
518 // selection policy!!
519 SetSelectionPolicy();
522 XtManageChild(listBox
);
526 m_noItems
+= items
.GetCount();
529 void wxListBox::SetString(int N
, const wxString
& s
)
531 wxSizeKeeper
sk( this );
532 Widget listBox
= (Widget
) m_mainWidget
;
534 wxXmString
text( s
);
536 // delete the item and add it again.
537 // FIXME isn't there a way to change it in place?
538 XmListDeletePos (listBox
, N
+1);
539 XmListAddItem (listBox
, text(), N
+1);
544 void wxListBox::Command (wxCommandEvent
& event
)
546 if (event
.m_extraLong
)
547 SetSelection (event
.m_commandInt
);
550 Deselect (event
.m_commandInt
);
553 ProcessCommand (event
);
556 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
557 XmListCallbackStruct
* cbs
)
559 wxListBox
*item
= (wxListBox
*) clientData
;
561 if (item
->InSetValue())
566 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
567 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
569 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
571 int n
= cbs
->item_position
- 1;
572 wxCommandEvent
event (evtType
, item
->GetId());
573 if ( item
->HasClientObjectData() )
574 event
.SetClientObject( item
->GetClientObject(n
) );
575 else if ( item
->HasClientUntypedData() )
576 event
.SetClientData( item
->GetClientData(n
) );
577 event
.m_commandInt
= n
;
578 event
.m_extraLong
= TRUE
;
579 event
.SetEventObject(item
);
580 event
.SetString( item
->GetString( n
) );
583 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
585 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
592 case XmCR_MULTIPLE_SELECT
:
593 case XmCR_BROWSE_SELECT
:
594 #if wxUSE_CHECKLISTBOX
595 item
->DoToggleItem( n
, x
);
597 case XmCR_DEFAULT_ACTION
:
598 item
->GetEventHandler()->ProcessEvent(event
);
600 case XmCR_EXTENDED_SELECT
:
601 switch (cbs
->selection_type
)
606 item
->DoToggleItem( n
, x
);
607 item
->GetEventHandler()->ProcessEvent(event
);
614 WXWidget
wxListBox::GetTopWidget() const
616 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
619 void wxListBox::ChangeBackgroundColour()
621 wxWindow::ChangeBackgroundColour();
623 Widget parent
= XtParent ((Widget
) m_mainWidget
);
626 XtVaGetValues (parent
,
627 XmNhorizontalScrollBar
, &hsb
,
628 XmNverticalScrollBar
, &vsb
,
631 /* TODO: should scrollbars be affected? Should probably have separate
632 * function to change them (by default, taken from wxSystemSettings)
634 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
635 wxDoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, TRUE
);
636 wxDoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, TRUE
);
639 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
642 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
645 // MBN: why change parent's background? It looks really ugly.
646 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
649 void wxListBox::ChangeForegroundColour()
651 wxWindow::ChangeForegroundColour();
653 Widget parent
= XtParent ((Widget
) m_mainWidget
);
656 XtVaGetValues(parent
,
657 XmNhorizontalScrollBar
, &hsb
,
658 XmNverticalScrollBar
, &vsb
,
661 /* TODO: should scrollbars be affected? Should probably have separate
662 function to change them (by default, taken from wxSystemSettings)
664 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
665 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
666 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
670 int wxListBox::GetCount() const
675 #define LIST_SCROLL_SPACING 6
677 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
680 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
684 XtVaGetValues( listWidget
,
686 XmNlistSpacing
, &spacing
,
687 XmNhighlightThickness
, &highlight
,
688 XmNlistMarginWidth
, &xmargin
,
689 XmNlistMarginHeight
, &ymargin
,
690 XmNshadowThickness
, &shadow
,
693 for( size_t i
= 0; i
< (size_t)max
; ++i
)
695 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
696 width
= wxMax( width
, x
);
699 // use some arbitrary value if there are no strings
704 window
->GetTextExtent( "v", &x
, &y
);
706 // make it a little larger than widest string, plus the scrollbar
707 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
708 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
710 // at least 3 items, at most 10
711 int height
= wxMax( 3, wxMin( 10, max
) ) *
712 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
714 return wxSize( width
, height
);
717 wxSize
wxListBox::DoGetBestSize() const
719 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );