]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/motif/listbox.cpp
really show the menubar (calling Show(TRUE) doesn't do it any more)
[wxWidgets.git] / src / motif / listbox.cpp
... / ...
CommitLineData
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
39static void wxListBoxCallback(Widget w,
40 XtPointer clientData,
41 XmListCallbackStruct * cbs);
42
43// ----------------------------------------------------------------------------
44// wxSizeKeeper
45// ----------------------------------------------------------------------------
46
47// helper class to reduce code duplication
48class wxSizeKeeper
49{
50 int m_x, m_y;
51 wxWindow* m_w;
52public:
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
74wxListBox::wxListBox()
75{
76 m_noItems = 0;
77}
78
79bool 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
161bool 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
174wxListBox::~wxListBox()
175{
176 if( HasClientObjectData() )
177 m_clientDataDict.DestroyData();
178}
179
180void 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
195void 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
210void 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
230int 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
258void 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
296int 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
315int wxListBox::FindString(const wxString& s) const
316{
317 return wxDoFindStringInList( (Widget)m_mainWidget, s );
318}
319
320void 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
337void 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
377bool 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
394void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
395{
396 m_clientDataDict.Set(n, clientData, FALSE);
397}
398
399wxClientData* wxListBox::DoGetItemClientObject(int n) const
400{
401 return m_clientDataDict.Get(n);
402}
403
404void *wxListBox::DoGetItemClientData(int N) const
405{
406 return (void*)m_clientDataDict.Get(N);
407}
408
409void 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
415int 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
444int 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
461int wxListBox::GetSelection() const
462{
463 return wxDoGetSelectionInList((Widget) m_mainWidget);
464}
465
466// Find string for position
467wxString 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
481wxString wxListBox::GetString( int n ) const
482{
483 return wxDoGetStringInList( (Widget)m_mainWidget, n );
484}
485
486void 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
529void 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
544void 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
556void 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
614WXWidget wxListBox::GetTopWidget() const
615{
616 return (WXWidget) XtParent( (Widget) m_mainWidget );
617}
618
619void 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
649void 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
670int wxListBox::GetCount() const
671{
672 return m_noItems;
673}
674
675#define LIST_SCROLL_SPACING 6
676
677wxSize 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
717wxSize wxListBox::DoGetBestSize() const
718{
719 return wxDoGetListBoxBestSize( (Widget)m_mainWidget, this );
720}
721