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