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