X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/1e6feb95a79834836e88143b15d9f424ebe79621..aa78d22e2008726218d47da1f2ac5864c11b8ebb:/src/univ/combobox.cpp diff --git a/src/univ/combobox.cpp b/src/univ/combobox.cpp index 40020d468a..43e9e55172 100644 --- a/src/univ/combobox.cpp +++ b/src/univ/combobox.cpp @@ -1,24 +1,14 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: univ/combobox.cpp +// Name: src/univ/combobox.cpp // Purpose: wxComboControl and wxComboBox implementation // Author: Vadim Zeitlin // Modified by: // Created: 15.12.00 // RCS-ID: $Id$ -// Copyright: (c) 2000 Vadim Zeitlin +// Copyright: (c) 2000 SciTech Software, Inc. (www.scitechsoft.com) // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -/* - TODO: - - +1. typing in the text should select the string in listbox - +2. scrollbars in listbox are unusable - +3. the initially selected item is not selected - ?4. kbd interface (what does GTK do?) - 5. there is still autoscrolling without scrollbars - but is it bad? - */ - // ============================================================================ // declarations // ============================================================================ @@ -27,10 +17,6 @@ // headers // ---------------------------------------------------------------------------- -#ifdef __GNUG__ - #pragma implementation "univcombobox.h" -#endif - #include "wx/wxprec.h" #ifdef __BORLANDC__ @@ -46,10 +32,12 @@ #include "wx/combobox.h" #include "wx/listbox.h" #include "wx/textctrl.h" + #include "wx/bmpbuttn.h" #include "wx/validate.h" #endif +#include "wx/tooltip.h" #include "wx/popupwin.h" #include "wx/univ/renderer.h" @@ -64,6 +52,12 @@ 3. which passes them to the popup window if it is popped up */ +// constants +// ---------------------------------------------------------------------------- + +// the margin between the text control and the combo button +static const wxCoord g_comboMargin = 2; + // ---------------------------------------------------------------------------- // wxComboButton is just a normal button except that it sends commands to the // combobox and not its parent @@ -73,27 +67,37 @@ class wxComboButton : public wxBitmapButton { public: wxComboButton(wxComboControl *combo) - : wxBitmapButton(combo->GetParent(), -1, wxNullBitmap, + : wxBitmapButton(combo->GetParent(), wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, - wxBORDER_NONE) + wxBORDER_NONE | wxBU_EXACTFIT) { m_combo = combo; - wxBitmap bmpNormal, bmpPressed, bmpDisabled; + wxBitmap bmpNormal, bmpFocus, bmpPressed, bmpDisabled; + + GetRenderer()->GetComboBitmaps(&bmpNormal, + &bmpFocus, + &bmpPressed, + &bmpDisabled); - GetRenderer()->GetComboBitmaps(&bmpNormal, &bmpPressed, &bmpDisabled); SetBitmapLabel(bmpNormal); - SetBitmapFocus(bmpNormal); - SetBitmapSelected(bmpPressed); - SetBitmapDisabled(bmpDisabled); + SetBitmapFocus(bmpFocus.Ok() ? bmpFocus : bmpNormal); + SetBitmapSelected(bmpPressed.Ok() ? bmpPressed : bmpNormal); + SetBitmapDisabled(bmpDisabled.Ok() ? bmpDisabled : bmpNormal); - SetSize(bmpNormal.GetWidth(), bmpNormal.GetHeight()); + SetBestSize(wxDefaultSize); } protected: - void OnButton(wxCommandEvent& event) { m_combo->ShowPopup(); } + void OnButton(wxCommandEvent& WXUNUSED(event)) { m_combo->ShowPopup(); } + + virtual wxSize DoGetBestClientSize() const + { + const wxBitmap& bmp = GetBitmapLabel(); - virtual wxSize DoGetBestSize() const { return GetSize(); } + return wxSize(bmp.GetWidth(), bmp.GetHeight()); + + } private: wxComboControl *m_combo; @@ -114,9 +118,19 @@ public: virtual ~wxComboListBox(); // implement wxComboPopup methods - virtual bool SetSelection(const wxString& value); + virtual bool SetSelection(const wxString& s); virtual wxControl *GetControl() { return this; } virtual void OnShow(); + virtual wxCoord GetBestWidth() const; + + // fix virtual function hiding + virtual void SetSelection(int n) { DoSetSelection(n, true); } + void SetSelection(int n, bool select) { DoSetSelection(n, select); } + + // used to process wxUniv actions + bool PerformAction(const wxControlAction& action, + long numArg, + const wxString& strArg); protected: // we shouldn't return height too big from here @@ -125,15 +139,16 @@ protected: // filter mouse move events happening outside the list box void OnMouseMove(wxMouseEvent& event); + // set m_clicked value from here + void OnLeftUp(wxMouseEvent& event); + // called whenever the user selects or activates a listbox item void OnSelect(wxCommandEvent& event); - // used to process wxUniv actions - bool PerformAction(const wxControlAction& action, - long numArg, - const wxString& strArg); - private: + // has the mouse been released on this control? + bool m_clicked; + DECLARE_EVENT_TABLE() }; @@ -165,13 +180,14 @@ private: // ---------------------------------------------------------------------------- BEGIN_EVENT_TABLE(wxComboButton, wxButton) - EVT_BUTTON(-1, wxComboButton::OnButton) + EVT_BUTTON(wxID_ANY, wxComboButton::OnButton) END_EVENT_TABLE() BEGIN_EVENT_TABLE(wxComboListBox, wxListBox) - EVT_LISTBOX(-1, wxComboListBox::OnSelect) - EVT_LISTBOX_DCLICK(-1, wxComboListBox::OnSelect) + EVT_LISTBOX(wxID_ANY, wxComboListBox::OnSelect) + EVT_LISTBOX_DCLICK(wxID_ANY, wxComboListBox::OnSelect) EVT_MOTION(wxComboListBox::OnMouseMove) + EVT_LEFT_UP(wxComboListBox::OnLeftUp) END_EVENT_TABLE() BEGIN_EVENT_TABLE(wxComboControl, wxControl) @@ -182,10 +198,10 @@ END_EVENT_TABLE() BEGIN_EVENT_TABLE(wxComboTextCtrl, wxTextCtrl) EVT_KEY_DOWN(wxComboTextCtrl::OnKey) EVT_KEY_UP(wxComboTextCtrl::OnKey) - EVT_TEXT(-1, wxComboTextCtrl::OnText) + EVT_TEXT(wxID_ANY, wxComboTextCtrl::OnText) END_EVENT_TABLE() -IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl); +IMPLEMENT_DYNAMIC_CLASS(wxComboBox, wxControl) // ============================================================================ // implementation @@ -199,7 +215,9 @@ void wxComboControl::Init() { m_popup = (wxComboPopup *)NULL; m_winPopup = (wxPopupComboWindow *)NULL; - m_isPopupShown = FALSE; + m_isPopupShown = false; + m_btn = NULL; + m_text = NULL; } bool wxComboControl::Create(wxWindow *parent, @@ -216,7 +234,7 @@ bool wxComboControl::Create(wxWindow *parent, style &= ~wxBORDER_NONE; style |= wxBORDER_SUNKEN; if ( !wxControl::Create(parent, id, pos, size, style, validator, name) ) - return FALSE; + return false; // create the text control and the button as our siblings (*not* children), // don't care about size/position here - they will be set in DoMoveWindow() @@ -228,17 +246,18 @@ bool wxComboControl::Create(wxWindow *parent, // for compatibility with the other ports, the height specified is the // combined height of the combobox itself and the popup - if ( size.y == -1 ) + if ( size.y == wxDefaultCoord ) { // ok, use default height for popup too - m_heightPopup = -1; + m_heightPopup = wxDefaultCoord; } else { m_heightPopup = size.y - DoGetBestSize().y; } - DoSetSize(pos.x, pos.y, size.x, size.y); + SetBestSize(size); + Move(pos); // create the popup window immediately here to allow creating the controls // with parent == GetPopupWindow() from the derived class ctor @@ -246,20 +265,20 @@ bool wxComboControl::Create(wxWindow *parent, // have to disable this window to avoid interfering it with message // processing to the text and the button... but pretend it is enabled to - // make IsEnabled() return TRUE - wxControl::Enable(FALSE); // don't use non virtual Disable() here! - m_isEnabled = TRUE; + // make IsEnabled() return true + wxControl::Enable(false); // don't use non virtual Disable() here! + m_isEnabled = true; CreateInputHandler(wxINP_HANDLER_COMBOBOX); - return TRUE; + return true; } wxComboControl::~wxComboControl() { // as the button and the text control are the parent's children and not // ours, we have to delete them manually - they are not deleted - // automatically by wxWindows when we're deleted + // automatically by wxWidgets when we're deleted delete m_btn; delete m_text; @@ -271,7 +290,7 @@ wxComboControl::~wxComboControl() // ---------------------------------------------------------------------------- void wxComboControl::DoSetSize(int x, int y, - int width, int height, + int width, int WXUNUSED(height), int sizeFlags) { // combo height is always fixed @@ -282,8 +301,15 @@ wxSize wxComboControl::DoGetBestClientSize() const { wxSize sizeBtn = m_btn->GetBestSize(), sizeText = m_text->GetBestSize(); + wxCoord widthPopup = 0; + + if (m_popup) + { + widthPopup = m_popup->GetBestWidth(); + } - return wxSize(sizeBtn.x + sizeText.x, wxMax(sizeBtn.y, sizeText.y)); + return wxSize(wxMax(sizeText.x + g_comboMargin + sizeBtn.x, widthPopup), + wxMax(sizeBtn.y, sizeText.y)); } void wxComboControl::DoMoveWindow(int x, int y, int width, int height) @@ -297,12 +323,12 @@ void wxComboControl::DoMoveWindow(int x, int y, int width, int height) width -= rectBorders.x + rectBorders.width; height -= rectBorders.y + rectBorders.height; - wxSize sizeBtn = m_btn->GetSize(), - sizeText = m_text->GetSize(); + wxSize sizeBtn = m_btn->GetBestSize(); wxCoord wText = width - sizeBtn.x; - m_text->SetSize(x, y, wText, height); - m_btn->SetSize(x + wText, y, -1, height); + wxPoint p = GetParent() ? GetParent()->GetClientAreaOrigin() : wxPoint(0,0); + m_text->SetSize(x - p.x, y - p.y, wText, height); + m_btn->SetSize(x - p.x + wText, y - p.y, sizeBtn.x, height); } // ---------------------------------------------------------------------------- @@ -312,25 +338,51 @@ void wxComboControl::DoMoveWindow(int x, int y, int width, int height) bool wxComboControl::Enable(bool enable) { if ( !wxControl::Enable(enable) ) - return FALSE; + return false; m_btn->Enable(enable); m_text->Enable(enable); - return TRUE; + return true; } bool wxComboControl::Show(bool show) { if ( !wxControl::Show(show) ) - return FALSE; + return false; + + if (m_btn) + m_btn->Show(show); - m_btn->Show(show); - m_text->Show(show); + if (m_text) + m_text->Show(show); - return TRUE; + return true; } +#if wxUSE_TOOLTIPS +void wxComboControl::DoSetToolTip(wxToolTip *tooltip) +{ + wxControl::DoSetToolTip(tooltip); + + // Set tool tip for button and text box + if (m_text && m_btn) + { + if (tooltip) + { + const wxString &tip = tooltip->GetTip(); + m_text->SetToolTip(tip); + m_btn->SetToolTip(tip); + } + else + { + m_text->SetToolTip(NULL); + m_btn->SetToolTip(NULL); + } + } +} +#endif // wxUSE_TOOLTIPS + // ---------------------------------------------------------------------------- // popup window handling // ---------------------------------------------------------------------------- @@ -349,7 +401,7 @@ void wxComboControl::ShowPopup() // size and position the popup window correctly m_winPopup->SetSize(GetSize().x, - m_heightPopup == -1 ? control->GetBestSize().y + m_heightPopup == wxDefaultCoord ? control->GetBestSize().y : m_heightPopup); wxSize sizePopup = m_winPopup->GetClientSize(); control->SetSize(0, 0, sizePopup.x, sizePopup.y); @@ -365,11 +417,12 @@ void wxComboControl::ShowPopup() m_winPopup->PositionNearCombo(); // show it + m_popup->OnShow(); m_winPopup->Popup(m_text); m_text->SelectAll(); m_popup->SetSelection(m_text->GetValue()); - m_isPopupShown = TRUE; + m_isPopupShown = true; } void wxComboControl::HidePopup() @@ -379,7 +432,7 @@ void wxComboControl::HidePopup() m_winPopup->Dismiss(); - m_isPopupShown = FALSE; + m_isPopupShown = false; } void wxComboControl::OnSelect(const wxString& value) @@ -404,7 +457,7 @@ wxComboTextCtrl::wxComboTextCtrl(wxComboControl *combo, const wxString& value, long style, const wxValidator& validator) - : wxTextCtrl(combo->GetParent(), -1, value, + : wxTextCtrl(combo->GetParent(), wxID_ANY, value, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE | style, validator) @@ -467,7 +520,7 @@ void wxComboTextCtrl::OnKey(wxKeyEvent& event) // ---------------------------------------------------------------------------- wxComboListBox::wxComboListBox(wxComboControl *combo, int style) - : wxListBox(combo->GetPopupWindow(), -1, + : wxListBox(combo->GetPopupWindow(), wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, NULL, wxBORDER_SIMPLE | wxLB_INT_HEIGHT | style), @@ -487,7 +540,7 @@ bool wxComboListBox::SetSelection(const wxString& value) // always matches), but we want to show the first one in such case if ( value.empty() ) { - if ( GetCount() ) + if ( GetCount() > 0 ) { wxListBox::SetSelection(0); } @@ -496,30 +549,36 @@ bool wxComboListBox::SetSelection(const wxString& value) else if ( !FindItem(value) ) { // no match att all - return FALSE; + return false; } - return TRUE; + return true; } void wxComboListBox::OnSelect(wxCommandEvent& event) { - // first let the user code have the event - - // all fields are already filled by the listbox, just change the event - // type and send it to the combo - wxCommandEvent event2 = event; - event2.SetEventType(wxEVT_COMMAND_COMBOBOX_SELECTED); - event2.SetEventObject(m_combo); - event2.SetId(m_combo->GetId()); - m_combo->ProcessEvent(event2); - - // next update the combo and close the listbox - m_combo->OnSelect(event.GetString()); + if ( m_clicked ) + { + // first update the combo and close the listbox + m_combo->OnSelect(event.GetString()); + + // next let the user code have the event + + // all fields are already filled by the listbox, just change the event + // type and send it to the combo + wxCommandEvent event2 = event; + event2.SetEventType(wxEVT_COMMAND_COMBOBOX_SELECTED); + event2.SetEventObject(m_combo); + event2.SetId(m_combo->GetId()); + m_combo->ProcessEvent(event2); + } + //else: ignore the events resulting from just moving the mouse initially } void wxComboListBox::OnShow() { + // nobody clicked us yet + m_clicked = false; } bool wxComboListBox::PerformAction(const wxControlAction& action, @@ -532,18 +591,26 @@ bool wxComboListBox::PerformAction(const wxControlAction& action, // we don't let the listbox handle this as instead of just using the // single key presses, as usual, we use the text ctrl value as prefix // and this is done by wxComboControl itself - return TRUE; + return true; } return wxListBox::PerformAction(action, numArg, strArg); } +void wxComboListBox::OnLeftUp(wxMouseEvent& event) +{ + // we should dismiss the combo now + m_clicked = true; + + event.Skip(); +} + void wxComboListBox::OnMouseMove(wxMouseEvent& event) { // while a wxComboListBox is shown, it always has capture, so if it doesn't // we're about to go away anyhow (normally this shouldn't happen at all, // but I don't put assert here as it just might do on other platforms and - // it doesn't break anythign anyhow) + // it doesn't break anything anyhow) if ( this == wxWindow::GetCapture() ) { if ( HitTest(event.GetPosition()) == wxHT_WINDOW_INSIDE ) @@ -556,6 +623,12 @@ void wxComboListBox::OnMouseMove(wxMouseEvent& event) } } +wxCoord wxComboListBox::GetBestWidth() const +{ + wxSize size = wxListBox::GetBestSize(); + return size.x; +} + wxSize wxComboListBox::DoGetBestClientSize() const { // don't return size too big or we risk to not fit on the screen @@ -582,13 +655,44 @@ void wxComboBox::Init() m_lbox = (wxListBox *)NULL; } +wxComboBox::wxComboBox(wxWindow *parent, + wxWindowID id, + const wxString& value, + const wxPoint& pos, + const wxSize& size, + const wxArrayString& choices, + long style, + const wxValidator& validator, + const wxString& name) +{ + Init(); + + Create(parent, id, value, pos, size, choices, style, validator, name); +} + +bool wxComboBox::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) +{ + wxCArrayString chs(choices); + + return Create(parent, id, value, pos, size, chs.GetCount(), + chs.GetStrings(), style, validator, name); +} + bool wxComboBox::Create(wxWindow *parent, wxWindowID id, const wxString& value, const wxPoint& pos, const wxSize& size, int n, - const wxString *choices, + const wxString choices[], long style, const wxValidator& validator, const wxString& name) @@ -596,7 +700,7 @@ bool wxComboBox::Create(wxWindow *parent, if ( !wxComboControl::Create(parent, id, value, pos, size, style, validator, name) ) { - return FALSE; + return false; } wxComboListBox *combolbox = @@ -606,7 +710,7 @@ bool wxComboBox::Create(wxWindow *parent, SetPopupControl(combolbox); - return TRUE; + return true; } wxComboBox::~wxComboBox() @@ -657,7 +761,7 @@ long wxComboBox::GetInsertionPoint() const return GetText()->GetInsertionPoint(); } -long wxComboBox::GetLastPosition() const +wxTextPos wxComboBox::GetLastPosition() const { return GetText()->GetLastPosition(); } @@ -689,36 +793,46 @@ void wxComboBox::SetEditable(bool editable) void wxComboBox::Clear() { GetLBox()->Clear(); + GetText()->SetValue(wxEmptyString); } void wxComboBox::Delete(int n) { + wxCHECK_RET( IsValid(n), _T("invalid index in wxComboBox::Delete") ); + + if (GetSelection() == n) + GetText()->SetValue(wxEmptyString); + GetLBox()->Delete(n); } -int wxComboBox::GetCount() const +size_t wxComboBox::GetCount() const { return GetLBox()->GetCount(); } wxString wxComboBox::GetString(int n) const { + wxCHECK_MSG( IsValid(n), wxEmptyString, _T("invalid index in wxComboBox::GetString") ); + return GetLBox()->GetString(n); } void wxComboBox::SetString(int n, const wxString& s) { + wxCHECK_RET( IsValid(n), _T("invalid index in wxComboBox::SetString") ); + GetLBox()->SetString(n, s); } -int wxComboBox::FindString(const wxString& s) const +int wxComboBox::FindString(const wxString& s, bool bCase) const { - return GetLBox()->FindString(s); + return GetLBox()->FindString(s, bCase); } -void wxComboBox::Select(int n) +void wxComboBox::SetSelection(int n) { - wxCHECK_RET( (n >= 0) && (n < GetCount()), _T("invalid combobox index") ); + wxCHECK_RET( IsValid(n), _T("invalid index in wxComboBox::Select") ); GetLBox()->SetSelection(n); GetText()->SetValue(GetLBox()->GetString(n)); @@ -726,8 +840,15 @@ void wxComboBox::Select(int n) int wxComboBox::GetSelection() const { +#if 1 // FIXME:: What is the correct behavior? // if the current value isn't one of the listbox strings, return -1 + return GetLBox()->GetSelection(); +#else + // Why oh why is this done this way? + // It is not because the value displayed in the text can be found + // in the list that it is the item that is selected! return FindString(GetText()->GetValue()); +#endif } int wxComboBox::DoAppend(const wxString& item) @@ -735,6 +856,18 @@ int wxComboBox::DoAppend(const wxString& item) return GetLBox()->Append(item); } +int wxComboBox::DoInsert(const wxString& item, int pos) +{ + wxCHECK_MSG(!(GetWindowStyle() & wxCB_SORT), -1, wxT("can't insert into sorted list")); + wxCHECK_MSG(IsValidInsert(pos), -1, wxT("invalid index")); + + if ((size_t)pos == GetCount()) + return DoAppend(item); + + GetLBox()->Insert(item, pos); + return pos; +} + void wxComboBox::DoSetItemClientData(int n, void* clientData) { GetLBox()->SetClientData(n, clientData); @@ -755,11 +888,74 @@ wxClientData* wxComboBox::DoGetItemClientObject(int n) const return GetLBox()->GetClientObject(n); } +bool wxComboBox::IsEditable() const +{ + return GetText() != NULL && (!HasFlag(wxCB_READONLY) || GetText()->IsEditable()); +} + +void wxComboBox::Undo() +{ + if (IsEditable()) + GetText()->Undo(); +} + +void wxComboBox::Redo() +{ + if (IsEditable()) + GetText()->Redo(); +} + +void wxComboBox::SelectAll() +{ + GetText()->SelectAll(); +} + +bool wxComboBox::CanCopy() const +{ + if (GetText() != NULL) + return GetText()->CanCopy(); + else + return false; +} + +bool wxComboBox::CanCut() const +{ + if (GetText() != NULL) + return GetText()->CanCut(); + else + return false; +} + +bool wxComboBox::CanPaste() const +{ + if (IsEditable()) + return GetText()->CanPaste(); + else + return false; +} + +bool wxComboBox::CanUndo() const +{ + if (IsEditable()) + return GetText()->CanUndo(); + else + return false; +} + +bool wxComboBox::CanRedo() const +{ + if (IsEditable()) + return GetText()->CanRedo(); + else + return false; +} + + // ---------------------------------------------------------------------------- // input handling // ---------------------------------------------------------------------------- -void wxComboControl::OnKey(wxCommandEvent& event) +void wxComboControl::OnKey(wxKeyEvent& event) { if ( m_isPopupShown ) { @@ -776,14 +972,14 @@ bool wxComboControl::PerformAction(const wxControlAction& action, long numArg, const wxString& strArg) { - bool processed = FALSE; + bool processed = false; if ( action == wxACTION_COMBOBOX_POPUP ) { if ( !m_isPopupShown ) { ShowPopup(); - processed = TRUE; + processed = true; } } else if ( action == wxACTION_COMBOBOX_DISMISS ) @@ -792,7 +988,7 @@ bool wxComboControl::PerformAction(const wxControlAction& action, { HidePopup(); - processed = TRUE; + processed = true; } } @@ -802,7 +998,7 @@ bool wxComboControl::PerformAction(const wxControlAction& action, return wxControl::PerformAction(action, numArg, strArg); } - return TRUE; + return true; } // ---------------------------------------------------------------------------- @@ -814,7 +1010,7 @@ wxStdComboBoxInputHandler::wxStdComboBoxInputHandler(wxInputHandler *inphand) { } -bool wxStdComboBoxInputHandler::HandleKey(wxControl *control, +bool wxStdComboBoxInputHandler::HandleKey(wxInputConsumer *consumer, const wxKeyEvent& event, bool pressed) { @@ -832,15 +1028,15 @@ bool wxStdComboBoxInputHandler::HandleKey(wxControl *control, break; } - if ( !!action ) + if ( !action.IsEmpty() ) { - control->PerformAction(action); + consumer->PerformAction(action); - return TRUE; + return true; } } - return wxStdInputHandler::HandleKey(control, event, pressed); + return wxStdInputHandler::HandleKey(consumer, event, pressed); } #endif // wxUSE_COMBOBOX