Listbox control code
[wxWidgets.git] / src / os2 / listbox.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: listbox.cpp
3 // Purpose: wxListBox
4 // Author: David Webster
5 // Modified by:
6 // Created: 10/09/99
7 // RCS-ID: $Id$
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #include "wx/window.h"
16 #include "wx/os2/private.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/listbox.h"
20 #include "wx/settings.h"
21 #include "wx/brush.h"
22 #include "wx/font.h"
23 #include "wx/dc.h"
24 #include "wx/utils.h"
25 #endif
26
27 #define INCL_M
28 #include <os2.h>
29
30 #include "wx/dynarray.h"
31 #include "wx/log.h"
32
33 #if wxUSE_LISTBOX
34
35 #if wxUSE_OWNER_DRAWN
36 #include "wx/ownerdrw.h"
37 #endif
38
39 IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl)
40
41 // ============================================================================
42 // list box item declaration and implementation
43 // ============================================================================
44
45 #if wxUSE_OWNER_DRAWN
46
47 class wxListBoxItem : public wxOwnerDrawn
48 {
49 public:
50 wxListBoxItem(const wxString& rsStr = "");
51 };
52
53 wxListBoxItem::wxListBoxItem(
54 const wxString& rsStr
55 )
56 : wxOwnerDrawn( rsStr
57 ,FALSE
58 )
59 {
60 //
61 // No bitmaps/checkmarks
62 //
63 SetMarginWidth(0);
64 } // end of wxListBoxItem::wxListBoxItem
65
66 wxOwnerDrawn* wxListBox::CreateItem(
67 size_t n
68 )
69 {
70 return new wxListBoxItem();
71 } // end of wxListBox::CreateItem
72
73 #endif //USE_OWNER_DRAWN
74
75 // ============================================================================
76 // list box control implementation
77 // ============================================================================
78
79 // Listbox item
80 wxListBox::wxListBox()
81 {
82 m_nNumItems = 0;
83 m_nSelected = 0;
84 } // end of wxListBox::wxListBox
85
86 bool wxListBox::Create(
87 wxWindow* pParent
88 , wxWindowID vId
89 , const wxPoint& rPos
90 , const wxSize& rSize
91 , int n
92 , const wxString asChoices[]
93 , long lStyle
94 #if wxUSE_VALIDATORS
95 , const wxValidator& rValidator
96 #endif
97 , const wxString& rsName
98 )
99 {
100 m_nNumItems = 0;
101 m_hWnd = 0;
102 m_nSelected = 0;
103
104 SetName(rsName);
105 #if wxUSE_VALIDATORS
106 SetValidator(rValidator);
107 #endif
108
109 if (pParent)
110 pParent->AddChild(this);
111
112 wxSystemSettings vSettings;
113
114 SetBackgroundColour(vSettings.GetSystemColour(wxSYS_COLOUR_WINDOW));
115 SetForegroundColour(pParent->GetForegroundColour());
116
117 m_windowId = (vId == -1) ? (int)NewControlId() : vId;
118
119 int nX = rPos.x;
120 int nY = rPos.y;
121 int nWidth = rSize.x;
122 int nHeight = rSize.y;
123
124 m_windowStyle = lStyle;
125
126 lStyle = WS_VISIBLE;
127
128 if (m_windowStyle & wxCLIP_SIBLINGS )
129 lStyle |= WS_CLIPSIBLINGS;
130 if (m_windowStyle & wxLB_MULTIPLE)
131 lStyle |= LS_MULTIPLESEL;
132 else if (m_windowStyle & wxLB_EXTENDED)
133 lStyle |= LS_EXTENDEDSEL;
134 if (m_windowStyle & wxLB_HSCROLL)
135 lStyle |= LS_HORZSCROLL;
136 if (m_windowStyle & wxLB_OWNERDRAW)
137 lStyle |= LS_OWNERDRAW;
138
139 //
140 // Without this style, you get unexpected heights, so e.g. constraint layout
141 // doesn't work properly
142 //
143 lStyle |= LS_NOADJUSTPOS;
144
145 m_hWnd = (WXHWND)::WinCreateWindow( GetWinHwnd(pParent) // Parent
146 ,WC_LISTBOX // Default Listbox class
147 ,"LISTBOX" // Control's name
148 ,lStyle // Initial Style
149 ,0, 0, 0, 0 // Position and size
150 ,GetWinHwnd(pParent) // Owner
151 ,HWND_TOP // Z-Order
152 ,(HMENU)m_windowId // Id
153 ,NULL // Control Data
154 ,NULL // Presentation Parameters
155 );
156 if (m_hWnd == 0)
157 {
158 return FALSE;
159 }
160
161 //
162 // Subclass again for purposes of dialog editing mode
163 //
164 SubclassWin(m_hWnd);
165
166 LONG lUi;
167
168 for (lUi = 0; lUi < (LONG)n; lUi++)
169 {
170 Append(asChoices[lUi]);
171 }
172 SetFont(pParent->GetFont());
173 SetSize( nX
174 ,nY
175 ,nWidth
176 ,nHeight
177 );
178 return TRUE;
179 } // end of wxListBox::Create
180
181 wxListBox::~wxListBox()
182 {
183 #if wxUSE_OWNER_DRAWN
184 size_t lUiCount = m_aItems.Count();
185
186 while (lUiCount-- != 0)
187 {
188 delete m_aItems[lUiCount];
189 }
190 #endif // wxUSE_OWNER_DRAWN
191 } // end of wxListBox::~wxListBox
192
193 void wxListBox::SetupColours()
194 {
195 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_WINDOW));
196 SetForegroundColour(GetParent()->GetForegroundColour());
197 } // end of wxListBox::SetupColours
198
199 // ----------------------------------------------------------------------------
200 // implementation of wxListBoxBase methods
201 // ----------------------------------------------------------------------------
202
203 void wxListBox::DoSetFirstItem(
204 int N
205 )
206 {
207 wxCHECK_RET( N >= 0 && N < m_nNumItems,
208 wxT("invalid index in wxListBox::SetFirstItem") );
209
210 ::WinSendMsg(GetHwnd(), LM_SETTOPINDEX, MPFROMLONG(N), (MPARAM)0);
211 } // end of wxListBox::DoSetFirstItem
212
213 void wxListBox::Delete(
214 int N
215 )
216 {
217 wxCHECK_RET( N >= 0 && N < m_nNumItems,
218 wxT("invalid index in wxListBox::Delete") );
219
220 #if wxUSE_OWNER_DRAWN
221 delete m_aItems[N];
222 m_aItems.RemoveAt(N);
223 #else // !wxUSE_OWNER_DRAWN
224 if (HasClientObjectData())
225 {
226 delete GetClientObject(N);
227 }
228 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
229
230 ::WinSendMsg(GetHwnd(), LM_DELETEITEM, (MPARAM)N, (MPARAM)0);
231 m_nNumItems--;
232 } // end of wxListBox::DoSetFirstItem
233
234 int wxListBox::DoAppend(
235 const wxString& rsItem
236 )
237 {
238 int nIndex = 0;
239 SHORT nIndexType = 0;
240
241 if (m_windowStyle & wxLB_SORT)
242 nIndexType = LIT_SORTASCENDING;
243 else
244 nIndexType = LIT_END;
245 nIndex = (int)::WinSendMsg(GetHwnd(), LM_INSERTITEM, (MPARAM)nIndexType, (MPARAM)rsItem.c_str());
246 m_nNumItems++;
247
248 #if wxUSE_OWNER_DRAWN
249 if (m_windowStyle & wxLB_OWNERDRAW)
250 {
251 wxOwnerDrawn* pNewItem = CreateItem(nIndex); // dummy argument
252
253 pNewItem->SetName(rsItem);
254 m_aItems.Add(pNewItem);
255 ::WinSendMsg(GetHwnd(), LM_SETITEMHANDLE, (MPARAM)((SHORT)nIndex), MPFROMP(pNewItem));
256 pNewItem->SetFont(GetFont());
257 }
258 #endif
259 return nIndex;
260 } // end of wxListBox::DoAppend
261
262 void wxListBox::DoSetItems(
263 const wxArrayString& raChoices
264 , void** ppClientData
265 )
266 {
267 BOOL bHideAndShow = IsShown();
268 int nCount = 0;
269 int i;
270 SHORT nIndexType = 0;
271
272 if (bHideAndShow)
273 {
274 ::WinShowWindow(GetHwnd(), FALSE);
275 }
276 ::WinSendMsg(GetHwnd(), LM_DELETEALL, (MPARAM)0, (MPARAM)0);
277 m_nNumItems = raChoices.GetCount();
278 for (i = 0; i < m_nNumItems; i++)
279 {
280
281 if (m_windowStyle & wxLB_SORT)
282 nIndexType = LIT_SORTASCENDING;
283 else
284 nIndexType = LIT_END;
285 ::WinSendMsg(GetHwnd(), LM_INSERTITEM, (MPARAM)nIndexType, (MPARAM)raChoices[i].c_str());
286
287 if (ppClientData)
288 {
289 #if wxUSE_OWNER_DRAWN
290 wxASSERT_MSG(ppClientData[i] == NULL,
291 wxT("Can't use client data with owner-drawn listboxes"));
292 #else // !wxUSE_OWNER_DRAWN
293 ::WinSendMsg(WinUtil_GetHwnd(), LM_SETITEMHANDLE, MPFROMLONG(lCount), MPFROMP(ppClientData[i]));
294 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
295 }
296 }
297
298 #if wxUSE_OWNER_DRAWN
299 if ( m_windowStyle & wxLB_OWNERDRAW )
300 {
301 //
302 // First delete old items
303 //
304 size_t lUi = m_aItems.Count();
305
306 while (lUi-- != 0)
307 {
308 delete m_aItems[lUi];
309 }
310 m_aItems.Empty();
311
312 //
313 // Then create new ones
314 //
315 for (lUi = 0; lUi < (size_t)m_nNumItems; lUi++)
316 {
317 wxOwnerDrawn* pNewItem = CreateItem(lUi);
318
319 pNewItem->SetName(raChoices[lUi]);
320 m_aItems.Add(pNewItem);
321 ::WinSendMsg(GetHwnd(), LM_SETITEMHANDLE, MPFROMLONG(lUi), MPFROMP(pNewItem));
322 }
323 }
324 #endif // wxUSE_OWNER_DRAWN
325 ::WinShowWindow(GetHwnd(), TRUE);
326 } // end of wxListBox::DoSetItems
327
328 int wxListBox::FindString(
329 const wxString& rsString
330 ) const
331 {
332 int nPos;
333 LONG lTextLength;
334 PSZ zStr;
335
336
337 for (nPos = 0; nPos < m_nNumItems; nPos++)
338 {
339 lTextLength = LONGFROMMR(::WinSendMsg(GetHwnd(), LM_QUERYITEMTEXTLENGTH, (MPARAM)nPos, (MPARAM)0));
340 zStr = new char[lTextLength + 1];
341 ::WinSendMsg(GetHwnd(), LM_QUERYITEMTEXT, MPFROM2SHORT(nPos, (SHORT)lTextLength), (MPARAM)zStr);
342 if (rsString == (char*)zStr)
343 {
344 delete [] zStr;
345 break;
346 }
347 delete [] zStr;
348 }
349 return nPos;
350 } // end of wxListBox::FindString
351
352 void wxListBox::Clear()
353 {
354 #if wxUSE_OWNER_DRAWN
355 size_t lUiCount = m_aItems.Count();
356
357 while (lUiCount-- != 0)
358 {
359 delete m_aItems[lUiCount];
360 }
361
362 m_aItems.Clear();
363 #else // !wxUSE_OWNER_DRAWN
364 if (HasClientObjectData())
365 {
366 for (size_t n = 0; n < (size_t)m_lNumItems; n++)
367 {
368 delete GetClientObject(n);
369 }
370 }
371 #endif // wxUSE_OWNER_DRAWN/!wxUSE_OWNER_DRAWN
372 ::WinSendMsg(GetHwnd(), LM_DELETEALL, (MPARAM)0, (MPARAM)0);
373
374 m_nNumItems = 0;
375 } // end of wxListBox::Clear
376
377 void wxListBox::SetSelection(
378 int N
379 , bool bSelect
380 )
381 {
382 wxCHECK_RET( N >= 0 && N < m_nNumItems,
383 wxT("invalid index in wxListBox::SetSelection") );
384 ::WinSendMsg( GetHwnd()
385 ,LM_SELECTITEM
386 ,MPFROMLONG(N)
387 ,(MPARAM)bSelect
388 );
389 } // end of wxListBox::SetSelection
390
391 bool wxListBox::IsSelected(
392 int N
393 ) const
394 {
395 wxCHECK_MSG( N >= 0 && N < m_nNumItems, FALSE,
396 wxT("invalid index in wxListBox::Selected") );
397
398 LONG lItem;
399
400 lItem = LONGFROMMR(::WinSendMsg(GetHwnd(), LM_QUERYSELECTION, (MPARAM)N, (MPARAM)0));
401 return (lItem != LIT_NONE);
402 } // end of wxListBox::IsSelected
403
404 wxClientData* wxListBox::DoGetItemClientObject(
405 int n
406 ) const
407 {
408 return (wxClientData *)DoGetItemClientData(n);
409 }
410
411 void* wxListBox::DoGetItemClientData(
412 int n
413 ) const
414 {
415 wxCHECK_MSG( n >= 0 && n < m_nNumItems, NULL,
416 wxT("invalid index in wxListBox::GetClientData") );
417
418 return((void *)::WinSendMsg(GetHwnd(), LM_QUERYITEMHANDLE, MPFROMLONG(n), (MPARAM)0));
419 } // end of wxListBox::DoGetItemClientData
420
421 void wxListBox::DoSetItemClientObject(
422 int n
423 , wxClientData* pClientData
424 )
425 {
426 DoSetItemClientData( n
427 ,pClientData
428 );
429 } // end of wxListBox::DoSetItemClientObject
430
431 void wxListBox::DoSetItemClientData(
432 int n
433 , void* pClientData
434 )
435 {
436 wxCHECK_RET( n >= 0 && n < m_nNumItems,
437 wxT("invalid index in wxListBox::SetClientData") );
438
439 #if wxUSE_OWNER_DRAWN
440 if ( m_windowStyle & wxLB_OWNERDRAW )
441 {
442 //
443 // Client data must be pointer to wxOwnerDrawn, otherwise we would crash
444 // in OnMeasure/OnDraw.
445 //
446 wxFAIL_MSG(wxT("Can't use client data with owner-drawn listboxes"));
447 }
448 #endif // wxUSE_OWNER_DRAWN
449
450 ::WinSendMsg(GetHwnd(), LM_SETITEMHANDLE, MPFROMLONG(n), MPFROMP(pClientData));
451 } // end of wxListBox::DoSetItemClientData
452
453 bool wxListBox::HasMultipleSelection() const
454 {
455 return (m_windowStyle & wxLB_MULTIPLE) || (m_windowStyle & wxLB_EXTENDED);
456 } // end of wxListBox::HasMultipleSelection
457
458 int wxListBox::GetSelections(
459 wxArrayInt& raSelections
460 ) const
461 {
462 int nCount = 0;
463 LONG lItem;
464
465
466 raSelections.Empty();
467 if (HasMultipleSelection())
468 {
469 lItem = LONGFROMMR(::WinSendMsg( GetHwnd()
470 ,LM_QUERYSELECTION
471 ,(MPARAM)LIT_FIRST
472 ,(MPARAM)0
473 )
474 );
475 if (lItem != LIT_NONE)
476 {
477 nCount++;
478 while ((lItem = LONGFROMMR(::WinSendMsg( GetHwnd()
479 ,LM_QUERYSELECTION
480 ,(MPARAM)lItem
481 ,(MPARAM)0
482 )
483 )) != LIT_NONE)
484 {
485 nCount++;
486 }
487 raSelections.Alloc(nCount);
488 lItem = LONGFROMMR(::WinSendMsg( GetHwnd()
489 ,LM_QUERYSELECTION
490 ,(MPARAM)LIT_FIRST
491 ,(MPARAM)0
492 )
493 );
494
495 raSelections.Add((int)lItem);
496 while ((lItem = LONGFROMMR(::WinSendMsg( GetHwnd()
497 ,LM_QUERYSELECTION
498 ,(MPARAM)lItem
499 ,(MPARAM)0
500 )
501 )) != LIT_NONE)
502 {
503 raSelections.Add((int)lItem);
504 }
505 return nCount;
506 }
507 return 0;
508 }
509 else // single-selection listbox
510 {
511 lItem = LONGFROMMR(::WinSendMsg( GetHwnd()
512 ,LM_QUERYSELECTION
513 ,(MPARAM)LIT_FIRST
514 ,(MPARAM)0
515 )
516 );
517 raSelections.Add((int)lItem);
518 return 1;
519 }
520 return 0;
521 } // end of wxListBox::GetSelections
522
523 int wxListBox::GetSelection() const
524 {
525 wxCHECK_MSG( !HasMultipleSelection(),
526 -1,
527 wxT("GetSelection() can't be used with multiple-selection "
528 "listboxes, use GetSelections() instead.") );
529
530 return(LONGFROMMR(::WinSendMsg( GetHwnd()
531 ,LM_QUERYSELECTION
532 ,(MPARAM)LIT_FIRST
533 ,(MPARAM)0
534 )
535 ));
536 } // end of wxListBox::GetSelection
537
538 wxString wxListBox::GetString(
539 int N
540 ) const
541 {
542 LONG lLen = 0;
543 char* zBuf;
544 wxString sResult;
545
546 wxCHECK_MSG( N >= 0 && N < m_nNumItems, "",
547 wxT("invalid index in wxListBox::GetClientData") );
548
549 lLen = LONGFROMMR(::WinSendMsg(GetHwnd(), LM_QUERYITEMTEXTLENGTH, (MPARAM)N, (MPARAM)0));
550 zBuf = new char[lLen + 1];
551 ::WinSendMsg(GetHwnd(), LM_QUERYITEMTEXT, MPFROM2SHORT((SHORT)N, (SHORT)lLen), (MPARAM)zBuf);
552 zBuf[lLen] = '\0';
553 sResult = zBuf;
554 delete [] zBuf;
555 return sResult;
556 } // end of wxListBox::GetString
557
558 void wxListBox::DoInsertItems(
559 const wxArrayString& asItems
560 , int nPos
561 )
562 {
563 wxCHECK_RET( nPos >= 0 && nPos <= m_nNumItems,
564 wxT("invalid index in wxListBox::InsertItems") );
565
566 int nItems = asItems.GetCount();
567
568 for (int i = 0; i < nItems; i++)
569 ::WinSendMsg(GetHwnd(), LM_INSERTITEM, MPFROMLONG((LONG)(i + nPos)), (MPARAM)asItems[i].c_str());
570 m_nNumItems += nItems;
571 } // end of wxListBox::DoInsertItems
572
573 void wxListBox::SetString(
574 int N
575 , const wxString& rsString
576 )
577 {
578 wxCHECK_RET( N >= 0 && N < m_nNumItems,
579 wxT("invalid index in wxListBox::SetString") );
580
581 //
582 // Remember the state of the item
583 //
584 bool bWasSelected = IsSelected(N);
585 void* pOldData = NULL;
586 wxClientData* pOldObjData = NULL;
587
588 if (m_clientDataItemsType == wxClientData_Void)
589 pOldData = GetClientData(N);
590 else if (m_clientDataItemsType == wxClientData_Object)
591 pOldObjData = GetClientObject(N);
592
593 //
594 // Delete and recreate it
595 //
596 ::WinSendMsg( GetHwnd()
597 ,LM_DELETEITEM
598 ,(MPARAM)N
599 ,(MPARAM)0
600 );
601
602 int nNewN = N;
603
604 if (N == m_nNumItems - 1)
605 nNewN = -1;
606
607 ::WinSendMsg( GetHwnd()
608 ,LM_INSERTITEM
609 ,(MPARAM)nNewN
610 ,(MPARAM)rsString.c_str()
611 );
612
613 //
614 // Restore the client data
615 //
616 if (pOldData)
617 SetClientData( N
618 ,pOldData
619 );
620 else if (pOldObjData)
621 SetClientObject( N
622 ,pOldObjData
623 );
624
625 //
626 // We may have lost the selection
627 //
628 if (bWasSelected)
629 Select(N);
630
631 #if wxUSE_OWNER_DRAWN
632 if (m_windowStyle & wxLB_OWNERDRAW)
633 //
634 // Update item's text
635 //
636 m_aItems[N]->SetName(rsString);
637 #endif //USE_OWNER_DRAWN
638 } // end of wxListBox::SetString
639
640 int wxListBox::GetCount() const
641 {
642 return m_nNumItems;
643 }
644
645 // ----------------------------------------------------------------------------
646 // helpers
647 // ----------------------------------------------------------------------------
648
649 wxSize wxListBox::DoGetBestSize() const
650 {
651 //
652 // Find the widest string
653 //
654 int nLine;
655 int nListbox = 0;
656 int nCx;
657 int nCy;
658
659 for (int i = 0; i < m_nNumItems; i++)
660 {
661 wxString vStr(GetString(i));
662
663 GetTextExtent( vStr
664 ,&nLine
665 ,NULL
666 );
667 if (nLine > nListbox)
668 nListbox = nLine;
669 }
670
671 //
672 // Give it some reasonable default value if there are no strings in the
673 // list.
674 //
675 if (nListbox == 0)
676 nListbox = 100;
677
678 //
679 // The listbox should be slightly larger than the widest string
680 //
681 wxGetCharSize( GetHWND()
682 ,&nCx
683 ,&nCy
684 ,(wxFont*)&GetFont()
685 );
686 nListbox += 3 * nCx;
687
688 int hListbox = EDIT_HEIGHT_FROM_CHAR_HEIGHT(nCy) * (wxMax(m_nNumItems, 7));
689
690 return wxSize( nListbox
691 ,hListbox
692 );
693 } // end of wxListBox::DoGetBestSize
694
695 // ----------------------------------------------------------------------------
696 // callbacks
697 // ----------------------------------------------------------------------------
698
699 bool wxListBox::OS2Command(
700 WXUINT uParam
701 , WXWORD WXUNUSED(wId))
702 {
703 wxEventType eEvtType;
704
705 if (uParam == LN_SELECT)
706 {
707 eEvtType = wxEVT_COMMAND_LISTBOX_SELECTED;
708 }
709 if (uParam == LN_ENTER)
710 {
711 eEvtType = wxEVT_COMMAND_LISTBOX_DOUBLECLICKED;
712 }
713 else
714 {
715 //
716 // Some event we're not interested in
717 //
718 return FALSE;
719 }
720 wxCommandEvent vEvent( eEvtType
721 ,m_windowId
722 );
723
724 vEvent.SetEventObject(this);
725
726 wxArrayInt aSelections;
727 int n;
728 int nCount = GetSelections(aSelections);
729
730 if (nCount > 0)
731 {
732 n = aSelections[0];
733 if (HasClientObjectData())
734 vEvent.SetClientObject(GetClientObject(n));
735 else if ( HasClientUntypedData() )
736 vEvent.SetClientData(GetClientData(n));
737 vEvent.SetString(GetString(n));
738 }
739 else
740 {
741 n = -1;
742 }
743 vEvent.m_commandInt = n;
744 return GetEventHandler()->ProcessEvent(vEvent);
745 } // end of wxListBox::OS2Command
746
747 // ----------------------------------------------------------------------------
748 // wxCheckListBox support
749 // ----------------------------------------------------------------------------
750
751 #if wxUSE_OWNER_DRAWN
752
753 //
754 // Drawing
755 // -------
756 //
757 #define OWNER_DRAWN_LISTBOX_EXTRA_SPACE (1)
758
759 bool wxListBox::OS2OnMeasure(WXMEASUREITEMSTRUCT *item)
760 {
761 //
762 // TODO: Get to this eventually
763 //
764 return TRUE;
765 }
766
767 bool wxListBox::OS2OnDraw(WXDRAWITEMSTRUCT *item)
768 {
769 //
770 // TODO: Get to this eventually
771 //
772 return FALSE;
773 }
774 #endif // ndef for wxUSE_OWNER_DRAWN
775
776 #endif // ndef for wxUSE_LISTBOX
777