]> git.saurik.com Git - wxWidgets.git/blob - src/motif/listbox.cpp
Implement vertical toolbar under Motif.
[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 wxListBox::FindString(const wxString& s) const
276 {
277 wxXmString str( s );
278 int *positions = NULL;
279 int no_positions = 0;
280 bool success = XmListGetMatchPos ((Widget) m_mainWidget, 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 void wxListBox::Clear()
295 {
296 if (m_noItems <= 0)
297 return;
298
299 wxSizeKeeper sk( this );
300 Widget listBox = (Widget) m_mainWidget;
301
302 XmListDeleteAllItems (listBox);
303 if( HasClientObjectData() )
304 m_clientDataDict.DestroyData();
305
306 sk.Restore();
307
308 m_noItems = 0;
309 }
310
311 void wxListBox::SetSelection(int N, bool select)
312 {
313 m_inSetValue = TRUE;
314 if (select)
315 {
316 #if 0
317 if (m_windowStyle & wxLB_MULTIPLE)
318 {
319 int *selections = NULL;
320 int n = GetSelections (&selections);
321
322 // This hack is supposed to work, to make it possible
323 // to select more than one item, but it DOESN'T under Motif 1.1.
324
325 XtVaSetValues ((Widget) m_mainWidget,
326 XmNselectionPolicy, XmMULTIPLE_SELECT,
327 NULL);
328
329 int i;
330 for (i = 0; i < n; i++)
331 XmListSelectPos ((Widget) m_mainWidget,
332 selections[i] + 1, FALSE);
333
334 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
335
336 XtVaSetValues ((Widget) m_mainWidget,
337 XmNselectionPolicy, XmEXTENDED_SELECT,
338 NULL);
339 }
340 else
341 #endif // 0
342 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
343
344 }
345 else
346 XmListDeselectPos ((Widget) m_mainWidget, N + 1);
347
348 m_inSetValue = FALSE;
349 }
350
351 bool wxListBox::IsSelected(int N) const
352 {
353 // In Motif, no simple way to determine if the item is selected.
354 wxArrayInt theSelections;
355 int count = GetSelections (theSelections);
356 if (count == 0)
357 return FALSE;
358 else
359 {
360 int j;
361 for (j = 0; j < count; j++)
362 if (theSelections[j] == N)
363 return TRUE;
364 }
365 return FALSE;
366 }
367
368 void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
369 {
370 m_clientDataDict.Set(n, clientData, FALSE);
371 }
372
373 wxClientData* wxListBox::DoGetItemClientObject(int n) const
374 {
375 return m_clientDataDict.Get(n);
376 }
377
378 void *wxListBox::DoGetItemClientData(int N) const
379 {
380 return (void*)m_clientDataDict.Get(N);
381 }
382
383 void wxListBox::DoSetItemClientData(int N, void *Client_data)
384 {
385 m_clientDataDict.Set(N, (wxClientData*)Client_data, FALSE);
386 }
387
388 // Return number of selections and an array of selected integers
389 int wxListBox::GetSelections(wxArrayInt& aSelections) const
390 {
391 aSelections.Empty();
392
393 Widget listBox = (Widget) m_mainWidget;
394 int *posList = NULL;
395 int posCnt = 0;
396 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
397 if (flag)
398 {
399 if (posCnt > 0)
400 {
401 aSelections.Alloc(posCnt);
402
403 int i;
404 for (i = 0; i < posCnt; i++)
405 aSelections.Add(posList[i] - 1);
406
407 XtFree ((char *) posList);
408 return posCnt;
409 }
410 else
411 return 0;
412 }
413 else
414 return 0;
415 }
416
417 // Get single selection, for single choice list items
418 int wxListBox::GetSelection() const
419 {
420 Widget listBox = (Widget) m_mainWidget;
421 int *posList = NULL;
422 int posCnt = 0;
423 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
424 if (flag)
425 {
426 int id = -1;
427 if (posCnt > 0)
428 id = posList[0] - 1;
429 XtFree ((char *) posList);
430 return id;
431 }
432 else
433 return -1;
434 }
435
436 // Find string for position
437 wxString wxListBox::GetString(int N) const
438 {
439 Widget listBox = (Widget) m_mainWidget;
440 XmString *strlist;
441 int n;
442 XtVaGetValues (listBox, XmNitemCount, &n, XmNitems, &strlist, NULL);
443 if (N <= n && N >= 0)
444 {
445 char *txt;
446 if (XmStringGetLtoR (strlist[N], XmSTRING_DEFAULT_CHARSET, &txt))
447 {
448 wxString str(txt);
449 XtFree (txt);
450 return str;
451 }
452 else
453 return wxEmptyString;
454 }
455 else
456 return wxEmptyString;
457 }
458
459 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
460 {
461 wxSizeKeeper sk( this );
462 Widget listBox = (Widget) m_mainWidget;
463
464 bool managed = XtIsManaged(listBox);
465
466 if (managed)
467 XtUnmanageChild(listBox);
468
469 XmString *text = new XmString[items.GetCount()];
470 size_t i;
471 // Steve Hammes: Motif 1.1 compatibility
472 // #if XmVersion > 1100
473 // Corrected by Sergey Krasnov from Steve Hammes' code
474 #if XmVersion > 1001
475 for (i = 0; i < items.GetCount(); i++)
476 text[i] = XmStringCreateSimple((char*)items[i].c_str());
477 XmListAddItemsUnselected(listBox, text, items.GetCount(), pos+1);
478 #else
479 for (i = 0; i < items.GetCount(); i++)
480 {
481 text[i] = XmStringCreateSimple((char*)items[i].c_str());
482 // Another Sergey correction
483 XmListAddItemUnselected(listBox, text[i], pos+i+1);
484 }
485 #endif
486 for (i = 0; i < items.GetCount(); i++)
487 XmStringFree(text[i]);
488 delete[] text;
489
490 // It seems that if the list is cleared, we must re-ask for
491 // selection policy!!
492 SetSelectionPolicy();
493
494 if (managed)
495 XtManageChild(listBox);
496
497 sk.Restore();
498
499 m_noItems += items.GetCount();
500 }
501
502 void wxListBox::SetString(int N, const wxString& s)
503 {
504 wxSizeKeeper sk( this );
505 Widget listBox = (Widget) m_mainWidget;
506
507 wxXmString text( s );
508
509 // delete the item and add it again.
510 // FIXME isn't there a way to change it in place?
511 XmListDeletePos (listBox, N+1);
512 XmListAddItem (listBox, text(), N+1);
513
514 sk.Restore();
515 }
516
517 void wxListBox::Command (wxCommandEvent & event)
518 {
519 if (event.m_extraLong)
520 SetSelection (event.m_commandInt);
521 else
522 {
523 Deselect (event.m_commandInt);
524 return;
525 }
526 ProcessCommand (event);
527 }
528
529 void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData,
530 XmListCallbackStruct * cbs)
531 {
532 wxListBox *item = (wxListBox *) clientData;
533
534 if (item->InSetValue())
535 return;
536
537 wxEventType evtType;
538
539 if( cbs->reason == XmCR_DEFAULT_ACTION )
540 evtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED;
541 else
542 evtType = wxEVT_COMMAND_LISTBOX_SELECTED;
543
544 int n = cbs->item_position - 1;
545 wxCommandEvent event (evtType, item->GetId());
546 if ( item->HasClientObjectData() )
547 event.SetClientObject( item->GetClientObject(n) );
548 else if ( item->HasClientUntypedData() )
549 event.SetClientData( item->GetClientData(n) );
550 event.m_commandInt = n;
551 event.m_extraLong = TRUE;
552 event.SetEventObject(item);
553 event.SetString( item->GetString( n ) );
554
555 int x = -1;
556 if( NULL != cbs->event && cbs->event->type == ButtonRelease )
557 {
558 XButtonEvent* evt = (XButtonEvent*)cbs->event;
559
560 x = evt->x;
561 }
562
563 switch (cbs->reason)
564 {
565 case XmCR_MULTIPLE_SELECT:
566 case XmCR_BROWSE_SELECT:
567 #if wxUSE_CHECKLISTBOX
568 item->DoToggleItem( n, x );
569 #endif
570 case XmCR_DEFAULT_ACTION:
571 item->GetEventHandler()->ProcessEvent(event);
572 break;
573 case XmCR_EXTENDED_SELECT:
574 switch (cbs->selection_type)
575 {
576 case XmINITIAL:
577 case XmADDITION:
578 case XmMODIFICATION:
579 item->DoToggleItem( n, x );
580 item->GetEventHandler()->ProcessEvent(event);
581 break;
582 }
583 break;
584 }
585 }
586
587 WXWidget wxListBox::GetTopWidget() const
588 {
589 return (WXWidget) XtParent( (Widget) m_mainWidget );
590 }
591
592 void wxListBox::ChangeBackgroundColour()
593 {
594 wxWindow::ChangeBackgroundColour();
595
596 Widget parent = XtParent ((Widget) m_mainWidget);
597 Widget hsb, vsb;
598
599 XtVaGetValues (parent,
600 XmNhorizontalScrollBar, &hsb,
601 XmNverticalScrollBar, &vsb,
602 NULL);
603
604 /* TODO: should scrollbars be affected? Should probably have separate
605 * function to change them (by default, taken from wxSystemSettings)
606 */
607 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
608 DoChangeBackgroundColour((WXWidget) hsb, backgroundColour, TRUE);
609 DoChangeBackgroundColour((WXWidget) vsb, backgroundColour, TRUE);
610
611 XtVaSetValues (hsb,
612 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)),
613 NULL);
614 XtVaSetValues (vsb,
615 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)),
616 NULL);
617
618 DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
619 }
620
621 void wxListBox::ChangeForegroundColour()
622 {
623 wxWindow::ChangeForegroundColour();
624
625 Widget parent = XtParent ((Widget) m_mainWidget);
626 Widget hsb, vsb;
627
628 XtVaGetValues(parent,
629 XmNhorizontalScrollBar, &hsb,
630 XmNverticalScrollBar, &vsb,
631 NULL);
632
633 /* TODO: should scrollbars be affected? Should probably have separate
634 function to change them (by default, taken from wxSystemSettings)
635
636 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
637 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
638 DoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
639 */
640 }
641
642 int wxListBox::GetCount() const
643 {
644 return m_noItems;
645 }