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