1 ///////////////////////////////////////////////////////////////////////////////
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "listbox.h"
16 #include "wx/listbox.h"
17 #include "wx/settings.h"
18 #include "wx/dynarray.h"
23 #include "wx/motif/private.h"
25 #if !USE_SHARED_LIBRARY
26 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
)
29 static void wxListBoxCallback(Widget w
,
31 XmListCallbackStruct
* cbs
);
33 static void wxListBoxDefaultActionProc(Widget list_w
,
34 XtPointer client_data
,
35 XmListCallbackStruct
* cbs
);
37 // ============================================================================
38 // list box control implementation
39 // ============================================================================
42 wxListBox::wxListBox() : m_clientDataList(wxKEY_INTEGER
)
48 bool wxListBox::Create(wxWindow
*parent
, wxWindowID id
,
51 int n
, const wxString choices
[],
53 const wxValidator
& validator
,
56 m_windowStyle
= style
;
59 // m_backgroundColour = parent->GetBackgroundColour();
60 m_backgroundColour
= * wxWHITE
;
61 m_foregroundColour
= parent
->GetForegroundColour();
64 SetValidator(validator
);
66 if (parent
) parent
->AddChild(this);
68 m_windowId
= ( id
== -1 ) ? (int)NewControlId() : id
;
70 Widget parentWidget
= (Widget
) parent
->GetClientWidget();
74 XtSetArg (args
[0], XmNlistSizePolicy
, XmCONSTANT
);
75 if (m_windowStyle
& wxLB_MULTIPLE
)
76 XtSetArg (args
[1], XmNselectionPolicy
, XmMULTIPLE_SELECT
);
77 else if (m_windowStyle
& wxLB_EXTENDED
)
78 XtSetArg (args
[1], XmNselectionPolicy
, XmEXTENDED_SELECT
);
80 XtSetArg (args
[1], XmNselectionPolicy
, XmBROWSE_SELECT
);
81 if (m_windowStyle
& wxLB_ALWAYS_SB
)
83 XtSetArg (args
[2], XmNscrollBarDisplayPolicy
, XmSTATIC
);
89 Widget listWidget
= XmCreateScrolledList (parentWidget
, (char*) (const char*) name
, args
, count
);
91 m_mainWidget
= (WXWidget
) listWidget
;
95 XtManageChild (listWidget
);
104 XtAddCallback (listWidget
, XmNbrowseSelectionCallback
, (XtCallbackProc
) wxListBoxCallback
,
106 XtAddCallback (listWidget
, XmNextendedSelectionCallback
, (XtCallbackProc
) wxListBoxCallback
,
108 XtAddCallback (listWidget
, XmNmultipleSelectionCallback
, (XtCallbackProc
) wxListBoxCallback
,
111 XtAddCallback (listWidget
, XmNdefaultActionCallback
, (XtCallbackProc
) wxListBoxDefaultActionProc
,
114 m_font
= parent
->GetFont();
117 SetCanAddEventHandler(TRUE
);
118 AttachWidget (parent
, m_mainWidget
, (WXWidget
) NULL
, pos
.x
, pos
.y
, width
, height
);
120 ChangeBackgroundColour();
125 wxListBox::~wxListBox()
129 void wxListBox::SetFirstItem(int N
)
135 XtVaGetValues ((Widget
) m_mainWidget
,
136 XmNvisibleItemCount
, &count
,
137 XmNitemCount
, &length
,
139 if ((N
+ count
) >= length
)
141 XmListSetPos ((Widget
) m_mainWidget
, N
+ 1);
144 void wxListBox::SetFirstItem(const wxString
& s
)
146 int N
= FindString (s
);
152 void wxListBox::Delete(int N
)
156 Widget listBox
= (Widget
) m_mainWidget
;
157 GetSize (&width1
, &height1
);
159 bool managed
= XtIsManaged(listBox
);
162 XtUnmanageChild (listBox
);
164 XmListDeletePos (listBox
, N
+ 1);
167 XtManageChild (listBox
);
169 GetSize (&width2
, &height2
);
170 // Correct for randomly resized listbox - bad boy, Motif!
171 if (width1
!= width2
|| height1
!= height2
)
172 SetSize (-1, -1, width1
, height1
);
174 // (JDH) need to add code here to take care of clientDataList
175 wxNode
*node
= m_clientDataList
.Find((long)N
); // get item from list
176 if (node
) m_clientDataList
.DeleteNode(node
); // if existed then delete from list
177 node
= m_clientDataList
.First(); // we now have to adjust all keys that
178 while (node
) // are >=N+1
180 if (node
->GetKeyInteger() >= (long)(N
+1))
181 node
->SetKeyInteger(node
->GetKeyInteger() - 1);
188 void wxListBox::Append(const wxString
& item
)
193 Widget listBox
= (Widget
) m_mainWidget
;
194 GetSize (&width1
, &height1
);
196 bool managed
= XtIsManaged(listBox
);
199 XtUnmanageChild (listBox
);
201 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
202 XmString text
= XmStringCreateSimple ((char*) (const char*) item
);
203 // XmListAddItem(listBox, text, n + 1);
204 XmListAddItemUnselected (listBox
, text
, 0);
207 // It seems that if the list is cleared, we must re-ask for
208 // selection policy!!
210 XtSetArg (args
[0], XmNlistSizePolicy
, XmCONSTANT
);
211 if (m_windowStyle
& wxLB_MULTIPLE
)
212 XtSetArg (args
[1], XmNselectionPolicy
, XmMULTIPLE_SELECT
);
213 else if (m_windowStyle
& wxLB_EXTENDED
)
214 XtSetArg (args
[1], XmNselectionPolicy
, XmEXTENDED_SELECT
);
216 XtSetArg (args
[1], XmNselectionPolicy
, XmBROWSE_SELECT
);
217 XtSetValues (listBox
, args
, 2);
220 XtManageChild (listBox
);
222 GetSize (&width2
, &height2
);
223 // Correct for randomly resized listbox - bad boy, Motif!
224 if (width1
!= width2
|| height1
!= height2
)
225 SetSize (-1, -1, width1
, height1
);
229 void wxListBox::Append(const wxString
& item
, void *clientData
)
234 Widget listBox
= (Widget
) m_mainWidget
;
236 GetSize (&width1
, &height1
);
237 Bool managed
= XtIsManaged(listBox
);
240 XtUnmanageChild (listBox
);
243 XtVaGetValues (listBox
, XmNitemCount
, &n
, NULL
);
244 XmString text
= XmStringCreateSimple ((char*) (const char*) item
);
245 // XmListAddItem(listBox, text, n + 1);
246 XmListAddItemUnselected (listBox
, text
, 0);
249 // It seems that if the list is cleared, we must re-ask for
250 // selection policy!!
252 XtSetArg (args
[0], XmNlistSizePolicy
, XmCONSTANT
);
253 if (m_windowStyle
& wxLB_MULTIPLE
)
254 XtSetArg (args
[1], XmNselectionPolicy
, XmMULTIPLE_SELECT
);
255 else if (m_windowStyle
& wxLB_EXTENDED
)
256 XtSetArg (args
[1], XmNselectionPolicy
, XmEXTENDED_SELECT
);
258 XtSetArg (args
[1], XmNselectionPolicy
, XmBROWSE_SELECT
);
259 XtSetValues (listBox
, args
, 2);
261 m_clientDataList
.Append ((long) n
, (wxObject
*) clientData
);
264 XtManageChild (listBox
);
266 GetSize (&width2
, &height2
);
268 // Correct for randomly resized listbox - bad boy, Motif!
269 if (width1
!= width2
|| height1
!= height2
)
270 SetSize (-1, -1, width1
, height1
);
275 void wxListBox::Set(int n
, const wxString
*choices
, void** clientData
)
277 m_clientDataList
.Clear();
281 Widget listBox
= (Widget
) m_mainWidget
;
282 GetSize (&width1
, &height1
);
284 bool managed
= XtIsManaged(listBox
);
287 XtUnmanageChild (listBox
);
289 for (int i=0; i<n; i++)
291 XmString text = XmStringCreateSimple(choices[i]);
292 XmListAddItemUnselected(listBox, text, 0);
296 XmString
*text
= new XmString
[n
];
298 for (i
= 0; i
< n
; i
++)
299 text
[i
] = XmStringCreateSimple ((char*) (const char*) choices
[i
]);
302 for (i
= 0; i
< n
; i
++)
303 m_clientDataList
.Append ((long) i
, (wxObject
*) clientData
[i
]);
305 XmListAddItems (listBox
, text
, n
, 0);
306 for (i
= 0; i
< n
; i
++)
307 XmStringFree (text
[i
]);
310 // It seems that if the list is cleared, we must re-ask for
311 // selection policy!!
313 XtSetArg (args
[0], XmNlistSizePolicy
, XmCONSTANT
);
314 if (m_windowStyle
& wxLB_MULTIPLE
)
315 XtSetArg (args
[1], XmNselectionPolicy
, XmMULTIPLE_SELECT
);
316 else if (m_windowStyle
& wxLB_EXTENDED
)
317 XtSetArg (args
[1], XmNselectionPolicy
, XmEXTENDED_SELECT
);
319 XtSetArg (args
[1], XmNselectionPolicy
, XmBROWSE_SELECT
);
320 XtSetValues (listBox
, args
, 2);
323 XtManageChild (listBox
);
325 GetSize (&width2
, &height2
);
326 // Correct for randomly resized listbox - bad boy, Motif!
327 if (width1
!= width2
|| height1
!= height2
)
328 SetSize (-1, -1, width1
, height1
);
333 int wxListBox::FindString(const wxString
& s
) const
335 XmString str
= XmStringCreateSimple ((char*) (const char*) s
);
336 int *positions
= NULL
;
337 int no_positions
= 0;
338 bool success
= XmListGetMatchPos ((Widget
) m_mainWidget
, str
, &positions
, &no_positions
);
342 int pos
= positions
[0];
344 XtFree ((char *) positions
);
351 void wxListBox::Clear()
359 Widget listBox
= (Widget
) m_mainWidget
;
360 GetSize (&width1
, &height1
);
362 XmListDeleteAllItems (listBox
);
363 m_clientDataList
.Clear ();
364 GetSize (&width2
, &height2
);
366 // Correct for randomly resized listbox - bad boy, Motif!
367 if (width1
!= width2
|| height1
!= height2
)
368 SetSize (-1, -1, width1
, height1
);
373 void wxListBox::SetSelection(int N
, bool select
)
379 if (m_windowStyle
& wxLB_MULTIPLE
)
381 int *selections
= NULL
;
382 int n
= GetSelections (&selections
);
384 // This hack is supposed to work, to make it possible to select more
385 // than one item, but it DOESN'T under Motif 1.1.
387 XtVaSetValues ((Widget
) m_mainWidget
, XmNselectionPolicy
, XmMULTIPLE_SELECT
, NULL
);
390 for (i
= 0; i
< n
; i
++)
391 XmListSelectPos ((Widget
) m_mainWidget
, selections
[i
] + 1, FALSE
);
393 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
395 XtVaSetValues ((Widget
) m_mainWidget
, XmNselectionPolicy
, XmEXTENDED_SELECT
, NULL
);
399 XmListSelectPos ((Widget
) m_mainWidget
, N
+ 1, FALSE
);
403 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
405 m_inSetValue
= FALSE
;
408 bool wxListBox::Selected(int N
) const
410 // In Motif, no simple way to determine if the item is selected.
411 wxArrayInt theSelections
;
412 int count
= GetSelections (theSelections
);
418 for (j
= 0; j
< count
; j
++)
419 if (theSelections
[j
] == N
)
425 void wxListBox::Deselect(int N
)
427 XmListDeselectPos ((Widget
) m_mainWidget
, N
+ 1);
430 void *wxListBox::GetClientData(int N
) const
432 wxNode
*node
= m_clientDataList
.Find ((long) N
);
434 return (void *) node
->Data ();
439 void wxListBox::SetClientData(int N
, void *Client_data
)
441 wxNode
*node
= m_clientDataList
.Find ((long) N
);
443 node
->SetData ((wxObject
*)Client_data
);
445 node
= m_clientDataList
.Append((long) N
, (wxObject
*) Client_data
);
448 // Return number of selections and an array of selected integers
449 int wxListBox::GetSelections(wxArrayInt
& aSelections
) const
453 Widget listBox
= (Widget
) m_mainWidget
;
456 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
461 aSelections
.Alloc(posCnt
);
464 for (i
= 0; i
< posCnt
; i
++)
465 aSelections
.Add(posList
[i
] - 1);
467 XtFree ((char *) posList
);
477 // Get single selection, for single choice list items
478 int wxListBox::GetSelection() const
480 Widget listBox
= (Widget
) m_mainWidget
;
483 bool flag
= XmListGetSelectedPos (listBox
, &posList
, &posCnt
);
489 XtFree ((char *) posList
);
496 // Find string for position
497 wxString
wxListBox::GetString(int N
) const
499 Widget listBox
= (Widget
) m_mainWidget
;
502 XtVaGetValues (listBox
, XmNitemCount
, &n
, XmNitems
, &strlist
, NULL
);
503 if (N
<= n
&& N
>= 0)
506 if (XmStringGetLtoR (strlist
[N
], XmSTRING_DEFAULT_CHARSET
, &txt
))
513 return wxEmptyString
;
516 return wxEmptyString
;
519 void wxListBox::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
)
521 wxWindow::DoSetSize(x
, y
, width
, height
, sizeFlags
);
523 // Check resulting size is correct
525 GetSize (&tempW
, &tempH
);
528 if (tempW != width || tempH != height)
530 cout << "wxListBox::SetSize sizes not set correctly.");
535 void wxListBox::InsertItems(int nItems
, const wxString items
[], int pos
)
540 Widget listBox
= (Widget
) m_mainWidget
;
542 GetSize(&width1
, &height1
);
544 bool managed
= XtIsManaged(listBox
);
547 XtUnmanageChild(listBox
);
549 XmString
*text
= new XmString
[nItems
];
551 // Steve Hammes: Motif 1.1 compatibility
552 // #if XmVersion > 1100
553 // Corrected by Sergey Krasnov from Steve Hammes' code
555 for (i
= 0; i
< nItems
; i
++)
556 text
[i
] = XmStringCreateSimple((char*) (const char*) items
[i
]);
557 XmListAddItemsUnselected(listBox
, text
, nItems
, pos
+1);
559 for (i
= 0; i
< nItems
; i
++)
561 text
[i
] = XmStringCreateSimple((char*) (const char*) items
[i
]);
562 // XmListAddItemUnselected(listBox, text[i], i);
563 XmListAddItemUnselected(listBox
, text
[i
], pos
+i
+1); // Another Sergey correction
566 for (i
= 0; i
< nItems
; i
++)
567 XmStringFree(text
[i
]);
571 // It seems that if the list is cleared, we must re-ask for
572 // selection policy!!
574 XtSetArg(args
[0], XmNlistSizePolicy
, XmCONSTANT
);
575 if (m_windowStyle
& wxLB_MULTIPLE
)
576 XtSetArg(args
[1], XmNselectionPolicy
, XmMULTIPLE_SELECT
);
577 else if (m_windowStyle
& wxLB_EXTENDED
)
578 XtSetArg(args
[1], XmNselectionPolicy
, XmEXTENDED_SELECT
);
579 else XtSetArg(args
[1], XmNselectionPolicy
, XmBROWSE_SELECT
);
580 XtSetValues(listBox
,args
,2) ;
583 XtManageChild(listBox
);
585 GetSize(&width2
, &height2
);
586 // Correct for randomly resized listbox - bad boy, Motif!
587 if (width1
!= width2
/*|| height1 != height2*/)
588 SetSize(-1, -1, width1
, height1
);
593 void wxListBox::SetString(int N
, const wxString
& s
)
598 Widget listBox
= (Widget
) m_mainWidget
;
599 GetSize (&width1
, &height1
);
601 XmString text
= XmStringCreateSimple ((char*) (const char*) s
);
603 // delete the item and add it again.
604 // FIXME isn't there a way to change it in place?
605 XmListDeletePos (listBox
, N
+1);
606 XmListAddItem (listBox
, text
, N
+1);
610 GetSize (&width2
, &height2
);
611 // Correct for randomly resized listbox - bad boy, Motif!
612 if (width1
!= width2
|| height1
!= height2
)
613 SetSize (-1, -1, width1
, height1
);
616 int wxListBox::Number () const
621 // For single selection items only
622 wxString
wxListBox::GetStringSelection () const
625 int sel
= GetSelection();
627 res
= GetString(sel
);
632 bool wxListBox::SetStringSelection (const wxString
& s
, bool flag
)
634 int sel
= FindString (s
);
637 SetSelection (sel
, flag
);
644 void wxListBox::Command (wxCommandEvent
& event
)
646 if (event
.m_extraLong
)
647 SetSelection (event
.m_commandInt
);
650 Deselect (event
.m_commandInt
);
653 ProcessCommand (event
);
656 void wxListBoxCallback (Widget
WXUNUSED(w
), XtPointer clientData
,
657 XmListCallbackStruct
* cbs
)
660 if (cbs->reason == XmCR_EXTENDED_SELECT)
661 cout << "*** Extend select\n";
662 else if (cbs->reason == XmCR_SINGLE_SELECT)
663 cout << "*** Single select\n";
664 else if (cbs->reason == XmCR_MULTIPLE_SELECT)
665 cout << "*** Multiple select\n";
666 else if (cbs->reason == XmCR_BROWSE_SELECT)
667 cout << "*** Browse select\n";
669 if (cbs->selection_type == XmMODIFICATION)
670 cout << "*** Modification\n";
671 else if (cbs->selection_type == XmINITIAL)
672 cout << "*** Initial\n";
673 else if (cbs->selection_type == XmADDITION)
674 cout << "*** Addition\n";
677 wxListBox
*item
= (wxListBox
*) clientData
;
679 if (item
->InSetValue())
682 wxCommandEvent
event (wxEVT_COMMAND_LISTBOX_SELECTED
, item
->GetId());
685 case XmCR_MULTIPLE_SELECT
:
686 case XmCR_BROWSE_SELECT
:
688 event
.m_clientData
= item
->GetClientData (cbs
->item_position
- 1);
689 event
.m_commandInt
= cbs
->item_position
- 1;
690 event
.m_extraLong
= TRUE
;
691 event
.SetEventObject(item
);
692 item
->ProcessCommand (event
);
695 case XmCR_EXTENDED_SELECT
:
697 switch (cbs
->selection_type
)
703 event
.m_clientData
= item
->GetClientData (cbs
->item_position
- 1);
704 event
.m_commandInt
= cbs
->item_position
- 1;
705 event
.m_extraLong
= TRUE
;
706 event
.SetEventObject(item
);
707 item
->ProcessCommand (event
);
716 /* Respond by getting the
717 * designated "default button" in the action area and activate it
718 * as if the user had selected it.
720 void wxListBoxDefaultActionProc (Widget
WXUNUSED(list_w
), XtPointer client_data
, XmListCallbackStruct
* WXUNUSED(cbs
))
722 wxListBox
*lbox
= (wxListBox
*) client_data
;
724 wxCommandEvent
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, lbox
->GetId());
725 event
.SetEventObject( lbox
);
726 lbox
->GetEventHandler()->ProcessEvent(event
) ;
729 WXWidget
wxListBox::GetTopWidget() const
731 return (WXWidget
) XtParent( (Widget
) m_mainWidget
);
734 void wxListBox::ChangeFont(bool keepOriginalSize
)
736 wxWindow::ChangeFont(keepOriginalSize
);
739 void wxListBox::ChangeBackgroundColour()
741 wxWindow::ChangeBackgroundColour();
743 Widget parent
= XtParent ((Widget
) m_mainWidget
);
746 XtVaGetValues (parent
,
747 XmNhorizontalScrollBar
, &hsb
,
748 XmNverticalScrollBar
, &vsb
,
751 /* TODO: should scrollbars be affected? Should probably have separate
752 * function to change them (by default, taken from wxSystemSettings)
754 wxColour backgroundColour
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
);
755 DoChangeBackgroundColour((WXWidget
) hsb
, backgroundColour
, TRUE
);
756 DoChangeBackgroundColour((WXWidget
) vsb
, backgroundColour
, TRUE
);
759 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(hsb
)),
762 XmNtroughColor
, backgroundColour
.AllocColour(XtDisplay(vsb
)),
765 DoChangeBackgroundColour((WXWidget
) parent
, m_backgroundColour
, TRUE
);
768 void wxListBox::ChangeForegroundColour()
770 wxWindow::ChangeForegroundColour();
772 Widget parent
= XtParent ((Widget
) m_mainWidget
);
775 XtVaGetValues(parent
,
776 XmNhorizontalScrollBar
, &hsb
,
777 XmNverticalScrollBar
, &vsb
,
780 /* TODO: should scrollbars be affected? Should probably have separate
781 function to change them (by default, taken from wxSystemSettings)
783 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
784 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
785 DoChangeForegroundColour((WXWidget) parent, m_foregroundColour);