]> git.saurik.com Git - wxWidgets.git/blob - src/motif/listbox.cpp
merged 2.4 branch into the trunk
[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 // list box control implementation
44 // ============================================================================
45
46 // Listbox item
47 wxListBox::wxListBox() : m_clientDataList(wxKEY_INTEGER)
48 {
49 m_noItems = 0;
50 }
51
52 bool wxListBox::Create(wxWindow *parent, wxWindowID id,
53 const wxPoint& pos,
54 const wxSize& size,
55 int n, const wxString choices[],
56 long style,
57 const wxValidator& validator,
58 const wxString& name)
59 {
60 m_windowStyle = style;
61 m_noItems = n;
62 // m_backgroundColour = parent->GetBackgroundColour();
63 m_backgroundColour = * wxWHITE;
64 m_foregroundColour = parent->GetForegroundColour();
65
66 SetName(name);
67 SetValidator(validator);
68
69 if (parent) parent->AddChild(this);
70
71 m_windowId = ( id == -1 ) ? (int)NewControlId() : id;
72
73 Widget parentWidget = (Widget) parent->GetClientWidget();
74
75 Arg args[3];
76 int count;
77 XtSetArg (args[0], XmNlistSizePolicy, XmCONSTANT);
78 if (m_windowStyle & wxLB_MULTIPLE)
79 XtSetArg (args[1], XmNselectionPolicy, XmMULTIPLE_SELECT);
80 else if (m_windowStyle & wxLB_EXTENDED)
81 XtSetArg (args[1], XmNselectionPolicy, XmEXTENDED_SELECT);
82 else
83 XtSetArg (args[1], XmNselectionPolicy, XmBROWSE_SELECT);
84 if (m_windowStyle & wxLB_ALWAYS_SB)
85 {
86 XtSetArg (args[2], XmNscrollBarDisplayPolicy, XmSTATIC);
87 count = 3;
88 }
89 else
90 count = 2;
91
92 Widget listWidget = XmCreateScrolledList(parentWidget,
93 (char*)name.c_str(), args, count);
94
95 m_mainWidget = (WXWidget) listWidget;
96
97 Set(n, choices);
98
99 XtManageChild (listWidget);
100
101 long width = size.x;
102 long height = size.y;
103 if (width == -1)
104 width = 150;
105 if (height == -1)
106 height = 80;
107
108 XtAddCallback (listWidget,
109 XmNbrowseSelectionCallback,
110 (XtCallbackProc) wxListBoxCallback,
111 (XtPointer) this);
112 XtAddCallback (listWidget,
113 XmNextendedSelectionCallback,
114 (XtCallbackProc) wxListBoxCallback,
115 (XtPointer) this);
116 XtAddCallback (listWidget,
117 XmNmultipleSelectionCallback,
118 (XtCallbackProc) wxListBoxCallback,
119 (XtPointer) this);
120 XtAddCallback (listWidget,
121 XmNdefaultActionCallback,
122 (XtCallbackProc) wxListBoxCallback,
123 (XtPointer) this);
124
125 m_font = parent->GetFont();
126 ChangeFont(FALSE);
127
128 SetCanAddEventHandler(TRUE);
129 AttachWidget (parent, m_mainWidget, (WXWidget) NULL,
130 pos.x, pos.y, width, height);
131
132 ChangeBackgroundColour();
133
134 return TRUE;
135 }
136
137 wxListBox::~wxListBox()
138 {
139 }
140
141 void wxListBox::DoSetFirstItem( int N )
142 {
143 int count, length;
144
145 if (N < 0)
146 return;
147 XtVaGetValues ((Widget) m_mainWidget,
148 XmNvisibleItemCount, &count,
149 XmNitemCount, &length,
150 NULL);
151 if ((N + count) >= length)
152 N = length - count;
153 XmListSetPos ((Widget) m_mainWidget, N + 1);
154 }
155
156 void wxListBox::Delete(int N)
157 {
158 int width1, height1;
159 int width2, height2;
160 Widget listBox = (Widget) m_mainWidget;
161 GetSize (&width1, &height1);
162
163 bool managed = XtIsManaged(listBox);
164
165 if (managed)
166 XtUnmanageChild (listBox);
167
168 XmListDeletePos (listBox, N + 1);
169
170 if (managed)
171 XtManageChild (listBox);
172
173 GetSize (&width2, &height2);
174 // Correct for randomly resized listbox - bad boy, Motif!
175 if (width1 != width2 || height1 != height2)
176 SetSize (-1, -1, width1, height1);
177
178 // (JDH) need to add code here to take care of clientDataList
179 // get item from list
180 wxNode *node = m_clientDataList.Find((long)N);
181 // if existed then delete from list
182 if (node) m_clientDataList.DeleteNode(node);
183 // we now have to adjust all keys that are >=N+1
184 node = m_clientDataList.First();
185 while (node)
186 {
187 if (node->GetKeyInteger() >= (long)(N+1))
188 node->SetKeyInteger(node->GetKeyInteger() - 1);
189 node = node->Next();
190 }
191
192 m_noItems --;
193 }
194
195 int wxListBox::DoAppend(const wxString& item)
196 {
197 int width1, height1;
198 int width2, height2;
199
200 Widget listBox = (Widget) m_mainWidget;
201 GetSize (&width1, &height1);
202
203 bool managed = XtIsManaged(listBox);
204
205 if (managed)
206 XtUnmanageChild (listBox);
207 int n;
208 XtVaGetValues (listBox, XmNitemCount, &n, NULL);
209 XmString text = XmStringCreateSimple ((char*) (const char*) item);
210 // XmListAddItem(listBox, text, n + 1);
211 XmListAddItemUnselected (listBox, text, 0);
212 XmStringFree (text);
213
214 // It seems that if the list is cleared, we must re-ask for
215 // selection policy!!
216 Arg args[3];
217 XtSetArg (args[0], XmNlistSizePolicy, XmCONSTANT);
218 if (m_windowStyle & wxLB_MULTIPLE)
219 XtSetArg (args[1], XmNselectionPolicy, XmMULTIPLE_SELECT);
220 else if (m_windowStyle & wxLB_EXTENDED)
221 XtSetArg (args[1], XmNselectionPolicy, XmEXTENDED_SELECT);
222 else
223 XtSetArg (args[1], XmNselectionPolicy, XmBROWSE_SELECT);
224 XtSetValues (listBox, args, 2);
225
226 if (managed)
227 XtManageChild (listBox);
228
229 GetSize (&width2, &height2);
230 // Correct for randomly resized listbox - bad boy, Motif!
231 if (width1 != width2 || height1 != height2)
232 SetSize (-1, -1, width1, height1);
233 m_noItems ++;
234
235 return GetCount() - 1;
236 }
237
238 void wxListBox::DoSetItems(const wxArrayString& items, void** clientData)
239 {
240 m_clientDataList.Clear();
241 int width1, height1;
242 int width2, height2;
243
244 Widget listBox = (Widget) m_mainWidget;
245 GetSize (&width1, &height1);
246
247 bool managed = XtIsManaged(listBox);
248
249 if (managed)
250 XtUnmanageChild (listBox);
251 XmString *text = new XmString[items.GetCount()];
252 size_t i;
253 for (i = 0; i < items.GetCount(); ++i)
254 text[i] = XmStringCreateSimple ((char*)items[i].c_str());
255
256 if ( clientData )
257 for (i = 0; i < items.GetCount(); ++i)
258 m_clientDataList.Append ((long) i, (wxObject *) clientData[i]);
259
260 XmListAddItems (listBox, text, items.GetCount(), 0);
261 for (i = 0; i < items.GetCount(); i++)
262 XmStringFree (text[i]);
263 delete[] text;
264
265 // It seems that if the list is cleared, we must re-ask for
266 // selection policy!!
267 Arg args[3];
268 XtSetArg (args[0], XmNlistSizePolicy, XmCONSTANT);
269 if (m_windowStyle & wxLB_MULTIPLE)
270 XtSetArg (args[1], XmNselectionPolicy, XmMULTIPLE_SELECT);
271 else if (m_windowStyle & wxLB_EXTENDED)
272 XtSetArg (args[1], XmNselectionPolicy, XmEXTENDED_SELECT);
273 else
274 XtSetArg (args[1], XmNselectionPolicy, XmBROWSE_SELECT);
275 XtSetValues (listBox, args, 2);
276
277 if (managed)
278 XtManageChild (listBox);
279
280 GetSize (&width2, &height2);
281 // Correct for randomly resized listbox - bad boy, Motif!
282 if (width1 != width2 || height1 != height2)
283 SetSize (-1, -1, width1, height1);
284
285 m_noItems = items.GetCount();
286 }
287
288 int wxListBox::FindString(const wxString& s) const
289 {
290 XmString str = XmStringCreateSimple ((char*) (const char*) s);
291 int *positions = NULL;
292 int no_positions = 0;
293 bool success = XmListGetMatchPos ((Widget) m_mainWidget, str,
294 &positions, &no_positions);
295 XmStringFree (str);
296 if (success)
297 {
298 int pos = positions[0];
299 if (positions)
300 XtFree ((char *) positions);
301 return pos - 1;
302 }
303 else
304 return -1;
305 }
306
307 void wxListBox::Clear()
308 {
309 if (m_noItems <= 0)
310 return;
311
312 int width1, height1;
313 int width2, height2;
314
315 Widget listBox = (Widget) m_mainWidget;
316 GetSize (&width1, &height1);
317
318 XmListDeleteAllItems (listBox);
319 m_clientDataList.Clear ();
320 GetSize (&width2, &height2);
321
322 // Correct for randomly resized listbox - bad boy, Motif!
323 if (width1 != width2 || height1 != height2)
324 SetSize (-1, -1, width1, height1);
325
326 m_noItems = 0;
327 }
328
329 void wxListBox::SetSelection(int N, bool select)
330 {
331 m_inSetValue = TRUE;
332 if (select)
333 {
334 #if 0
335 if (m_windowStyle & wxLB_MULTIPLE)
336 {
337 int *selections = NULL;
338 int n = GetSelections (&selections);
339
340 // This hack is supposed to work, to make it possible
341 // to select more than one item, but it DOESN'T under Motif 1.1.
342
343 XtVaSetValues ((Widget) m_mainWidget,
344 XmNselectionPolicy, XmMULTIPLE_SELECT,
345 NULL);
346
347 int i;
348 for (i = 0; i < n; i++)
349 XmListSelectPos ((Widget) m_mainWidget,
350 selections[i] + 1, FALSE);
351
352 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
353
354 XtVaSetValues ((Widget) m_mainWidget,
355 XmNselectionPolicy, XmEXTENDED_SELECT,
356 NULL);
357 }
358 else
359 #endif // 0
360 XmListSelectPos ((Widget) m_mainWidget, N + 1, FALSE);
361
362 }
363 else
364 XmListDeselectPos ((Widget) m_mainWidget, N + 1);
365
366 m_inSetValue = FALSE;
367 }
368
369 bool wxListBox::IsSelected(int N) const
370 {
371 // In Motif, no simple way to determine if the item is selected.
372 wxArrayInt theSelections;
373 int count = GetSelections (theSelections);
374 if (count == 0)
375 return FALSE;
376 else
377 {
378 int j;
379 for (j = 0; j < count; j++)
380 if (theSelections[j] == N)
381 return TRUE;
382 }
383 return FALSE;
384 }
385
386 void wxListBox::DoSetItemClientObject(int n, wxClientData* clientData)
387 {
388 DoSetItemClientData(n, (void*) clientData);
389 }
390
391 wxClientData* wxListBox::DoGetItemClientObject(int n) const
392 {
393 return (wxClientData*) DoGetItemClientData(n);
394 }
395
396 void *wxListBox::DoGetItemClientData(int N) const
397 {
398 wxNode *node = m_clientDataList.Find ((long) N);
399 if (node)
400 return (void *) node->Data ();
401 else
402 return NULL;
403 }
404
405 void wxListBox::DoSetItemClientData(int N, void *Client_data)
406 {
407 wxNode *node = m_clientDataList.Find ((long) N);
408 if (node)
409 node->SetData ((wxObject *)Client_data);
410 else
411 node = m_clientDataList.Append((long) N, (wxObject*) Client_data);
412 }
413
414 // Return number of selections and an array of selected integers
415 int 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
444 int wxListBox::GetSelection() const
445 {
446 Widget listBox = (Widget) m_mainWidget;
447 int *posList = NULL;
448 int posCnt = 0;
449 bool flag = XmListGetSelectedPos (listBox, &posList, &posCnt);
450 if (flag)
451 {
452 int id = -1;
453 if (posCnt > 0)
454 id = posList[0] - 1;
455 XtFree ((char *) posList);
456 return id;
457 }
458 else
459 return -1;
460 }
461
462 // Find string for position
463 wxString wxListBox::GetString(int N) const
464 {
465 Widget listBox = (Widget) m_mainWidget;
466 XmString *strlist;
467 int n;
468 XtVaGetValues (listBox, XmNitemCount, &n, XmNitems, &strlist, NULL);
469 if (N <= n && N >= 0)
470 {
471 char *txt;
472 if (XmStringGetLtoR (strlist[N], XmSTRING_DEFAULT_CHARSET, &txt))
473 {
474 wxString str(txt);
475 XtFree (txt);
476 return str;
477 }
478 else
479 return wxEmptyString;
480 }
481 else
482 return wxEmptyString;
483 }
484
485 void wxListBox::DoSetSize(int x, int y, int width, int height, int sizeFlags)
486 {
487 wxWindow::DoSetSize(x, y, width, height, sizeFlags);
488
489 // Check resulting size is correct
490 int tempW, tempH;
491 GetSize (&tempW, &tempH);
492
493 /*
494 if (tempW != width || tempH != height)
495 {
496 cout << "wxListBox::SetSize sizes not set correctly.");
497 }
498 */
499 }
500
501 void wxListBox::DoInsertItems(const wxArrayString& items, int pos)
502 {
503 int width1, height1;
504 int width2, height2;
505
506 Widget listBox = (Widget) m_mainWidget;
507
508 GetSize(&width1, &height1);
509
510 bool managed = XtIsManaged(listBox);
511
512 if (managed)
513 XtUnmanageChild(listBox);
514
515 XmString *text = new XmString[items.GetCount()];
516 size_t i;
517 // Steve Hammes: Motif 1.1 compatibility
518 // #if XmVersion > 1100
519 // Corrected by Sergey Krasnov from Steve Hammes' code
520 #if XmVersion > 1001
521 for (i = 0; i < items.GetCount(); i++)
522 text[i] = XmStringCreateSimple((char*)items[i].c_str());
523 XmListAddItemsUnselected(listBox, text, items.GetCount(), pos+1);
524 #else
525 for (i = 0; i < items.GetCount(); i++)
526 {
527 text[i] = XmStringCreateSimple((char*)items[i].c_str());
528 // Another Sergey correction
529 XmListAddItemUnselected(listBox, text[i], pos+i+1);
530 }
531 #endif
532 for (i = 0; i < items.GetCount(); i++)
533 XmStringFree(text[i]);
534 delete[] text;
535
536 // It seems that if the list is cleared, we must re-ask for
537 // selection policy!!
538 Arg args[3];
539 XtSetArg(args[0], XmNlistSizePolicy, XmCONSTANT);
540 if (m_windowStyle & wxLB_MULTIPLE)
541 XtSetArg(args[1], XmNselectionPolicy, XmMULTIPLE_SELECT);
542 else if (m_windowStyle & wxLB_EXTENDED)
543 XtSetArg(args[1], XmNselectionPolicy, XmEXTENDED_SELECT);
544 else XtSetArg(args[1], XmNselectionPolicy, XmBROWSE_SELECT);
545 XtSetValues(listBox,args,2) ;
546
547 if (managed)
548 XtManageChild(listBox);
549
550 GetSize(&width2, &height2);
551 // Correct for randomly resized listbox - bad boy, Motif!
552 if (width1 != width2 /*|| height1 != height2*/)
553 SetSize(-1, -1, width1, height1);
554
555 m_noItems += items.GetCount();
556 }
557
558 void wxListBox::SetString(int N, const wxString& s)
559 {
560 int width1, height1;
561 int width2, height2;
562
563 Widget listBox = (Widget) m_mainWidget;
564 GetSize (&width1, &height1);
565
566 XmString text = XmStringCreateSimple ((char*) (const char*) s);
567
568 // delete the item and add it again.
569 // FIXME isn't there a way to change it in place?
570 XmListDeletePos (listBox, N+1);
571 XmListAddItem (listBox, text, N+1);
572
573 XmStringFree(text);
574
575 GetSize (&width2, &height2);
576 // Correct for randomly resized listbox - bad boy, Motif!
577 if (width1 != width2 || height1 != height2)
578 SetSize (-1, -1, width1, height1);
579 }
580
581 void wxListBox::Command (wxCommandEvent & event)
582 {
583 if (event.m_extraLong)
584 SetSelection (event.m_commandInt);
585 else
586 {
587 Deselect (event.m_commandInt);
588 return;
589 }
590 ProcessCommand (event);
591 }
592
593 void wxListBoxCallback (Widget WXUNUSED(w), XtPointer clientData,
594 XmListCallbackStruct * cbs)
595 {
596 wxListBox *item = (wxListBox *) clientData;
597
598 if (item->InSetValue())
599 return;
600
601 wxEventType evtType;
602
603 if( cbs->reason == XmCR_DEFAULT_ACTION )
604 evtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED;
605 else
606 evtType = wxEVT_COMMAND_LISTBOX_SELECTED;
607
608 int n = cbs->item_position - 1;
609 wxCommandEvent event (evtType, item->GetId());
610 if ( item->HasClientObjectData() )
611 event.SetClientObject( item->GetClientObject(n) );
612 else if ( item->HasClientUntypedData() )
613 event.SetClientData( item->GetClientData(n) );
614 event.m_commandInt = n;
615 event.m_extraLong = TRUE;
616 event.SetEventObject(item);
617 event.SetString( item->GetString( n ) );
618
619 int x = -1;
620 if( NULL != cbs->event && cbs->event->type == ButtonRelease )
621 {
622 XButtonEvent* evt = (XButtonEvent*)cbs->event;
623
624 x = evt->x;
625 }
626
627 switch (cbs->reason)
628 {
629 case XmCR_MULTIPLE_SELECT:
630 case XmCR_BROWSE_SELECT:
631 #if wxUSE_CHECKLISTBOX
632 item->DoToggleItem( n, x );
633 #endif
634 case XmCR_DEFAULT_ACTION:
635 item->GetEventHandler()->ProcessEvent(event);
636 break;
637 case XmCR_EXTENDED_SELECT:
638 switch (cbs->selection_type)
639 {
640 case XmINITIAL:
641 case XmADDITION:
642 case XmMODIFICATION:
643 item->DoToggleItem( n, x );
644 item->GetEventHandler()->ProcessEvent(event);
645 break;
646 }
647 break;
648 }
649 }
650
651 WXWidget wxListBox::GetTopWidget() const
652 {
653 return (WXWidget) XtParent( (Widget) m_mainWidget );
654 }
655
656 void wxListBox::ChangeFont(bool keepOriginalSize)
657 {
658 wxWindow::ChangeFont(keepOriginalSize);
659 }
660
661 void wxListBox::ChangeBackgroundColour()
662 {
663 wxWindow::ChangeBackgroundColour();
664
665 Widget parent = XtParent ((Widget) m_mainWidget);
666 Widget hsb, vsb;
667
668 XtVaGetValues (parent,
669 XmNhorizontalScrollBar, &hsb,
670 XmNverticalScrollBar, &vsb,
671 NULL);
672
673 /* TODO: should scrollbars be affected? Should probably have separate
674 * function to change them (by default, taken from wxSystemSettings)
675 */
676 wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
677 DoChangeBackgroundColour((WXWidget) hsb, backgroundColour, TRUE);
678 DoChangeBackgroundColour((WXWidget) vsb, backgroundColour, TRUE);
679
680 XtVaSetValues (hsb,
681 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(hsb)),
682 NULL);
683 XtVaSetValues (vsb,
684 XmNtroughColor, backgroundColour.AllocColour(XtDisplay(vsb)),
685 NULL);
686
687 DoChangeBackgroundColour((WXWidget) parent, m_backgroundColour, TRUE);
688 }
689
690 void wxListBox::ChangeForegroundColour()
691 {
692 wxWindow::ChangeForegroundColour();
693
694 Widget parent = XtParent ((Widget) m_mainWidget);
695 Widget hsb, vsb;
696
697 XtVaGetValues(parent,
698 XmNhorizontalScrollBar, &hsb,
699 XmNverticalScrollBar, &vsb,
700 NULL);
701
702 /* TODO: should scrollbars be affected? Should probably have separate
703 function to change them (by default, taken from wxSystemSettings)
704
705 DoChangeForegroundColour((WXWidget) hsb, m_foregroundColour);
706 DoChangeForegroundColour((WXWidget) vsb, m_foregroundColour);
707 DoChangeForegroundColour((WXWidget) parent, m_foregroundColour);
708 */
709 }
710
711 int wxListBox::GetCount() const
712 {
713 return m_noItems;
714 }