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