]> git.saurik.com Git - wxWidgets.git/blob - src/motif/listbox.cpp
fix crash in wxWindowGTK::GTKHandleUnrealize(), closes #14752
[wxWidgets.git] / src / motif / listbox.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/motif/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 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #if wxUSE_LISTBOX
16
17 #include "wx/listbox.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/dynarray.h"
21 #include "wx/log.h"
22 #include "wx/utils.h"
23 #include "wx/settings.h"
24 #include "wx/arrstr.h"
25 #endif
26
27 #ifdef __VMS__
28 #pragma message disable nosimpint
29 #endif
30 #include <Xm/List.h>
31 #ifdef __VMS__
32 #pragma message enable nosimpint
33 #endif
34 #include "wx/motif/private.h"
35
36 static void wxListBoxCallback(Widget w,
37 XtPointer clientData,
38 XmListCallbackStruct * cbs);
39
40 // ----------------------------------------------------------------------------
41 // wxSizeKeeper
42 // ----------------------------------------------------------------------------
43
44 // helper class to reduce code duplication
45 class wxSizeKeeper
46 {
47 int m_x, m_y;
48 int m_w, m_h;
49 wxWindow* m_wnd;
50 public:
51 wxSizeKeeper( wxWindow* w )
52 : m_wnd( w )
53 {
54 m_wnd->GetSize( &m_w, &m_h );
55 m_wnd->GetPosition( &m_x, &m_y );
56 }
57
58 void Restore()
59 {
60 int x, y;
61
62 m_wnd->GetSize( &x, &y );
63 if( x != m_x || y != m_y )
64 m_wnd->SetSize( m_x, m_y, m_w, m_h );
65 }
66 };
67
68 // ============================================================================
69 // list box control implementation
70 // ============================================================================
71
72 // Listbox item
73 wxListBox::wxListBox()
74 {
75 m_noItems = 0;
76 }
77
78 bool 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 {
86 if( !wxControl::CreateControl( parent, id, pos, size, style,
87 validator, name ) )
88 return false;
89 PreCreation();
90
91 m_noItems = (unsigned int)n;
92
93 Widget parentWidget = (Widget) parent->GetClientWidget();
94 Display* dpy = XtDisplay(parentWidget);
95
96 Arg args[4];
97 int count = 0;
98 XtSetArg( args[count], XmNlistSizePolicy, XmCONSTANT ); ++count;
99 XtSetArg( args[count], XmNselectionPolicy,
100 ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT :
101 ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT :
102 XmBROWSE_SELECT );
103 ++count;
104 if( m_font.IsOk() )
105 {
106 XtSetArg( args[count],
107 (String)wxFont::GetFontTag(), m_font.GetFontTypeC(dpy) );
108 ++count;
109 }
110 if( m_windowStyle & wxLB_ALWAYS_SB )
111 {
112 XtSetArg( args[count], XmNscrollBarDisplayPolicy, XmSTATIC );
113 ++count;
114 }
115
116 Widget listWidget =
117 XmCreateScrolledList(parentWidget,
118 name.char_str(), args, count);
119
120 m_mainWidget = (WXWidget) listWidget;
121
122 Set(n, choices);
123
124 XtManageChild (listWidget);
125
126 wxSize best = GetBestSize();
127 if( size.x != -1 ) best.x = size.x;
128 if( size.y != -1 ) best.y = size.y;
129
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);
146
147 PostCreation();
148 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
149 pos.x, pos.y, best.x, best.y);
150
151 return true;
152 }
153
154 bool 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
167 void 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 );
180 }
181
182 void wxListBox::DoSetFirstItem( int N )
183 {
184 int count, length;
185
186 if (!IsValid(N))
187 return;
188
189 XtVaGetValues ((Widget) m_mainWidget,
190 XmNvisibleItemCount, &count,
191 XmNitemCount, &length,
192 NULL);
193 if ((N + count) >= length)
194 N = length - count;
195 XmListSetPos ((Widget) m_mainWidget, N + 1);
196 }
197
198 void wxListBox::DoDeleteOneItem(unsigned int n)
199 {
200 Widget listBox = (Widget) m_mainWidget;
201
202 XmListDeletePos (listBox, n + 1);
203
204 wxListBoxBase::DoDeleteOneItem(n);
205 m_noItems --;
206 }
207
208 int wxDoFindStringInList(Widget w, const wxString& s)
209 {
210 wxXmString str( s );
211 int *positions = NULL;
212 int no_positions = 0;
213 bool success = XmListGetMatchPos (w, str(),
214 &positions, &no_positions);
215
216 if (success && positions)
217 {
218 int pos = positions[0];
219 XtFree ((char *) positions);
220 return pos - 1;
221 }
222 else
223 return -1;
224 }
225
226 int wxListBox::FindString(const wxString& s, bool WXUNUSED(bCase)) const
227 {
228 // FIXME: back to base class for not supported value of bCase
229
230 return wxDoFindStringInList( (Widget)m_mainWidget, s );
231 }
232
233 void wxListBox::DoClear()
234 {
235 if (!m_noItems)
236 return;
237
238 wxSizeKeeper sk( this );
239 Widget listBox = (Widget) m_mainWidget;
240
241 XmListDeleteAllItems (listBox);
242
243 sk.Restore();
244
245 wxListBoxBase::DoClear();
246 m_noItems = 0;
247 }
248
249 void wxListBox::DoSetSelection(int N, bool select)
250 {
251 m_inSetValue = true;
252 if (select)
253 {
254 #if 0
255 if (m_windowStyle & wxLB_MULTIPLE)
256 {
257 int *selections = NULL;
258 int n = GetSelections (&selections);
259
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.
262
263 XtVaSetValues ((Widget) m_mainWidget,
264 XmNselectionPolicy, XmMULTIPLE_SELECT,
265 NULL);
266
267 int i;
268 for (i = 0; i < n; i++)
269 XmListSelectPos ((Widget) m_mainWidget,
270 selections[i] + 1, False);
271
272 XmListSelectPos ((Widget) m_mainWidget, N + 1, False);
273
274 XtVaSetValues ((Widget) m_mainWidget,
275 XmNselectionPolicy, XmEXTENDED_SELECT,
276 NULL);
277 }
278 else
279 #endif // 0
280 XmListSelectPos ((Widget) m_mainWidget, N + 1, False);
281
282 }
283 else
284 XmListDeselectPos ((Widget) m_mainWidget, N + 1);
285
286 m_inSetValue = false;
287 }
288
289 bool wxListBox::IsSelected(int N) const
290 {
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)
295 return false;
296 else
297 {
298 int j;
299 for (j = 0; j < count; j++)
300 if (theSelections[j] == N)
301 return true;
302 }
303 return false;
304 }
305
306 // Return number of selections and an array of selected integers
307 int wxListBox::GetSelections(wxArrayInt& aSelections) const
308 {
309 aSelections.Empty();
310
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);
320
321 int i;
322 for (i = 0; i < posCnt; i++)
323 aSelections.Add(posList[i] - 1);
324
325 XtFree ((char *) posList);
326 return posCnt;
327 }
328 else
329 return 0;
330 }
331 else
332 return 0;
333 }
334
335 // Get single selection, for single choice list items
336 int wxDoGetSelectionInList(Widget listBox)
337 {
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;
351 }
352
353 int wxListBox::GetSelection() const
354 {
355 return wxDoGetSelectionInList((Widget) m_mainWidget);
356 }
357
358 // Find string for position
359 wxString wxDoGetStringInList( Widget listBox, int n )
360 {
361 XmString *strlist;
362 int count;
363 XtVaGetValues( listBox,
364 XmNitemCount, &count,
365 XmNitems, &strlist,
366 NULL );
367 if( n < count && n >= 0 )
368 return wxXmStringToString( strlist[n] );
369 else
370 return wxEmptyString;
371 }
372
373 wxString wxListBox::GetString(unsigned int n) const
374 {
375 return wxDoGetStringInList( (Widget)m_mainWidget, n );
376 }
377
378 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items,
379 unsigned int pos,
380 void **clientData, wxClientDataType type)
381 {
382 Widget listBox = (Widget) m_mainWidget;
383
384 const unsigned int numItems = items.GetCount();
385
386 XmString *text = new XmString[numItems];
387 unsigned int i;
388 #if XmVersion > 1001
389 for (i = 0; i < numItems; i++)
390 {
391 text[i] = wxStringToXmString(items[i]);
392 }
393 XmListAddItemsUnselected(listBox, text, numItems, GetMotifPosition(pos));
394 InsertNewItemsClientData(pos, numItems, clientData, type);
395 #else
396 AllocClientData(numItems);
397
398 unsigned int idx = pos;
399 for ( i = 0; i < numItems; i++, idx++ )
400 {
401 text[i] = wxStringToXmString(items[i]);
402 XmListAddItemUnselected(listBox, text[i], GetMotifPosition(idx));
403 InsertNewItemClientData(idx, clientData, i, type);
404 }
405 #endif
406 for (i = 0; i < numItems; i++)
407 XmStringFree(text[i]);
408 delete[] text;
409
410 m_noItems += numItems;
411
412 SetSelectionPolicy();
413
414 return pos + numItems - 1;
415 }
416
417 void wxListBox::SetString(unsigned int n, const wxString& s)
418 {
419 wxSizeKeeper sk( this );
420 Widget listBox = (Widget) m_mainWidget;
421
422 wxXmString text( s );
423
424 // delete the item and add it again.
425 // FIXME isn't there a way to change it in place?
426 XmListDeletePos (listBox, n+1);
427 XmListAddItem (listBox, text(), n+1);
428
429 sk.Restore();
430 }
431
432 void wxListBox::Command (wxCommandEvent & event)
433 {
434 if (event.GetExtraLong())
435 SetSelection (event.GetInt());
436 else
437 {
438 Deselect (event.GetInt());
439 return;
440 }
441 ProcessCommand (event);
442 }
443
444 void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData,
445 XmListCallbackStruct * cbs)
446 {
447 wxListBox *item = (wxListBox *) clientData;
448
449 if (item->InSetValue())
450 return;
451
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) );
465 event.SetInt(n);
466 event.SetExtraLong(true);
467 event.SetEventObject(item);
468 event.SetString( item->GetString( n ) );
469
470 int x = -1;
471 if( NULL != cbs->event && cbs->event->type == ButtonRelease )
472 {
473 XButtonEvent* evt = (XButtonEvent*)cbs->event;
474
475 x = evt->x;
476 }
477
478 switch (cbs->reason)
479 {
480 case XmCR_MULTIPLE_SELECT:
481 case XmCR_BROWSE_SELECT:
482 #if wxUSE_CHECKLISTBOX
483 item->DoToggleItem( n, x );
484 #endif
485 case XmCR_DEFAULT_ACTION:
486 item->HandleWindowEvent(event);
487 break;
488 case XmCR_EXTENDED_SELECT:
489 switch (cbs->selection_type)
490 {
491 case XmINITIAL:
492 case XmADDITION:
493 case XmMODIFICATION:
494 item->DoToggleItem( n, x );
495 item->HandleWindowEvent(event);
496 break;
497 }
498 break;
499 }
500 }
501
502 WXWidget wxListBox::GetTopWidget() const
503 {
504 return (WXWidget) XtParent( (Widget) m_mainWidget );
505 }
506
507 void wxListBox::ChangeBackgroundColour()
508 {
509 wxWindow::ChangeBackgroundColour();
510
511 Widget parent = XtParent ((Widget) m_mainWidget);
512 Widget hsb, vsb;
513
514 XtVaGetValues (parent,
515 XmNhorizontalScrollBar, &hsb,
516 XmNverticalScrollBar, &vsb,
517 NULL);
518
519 /* TODO: should scrollbars be affected? Should probably have separate
520 * function to change them (by default, taken from wxSystemSettings)
521 */
522 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
523 wxDoChangeBackgroundColour((WXWidget) hsb, backgroundColour, true);
524 wxDoChangeBackgroundColour((WXWidget) vsb, backgroundColour, true);
525
526 XtVaSetValues (hsb,
527 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)),
528 NULL);
529 XtVaSetValues (vsb,
530 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)),
531 NULL);
532
533 // MBN: why change parent's background? It looks really ugly.
534 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
535 }
536
537 void wxListBox::ChangeForegroundColour()
538 {
539 wxWindow::ChangeForegroundColour();
540
541 Widget parent = XtParent ((Widget) m_mainWidget);
542 Widget hsb, vsb;
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
552 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
553 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
554 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
555 */
556 }
557
558 unsigned int wxListBox::GetCount() const
559 {
560 return m_noItems;
561 }
562
563 #define LIST_SCROLL_SPACING 6
564
565 wxSize 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
605 wxSize wxListBox::DoGetBestSize() const
606 {
607 return wxDoGetListBoxBestSize( (Widget)m_mainWidget, this );
608 }
609
610 #endif // wxUSE_LISTBOX