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