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