diff --git a/include/wx/msw/dcclient.h b/include/wx/msw/dcclient.h index 5568f0a6a2..f3b6018ef1 100644 --- a/include/wx/msw/dcclient.h +++ b/include/wx/msw/dcclient.h @@ -51,8 +51,6 @@ public: class WXDLLEXPORT wxClientDC : public wxWindowDC { - DECLARE_DYNAMIC_CLASS(wxClientDC) - public: wxClientDC(); @@ -60,12 +58,16 @@ public: wxClientDC(wxWindow *win); virtual ~wxClientDC(); + +protected: + void InitDC(); + +private: + DECLARE_DYNAMIC_CLASS(wxClientDC) }; -class WXDLLEXPORT wxPaintDC : public wxWindowDC +class WXDLLEXPORT wxPaintDC : public wxClientDC { - DECLARE_DYNAMIC_CLASS(wxPaintDC) - public: wxPaintDC(); @@ -82,6 +84,9 @@ protected: // find the entry for this DC in the cache (keyed by the window) wxPaintDCInfo *FindInCache(size_t *index = NULL) const; + +private: + DECLARE_DYNAMIC_CLASS(wxPaintDC) }; #endif diff --git a/include/wx/univ/inphand.h b/include/wx/univ/inphand.h index 51393e7d0e..3e92a93673 100644 --- a/include/wx/univ/inphand.h +++ b/include/wx/univ/inphand.h @@ -265,7 +265,7 @@ public: }; // ---------------------------------------------------------------------------- -// wxStdCheckListBoxHandler +// wxStdCheckListBoxInputHandler // ---------------------------------------------------------------------------- class WXDLLEXPORT wxStdCheckListboxInputHandler : public wxStdListboxInputHandler @@ -280,4 +280,23 @@ public: const wxMouseEvent& event); }; +// ---------------------------------------------------------------------------- +// wxStdTextCtrlInputHandler: this control handles only the mouse/kbd actions +// common to Win32 and GTK, platform-specific things are implemented elsewhere +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxStdTextCtrlInputHandler : public wxStdInputHandler +{ +public: + wxStdTextCtrlInputHandler(wxInputHandler *inphand); + + virtual bool HandleKey(wxControl *control, + const wxKeyEvent& event, + bool pressed); + virtual bool HandleMouse(wxControl *control, + const wxMouseEvent& event); + virtual bool HandleMouseMove(wxControl *control, + const wxMouseEvent& event); +}; + #endif // _WX_UNIV_INPHAND_H_ diff --git a/include/wx/univ/renderer.h b/include/wx/univ/renderer.h index 77fbfed640..73a0a3a535 100644 --- a/include/wx/univ/renderer.h +++ b/include/wx/univ/renderer.h @@ -97,6 +97,12 @@ public: int indexAccel = -1, wxRect *rectBounds = NULL) = 0; + // draw a line of the text ctrl + virtual void DrawTextLine(wxDC& dc, + const wxString& text, + const wxRect &rect, + int flags = 0) = 0; + // draw the border and optionally return the rectangle containing the // region inside the border virtual void DrawBorder(wxDC& dc, @@ -291,6 +297,11 @@ public: wxRect *rectBounds = NULL) { m_renderer->DrawButtonLabel(dc, label, image, rect, flags, align, indexAccel, rectBounds); } + virtual void DrawTextLine(wxDC& dc, + const wxString& text, + const wxRect &rect, + int flags = 0) + { m_renderer->DrawTextLine(dc, text, rect, flags); } virtual void DrawBorder(wxDC& dc, wxBorder border, const wxRect& rect, @@ -412,6 +423,7 @@ public: // operations void DrawLabel(const wxBitmap& bitmap = wxNullBitmap, wxCoord marginX = 0, wxCoord marginY = 0); + void DrawTextLine(const wxString& text); void DrawItems(const wxListBox *listbox, size_t itemFirst, size_t itemLast); void DrawCheckItems(const wxCheckListBox *listbox, diff --git a/include/wx/univ/setup.h b/include/wx/univ/setup.h index 1032baa1c4..55d9dd069e 100644 --- a/include/wx/univ/setup.h +++ b/include/wx/univ/setup.h @@ -20,7 +20,7 @@ #define WXWIN_COMPATIBILITY 0 #define wxICON_IS_BITMAP 0 #define wxFONT_SIZE_COMPATIBILITY 0 -#define wxDIALOG_UNIT_COMPATIBILITY 1 +#define wxDIALOG_UNIT_COMPATIBILITY 0 #define wxUSE_DEBUG_CONTEXT 0 #define wxUSE_MEMORY_TRACING 0 #define wxUSE_GLOBAL_MEMORY_OPERATORS 0 @@ -68,7 +68,7 @@ #define wxUSE_CONTROLS 1 #define wxUSE_BUTTON 1 #define wxUSE_BMPBUTTON 1 -#define wxUSE_CARET 0 +#define wxUSE_CARET 1 #define wxUSE_CHECKBOX 1 #define wxUSE_CHECKLISTBOX 1 #define wxUSE_CHOICE 0 @@ -88,6 +88,7 @@ #define wxUSE_STATTEXT 1 #define wxUSE_STATBMP 1 #define wxUSE_STATUSBAR 0 +#define wxUSE_TEXTCTRL 1 #define wxUSE_TOOLTIPS 0 #define wxUSE_TREECTRL 0 diff --git a/include/wx/univ/textctrl.h b/include/wx/univ/textctrl.h index 49643985de..96d07ff008 100644 --- a/include/wx/univ/textctrl.h +++ b/include/wx/univ/textctrl.h @@ -22,6 +22,32 @@ class WXDLLEXPORT wxCaret; // wxTextCtrl actions // ---------------------------------------------------------------------------- +// cursor movement and also selection and delete operations +#define wxACTION_TEXT_HOME _T("home") +#define wxACTION_TEXT_END _T("end") +#define wxACTION_TEXT_LEFT _T("left") +#define wxACTION_TEXT_RIGHT _T("right") +#define wxACTION_TEXT_UP _T("up") +#define wxACTION_TEXT_DOWN _T("down") +#define wxACTION_TEXT_WORD_LEFT _T("wordleft") +#define wxACTION_TEXT_WORD_RIGHT _T("wordright") +#define wxACTION_TEXT_PAGE_UP _T("pageup") +#define wxACTION_TEXT_PAGE_DOWN _T("pagedown") + +// clipboard operations +#define wxACTION_TEXT_COPY _T("copy") +#define wxACTION_TEXT_CUT _T("cut") +#define wxACTION_TEXT_PASTE _T("paste") + +// insert text at the cursor position: the text is in strArg of PerformAction +#define wxACTION_TEXT_INSERT _T("insert") + +// if the action starts with either of these prefixes and the rest of the +// string is one of the movement commands, it means to select/delete text from +// the current cursor position to the new one +#define wxACTION_TEXT_PREFIX_SEL _T("sel") +#define wxACTION_TEXT_PREFIX_DEL _T("del") + // ---------------------------------------------------------------------------- // wxTextCtrl // ---------------------------------------------------------------------------- @@ -125,6 +151,10 @@ public: virtual void ShowCaret(bool show = TRUE); void HideCaret() { ShowCaret(FALSE); } + // helpers for cursor movement + long GetWordStart() const; + long GetWordEnd() const; + // implementation only from now on // ------------------------------- @@ -151,6 +181,9 @@ protected: // is this a single-line control? bool IsSingleLine() const { return !(GetWindowStyle() & wxTE_MULTILINE); } + // event handlers + void OnChar(wxKeyEvent& event); + private: // the value (may be only part of it for the multiline controls) wxString m_value; diff --git a/samples/univ/univ.cpp b/samples/univ/univ.cpp index 31b60945d4..e1e76cfd0f 100644 --- a/samples/univ/univ.cpp +++ b/samples/univ/univ.cpp @@ -115,6 +115,7 @@ protected: void OnCheckBox(wxCommandEvent& event); void OnRadioBox(wxCommandEvent& event); void OnListBox(wxCommandEvent& event); + void OnTextChange(wxCommandEvent& event); void OnLeftUp(wxMouseEvent& event); private: @@ -157,6 +158,7 @@ BEGIN_EVENT_TABLE(MyUnivFrame, wxFrame) EVT_CHECKBOX(-1, MyUnivFrame::OnCheckBox) EVT_RADIOBUTTON(-1, MyUnivFrame::OnRadioBox) EVT_LISTBOX(-1, MyUnivFrame::OnListBox) + EVT_TEXT(-1, MyUnivFrame::OnTextChange) EVT_LEFT_UP(MyUnivFrame::OnLeftUp) END_EVENT_TABLE() @@ -225,18 +227,18 @@ MyUnivFrame::MyUnivFrame(const wxString& title) { SetBackgroundColour(wxGetApp().GetBgColour()); - wxStaticText *text; - new wxStaticText(this, _T("Test static text"), wxPoint(10, 10)); - new wxStaticText(this, -1, +#if 0 + new wxStaticText(this, _T("&Multi line\n(and very very very very long)\nstatic text"), - wxPoint(210, 10), wxDefaultSize, wxBORDER_SUNKEN); + wxPoint(210, 10)); (new wxStaticText(this, _T("&Disabled text"), wxPoint(10, 30)))->Disable(); new wxStaticLine(this, wxPoint(190, 10), 50, wxLI_VERTICAL); - text = new wxStaticText(this, _T("Demo of &border styles:"), wxPoint(10, 60)); + wxStaticText *text = new wxStaticText(this, _T("Demo of &border styles:"), + wxPoint(10, 60)); text->SetFont(*wxITALIC_FONT); text->SetBackgroundColour(*wxWHITE); text->SetForegroundColour(*wxBLUE); @@ -368,6 +370,10 @@ MyUnivFrame::MyUnivFrame(const wxString& title) new wxTextCtrl(this, -1, _T("Hello, Universe!"), wxPoint(550, 150), wxDefaultSize); +#else // 1 + new wxTextCtrl(this, -1, _T("Hello, Universe!"), + wxPoint(10, 50), wxSize(200, -1)); +#endif // 0/1 } void MyUnivFrame::OnButton(wxCommandEvent& event) @@ -399,6 +405,11 @@ void MyUnivFrame::OnListBox(wxCommandEvent& event) wxLogDebug(_T("Listbox item %d selected."), event.GetInt()); } +void MyUnivFrame::OnTextChange(wxCommandEvent& event) +{ + wxLogDebug(_T("Text control value changed: now '%s'"), event.GetString()); +} + void MyUnivFrame::OnLeftUp(wxMouseEvent& event) { if ( event.ControlDown() ) diff --git a/src/generic/scrolwin.cpp b/src/generic/scrolwin.cpp index 5ab6a0e175..da08db1170 100644 --- a/src/generic/scrolwin.cpp +++ b/src/generic/scrolwin.cpp @@ -483,16 +483,6 @@ void wxScrollHelper::DoPrepareDC(wxDC& dc) dc.SetDeviceOrigin( -m_xScrollPosition * m_xScrollPixelsPerLine, -m_yScrollPosition * m_yScrollPixelsPerLine ); dc.SetUserScale( m_scaleX, m_scaleY ); - - // now done in wxPaintDC itself -#if 0 - // for wxUniversal we need to set the clipping region to avoid overwriting - // the scrollbars with the user drawing - wxSize size = m_win->GetClientSize(); - dc.SetClippingRegion(m_xScrollPosition * m_xScrollPixelsPerLine, - m_yScrollPosition * m_yScrollPixelsPerLine, - size.x, size.y); -#endif // 0 } void wxScrollHelper::GetScrollPixelsPerUnit (int *x_unit, int *y_unit) const diff --git a/src/gtk/dcclient.cpp b/src/gtk/dcclient.cpp index 6ca124e19e..87ee3bdcb3 100644 --- a/src/gtk/dcclient.cpp +++ b/src/gtk/dcclient.cpp @@ -2223,7 +2223,7 @@ wxPaintDC::wxPaintDC( wxWindow *win ) #if USE_PAINT_REGION if (!win->m_clipPaintRegion) return; - + m_paintClippingRegion = win->GetUpdateRegion(); GdkRegion *region = m_paintClippingRegion.GetRegion(); if ( region ) diff --git a/src/gtk1/dcclient.cpp b/src/gtk1/dcclient.cpp index 6ca124e19e..87ee3bdcb3 100644 --- a/src/gtk1/dcclient.cpp +++ b/src/gtk1/dcclient.cpp @@ -2223,7 +2223,7 @@ wxPaintDC::wxPaintDC( wxWindow *win ) #if USE_PAINT_REGION if (!win->m_clipPaintRegion) return; - + m_paintClippingRegion = win->GetUpdateRegion(); GdkRegion *region = m_paintClippingRegion.GetRegion(); if ( region ) diff --git a/src/msw/dcclient.cpp b/src/msw/dcclient.cpp index 46642e078f..b2edbb6003 100644 --- a/src/msw/dcclient.cpp +++ b/src/msw/dcclient.cpp @@ -62,9 +62,9 @@ WX_DEFINE_OBJARRAY(wxArrayDCInfo); // macros // ---------------------------------------------------------------------------- - IMPLEMENT_DYNAMIC_CLASS(wxWindowDC, wxDC) - IMPLEMENT_DYNAMIC_CLASS(wxClientDC, wxWindowDC) - IMPLEMENT_DYNAMIC_CLASS(wxPaintDC, wxWindowDC) +IMPLEMENT_DYNAMIC_CLASS(wxWindowDC, wxDC) +IMPLEMENT_DYNAMIC_CLASS(wxClientDC, wxWindowDC) +IMPLEMENT_DYNAMIC_CLASS(wxPaintDC, wxClientDC) // ---------------------------------------------------------------------------- // global variables @@ -90,33 +90,33 @@ static PAINTSTRUCT g_paintStruct; wxWindowDC::wxWindowDC() { - m_canvas = NULL; + m_canvas = NULL; } -wxWindowDC::wxWindowDC(wxWindow *the_canvas) +wxWindowDC::wxWindowDC(wxWindow *canvas) { - m_canvas = the_canvas; - m_hDC = (WXHDC) ::GetWindowDC(GetWinHwnd(the_canvas) ); - m_hDCCount++; + m_canvas = canvas; + m_hDC = (WXHDC) ::GetWindowDC(GetWinHwnd(canvas) ); + m_hDCCount++; - SetBackground(wxBrush(m_canvas->GetBackgroundColour(), wxSOLID)); + SetBackground(wxBrush(m_canvas->GetBackgroundColour(), wxSOLID)); } wxWindowDC::~wxWindowDC() { - if (m_canvas && m_hDC) - { - SelectOldObjects(m_hDC); - - if ( !::ReleaseDC(GetWinHwnd(m_canvas), GetHdc()) ) + if (m_canvas && m_hDC) { - wxLogLastError(wxT("ReleaseDC")); + SelectOldObjects(m_hDC); + + if ( !::ReleaseDC(GetWinHwnd(m_canvas), GetHdc()) ) + { + wxLogLastError(wxT("ReleaseDC")); + } + + m_hDC = 0; } - m_hDC = 0; - } - - m_hDCCount--; + m_hDCCount--; } // ---------------------------------------------------------------------------- @@ -125,31 +125,45 @@ wxWindowDC::~wxWindowDC() wxClientDC::wxClientDC() { - m_canvas = NULL; + m_canvas = NULL; } -wxClientDC::wxClientDC(wxWindow *the_canvas) +wxClientDC::wxClientDC(wxWindow *canvas) { - m_canvas = the_canvas; - m_hDC = (WXHDC) ::GetDC(GetWinHwnd(the_canvas)); + // init the DC + m_canvas = canvas; + m_hDC = (WXHDC) ::GetDC(GetWinHwnd(canvas)); - // the background mode is only used for text background - // and is set in DrawText() to OPAQUE as required, other- - // wise always TRANSPARENT, RR - ::SetBkMode( GetHdc(), TRANSPARENT ); + // (re)set the DC parameters + InitDC(); +} - SetBackground(wxBrush(m_canvas->GetBackgroundColour(), wxSOLID)); +void wxClientDC::InitDC() +{ + // the background mode is only used for text background and is set in + // DrawText() to OPAQUE as required, otherwise always TRANSPARENT (RR) + ::SetBkMode(GetHdc(), TRANSPARENT); + + SetBackground(wxBrush(m_canvas->GetBackgroundColour(), wxSOLID)); + + // clip the DC to avoid overwriting the non client area +#ifdef __WXUNIVERSAL__ + wxPoint ptOrigin = m_canvas->GetClientAreaOrigin(); + SetDeviceOrigin(ptOrigin.x, ptOrigin.y); + wxSize size = m_canvas->GetClientSize(); + SetClippingRegion(wxPoint(0, 0), size); +#endif // __WXUNIVERSAL__ } wxClientDC::~wxClientDC() { - if ( m_canvas && GetHdc() ) - { - SelectOldObjects(m_hDC); + if ( m_canvas && GetHdc() ) + { + SelectOldObjects(m_hDC); - ::ReleaseDC(GetWinHwnd(m_canvas), GetHdc()); - m_hDC = 0; - } + ::ReleaseDC(GetWinHwnd(m_canvas), GetHdc()); + m_hDC = 0; + } } // ---------------------------------------------------------------------------- @@ -208,12 +222,8 @@ wxPaintDC::wxPaintDC(wxWindow *canvas) ms_cache.Add(new wxPaintDCInfo(m_canvas, this)); } - // the background mode is only used for text background - // and is set in DrawText() to OPAQUE as required, other- - // wise always TRANSPARENT, RR - ::SetBkMode( GetHdc(), TRANSPARENT ); - - SetBackground(wxBrush(m_canvas->GetBackgroundColour(), wxSOLID)); + // (re)set the DC parameters + InitDC(); } wxPaintDC::~wxPaintDC() diff --git a/src/msw/window.cpp b/src/msw/window.cpp index 8935b07ad4..cbe563acfe 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -1375,7 +1375,7 @@ int wxWindowMSW::GetCharWidth() const // +1 is needed because Windows apparently adds it when calculating the // dialog units size in pixels #if wxDIALOG_UNIT_COMPATIBILITY - return wxGetTextMetrics(this).tmAveCharWidth ; + return wxGetTextMetrics(this).tmAveCharWidth; #else return wxGetTextMetrics(this).tmAveCharWidth + 1; #endif @@ -2992,19 +2992,28 @@ bool wxWindowMSW::HandlePaint() wxLogLastError(wxT("GetUpdateRgn")); m_updateRegion = wxRegion((WXHRGN) hRegion); -#else +#else // Win16 RECT updateRect; - ::GetUpdateRect(GetHwnd(), & updateRect, FALSE); + ::GetUpdateRect(GetHwnd(), &updateRect, FALSE); m_updateRegion = wxRegion(updateRect.left, updateRect.top, updateRect.right - updateRect.left, updateRect.bottom - updateRect.top); -#endif +#endif // Win32/16 wxPaintEvent event(m_windowId); event.SetEventObject(this); - return GetEventHandler()->ProcessEvent(event); + bool processed = GetEventHandler()->ProcessEvent(event); + + // note that we must generate NC event after the normal one as otherwise + // BeginPaint() will happily overwrite our decorations with the background + // colour + wxNcPaintEvent eventNc(m_windowId); + eventNc.SetEventObject(this); + GetEventHandler()->ProcessEvent(eventNc); + + return processed; } // Can be called from an application's OnPaint handler diff --git a/src/univ/listbox.cpp b/src/univ/listbox.cpp index bc72bf2c12..9def6eb3b9 100644 --- a/src/univ/listbox.cpp +++ b/src/univ/listbox.cpp @@ -132,9 +132,8 @@ int wxListBox::DoAppend(const wxString& item) if ( HasHorzScrollbar() ) { // has the max width increased? - wxClientDC dc(this); wxCoord width; - dc.GetTextExtent(item, &width, NULL); + GetTextExtent(item, &width, NULL); if ( width > m_maxWidth ) { m_maxWidth = width; @@ -523,12 +522,12 @@ void wxListBox::DoDraw(wxControlRenderer *renderer) // adjust the DC to account for scrolling wxDC& dc = renderer->GetDC(); PrepareDC(dc); + dc.SetFont(GetFont()); // get the items which must be redrawn wxCoord lineHeight = GetLineHeight(); wxRegion rgnUpdate = GetUpdateRegion(); rgnUpdate.Intersect(GetClientRect()); - //dc.SetClippingRegion(rgnUpdate); wxRect rectUpdate = rgnUpdate.GetBox(); int yTop, yBottom; @@ -575,8 +574,7 @@ bool wxListBox::SetFont(const wxFont& font) void wxListBox::CalcItemsPerPage() { - m_lineHeight = GetRenderer()-> - GetListboxItemHeight(wxClientDC(this).GetCharHeight()); + m_lineHeight = GetRenderer()->GetListboxItemHeight(GetCharHeight()); m_itemsPerPage = GetClientSize().y / m_lineHeight; } @@ -634,8 +632,6 @@ void wxListBox::DoSetFirstItem(int n) wxSize wxListBox::DoGetBestClientSize() const { - wxClientDC dc(wxConstCast(this, wxListBox)); - wxCoord width = 0, height = 0; @@ -643,7 +639,7 @@ wxSize wxListBox::DoGetBestClientSize() const for ( size_t n = 0; n < count; n++ ) { wxCoord w,h; - dc.GetTextExtent(m_strings[n], &w, &h); + GetTextExtent(m_strings[n], &w, &h); if ( w > width ) width = w; @@ -656,10 +652,10 @@ wxSize wxListBox::DoGetBestClientSize() const if ( !width ) width = 100; else - width += 3*dc.GetCharWidth(); + width += 3*GetCharWidth(); if ( !height ) - height = dc.GetCharHeight(); + height = GetCharHeight(); // we need the height of the entire listbox, not just of one line height *= wxMax(count, 7); diff --git a/src/univ/radiobox.cpp b/src/univ/radiobox.cpp index 34af566dc4..4f7081b0ea 100644 --- a/src/univ/radiobox.cpp +++ b/src/univ/radiobox.cpp @@ -45,8 +45,8 @@ // constants // ---------------------------------------------------------------------------- -static const int BUTTON_BORDER_X = 3; -static const int BUTTON_BORDER_Y = 3; +static const int BUTTON_BORDER_X = 0; +static const int BUTTON_BORDER_Y = 0; static const int BOX_BORDER_X = 0; static const int BOX_BORDER_Y = 0; diff --git a/src/univ/renderer.cpp b/src/univ/renderer.cpp index 267c373776..6bae292186 100644 --- a/src/univ/renderer.cpp +++ b/src/univ/renderer.cpp @@ -575,7 +575,10 @@ void wxControlRenderer::DoDrawItems(const wxListBox *lbox, // note that SetClippingRegion() needs the physical (unscrolled) // coordinates while we use the logical (scrolled) ones for the drawing // itself - wxRect rect = lbox->GetClientRect(); + wxRect rect; + wxSize size = lbox->GetClientSize(); + rect.width = size.x; + rect.height = size.y; // keep the text inside the client rect or we will overwrite the vertical // scrollbar for the long strings @@ -626,3 +629,11 @@ void wxControlRenderer::DoDrawItems(const wxListBox *lbox, rect.y += lineHeight; } } + +void wxControlRenderer::DrawTextLine(const wxString& text) +{ + m_dc.SetFont(m_window->GetFont()); + m_dc.SetTextForeground(m_window->GetForegroundColour()); + + m_renderer->DrawTextLine(m_dc, text, m_rect, m_window->GetStateFlags()); +} diff --git a/src/univ/textctrl.cpp b/src/univ/textctrl.cpp index 959a8afcd4..f5be3682e8 100644 --- a/src/univ/textctrl.cpp +++ b/src/univ/textctrl.cpp @@ -46,6 +46,7 @@ // ============================================================================ BEGIN_EVENT_TABLE(wxTextCtrl, wxControl) + EVT_CHAR(OnChar) END_EVENT_TABLE() IMPLEMENT_DYNAMIC_CLASS(wxTextCtrl, wxControl) @@ -80,7 +81,8 @@ bool wxTextCtrl::Create(wxWindow *parent, return FALSE; } - wxCaret *caret = new wxCaret(this, 1, GetCharHeight()); + // FIXME use renderer + wxCaret *caret = new wxCaret(this, 2, GetCharHeight()); SetCaret(caret); caret->Show(); @@ -98,6 +100,9 @@ bool wxTextCtrl::Create(wxWindow *parent, void wxTextCtrl::SetValue(const wxString& value) { + if ( m_value == value ) + return; + m_value = value; if ( IsSingleLine() ) @@ -108,6 +113,8 @@ void wxTextCtrl::SetValue(const wxString& value) { SetInsertionPoint(0); } + + Refresh(); } wxString wxTextCtrl::GetValue() const @@ -120,15 +127,38 @@ void wxTextCtrl::Clear() SetValue(_T("")); } -void wxTextCtrl::Replace(long from, long to, const wxString& value) +void wxTextCtrl::Replace(long from, long to, const wxString& text) { - wxFAIL_MSG(_T("not implemented")); + wxCHECK_RET( from >= 0 && to >= 0 && from <= to, + _T("invalid range in wxTextCtrl::Replace") ); + + // replace the part of the text with the new value + wxString valueNew(m_value, (size_t)from); + valueNew += text; + if ( (unsigned long)to < m_value.length() ) + { + valueNew += m_value.c_str() + (size_t)to; + } + m_value = valueNew; // update current position + SetInsertionPoint(from + text.length()); + + // FIXME shouldn't refresh everything of course + Refresh(); } void wxTextCtrl::Remove(long from, long to) { + if ( from > to ) + { + // Replace() only works with correctly ordered arguments, so exchange + // them + long tmp = from; + from = to; + to = tmp; + } + Replace(from, to, _T("")); } @@ -149,11 +179,27 @@ void wxTextCtrl::AppendText(const wxString& text) void wxTextCtrl::SetInsertionPoint(long pos) { - HideCaret(); + wxCHECK_RET( pos >= 0 && pos <= GetLastPosition(), + _T("insertion poitn position out of range") ); - m_curPos = pos; + if ( pos != m_curPos ) + { + HideCaret(); - ShowCaret(); + m_curPos = pos; + + if ( IsSingleLine() ) + { + m_curLine = 0; + m_curRow = m_curPos; + } + else // multi line + { + wxFAIL_MSG(_T("unimplemented for multi line")); + } + + ShowCaret(); + } } void wxTextCtrl::SetInsertionPointEnd() @@ -322,6 +368,82 @@ void wxTextCtrl::ShowPosition(long pos) wxFAIL_MSG(_T("not implemented")); } +// ---------------------------------------------------------------------------- +// word stuff +// ---------------------------------------------------------------------------- + +/* + TODO: we could have (easy to do) vi-like options for word movement, i.e. + distinguish between inlusive/exclusive words and between words and + WORDS (in vim sense) and also, finally, make the set of characters + which make up a word configurable - currently we use the exclusive + WORDS only (coincidentally, this is what Windows edit control does) + + For future references, here is what vim help says: + + A word consists of a sequence of letters, digits and underscores, or + a sequence of other non-blank characters, separated with white space + (spaces, tabs, ). This can be changed with the 'iskeyword' + option. + + A WORD consists of a sequence of non-blank characters, separated with + white space. An empty line is also considered to be a word and a + WORD. + */ + +static inline bool IsWordChar(wxChar ch) +{ + return !wxIsspace(ch); +} + +long wxTextCtrl::GetWordStart() const +{ + if ( m_curPos == -1 || m_curPos == 0 ) + return 0; + + // start at the previous position + const wxChar *p0 = m_value.c_str(); + const wxChar *p = p0 + m_curPos - 1; + + // find the end of the previous word + while ( (p > p0) && !IsWordChar(*p) ) + p--; + + // now find the beginning of this word + while ( (p > p0) && IsWordChar(*p) ) + p--; + + // we might have gone too far + if ( !IsWordChar(*p) ) + p++; + + return p - p0; +} + +long wxTextCtrl::GetWordEnd() const +{ + if ( m_curPos == -1 ) + return 0; + + // start at the current position + const wxChar *p0 = m_value.c_str(); + const wxChar *p = p0 + m_curPos; + + // find the start of the next word + while ( *p && !IsWordChar(*p) ) + p++; + + // now find the end of it + while ( *p && IsWordChar(*p) ) + p++; + + // and find the start of the next word + while ( *p && !IsWordChar(*p) ) + p++; + + return p - p0; +} + // ---------------------------------------------------------------------------- // clipboard stuff // ---------------------------------------------------------------------------- @@ -381,6 +503,7 @@ wxSize wxTextCtrl::DoGetBestClientSize() const h = hChar; w += 2*wChar; + h += hChar / 2; return wxSize(w, h); } @@ -394,7 +517,7 @@ void wxTextCtrl::DoDraw(wxControlRenderer *renderer) if ( IsSingleLine() ) { // just redraw everything - renderer->GetDC().DrawText(m_value, 0, 0); + renderer->DrawTextLine(m_value); } else { @@ -416,7 +539,8 @@ void wxTextCtrl::ShowCaret(bool show) wxCoord x; GetTextExtent(textBeforeCaret, &x, NULL); - caret->Move(x + 1, 0); + // FIXME: use renderer + caret->Move(x + 3, 3); } } } @@ -430,11 +554,194 @@ wxString wxTextCtrl::GetInputHandlerType() const return wxINP_HANDLER_TEXTCTRL; } -bool wxTextCtrl::PerformAction(const wxControlAction& action, +bool wxTextCtrl::PerformAction(const wxControlAction& actionOrig, long numArg, const wxString& strArg) { - return wxControl::PerformAction(action, numArg, strArg); + bool textChanged = FALSE; + + wxString action; + bool del = FALSE; + if ( actionOrig.StartsWith(wxACTION_TEXT_PREFIX_DEL, &action) ) + { + del = TRUE; + } + else // not selection nor delete action + { + action = actionOrig; + } + + long newPos = -1; + if ( action == wxACTION_TEXT_HOME ) + { + newPos = m_curPos - m_curRow; + } + else if ( action == wxACTION_TEXT_END ) + { + newPos = m_curPos + GetLineLength(m_curLine) - m_curRow; + } + else if ( action == wxACTION_TEXT_LEFT ) + { + newPos = m_curPos - 1; + } + else if ( action == wxACTION_TEXT_WORD_LEFT ) + { + newPos = GetWordStart(); + } + else if ( action == wxACTION_TEXT_RIGHT ) + { + newPos = m_curPos + 1; + } + else if ( action == wxACTION_TEXT_WORD_RIGHT ) + { + newPos = GetWordEnd(); + } + else if ( action == wxACTION_TEXT_INSERT ) + { + if ( !strArg.empty() ) + { + WriteText(strArg); + + textChanged = TRUE; + } + } + else + { + return wxControl::PerformAction(action, numArg, strArg); + } + + if ( newPos != -1 ) + { + // bring the new position into the range + if ( newPos < 0 ) + newPos = 0; + + long posLast = GetLastPosition(); + if ( newPos > posLast ) + newPos = posLast; + + if ( del ) + { + // delete everything between current opsition and the new one + if ( m_curPos != newPos ) + { + Remove(m_curPos, newPos); + + textChanged = TRUE; + } + } + else + { + // just go there + SetInsertionPoint(newPos); + } + } + + if ( textChanged ) + { + wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId()); + InitCommandEvent(event); + event.SetString(GetValue()); + GetEventHandler()->ProcessEvent(event); + } + + return TRUE; +} + +void wxTextCtrl::OnChar(wxKeyEvent& event) +{ + // only process the key events from "simple keys" here + if ( !event.HasModifiers() ) + { + int keycode = event.GetKeyCode(); + if ( keycode != WXK_DELETE && keycode != WXK_BACK ) + { + PerformAction(wxACTION_TEXT_INSERT, -1, (wxChar)keycode); + + // skip event.Skip() below + return; + } + } + + event.Skip(); +} + +// ---------------------------------------------------------------------------- +// wxStdTextCtrlInputHandler +// ---------------------------------------------------------------------------- + +wxStdTextCtrlInputHandler::wxStdTextCtrlInputHandler(wxInputHandler *inphand) + : wxStdInputHandler(inphand) +{ +} + +bool wxStdTextCtrlInputHandler::HandleKey(wxControl *control, + const wxKeyEvent& event, + bool pressed) +{ + if ( !pressed ) + return FALSE; + + wxControlAction action; + wxString str; + bool ctrlDown = event.ControlDown(); + if ( event.ShiftDown() ) + { + action = wxACTION_TEXT_PREFIX_SEL; + } + + int keycode = event.GetKeyCode(); + switch ( keycode ) + { + // cursor movement + case WXK_HOME: + action << wxACTION_TEXT_HOME; + break; + + case WXK_END: + action << wxACTION_TEXT_END; + break; + + case WXK_LEFT: + action << (ctrlDown ? wxACTION_TEXT_WORD_LEFT + : wxACTION_TEXT_LEFT); + break; + + case WXK_RIGHT: + action << (ctrlDown ? wxACTION_TEXT_WORD_RIGHT + : wxACTION_TEXT_RIGHT); + break; + + // delete + case WXK_DELETE: + action << wxACTION_TEXT_PREFIX_DEL << wxACTION_TEXT_RIGHT; + break; + + case WXK_BACK: + action << wxACTION_TEXT_PREFIX_DEL << wxACTION_TEXT_LEFT; + break; + } + + if ( !!action ) + { + control->PerformAction(action, -1, str); + + return TRUE; + } + + return wxStdInputHandler::HandleKey(control, event, pressed); +} + +bool wxStdTextCtrlInputHandler::HandleMouse(wxControl *control, + const wxMouseEvent& event) +{ + return wxStdInputHandler::HandleMouse(control, event); +} + +bool wxStdTextCtrlInputHandler::HandleMouseMove(wxControl *control, + const wxMouseEvent& event) +{ + return wxStdInputHandler::HandleMouseMove(control, event); } #endif // wxUSE_TEXTCTRL diff --git a/src/univ/themes/gtk.cpp b/src/univ/themes/gtk.cpp index 4cc4038a36..b190f27ec0 100644 --- a/src/univ/themes/gtk.cpp +++ b/src/univ/themes/gtk.cpp @@ -72,6 +72,10 @@ public: int alignment = wxALIGN_LEFT | wxALIGN_TOP, int indexAccel = -1, wxRect *rectBounds = NULL); + virtual void DrawTextLine(wxDC& dc, + const wxString& text, + const wxRect &rect, + int flags = 0); virtual void DrawBorder(wxDC& dc, wxBorder border, const wxRect& rect, @@ -440,6 +444,8 @@ wxInputHandler *wxGTKTheme::GetInputHandler(const wxString& control) handler = new wxStdListboxInputHandler(GetDefaultInputHandler()); else if ( control == wxINP_HANDLER_CHECKLISTBOX ) handler = new wxStdCheckListboxInputHandler(GetDefaultInputHandler()); + else if ( control == wxINP_HANDLER_TEXTCTRL ) + handler = new wxStdTextCtrlInputHandler(GetDefaultInputHandler()); else handler = GetDefaultInputHandler(); @@ -905,6 +911,14 @@ void wxGTKRenderer::DrawButtonLabel(wxDC& dc, dc.DrawLabel(label, image, rect, alignment, indexAccel, rectBounds); } +void wxGTKRenderer::DrawTextLine(wxDC& dc, + const wxString& text, + const wxRect &rect, + int flags) +{ + dc.DrawText(text, rect.x, rect.y); +} + void wxGTKRenderer::DrawItem(wxDC& dc, const wxString& label, const wxRect& rect, diff --git a/src/univ/themes/win32.cpp b/src/univ/themes/win32.cpp index 7de2f70dd7..5e4900cb43 100644 --- a/src/univ/themes/win32.cpp +++ b/src/univ/themes/win32.cpp @@ -89,6 +89,10 @@ public: int alignment = wxALIGN_LEFT | wxALIGN_TOP, int indexAccel = -1, wxRect *rectBounds = NULL); + virtual void DrawTextLine(wxDC& dc, + const wxString& text, + const wxRect &rect, + int flags = 0); virtual void DrawBorder(wxDC& dc, wxBorder border, const wxRect& rect, @@ -160,7 +164,7 @@ public: int thumbPos = -1); virtual int PixelToScrollbar(const wxScrollBar *scrollbar, wxCoord coord); virtual wxCoord GetListboxItemHeight(wxCoord fontHeight) - { return fontHeight; } + { return fontHeight + 2; } virtual wxSize GetCheckBitmapSize() const { return wxSize(13, 13); } virtual wxSize GetRadioBitmapSize() const @@ -650,6 +654,8 @@ wxInputHandler *wxWin32Theme::GetInputHandler(const wxString& control) handler = new wxStdListboxInputHandler(GetDefaultInputHandler()); else if ( control == wxINP_HANDLER_CHECKLISTBOX ) handler = new wxStdCheckListboxInputHandler(GetDefaultInputHandler()); + else if ( control == wxINP_HANDLER_TEXTCTRL ) + handler = new wxStdTextCtrlInputHandler(GetDefaultInputHandler()); else handler = GetDefaultInputHandler(); @@ -1385,6 +1391,14 @@ void wxWin32Renderer::DrawButtonLabel(wxDC& dc, } } +void wxWin32Renderer::DrawTextLine(wxDC& dc, + const wxString& text, + const wxRect &rect, + int flags) +{ + dc.DrawText(text, rect.x + 2, rect.y + 1); +} + // ---------------------------------------------------------------------------- // (check)listbox items // ---------------------------------------------------------------------------- @@ -1439,7 +1453,7 @@ void wxWin32Renderer::DrawCheckItem(wxDC& dc, : unchecked_item_xpm); } - dc.DrawBitmap(bmp, rect.x, rect.y + (rect.height - bmp.GetHeight()) / 2, + dc.DrawBitmap(bmp, rect.x, rect.y + (rect.height - bmp.GetHeight()) / 2 - 1, TRUE /* use mask */); wxRect rectLabel = rect;