1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/odcombo.cpp
3 // Purpose: wxOwnerDrawnComboBox, wxVListBoxComboPopup
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: Apr-30-2006
7 // RCS-ID: $Id: odcombo.cpp 64259 2010-05-09 10:48:37Z JMS $
8 // Copyright: (c) 2005 Jaakko Salli
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_ODCOMBOBOX
27
28 #include "wx/odcombo.h"
29
30 #ifndef WX_PRECOMP
31 #include "wx/log.h"
32 #include "wx/combobox.h"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35 #include "wx/dialog.h"
36 #endif
37
38 #include "wx/combo.h"
39
40 // ============================================================================
41 // implementation
42 // ============================================================================
43
44 // time in milliseconds before partial completion buffer drops
45 #define wxODCB_PARTIAL_COMPLETION_TIME 1000
46
47 // ----------------------------------------------------------------------------
48 // wxVListBoxComboPopup is a wxVListBox customized to act as a popup control
49 //
50 // ----------------------------------------------------------------------------
51
52
BEGIN_EVENT_TABLE(wxVListBoxComboPopup,wxVListBox)53 BEGIN_EVENT_TABLE(wxVListBoxComboPopup, wxVListBox)
54 EVT_MOTION(wxVListBoxComboPopup::OnMouseMove)
55 EVT_KEY_DOWN(wxVListBoxComboPopup::OnKey)
56 EVT_LEFT_UP(wxVListBoxComboPopup::OnLeftClick)
57 END_EVENT_TABLE()
58
59
60 void wxVListBoxComboPopup::Init()
61 {
62 m_widestWidth = 0;
63 m_widestItem = -1;
64 m_widthsDirty = false;
65 m_findWidest = false;
66 m_itemHeight = 0;
67 m_value = -1;
68 m_itemHover = -1;
69 m_clientDataItemsType = wxClientData_None;
70 m_partialCompletionString = wxEmptyString;
71 }
72
Create(wxWindow * parent)73 bool wxVListBoxComboPopup::Create(wxWindow* parent)
74 {
75 if ( !wxVListBox::Create(parent,
76 wxID_ANY,
77 wxDefaultPosition,
78 wxDefaultSize,
79 wxBORDER_SIMPLE | wxLB_INT_HEIGHT | wxWANTS_CHARS) )
80 return false;
81
82 m_useFont = m_combo->GetFont();
83
84 wxVListBox::SetItemCount(m_strings.GetCount());
85
86 // TODO: Move this to SetFont
87 m_itemHeight = GetCharHeight() + 0;
88
89 return true;
90 }
91
~wxVListBoxComboPopup()92 wxVListBoxComboPopup::~wxVListBoxComboPopup()
93 {
94 Clear();
95 }
96
97 #ifdef __WXMSW__
98
SetFocus()99 void wxVListBoxComboPopup::SetFocus()
100 {
101 // Suppress SetFocus() warning by simply not calling it. This combo popup
102 // has already been designed with the assumption that SetFocus() may not
103 // do anything useful, so it really doesn't need to be called.
104 }
105
106 #endif // __WXMSW__
107
LazyCreate()108 bool wxVListBoxComboPopup::LazyCreate()
109 {
110 // NB: There is a bug with wxVListBox that can be avoided by creating
111 // it later (bug causes empty space to be shown if initial selection
112 // is at the end of a list longer than the control can show at once).
113 return true;
114 }
115
116 // paint the control itself
PaintComboControl(wxDC & dc,const wxRect & rect)117 void wxVListBoxComboPopup::PaintComboControl( wxDC& dc, const wxRect& rect )
118 {
119 if ( !(m_combo->GetWindowStyle() & wxODCB_STD_CONTROL_PAINT) )
120 {
121 int flags = wxODCB_PAINTING_CONTROL;
122
123 if ( m_combo->ShouldDrawFocus() )
124 flags |= wxODCB_PAINTING_SELECTED;
125
126 OnDrawBg(dc, rect, m_value, flags);
127
128 if ( m_value >= 0 )
129 {
130 OnDrawItem(dc,rect,m_value,flags);
131 return;
132 }
133 }
134
135 wxComboPopup::PaintComboControl(dc,rect);
136 }
137
OnDrawItem(wxDC & dc,const wxRect & rect,size_t n) const138 void wxVListBoxComboPopup::OnDrawItem(wxDC& dc, const wxRect& rect, size_t n) const
139 {
140 // TODO: Maybe this code could be moved to wxVListBox::OnPaint?
141 dc.SetFont(m_useFont);
142
143 int flags = 0;
144
145 // Set correct text colour for selected items
146 if ( wxVListBox::GetSelection() == (int) n )
147 {
148 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT) );
149 flags |= wxODCB_PAINTING_SELECTED;
150 }
151 else
152 {
153 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT) );
154 }
155
156 OnDrawItem(dc,rect,(int)n,flags);
157 }
158
OnMeasureItem(size_t n) const159 wxCoord wxVListBoxComboPopup::OnMeasureItem(size_t n) const
160 {
161 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
162
163 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)),
164 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
165
166 wxCoord h = combo->OnMeasureItem(n);
167 if ( h < 0 )
168 h = m_itemHeight;
169 return h;
170 }
171
OnMeasureItemWidth(size_t n) const172 wxCoord wxVListBoxComboPopup::OnMeasureItemWidth(size_t n) const
173 {
174 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
175
176 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)),
177 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
178
179 return combo->OnMeasureItemWidth(n);
180 }
181
OnDrawBg(wxDC & dc,const wxRect & rect,int item,int flags) const182 void wxVListBoxComboPopup::OnDrawBg( wxDC& dc,
183 const wxRect& rect,
184 int item,
185 int flags ) const
186 {
187 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
188
189 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)),
190 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
191
192 if ( IsCurrent((size_t)item) && !(flags & wxODCB_PAINTING_CONTROL) )
193 flags |= wxODCB_PAINTING_SELECTED;
194
195 combo->OnDrawBackground(dc,rect,item,flags);
196 }
197
OnDrawBackground(wxDC & dc,const wxRect & rect,size_t n) const198 void wxVListBoxComboPopup::OnDrawBackground(wxDC& dc, const wxRect& rect, size_t n) const
199 {
200 OnDrawBg(dc,rect,(int)n,0);
201 }
202
203 // This is called from wxVListBoxComboPopup::OnDrawItem, with text colour and font prepared
OnDrawItem(wxDC & dc,const wxRect & rect,int item,int flags) const204 void wxVListBoxComboPopup::OnDrawItem( wxDC& dc, const wxRect& rect, int item, int flags ) const
205 {
206 wxOwnerDrawnComboBox* combo = (wxOwnerDrawnComboBox*) m_combo;
207
208 wxASSERT_MSG( combo->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)),
209 wxT("you must subclass wxVListBoxComboPopup for drawing and measuring methods") );
210
211 combo->OnDrawItem(dc,rect,item,flags);
212 }
213
DismissWithEvent()214 void wxVListBoxComboPopup::DismissWithEvent()
215 {
216 StopPartialCompletion();
217
218 int selection = wxVListBox::GetSelection();
219
220 Dismiss();
221
222 wxString valStr;
223 if ( selection != wxNOT_FOUND )
224 valStr = m_strings[selection];
225 else
226 valStr = wxEmptyString;
227
228 m_value = selection;
229
230 if ( valStr != m_combo->GetValue() )
231 m_combo->SetValueWithEvent(valStr);
232
233 SendComboBoxEvent(selection);
234 }
235
SendComboBoxEvent(int selection)236 void wxVListBoxComboPopup::SendComboBoxEvent( int selection )
237 {
238 wxCommandEvent evt(wxEVT_COMMAND_COMBOBOX_SELECTED,m_combo->GetId());
239
240 evt.SetEventObject(m_combo);
241
242 evt.SetInt(selection);
243
244 // Set client data, if any
245 if ( selection >= 0 && (int)m_clientDatas.GetCount() > selection )
246 {
247 void* clientData = m_clientDatas[selection];
248 if ( m_clientDataItemsType == wxClientData_Object )
249 evt.SetClientObject((wxClientData*)clientData);
250 else
251 evt.SetClientData(clientData);
252 }
253
254 m_combo->GetEventHandler()->AddPendingEvent(evt);
255 }
256
257 // returns true if key was consumed
HandleKey(int keycode,bool saturate,wxChar unicode)258 bool wxVListBoxComboPopup::HandleKey( int keycode, bool saturate, wxChar unicode )
259 {
260 const int itemCount = GetCount();
261
262 // keys do nothing in the empty control and returning immediately avoids
263 // using invalid indices below
264 if ( !itemCount )
265 return false;
266
267 int value = m_value;
268 int comboStyle = m_combo->GetWindowStyle();
269
270 // this is the character equivalent of the code
271 wxChar keychar = 0;
272 if ( keycode < WXK_START )
273 {
274 #if wxUSE_UNICODE
275 if ( unicode > 0 )
276 {
277 if ( wxIsprint(unicode) )
278 keychar = unicode;
279 }
280 else
281 #else
282 wxUnusedVar(unicode);
283 #endif
284 if ( wxIsprint(keycode) )
285 {
286 keychar = (wxChar) keycode;
287 }
288 }
289
290 if ( keycode == WXK_DOWN || keycode == WXK_RIGHT )
291 {
292 value++;
293 StopPartialCompletion();
294 }
295 else if ( keycode == WXK_UP || keycode == WXK_LEFT )
296 {
297 value--;
298 StopPartialCompletion();
299 }
300 else if ( keycode == WXK_PAGEDOWN )
301 {
302 value+=10;
303 StopPartialCompletion();
304 }
305 else if ( keycode == WXK_PAGEUP )
306 {
307 value-=10;
308 StopPartialCompletion();
309 }
310 else if ( keychar && (comboStyle & wxCB_READONLY) )
311 {
312 // Try partial completion
313
314 // find the new partial completion string
315 #if wxUSE_TIMER
316 if (m_partialCompletionTimer.IsRunning())
317 m_partialCompletionString+=wxString(keychar);
318 else
319 #endif // wxUSE_TIMER
320 m_partialCompletionString=wxString(keychar);
321
322 // now search through the values to see if this is found
323 int found = -1;
324 unsigned int length=m_partialCompletionString.length();
325 int i;
326 for (i=0; i<itemCount; i++)
327 {
328 wxString item=GetString(i);
329 if (( item.length() >= length) && (! m_partialCompletionString.CmpNoCase(item.Left(length))))
330 {
331 found=i;
332 break;
333 }
334 }
335
336 if (found<0)
337 {
338 StopPartialCompletion();
339 ::wxBell();
340 return true; // to stop the first value being set
341 }
342 else
343 {
344 value=i;
345 #if wxUSE_TIMER
346 m_partialCompletionTimer.Start(wxODCB_PARTIAL_COMPLETION_TIME, true);
347 #endif // wxUSE_TIMER
348 }
349 }
350 else
351 return false;
352
353 if ( saturate )
354 {
355 if ( value >= itemCount )
356 value = itemCount - 1;
357 else if ( value < 0 )
358 value = 0;
359 }
360 else
361 {
362 if ( value >= itemCount )
363 value -= itemCount;
364 else if ( value < 0 )
365 value += itemCount;
366 }
367
368 if ( value == m_value )
369 // Even if value was same, don't skip the event
370 // (good for consistency)
371 return true;
372
373 m_value = value;
374
375 if ( value >= 0 )
376 m_combo->SetValue(m_strings[value]);
377
378 SendComboBoxEvent(m_value);
379
380 return true;
381 }
382
383 // stop partial completion
StopPartialCompletion()384 void wxVListBoxComboPopup::StopPartialCompletion()
385 {
386 m_partialCompletionString = wxEmptyString;
387 #if wxUSE_TIMER
388 m_partialCompletionTimer.Stop();
389 #endif // wxUSE_TIMER
390 }
391
OnComboDoubleClick()392 void wxVListBoxComboPopup::OnComboDoubleClick()
393 {
394 // Cycle on dclick (disable saturation to allow true cycling).
395 if ( !::wxGetKeyState(WXK_SHIFT) )
396 HandleKey(WXK_DOWN,false);
397 else
398 HandleKey(WXK_UP,false);
399 }
400
OnComboKeyEvent(wxKeyEvent & event)401 void wxVListBoxComboPopup::OnComboKeyEvent( wxKeyEvent& event )
402 {
403 // Saturated key movement on
404 if ( !HandleKey(event.GetKeyCode(),true,
405 #if wxUSE_UNICODE
406 event.GetUnicodeKey()
407 #else
408 0
409 #endif
410 ) )
411 event.Skip();
412 }
413
OnPopup()414 void wxVListBoxComboPopup::OnPopup()
415 {
416 // *must* set value after size is set (this is because of a vlbox bug)
417 wxVListBox::SetSelection(m_value);
418 }
419
OnMouseMove(wxMouseEvent & event)420 void wxVListBoxComboPopup::OnMouseMove(wxMouseEvent& event)
421 {
422 event.Skip();
423
424 // Move selection to cursor if it is inside the popup
425
426 int y = event.GetPosition().y;
427 int fromBottom = GetClientSize().y - y;
428
429 // Since in any case we need to find out if the last item is only
430 // partially visible, we might just as well replicate the HitTest
431 // loop here.
432 const size_t lineMax = GetVisibleEnd();
433 for ( size_t line = GetVisibleBegin(); line < lineMax; line++ )
434 {
435 y -= OnGetLineHeight(line);
436 if ( y < 0 )
437 {
438 // Only change selection if item is fully visible
439 if ( (y + fromBottom) >= 0 )
440 {
441 wxVListBox::SetSelection((int)line);
442 return;
443 }
444 }
445 }
446 }
447
OnLeftClick(wxMouseEvent & WXUNUSED (event))448 void wxVListBoxComboPopup::OnLeftClick(wxMouseEvent& WXUNUSED(event))
449 {
450 DismissWithEvent();
451 }
452
OnKey(wxKeyEvent & event)453 void wxVListBoxComboPopup::OnKey(wxKeyEvent& event)
454 {
455 // Hide popup if certain key or key combination was pressed
456 if ( m_combo->IsKeyPopupToggle(event) )
457 {
458 StopPartialCompletion();
459 Dismiss();
460 }
461 else if ( event.AltDown() )
462 {
463 // On both wxGTK and wxMSW, pressing Alt down seems to
464 // completely freeze things in popup (ie. arrow keys and
465 // enter won't work).
466 return;
467 }
468 // Select item if ENTER is pressed
469 else if ( event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_NUMPAD_ENTER )
470 {
471 DismissWithEvent();
472 }
473 else
474 {
475 int comboStyle = m_combo->GetWindowStyle();
476 int keycode = event.GetKeyCode();
477 // Process partial completion key codes here, but not the arrow keys as the base class will do that for us
478 if ((comboStyle & wxCB_READONLY) &&
479 (keycode >= WXK_SPACE) && (keycode <=255) && (keycode != WXK_DELETE) && wxIsprint(keycode))
480 {
481 OnComboKeyEvent(event);
482 SetSelection(m_value); // ensure the highlight bar moves
483 }
484 else
485 event.Skip();
486 }
487 }
488
Insert(const wxString & item,int pos)489 void wxVListBoxComboPopup::Insert( const wxString& item, int pos )
490 {
491 // Need to change selection?
492 wxString strValue;
493 if ( !(m_combo->GetWindowStyle() & wxCB_READONLY) &&
494 m_combo->GetValue() == item )
495 {
496 m_value = pos;
497 }
498
499 m_strings.Insert(item,pos);
500 m_widths.Insert(-1,pos);
501 m_widthsDirty = true;
502
503 if ( IsCreated() )
504 wxVListBox::SetItemCount( wxVListBox::GetItemCount()+1 );
505 }
506
Append(const wxString & item)507 int wxVListBoxComboPopup::Append(const wxString& item)
508 {
509 int pos = (int)m_strings.GetCount();
510
511 if ( m_combo->GetWindowStyle() & wxCB_SORT )
512 {
513 // Find position
514 // TODO: Could be optimized with binary search
515 wxArrayString strings = m_strings;
516 unsigned int i;
517
518 for ( i=0; i<strings.GetCount(); i++ )
519 {
520 if ( item.CmpNoCase(strings.Item(i)) < 0 )
521 {
522 pos = (int)i;
523 break;
524 }
525 }
526 }
527
528 Insert(item,pos);
529
530 return pos;
531 }
532
Clear()533 void wxVListBoxComboPopup::Clear()
534 {
535 wxASSERT(m_combo);
536
537 m_strings.Empty();
538 m_widths.Empty();
539
540 m_widestWidth = 0;
541 m_widestItem = -1;
542
543 ClearClientDatas();
544
545 m_value = wxNOT_FOUND;
546
547 if ( IsCreated() )
548 wxVListBox::SetItemCount(0);
549 }
550
ClearClientDatas()551 void wxVListBoxComboPopup::ClearClientDatas()
552 {
553 if ( m_clientDataItemsType == wxClientData_Object )
554 {
555 size_t i;
556 for ( i=0; i<m_clientDatas.GetCount(); i++ )
557 delete (wxClientData*) m_clientDatas[i];
558 }
559
560 m_clientDatas.Empty();
561 }
562
SetItemClientData(unsigned int n,void * clientData,wxClientDataType clientDataItemsType)563 void wxVListBoxComboPopup::SetItemClientData( unsigned int n,
564 void* clientData,
565 wxClientDataType clientDataItemsType )
566 {
567 // It should be sufficient to update this variable only here
568 m_clientDataItemsType = clientDataItemsType;
569
570 m_clientDatas.SetCount(n+1,NULL);
571 m_clientDatas[n] = clientData;
572
573 ItemWidthChanged(n);
574 }
575
GetItemClientData(unsigned int n) const576 void* wxVListBoxComboPopup::GetItemClientData(unsigned int n) const
577 {
578 if ( m_clientDatas.GetCount() > n )
579 return m_clientDatas[n];
580
581 return NULL;
582 }
583
Delete(unsigned int item)584 void wxVListBoxComboPopup::Delete( unsigned int item )
585 {
586 // Remove client data, if set
587 if ( m_clientDatas.GetCount() )
588 {
589 if ( m_clientDataItemsType == wxClientData_Object )
590 delete (wxClientData*) m_clientDatas[item];
591
592 m_clientDatas.RemoveAt(item);
593 }
594
595 m_strings.RemoveAt(item);
596 m_widths.RemoveAt(item);
597
598 if ( (int)item == m_widestItem )
599 m_findWidest = true;
600
601 int sel = GetSelection();
602
603 if ( IsCreated() )
604 wxVListBox::SetItemCount( wxVListBox::GetItemCount()-1 );
605
606 // Fix selection
607 if ( (int)item < sel )
608 SetSelection(sel-1);
609 else if ( (int)item == sel )
610 SetSelection(wxNOT_FOUND);
611 }
612
FindString(const wxString & s,bool bCase) const613 int wxVListBoxComboPopup::FindString(const wxString& s, bool bCase) const
614 {
615 return m_strings.Index(s, bCase);
616 }
617
GetCount() const618 unsigned int wxVListBoxComboPopup::GetCount() const
619 {
620 return m_strings.GetCount();
621 }
622
GetString(int item) const623 wxString wxVListBoxComboPopup::GetString( int item ) const
624 {
625 return m_strings[item];
626 }
627
SetString(int item,const wxString & str)628 void wxVListBoxComboPopup::SetString( int item, const wxString& str )
629 {
630 m_strings[item] = str;
631 ItemWidthChanged(item);
632 }
633
GetStringValue() const634 wxString wxVListBoxComboPopup::GetStringValue() const
635 {
636 if ( m_value >= 0 )
637 return m_strings[m_value];
638 return wxEmptyString;
639 }
640
SetSelection(int item)641 void wxVListBoxComboPopup::SetSelection( int item )
642 {
643 wxCHECK_RET( item == wxNOT_FOUND || ((unsigned int)item < GetCount()),
644 wxT("invalid index in wxVListBoxComboPopup::SetSelection") );
645
646 m_value = item;
647
648 if ( IsCreated() )
649 wxVListBox::SetSelection(item);
650 }
651
GetSelection() const652 int wxVListBoxComboPopup::GetSelection() const
653 {
654 return m_value;
655 }
656
SetStringValue(const wxString & value)657 void wxVListBoxComboPopup::SetStringValue( const wxString& value )
658 {
659 int index = m_strings.Index(value);
660
661 if ( index >= 0 && index < (int)wxVListBox::GetItemCount() )
662 {
663 m_value = index;
664 wxVListBox::SetSelection(index);
665 }
666 }
667
CalcWidths()668 void wxVListBoxComboPopup::CalcWidths()
669 {
670 bool doFindWidest = m_findWidest;
671
672 // Measure items with dirty width.
673 if ( m_widthsDirty )
674 {
675 unsigned int i;
676 unsigned int n = m_widths.GetCount();
677 int dirtyHandled = 0;
678 wxArrayInt& widths = m_widths;
679
680 // I think using wxDC::GetTextExtent is faster than
681 // wxWindow::GetTextExtent (assuming same dc is used
682 // for all calls, as we do here).
683 wxClientDC dc(m_combo);
684 dc.SetFont(m_useFont);
685
686 for ( i=0; i<n; i++ )
687 {
688 if ( widths[i] < 0 )
689 {
690 wxCoord x = OnMeasureItemWidth(i);
691
692 if ( x < 0 )
693 {
694 const wxString& text = m_strings[i];
695
696 // To make sure performance won't suck in extreme scenarios,
697 // we'll estimate length after some arbitrary number of items
698 // have been checked precily.
699 if ( dirtyHandled < 1024 )
700 {
701 wxCoord y;
702 dc.GetTextExtent(text, &x, &y, 0, 0);
703 x += 4;
704 }
705 else
706 {
707 x = text.length() * (dc.GetCharWidth()+1);
708 }
709 }
710
711 widths[i] = x;
712
713 if ( x >= m_widestWidth )
714 {
715 m_widestWidth = x;
716 m_widestItem = (int)i;
717 }
718 else if ( (int)i == m_widestItem )
719 {
720 // Width of previously widest item has been decreased, so
721 // we'll have to check all to find current widest item.
722 doFindWidest = true;
723 }
724
725 dirtyHandled++;
726 }
727 }
728
729 m_widthsDirty = false;
730 }
731
732 if ( doFindWidest )
733 {
734 unsigned int i;
735 unsigned int n = m_widths.GetCount();
736
737 int bestWidth = -1;
738 int bestIndex = -1;
739
740 for ( i=0; i<n; i++ )
741 {
742 int w = m_widths[i];
743 if ( w > bestWidth )
744 {
745 bestIndex = (int)i;
746 bestWidth = w;
747 }
748 }
749
750 m_widestWidth = bestWidth;
751 m_widestItem = bestIndex;
752
753 m_findWidest = false;
754 }
755 }
756
GetAdjustedSize(int minWidth,int prefHeight,int maxHeight)757 wxSize wxVListBoxComboPopup::GetAdjustedSize( int minWidth, int prefHeight, int maxHeight )
758 {
759 int height = 250;
760
761 maxHeight -= 2; // Must take borders into account
762
763 if ( m_strings.GetCount() )
764 {
765 if ( prefHeight > 0 )
766 height = prefHeight;
767
768 if ( height > maxHeight )
769 height = maxHeight;
770
771 int totalHeight = GetTotalHeight(); // + 3;
772
773 #if defined(__WXMAC__)
774 // Take borders into account, or there will be scrollbars even for one or two items.
775 totalHeight += 2;
776 #endif
777 if ( height >= totalHeight )
778 {
779 height = totalHeight;
780 }
781 else
782 {
783 // Adjust height to a multiple of the height of the first item
784 // NB: Calculations that take variable height into account
785 // are unnecessary.
786 int fih = GetLineHeight(0);
787 height -= height % fih;
788 }
789 }
790 else
791 height = 50;
792
793 CalcWidths();
794
795 // Take scrollbar into account in width calculations
796 int widestWidth = m_widestWidth + wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
797 return wxSize(minWidth > widestWidth ? minWidth : widestWidth,
798 height+2);
799 }
800
801 //void wxVListBoxComboPopup::Populate( int n, const wxString choices[] )
Populate(const wxArrayString & choices)802 void wxVListBoxComboPopup::Populate( const wxArrayString& choices )
803 {
804 int i;
805
806 int n = choices.GetCount();
807
808 for ( i=0; i<n; i++ )
809 {
810 const wxString& item = choices.Item(i);
811 m_strings.Add(item);
812 }
813
814 m_widths.SetCount(n,-1);
815 m_widthsDirty = true;
816
817 if ( IsCreated() )
818 wxVListBox::SetItemCount(n);
819
820 // Sort the initial choices
821 if ( m_combo->GetWindowStyle() & wxCB_SORT )
822 m_strings.Sort();
823
824 // Find initial selection
825 wxString strValue = m_combo->GetValue();
826 if ( strValue.length() )
827 m_value = m_strings.Index(strValue);
828 }
829
830 // ----------------------------------------------------------------------------
831 // wxOwnerDrawnComboBox
832 // ----------------------------------------------------------------------------
833
834
BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox,wxComboCtrl)835 BEGIN_EVENT_TABLE(wxOwnerDrawnComboBox, wxComboCtrl)
836 END_EVENT_TABLE()
837
838
839 IMPLEMENT_DYNAMIC_CLASS2(wxOwnerDrawnComboBox, wxComboCtrl, wxControlWithItems)
840
841 void wxOwnerDrawnComboBox::Init()
842 {
843 }
844
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)845 bool wxOwnerDrawnComboBox::Create(wxWindow *parent,
846 wxWindowID id,
847 const wxString& value,
848 const wxPoint& pos,
849 const wxSize& size,
850 long style,
851 const wxValidator& validator,
852 const wxString& name)
853 {
854 return wxComboCtrl::Create(parent,id,value,pos,size,style,validator,name);
855 }
856
wxOwnerDrawnComboBox(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)857 wxOwnerDrawnComboBox::wxOwnerDrawnComboBox(wxWindow *parent,
858 wxWindowID id,
859 const wxString& value,
860 const wxPoint& pos,
861 const wxSize& size,
862 const wxArrayString& choices,
863 long style,
864 const wxValidator& validator,
865 const wxString& name)
866 : wxComboCtrl()
867 {
868 Init();
869
870 Create(parent,id,value,pos,size,choices,style, validator, name);
871 }
872
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style,const wxValidator & validator,const wxString & name)873 bool wxOwnerDrawnComboBox::Create(wxWindow *parent,
874 wxWindowID id,
875 const wxString& value,
876 const wxPoint& pos,
877 const wxSize& size,
878 const wxArrayString& choices,
879 long style,
880 const wxValidator& validator,
881 const wxString& name)
882 {
883 m_initChs = choices;
884 //wxCArrayString chs(choices);
885
886 //return Create(parent, id, value, pos, size, chs.GetCount(),
887 // chs.GetStrings(), style, validator, name);
888 return Create(parent, id, value, pos, size, 0,
889 NULL, style, validator, name);
890 }
891
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,int n,const wxString choices[],long style,const wxValidator & validator,const wxString & name)892 bool wxOwnerDrawnComboBox::Create(wxWindow *parent,
893 wxWindowID id,
894 const wxString& value,
895 const wxPoint& pos,
896 const wxSize& size,
897 int n,
898 const wxString choices[],
899 long style,
900 const wxValidator& validator,
901 const wxString& name)
902 {
903
904 if ( !Create(parent, id, value, pos, size, style,
905 validator, name) )
906 {
907 return false;
908 }
909
910 int i;
911 for ( i=0; i<n; i++ )
912 m_initChs.Add(choices[i]);
913
914 return true;
915 }
916
~wxOwnerDrawnComboBox()917 wxOwnerDrawnComboBox::~wxOwnerDrawnComboBox()
918 {
919 if ( m_popupInterface )
920 GetVListBoxComboPopup()->ClearClientDatas();
921 }
922
DoSetPopupControl(wxComboPopup * popup)923 void wxOwnerDrawnComboBox::DoSetPopupControl(wxComboPopup* popup)
924 {
925 if ( !popup )
926 {
927 popup = new wxVListBoxComboPopup();
928 }
929
930 wxComboCtrl::DoSetPopupControl(popup);
931
932 wxASSERT(popup);
933
934 // Add initial choices to the wxVListBox
935 if ( !GetVListBoxComboPopup()->GetCount() )
936 {
937 GetVListBoxComboPopup()->Populate(m_initChs);
938 m_initChs.Clear();
939 }
940 }
941
942 // ----------------------------------------------------------------------------
943 // wxOwnerDrawnComboBox item manipulation methods
944 // ----------------------------------------------------------------------------
945
Clear()946 void wxOwnerDrawnComboBox::Clear()
947 {
948 EnsurePopupControl();
949
950 GetVListBoxComboPopup()->Clear();
951
952 SetValue(wxEmptyString);
953 }
954
Delete(unsigned int n)955 void wxOwnerDrawnComboBox::Delete(unsigned int n)
956 {
957 wxCHECK_RET( IsValid(n), _T("invalid index in wxOwnerDrawnComboBox::Delete") );
958
959 if ( GetSelection() == (int) n )
960 SetValue(wxEmptyString);
961
962 GetVListBoxComboPopup()->Delete(n);
963 }
964
GetCount() const965 unsigned int wxOwnerDrawnComboBox::GetCount() const
966 {
967 if ( !m_popupInterface )
968 return m_initChs.GetCount();
969
970 return GetVListBoxComboPopup()->GetCount();
971 }
972
GetString(unsigned int n) const973 wxString wxOwnerDrawnComboBox::GetString(unsigned int n) const
974 {
975 wxCHECK_MSG( IsValid(n), wxEmptyString, _T("invalid index in wxOwnerDrawnComboBox::GetString") );
976
977 if ( !m_popupInterface )
978 return m_initChs.Item(n);
979
980 return GetVListBoxComboPopup()->GetString(n);
981 }
982
SetString(unsigned int n,const wxString & s)983 void wxOwnerDrawnComboBox::SetString(unsigned int n, const wxString& s)
984 {
985 EnsurePopupControl();
986
987 wxCHECK_RET( IsValid(n), _T("invalid index in wxOwnerDrawnComboBox::SetString") );
988
989 GetVListBoxComboPopup()->SetString(n,s);
990 }
991
FindString(const wxString & s,bool bCase) const992 int wxOwnerDrawnComboBox::FindString(const wxString& s, bool bCase) const
993 {
994 if ( !m_popupInterface )
995 return m_initChs.Index(s, bCase);
996
997 return GetVListBoxComboPopup()->FindString(s, bCase);
998 }
999
Select(int n)1000 void wxOwnerDrawnComboBox::Select(int n)
1001 {
1002 EnsurePopupControl();
1003
1004 wxCHECK_RET( (n == wxNOT_FOUND) || IsValid(n), _T("invalid index in wxOwnerDrawnComboBox::Select") );
1005
1006 GetVListBoxComboPopup()->SetSelection(n);
1007
1008 wxString str;
1009 if ( n >= 0 )
1010 str = GetVListBoxComboPopup()->GetString(n);
1011
1012 // Refresh text portion in control
1013 if ( m_text )
1014 m_text->SetValue( str );
1015 else
1016 m_valueString = str;
1017
1018 Refresh();
1019 }
1020
GetSelection() const1021 int wxOwnerDrawnComboBox::GetSelection() const
1022 {
1023 if ( !m_popupInterface )
1024 return m_initChs.Index(m_valueString);
1025
1026 return GetVListBoxComboPopup()->GetSelection();
1027 }
1028
DoAppend(const wxString & item)1029 int wxOwnerDrawnComboBox::DoAppend(const wxString& item)
1030 {
1031 EnsurePopupControl();
1032 wxASSERT(m_popupInterface);
1033
1034 return GetVListBoxComboPopup()->Append(item);
1035 }
1036
DoInsert(const wxString & item,unsigned int pos)1037 int wxOwnerDrawnComboBox::DoInsert(const wxString& item, unsigned int pos)
1038 {
1039 EnsurePopupControl();
1040
1041 wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into sorted list"));
1042 wxCHECK_MSG(IsValidInsert(pos), -1, wxT("invalid index"));
1043
1044 GetVListBoxComboPopup()->Insert(item,pos);
1045
1046 return pos;
1047 }
1048
DoSetItemClientData(unsigned int n,void * clientData)1049 void wxOwnerDrawnComboBox::DoSetItemClientData(unsigned int n, void* clientData)
1050 {
1051 EnsurePopupControl();
1052
1053 GetVListBoxComboPopup()->SetItemClientData(n,clientData,m_clientDataItemsType);
1054 }
1055
DoGetItemClientData(unsigned int n) const1056 void* wxOwnerDrawnComboBox::DoGetItemClientData(unsigned int n) const
1057 {
1058 if ( !m_popupInterface )
1059 return NULL;
1060
1061 return GetVListBoxComboPopup()->GetItemClientData(n);
1062 }
1063
DoSetItemClientObject(unsigned int n,wxClientData * clientData)1064 void wxOwnerDrawnComboBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData)
1065 {
1066 DoSetItemClientData(n, (void*) clientData);
1067 }
1068
DoGetItemClientObject(unsigned int n) const1069 wxClientData* wxOwnerDrawnComboBox::DoGetItemClientObject(unsigned int n) const
1070 {
1071 return (wxClientData*) DoGetItemClientData(n);
1072 }
1073
1074 // ----------------------------------------------------------------------------
1075 // wxOwnerDrawnComboBox item drawing and measuring default implementations
1076 // ----------------------------------------------------------------------------
1077
OnDrawItem(wxDC & dc,const wxRect & rect,int item,int flags) const1078 void wxOwnerDrawnComboBox::OnDrawItem( wxDC& dc,
1079 const wxRect& rect,
1080 int item,
1081 int flags ) const
1082 {
1083 if ( flags & wxODCB_PAINTING_CONTROL )
1084 {
1085 dc.DrawText( GetValue(),
1086 rect.x + GetTextIndent(),
1087 (rect.height-dc.GetCharHeight())/2 + rect.y );
1088 }
1089 else
1090 {
1091 dc.DrawText( GetVListBoxComboPopup()->GetString(item), rect.x + 2, rect.y );
1092 }
1093 }
1094
OnMeasureItem(size_t WXUNUSED (item)) const1095 wxCoord wxOwnerDrawnComboBox::OnMeasureItem( size_t WXUNUSED(item) ) const
1096 {
1097 return -1;
1098 }
1099
OnMeasureItemWidth(size_t WXUNUSED (item)) const1100 wxCoord wxOwnerDrawnComboBox::OnMeasureItemWidth( size_t WXUNUSED(item) ) const
1101 {
1102 return -1;
1103 }
1104
OnDrawBackground(wxDC & dc,const wxRect & rect,int WXUNUSED (item),int flags) const1105 void wxOwnerDrawnComboBox::OnDrawBackground(wxDC& dc,
1106 const wxRect& rect,
1107 int WXUNUSED(item),
1108 int flags) const
1109 {
1110 // We need only to explicitly draw background for items
1111 // that should have selected background. Also, call PrepareBackground
1112 // always when painting the control so that clipping is done properly.
1113
1114 if ( (flags & wxODCB_PAINTING_SELECTED) ||
1115 ((flags & wxODCB_PAINTING_CONTROL) && HasFlag(wxCB_READONLY)) )
1116 {
1117 int bgFlags = wxCONTROL_SELECTED;
1118
1119 if ( !(flags & wxODCB_PAINTING_CONTROL) )
1120 bgFlags |= wxCONTROL_ISSUBMENU;
1121
1122 PrepareBackground(dc, rect, bgFlags);
1123 }
1124 }
1125
1126 #endif // wxUSE_ODCOMBOBOX
1127