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