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