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