1 ///////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
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"
28 #pragma message disable nosimpint
32 #pragma message enable nosimpint
34 #include "wx/motif/private.h"
36 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
38 static void wxListBoxCallback(Widget w
,
40 XmListCallbackStruct
* cbs
);
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
46 // helper class to reduce code duplication
52 wxSizeKeeper( wxWindow
* w
)
55 m_w
->GetSize( &m_x
, &m_y
);
62 m_w
->GetSize( &x
, &y
);
63 if( x
!= m_x
|| y
!= m_y
)
64 m_w
->SetSize( -1, -1, m_x
, m_y
);
68 // ============================================================================
69 // list box control implementation
70 // ============================================================================
73 wxListBox::wxListBox()
78 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
81 int n
, const wxString choices
[],
83 const wxValidator
& validator
,
86 if( !wxControl::CreateControl( parent
, id
, pos
, size
, style
,
91 m_backgroundColour
= * wxWHITE
;
93 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
95 WXFontType fontType
= (WXFontType
)NULL
;
99 fontType
= m_font
.GetFontType(XtDisplay(parentWidget
));
104 XtSetArg( args
[count
], XmNlistSizePolicy
, XmCONSTANT
); ++count
;
105 XtSetArg( args
[count
], XmNselectionPolicy
,
106 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
107 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
112 XtSetArg( args
[count
], (String
)wxFont::GetFontTag(), fontType
);
115 if( m_windowStyle
& wxLB_ALWAYS_SB
)
117 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
122 XmCreateScrolledList(parentWidget
,
123 wxConstCast(name
.c_str(), char), args
, count
);
125 m_mainWidget
= (WXWidget
) listWidget
;
129 XtManageChild (listWidget
);
131 wxSize best
= GetBestSize();
132 if( size
.x
!= -1 ) best
.x
= size
.x
;
133 if( size
.y
!= -1 ) best
.y
= size
.y
;
135 XtAddCallback (listWidget
,
136 XmNbrowseSelectionCallback
,
137 (XtCallbackProc
) wxListBoxCallback
,
139 XtAddCallback (listWidget
,
140 XmNextendedSelectionCallback
,
141 (XtCallbackProc
) wxListBoxCallback
,
143 XtAddCallback (listWidget
,
144 XmNmultipleSelectionCallback
,
145 (XtCallbackProc
) wxListBoxCallback
,
147 XtAddCallback (listWidget
,
148 XmNdefaultActionCallback
,
149 (XtCallbackProc
) wxListBoxCallback
,
152 SetCanAddEventHandler(TRUE
);
153 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
154 pos
.x
, pos
.y
, best
.x
, best
.y
);
156 ChangeBackgroundColour();
161 wxListBox::~wxListBox()
163 if( HasClientObjectData() )
164 m_clientDataDict
.DestroyData();
167 void wxListBox::SetSelectionPolicy()
169 Widget listBox
= (Widget
)m_mainWidget
;
172 XtSetArg( args
[0], XmNlistSizePolicy
, XmCONSTANT
);
174 XtSetArg( args
[1], XmNselectionPolicy
,
175 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
176 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
179 XtSetValues( listBox
, args
, 2 );
182 void wxListBox::DoSetFirstItem( int N
)
188 XtVaGetValues ((Widget
) m_mainWidget
,
189 XmNvisibleItemCount
, &count
,
190 XmNitemCount
, &length
,
192 if ((N
+ count
) >= length
)
194 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
197 void wxListBox::Delete(int N
)
199 wxSizeKeeper
sk( this );
200 Widget listBox
= (Widget
) m_mainWidget
;
202 bool managed
= XtIsManaged(listBox
);
205 XtUnmanageChild (listBox
);
207 XmListDeletePos (listBox
, N
+ 1);
210 XtManageChild (listBox
);
213 m_clientDataDict
.Delete(N
, HasClientObjectData());
217 int wxListBox::DoAppend(const wxString
& item
)
219 wxSizeKeeper
sk( this );
220 Widget listBox
= (Widget
) m_mainWidget
;
222 bool managed
= XtIsManaged(listBox
);
225 XtUnmanageChild (listBox
);
227 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
228 wxXmString
text( item
);
229 // XmListAddItem(listBox, text, n + 1);
230 XmListAddItemUnselected (listBox
, text(), 0);
232 // It seems that if the list is cleared, we must re-ask for
233 // selection policy!!
234 SetSelectionPolicy();
237 XtManageChild (listBox
);
242 return GetCount() - 1;
245 void wxListBox::DoSetItems(const wxArrayString
& items
, void** clientData
)
247 wxSizeKeeper
sk( this );
248 Widget listBox
= (Widget
) m_mainWidget
;
250 if( HasClientObjectData() )
251 m_clientDataDict
.DestroyData();
253 bool managed
= XtIsManaged(listBox
);
256 XtUnmanageChild (listBox
);
257 XmString
*text
= new XmString
[items
.GetCount()];
259 for (i
= 0; i
< items
.GetCount(); ++i
)
260 text
[i
] = wxStringToXmString (items
[i
]);
263 for (i
= 0; i
< items
.GetCount(); ++i
)
264 m_clientDataDict
.Set(i
, (wxClientData
*)clientData
[i
], FALSE
);
266 XmListAddItems (listBox
, text
, items
.GetCount(), 0);
267 for (i
= 0; i
< items
.GetCount(); i
++)
268 XmStringFree (text
[i
]);
271 // It seems that if the list is cleared, we must re-ask for
272 // selection policy!!
273 SetSelectionPolicy();
276 XtManageChild (listBox
);
280 m_noItems
= items
.GetCount();
283 int wxDoFindStringInList(Widget w
, const wxString
& s
)
286 int *positions
= NULL
;
287 int no_positions
= 0;
288 bool success
= XmListGetMatchPos (w
, str(),
289 &positions
, &no_positions
);
293 int pos
= positions
[0];
295 XtFree ((char *) positions
);
302 int wxListBox::FindString(const wxString
& s
) const
304 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
307 void wxListBox::Clear()
312 wxSizeKeeper
sk( this );
313 Widget listBox
= (Widget
) m_mainWidget
;
315 XmListDeleteAllItems (listBox
);
316 if( HasClientObjectData() )
317 m_clientDataDict
.DestroyData();
324 void wxListBox::SetSelection(int N
, bool select
)
330 if (m_windowStyle
& wxLB_MULTIPLE
)
332 int *selections
= NULL
;
333 int n
= GetSelections (&selections
);
335 // This hack is supposed to work, to make it possible
336 // to select more than one item, but it DOESN'T under Motif 1.1.
338 XtVaSetValues ((Widget
) m_mainWidget
,
339 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
343 for (i
= 0; i
< n
; i
++)
344 XmListSelectPos ((Widget
) m_mainWidget
,
345 selections
[i
] + 1, FALSE
);
347 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
349 XtVaSetValues ((Widget
) m_mainWidget
,
350 XmNselectionPolicy
, XmEXTENDED_SELECT
,
355 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
359 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
361 m_inSetValue
= FALSE
;
364 bool wxListBox::IsSelected(int N
) const
366 // In Motif, no simple way to determine if the item is selected.
367 wxArrayInt theSelections
;
368 int count
= GetSelections (theSelections
);
374 for (j
= 0; j
< count
; j
++)
375 if (theSelections
[j
] == N
)
381 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
383 m_clientDataDict
.Set(n
, clientData
, FALSE
);
386 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
388 return m_clientDataDict
.Get(n
);
391 void *wxListBox::DoGetItemClientData(int N
) const
393 return (void*)m_clientDataDict
.Get(N
);
396 void wxListBox::DoSetItemClientData(int N
, void *Client_data
)
398 m_clientDataDict
.Set(N
, (wxClientData
*)Client_data
, FALSE
);
401 // Return number of selections and an array of selected integers
402 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
406 Widget listBox
= (Widget
) m_mainWidget
;
409 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
414 aSelections
.Alloc(posCnt
);
417 for (i
= 0; i
< posCnt
; i
++)
418 aSelections
.Add(posList
[i
] - 1);
420 XtFree ((char *) posList
);
430 // Get single selection, for single choice list items
431 int wxDoGetSelectionInList(Widget listBox
)
435 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
441 XtFree ((char *) posList
);
448 int wxListBox::GetSelection() const
450 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
453 // Find string for position
454 wxString
wxDoGetStringInList( Widget listBox
, int n
)
458 XtVaGetValues( listBox
,
459 XmNitemCount
, &count
,
462 if( n
< count
&& n
>= 0 )
463 return wxXmStringToString( strlist
[n
] );
465 return wxEmptyString
;
468 wxString
wxListBox::GetString( int n
) const
470 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
473 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
475 wxSizeKeeper
sk( this );
476 Widget listBox
= (Widget
) m_mainWidget
;
478 bool managed
= XtIsManaged(listBox
);
481 XtUnmanageChild(listBox
);
483 XmString
*text
= new XmString
[items
.GetCount()];
485 // Steve Hammes: Motif 1.1 compatibility
486 // #if XmVersion > 1100
487 // Corrected by Sergey Krasnov from Steve Hammes' code
489 for (i
= 0; i
< items
.GetCount(); i
++)
490 text
[i
] = wxStringToXmString(items
[i
]);
491 XmListAddItemsUnselected(listBox
, text
, items
.GetCount(), pos
+1);
493 for (i
= 0; i
< items
.GetCount(); i
++)
495 text
[i
] = wxStringToXmString(items
[i
]);
496 // Another Sergey correction
497 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1);
500 for (i
= 0; i
< items
.GetCount(); i
++)
501 XmStringFree(text
[i
]);
504 // It seems that if the list is cleared, we must re-ask for
505 // selection policy!!
506 SetSelectionPolicy();
509 XtManageChild(listBox
);
513 m_noItems
+= items
.GetCount();
516 void wxListBox::SetString(int N
, const wxString
& s
)
518 wxSizeKeeper
sk( this );
519 Widget listBox
= (Widget
) m_mainWidget
;
521 wxXmString
text( s
);
523 // delete the item and add it again.
524 // FIXME isn't there a way to change it in place?
525 XmListDeletePos (listBox
, N
+1);
526 XmListAddItem (listBox
, text(), N
+1);
531 void wxListBox::Command (wxCommandEvent
& event
)
533 if (event
.m_extraLong
)
534 SetSelection (event
.m_commandInt
);
537 Deselect (event
.m_commandInt
);
540 ProcessCommand (event
);
543 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
544 XmListCallbackStruct
* cbs
)
546 wxListBox
*item
= (wxListBox
*) clientData
;
548 if (item
->InSetValue())
553 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
554 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
556 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
558 int n
= cbs
->item_position
- 1;
559 wxCommandEvent
event (evtType
, item
->GetId());
560 if ( item
->HasClientObjectData() )
561 event
.SetClientObject( item
->GetClientObject(n
) );
562 else if ( item
->HasClientUntypedData() )
563 event
.SetClientData( item
->GetClientData(n
) );
564 event
.m_commandInt
= n
;
565 event
.m_extraLong
= TRUE
;
566 event
.SetEventObject(item
);
567 event
.SetString( item
->GetString( n
) );
570 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
572 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
579 case XmCR_MULTIPLE_SELECT
:
580 case XmCR_BROWSE_SELECT
:
581 #if wxUSE_CHECKLISTBOX
582 item
->DoToggleItem( n
, x
);
584 case XmCR_DEFAULT_ACTION
:
585 item
->GetEventHandler()->ProcessEvent(event
);
587 case XmCR_EXTENDED_SELECT
:
588 switch (cbs
->selection_type
)
593 item
->DoToggleItem( n
, x
);
594 item
->GetEventHandler()->ProcessEvent(event
);
601 WXWidget
wxListBox::GetTopWidget() const
603 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
606 void wxListBox::ChangeBackgroundColour()
608 wxWindow::ChangeBackgroundColour();
610 Widget parent
= XtParent ((Widget
) m_mainWidget
);
613 XtVaGetValues (parent
,
614 XmNhorizontalScrollBar
, &hsb
,
615 XmNverticalScrollBar
, &vsb
,
618 /* TODO: should scrollbars be affected? Should probably have separate
619 * function to change them (by default, taken from wxSystemSettings)
621 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
622 DoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, TRUE
);
623 DoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, TRUE
);
626 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
629 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
632 // MBN: why change parent's background? It looks really ugly.
633 // DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
636 void wxListBox::ChangeForegroundColour()
638 wxWindow::ChangeForegroundColour();
640 Widget parent
= XtParent ((Widget
) m_mainWidget
);
643 XtVaGetValues(parent
,
644 XmNhorizontalScrollBar
, &hsb
,
645 XmNverticalScrollBar
, &vsb
,
648 /* TODO: should scrollbars be affected? Should probably have separate
649 function to change them (by default, taken from wxSystemSettings)
651 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
652 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
653 DoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
657 int wxListBox::GetCount() const
662 #define LIST_SCROLL_SPACING 6
664 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
667 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
671 XtVaGetValues( listWidget
,
673 XmNlistSpacing
, &spacing
,
674 XmNhighlightThickness
, &highlight
,
675 XmNlistMarginWidth
, &xmargin
,
676 XmNlistMarginHeight
, &ymargin
,
677 XmNshadowThickness
, &shadow
,
680 for( size_t i
= 0; i
< (size_t)max
; ++i
)
682 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
683 width
= wxMax( width
, x
);
686 // use some arbitrary value if there are no strings
691 window
->GetTextExtent( "v", &x
, &y
);
693 // make it a little larger than widest string, plus the scrollbar
694 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
695 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
697 // at least 3 items, at most 10
698 int height
= wxMax( 3, wxMin( 10, max
) ) *
699 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
701 return wxSize( width
, height
);
704 wxSize
wxListBox::DoGetBestSize() const
706 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );