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