]> git.saurik.com Git - wxWidgets.git/blob - src/motif/listbox.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[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 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #if wxUSE_LISTBOX
15
16 #include "wx/listbox.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/dynarray.h"
20 #include "wx/log.h"
21 #include "wx/utils.h"
22 #include "wx/settings.h"
23 #include "wx/arrstr.h"
24 #endif
25
26 #ifdef __VMS__
27 #pragma message disable nosimpint
28 #endif
29 #include <Xm/List.h>
30 #ifdef __VMS__
31 #pragma message enable nosimpint
32 #endif
33 #include "wx/motif/private.h"
34
35 static void wxListBoxCallback(Widget w,
36 XtPointer clientData,
37 XmListCallbackStruct * cbs);
38
39 // ----------------------------------------------------------------------------
40 // wxSizeKeeper
41 // ----------------------------------------------------------------------------
42
43 // helper class to reduce code duplication
44 class wxSizeKeeper
45 {
46 int m_x, m_y;
47 int m_w, m_h;
48 wxWindow* m_wnd;
49 public:
50 wxSizeKeeper( wxWindow* w )
51 : m_wnd( w )
52 {
53 m_wnd->GetSize( &m_w, &m_h );
54 m_wnd->GetPosition( &m_x, &m_y );
55 }
56
57 void Restore()
58 {
59 int x, y;
60
61 m_wnd->GetSize( &x, &y );
62 if( x != m_x || y != m_y )
63 m_wnd->SetSize( m_x, m_y, m_w, m_h );
64 }
65 };
66
67 // ============================================================================
68 // list box control implementation
69 // ============================================================================
70
71 // Listbox item
72 wxListBox::wxListBox()
73 {
74 m_noItems = 0;
75 }
76
77 bool wxListBox::Create(wxWindow *parent, wxWindowID id,
78 const wxPoint& pos,
79 const wxSize& size,
80 int n, const wxString choices[],
81 long style,
82 const wxValidator& validator,
83 const wxString& name)
84 {
85 if( !wxControl::CreateControl( parent, id, pos, size, style,
86 validator, name ) )
87 return false;
88 PreCreation();
89
90 m_noItems = (unsigned int)n;
91
92 Widget parentWidget = (Widget) parent->GetClientWidget();
93 Display* dpy = XtDisplay(parentWidget);
94
95 Arg args[4];
96 int count = 0;
97 XtSetArg( args[count], XmNlistSizePolicy, XmCONSTANT ); ++count;
98 XtSetArg( args[count], XmNselectionPolicy,
99 ( m_windowStyle & wxLB_MULTIPLE ) ? XmMULTIPLE_SELECT :
100 ( m_windowStyle & wxLB_EXTENDED ) ? XmEXTENDED_SELECT :
101 XmBROWSE_SELECT );
102 ++count;
103 if( m_font.IsOk() )
104 {
105 XtSetArg( args[count],
106 (String)wxFont::GetFontTag(), m_font.GetFontTypeC(dpy) );
107 ++count;
108 }
109 if( m_windowStyle & wxLB_ALWAYS_SB )
110 {
111 XtSetArg( args[count], XmNscrollBarDisplayPolicy, XmSTATIC );
112 ++count;
113 }
114
115 Widget listWidget =
116 XmCreateScrolledList(parentWidget,
117 name.char_str(), args, count);
118
119 m_mainWidget = (WXWidget) listWidget;
120
121 Set(n, choices);
122
123 XtManageChild (listWidget);
124
125 wxSize best = GetBestSize();
126 if( size.x != -1 ) best.x = size.x;
127 if( size.y != -1 ) best.y = size.y;
128
129 XtAddCallback (listWidget,
130 XmNbrowseSelectionCallback,
131 (XtCallbackProc) wxListBoxCallback,
132 (XtPointer) this);
133 XtAddCallback (listWidget,
134 XmNextendedSelectionCallback,
135 (XtCallbackProc) wxListBoxCallback,
136 (XtPointer) this);
137 XtAddCallback (listWidget,
138 XmNmultipleSelectionCallback,
139 (XtCallbackProc) wxListBoxCallback,
140 (XtPointer) this);
141 XtAddCallback (listWidget,
142 XmNdefaultActionCallback,
143 (XtCallbackProc) wxListBoxCallback,
144 (XtPointer) this);
145
146 PostCreation();
147 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
148 pos.x, pos.y, best.x, best.y);
149
150 return true;
151 }
152
153 bool wxListBox::Create(wxWindow *parent, wxWindowID id,
154 const wxPoint& pos,
155 const wxSize& size,
156 const wxArrayString& choices,
157 long style,
158 const wxValidator& validator,
159 const wxString& name)
160 {
161 wxCArrayString chs(choices);
162 return Create(parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
163 style, validator, name);
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 (!IsValid(N))
186 return;
187
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::DoDeleteOneItem(unsigned int n)
198 {
199 Widget listBox = (Widget) m_mainWidget;
200
201 XmListDeletePos (listBox, n + 1);
202
203 wxListBoxBase::DoDeleteOneItem(n);
204 m_noItems --;
205 }
206
207 int wxDoFindStringInList(Widget w, const wxString& s)
208 {
209 wxXmString str( s );
210 int *positions = NULL;
211 int no_positions = 0;
212 bool success = XmListGetMatchPos (w, str(),
213 &positions, &no_positions);
214
215 if (success && positions)
216 {
217 int pos = positions[0];
218 XtFree ((char *) positions);
219 return pos - 1;
220 }
221 else
222 return -1;
223 }
224
225 int wxListBox::FindString(const wxString& s, bool WXUNUSED(bCase)) const
226 {
227 // FIXME: back to base class for not supported value of bCase
228
229 return wxDoFindStringInList( (Widget)m_mainWidget, s );
230 }
231
232 void wxListBox::DoClear()
233 {
234 if (!m_noItems)
235 return;
236
237 wxSizeKeeper sk( this );
238 Widget listBox = (Widget) m_mainWidget;
239
240 XmListDeleteAllItems (listBox);
241
242 sk.Restore();
243
244 wxListBoxBase::DoClear();
245 m_noItems = 0;
246 }
247
248 void wxListBox::DoSetSelection(int N, bool select)
249 {
250 m_inSetValue = true;
251 if (select)
252 {
253 #if 0
254 if (m_windowStyle & wxLB_MULTIPLE)
255 {
256 int *selections = NULL;
257 int n = GetSelections (&selections);
258
259 // This hack is supposed to work, to make it possible
260 // to select more than one item, but it DOESN'T under Motif 1.1.
261
262 XtVaSetValues ((Widget) m_mainWidget,
263 XmNselectionPolicy, XmMULTIPLE_SELECT,
264 NULL);
265
266 int i;
267 for (i = 0; i < n; i++)
268 XmListSelectPos ((Widget) m_mainWidget,
269 selections[i] + 1, False);
270
271 XmListSelectPos ((Widget) m_mainWidget, N + 1, False);
272
273 XtVaSetValues ((Widget) m_mainWidget,
274 XmNselectionPolicy, XmEXTENDED_SELECT,
275 NULL);
276 }
277 else
278 #endif // 0
279 XmListSelectPos ((Widget) m_mainWidget, N + 1, False);
280
281 }
282 else
283 XmListDeselectPos ((Widget) m_mainWidget, N + 1);
284
285 m_inSetValue = false;
286 }
287
288 bool wxListBox::IsSelected(int N) const
289 {
290 // In Motif, no simple way to determine if the item is selected.
291 wxArrayInt theSelections;
292 int count = GetSelections (theSelections);
293 if (count == 0)
294 return false;
295 else
296 {
297 int j;
298 for (j = 0; j < count; j++)
299 if (theSelections[j] == N)
300 return true;
301 }
302 return false;
303 }
304
305 // Return number of selections and an array of selected integers
306 int wxListBox::GetSelections(wxArrayInt& aSelections) const
307 {
308 aSelections.Empty();
309
310 Widget listBox = (Widget) m_mainWidget;
311 int *posList = NULL;
312 int posCnt = 0;
313 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
314 if (flag)
315 {
316 if (posCnt > 0)
317 {
318 aSelections.Alloc(posCnt);
319
320 int i;
321 for (i = 0; i < posCnt; i++)
322 aSelections.Add(posList[i] - 1);
323
324 XtFree ((char *) posList);
325 return posCnt;
326 }
327 else
328 return 0;
329 }
330 else
331 return 0;
332 }
333
334 // Get single selection, for single choice list items
335 int wxDoGetSelectionInList(Widget listBox)
336 {
337 int *posList = NULL;
338 int posCnt = 0;
339 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
340 if (flag)
341 {
342 int id = -1;
343 if (posCnt > 0)
344 id = posList[0] - 1;
345 XtFree ((char *) posList);
346 return id;
347 }
348 else
349 return -1;
350 }
351
352 int wxListBox::GetSelection() const
353 {
354 return wxDoGetSelectionInList((Widget) m_mainWidget);
355 }
356
357 // Find string for position
358 wxString wxDoGetStringInList( Widget listBox, int n )
359 {
360 XmString *strlist;
361 int count;
362 XtVaGetValues( listBox,
363 XmNitemCount, &count,
364 XmNitems, &strlist,
365 NULL );
366 if( n < count && n >= 0 )
367 return wxXmStringToString( strlist[n] );
368 else
369 return wxEmptyString;
370 }
371
372 wxString wxListBox::GetString(unsigned int n) const
373 {
374 return wxDoGetStringInList( (Widget)m_mainWidget, n );
375 }
376
377 int wxListBox::DoInsertItems(const wxArrayStringsAdapter & items,
378 unsigned int pos,
379 void **clientData, wxClientDataType type)
380 {
381 Widget listBox = (Widget) m_mainWidget;
382
383 const unsigned int numItems = items.GetCount();
384
385 XmString *text = new XmString[numItems];
386 unsigned int i;
387 #if XmVersion > 1001
388 for (i = 0; i < numItems; i++)
389 {
390 text[i] = wxStringToXmString(items[i]);
391 }
392 XmListAddItemsUnselected(listBox, text, numItems, GetMotifPosition(pos));
393 InsertNewItemsClientData(pos, numItems, clientData, type);
394 #else
395 AllocClientData(numItems);
396
397 unsigned int idx = pos;
398 for ( i = 0; i < numItems; i++, idx++ )
399 {
400 text[i] = wxStringToXmString(items[i]);
401 XmListAddItemUnselected(listBox, text[i], GetMotifPosition(idx));
402 InsertNewItemClientData(idx, clientData, i, type);
403 }
404 #endif
405 for (i = 0; i < numItems; i++)
406 XmStringFree(text[i]);
407 delete[] text;
408
409 m_noItems += numItems;
410
411 SetSelectionPolicy();
412
413 return pos + numItems - 1;
414 }
415
416 void wxListBox::SetString(unsigned int n, const wxString& s)
417 {
418 wxSizeKeeper sk( this );
419 Widget listBox = (Widget) m_mainWidget;
420
421 wxXmString text( s );
422
423 // delete the item and add it again.
424 // FIXME isn't there a way to change it in place?
425 XmListDeletePos (listBox, n+1);
426 XmListAddItem (listBox, text(), n+1);
427
428 sk.Restore();
429 }
430
431 void wxListBox::Command (wxCommandEvent & event)
432 {
433 if (event.GetExtraLong())
434 SetSelection (event.GetInt());
435 else
436 {
437 Deselect (event.GetInt());
438 return;
439 }
440 ProcessCommand (event);
441 }
442
443 void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData,
444 XmListCallbackStruct * cbs)
445 {
446 wxListBox *item = (wxListBox *) clientData;
447
448 if (item->InSetValue())
449 return;
450
451 wxEventType evtType;
452
453 if( cbs->reason == XmCR_DEFAULT_ACTION )
454 evtType = wxEVT_LISTBOX_DCLICK;
455 else
456 evtType = wxEVT_LISTBOX;
457
458 int n = cbs->item_position - 1;
459 wxCommandEvent event (evtType, item->GetId());
460 if ( item->HasClientObjectData() )
461 event.SetClientObject( item->GetClientObject(n) );
462 else if ( item->HasClientUntypedData() )
463 event.SetClientData( item->GetClientData(n) );
464 event.SetInt(n);
465 event.SetExtraLong(true);
466 event.SetEventObject(item);
467 event.SetString( item->GetString( n ) );
468
469 int x = -1;
470 if( NULL != cbs->event && cbs->event->type == ButtonRelease )
471 {
472 XButtonEvent* evt = (XButtonEvent*)cbs->event;
473
474 x = evt->x;
475 }
476
477 switch (cbs->reason)
478 {
479 case XmCR_MULTIPLE_SELECT:
480 case XmCR_BROWSE_SELECT:
481 #if wxUSE_CHECKLISTBOX
482 item->DoToggleItem( n, x );
483 #endif
484 case XmCR_DEFAULT_ACTION:
485 item->HandleWindowEvent(event);
486 break;
487 case XmCR_EXTENDED_SELECT:
488 switch (cbs->selection_type)
489 {
490 case XmINITIAL:
491 case XmADDITION:
492 case XmMODIFICATION:
493 item->DoToggleItem( n, x );
494 item->HandleWindowEvent(event);
495 break;
496 }
497 break;
498 }
499 }
500
501 WXWidget wxListBox::GetTopWidget() const
502 {
503 return (WXWidget) XtParent( (Widget) m_mainWidget );
504 }
505
506 void wxListBox::ChangeBackgroundColour()
507 {
508 wxWindow::ChangeBackgroundColour();
509
510 Widget parent = XtParent ((Widget) m_mainWidget);
511 Widget hsb, vsb;
512
513 XtVaGetValues (parent,
514 XmNhorizontalScrollBar, &hsb,
515 XmNverticalScrollBar, &vsb,
516 NULL);
517
518 /* TODO: should scrollbars be affected? Should probably have separate
519 * function to change them (by default, taken from wxSystemSettings)
520 */
521 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
522 wxDoChangeBackgroundColour((WXWidget) hsb, backgroundColour, true);
523 wxDoChangeBackgroundColour((WXWidget) vsb, backgroundColour, true);
524
525 XtVaSetValues (hsb,
526 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)),
527 NULL);
528 XtVaSetValues (vsb,
529 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)),
530 NULL);
531
532 // MBN: why change parent's background? It looks really ugly.
533 // wxDoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, true);
534 }
535
536 void wxListBox::ChangeForegroundColour()
537 {
538 wxWindow::ChangeForegroundColour();
539
540 Widget parent = XtParent ((Widget) m_mainWidget);
541 Widget hsb, vsb;
542
543 XtVaGetValues(parent,
544 XmNhorizontalScrollBar, &hsb,
545 XmNverticalScrollBar, &vsb,
546 NULL);
547
548 /* TODO: should scrollbars be affected? Should probably have separate
549 function to change them (by default, taken from wxSystemSettings)
550
551 wxDoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
552 wxDoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
553 wxDoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
554 */
555 }
556
557 unsigned int wxListBox::GetCount() const
558 {
559 return m_noItems;
560 }
561
562 #define LIST_SCROLL_SPACING 6
563
564 wxSize wxDoGetListBoxBestSize( Widget listWidget, const wxWindow* window )
565 {
566 int max;
567 Dimension spacing, highlight, xmargin, ymargin, shadow;
568 int width = 0;
569 int x, y;
570
571 XtVaGetValues( listWidget,
572 XmNitemCount, &max,
573 XmNlistSpacing, &spacing,
574 XmNhighlightThickness, &highlight,
575 XmNlistMarginWidth, &xmargin,
576 XmNlistMarginHeight, &ymargin,
577 XmNshadowThickness, &shadow,
578 NULL );
579
580 for( size_t i = 0; i < (size_t)max; ++i )
581 {
582 window->GetTextExtent( wxDoGetStringInList( listWidget, i ), &x, &y );
583 width = wxMax( width, x );
584 }
585
586 // use some arbitrary value if there are no strings
587 if( width == 0 )
588 width = 100;
589
590 // get my
591 window->GetTextExtent( "v", &x, &y );
592
593 // make it a little larger than widest string, plus the scrollbar
594 width += wxSystemSettings::GetMetric( wxSYS_VSCROLL_X )
595 + 2 * highlight + LIST_SCROLL_SPACING + 2 * xmargin + 2 * shadow;
596
597 // at least 3 items, at most 10
598 int height = wxMax( 3, wxMin( 10, max ) ) *
599 ( y + spacing + 2 * highlight ) + 2 * ymargin + 2 * shadow;
600
601 return wxSize( width, height );
602 }
603
604 wxSize wxListBox::DoGetBestSize() const
605 {
606 return wxDoGetListBoxBestSize( (Widget)m_mainWidget, this );
607 }
608
609 #endif // wxUSE_LISTBOX