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"
16 #define XtParent XTPARENT
17 #define XtDisplay XTDISPLAY
20 # include "wx/listbox.h"
21 #include "wx/settings.h"
22 #include "wx/dynarray.h"
25 #include "wx/arrstr.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();
94 Display
* dpy
= XtDisplay(parentWidget
);
98 XtSetArg( args
[count
], XmNlistSizePolicy
, XmCONSTANT
); ++count
;
99 XtSetArg( args
[count
], XmNselectionPolicy
,
100 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
101 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
106 XtSetArg( args
[count
],
107 (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
) );
110 if( m_windowStyle
& wxLB_ALWAYS_SB
)
112 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
117 XmCreateScrolledList(parentWidget
,
118 wxConstCast(name
.c_str(), char), args
, count
);
120 m_mainWidget
= (WXWidget
) listWidget
;
124 XtManageChild (listWidget
);
126 wxSize best
= GetBestSize();
127 if( size
.x
!= -1 ) best
.x
= size
.x
;
128 if( size
.y
!= -1 ) best
.y
= size
.y
;
130 XtAddCallback (listWidget
,
131 XmNbrowseSelectionCallback
,
132 (XtCallbackProc
) wxListBoxCallback
,
134 XtAddCallback (listWidget
,
135 XmNextendedSelectionCallback
,
136 (XtCallbackProc
) wxListBoxCallback
,
138 XtAddCallback (listWidget
,
139 XmNmultipleSelectionCallback
,
140 (XtCallbackProc
) wxListBoxCallback
,
142 XtAddCallback (listWidget
,
143 XmNdefaultActionCallback
,
144 (XtCallbackProc
) wxListBoxCallback
,
147 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
148 pos
.x
, pos
.y
, best
.x
, best
.y
);
150 ChangeBackgroundColour();
155 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
158 const wxArrayString
& choices
,
160 const wxValidator
& validator
,
161 const wxString
& name
)
163 wxCArrayString
chs(choices
);
164 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
165 style
, validator
, name
);
168 wxListBox::~wxListBox()
170 if( HasClientObjectData() )
171 m_clientDataDict
.DestroyData();
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
)
195 XtVaGetValues ((Widget
) m_mainWidget
,
196 XmNvisibleItemCount
, &count
,
197 XmNitemCount
, &length
,
199 if ((N
+ count
) >= length
)
201 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
204 void wxListBox::Delete(int N
)
206 wxSizeKeeper
sk( this );
207 Widget listBox
= (Widget
) m_mainWidget
;
209 bool managed
= XtIsManaged(listBox
);
212 XtUnmanageChild (listBox
);
214 XmListDeletePos (listBox
, N
+ 1);
217 XtManageChild (listBox
);
220 m_clientDataDict
.Delete(N
, HasClientObjectData());
224 int wxListBox::DoAppend(const wxString
& item
)
226 wxSizeKeeper
sk( this );
227 Widget listBox
= (Widget
) m_mainWidget
;
229 bool managed
= XtIsManaged(listBox
);
232 XtUnmanageChild (listBox
);
234 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
235 wxXmString
text( item
);
236 // XmListAddItem(listBox, text, n + 1);
237 XmListAddItemUnselected (listBox
, text(), 0);
239 // It seems that if the list is cleared, we must re-ask for
240 // selection policy!!
241 SetSelectionPolicy();
244 XtManageChild (listBox
);
249 return GetCount() - 1;
252 void wxListBox::DoSetItems(const wxArrayString
& items
, void** clientData
)
254 wxSizeKeeper
sk( this );
255 Widget listBox
= (Widget
) m_mainWidget
;
257 if( HasClientObjectData() )
258 m_clientDataDict
.DestroyData();
260 bool managed
= XtIsManaged(listBox
);
263 XtUnmanageChild (listBox
);
264 XmString
*text
= new XmString
[items
.GetCount()];
266 for (i
= 0; i
< items
.GetCount(); ++i
)
267 text
[i
] = wxStringToXmString (items
[i
]);
270 for (i
= 0; i
< items
.GetCount(); ++i
)
271 m_clientDataDict
.Set(i
, (wxClientData
*)clientData
[i
], false);
273 XmListAddItems (listBox
, text
, items
.GetCount(), 0);
274 for (i
= 0; i
< items
.GetCount(); i
++)
275 XmStringFree (text
[i
]);
278 // It seems that if the list is cleared, we must re-ask for
279 // selection policy!!
280 SetSelectionPolicy();
283 XtManageChild (listBox
);
287 m_noItems
= items
.GetCount();
290 int wxDoFindStringInList(Widget w
, const wxString
& s
)
293 int *positions
= NULL
;
294 int no_positions
= 0;
295 bool success
= XmListGetMatchPos (w
, str(),
296 &positions
, &no_positions
);
300 int pos
= positions
[0];
302 XtFree ((char *) positions
);
309 int wxListBox::FindString(const wxString
& s
, bool WXUNUSED(bCase
)) const
311 // FIXME: back to base class for not supported value of bCase
313 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
316 void wxListBox::Clear()
321 wxSizeKeeper
sk( this );
322 Widget listBox
= (Widget
) m_mainWidget
;
324 XmListDeleteAllItems (listBox
);
325 if( HasClientObjectData() )
326 m_clientDataDict
.DestroyData();
333 void wxListBox::DoSetSelection(int N
, bool select
)
339 if (m_windowStyle
& wxLB_MULTIPLE
)
341 int *selections
= NULL
;
342 int n
= GetSelections (&selections
);
344 // This hack is supposed to work, to make it possible
345 // to select more than one item, but it DOESN'T under Motif 1.1.
347 XtVaSetValues ((Widget
) m_mainWidget
,
348 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
352 for (i
= 0; i
< n
; i
++)
353 XmListSelectPos ((Widget
) m_mainWidget
,
354 selections
[i
] + 1, False
);
356 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
358 XtVaSetValues ((Widget
) m_mainWidget
,
359 XmNselectionPolicy
, XmEXTENDED_SELECT
,
364 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
368 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
370 m_inSetValue
= false;
373 bool wxListBox::IsSelected(int N
) const
375 // In Motif, no simple way to determine if the item is selected.
376 wxArrayInt theSelections
;
377 int count
= GetSelections (theSelections
);
383 for (j
= 0; j
< count
; j
++)
384 if (theSelections
[j
] == N
)
390 void wxListBox::DoSetItemClientObject(int n
, wxClientData
* clientData
)
392 m_clientDataDict
.Set(n
, clientData
, false);
395 wxClientData
* wxListBox::DoGetItemClientObject(int n
) const
397 return m_clientDataDict
.Get(n
);
400 void *wxListBox::DoGetItemClientData(int N
) const
402 return (void*)m_clientDataDict
.Get(N
);
405 void wxListBox::DoSetItemClientData(int N
, void *Client_data
)
407 m_clientDataDict
.Set(N
, (wxClientData
*)Client_data
, false);
410 // Return number of selections and an array of selected integers
411 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
415 Widget listBox
= (Widget
) m_mainWidget
;
418 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
423 aSelections
.Alloc(posCnt
);
426 for (i
= 0; i
< posCnt
; i
++)
427 aSelections
.Add(posList
[i
] - 1);
429 XtFree ((char *) posList
);
439 // Get single selection, for single choice list items
440 int wxDoGetSelectionInList(Widget listBox
)
444 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
450 XtFree ((char *) posList
);
457 int wxListBox::GetSelection() const
459 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
462 // Find string for position
463 wxString
wxDoGetStringInList( Widget listBox
, int n
)
467 XtVaGetValues( listBox
,
468 XmNitemCount
, &count
,
471 if( n
< count
&& n
>= 0 )
472 return wxXmStringToString( strlist
[n
] );
474 return wxEmptyString
;
477 wxString
wxListBox::GetString( int n
) const
479 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
482 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
)
484 wxSizeKeeper
sk( this );
485 Widget listBox
= (Widget
) m_mainWidget
;
487 bool managed
= XtIsManaged(listBox
);
490 XtUnmanageChild(listBox
);
492 XmString
*text
= new XmString
[items
.GetCount()];
494 // Steve Hammes: Motif 1.1 compatibility
495 // #if XmVersion > 1100
496 // Corrected by Sergey Krasnov from Steve Hammes' code
498 for (i
= 0; i
< items
.GetCount(); i
++)
499 text
[i
] = wxStringToXmString(items
[i
]);
500 XmListAddItemsUnselected(listBox
, text
, items
.GetCount(), pos
+1);
502 for (i
= 0; i
< items
.GetCount(); i
++)
504 text
[i
] = wxStringToXmString(items
[i
]);
505 // Another Sergey correction
506 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1);
509 for (i
= 0; i
< items
.GetCount(); i
++)
510 XmStringFree(text
[i
]);
513 // It seems that if the list is cleared, we must re-ask for
514 // selection policy!!
515 SetSelectionPolicy();
518 XtManageChild(listBox
);
522 m_noItems
+= items
.GetCount();
525 void wxListBox::SetString(int N
, const wxString
& s
)
527 wxSizeKeeper
sk( this );
528 Widget listBox
= (Widget
) m_mainWidget
;
530 wxXmString
text( s
);
532 // delete the item and add it again.
533 // FIXME isn't there a way to change it in place?
534 XmListDeletePos (listBox
, N
+1);
535 XmListAddItem (listBox
, text(), N
+1);
540 void wxListBox::Command (wxCommandEvent
& event
)
542 if (event
.GetExtraLong())
543 SetSelection (event
.GetInt());
546 Deselect (event
.GetInt());
549 ProcessCommand (event
);
552 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
553 XmListCallbackStruct
* cbs
)
555 wxListBox
*item
= (wxListBox
*) clientData
;
557 if (item
->InSetValue())
562 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
563 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
565 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
567 int n
= cbs
->item_position
- 1;
568 wxCommandEvent
event (evtType
, item
->GetId());
569 if ( item
->HasClientObjectData() )
570 event
.SetClientObject( item
->GetClientObject(n
) );
571 else if ( item
->HasClientUntypedData() )
572 event
.SetClientData( item
->GetClientData(n
) );
574 event
.SetExtraLong(true);
575 event
.SetEventObject(item
);
576 event
.SetString( item
->GetString( n
) );
579 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
581 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
588 case XmCR_MULTIPLE_SELECT
:
589 case XmCR_BROWSE_SELECT
:
590 #if wxUSE_CHECKLISTBOX
591 item
->DoToggleItem( n
, x
);
593 case XmCR_DEFAULT_ACTION
:
594 item
->GetEventHandler()->ProcessEvent(event
);
596 case XmCR_EXTENDED_SELECT
:
597 switch (cbs
->selection_type
)
602 item
->DoToggleItem( n
, x
);
603 item
->GetEventHandler()->ProcessEvent(event
);
610 WXWidget
wxListBox::GetTopWidget() const
612 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
615 void wxListBox::ChangeBackgroundColour()
617 wxWindow::ChangeBackgroundColour();
619 Widget parent
= XtParent ((Widget
) m_mainWidget
);
622 XtVaGetValues (parent
,
623 XmNhorizontalScrollBar
, &hsb
,
624 XmNverticalScrollBar
, &vsb
,
627 /* TODO: should scrollbars be affected? Should probably have separate
628 * function to change them (by default, taken from wxSystemSettings)
630 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
631 wxDoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, true);
632 wxDoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, true);
635 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
638 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
641 // MBN: why change parent's background? It looks really ugly.
642 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
645 void wxListBox::ChangeForegroundColour()
647 wxWindow::ChangeForegroundColour();
649 Widget parent
= XtParent ((Widget
) m_mainWidget
);
652 XtVaGetValues(parent
,
653 XmNhorizontalScrollBar
, &hsb
,
654 XmNverticalScrollBar
, &vsb
,
657 /* TODO: should scrollbars be affected? Should probably have separate
658 function to change them (by default, taken from wxSystemSettings)
660 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
661 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
662 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
666 int wxListBox::GetCount() const
671 #define LIST_SCROLL_SPACING 6
673 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
676 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
680 XtVaGetValues( listWidget
,
682 XmNlistSpacing
, &spacing
,
683 XmNhighlightThickness
, &highlight
,
684 XmNlistMarginWidth
, &xmargin
,
685 XmNlistMarginHeight
, &ymargin
,
686 XmNshadowThickness
, &shadow
,
689 for( size_t i
= 0; i
< (size_t)max
; ++i
)
691 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
692 width
= wxMax( width
, x
);
695 // use some arbitrary value if there are no strings
700 window
->GetTextExtent( "v", &x
, &y
);
702 // make it a little larger than widest string, plus the scrollbar
703 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
704 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
706 // at least 3 items, at most 10
707 int height
= wxMax( 3, wxMin( 10, max
) ) *
708 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
710 return wxSize( width
, height
);
713 wxSize
wxListBox::DoGetBestSize() const
715 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );