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