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 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 list_w
, XtPointer client_data
, XmListCallbackStruct 
* 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);