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