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"
18 #define XtParent XTPARENT
19 #define XtDisplay XTDISPLAY
22 # include "wx/listbox.h"
23 #include "wx/settings.h"
24 #include "wx/dynarray.h"
27 #include "wx/arrstr.h"
30 #pragma message disable nosimpint
34 #pragma message enable nosimpint
36 #include "wx/motif/private.h"
38 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
40 static void wxListBoxCallback(Widget w
,
42 XmListCallbackStruct
* cbs
);
44 // ----------------------------------------------------------------------------
46 // ----------------------------------------------------------------------------
48 // helper class to reduce code duplication
54 wxSizeKeeper( wxWindow
* w
)
57 m_w
->GetSize( &m_x
, &m_y
);
64 m_w
->GetSize( &x
, &y
);
65 if( x
!= m_x
|| y
!= m_y
)
66 m_w
->SetSize( -1, -1, m_x
, m_y
);
70 // ============================================================================
71 // list box control implementation
72 // ============================================================================
75 wxListBox::wxListBox()
80 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
83 int n
, const wxString choices
[],
85 const wxValidator
& validator
,
88 if( !wxControl::CreateControl( parent
, id
, pos
, size
, style
,
92 m_noItems
= (unsigned int)n
;
93 m_backgroundColour
= * wxWHITE
;
95 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
96 Display
* dpy
= XtDisplay(parentWidget
);
100 XtSetArg( args
[count
], XmNlistSizePolicy
, XmCONSTANT
); ++count
;
101 XtSetArg( args
[count
], XmNselectionPolicy
,
102 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
103 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
108 XtSetArg( args
[count
],
109 (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
) );
112 if( m_windowStyle
& wxLB_ALWAYS_SB
)
114 XtSetArg( args
[count
], XmNscrollBarDisplayPolicy
, XmSTATIC
);
119 XmCreateScrolledList(parentWidget
,
120 wxConstCast(name
.c_str(), char), args
, count
);
122 m_mainWidget
= (WXWidget
) listWidget
;
126 XtManageChild (listWidget
);
128 wxSize best
= GetBestSize();
129 if( size
.x
!= -1 ) best
.x
= size
.x
;
130 if( size
.y
!= -1 ) best
.y
= size
.y
;
132 XtAddCallback (listWidget
,
133 XmNbrowseSelectionCallback
,
134 (XtCallbackProc
) wxListBoxCallback
,
136 XtAddCallback (listWidget
,
137 XmNextendedSelectionCallback
,
138 (XtCallbackProc
) wxListBoxCallback
,
140 XtAddCallback (listWidget
,
141 XmNmultipleSelectionCallback
,
142 (XtCallbackProc
) wxListBoxCallback
,
144 XtAddCallback (listWidget
,
145 XmNdefaultActionCallback
,
146 (XtCallbackProc
) wxListBoxCallback
,
149 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
,
150 pos
.x
, pos
.y
, best
.x
, best
.y
);
152 ChangeBackgroundColour();
157 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
160 const wxArrayString
& choices
,
162 const wxValidator
& validator
,
163 const wxString
& name
)
165 wxCArrayString
chs(choices
);
166 return Create(parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(),
167 style
, validator
, name
);
170 wxListBox::~wxListBox()
172 if( HasClientObjectData() )
173 m_clientDataDict
.DestroyData();
176 void wxListBox::SetSelectionPolicy()
178 Widget listBox
= (Widget
)m_mainWidget
;
181 XtSetArg( args
[0], XmNlistSizePolicy
, XmCONSTANT
);
183 XtSetArg( args
[1], XmNselectionPolicy
,
184 ( m_windowStyle
& wxLB_MULTIPLE
) ? XmMULTIPLE_SELECT
:
185 ( m_windowStyle
& wxLB_EXTENDED
) ? XmEXTENDED_SELECT
:
188 XtSetValues( listBox
, args
, 2 );
191 void wxListBox::DoSetFirstItem( int N
)
198 XtVaGetValues ((Widget
) m_mainWidget
,
199 XmNvisibleItemCount
, &count
,
200 XmNitemCount
, &length
,
202 if ((N
+ count
) >= length
)
204 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
207 void wxListBox::Delete(unsigned int n
)
209 wxSizeKeeper
sk( this );
210 Widget listBox
= (Widget
) m_mainWidget
;
212 bool managed
= XtIsManaged(listBox
);
215 XtUnmanageChild (listBox
);
217 XmListDeletePos (listBox
, n
+ 1);
220 XtManageChild (listBox
);
223 m_clientDataDict
.Delete(n
, HasClientObjectData());
227 int wxListBox::DoAppend(const wxString
& item
)
229 wxSizeKeeper
sk( this );
230 Widget listBox
= (Widget
) m_mainWidget
;
232 bool managed
= XtIsManaged(listBox
);
235 XtUnmanageChild (listBox
);
237 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
238 wxXmString
text( item
);
239 // XmListAddItem(listBox, text, n + 1);
240 XmListAddItemUnselected (listBox
, text(), 0);
242 // It seems that if the list is cleared, we must re-ask for
243 // selection policy!!
244 SetSelectionPolicy();
247 XtManageChild (listBox
);
252 return GetCount() - 1;
255 void wxListBox::DoSetItems(const wxArrayString
& items
, void** clientData
)
257 wxSizeKeeper
sk( this );
258 Widget listBox
= (Widget
) m_mainWidget
;
260 if( HasClientObjectData() )
261 m_clientDataDict
.DestroyData();
263 bool managed
= XtIsManaged(listBox
);
266 XtUnmanageChild (listBox
);
267 XmString
*text
= new XmString
[items
.GetCount()];
269 for (i
= 0; i
< items
.GetCount(); ++i
)
270 text
[i
] = wxStringToXmString (items
[i
]);
273 for (i
= 0; i
< items
.GetCount(); ++i
)
274 m_clientDataDict
.Set(i
, (wxClientData
*)clientData
[i
], false);
276 XmListAddItems (listBox
, text
, items
.GetCount(), 0);
277 for (i
= 0; i
< items
.GetCount(); i
++)
278 XmStringFree (text
[i
]);
281 // It seems that if the list is cleared, we must re-ask for
282 // selection policy!!
283 SetSelectionPolicy();
286 XtManageChild (listBox
);
290 m_noItems
= items
.GetCount();
293 int wxDoFindStringInList(Widget w
, const wxString
& s
)
296 int *positions
= NULL
;
297 int no_positions
= 0;
298 bool success
= XmListGetMatchPos (w
, str(),
299 &positions
, &no_positions
);
303 int pos
= positions
[0];
305 XtFree ((char *) positions
);
312 int wxListBox::FindString(const wxString
& s
, bool WXUNUSED(bCase
)) const
314 // FIXME: back to base class for not supported value of bCase
316 return wxDoFindStringInList( (Widget
)m_mainWidget
, s
);
319 void wxListBox::Clear()
324 wxSizeKeeper
sk( this );
325 Widget listBox
= (Widget
) m_mainWidget
;
327 XmListDeleteAllItems (listBox
);
328 if( HasClientObjectData() )
329 m_clientDataDict
.DestroyData();
336 void wxListBox::DoSetSelection(int N
, bool select
)
342 if (m_windowStyle
& wxLB_MULTIPLE
)
344 int *selections
= NULL
;
345 int n
= GetSelections (&selections
);
347 // This hack is supposed to work, to make it possible
348 // to select more than one item, but it DOESN'T under Motif 1.1.
350 XtVaSetValues ((Widget
) m_mainWidget
,
351 XmNselectionPolicy
, XmMULTIPLE_SELECT
,
355 for (i
= 0; i
< n
; i
++)
356 XmListSelectPos ((Widget
) m_mainWidget
,
357 selections
[i
] + 1, False
);
359 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
361 XtVaSetValues ((Widget
) m_mainWidget
,
362 XmNselectionPolicy
, XmEXTENDED_SELECT
,
367 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, False
);
371 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
373 m_inSetValue
= false;
376 bool wxListBox::IsSelected(int N
) const
378 // In Motif, no simple way to determine if the item is selected.
379 wxArrayInt theSelections
;
380 int count
= GetSelections (theSelections
);
386 for (j
= 0; j
< count
; j
++)
387 if (theSelections
[j
] == N
)
393 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
)
395 m_clientDataDict
.Set(n
, clientData
, false);
398 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const
400 return m_clientDataDict
.Get(n
);
403 void *wxListBox::DoGetItemClientData(unsigned int n
) const
405 return (void*)m_clientDataDict
.Get(n
);
408 void wxListBox::DoSetItemClientData(unsigned int n
, void *Client_data
)
410 m_clientDataDict
.Set(n
, (wxClientData
*)Client_data
, false);
413 // Return number of selections and an array of selected integers
414 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
418 Widget listBox
= (Widget
) m_mainWidget
;
421 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
426 aSelections
.Alloc(posCnt
);
429 for (i
= 0; i
< posCnt
; i
++)
430 aSelections
.Add(posList
[i
] - 1);
432 XtFree ((char *) posList
);
442 // Get single selection, for single choice list items
443 int wxDoGetSelectionInList(Widget listBox
)
447 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
453 XtFree ((char *) posList
);
460 int wxListBox::GetSelection() const
462 return wxDoGetSelectionInList((Widget
) m_mainWidget
);
465 // Find string for position
466 wxString
wxDoGetStringInList( Widget listBox
, int n
)
470 XtVaGetValues( listBox
,
471 XmNitemCount
, &count
,
474 if( n
< count
&& n
>= 0 )
475 return wxXmStringToString( strlist
[n
] );
477 return wxEmptyString
;
480 wxString
wxListBox::GetString(unsigned int n
) const
482 return wxDoGetStringInList( (Widget
)m_mainWidget
, n
);
485 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
)
487 wxSizeKeeper
sk( this );
488 Widget listBox
= (Widget
) m_mainWidget
;
490 bool managed
= XtIsManaged(listBox
);
493 XtUnmanageChild(listBox
);
495 XmString
*text
= new XmString
[items
.GetCount()];
497 // Steve Hammes: Motif 1.1 compatibility
498 // #if XmVersion > 1100
499 // Corrected by Sergey Krasnov from Steve Hammes' code
501 for (i
= 0; i
< items
.GetCount(); i
++)
502 text
[i
] = wxStringToXmString(items
[i
]);
503 XmListAddItemsUnselected(listBox
, text
, items
.GetCount(), pos
+1);
505 for (i
= 0; i
< items
.GetCount(); i
++)
507 text
[i
] = wxStringToXmString(items
[i
]);
508 // Another Sergey correction
509 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1);
512 for (i
= 0; i
< items
.GetCount(); i
++)
513 XmStringFree(text
[i
]);
516 // It seems that if the list is cleared, we must re-ask for
517 // selection policy!!
518 SetSelectionPolicy();
521 XtManageChild(listBox
);
525 m_noItems
+= items
.GetCount();
528 void wxListBox::SetString(unsigned int n
, const wxString
& s
)
530 wxSizeKeeper
sk( this );
531 Widget listBox
= (Widget
) m_mainWidget
;
533 wxXmString
text( s
);
535 // delete the item and add it again.
536 // FIXME isn't there a way to change it in place?
537 XmListDeletePos (listBox
, n
+1);
538 XmListAddItem (listBox
, text(), n
+1);
543 void wxListBox::Command (wxCommandEvent
& event
)
545 if (event
.GetExtraLong())
546 SetSelection (event
.GetInt());
549 Deselect (event
.GetInt());
552 ProcessCommand (event
);
555 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
556 XmListCallbackStruct
* cbs
)
558 wxListBox
*item
= (wxListBox
*) clientData
;
560 if (item
->InSetValue())
565 if( cbs
->reason
== XmCR_DEFAULT_ACTION
)
566 evtType
= wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
;
568 evtType
= wxEVT_COMMAND_LISTBOX_SELECTED
;
570 int n
= cbs
->item_position
- 1;
571 wxCommandEvent
event (evtType
, item
->GetId());
572 if ( item
->HasClientObjectData() )
573 event
.SetClientObject( item
->GetClientObject(n
) );
574 else if ( item
->HasClientUntypedData() )
575 event
.SetClientData( item
->GetClientData(n
) );
577 event
.SetExtraLong(true);
578 event
.SetEventObject(item
);
579 event
.SetString( item
->GetString( n
) );
582 if( NULL
!= cbs
->event
&& cbs
->event
->type
== ButtonRelease
)
584 XButtonEvent
* evt
= (XButtonEvent
*)cbs
->event
;
591 case XmCR_MULTIPLE_SELECT
:
592 case XmCR_BROWSE_SELECT
:
593 #if wxUSE_CHECKLISTBOX
594 item
->DoToggleItem( n
, x
);
596 case XmCR_DEFAULT_ACTION
:
597 item
->GetEventHandler()->ProcessEvent(event
);
599 case XmCR_EXTENDED_SELECT
:
600 switch (cbs
->selection_type
)
605 item
->DoToggleItem( n
, x
);
606 item
->GetEventHandler()->ProcessEvent(event
);
613 WXWidget
wxListBox::GetTopWidget() const
615 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
618 void wxListBox::ChangeBackgroundColour()
620 wxWindow::ChangeBackgroundColour();
622 Widget parent
= XtParent ((Widget
) m_mainWidget
);
625 XtVaGetValues (parent
,
626 XmNhorizontalScrollBar
, &hsb
,
627 XmNverticalScrollBar
, &vsb
,
630 /* TODO: should scrollbars be affected? Should probably have separate
631 * function to change them (by default, taken from wxSystemSettings)
633 wxColour backgroundColour
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
);
634 wxDoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, true);
635 wxDoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, true);
638 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
641 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
644 // MBN: why change parent's background? It looks really ugly.
645 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
648 void wxListBox::ChangeForegroundColour()
650 wxWindow::ChangeForegroundColour();
652 Widget parent
= XtParent ((Widget
) m_mainWidget
);
655 XtVaGetValues(parent
,
656 XmNhorizontalScrollBar
, &hsb
,
657 XmNverticalScrollBar
, &vsb
,
660 /* TODO: should scrollbars be affected? Should probably have separate
661 function to change them (by default, taken from wxSystemSettings)
663 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
664 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
665 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
669 unsigned int wxListBox::GetCount() const
674 #define LIST_SCROLL_SPACING 6
676 wxSize
wxDoGetListBoxBestSize( Widget listWidget
, const wxWindow
* window
)
679 Dimension spacing
, highlight
, xmargin
, ymargin
, shadow
;
683 XtVaGetValues( listWidget
,
685 XmNlistSpacing
, &spacing
,
686 XmNhighlightThickness
, &highlight
,
687 XmNlistMarginWidth
, &xmargin
,
688 XmNlistMarginHeight
, &ymargin
,
689 XmNshadowThickness
, &shadow
,
692 for( size_t i
= 0; i
< (size_t)max
; ++i
)
694 window
->GetTextExtent( wxDoGetStringInList( listWidget
, i
), &x
, &y
);
695 width
= wxMax( width
, x
);
698 // use some arbitrary value if there are no strings
703 window
->GetTextExtent( "v", &x
, &y
);
705 // make it a little larger than widest string, plus the scrollbar
706 width
+= wxSystemSettings::GetMetric( wxSYS_VSCROLL_X
)
707 + 2 * highlight
+ LIST_SCROLL_SPACING
+ 2 * xmargin
+ 2 * shadow
;
709 // at least 3 items, at most 10
710 int height
= wxMax( 3, wxMin( 10, max
) ) *
711 ( y
+ spacing
+ 2 * highlight
) + 2 * ymargin
+ 2 * shadow
;
713 return wxSize( width
, height
);
716 wxSize
wxListBox::DoGetBestSize() const
718 return wxDoGetListBoxBestSize( (Widget
)m_mainWidget
, this );
721 #endif // wxUSE_LISTBOX