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