From b480d851282158791498ebdd7b5ff186fb8ecec3 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin <vadim@wxwidgets.org> Date: Sat, 5 Jun 1999 21:29:41 +0000 Subject: [PATCH] repositioning the cursor with mouse clicks works git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@2677 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- user/wxLayout/wxllist.cpp | 87 +++++++++++++--- user/wxLayout/wxllist.h | 19 +++- user/wxLayout/wxlwindow.cpp | 201 ++++++++++++++++++++++++++---------- user/wxLayout/wxlwindow.h | 2 +- 4 files changed, 234 insertions(+), 75 deletions(-) diff --git a/user/wxLayout/wxllist.cpp b/user/wxLayout/wxllist.cpp index 4596fc7205..cc7aa01872 100644 --- a/user/wxLayout/wxllist.cpp +++ b/user/wxLayout/wxllist.cpp @@ -75,7 +75,7 @@ // waste time looking for it right now. Search for occurences of // MSW_CORRECTION to find all the places where I did it. #ifdef __WXMSW__ - static const int MSW_CORRECTION = 5; + static const int MSW_CORRECTION = 10; #else static const int MSW_CORRECTION = 0; #endif @@ -1453,6 +1453,7 @@ wxLayoutList::Empty(void) m_CursorPos = wxPoint(0,0); m_CursorScreenPos = wxPoint(0,0); m_CursorSize = wxPoint(0,0); + m_movedCursor = true; m_FirstLine = new wxLayoutLine(NULL, this); // empty first line m_CursorLine = m_FirstLine; InvalidateUpdateRect(); @@ -1556,6 +1557,8 @@ wxLayoutList::MoveCursorTo(wxPoint const &p) { AddCursorPosToUpdateRect(); + wxPoint cursorPosOld = m_CursorPos; + wxLayoutLine *line = m_FirstLine; while(line && line->GetLineNumber() != p.y) line = line->GetNextLine(); @@ -1567,15 +1570,16 @@ wxLayoutList::MoveCursorTo(wxPoint const &p) if(len >= p.x) { m_CursorPos.x = p.x; - return true; } else { m_CursorPos.x = len; - return false; } } - return false; + + m_movedCursor = m_CursorPos != cursorPosOld; + + return m_CursorPos == p; } bool @@ -1583,6 +1587,8 @@ wxLayoutList::MoveCursorVertically(int n) { AddCursorPosToUpdateRect(); + wxPoint cursorPosOld = m_CursorPos; + bool rc; if(n < 0) // move up { @@ -1629,6 +1635,9 @@ wxLayoutList::MoveCursorVertically(int n) rc = true; } } + + m_movedCursor = m_CursorPos != cursorPosOld; + return rc; } @@ -1637,6 +1646,8 @@ wxLayoutList::MoveCursorHorizontally(int n) { AddCursorPosToUpdateRect(); + wxPoint cursorPosOld = m_CursorPos; + int move; while(n < 0) { @@ -1669,6 +1680,9 @@ wxLayoutList::MoveCursorHorizontally(int n) m_CursorPos.x += move; n -= move; } + + m_movedCursor = m_CursorPos != cursorPosOld; + return n == 0; } @@ -1760,6 +1774,8 @@ wxLayoutList::Insert(wxString const &text) m_CursorPos.x += text.Length(); + m_movedCursor = true; + m_CursorLine->RecalculatePositions(0, this); return true; @@ -1777,6 +1793,7 @@ wxLayoutList::Insert(wxLayoutObject *obj) m_CursorLine->Insert(m_CursorPos.x, obj); m_CursorPos.x += obj->GetLength(); + m_movedCursor = true; m_CursorLine->RecalculatePositions(0, this); @@ -1822,13 +1839,14 @@ wxLayoutList::LineBreak(void) if(m_CursorPos.x != 0) m_CursorPos.y++; m_CursorPos.x = 0; -// doesn't help m_CursorLine.MarkDirty(); wxLayoutLine *prev = m_CursorLine->GetPreviousLine(); wxCHECK_MSG(prev, false, "just broke the line, where is the previous one?"); height += prev->GetHeight(); + m_movedCursor = true; + SetUpdateRect(position); SetUpdateRect(position.x + width + MSW_CORRECTION, position.y + height + MSW_CORRECTION); @@ -1858,6 +1876,8 @@ wxLayoutList::WrapLine(CoordType column) m_CursorLine->RecalculatePositions(1, this); + m_movedCursor = true; + return true; } } @@ -1866,7 +1886,9 @@ bool wxLayoutList::Delete(CoordType npos) { wxCHECK_MSG(m_CursorLine, false, "can't delete in non existing line"); - wxASSERT_MSG(npos > 0, "nothing to delete?"); + + if ( npos == 0 ) + return true; AddCursorPosToUpdateRect(); @@ -1988,16 +2010,43 @@ wxLayoutList::Recalculate(wxDC &dc, CoordType bottom) } void -wxLayoutList::UpdateCursorScreenPos(wxDC &dc) +wxLayoutList::UpdateCursorScreenPos(wxDC &dc, + bool resetCursorMovedFlag, + const wxPoint& translate) { - wxASSERT(m_CursorLine); - m_CursorLine->Layout(dc, this, (wxPoint *)&m_CursorScreenPos, (wxPoint *)&m_CursorSize, m_CursorPos.x); + wxCHECK_RET( m_CursorLine, "no cursor line" ); + + if ( m_movedCursor ) + { + m_CursorLine->Layout(dc, this, + &m_CursorScreenPos, &m_CursorSize, + m_CursorPos.x); + + if ( resetCursorMovedFlag ) + { + #ifdef WXLAYOUT_USE_CARET + // adjust the caret position + wxPoint coords(m_CursorScreenPos); + coords += translate; + + // and set it + m_caret->Move(coords); + #endif // WXLAYOUT_USE_CARET + + m_movedCursor = false; + } + } } wxPoint wxLayoutList::GetCursorScreenPos(wxDC &dc) { - UpdateCursorScreenPos(dc); + // this function is called with wxMemoryDC argument from ScrollToCursor(), + // for example, so it shouldn't clear "cursor moved" flag - or else the + // cursor won't be moved when UpdateCursorScreenPos() is called with the + // "real" (i.e. the one used for drawing) wxDC. + UpdateCursorScreenPos(dc, false /* don't reset the flag */); + return m_CursorScreenPos; } @@ -2140,6 +2189,20 @@ wxLayoutList::GetSize(void) const } maxPoint.y = last->GetPosition().y + last->GetHeight(); + + // if the line was just added, its height would be 0 and we can't call + // Layout() from here because we don't have a dc and we might be not drawing + // at all, besides... So take the cursor height by default (taking 0 is bad + // because then the scrollbars won't be resized and the new line won't be + // shown at all) + if ( last->IsDirty() ) + { + if ( last->GetHeight() == 0 ) + maxPoint.y += m_CursorSize.y; + if ( last->GetWidth() == 0 && maxPoint.x < m_CursorSize.x ) + maxPoint.x = m_CursorSize.x; + } + return maxPoint; } @@ -2161,9 +2224,7 @@ wxLayoutList::DrawCursor(wxDC &dc, bool active, wxPoint const &translate) wxLogStatus("Cursor is at (%d, %d)", m_CursorPos.x, m_CursorPos.y); #endif -#ifdef WXLAYOUT_USE_CARET - m_caret->Move(coords); -#else // !WXLAYOUT_USE_CARET +#ifndef WXLAYOUT_USE_CARET dc.SetBrush(*wxBLACK_BRUSH); dc.SetLogicalFunction(wxXOR); dc.SetPen(wxPen(*wxBLACK,1,wxSOLID)); diff --git a/user/wxLayout/wxllist.h b/user/wxLayout/wxllist.h index 05e4498652..53234268a5 100644 --- a/user/wxLayout/wxllist.h +++ b/user/wxLayout/wxllist.h @@ -59,7 +59,6 @@ # define WXLO_BITMAP_FORMAT wxBITMAP_TYPE_PNG #endif - /// Types of currently supported layout objects. enum wxLayoutObjectType { @@ -942,6 +941,14 @@ public: @return cursor position in pixels */ wxPoint GetCursorScreenPos(wxDC &dc); + /** Calculates the cursor position on the screen. + @param dc the dc to use for cursor position calculations + @param resetCursorMovedFlag: if true, reset "cursor moved" flag + @param translate optional translation of cursor coords on screen + */ + void UpdateCursorScreenPos(wxDC &dc, + bool resetCursorMovedFlag = true, + const wxPoint& translate = wxPoint(0, 0)); /** Draws the cursor. @param active If true, draw a bold cursor to mark window as @@ -973,7 +980,7 @@ public: /** Called by the objects to update the update rectangle. @param p a point to include in it */ - inline void SetUpdateRect(const wxPoint &p) + void SetUpdateRect(const wxPoint &p) { SetUpdateRect(p.x,p.y); } /// adds the cursor position to the update rectangle void AddCursorPosToUpdateRect() @@ -990,6 +997,9 @@ public: const wxRect *GetUpdateRect(void) const { return &m_UpdateRect; } //@} + /// get the current cursor size + const wxPoint& GetCursorSize() const { return m_CursorSize; } + /**@name For exporting one object after another. */ //@{ /** Returns a pointer to the first line in the list. */ @@ -1044,9 +1054,6 @@ public: private: /// Clear the list. void InternalClear(void); - /** Calculates the cursor position on the screen. - */ - void UpdateCursorScreenPos(wxDC &dc); /// The list of lines. wxLayoutLine *m_FirstLine; @@ -1064,6 +1071,8 @@ private: wxLayoutLine *m_CursorLine; /// The size of the cursor. wxPoint m_CursorSize; + /// Has the cursor moved (is m_CursorScreenPos up to date)? + bool m_movedCursor; #ifdef WXLAYOUT_USE_CARET /// the caret wxCaret *m_caret; diff --git a/user/wxLayout/wxlwindow.cpp b/user/wxLayout/wxlwindow.cpp index 7f0bbbdfe1..16d4d0d5f3 100644 --- a/user/wxLayout/wxlwindow.cpp +++ b/user/wxLayout/wxlwindow.cpp @@ -6,6 +6,14 @@ * $Id$ *******************************************************************/ +// =========================================================================== +// declarations +// =========================================================================== + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + #ifdef __GNUG__ # pragma implementation "wxlwindow.h" #endif @@ -45,12 +53,20 @@ #include <ctype.h> +// ---------------------------------------------------------------------------- +// macros +// ---------------------------------------------------------------------------- + #ifdef WXLAYOUT_DEBUG # define WXLO_DEBUG(x) wxLogDebug x #else # define WXLO_DEBUG(x) #endif +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + /// offsets to put a nice frame around text #define WXLO_XOFFSET 4 #define WXLO_YOFFSET 4 @@ -59,6 +75,14 @@ #define WXLO_ROFFSET 20 #define WXLO_BOFFSET 20 +/// the size of one scrollbar page in pixels +static const int X_SCROLL_PAGE = 10; +static const int Y_SCROLL_PAGE = 20; + +// ---------------------------------------------------------------------------- +// event tables +// ---------------------------------------------------------------------------- + BEGIN_EVENT_TABLE(wxLayoutWindow,wxScrolledWindow) EVT_PAINT (wxLayoutWindow::OnPaint) EVT_CHAR (wxLayoutWindow::OnChar) @@ -72,6 +96,14 @@ BEGIN_EVENT_TABLE(wxLayoutWindow,wxScrolledWindow) EVT_KILL_FOCUS(wxLayoutWindow::OnKillFocus) END_EVENT_TABLE() +// =========================================================================== +// implementation +// =========================================================================== + +// ---------------------------------------------------------------------------- +// wxLayoutWindow +// ---------------------------------------------------------------------------- + wxLayoutWindow::wxLayoutWindow(wxWindow *parent) : wxScrolledWindow(parent, -1, wxDefaultPosition, wxDefaultSize, @@ -95,9 +127,11 @@ wxLayoutWindow::wxLayoutWindow(wxWindow *parent) m_ScrollToCursor = false; SetWrapMargin(0); wxPoint max = m_llist->GetSize(); - SetScrollbars(10, 20 /*lineHeight*/, max.x/10+1, max.y/20+1); - EnableScrolling(true,true); - m_maxx = max.x; m_maxy = max.y; + SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE, + max.x / X_SCROLL_PAGE + 1, max.y / Y_SCROLL_PAGE + 1); + EnableScrolling(true, true); + m_maxx = max.x + X_SCROLL_PAGE; + m_maxy = max.y + Y_SCROLL_PAGE; m_Selecting = false; #ifdef WXLAYOUT_USE_CARET @@ -220,13 +254,32 @@ wxLayoutWindow::OnMouse(int eventId, wxMouseEvent& event) } // always move cursor to mouse click: - if(obj && eventId == WXLOWIN_MENU_LCLICK) + if(eventId == WXLOWIN_MENU_LCLICK) { m_llist->MoveCursorTo(cursorPos); + + // Calculate where the top of the visible area is: + int x0, y0; + ViewStart(&x0,&y0); + int dx, dy; + GetScrollPixelsPerUnit(&dx, &dy); + x0 *= dx; y0 *= dy; + + wxPoint offset(-x0+WXLO_XOFFSET, -y0+WXLO_YOFFSET); + m_llist->UpdateCursorScreenPos(dc, true, offset); + if(m_CursorVisibility == -1) m_CursorVisibility = 1; + + // VZ: this should be unnecessary because mouse can only click on a + // visible part of the canvas +#if 0 ScrollToCursor(); +#endif // 0 + +#ifdef __WXGTK__ DoPaint(FALSE); // DoPaint suppresses flicker under GTK +#endif // wxGTK } if(!m_doSendEvents) // nothing to do @@ -323,10 +376,10 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) m_llist->MoveCursorVertically(1); break; case WXK_PRIOR: - m_llist->MoveCursorVertically(-20); + m_llist->MoveCursorVertically(-Y_SCROLL_PAGE); break; case WXK_NEXT: - m_llist->MoveCursorVertically(20); + m_llist->MoveCursorVertically(Y_SCROLL_PAGE); break; case WXK_HOME: if ( ctrlDown ) @@ -336,12 +389,11 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) break; case WXK_END: if ( ctrlDown ) - { - // TODO VZ: how to move the cursor to the last line of the text? - m_llist->MoveCursorVertically(1000); - } - m_llist->MoveCursorToEndOfLine(); + m_llist->MoveCursorTo(m_llist->GetSize()); + else + m_llist->MoveCursorToEndOfLine(); break; + default: if(keyCode == 'c' && ctrlDown) { @@ -430,11 +482,9 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) && (keyCode < 256 && keyCode >= 32) ) { - wxString tmp; - tmp += keyCode; if(m_WrapMargin > 0 && isspace(keyCode)) m_llist->WrapLine(m_WrapMargin); - m_llist->Insert(tmp); + m_llist->Insert((char)keyCode); } break; } @@ -454,9 +504,13 @@ wxLayoutWindow::OnChar(wxKeyEvent& event) } } + // we must call ResizeScrollbars() before ScrollToCursor(), otherwise the + // ne cursor position might be outside the current scrolllbar range + ResizeScrollbars(); ScrollToCursor(); - wxRect r = *m_llist->GetUpdateRect(); - DoPaint(&r); + + // refresh the screen + DoPaint(m_llist->GetUpdateRect()); } void @@ -488,34 +542,57 @@ wxLayoutWindow::ScrollToCursor(void) // Get the size of the visible window: GetClientSize(&x1,&y1); - wxASSERT(x1 > 0); - wxASSERT(y1 > 0); - // As we have the values anyway, use them to avoid unnecessary - // scrollbar updates. + + // notice that the client size may be (0, 0)... + wxASSERT(x1 >= 0 && y1 >= 0); + + // VZ: I think this is false - if you do it here, ResizeScrollbars() won't + // call SetScrollbars() later +#if 0 + // As we have the values anyway, use them to avoid unnecessary scrollbar + // updates. if(x1 > m_maxx) m_maxx = x1; if(y1 > m_maxy) m_maxy = y1; - /* Make sure that the scrollbars are at a position so that the - cursor is visible if we are editing. */ - /** Scroll so that cursor is visible! */ +#endif // 0 + + // Make sure that the scrollbars are at a position so that the cursor is + // visible if we are editing WXLO_DEBUG(("m_ScrollToCursor = %d", (int) m_ScrollToCursor)); wxPoint cc = m_llist->GetCursorScreenPos(*m_memDC); - if(cc.x < x0 || cc.y < y0 - || cc.x >= x0+(9*x1)/10 || cc.y >= y0+(9*y1/10)) // (9*x)/10 == 90% + + // the cursor should be completely visible in both directions + wxPoint cs(m_llist->GetCursorSize()); + int nx = -1, + ny = -1; + if ( cc.x < x0 || cc.x >= x0 + x1 - cs.x ) + { + nx = cc.x - x1/2; + if ( nx < 0 ) + nx = 0; + } + + if ( cc.y < y0 || cc.y >= y0 + y1 - cs.y ) { - int nx, ny; - nx = cc.x - x1/2; if(nx < 0) nx = 0; - ny = cc.y - y1/2; if(ny < 0) ny = 0; - Scroll(nx/dx,ny/dy); // new view start - x0 = nx; y0 = ny; - m_ScrollToCursor = false; // avoid recursion + ny = cc.y - y1/2; + if ( ny < 0) + ny = 0; + } + + if ( nx != -1 || ny != -1 ) + { + // set new view start + Scroll(nx == -1 ? -1 : (nx+dx-1)/dx, ny == -1 ? -1 : (ny+dy-1)/dy); + + // avoid recursion + m_ScrollToCursor = false; } } void -wxLayoutWindow::OnPaint( wxPaintEvent &WXUNUSED(event)) // or: OnDraw(wxDC& dc) +wxLayoutWindow::OnPaint( wxPaintEvent &WXUNUSED(event)) { wxRect region = GetUpdateRegion().GetBox(); - InternalPaint(& region); + InternalPaint(®ion); } void @@ -551,13 +628,8 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) // Get the size of the visible window: GetClientSize(&x1,&y1); - wxASSERT(x1 > 0); - wxASSERT(y1 > 0); - // As we have the values anyway, use them to avoid unnecessary - // scrollbar updates. - if(x1 > m_maxx) m_maxx = x1; - if(y1 > m_maxy) m_maxy = y1; - + wxASSERT(x1 >= 0); + wxASSERT(y1 >= 0); if(updateRect) { @@ -634,10 +706,13 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) // needed to erase it): m_llist->InvalidateUpdateRect(); if(m_CursorVisibility != 0) + { + m_llist->UpdateCursorScreenPos(dc, true, offset); m_llist->DrawCursor(*m_memDC, m_HaveFocus && IsEditable(), // draw a thick // cursor for editable windows with focus offset); + } // Now copy everything to the screen: #if 0 @@ -672,13 +747,22 @@ wxLayoutWindow::InternalPaint(const wxRect *updateRect) ResetDirty(); m_ScrollToCursor = false; - if(m_StatusBar && m_StatusFieldCursor != -1) + + if ( m_StatusBar && m_StatusFieldCursor != -1 ) { - wxString label; - label.Printf(_("Ln:%d Col:%d"), - m_llist->GetCursorPos().y+1, - m_llist->GetCursorPos().x+1); - m_StatusBar->SetStatusText(label, m_StatusFieldCursor); + static wxPoint s_oldCursorPos(-1, -1); + + wxPoint pos(m_llist->GetCursorPos()); + + // avoid unnecessary status bar refreshes + if ( pos != s_oldCursorPos ) + { + s_oldCursorPos = pos; + + wxString label; + label.Printf(_("Ln:%d Col:%d"), pos.y + 1, pos.x + 1); + m_StatusBar->SetStatusText(label, m_StatusFieldCursor); + } } } @@ -688,21 +772,26 @@ wxLayoutWindow::ResizeScrollbars(bool exact) { wxPoint max = m_llist->GetSize(); - WXLO_DEBUG(("ResizeScrollbars: GetSize: %ld, %ld", (long int)max.x, - (long int) max.y)); - if(max.x > m_maxx || max.y > m_maxy - || max.x > m_maxx-WXLO_ROFFSET || max.y > m_maxy-WXLO_BOFFSET - || exact) + WXLO_DEBUG(("ResizeScrollbars: max size = (%ld, %ld)", + (long int)max.x, (long int) max.y)); + + if( max.x > m_maxx - WXLO_ROFFSET || max.y > m_maxy - WXLO_BOFFSET || exact ) { - if(! exact) + if ( !exact ) { // add an extra bit to the sizes to avoid future updates - max.x = max.x+WXLO_ROFFSET; - max.y = max.y+WXLO_BOFFSET; + max.x += WXLO_ROFFSET; + max.y += WXLO_BOFFSET; } + ViewStart(&m_ViewStartX, &m_ViewStartY); - SetScrollbars(10, 20, max.x/10+1,max.y/20+1,m_ViewStartX,m_ViewStartY,true); - m_maxx = max.x; m_maxy = max.y; + SetScrollbars(X_SCROLL_PAGE, Y_SCROLL_PAGE, + max.x / X_SCROLL_PAGE + 1, max.y / Y_SCROLL_PAGE + 1, + m_ViewStartX, m_ViewStartY, + true); + + m_maxx = max.x + X_SCROLL_PAGE; + m_maxy = max.y + Y_SCROLL_PAGE; } } diff --git a/user/wxLayout/wxlwindow.h b/user/wxLayout/wxlwindow.h index c857b1572c..f943c10773 100644 --- a/user/wxLayout/wxlwindow.h +++ b/user/wxLayout/wxlwindow.h @@ -187,7 +187,7 @@ protected: bool m_HaveFocus; /// do we handle clicks of the right mouse button? bool m_DoPopupMenu; - /// Should InternalPaint() scroll to cursor. + /// Should InternalPaint() scroll to cursor (VZ: seems unused any more) bool m_ScrollToCursor; /// Do we currently have a non-standard cursor? bool m_HandCursor; -- 2.45.2