]> git.saurik.com Git - wxWidgets.git/blob - src/motif/listbox.cpp
libpng vulnerability CAN-2004-0421
[wxWidgets.git] / src / motif / listbox.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "listbox.h"
14 #endif
15
16 #ifdef __VMS
17 #define XtParent XTPARENT
18 #define XtDisplay XTDISPLAY
19 #endif
20
21 # include "wx/listbox.h"
22 #include "wx/settings.h"
23 #include "wx/dynarray.h"
24 #include "wx/log.h"
25 #include "wx/utils.h"
26 #include "wx/arrstr.h"
27
28 #ifdef __VMS__
29 #pragma message disable nosimpint
30 #endif
31 #include <Xm/List.h>
32 #ifdef __VMS__
33 #pragma message enable nosimpint
34 #endif
35 #include "wx/motif/private.h"
36
37 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
38
39 static void wxListBoxCallback(Widget w,
40 XtPointer clientData,
41 XmListCallbackStruct * cbs);
42
43 // ----------------------------------------------------------------------------
44 // wxSizeKeeper
45 // ----------------------------------------------------------------------------
46
47 // helper class to reduce code duplication
48 class wxSizeKeeper
49 {
50 int m_x, m_y;
51 wxWindow* m_w;
52 public:
53 wxSizeKeeper( wxWindow* w )
54 : m_w( w )
55 {
56 m_w->GetSize( &m_x, &m_y );
57 }
58
59 void Restore()
60 {
61 int x, y;
62
63 m_w->GetSize( &x, &y );
64 if( x != m_x || y != m_y )
65 m_w->SetSize( -1, -1, m_x, m_y );
66 }
67 };
68
69 // ============================================================================
70 // list box control implementation
71 // ============================================================================
72
73 // Listbox item
74 wxListBox::wxListBox()
75 {
76 m_noItems = 0;
77 }
78
79 bool wxListBox::Create(wxWindow *parent, wxWindowID id,
80 const wxPoint& pos,
81 const wxSize& size,
82 int n, const wxString choices[],
83 long style,
84 const wxValidator& validator,
85 const wxString& name)
86 {
87 if( !wxControl::CreateControl( parent, id, pos, size, style,
88 validator, name ) )
89 return FALSE;
90
91 m_noItems = n;
92 m_backgroundColour = * wxWHITE;
93
94 Widget parentWidget = (Widget) parent->GetClientWidget();
95
96 WXFontType fontType = (WXFontType)NULL;
97
98 if( m_font.Ok() )
99 {
100 fontType = m_font.GetFontType(XtDisplay(parentWidget));
101 }
102
103 Arg args[4];
104 int count = 0;
105 XtSetArg( args[count], XmNlistSizePolicy, XmCONSTANT ); ++count;
106 XtSetArg( args[count], XmNselectionPolicy,
107 ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT :
108 ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT :
109 XmBROWSE_SELECT );
110 ++count;
111 if( fontType )
112 {
113 XtSetArg( args[count], (String)wxFont::GetFontTag(), fontType );
114 ++count;
115 }
116 if( m_windowStyle & wxLB_ALWAYS_SB )
117 {
118 XtSetArg( args[count], XmNscrollBarDisplayPolicy, XmSTATIC );
119 ++count;
120 }
121
122 Widget listWidget =
123 XmCreateScrolledList(parentWidget,
124 wxConstCast(name.c_str(), char), args, count);
125
126 m_mainWidget = (WXWidget) listWidget;
127
128 Set(n, choices);
129
130 XtManageChild (listWidget);
131
132 wxSize best = GetBestSize();
133 if( size.x != -1 ) best.x = size.x;
134 if( size.y != -1 ) best.y = size.y;
135
136 XtAddCallback (listWidget,
137 XmNbrowseSelectionCallback,
138 (XtCallbackProc) wxListBoxCallback,
139 (XtPointer) this);
140 XtAddCallback (listWidget,
141 XmNextendedSelectionCallback,
142 (XtCallbackProc) wxListBoxCallback,
143 (XtPointer) this);
144 XtAddCallback (listWidget,
145 XmNmultipleSelectionCallback,
146 (XtCallbackProc) wxListBoxCallback,
147 (XtPointer) this);
148 XtAddCallback (listWidget,
149 XmNdefaultActionCallback,
150 (XtCallbackProc) wxListBoxCallback,
151 (XtPointer) this);
152
153 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
154 pos.x, pos.y, best.x, best.y);
155
156 ChangeBackgroundColour();
157
158 return TRUE;
159 }
160
161 bool 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
174 wxListBox::~wxListBox()
175 {
176 if( HasClientObjectData() )
177 m_clientDataDict.DestroyData();
178 }
179
180 void wxListBox::SetSelectionPolicy()
181 {
182 Widget listBox = (Widget)m_mainWidget;
183 Arg args[3];
184
185 XtSetArg( args[0], XmNlistSizePolicy, XmCONSTANT );
186
187 XtSetArg( args[1], XmNselectionPolicy,
188 ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT :
189 ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT :
190 XmBROWSE_SELECT );
191
192 XtSetValues( listBox, args, 2 );
193 }
194
195 void wxListBox::DoSetFirstItem( int N )
196 {
197 int count, length;
198
199 if (N < 0)
200 return;
201 XtVaGetValues ((Widget) m_mainWidget,
202 XmNvisibleItemCount, &count,
203 XmNitemCount, &length,
204 NULL);
205 if ((N + count) >= length)
206 N = length - count;
207 XmListSetPos ((Widget) m_mainWidget, N + 1);
208 }
209
210 void wxListBox::Delete(int N)
211 {
212 wxSizeKeeper sk( this );
213 Widget listBox = (Widget) m_mainWidget;
214
215 bool managed = XtIsManaged(listBox);
216
217 if (managed)
218 XtUnmanageChild (listBox);
219
220 XmListDeletePos (listBox, N + 1);
221
222 if (managed)
223 XtManageChild (listBox);
224
225 sk.Restore();
226 m_clientDataDict.Delete(N, HasClientObjectData());
227 m_noItems --;
228 }
229
230 int wxListBox::DoAppend(const wxString& item)
231 {
232 wxSizeKeeper sk( this );
233 Widget listBox = (Widget) m_mainWidget;
234
235 bool managed = XtIsManaged(listBox);
236
237 if (managed)
238 XtUnmanageChild (listBox);
239 int n;
240 XtVaGetValues (listBox, XmNitemCount, &n, NULL);
241 wxXmString text( item );
242 // XmListAddItem(listBox, text, n + 1);
243 XmListAddItemUnselected (listBox, text(), 0);
244
245 // It seems that if the list is cleared, we must re-ask for
246 // selection policy!!
247 SetSelectionPolicy();
248
249 if (managed)
250 XtManageChild (listBox);
251
252 sk.Restore();
253 m_noItems ++;
254
255 return GetCount() - 1;
256 }
257
258 void wxListBox::DoSetItems(const wxArrayString& items, void** clientData)
259 {
260 wxSizeKeeper sk( this );
261 Widget listBox = (Widget) m_mainWidget;
262
263 if( HasClientObjectData() )
264 m_clientDataDict.DestroyData();
265
266 bool managed = XtIsManaged(listBox);
267
268 if (managed)
269 XtUnmanageChild (listBox);
270 XmString *text = new XmString[items.GetCount()];
271 size_t i;
272 for (i = 0; i < items.GetCount(); ++i)
273 text[i] = wxStringToXmString (items[i]);
274
275 if ( clientData )
276 for (i = 0; i < items.GetCount(); ++i)
277 m_clientDataDict.Set(i, (wxClientData*)clientData[i], FALSE);
278
279 XmListAddItems (listBox, text, items.GetCount(), 0);
280 for (i = 0; i < items.GetCount(); i++)
281 XmStringFree (text[i]);
282 delete[] text;
283
284 // It seems that if the list is cleared, we must re-ask for
285 // selection policy!!
286 SetSelectionPolicy();
287
288 if (managed)
289 XtManageChild (listBox);
290
291 sk.Restore();
292
293 m_noItems = items.GetCount();
294 }
295
296 int wxDoFindStringInList(Widget w, const wxString& s)
297 {
298 wxXmString str( s );
299 int *positions = NULL;
300 int no_positions = 0;
301 bool success = XmListGetMatchPos (w, str(),
302 &positions, &no_positions);
303
304 if (success)
305 {
306 int pos = positions[0];
307 if (positions)
308 XtFree ((char *) positions);
309 return pos - 1;
310 }
311 else
312 return -1;
313 }
314
315 int wxListBox::FindString(const wxString& s) const
316 {
317 return wxDoFindStringInList( (Widget)m_mainWidget, s );
318 }
319
320 void wxListBox::Clear()
321 {
322 if (m_noItems <= 0)
323 return;
324
325 wxSizeKeeper sk( this );
326 Widget listBox = (Widget) m_mainWidget;
327
328 XmListDeleteAllItems (listBox);
329 if( HasClientObjectData() )
330 m_clientDataDict.DestroyData();
331
332 sk.Restore();
333
334 m_noItems = 0;
335 }
336
337 void wxListBox::SetSelection(int N, bool select)
338 {
339 m_inSetValue = TRUE;
340 if (select)
341 {
342 #if 0
343 if (m_windowStyle & wxLB_MULTIPLE)
344 {
345 int *selections = NULL;
346 int n = GetSelections (&selections);
347
348 // This hack is supposed to work, to make it possible
349 // to select more than one item, but it DOESN'T under Motif 1.1.
350
351 XtVaSetValues ((Widget) m_mainWidget,
352 XmNselectionPolicy, XmMULTIPLE_SELECT,
353 NULL);
354
355 int i;
356 for (i = 0; i < n; i++)
357 XmListSelectPos ((Widget) m_mainWidget,
358 selections[i] + 1, FALSE);
359
360 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
361
362 XtVaSetValues ((Widget) m_mainWidget,
363 XmNselectionPolicy, XmEXTENDED_SELECT,
364 NULL);
365 }
366 else
367 #endif // 0
368 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
369
370 }
371 else
372 XmListDeselectPos ((Widget) m_mainWidget, N + 1);
373
374 m_inSetValue = FALSE;
375 }
376
377 bool wxListBox::IsSelected(int N) const
378 {
379 // In Motif, no simple way to determine if the item is selected.
380 wxArrayInt theSelections;
381 int count = GetSelections (theSelections);
382 if (count == 0)
383 return FALSE;
384 else
385 {
386 int j;
387 for (j = 0; j < count; j++)
388 if (theSelections[j] == N)
389 return TRUE;
390 }
391 return FALSE;
392 }
393
394 void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
395 {
396 m_clientDataDict.Set(n, clientData, FALSE);
397 }
398
399 wxClientData* wxListBox::DoGetItemClientObject(int n) const
400 {
401 return m_clientDataDict.Get(n);
402 }
403
404 void *wxListBox::DoGetItemClientData(int N) const
405 {
406 return (void*)m_clientDataDict.Get(N);
407 }
408
409 void wxListBox::DoSetItemClientData(int N, void *Client_data)
410 {
411 m_clientDataDict.Set(N, (wxClientData*)Client_data, FALSE);
412 }
413
414 // Return number of selections and an array of selected integers
415 int wxListBox::GetSelections(wxArrayInt& aSelections) const
416 {
417 aSelections.Empty();
418
419 Widget listBox = (Widget) m_mainWidget;
420 int *posList = NULL;
421 int posCnt = 0;
422 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
423 if (flag)
424 {
425 if (posCnt > 0)
426 {
427 aSelections.Alloc(posCnt);
428
429 int i;
430 for (i = 0; i < posCnt; i++)
431 aSelections.Add(posList[i] - 1);
432
433 XtFree ((char *) posList);
434 return posCnt;
435 }
436 else
437 return 0;
438 }
439 else
440 return 0;
441 }
442
443 // Get single selection, for single choice list items
444 int wxDoGetSelectionInList(Widget listBox)
445 {
446 int *posList = NULL;
447 int posCnt = 0;
448 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
449 if (flag)
450 {
451 int id = -1;
452 if (posCnt > 0)
453 id = posList[0] - 1;
454 XtFree ((char *) posList);
455 return id;
456 }
457 else
458 return -1;
459 }
460
461 int wxListBox::GetSelection() const
462 {
463 return wxDoGetSelectionInList((Widget) m_mainWidget);
464 }
465
466 // Find string for position
467 wxString wxDoGetStringInList( Widget listBox, int n )
468 {
469 XmString *strlist;
470 int count;
471 XtVaGetValues( listBox,
472 XmNitemCount, &count,
473 XmNitems, &strlist,
474 NULL );
475 if( n < count && n >= 0 )
476 return wxXmStringToString( strlist[n] );
477 else
478 return wxEmptyString;
479 }
480
481 wxString wxListBox::GetString( int n ) const
482 {
483 return wxDoGetStringInList( (Widget)m_mainWidget, n );
484 }
485
486 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
487 {
488 wxSizeKeeper sk( this );
489 Widget listBox = (Widget) m_mainWidget;
490
491 bool managed = XtIsManaged(listBox);
492
493 if (managed)
494 XtUnmanageChild(listBox);
495
496 XmString *text = new XmString[items.GetCount()];
497 size_t i;
498 // Steve Hammes: Motif 1.1 compatibility
499 // #if XmVersion > 1100
500 // Corrected by Sergey Krasnov from Steve Hammes' code
501 #if XmVersion > 1001
502 for (i = 0; i < items.GetCount(); i++)
503 text[i] = wxStringToXmString(items[i]);
504 XmListAddItemsUnselected(listBox, text, items.GetCount(), pos+1);
505 #else
506 for (i = 0; i < items.GetCount(); i++)
507 {
508 text[i] = wxStringToXmString(items[i]);
509 // Another Sergey correction
510 XmListAddItemUnselected(listBox, text[i], pos+i+1);
511 }
512 #endif
513 for (i = 0; i < items.GetCount(); i++)
514 XmStringFree(text[i]);
515 delete[] text;
516
517 // It seems that if the list is cleared, we must re-ask for
518 // selection policy!!
519 SetSelectionPolicy();
520
521 if (managed)
522 XtManageChild(listBox);
523
524 sk.Restore();
525
526 m_noItems += items.GetCount();
527 }
528
529 void wxListBox::SetString(int N, const wxString& s)
530 {
531 wxSizeKeeper sk( this );
532 Widget listBox = (Widget) m_mainWidget;
533
534 wxXmString text( s );
535
536 // delete the item and add it again.
537 // FIXME isn't there a way to change it in place?
538 XmListDeletePos (listBox, N+1);
539 XmListAddItem (listBox, text(), N+1);
540
541 sk.Restore();
542 }
543
544 void wxListBox::Command (wxCommandEvent & event)
545 {
546 if (event.m_extraLong)
547 SetSelection (event.m_commandInt);
548 else
549 {
550 Deselect (event.m_commandInt);
551 return;
552 }
553 ProcessCommand (event);
554 }
555
556 void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData,
557 XmListCallbackStruct * cbs)
558 {
559 wxListBox *item = (wxListBox *) clientData;
560
561 if (item->InSetValue())
562 return;
563
564 wxEventType evtType;
565
566 if( cbs->reason == XmCR_DEFAULT_ACTION )
567 evtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED;
568 else
569 evtType = wxEVT_COMMAND_LISTBOX_SELECTED;
570
571 int n = cbs->item_position - 1;
572 wxCommandEvent event (evtType, item->GetId());
573 if ( item->HasClientObjectData() )
574 event.SetClientObject( item->GetClientObject(n) );
575 else if ( item->HasClientUntypedData() )
576 event.SetClientData( item->GetClientData(n) );
577 event.m_commandInt = n;
578 event.m_extraLong = TRUE;
579 event.SetEventObject(item);
580 event.SetString( item->GetString( n ) );
581
582 int x = -1;
583 if( NULL != cbs->event && cbs->event->type == ButtonRelease )
584 {
585 XButtonEvent* evt = (XButtonEvent*)cbs->event;
586
587 x = evt->x;
588 }
589
590 switch (cbs->reason)
591 {
592 case XmCR_MULTIPLE_SELECT:
593 case XmCR_BROWSE_SELECT:
594 #if wxUSE_CHECKLISTBOX
595 item->DoToggleItem( n, x );
596 #endif
597 case XmCR_DEFAULT_ACTION:
598 item->GetEventHandler()->ProcessEvent(event);
599 break;
600 case XmCR_EXTENDED_SELECT:
601 switch (cbs->selection_type)
602 {
603 case XmINITIAL:
604 case XmADDITION:
605 case XmMODIFICATION:
606 item->DoToggleItem( n, x );
607 item->GetEventHandler()->ProcessEvent(event);
608 break;
609 }
610 break;
611 }
612 }
613
614 WXWidget wxListBox::GetTopWidget() const
615 {
616 return (WXWidget) XtParent( (Widget) m_mainWidget );
617 }
618
619 void wxListBox::ChangeBackgroundColour()
620 {
621 wxWindow::ChangeBackgroundColour();
622
623 Widget parent = XtParent ((Widget) m_mainWidget);
624 Widget hsb, vsb;
625
626 XtVaGetValues (parent,
627 XmNhorizontalScrollBar, &hsb,
628 XmNverticalScrollBar, &vsb,
629 NULL);
630
631 /* TODO: should scrollbars be affected? Should probably have separate
632 * function to change them (by default, taken from wxSystemSettings)
633 */
634 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
635 wxDoChangeBackgroundColour((WXWidget) hsb, backgroundColour, TRUE);
636 wxDoChangeBackgroundColour((WXWidget) vsb, backgroundColour, TRUE);
637
638 XtVaSetValues (hsb,
639 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)),
640 NULL);
641 XtVaSetValues (vsb,
642 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)),
643 NULL);
644
645 // MBN: why change parent's background? It looks really ugly.
646 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
647 }
648
649 void wxListBox::ChangeForegroundColour()
650 {
651 wxWindow::ChangeForegroundColour();
652
653 Widget parent = XtParent ((Widget) m_mainWidget);
654 Widget hsb, vsb;
655
656 XtVaGetValues(parent,
657 XmNhorizontalScrollBar, &hsb,
658 XmNverticalScrollBar, &vsb,
659 NULL);
660
661 /* TODO: should scrollbars be affected? Should probably have separate
662 function to change them (by default, taken from wxSystemSettings)
663
664 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
665 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
666 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
667 */
668 }
669
670 int wxListBox::GetCount() const
671 {
672 return m_noItems;
673 }
674
675 #define LIST_SCROLL_SPACING 6
676
677 wxSize wxDoGetListBoxBestSize( Widget listWidget, const wxWindow* window )
678 {
679 int max;
680 Dimension spacing, highlight, xmargin, ymargin, shadow;
681 int width = 0;
682 int x, y;
683
684 XtVaGetValues( listWidget,
685 XmNitemCount, &max,
686 XmNlistSpacing, &spacing,
687 XmNhighlightThickness, &highlight,
688 XmNlistMarginWidth, &xmargin,
689 XmNlistMarginHeight, &ymargin,
690 XmNshadowThickness, &shadow,
691 NULL );
692
693 for( size_t i = 0; i < (size_t)max; ++i )
694 {
695 window->GetTextExtent( wxDoGetStringInList( listWidget, i ), &x, &y );
696 width = wxMax( width, x );
697 }
698
699 // use some arbitrary value if there are no strings
700 if( width == 0 )
701 width = 100;
702
703 // get my
704 window->GetTextExtent( "v", &x, &y );
705
706 // make it a little larger than widest string, plus the scrollbar
707 width += wxSystemSettings::GetMetric( wxSYS_VSCROLL_X )
708 + 2 * highlight + LIST_SCROLL_SPACING + 2 * xmargin + 2 * shadow;
709
710 // at least 3 items, at most 10
711 int height = wxMax( 3, wxMin( 10, max ) ) *
712 ( y + spacing + 2 * highlight ) + 2 * ymargin + 2 * shadow;
713
714 return wxSize( width, height );
715 }
716
717 wxSize wxListBox::DoGetBestSize() const
718 {
719 return wxDoGetListBoxBestSize( (Widget)m_mainWidget, this );
720 }
721