]> git.saurik.com Git - wxWidgets.git/blame - src/motif/listbox.cpp
wxMenu Review, added Carbon Events and full OSX Support
[wxWidgets.git] / src / motif / listbox.cpp
CommitLineData
4bb6408c
JS
1///////////////////////////////////////////////////////////////////////////////
2// Name: listbox.cpp
3// Purpose: wxListBox
4// Author: Julian Smart
5// Modified by:
6// Created: 17/09/98
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart
9// Licence: wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
29006414 13 #pragma implementation "listbox.h"
4bb6408c
JS
14#endif
15
4dff3400
JJ
16#ifdef __VMS
17#define XtParent XTPARENT
18#define XtDisplay XTDISPLAY
19#endif
20
21# include "wx/listbox.h"
4bb6408c
JS
22#include "wx/settings.h"
23#include "wx/dynarray.h"
24#include "wx/log.h"
f97c9854
JS
25#include "wx/utils.h"
26
338dd992
JJ
27#ifdef __VMS__
28#pragma message disable nosimpint
29#endif
f97c9854 30#include <Xm/List.h>
338dd992
JJ
31#ifdef __VMS__
32#pragma message enable nosimpint
33#endif
f97c9854 34#include "wx/motif/private.h"
4bb6408c 35
29006414 36 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
4bb6408c 37
29006414
VZ
38static void wxListBoxCallback(Widget w,
39 XtPointer clientData,
40 XmListCallbackStruct * cbs);
f97c9854 41
99ab3e3f
MB
42// ----------------------------------------------------------------------------
43// wxSizeKeeper
44// ----------------------------------------------------------------------------
45
46// helper class to reduce code duplication
47class wxSizeKeeper
48{
49 int m_x, m_y;
50 wxWindow* m_w;
51public:
52 wxSizeKeeper( wxWindow* w )
53 : m_w( w )
54 {
55 m_w->GetSize( &m_x, &m_y );
56 }
57
58 void Restore()
59 {
60 int x, y;
61
62 m_w->GetSize( &x, &y );
63 if( x != m_x || y != m_y )
64 m_w->SetSize( -1, -1, m_x, m_y );
65 }
66};
67
4bb6408c
JS
68// ============================================================================
69// list box control implementation
70// ============================================================================
71
72// Listbox item
99ab3e3f 73wxListBox::wxListBox()
4bb6408c 74{
f97c9854 75 m_noItems = 0;
4bb6408c
JS
76}
77
78bool wxListBox::Create(wxWindow *parent, wxWindowID id,
79 const wxPoint& pos,
80 const wxSize& size,
81 int n, const wxString choices[],
82 long style,
83 const wxValidator& validator,
84 const wxString& name)
85{
99ab3e3f
MB
86 if( !wxControl::CreateControl( parent, id, pos, size, style,
87 validator, name ) )
88 return FALSE;
89
f97c9854 90 m_noItems = n;
94b49b93 91 m_backgroundColour = * wxWHITE;
29006414 92
f97c9854 93 Widget parentWidget = (Widget) parent->GetClientWidget();
29006414 94
f97c9854 95 Arg args[3];
99ab3e3f
MB
96 int count = 0;
97 XtSetArg( args[0], XmNlistSizePolicy, XmCONSTANT ); ++count;
98 XtSetArg( args[1], XmNselectionPolicy,
99 ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT :
100 ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT :
101 XmBROWSE_SELECT );
102 ++count;
103 if( m_windowStyle & wxLB_ALWAYS_SB )
f97c9854 104 {
99ab3e3f
MB
105 XtSetArg( args[2], XmNscrollBarDisplayPolicy, XmSTATIC );
106 ++count;
f97c9854 107 }
29006414 108
ef41d80c
MB
109 Widget listWidget = XmCreateScrolledList(parentWidget,
110 (char*)name.c_str(), args, count);
29006414 111
f97c9854 112 m_mainWidget = (WXWidget) listWidget;
29006414 113
a4294b78 114 Set(n, choices);
29006414 115
f97c9854 116 XtManageChild (listWidget);
29006414 117
f97c9854
JS
118 long width = size.x;
119 long height = size.y;
120 if (width == -1)
121 width = 150;
122 if (height == -1)
123 height = 80;
29006414 124
ef41d80c
MB
125 XtAddCallback (listWidget,
126 XmNbrowseSelectionCallback,
127 (XtCallbackProc) wxListBoxCallback,
128 (XtPointer) this);
129 XtAddCallback (listWidget,
130 XmNextendedSelectionCallback,
131 (XtCallbackProc) wxListBoxCallback,
132 (XtPointer) this);
133 XtAddCallback (listWidget,
134 XmNmultipleSelectionCallback,
135 (XtCallbackProc) wxListBoxCallback,
136 (XtPointer) this);
137 XtAddCallback (listWidget,
138 XmNdefaultActionCallback,
139 (XtCallbackProc) wxListBoxCallback,
140 (XtPointer) this);
29006414 141
4b5f3fe6 142 ChangeFont(FALSE);
29006414 143
15d5ab67 144 SetCanAddEventHandler(TRUE);
ef41d80c
MB
145 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
146 pos.x, pos.y, width, height);
29006414 147
0d57be45 148 ChangeBackgroundColour();
29006414 149
f97c9854 150 return TRUE;
4bb6408c
JS
151}
152
153wxListBox::~wxListBox()
154{
99ab3e3f
MB
155 if( HasClientObjectData() )
156 m_clientDataDict.DestroyData();
157}
158
159void wxListBox::SetSelectionPolicy()
160{
161 Widget listBox = (Widget)m_mainWidget;
162 Arg args[3];
163
164 XtSetArg( args[0], XmNlistSizePolicy, XmCONSTANT );
165
166 XtSetArg( args[1], XmNselectionPolicy,
167 ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT :
168 ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT :
169 XmBROWSE_SELECT );
170
171 XtSetValues( listBox, args, 2 );
4bb6408c
JS
172}
173
ef41d80c 174void wxListBox::DoSetFirstItem( int N )
4bb6408c 175{
2d120f83 176 int count, length;
29006414 177
2d120f83
JS
178 if (N < 0)
179 return;
180 XtVaGetValues ((Widget) m_mainWidget,
29006414
VZ
181 XmNvisibleItemCount, &count,
182 XmNitemCount, &length,
183 NULL);
2d120f83
JS
184 if ((N + count) >= length)
185 N = length - count;
186 XmListSetPos ((Widget) m_mainWidget, N + 1);
4bb6408c
JS
187}
188
4bb6408c
JS
189void wxListBox::Delete(int N)
190{
99ab3e3f 191 wxSizeKeeper sk( this );
2d120f83 192 Widget listBox = (Widget) m_mainWidget;
29006414 193
2d120f83 194 bool managed = XtIsManaged(listBox);
29006414 195
2d120f83
JS
196 if (managed)
197 XtUnmanageChild (listBox);
29006414 198
2d120f83 199 XmListDeletePos (listBox, N + 1);
29006414 200
2d120f83
JS
201 if (managed)
202 XtManageChild (listBox);
29006414 203
99ab3e3f
MB
204 sk.Restore();
205 m_clientDataDict.Delete(N, HasClientObjectData());
2d120f83 206 m_noItems --;
4bb6408c
JS
207}
208
ef41d80c 209int wxListBox::DoAppend(const wxString& item)
4bb6408c 210{
99ab3e3f 211 wxSizeKeeper sk( this );
2d120f83 212 Widget listBox = (Widget) m_mainWidget;
29006414 213
2d120f83 214 bool managed = XtIsManaged(listBox);
29006414 215
2d120f83
JS
216 if (managed)
217 XtUnmanageChild (listBox);
218 int n;
219 XtVaGetValues (listBox, XmNitemCount, &n, NULL);
99ab3e3f 220 wxXmString text( item );
2d120f83 221 // XmListAddItem(listBox, text, n + 1);
99ab3e3f 222 XmListAddItemUnselected (listBox, text(), 0);
29006414 223
2d120f83
JS
224 // It seems that if the list is cleared, we must re-ask for
225 // selection policy!!
99ab3e3f 226 SetSelectionPolicy();
29006414 227
2d120f83
JS
228 if (managed)
229 XtManageChild (listBox);
29006414 230
99ab3e3f 231 sk.Restore();
2d120f83 232 m_noItems ++;
ef41d80c
MB
233
234 return GetCount() - 1;
4bb6408c
JS
235}
236
ef41d80c 237void wxListBox::DoSetItems(const wxArrayString& items, void** clientData)
4bb6408c 238{
99ab3e3f 239 wxSizeKeeper sk( this );
2d120f83 240 Widget listBox = (Widget) m_mainWidget;
99ab3e3f
MB
241
242 if( HasClientObjectData() )
243 m_clientDataDict.DestroyData();
ef41d80c
MB
244
245 bool managed = XtIsManaged(listBox);
29006414 246
2d120f83
JS
247 if (managed)
248 XtUnmanageChild (listBox);
ef41d80c
MB
249 XmString *text = new XmString[items.GetCount()];
250 size_t i;
251 for (i = 0; i < items.GetCount(); ++i)
252 text[i] = XmStringCreateSimple ((char*)items[i].c_str());
29006414 253
ef41d80c
MB
254 if ( clientData )
255 for (i = 0; i < items.GetCount(); ++i)
99ab3e3f 256 m_clientDataDict.Set(i, (wxClientData*)clientData[i], FALSE);
ef41d80c
MB
257
258 XmListAddItems (listBox, text, items.GetCount(), 0);
259 for (i = 0; i < items.GetCount(); i++)
260 XmStringFree (text[i]);
261 delete[] text;
29006414 262
2d120f83
JS
263 // It seems that if the list is cleared, we must re-ask for
264 // selection policy!!
99ab3e3f 265 SetSelectionPolicy();
29006414 266
2d120f83
JS
267 if (managed)
268 XtManageChild (listBox);
29006414 269
99ab3e3f 270 sk.Restore();
29006414 271
ef41d80c 272 m_noItems = items.GetCount();
4bb6408c
JS
273}
274
275int wxListBox::FindString(const wxString& s) const
276{
99ab3e3f 277 wxXmString str( s );
2d120f83
JS
278 int *positions = NULL;
279 int no_positions = 0;
99ab3e3f 280 bool success = XmListGetMatchPos ((Widget) m_mainWidget, str(),
ef41d80c 281 &positions, &no_positions);
99ab3e3f 282
2d120f83 283 if (success)
f97c9854 284 {
2d120f83
JS
285 int pos = positions[0];
286 if (positions)
287 XtFree ((char *) positions);
288 return pos - 1;
f97c9854 289 }
2d120f83
JS
290 else
291 return -1;
4bb6408c
JS
292}
293
294void wxListBox::Clear()
295{
2d120f83
JS
296 if (m_noItems <= 0)
297 return;
29006414 298
99ab3e3f 299 wxSizeKeeper sk( this );
2d120f83 300 Widget listBox = (Widget) m_mainWidget;
29006414 301
2d120f83 302 XmListDeleteAllItems (listBox);
99ab3e3f
MB
303 if( HasClientObjectData() )
304 m_clientDataDict.DestroyData();
29006414 305
99ab3e3f 306 sk.Restore();
29006414 307
2d120f83 308 m_noItems = 0;
4bb6408c
JS
309}
310
311void wxListBox::SetSelection(int N, bool select)
312{
2d120f83
JS
313 m_inSetValue = TRUE;
314 if (select)
f97c9854 315 {
29006414
VZ
316#if 0
317 if (m_windowStyle & wxLB_MULTIPLE)
318 {
319 int *selections = NULL;
320 int n = GetSelections (&selections);
321
ef41d80c
MB
322 // This hack is supposed to work, to make it possible
323 // to select more than one item, but it DOESN'T under Motif 1.1.
29006414 324
ef41d80c
MB
325 XtVaSetValues ((Widget) m_mainWidget,
326 XmNselectionPolicy, XmMULTIPLE_SELECT,
327 NULL);
29006414
VZ
328
329 int i;
330 for (i = 0; i < n; i++)
ef41d80c
MB
331 XmListSelectPos ((Widget) m_mainWidget,
332 selections[i] + 1, FALSE);
29006414
VZ
333
334 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
335
ef41d80c
MB
336 XtVaSetValues ((Widget) m_mainWidget,
337 XmNselectionPolicy, XmEXTENDED_SELECT,
338 NULL);
29006414
VZ
339 }
340 else
341#endif // 0
2d120f83 342 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
29006414 343
f97c9854 344 }
2d120f83
JS
345 else
346 XmListDeselectPos ((Widget) m_mainWidget, N + 1);
29006414 347
2d120f83 348 m_inSetValue = FALSE;
4bb6408c
JS
349}
350
d7d38ea4 351bool wxListBox::IsSelected(int N) const
4bb6408c 352{
2d120f83
JS
353 // In Motif, no simple way to determine if the item is selected.
354 wxArrayInt theSelections;
355 int count = GetSelections (theSelections);
356 if (count == 0)
357 return FALSE;
358 else
359 {
360 int j;
361 for (j = 0; j < count; j++)
362 if (theSelections[j] == N)
363 return TRUE;
364 }
4bb6408c
JS
365 return FALSE;
366}
367
ef41d80c
MB
368void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
369{
99ab3e3f 370 m_clientDataDict.Set(n, clientData, FALSE);
ef41d80c
MB
371}
372
373wxClientData* wxListBox::DoGetItemClientObject(int n) const
4bb6408c 374{
99ab3e3f 375 return m_clientDataDict.Get(n);
4bb6408c
JS
376}
377
ef41d80c 378void *wxListBox::DoGetItemClientData(int N) const
4bb6408c 379{
99ab3e3f 380 return (void*)m_clientDataDict.Get(N);
4bb6408c
JS
381}
382
ef41d80c 383void wxListBox::DoSetItemClientData(int N, void *Client_data)
4bb6408c 384{
99ab3e3f 385 m_clientDataDict.Set(N, (wxClientData*)Client_data, FALSE);
4bb6408c
JS
386}
387
388// Return number of selections and an array of selected integers
389int wxListBox::GetSelections(wxArrayInt& aSelections) const
390{
2d120f83 391 aSelections.Empty();
29006414 392
2d120f83
JS
393 Widget listBox = (Widget) m_mainWidget;
394 int *posList = NULL;
395 int posCnt = 0;
396 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
397 if (flag)
398 {
399 if (posCnt > 0)
400 {
401 aSelections.Alloc(posCnt);
29006414 402
2d120f83
JS
403 int i;
404 for (i = 0; i < posCnt; i++)
405 aSelections.Add(posList[i] - 1);
29006414 406
2d120f83
JS
407 XtFree ((char *) posList);
408 return posCnt;
409 }
410 else
411 return 0;
4bb6408c 412 }
2d120f83
JS
413 else
414 return 0;
4bb6408c
JS
415}
416
417// Get single selection, for single choice list items
418int wxListBox::GetSelection() const
419{
f97c9854
JS
420 Widget listBox = (Widget) m_mainWidget;
421 int *posList = NULL;
422 int posCnt = 0;
423 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
424 if (flag)
425 {
426 int id = -1;
427 if (posCnt > 0)
428 id = posList[0] - 1;
429 XtFree ((char *) posList);
430 return id;
431 }
432 else
433 return -1;
4bb6408c
JS
434}
435
436// Find string for position
437wxString wxListBox::GetString(int N) const
438{
f97c9854
JS
439 Widget listBox = (Widget) m_mainWidget;
440 XmString *strlist;
441 int n;
442 XtVaGetValues (listBox, XmNitemCount, &n, XmNitems, &strlist, NULL);
443 if (N <= n && N >= 0)
444 {
445 char *txt;
446 if (XmStringGetLtoR (strlist[N], XmSTRING_DEFAULT_CHARSET, &txt))
447 {
448 wxString str(txt);
449 XtFree (txt);
450 return str;
451 }
452 else
453 return wxEmptyString;
454 }
455 else
456 return wxEmptyString;
4bb6408c
JS
457}
458
ef41d80c 459void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
4bb6408c 460{
99ab3e3f 461 wxSizeKeeper sk( this );
f97c9854 462 Widget listBox = (Widget) m_mainWidget;
29006414 463
f97c9854 464 bool managed = XtIsManaged(listBox);
29006414 465
f97c9854
JS
466 if (managed)
467 XtUnmanageChild(listBox);
29006414 468
ef41d80c
MB
469 XmString *text = new XmString[items.GetCount()];
470 size_t i;
2d120f83
JS
471 // Steve Hammes: Motif 1.1 compatibility
472 // #if XmVersion > 1100
473 // Corrected by Sergey Krasnov from Steve Hammes' code
f97c9854 474#if XmVersion > 1001
ef41d80c
MB
475 for (i = 0; i < items.GetCount(); i++)
476 text[i] = XmStringCreateSimple((char*)items[i].c_str());
477 XmListAddItemsUnselected(listBox, text, items.GetCount(), pos+1);
f97c9854 478#else
ef41d80c 479 for (i = 0; i < items.GetCount(); i++)
f97c9854 480 {
ef41d80c
MB
481 text[i] = XmStringCreateSimple((char*)items[i].c_str());
482 // Another Sergey correction
483 XmListAddItemUnselected(listBox, text[i], pos+i+1);
f97c9854
JS
484 }
485#endif
ef41d80c 486 for (i = 0; i < items.GetCount(); i++)
f97c9854 487 XmStringFree(text[i]);
f97c9854 488 delete[] text;
29006414 489
f97c9854
JS
490 // It seems that if the list is cleared, we must re-ask for
491 // selection policy!!
99ab3e3f 492 SetSelectionPolicy();
29006414 493
f97c9854
JS
494 if (managed)
495 XtManageChild(listBox);
29006414 496
99ab3e3f 497 sk.Restore();
29006414 498
ef41d80c 499 m_noItems += items.GetCount();
4bb6408c
JS
500}
501
502void wxListBox::SetString(int N, const wxString& s)
503{
99ab3e3f 504 wxSizeKeeper sk( this );
f97c9854 505 Widget listBox = (Widget) m_mainWidget;
29006414 506
99ab3e3f 507 wxXmString text( s );
29006414
VZ
508
509 // delete the item and add it again.
510 // FIXME isn't there a way to change it in place?
f97c9854 511 XmListDeletePos (listBox, N+1);
99ab3e3f 512 XmListAddItem (listBox, text(), N+1);
29006414 513
99ab3e3f 514 sk.Restore();
4bb6408c
JS
515}
516
4bb6408c
JS
517void wxListBox::Command (wxCommandEvent & event)
518{
f97c9854
JS
519 if (event.m_extraLong)
520 SetSelection (event.m_commandInt);
521 else
522 {
523 Deselect (event.m_commandInt);
524 return;
525 }
526 ProcessCommand (event);
527}
528
af111fc3 529void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData,
2d120f83 530 XmListCallbackStruct * cbs)
f97c9854 531{
f97c9854 532 wxListBox *item = (wxListBox *) clientData;
29006414 533
a4294b78 534 if (item->InSetValue())
f97c9854 535 return;
29006414 536
ef41d80c
MB
537 wxEventType evtType;
538
539 if( cbs->reason == XmCR_DEFAULT_ACTION )
540 evtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED;
541 else
542 evtType = wxEVT_COMMAND_LISTBOX_SELECTED;
543
544 int n = cbs->item_position - 1;
545 wxCommandEvent event (evtType, item->GetId());
546 if ( item->HasClientObjectData() )
547 event.SetClientObject( item->GetClientObject(n) );
548 else if ( item->HasClientUntypedData() )
549 event.SetClientData( item->GetClientData(n) );
550 event.m_commandInt = n;
551 event.m_extraLong = TRUE;
552 event.SetEventObject(item);
553 event.SetString( item->GetString( n ) );
554
555 int x = -1;
2b5f62a0 556 if( NULL != cbs->event && cbs->event->type == ButtonRelease )
ef41d80c
MB
557 {
558 XButtonEvent* evt = (XButtonEvent*)cbs->event;
559
560 x = evt->x;
561 }
562
f97c9854 563 switch (cbs->reason)
4bb6408c 564 {
2d120f83
JS
565 case XmCR_MULTIPLE_SELECT:
566 case XmCR_BROWSE_SELECT:
ef41d80c
MB
567#if wxUSE_CHECKLISTBOX
568 item->DoToggleItem( n, x );
569#endif
570 case XmCR_DEFAULT_ACTION:
571 item->GetEventHandler()->ProcessEvent(event);
572 break;
2d120f83 573 case XmCR_EXTENDED_SELECT:
ef41d80c 574 switch (cbs->selection_type)
f97c9854 575 {
ef41d80c
MB
576 case XmINITIAL:
577 case XmADDITION:
578 case XmMODIFICATION:
579 item->DoToggleItem( n, x );
580 item->GetEventHandler()->ProcessEvent(event);
f97c9854
JS
581 break;
582 }
ef41d80c 583 break;
4bb6408c 584 }
4bb6408c
JS
585}
586
89c7e962
JS
587WXWidget wxListBox::GetTopWidget() const
588{
2d120f83 589 return (WXWidget) XtParent( (Widget) m_mainWidget );
89c7e962 590}
0d57be45 591
0d57be45
JS
592void wxListBox::ChangeBackgroundColour()
593{
321db4b6 594 wxWindow::ChangeBackgroundColour();
29006414 595
02800301
JS
596 Widget parent = XtParent ((Widget) m_mainWidget);
597 Widget hsb, vsb;
29006414 598
02800301 599 XtVaGetValues (parent,
2d120f83
JS
600 XmNhorizontalScrollBar, &hsb,
601 XmNverticalScrollBar, &vsb,
602 NULL);
29006414 603
a91b47e8
JS
604 /* TODO: should scrollbars be affected? Should probably have separate
605 * function to change them (by default, taken from wxSystemSettings)
2d120f83 606 */
a756f210 607 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
94b49b93
JS
608 DoChangeBackgroundColour((WXWidget) hsb, backgroundColour, TRUE);
609 DoChangeBackgroundColour((WXWidget) vsb, backgroundColour, TRUE);
15d5ab67
JS
610
611 XtVaSetValues (hsb,
612 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)),
613 NULL);
614 XtVaSetValues (vsb,
615 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)),
616 NULL);
29006414 617
02800301 618 DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
0d57be45
JS
619}
620
621void wxListBox::ChangeForegroundColour()
622{
321db4b6 623 wxWindow::ChangeForegroundColour();
29006414 624
02800301
JS
625 Widget parent = XtParent ((Widget) m_mainWidget);
626 Widget hsb, vsb;
29006414
VZ
627
628 XtVaGetValues(parent,
629 XmNhorizontalScrollBar, &hsb,
630 XmNverticalScrollBar, &vsb,
631 NULL);
632
633 /* TODO: should scrollbars be affected? Should probably have separate
634 function to change them (by default, taken from wxSystemSettings)
635
2d120f83
JS
636 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
637 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
638 DoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
02800301 639 */
0d57be45
JS
640}
641
6adaedf0
JS
642int wxListBox::GetCount() const
643{
ef41d80c 644 return m_noItems;
6adaedf0 645}