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
, wxControl
)
43 static void wxListBoxCallback(Widget w
,
45 XmListCallbackStruct
* cbs
);
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
51 // helper class to reduce code duplication
57 wxSizeKeeper( wxWindow
* w
)
60 m_w
->GetSize( &m_x
, &m_y
);
67 m_w
->GetSize( &x
, &y
);
68 if( x
!= m_x
|| y
!= m_y
)
69 m_w
->SetSize( -1, -1, m_x
, m_y
);
73 // ============================================================================
74 // list box control implementation
75 // ============================================================================
78 wxListBox::wxListBox()
83 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
86 int n
, const wxString choices
[],
88 const wxValidator
& validator
,
91 if( !wxControl::CreateControl( parent
, id
, pos
, size
, style
,
95 m_noItems
= (unsigned int)n
;
96 m_backgroundColour
= * wxWHITE
;
98 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
99 Display
* dpy
= XtDisplay(parentWidget
);
103 XtSetArg( args
[count
], XmNlistSizePolicy
, XmCONSTANT
); ++count
;
104 XtSetArg( args
[count
], XmNselectionPolicy
,
105 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
106 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
111 XtSetArg( args
[count
],
112 (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
) );
115 if( m_windowStyle
& wxLB_ALWAYS_SB
)
117 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
122 XmCreateScrolledList(parentWidget
,
123 wxConstCast(name
.mb_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 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
153 pos
.x
, pos
.y
, best
.x
, best
.y
);
155 ChangeBackgroundColour();
160 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
163 const wxArrayString
& choices
,
165 const wxValidator
& validator
,
166 const wxString
& name
)
168 wxCArrayString
chs(choices
);
169 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
170 style
, validator
, name
);
173 wxListBox::~wxListBox()
175 if( HasClientObjectData() )
176 m_clientDataDict
.DestroyData();
179 void wxListBox::SetSelectionPolicy()
181 Widget listBox
= (Widget
)m_mainWidget
;
184 XtSetArg( args
[0], XmNlistSizePolicy
, XmCONSTANT
);
186 XtSetArg( args
[1], XmNselectionPolicy
,
187 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
188 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
191 XtSetValues( listBox
, args
, 2 );
194 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(unsigned int n
)
212 Widget listBox
= (Widget
) m_mainWidget
;
214 XmListDeletePos (listBox
, n
+ 1);
216 m_clientDataDict
.Delete(n
, HasClientObjectData());
220 int wxListBox::DoAppend(const wxString
& item
)
222 Widget listBox
= (Widget
) m_mainWidget
;
225 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
226 wxXmString
text( item
);
227 // XmListAddItem(listBox, text, n + 1);
228 XmListAddItemUnselected (listBox
, text(), 0);
230 // It seems that if the list is cleared, we must re-ask for
231 // selection policy!!
232 SetSelectionPolicy();
236 return GetCount() - 1;
239 void wxListBox::DoSetItems(const wxArrayString
& items
, void** clientData
)
241 Widget listBox
= (Widget
) m_mainWidget
;
243 if( HasClientObjectData() )
244 m_clientDataDict
.DestroyData();
246 XmString
*text
= new XmString
[items
.GetCount()];
248 for (i
= 0; i
< items
.GetCount(); ++i
)
249 text
[i
] = wxStringToXmString (items
[i
]);
252 for (i
= 0; i
< items
.GetCount(); ++i
)
253 m_clientDataDict
.Set(i
, (wxClientData
*)clientData
[i
], false);
255 XmListAddItems (listBox
, text
, items
.GetCount(), 0);
256 for (i
= 0; i
< items
.GetCount(); i
++)
257 XmStringFree (text
[i
]);
260 // It seems that if the list is cleared, we must re-ask for
261 // selection policy!!
262 SetSelectionPolicy();
264 m_noItems
= items
.GetCount();
267 int wxDoFindStringInList(Widget w
, const wxString
& s
)
270 int *positions
= NULL
;
271 int no_positions
= 0;
272 bool success
= XmListGetMatchPos (w
, str(),
273 &positions
, &no_positions
);
275 if (success
&& positions
)
277 int pos
= positions
[0];
278 XtFree ((char *) positions
);
285 int wxListBox::FindString(const wxString
& s
, bool WXUNUSED(bCase
)) const
287 // FIXME: back to base class for not supported value of bCase
289 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
292 void wxListBox::Clear()
297 wxSizeKeeper
sk( this );
298 Widget listBox
= (Widget
) m_mainWidget
;
300 XmListDeleteAllItems (listBox
);
301 if( HasClientObjectData() )
302 m_clientDataDict
.DestroyData();
309 void wxListBox::DoSetSelection(int N
, bool select
)
315 if (m_windowStyle
& wxLB_MULTIPLE
)
317 int *selections
= NULL
;
318 int n
= GetSelections (&selections
);
320 // This hack is supposed to work, to make it possible
321 // to select more than one item, but it DOESN'T under Motif 1.1.
323 XtVaSetValues ((Widget
) m_mainWidget
,
324 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
328 for (i
= 0; i
< n
; i
++)
329 XmListSelectPos ((Widget
) m_mainWidget
,
330 selections
[i
] + 1, False
);
332 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
334 XtVaSetValues ((Widget
) m_mainWidget
,
335 XmNselectionPolicy
, XmEXTENDED_SELECT
,
340 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
344 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
346 m_inSetValue
= false;
349 bool wxListBox::IsSelected(int N
) const
351 // In Motif, no simple way to determine if the item is selected.
352 wxArrayInt theSelections
;
353 int count
= GetSelections (theSelections
);
359 for (j
= 0; j
< count
; j
++)
360 if (theSelections
[j
] == N
)
366 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
368 m_clientDataDict
.Set(n
, clientData
, false);
371 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const
373 return m_clientDataDict
.Get(n
);
376 void *wxListBox::DoGetItemClientData(unsigned int n
) const
378 return (void*)m_clientDataDict
.Get(n
);
381 void wxListBox::DoSetItemClientData(unsigned int n
, void *Client_data
)
383 m_clientDataDict
.Set(n
, (wxClientData
*)Client_data
, false);
386 // Return number of selections and an array of selected integers
387 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
391 Widget listBox
= (Widget
) m_mainWidget
;
394 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
399 aSelections
.Alloc(posCnt
);
402 for (i
= 0; i
< posCnt
; i
++)
403 aSelections
.Add(posList
[i
] - 1);
405 XtFree ((char *) posList
);
415 // Get single selection, for single choice list items
416 int wxDoGetSelectionInList(Widget listBox
)
420 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
426 XtFree ((char *) posList
);
433 int wxListBox::GetSelection() const
435 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
438 // Find string for position
439 wxString
wxDoGetStringInList( Widget listBox
, int n
)
443 XtVaGetValues( listBox
,
444 XmNitemCount
, &count
,
447 if( n
< count
&& n
>= 0 )
448 return wxXmStringToString( strlist
[n
] );
450 return wxEmptyString
;
453 wxString
wxListBox::GetString(unsigned int n
) const
455 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
458 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
)
460 Widget listBox
= (Widget
) m_mainWidget
;
462 XmString
*text
= new XmString
[items
.GetCount()];
464 // Steve Hammes: Motif 1.1 compatibility
465 // #if XmVersion > 1100
466 // Corrected by Sergey Krasnov from Steve Hammes' code
468 for (i
= 0; i
< items
.GetCount(); i
++)
469 text
[i
] = wxStringToXmString(items
[i
]);
470 XmListAddItemsUnselected(listBox
, text
, items
.GetCount(), pos
+1);
472 for (i
= 0; i
< items
.GetCount(); i
++)
474 text
[i
] = wxStringToXmString(items
[i
]);
475 // Another Sergey correction
476 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1);
479 for (i
= 0; i
< items
.GetCount(); i
++)
480 XmStringFree(text
[i
]);
483 // It seems that if the list is cleared, we must re-ask for
484 // selection policy!!
485 SetSelectionPolicy();
487 m_noItems
+= items
.GetCount();
490 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
492 wxSizeKeeper
sk( this );
493 Widget listBox
= (Widget
) m_mainWidget
;
495 wxXmString
text( s
);
497 // delete the item and add it again.
498 // FIXME isn't there a way to change it in place?
499 XmListDeletePos (listBox
, n
+1);
500 XmListAddItem (listBox
, text(), n
+1);
505 void wxListBox::Command (wxCommandEvent
& event
)
507 if (event
.GetExtraLong())
508 SetSelection (event
.GetInt());
511 Deselect (event
.GetInt());
514 ProcessCommand (event
);
517 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
518 XmListCallbackStruct
* cbs
)
520 wxListBox
*item
= (wxListBox
*) clientData
;
522 if (item
->InSetValue())
527 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
528 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
530 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
532 int n
= cbs
->item_position
- 1;
533 wxCommandEvent
event (evtType
, item
->GetId());
534 if ( item
->HasClientObjectData() )
535 event
.SetClientObject( item
->GetClientObject(n
) );
536 else if ( item
->HasClientUntypedData() )
537 event
.SetClientData( item
->GetClientData(n
) );
539 event
.SetExtraLong(true);
540 event
.SetEventObject(item
);
541 event
.SetString( item
->GetString( n
) );
544 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
546 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
553 case XmCR_MULTIPLE_SELECT
:
554 case XmCR_BROWSE_SELECT
:
555 #if wxUSE_CHECKLISTBOX
556 item
->DoToggleItem( n
, x
);
558 case XmCR_DEFAULT_ACTION
:
559 item
->GetEventHandler()->ProcessEvent(event
);
561 case XmCR_EXTENDED_SELECT
:
562 switch (cbs
->selection_type
)
567 item
->DoToggleItem( n
, x
);
568 item
->GetEventHandler()->ProcessEvent(event
);
575 WXWidget
wxListBox::GetTopWidget() const
577 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
580 void wxListBox::ChangeBackgroundColour()
582 wxWindow::ChangeBackgroundColour();
584 Widget parent
= XtParent ((Widget
) m_mainWidget
);
587 XtVaGetValues (parent
,
588 XmNhorizontalScrollBar
, &hsb
,
589 XmNverticalScrollBar
, &vsb
,
592 /* TODO: should scrollbars be affected? Should probably have separate
593 * function to change them (by default, taken from wxSystemSettings)
595 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
596 wxDoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, true);
597 wxDoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, true);
600 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
603 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
606 // MBN: why change parent's background? It looks really ugly.
607 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
610 void wxListBox::ChangeForegroundColour()
612 wxWindow::ChangeForegroundColour();
614 Widget parent
= XtParent ((Widget
) m_mainWidget
);
617 XtVaGetValues(parent
,
618 XmNhorizontalScrollBar
, &hsb
,
619 XmNverticalScrollBar
, &vsb
,
622 /* TODO: should scrollbars be affected? Should probably have separate
623 function to change them (by default, taken from wxSystemSettings)
625 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
626 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
627 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
631 unsigned int wxListBox::GetCount() const
636 #define LIST_SCROLL_SPACING 6
638 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
641 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
645 XtVaGetValues( listWidget
,
647 XmNlistSpacing
, &spacing
,
648 XmNhighlightThickness
, &highlight
,
649 XmNlistMarginWidth
, &xmargin
,
650 XmNlistMarginHeight
, &ymargin
,
651 XmNshadowThickness
, &shadow
,
654 for( size_t i
= 0; i
< (size_t)max
; ++i
)
656 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
657 width
= wxMax( width
, x
);
660 // use some arbitrary value if there are no strings
665 window
->GetTextExtent( "v", &x
, &y
);
667 // make it a little larger than widest string, plus the scrollbar
668 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
669 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
671 // at least 3 items, at most 10
672 int height
= wxMax( 3, wxMin( 10, max
) ) *
673 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
675 return wxSize( width
, height
);
678 wxSize
wxListBox::DoGetBestSize() const
680 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );
683 #endif // wxUSE_LISTBOX